diff --git a/config/config.go b/config/config.go index 09fb4b6..6de70f7 100644 --- a/config/config.go +++ b/config/config.go @@ -71,6 +71,7 @@ type ThridPartyConfig struct { Interlocks []InterlockConfig `json:"interlock" description:"联锁配置"` RsspAxleCfgs []RsspAxleConfig `json:"rsspAxleCfgs" description:"所有联锁集中站计轴RSSP-I配置"` ElectricMachinery ElectricMachineryConfig `json:"electricMachinery" description:"电机配置"` + BtmCanet BtmCanetConfig `json:"btmCanet" description:"BTM关联的网关设备CANET配置"` } type DynamicsConfig struct { Ip string `json:"ip" description:"IP配置"` @@ -99,6 +100,14 @@ type ElectricMachineryConfig struct { Open bool `json:"open" description:"是否开启"` } +// BtmCanetConfig BTM CANET网关设备配置 +type BtmCanetConfig struct { + LocalUdpPort int `json:"localUdpPort" description:"本机监听接收UDP端口"` + RemoteIp string `json:"remoteIp" description:"CANET设备IP配置"` + RemoteUdpPort int `json:"remoteUdpPort" description:"CANET设备UDP端口"` + Open bool `json:"open" description:"是否开启"` +} + // RsspAxleConfig 计轴区段与联锁安全通信配置 type RsspAxleConfig struct { Open bool `json:"open" description:"是否开启"` diff --git a/third_party/can_btm/balise_btm.go b/third_party/can_btm/balise_btm.go index ef06e21..dfd8e22 100644 --- a/third_party/can_btm/balise_btm.go +++ b/third_party/can_btm/balise_btm.go @@ -2,17 +2,29 @@ package can_btm import ( "fmt" + "joylink.club/bj-rtsts-server/config" "joylink.club/bj-rtsts-server/third_party/message" "joylink.club/bj-rtsts-server/third_party/udp" - "joylink.club/ecs" + "joylink.club/bj-rtsts-server/ts/simulation/wayside/memory" "joylink.club/rtsssimulation/fi" "log/slog" "sort" + "sync" + "time" ) +//使用:当有多个虚拟车时,同一时刻只有一个虚拟车与CANET关联 + +// BtmCanetManager BTM CANET 管理器 +type BtmCanetManager interface { + memory.VerifyEvn + //GetBtmCanetConfig 获取CANET配置信息 + GetBtmCanetConfig() config.BtmCanetConfig +} + // btm与canet(网口-CAN口转换器) type btmCanetClient struct { - w ecs.World + bcm BtmCanetManager //udp server udpServer udp.UdpServer //udp client @@ -23,28 +35,53 @@ type btmCanetClient struct { remoteUdpPort int //udp 远程ip remoteIp string - //该CANET关联的唯一列车的id - trainId string - //车载ATP系统当前时间ms - atpSystemTime uint32 //最近一次车载ATP系统查询帧序号 atpReqSn byte - //最近一次车载ATP系统查询帧CRC16校验结果 + //最近一次车载ATP系统查询帧CRC16校验结果,true-校验通过 atpReqCrc16Check bool + //btm系统时间,每次接收到ATP查询请求帧时同步一次时间 + btmTime btmClock + //数据流水号 + dsn byte } -type BtmCanetClient interface { - Start() - Stop() - TrainId() string +type btmClock struct { + btmTk uint32 //与ATP系统同步的时间ms + sysTk time.Time //本地系统时间 } -func NewBtmCanetClient(trainId string, remoteIp string, remoteUdpPort int, localUdpPort int) BtmCanetClient { - return &btmCanetClient{trainId: trainId, remoteIp: remoteIp, remoteUdpPort: remoteUdpPort, localUdpPort: localUdpPort} +// 获取以btmTk为基准的当前时间ms +func (c *btmClock) tkNow() uint32 { + return c.btmTk + uint32(time.Now().UnixMilli()-c.sysTk.UnixMilli()) } -func (s *btmCanetClient) TrainId() string { - return s.trainId + +type BtmCanetClient interface { + Start(bcm BtmCanetManager) + Stop() } -func (s *btmCanetClient) Start() { + +var ( + btmClientLocker sync.Mutex + btmClient BtmCanetClient +) + +func Default() BtmCanetClient { + btmClientLocker.Lock() + defer btmClientLocker.Unlock() + if btmClient == nil { + btmClient = &btmCanetClient{} + } + return btmClient +} + +func (s *btmCanetClient) Start(bcm BtmCanetManager) { + s.bcm = bcm + cfg := s.bcm.GetBtmCanetConfig() + if !cfg.Open { + return + } + s.localUdpPort = cfg.LocalUdpPort + s.remoteIp = cfg.RemoteIp + s.remoteUdpPort = cfg.RemoteUdpPort // s.udpServer = udp.NewServer(fmt.Sprintf(":%d", s.localUdpPort), s.handleCanetFrames) s.udpServer.Listen() @@ -55,9 +92,11 @@ func (s *btmCanetClient) Start() { func (s *btmCanetClient) Stop() { if s.udpServer != nil { s.udpServer.Close() + s.udpServer = nil } if s.udpClient != nil { s.udpClient.Close() + s.udpClient = nil } } func (s *btmCanetClient) handleCanetFrames(cfs []byte) { @@ -116,32 +155,69 @@ func (s *btmCanetClient) dealWithAptReq(f *message.CanetFrame) { //处理查询请求 slog.Debug(fmt.Sprintf("处理查询请求:%s", atpReq.String())) // - s.atpSystemTime = atpReq.Time + s.btmTime.btmTk = atpReq.Time + s.btmTime.sysTk = time.Now() s.atpReqSn = atpReq.FId.ID4 s.atpReqCrc16Check = atpReq.Crc16CheckOk - se := fi.TrainBalisePowerAmplifierSwitch(s.w, s.trainId, atpReq.PowerAmplifierTurnOn) + se := fi.TrainBalisePowerAmplifierSwitch(s.bcm.EvnWorld(), atpReq.PowerAmplifierTurnOn) if se != nil { - slog.Warn(fmt.Sprintf("列车[%s]车载BTM功率放大器开关控制异常[%s]", s.trainId, se.Error())) + slog.Warn(fmt.Sprintf("列车车载BTM功率放大器开关控制异常[%s]", se.Error())) } + //ATP 是否要求BTM 重发上一应答器报文 + isResendRequest := atpReq.ResendRequest == 2 //0b10 + s.rspToAtp(isResendRequest) } // BTM发送响应给ATP // 当收到应答器报文时响应:时间同步帧、状态应答帧、数据帧 // 当未收到应答器报文时响应:时间同步帧、状态应答帧 -func (s *btmCanetClient) rspToAtp() { - timeSyncF := message.NewBtmTimeSyncCheckFrame(s.atpReqSn) - timeSyncCf := timeSyncF.Encode().Encode() +func (s *btmCanetClient) rspToAtp(isResendRequest bool) { + //BTM状态 statusF := message.NewBtmStatusRspFrame(s.atpReqSn) + btmStatus, btmStatusErr := fi.FindTrainBaliseBtmStatus(s.bcm.EvnWorld()) + if btmStatusErr != nil { + slog.Debug(fmt.Sprintf("从仿真获取BTM状态失败:%s", btmStatusErr.Error())) + return + } + statusF.AntennaFault = btmStatus.AntennaFault + statusF.BaliseCounter = byte(btmStatus.BaliseCounter) + statusF.MessageCounter = byte(btmStatus.MessageCounter) + statusF.PowerAmplifierOn = btmStatus.PowerAmplifierOn + statusF.TkTimeA = s.btmTime.tkNow() + statusF.PowerAmplifierFailure = btmStatus.PowerAmplifierFault + statusF.DetailedCode = 0 + if btmStatus.AboveBalise { + statusF.DetailedCode = 0x07 + } + statusF.AtpReqCrcCheckWrong = !s.atpReqCrc16Check + statusF.Dsn = s.dsn + s.dsnAdd1() + // + baliseTelegram, btSendErr := fi.BaliseTelegramForSend(s.bcm.EvnWorld()) + if btSendErr != nil { + //slog.Debug(btSendErr.Error()) + } //true-收到应答器报文 - //todo - isRcvTelegram := false - + isRcvTelegram := baliseTelegram != nil if isRcvTelegram { //当收到应答器报文时响应:时间同步帧、状态应答帧、数据帧 - s.sendCanetFrame(timeSyncCf) - + statusDataCf, statusDataCfOk := message.CreateBtmRspFramesData(statusF, baliseTelegram.Telegram, false, s.btmTime.tkNow(), s.btmTime.tkNow(), s.btmTime.tkNow()) + if statusDataCfOk { + timeSyncF := message.NewBtmTimeSyncCheckFrame(s.atpReqSn) + timeSyncF.T2 = s.btmTime.btmTk + timeSyncF.T3 = s.btmTime.tkNow() + s.sendCanetFrame(timeSyncF.Encode().Encode()) + // + s.sendCanetFrame(statusDataCf) + } else { + slog.Warn("BtmCanetClient应答帧、数据帧编码失败") + } } else { //当未收到应答器报文时响应:时间同步帧、状态应答帧 + timeSyncF := message.NewBtmTimeSyncCheckFrame(s.atpReqSn) + timeSyncF.T2 = s.btmTime.btmTk + timeSyncF.T3 = s.btmTime.tkNow() + s.sendCanetFrame(timeSyncF.Encode().Encode()) + // statusCf := statusF.Encode().Encode() - s.sendCanetFrame(timeSyncCf) s.sendCanetFrame(statusCf) } } @@ -150,6 +226,13 @@ func (s *btmCanetClient) rspToAtp() { func (s *btmCanetClient) sendCanetFrame(cf []byte) { s.udpClient.Send(cf) } +func (s *btmCanetClient) dsnAdd1() { + if s.dsn >= 255 { + s.dsn = 0 + } else { + s.dsn++ + } +} ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/ts/simulation/wayside/memory/wayside_simulation.go b/ts/simulation/wayside/memory/wayside_simulation.go index f65e111..68d5182 100644 --- a/ts/simulation/wayside/memory/wayside_simulation.go +++ b/ts/simulation/wayside/memory/wayside_simulation.go @@ -147,6 +147,11 @@ func (s *VerifySimulation) GetComIdByUid(uid string) string { } return es[uid].CommonId } + +// GetBtmCanetConfig 获取CANET配置信息 +func (s *VerifySimulation) GetBtmCanetConfig() config.BtmCanetConfig { + return s.runConfig.BtmCanet +} func (s *VerifySimulation) GetLineAllRsspAxleCfgs() []config.RsspAxleConfig { return s.runConfig.RsspAxleCfgs } @@ -502,6 +507,9 @@ func (s *VerifySimulation) GetRunConfigId() int32 { } return s.runConfig.Id } +func (s *VerifySimulation) EvnWorld() ecs.World { + return s.World +} // 初始化运行资源 func (s *VerifySimulation) initRepository() error { @@ -1501,3 +1509,8 @@ func convertToProtoBaliseType(bt graphicData.Transponder_TransponderTypeEnum) pr panic(fmt.Sprintf("graphicData.Transponder_TransponderTypeEnum[%d]无法映射到proto.Transponder_Type", bt)) } } + +// VerifyEvn 测试环境 +type VerifyEvn interface { + EvnWorld() ecs.World +} diff --git a/ts/test_simulation_manage.go b/ts/test_simulation_manage.go index 54cc095..abf340f 100644 --- a/ts/test_simulation_manage.go +++ b/ts/test_simulation_manage.go @@ -2,6 +2,7 @@ package ts import ( "fmt" + "joylink.club/bj-rtsts-server/third_party/can_btm" "log/slog" "runtime" "strconv" @@ -117,6 +118,8 @@ func runThirdParty(s *memory.VerifySimulation) error { axle_device.StartLineAllRsspAxleServices(s) // 电机UDP启动 electrical_machinery.Default().Start(s) + // 车载BTM启动 + can_btm.Default().Start(s) return nil } @@ -134,6 +137,9 @@ func stopThirdParty(s *memory.VerifySimulation) { axle_device.StopLineAllRsspAxleServices() // 电机UDP停止 electrical_machinery.Default().Stop() + // 车载BTM停止 + can_btm.Default().Stop() + } func createSimulationId(projectId int32) string {