package semi_physical_train import ( "context" "encoding/hex" "fmt" "github.com/google/uuid" "joylink.club/bj-rtsts-server/config" "joylink.club/bj-rtsts-server/const/balise_const" "joylink.club/bj-rtsts-server/dto/state_proto" "joylink.club/bj-rtsts-server/third_party/can_btm" "joylink.club/bj-rtsts-server/third_party/message" "joylink.club/bj-rtsts-server/third_party/udp" "log/slog" "math" "time" "strings" "sync" ) type BtmVobcManage interface { GetBtmVobcConfig() config.BtmVobcConfig GetAllTrain() []*state_proto.TrainState GetConnVobcTrain() *state_proto.TrainState } type BtmVobcService interface { Start(btmVobcManage BtmVobcManage) Stop() SendData(data []byte) } type BtmVobcClient struct { calFun context.CancelFunc client udp.UdpClient server udp.UdpServer manage BtmVobcManage } var ( btmVobcLocker sync.Mutex btmVobcClient *BtmVobcClient btmVobcBaliseLocker sync.Mutex //最新接受数据时间 reviceTimeStamp int64 ) func BtmDefault() BtmVobcService { defer btmVobcLocker.Unlock() btmVobcLocker.Lock() if btmVobcClient == nil { btmVobcClient = &BtmVobcClient{} } return btmVobcClient } func (b *BtmVobcClient) Start(btmVobcManage BtmVobcManage) { cfg := btmVobcManage.GetBtmVobcConfig() if !cfg.Open { slog.Info("11号线 btm vobc配置未开启...") return } udpServer := udp.NewServer(fmt.Sprintf("%v:%d", cfg.LocalUdpIp, cfg.LocalUdpPort), b.handleBtmVobcFrames) err := udpServer.Listen() if err != nil { slog.Error("11号线 btm VOBC 服务启动失败...") return } // udpClient := udp.NewClient(fmt.Sprintf("%s:%d", cfg.RemoteIp, cfg.RemoteUdpPort)) b.manage = btmVobcManage b.server = udpServer b.client = udpClient reviceTimeStamp = time.Now().UnixMilli() _, calFun := context.WithCancel(context.Background()) b.calFun = calFun } func (b *BtmVobcClient) vobcBtmQueryHandle(data []byte) { train := b.manage.GetConnVobcTrain() if train == nil { slog.Error("vobc btm 未找到VOBC连接的列车...") return } requestId := uuid.New().String() frameType, dataText, err := message.BtmVobcDecode(data) if err != nil { slog.Error(fmt.Sprintf("%v,请求id:%v", err, requestId)) return } receiveDataTime := time.Now().UnixMilli() if frameType == message.COMMAND_TYPE { idCommand := &message.BtmVobcIdCommand{} idCommand.Decode(dataText) slog.Info(fmt.Sprintf("成功接受btm vobc的id命令帧,requestId:%v,接受时间:%v", requestId, receiveDataTime)) if train.VobcState.VobcBtmInfo == nil { train.VobcState.VobcBtmInfo = &state_proto.TrainVobcState_VobcBtmInfo{BtmId: uint32(idCommand.BtmId), VobcId: uint32(idCommand.VobcId), VobcLifeId: idCommand.VobcLifeNum} } } else if train.VobcState.VobcBtmInfo != nil && frameType == message.REQUEST_TYPE { slog.Info(fmt.Sprintf("成功接受btm vobc的请求帧,requestId:%v,接受时间(微秒):%v", requestId, receiveDataTime)) req := &message.BtmVobcReq{} req.Decode(dataText) b.requestFramePackets(requestId, req, train, receiveDataTime) } else { //slog.Error(fmt.Sprintf("btm vobc 解析未知命令帧类型:0x%v,原始数据:%v,长度:%v,requestId:%v", strconv.FormatInt(int64(frameType), 16), hex.EncodeToString(cfs), len(cfs), requestId)) return } } func (b *BtmVobcClient) handleBtmVobcFrames(cfs []byte) { /* reviceTimeStamp = time.Now().UnixMilli() train := b.manage.GetConnVobcTrain() if train == nil { //slog.Error("vobc btm 未找到VOBC连接的列车...") return } requestId := uuid.New().String() slog.Info(fmt.Sprintf("获取到vobc btm原始数据:%v, requestId:%v", hex.EncodeToString(cfs), requestId)) frameType, dataText, err := message.BtmVobcDecode(cfs) if err != nil { slog.Error(fmt.Sprintf("%v,请求id:%v", err, requestId)) return } receiveDataTime := time.Now().UnixMicro() decodePayMicoTime := (receiveDataTime - receiveDataTime) / 100 if frameType == message.COMMAND_TYPE { idCommand := &message.BtmVobcIdCommand{} idCommand.Decode(dataText) slog.Info(fmt.Sprintf("成功接受btm vobc的id命令帧,requestId:%v,接受时间(微秒):%v", requestId, receiveDataTime)) b.packets(requestId, idCommand.VobcLifeNum, idCommand.AutoIdFrame, receiveDataTime, decodePayMicoTime, train.VobcBtm) } else if frameType == message.REQUEST_TYPE { slog.Info(fmt.Sprintf("成功接受btm vobc的请求帧,requestId:%v,接受时间(微秒):%v", requestId, receiveDataTime)) req := &message.BtmVobcReq{} req.Decode(dataText) b.RequestFramePackets(req.VobcLifeNum, req.AutoIdFrame, requestId, receiveDataTime, decodePayMicoTime, req, train.VobcBtm) } else { slog.Error(fmt.Sprintf("btm vobc 解析未知命令帧类型:0x%v,原始数据:%v,长度:%v,requestId:%v", strconv.FormatInt(int64(frameType), 16), hex.EncodeToString(cfs), len(cfs), requestId)) return }*/ } func createFreeBalisePacketString() string { return strings.Repeat("00", balise_const.UserTelegramByteLen) } func createFreeBalisePackets() []byte { str := createFreeBalisePacketString() d, _ := hex.DecodeString(str) return d } // 请求帧 func (b *BtmVobcClient) requestFramePackets(requestId string, req *message.BtmVobcReq, train *state_proto.TrainState, receiveTime int64) { if req.FrameStatus == message.REQ_FRAME_STATUS_BOOT && req.MessageType == message.REQ_PACKETS_TYPE_BOOT { //vobc 上电,清空应答器 can_btm.ClearBalise(train) } else { var ( btmState *state_proto.BTMState dsn byte matcher bool ) ms := req.MessageSerial if req.FrameStatus == message.REQ_FRAME_STATUS_ERROR { //抢答器错误,重新发送 btmState, dsn, _, _ = can_btm.FindBaliseResend(train.BtmBaliseCacheA, false) } else { //判断 报文序列号与之前发送的 是否一致,不一致 //如果一致返回新的应答器,如果不一致返回之前发送的应答器,如果不一致并且没有找到之前发送的应答器,则返回新应答器 btmState, dsn, matcher = can_btm.FindBaliseByMessageSerial(train.BtmBaliseCacheA, false, req.MessageSerial) if matcher { if btmState != nil { //正常应答 btmState.IsSend = true ms = req.MessageSerial + 1 if ms > 255 { ms = 1 } } } else { //vobc 未收到应答器,重新发送 } } b.packets(requestId, btmState, ms, dsn, req.VobcLifeNum, receiveTime) } } // 有应答器报文 func (b *BtmVobcClient) balisePackets(telegram, requestId string, messageSerial, dsn byte, vobcLifeNum uint32, receiveTime int64) { data, e := hex.DecodeString(telegram) if e != nil { slog.Error(fmt.Sprintf("解析应答器报文失败应答器报文长度:%v", telegram), e) return } //前沿时间 var fttl uint16 = 0 tmpFttl := int64(math.Abs(float64(time.Now().UnixMilli() - receiveTime))) if tmpFttl >= 0xffff { fttl = 0xffff } else { fttl = uint16(tmpFttl) } var bttl uint16 = 0 tmpBttl := int64(math.Abs(float64(time.Now().UnixMilli() - receiveTime))) if tmpBttl >= 0xffff { bttl = 0xffff } else { bttl = uint16(tmpBttl) } repTimeMicro := (time.Now().UnixMilli() - receiveTime) / 10 baliseMsg := &message.BtmVobcMessage{FontTtl: fttl, BtmStatus: message.BTM_STSTUS_NORMAL, DecodeTime: uint16(repTimeMicro), BackTtl: bttl, ResponseTime: byte(repTimeMicro), MsgSerial: messageSerial, BtmMsg: data, VobcLifeNum: vobcLifeNum, BaseBtmVobc: message.BaseBtmVobc{AutoIdFrame: dsn}} baliseData := baliseMsg.Encode() slog.Info(fmt.Sprintf("发送btm vobc 报文数据 报文序列id:%v 报文内容:%X 长度:%v ,requestId:%v", messageSerial, baliseData, len(baliseData), requestId)) err := b.client.Send(baliseData) if err != nil { slog.Error(fmt.Sprintf("发送btm vobc 报文数据 报文序列id:%v 报文内容:%X 长度:%v ,requestId:%v", messageSerial, baliseData, len(baliseData), requestId), err) return } return } // 无应答器报文 func (b *BtmVobcClient) balisePacketsFree(requestId string, receiveTime int64, vobcLifeNum uint32, autoCommandId, messageSerial byte) { repTimeMicro := (time.Now().UnixMicro() - receiveTime) / 10 freeMsg := &message.BtmVobcMsgFree{BtmStatus: message.BTM_STSTUS_NORMAL, Fun1: 0xffff, Fun2: 0x00CF, Fun3: uint16(0), Fun4: uint16(0), FreeMsg: createFreeBalisePackets(), RespTime: byte(repTimeMicro), MsgSerial: messageSerial, VobcLifeNum: vobcLifeNum, BaseBtmVobc: message.BaseBtmVobc{AutoIdFrame: autoCommandId}} freeData := freeMsg.Encode() logStr := fmt.Sprintf("发送btm vobc 报文序列id:%v 空闲帧报文:%X 长度:%v ,requestId:%v", messageSerial, freeData, len(freeData), requestId) slog.Info(logStr) err := b.client.Send(freeData) if err != nil { slog.Error(logStr, err) return } } // 应答器报文或空报文 func (b *BtmVobcClient) packets(requestId string, btmState *state_proto.BTMState, messageSerial, dsn byte, vobcLifeNum uint32, receiveTime int64) { if btmState == nil { b.balisePacketsFree(requestId, receiveTime, vobcLifeNum, dsn, messageSerial) } else { b.balisePackets(btmState.Telegram, requestId, messageSerial, dsn, vobcLifeNum, receiveTime) } } func (b *BtmVobcClient) SendData(data []byte) { if b.client != nil { slog.Info(fmt.Sprintf("发送btm vobc 报文:%v 长度:%v", hex.EncodeToString(data), len(data))) err := b.client.Send(data) if err != nil { slog.Error("发送btm vobc 报文失败:", err) return } } } func (b *BtmVobcClient) Stop() { if b.server != nil { b.calFun() b.server.Close() } if b.client != nil { b.client.Close() } }