package train_pc_sim import ( "context" "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" "joylink.club/ecs" "log/slog" "strconv" "sync" "time" ) type TrainControlEvent struct { Command byte Status byte } var FireTrainControlEventType = ecs.NewEventType[TrainControlEvent]() type TrainPcSim interface { Start(wd ecs.World, pcSimManage TrainPcSimManage) Stop() //发送驾驶端激活 SendDriverActive(tc *state_proto.TrainConnState, vobc *state_proto.TrainVobcState) //发送牵引制动手柄 SendHandleSwitch(oldTraction, oldBrakeForce int64, tractionState bool, tc *state_proto.TrainConnState, vobc *state_proto.TrainVobcState) //列车运行方向 //因文档说明不清楚,在调用的时候目前是注释状态,现场调试可能会用到 SendTrainDirection(trainForward, trainBackward bool) //发送应答器信息数据 SendBaliseData(msgType uint16, data []byte) //发布列车控制的相关事件 PublishTrainControlEvent(world ecs.World, events []TrainControlEvent) CreateOrRemoveSpeedPLace(train *state_proto.TrainState) CreateOrRemoveTrain(msgType byte, data []byte) error tpapi.ThirdPartyApiService } type TrainPcSimManage interface { GetTrainPcSimConfig() config.VehiclePCSimConfig GetConnTrain() *state_proto.TrainState //4.4.1. 车载输出数字量信息报文内容 TrainPcSimDigitalOutInfoHandle(data []byte) //4.4.2. 车载输出数字反馈量信息报文内容 TrainPcSimDigitalReportHandle(data []byte) //门模式 //TrainDoorModeHandle(state byte) //处理列车pc仿真模拟量数据 TrainPcSimMockInfo(data []byte) //处理列车btm查询 TrainBtmQuery(data []byte) } const Name = "车载pc仿真" 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 cancleContext context.CancelFunc trainPcSimManage TrainPcSimManage speedPlace *message.TrainSpeedPlaceReportMsg config config.VehiclePCSimConfig } // 接受来自pc仿真的消息 func (d *trainPcSimService) readError(err error) { slog.Error("连接车载pc仿真tcp服务断开", err) d.updateState(tpapi.ThirdPartyState_Broken) d.pcSimClient.Close() d.connTrainPcSim() } func (d *trainPcSimService) connTrainPcSim() { reconnIndex := 0 ctx, ctxFun := context.WithCancel(context.Background()) go func() { for { select { case <-ctx.Done(): return default: } client, err := tcp.StartTcpClient(fmt.Sprintf("%v:%v", d.config.PcSimIp, d.config.PcSimPort), d.reivceData, d.readError) if err != nil { reconnIndex++ slog.Error("连接车载pc平台失败,尝试=", strconv.Itoa(reconnIndex), err) d.updateState(tpapi.ThirdPartyState_Broken) } else { d.pcSimClient = client ctxFun() return } time.Sleep(time.Second) } }() } func (d *trainPcSimService) Start(wd ecs.World, pcSimManage TrainPcSimManage) { config := pcSimManage.GetTrainPcSimConfig() if config.PcSimIp == "" || !config.Open { slog.Info("车载pc仿真配置未开启") return } d.config = config d.connTrainPcSim() ctx, ctxFun := context.WithCancel(context.Background()) d.cancleContext = ctxFun d.trainPcSimManage = pcSimManage //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 } if d.pcSimClient != nil { //d.pcSimClient. d.pcSimClient.Close() } } func (d *trainPcSimService) CreateOrRemoveSpeedPLace(train *state_proto.TrainState) { if train.ConnState.Conn { train.PluseCount = &state_proto.SensorSpeedPulseCount{} d.speedPlace = &message.TrainSpeedPlaceReportMsg{TrainId: train.Id} } else { train.PluseCount = nil d.speedPlace = nil } } func (d *trainPcSimService) CreateOrRemoveTrain(msgType byte, data []byte) error { msg := &message.TrainPcSimBaseMessage{Data: data, Type: uint16(msgType)} return d.pcSimClient.Send(msg.Encode()) } // 依据文档80ms发送列车速度位置 func (d *trainPcSimService) sendTrainLocationAndSpeedTask(ctx context.Context) { for { select { case <-ctx.Done(): return default: } train := d.trainPcSimManage.GetConnTrain() if train != nil && train.ConnState.Conn && train.PluseCount != nil { s1, s2 := train.PluseCount.PulseCount1, train.PluseCount.PulseCount2 d.speedPlace.ParsePulseCount1(s1, s2) data := d.speedPlace.Encode(train.RunDirection, s1, s2) bm := &message.TrainPcSimBaseMessage{Type: SENDER_TRAIN_LOCATION_INFO, Data: data} train.PluseCount.PulseCount1 = 0 train.PluseCount.PulseCount2 = 0 d.pcSimClient.Send(bm.Encode()) } time.Sleep(time.Millisecond * 80) } } // 发送驾驶激活 func (d *trainPcSimService) SendDriverActive(tc *state_proto.TrainConnState, vobc *state_proto.TrainVobcState) { if tc.Conn && tc.ConnType == state_proto.TrainConnState_PC_SIM { defulatBuf := make([]byte, 0) msg := &message.TrainPcSimBaseMessage{Data: defulatBuf} if vobc.Tc1Active || vobc.Tc2Active { msg.Type = SENDER_TRAIN_TC_ACTIVE } else if vobc.Tc1Active == false && vobc.Tc2Active == false { msg.Type = SENDER_TRAIN_TC_NOT_ACTIVE } d.pcSimClient.Send(msg.Encode()) } } func (d *trainPcSimService) SendHandleSwitch(oldTraction, oldBrakeForce int64, tractionState bool, tc *state_proto.TrainConnState, vobc *state_proto.TrainVobcState) { if tc.Conn && tc.ConnType == state_proto.TrainConnState_PC_SIM { 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 } } d.pcSimClient.Send(msg.Encode()) } } func (d *trainPcSimService) SendTrainDirection(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}) } for _, msg := range baseMsgs { d.pcSimClient.Send(msg.Encode()) } } func (d *trainPcSimService) SendBaliseData(msgType uint16, data []byte) { msg := &message.TrainPcSimBaseMessage{} msg.Type = msgType msg.Data = data //fmt.Println(fmt.Sprintf("%X", msg.Encode())) d.pcSimClient.Send(msg.Encode()) } func (d *trainPcSimService) trainControlEventHandle(w ecs.World, 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(world ecs.World, events []TrainControlEvent) { if len(events) <= 0 { slog.Warn("发布事件数量为空") return } for _, event := range events { msg := &message.TrainPcSimBaseMessage{} msg.Type = SENDER_TRAIN_OUTR_INFO data := []byte{event.Command, event.Status} msg.Data = data d.pcSimClient.Send(msg.Encode()) //FireTrainControlEventType.Publish(world, &event) } } // 接受来自pc仿真的消息 func (d *trainPcSimService) reivceData(len int, data []byte) { 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(baseMsg.Data) case RECIVE_TRAIN_INTERFACE_CABINET_OUTR_BACK: d.trainPcSimManage.TrainPcSimDigitalReportHandle(baseMsg.Data) case RECIVE_TRAIN_QUERY_STATUS: d.trainPcSimManage.TrainBtmQuery(baseMsg.Data) case RECIVE_TRAIN_MOCK_DATA: d.trainPcSimManage.TrainPcSimMockInfo(baseMsg.Data) //case RECIVE_TRAIN_DOOR_MODE: // pc.trainPcSimManage.TrainDoorModeHandle(baseMsg.Data[0]) } }