package train_pc_sim import ( "context" "encoding/hex" "fmt" "joylink.club/bj-rtsts-server/config" "joylink.club/bj-rtsts-server/dto/state_proto" "joylink.club/bj-rtsts-server/third_party/message" "joylink.club/bj-rtsts-server/third_party/tcp" "joylink.club/bj-rtsts-server/third_party/tpapi" "log/slog" "sync" "time" ) type TrainControlEvent struct { Command byte Status byte } //var FireTrainControlEventType = ecs.NewEventType[TrainControlEvent]() type TrainPcSim interface { tpapi.ThirdPartyApiService Start(pcSimManage TrainPcSimManage) Stop() // SendDriverActive 发送驾驶端激活 SendDriverActive(train *state_proto.TrainState) // SendHandleSwitch 发送牵引制动手柄 SendHandleSwitch(oldTraction, oldBrakeForce int64, tractionState bool, train *state_proto.TrainState) // SendTrainDirection 列车运行方向 //因文档说明不清楚,在调用的时候目前是注释状态,现场调试可能会用到 SendTrainDirection(train *state_proto.TrainState, trainForward, trainBackward bool) //发送应答器信息数据 SendBaliseData(train *state_proto.TrainState, msgType uint16, data []byte) //发布列车控制的相关事件 PublishTrainControlEvent(train *state_proto.TrainState, events []TrainControlEvent) // CreateOrRemoveSpeedPLace 创建或删除速度位置信息 CreateOrRemoveSpeedPLace(train *state_proto.TrainState) // CreateOrRemoveTrain 创建或删除列车 CreateOrRemoveTrain(train *state_proto.TrainState, msgType byte, data []byte) error } type TrainPcSimManage interface { GetTrainPcSimConfig() []config.VehiclePCSimConfig //GetConnTrain() *state_proto.TrainState GetConnTrain2() []*state_proto.TrainState // TrainPcSimDigitalOutInfoHandle 4.4.1. 车载输出数字量信息报文内容 TrainPcSimDigitalOutInfoHandle(connType state_proto.TrainConnState_TrainConnType, data []byte) // TrainPcSimDigitalReportHandle 4.4.2. 车载输出数字反馈量信息报文内容 TrainPcSimDigitalReportHandle(connType state_proto.TrainConnState_TrainConnType, data []byte) // TrainPcSimMockInfo 门模式 //TrainDoorModeHandle(state byte) //处理列车pc仿真模拟量数据 TrainPcSimMockInfo(connType state_proto.TrainConnState_TrainConnType, data []byte) // TrainBtmQuery 处理列车btm查询 TrainBtmQuery(connType state_proto.TrainConnState_TrainConnType, data []byte) } type trainPcReciverData struct { clientKey string tcpClient *tcp.TcpClient pcSimManage TrainPcSimManage } func (rd *trainPcReciverData) reciverDataHandle(n int, data []byte) { connType := state_proto.TrainConnState_PC_SIM_A if rd.clientKey == "B" { connType = state_proto.TrainConnState_PC_SIM_B } baseMsg := &message.TrainPcSimBaseMessage{} err := baseMsg.Decode(data) if err != nil { slog.Error("车载pc仿真接受数据解析失败 ") return } switch baseMsg.Type { //case RECIVE_TRAIN_CREATE_REMOVE: // pc.trainPcSimManage.TrainPcSimConnOrRemoveHandle(baseMsg.Data[0]) case RECIVE_TRAIN_INTERFACE_CABINET_OUTR: rd.pcSimManage.TrainPcSimDigitalOutInfoHandle(connType, baseMsg.Data) case RECIVE_TRAIN_INTERFACE_CABINET_OUTR_BACK: rd.pcSimManage.TrainPcSimDigitalReportHandle(connType, baseMsg.Data) case RECIVE_TRAIN_QUERY_STATUS: rd.pcSimManage.TrainBtmQuery(connType, baseMsg.Data) case RECIVE_TRAIN_MOCK_DATA: rd.pcSimManage.TrainPcSimMockInfo(connType, baseMsg.Data) //case RECIVE_TRAIN_DOOR_MODE: // pc.trainPcSimManage.TrainDoorModeHandle(baseMsg.Data[0]) } } const Name = "车载pc仿真" const CLIENT_KEY = "clientKey" func FindTrainPcSimClientKey(t *state_proto.TrainState) string { if t.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_A { return "A" } else if t.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_B { return "B" } return "" } func (d *trainPcSimService) Name() string { return Name } func (d *trainPcSimService) State() tpapi.ThirdPartyApiServiceState { return d.state } func (d *trainPcSimService) updateState(state tpapi.ThirdPartyApiServiceState) { d.state = state } var ( initLock = &sync.Mutex{} singleObj *trainPcSimService ) func Default() TrainPcSim { defer initLock.Unlock() initLock.Lock() if singleObj == nil { singleObj = &trainPcSimService{} } return singleObj } type trainPcSimService struct { state tpapi.ThirdPartyApiServiceState //pcSimClient *tcp.TcpClient //pcSimClientMap map[string]*tcp.TcpClient newPcSimclientMap map[string]*trainPcReciverData cancleContext context.CancelFunc trainPcSimManage TrainPcSimManage speedPlace *message.TrainSpeedPlaceReportMsg configs []config.VehiclePCSimConfig } // 接受来自pc仿真的消息 func (d *trainPcSimService) readError(err error) { slog.Error("连接车载pc仿真tcp服务断开", err) d.updateState(tpapi.ThirdPartyState_Broken) } func (d *trainPcSimService) newCloseAllConn() { for _, rd := range d.newPcSimclientMap { if rd != nil { rd.tcpClient.Close() } } } /* func (d *trainPcSimService) closeAllConn() { for key, client := range d.pcSimClientMap { if client != nil { client.Close() } delete(d.pcSimClientMap, key) } } */ func (d *trainPcSimService) newCloseConn(clientKey string) { rd := d.newPcSimclientMap[clientKey] if rd != nil { rd.tcpClient.Close() } } /* func (d *trainPcSimService) closeConn(clientKey string) { if d.pcSimClientMap[clientKey] != nil { d.pcSimClientMap[clientKey].Close() delete(d.pcSimClientMap, clientKey) } } */ func (d *trainPcSimService) findConfig(tcChar string) (*config.VehiclePCSimConfig, error) { configFlag := false if tcChar == "A" { configFlag = true } else if tcChar == "B" { configFlag = false } else { return nil, fmt.Errorf("") } for _, config := range d.configs { if config.Open && config.TrainEnds == configFlag { return &config, nil } } return nil, fmt.Errorf("") } func (d *trainPcSimService) connTrainPcSim(ctx context.Context) { go func() { for { select { case <-ctx.Done(): //d.closeAllConn() d.newCloseAllConn() return default: } trains := d.trainPcSimManage.GetConnTrain2() if len(trains) > 0 { for _, t := range trains { clientKey := FindTrainPcSimClientKey(t) if clientKey == "" { slog.Error("未找到对应的pc仿真连接,trainId:", t.Id, "删除对应客户端") //d.closeConn(clientKey) d.newCloseConn(clientKey) continue } //client := d.pcSimClientMap[clientKey] rd := d.newPcSimclientMap[clientKey] if rd == nil { d.newPcSimclientMap[clientKey] = &trainPcReciverData{pcSimManage: d.trainPcSimManage, clientKey: clientKey, tcpClient: &tcp.TcpClient{}} } if !rd.tcpClient.IsConning() { //client.Close() d.newCloseConn(clientKey) d.initConn(clientKey) } } } time.Sleep(time.Second) } }() } func (d *trainPcSimService) initConn(clientKey string) { rd := d.newPcSimclientMap[clientKey] if rd == nil { rd = &trainPcReciverData{pcSimManage: d.trainPcSimManage, clientKey: clientKey, tcpClient: &tcp.TcpClient{}} d.newPcSimclientMap[clientKey] = rd } cfg, _ := d.findConfig(clientKey) addr := fmt.Sprintf("%v:%v", cfg.PcSimIp, cfg.PcSimPort) client2, err := tcp.StartTcpClient(addr, rd.reciverDataHandle, d.readError) if err != nil { slog.Error("车载pc连接失败 clientKey:", clientKey, "error:", err.Error()) d.updateState(tpapi.ThirdPartyState_Broken) } else { rd.tcpClient = client2 } /*if d.pcSimClientMap[clientKey] != nil { return } cfg, _ := d.findConfig(clientKey) addr := fmt.Sprintf("%v:%v", cfg.PcSimIp, cfg.PcSimPort) properties := map[string]interface{}{ CLIENT_KEY: clientKey, } client2, err := tcp.StartTcpClient(addr, properties, d.reivceData, d.readError) if err != nil { slog.Error("车载pc连接失败 clientKey:", clientKey, "error:", err.Error()) d.updateState(tpapi.ThirdPartyState_Broken) } else { d.pcSimClientMap[clientKey] = client2 }*/ } func (d *trainPcSimService) Start(pcSimManage TrainPcSimManage) { configs := pcSimManage.GetTrainPcSimConfig() d.newPcSimclientMap = make(map[string]*trainPcReciverData) //d.pcSimClientMap = map[string]*tcp.TcpClient{} if len(configs) <= 0 { slog.Info("车载pc仿真配置未开启") return } allClosed := true for _, c := range configs { if !c.Open { allClosed = false } } if !allClosed { slog.Info("车载pc仿真配置未开启") return } d.configs = configs ctx, ctxFun := context.WithCancel(context.Background()) d.cancleContext = ctxFun d.trainPcSimManage = pcSimManage d.connTrainPcSim(ctx) //FireTrainControlEventType.Subscribe(wd, d.trainControlEventHandle) d.updateState(tpapi.ThirdPartyState_Normal) go d.sendTrainLocationAndSpeedTask(ctx) } func (d *trainPcSimService) Stop() { d.updateState(tpapi.ThirdPartyState_Closed) if d.cancleContext != nil { d.cancleContext() d.cancleContext = nil } //d.closeAllConn() d.newCloseAllConn() } func (d *trainPcSimService) CreateOrRemoveSpeedPLace(train *state_proto.TrainState) { if train.ConnState.Conn && (train.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_A || train.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_B) { train.PluseCount = &state_proto.SensorSpeedPulseCount{} d.speedPlace = &message.TrainSpeedPlaceReportMsg{TrainId: train.Id} } else { train.PluseCount = nil d.speedPlace = nil } } func (d *trainPcSimService) CreateOrRemoveTrain(train *state_proto.TrainState, msgType byte, data []byte) error { clientKey := FindTrainPcSimClientKey(train) if msgType == RECIVE_TRAIN_CREATE_REMOVE && data[0] == 0x01 { d.initConn(clientKey) } msg := &message.TrainPcSimBaseMessage{Data: data, Type: uint16(msgType)} rd := d.newPcSimclientMap[clientKey] if rd != nil { err := rd.tcpClient.Send(msg.Encode()) if err != nil { return err } if data[0] != 0x01 { d.newCloseConn(clientKey) } } /*client := d.pcSimClientMap[clientKey] err := client.Send(msg.Encode()) if data[0] != 0x01 { d.closeConn(clientKey) }*/ return nil } // 依据文档80ms发送列车速度位置 func (d *trainPcSimService) sendTrainLocationAndSpeedTask(ctx context.Context) { for { select { case <-ctx.Done(): return default: } trains := d.trainPcSimManage.GetConnTrain2() for _, train := range trains { if train.ConnState.Conn && train.PluseCount != nil { clientKey := FindTrainPcSimClientKey(train) rd := d.newPcSimclientMap[clientKey] s1, s2 := train.PluseCount.PulseCount1, train.PluseCount.PulseCount2 d.speedPlace.ParsePulseCount1(s1, s2) data := d.speedPlace.Encode(train.TrainRunUp, s1, s2) bm := &message.TrainPcSimBaseMessage{Type: SENDER_TRAIN_LOCATION_INFO, Data: data} train.PluseCount.PulseCount1 = 0 train.PluseCount.PulseCount2 = 0 rd.tcpClient.Send(bm.Encode()) /*client := d.pcSimClientMap[clientKey] s1, s2 := train.PluseCount.PulseCount1, train.PluseCount.PulseCount2 d.speedPlace.ParsePulseCount1(s1, s2) data := d.speedPlace.Encode(train.TrainRunUp, s1, s2) bm := &message.TrainPcSimBaseMessage{Type: SENDER_TRAIN_LOCATION_INFO, Data: data} train.PluseCount.PulseCount1 = 0 train.PluseCount.PulseCount2 = 0 client.Send(bm.Encode())*/ } } time.Sleep(time.Millisecond * 80) } } // 发送驾驶激活 func (d *trainPcSimService) SendDriverActive(train *state_proto.TrainState) { vobc := train.VobcState clientKey := FindTrainPcSimClientKey(train) //client := d.pcSimClientMap[clientKey] rd := d.newPcSimclientMap[clientKey] defulatBuf := make([]byte, 0) msg := &message.TrainPcSimBaseMessage{Data: defulatBuf} if train.TrainRunUp { if vobc.Tc1Active { msg.Type = SENDER_TRAIN_TC_ACTIVE } else if vobc.Tc1Active == false { msg.Type = SENDER_TRAIN_TC_NOT_ACTIVE } } else if !train.TrainRunUp { if vobc.Tc2Active { msg.Type = SENDER_TRAIN_TC_ACTIVE } else if vobc.Tc2Active == false { msg.Type = SENDER_TRAIN_TC_NOT_ACTIVE } } da := msg.Encode() slog.Info("发送驾驶激活 列车", train.Id, "数据", hex.EncodeToString(da)) err := rd.tcpClient.Send(da) //err := client.Send(da) if err != nil { slog.Error("发送驾驶激活 列车", train.Id, "数据", hex.EncodeToString(da), err) } } func (d *trainPcSimService) SendHandleSwitch(oldTraction, oldBrakeForce int64, tractionState bool, train *state_proto.TrainState) { tc := train.ConnState if tc.Conn { vobc := train.VobcState clientKey := FindTrainPcSimClientKey(train) rd := d.newPcSimclientMap[clientKey] //client := d.pcSimClientMap[clientKey] msg := &message.TrainPcSimBaseMessage{} newTraction := vobc.TractionForce newBrake := -vobc.BrakeForce newOldBrakeForce := -oldBrakeForce if tractionState { if newTraction <= oldTraction && newTraction == 0 { //手柄取消前进 msg.Type = RECIVE_TRAIN_HAND_KEY_CANCLE_FORWARD } else if newTraction > oldTraction { //手柄前进 msg.Type = SENDER_TRAIN_HAND_KEY_FORWARD } else { //手柄前进 msg.Type = SENDER_TRAIN_HAND_KEY_FORWARD } } else { if newBrake >= newOldBrakeForce && newBrake == 0 { //手柄取消后退 msg.Type = RECIVE_TRAIN_HAND_KEY_CACLE_BACKWARD } else if newBrake < newOldBrakeForce { //手柄后退 msg.Type = RECIVE_TRAIN_HAND_KEY_BACKWARD } else { //手柄后退 msg.Type = RECIVE_TRAIN_HAND_KEY_BACKWARD } } da := msg.Encode() slog.Info("发送列车手柄消息", "clientKey", clientKey, "msg", hex.EncodeToString(da)) err := rd.tcpClient.Send(da) //err := client.Send(da) if err != nil { slog.Error("发送列车手柄消息失败", "clientKey", clientKey, "msg", hex.EncodeToString(da)) } } } func (d *trainPcSimService) SendTrainDirection(train *state_proto.TrainState, trainForward, trainBackward bool) { baseMsgs := make([]*message.TrainPcSimBaseMessage, 0) if !trainForward && !trainBackward { baseMsgs = append(baseMsgs, &message.TrainPcSimBaseMessage{Type: RECIVE_TRAIN_HAND_KEY_CANCLE_FORWARD}) baseMsgs = append(baseMsgs, &message.TrainPcSimBaseMessage{Type: RECIVE_TRAIN_HAND_KEY_CACLE_BACKWARD}) } else if trainForward { baseMsgs = append(baseMsgs, &message.TrainPcSimBaseMessage{Type: SENDER_TRAIN_HAND_KEY_FORWARD}) } else if trainBackward { baseMsgs = append(baseMsgs, &message.TrainPcSimBaseMessage{Type: RECIVE_TRAIN_HAND_KEY_BACKWARD}) } clientKey := FindTrainPcSimClientKey(train) rd := d.newPcSimclientMap[clientKey] //client := d.pcSimClientMap[clientKey] for _, msg := range baseMsgs { da := msg.Encode() slog.Info("发送列车方向列车", train.Id, "数据", hex.EncodeToString(da)) err := rd.tcpClient.Send(da) //err := client.Send(da) if err != nil { slog.Error("发送列车方向失败列车", train.Id, "数据", hex.EncodeToString(da)) } } } func (d *trainPcSimService) SendBaliseData(train *state_proto.TrainState, msgType uint16, data []byte) { msg := &message.TrainPcSimBaseMessage{} msg.Type = msgType msg.Data = data clientKey := FindTrainPcSimClientKey(train) rd := d.newPcSimclientMap[clientKey] //client := d.pcSimClientMap[clientKey] da := msg.Encode() slog.Info("发送列车PC仿真应答器信息,数据", hex.EncodeToString(da)) err := rd.tcpClient.Send(da) if err != nil { slog.Info("发送列车PC仿真应答器信息失败,数据", hex.EncodeToString(da)) } } /* func (d *trainPcSimService) trainControlEventHandle( event TrainControlEvent) { msg := &message.TrainPcSimBaseMessage{} msg.Type = SENDER_TRAIN_OUTR_INFO data := []byte{event.Command, event.Status} msg.Data = data d.pcSimClient.Send(msg.Encode()) } */ func (d *trainPcSimService) PublishTrainControlEvent(train *state_proto.TrainState, events []TrainControlEvent) { if len(events) <= 0 { slog.Warn("发布事件数量为空") return } clientKey := FindTrainPcSimClientKey(train) rd := d.newPcSimclientMap[clientKey] //client := d.pcSimClientMap[clientKey] for _, event := range events { msg := &message.TrainPcSimBaseMessage{} msg.Type = SENDER_TRAIN_OUTR_INFO data := []byte{event.Command, event.Status} msg.Data = data rd.tcpClient.Send(msg.Encode()) //client.Send(msg.Encode()) //FireTrainControlEventType.Publish(world, &event) } } // 接受来自pc仿真的消息 /*func (d *trainPcSimService) reivceData(len int, data []byte, properties map[string]interface{}) { clientKey := properties[CLIENT_KEY] ck := fmt.Sprintf("%v", clientKey) if d.pcSimClientMap[ck] == nil { slog.Error(fmt.Sprintf("不存在%v的客户端,数据解析不予处理", ck)) return } connType := state_proto.TrainConnState_PC_SIM_A if clientKey == "B" { connType = state_proto.TrainConnState_PC_SIM_B } baseMsg := &message.TrainPcSimBaseMessage{} err := baseMsg.Decode(data) if err != nil { slog.Error("车载pc仿真接受数据解析失败 ") return } switch baseMsg.Type { //case RECIVE_TRAIN_CREATE_REMOVE: // pc.trainPcSimManage.TrainPcSimConnOrRemoveHandle(baseMsg.Data[0]) case RECIVE_TRAIN_INTERFACE_CABINET_OUTR: d.trainPcSimManage.TrainPcSimDigitalOutInfoHandle(connType, baseMsg.Data) case RECIVE_TRAIN_INTERFACE_CABINET_OUTR_BACK: d.trainPcSimManage.TrainPcSimDigitalReportHandle(connType, baseMsg.Data) case RECIVE_TRAIN_QUERY_STATUS: d.trainPcSimManage.TrainBtmQuery(connType, baseMsg.Data) case RECIVE_TRAIN_MOCK_DATA: d.trainPcSimManage.TrainPcSimMockInfo(connType, baseMsg.Data) //case RECIVE_TRAIN_DOOR_MODE: // pc.trainPcSimManage.TrainDoorModeHandle(baseMsg.Data[0]) } }*/