package message_server import ( "fmt" "strings" "time" "google.golang.org/protobuf/proto" "joylink.club/bj-rtsts-server/message_server/ms_api" "joylink.club/bj-rtsts-server/ts/protos/graphicData" "joylink.club/bj-rtsts-server/ts/protos/state" "joylink.club/bj-rtsts-server/ts/simulation/wayside/memory" "joylink.club/ecs" "joylink.club/rtsssimulation/component" "joylink.club/rtsssimulation/entity" ) // 信号平面布置图消息服务 type SfpMs struct { vs *memory.VerifySimulation mapId int32 channel string } func NewSfpMs(vs *memory.VerifySimulation, mapId int32) *SfpMs { return &SfpMs{ vs: vs, mapId: mapId, channel: fmt.Sprintf(SimulationSfpTopic, vs.SimulationId, mapId), } } // 获取通道名 func (ms *SfpMs) GetChannel() string { return ms.channel } // 发送消息间隔时间,单位ms func (ms *SfpMs) GetInterval() time.Duration { return 200 * time.Millisecond } // 定时发送的消息 func (ms *SfpMs) OnTick() ([]*ms_api.TopicMsg, error) { turnoutStates, err := ms.collectTurnoutStates() if err != nil { return nil, err } trainStates, err := ms.collectTrainStates() if err != nil { return nil, err } signalStates, err := ms.collectSignalStates() if err != nil { return nil, err } buttonStates, err := ms.collectStationButtonStates() if err != nil { return nil, err } psdStates, err := ms.collectPsdStates() if err != nil { return nil, err } sectionStates, err := ms.collectSectionStates() if err != nil { return nil, err } platformStates, err := ms.collectPlatformStates() if err != nil { return nil, err } ststes := &state.PushedDevicesStatus{ All: true, AllStatus: &state.AllDevicesStatus{ SwitchState: turnoutStates, TrainState: trainStates, SignalState: signalStates, ButtonState: buttonStates, PsdState: psdStates, SectionState: sectionStates, PlatformState: platformStates, }, } b, err := proto.Marshal(ststes) if err != nil { return nil, fmt.Errorf("信号布置图设备状态消息服务数据序列化失败, %s", err) } return []*ms_api.TopicMsg{ms_api.NewTopicMsg(ms.channel, b)}, nil } func (ms *SfpMs) OnError(err error) { // TODO: 错误处理 } // 收集屏蔽门状态 func (ms *SfpMs) collectPsdStates() ([]*state.PsdState, error) { world := ms.vs.World uidStructure := memory.QueryUidStructure[*memory.StationUidStructure](ms.mapId) data := memory.QueryGiData[*graphicData.RtssGraphicStorage](ms.mapId) var psdStateArr []*state.PsdState for _, door := range data.ScreenDoors { uid := uidStructure.PsdIds[door.Common.Id].Uid psdEntry, ok := entity.GetEntityByUid(world, uid) if ok { list := component.AsdListType.Get(psdEntry).List asdStates := make([]*state.AsdState, len(list)) for i, asdEntry := range list { asdState := component.AsdStateType.Get(asdEntry) asdStates[i] = &state.AsdState{ Code: int32(i + 1), Kmdw: asdState.Kmdw, Gmdw: asdState.Gmdw, Mgj: asdState.Mgj, } } psdStateArr = append(psdStateArr, &state.PsdState{ Id: door.Common.Id, AsdStates: asdStates, Mgj: component.PsdStateType.Get(psdEntry).Close, }) } } return psdStateArr, nil } // 收集区段状态 func (ms *SfpMs) collectSectionStates() ([]*state.SectionState, error) { uidMap := memory.QueryMapUidMapByType(ms.mapId, &graphicData.Section{}) var sectionArr []*state.SectionState for _, u := range uidMap { s := handlerSectionState(ms.vs.World, u.Uid) if s == nil { continue } s.Id = u.CommonId sectionArr = append(sectionArr, s) } return sectionArr, nil } func handlerSectionState(w ecs.World, uid string) *state.SectionState { entry, ok := entity.GetEntityByUid(w, uid) if !ok { //fmt.Printf("id=%s的信号机不存在", uid) return nil } if entry.HasComponent(component.PhysicalSectionStateType) { //计轴区段 sectionState := &state.SectionState{} axleState := component.PhysicalSectionStateType.Get(entry) sectionState.Occupied = axleState.Occ sectionState.AxleFault = entry.HasComponent(component.AxleSectionFaultTag) return sectionState } return nil } // 收集车站按钮状态 func (ms *SfpMs) collectStationButtonStates() ([]*state.ButtonState, error) { // 获取地图上的按钮状态 uidMap := memory.QueryMapUidMapByType(ms.mapId, &graphicData.EsbButton{}) var btnStateArr []*state.ButtonState for _, u := range uidMap { entry, ok := entity.GetEntityByUid(ms.vs.World, u.Uid) if !ok { return nil, fmt.Errorf("ESB按钮实体不存在: World id=%d, uid=%s", ms.vs.World.Id(), u.Uid) } if entry.HasComponent(component.ButtonTag) { // 按钮 bit := component.BitStateType.Get(entry) btnStateArr = append(btnStateArr, &state.ButtonState{Id: u.CommonId, Down: bit.Val}) } } return btnStateArr, nil } // 收集信号机状态 func (ms *SfpMs) collectSignalStates() ([]*state.SignalState, error) { uidMap := memory.QueryMapUidMapByType(ms.mapId, &graphicData.Signal{}) var signalArr []*state.SignalState for _, u := range uidMap { s, err := handlerSignalState(ms.vs.World, u.Uid) if err != nil { return nil, err } s.Id = u.CommonId signalArr = append(signalArr, s) } return signalArr, nil } func handlerSignalState(w ecs.World, uid string) (*state.SignalState, error) { entry, ok := entity.GetEntityByUid(w, uid) if !ok { return nil, fmt.Errorf("信号机不存在: World id=%d, 信号机id=%s", w.Id(), uid) } if !entry.HasComponent(component.SignalLightsType) { //信号机灯列表 return nil, fmt.Errorf("信号机没有SignalLights组件: World id=%d, 信号机id=%s", w.Id(), uid) } signalState := &state.SignalState{} lights := component.SignalLightsType.Get(entry) isL := false isH := false isU := false isA := false isB := false for _, light := range lights.Lights { switch { case light.HasComponent(component.LdTag): isL = component.BitStateType.Get(light).Val case light.HasComponent(component.HdTag): isH = component.BitStateType.Get(light).Val case light.HasComponent(component.UdTag): isU = component.BitStateType.Get(light).Val case light.HasComponent(component.BdTag): isB = component.BitStateType.Get(light).Val case light.HasComponent(component.AdTag): isA = component.BitStateType.Get(light).Val } } if isH && isU { signalState.Aspect = state.Signal_HU } else { switch { case isL: signalState.Aspect = state.Signal_L case isH: signalState.Aspect = state.Signal_H case isU: signalState.Aspect = state.Signal_U case isB: signalState.Aspect = state.Signal_B case isA: signalState.Aspect = state.Signal_A } } return signalState, nil } // 收集列车状态 func (ms *SfpMs) collectTrainStates() ([]*state.TrainState, error) { allTrainMap := &ms.vs.Memory.Status.TrainStateMap var trainArr []*state.TrainState allTrainMap.Range(func(_, v any) bool { trainArr = append(trainArr, v.(*state.TrainState)) return true }) return trainArr, nil } // 收集道岔状态 func (ms *SfpMs) collectTurnoutStates() ([]*state.SwitchState, error) { sim := ms.vs uidMap := memory.QueryMapUidMapByType(ms.mapId, &graphicData.Turnout{}) wd := entity.GetWorldData(sim.World) var switchArr []*state.SwitchState for _, u := range uidMap { entry, ok := entity.GetEntityByUid(sim.World, u.Uid) if !ok { return nil, fmt.Errorf("道岔不存在: World id=%d,道岔id=%s", sim.World.Id(), u.Uid) } if !entry.HasComponent(component.TurnoutPositionType) { return nil, fmt.Errorf("道岔没有TurnoutPosition组件: World id=%d,道岔id=%s", sim.World.Id(), u.Uid) } pos := component.TurnoutPositionType.Get(entry) s := &state.SwitchState{ Id: u.CommonId, Normal: pos.Db, Reverse: pos.Fb, Dw: pos.Dw, Fw: pos.Fw, } // 强制(联锁驱动无效) s.Force = entry.HasComponent(component.TurnoutFaultCiqdType) // 失表 s.Sb = entry.HasComponent(component.TurnoutFaultSbType) // 定位失表 s.Dwsb = entry.HasComponent(component.TurnoutFaultDwsbType) // 反位失表 s.Fwsb = entry.HasComponent(component.TurnoutFaultFwsbType) // 挤岔 s.Jc = entry.HasComponent(component.TurnoutFaultJcType) if entry.HasComponent(component.Zdj9TwoElectronicType) { elec := component.Zdj9TwoElectronicType.Get(entry) dcj := component.BitStateType.Get(elec.TDC_DCJ) s.Dc = dcj.Val qdc, err := wd.QueryQdBit(component.UidType.Get(elec.TDC_DCJ).Id) if err == nil { s.Qdc = qdc } fcj := component.BitStateType.Get(elec.TDC_FCJ) s.Fc = fcj.Val qfc, err := wd.QueryQdBit(component.UidType.Get(elec.TDC_FCJ).Id) if err == nil { s.Qfc = qfc } ycj := component.BitStateType.Get(elec.TDC_YCJ) s.Yc = ycj.Val qyc, err := wd.QueryQdBit(component.UidType.Get(elec.TDC_YCJ).Id) if err == nil { s.Qyc = qyc } } // 查看道岔是否被占用 turnout := sim.Repo.FindTurnout(u.Uid) if turnout == nil { return nil, fmt.Errorf("道岔不存在: World id=%d,道岔id=%s", sim.World.Id(), u.Uid) } if turnout.GetPhysicalSection() == nil { return nil, fmt.Errorf("道岔关联的物理区段不存在: World id=%d,道岔id=%s", sim.World.Id(), u.Uid) } sectionState := handlerSectionState(sim.World, turnout.GetPhysicalSection().Id()) s.Occupied = sectionState.Occupied switchArr = append(switchArr, s) } return switchArr, nil } // 收集站台状态 func (ms *SfpMs) collectPlatformStates() ([]*state.PlatformState, error) { var states []*state.PlatformState mapData := memory.QueryGiData[*graphicData.RtssGraphicStorage](ms.mapId) uidsMap := memory.QueryUidStructure[*memory.StationUidStructure](ms.mapId) platformScreenDoorMap := wrapScreenDoorToPlatform(mapData) for _, platform := range mapData.Platforms { stationCommonId := platform.RefStationId if stationCommonId == "" { return nil, fmt.Errorf("站台没有绑定车站:id=%s", platform.Common.Id) } uidInfo := uidsMap.StationIds[stationCommonId] if uidInfo == nil { return nil, fmt.Errorf("车站实体不存在uid映射:id=%s", stationCommonId) } entry, ok := entity.GetEntityByUid(ms.vs.World, uidInfo.Uid) if !ok { return nil, fmt.Errorf("车站实体不存在: World id=%d, uid=%s", ms.vs.World.Id(), uidInfo.Uid) } sta := &state.PlatformState{Id: platform.Common.Id} isX := strings.Contains(platform.Code, "下行站台") //下行站台 if entry.HasComponent(component.SpkElectronicType) { // 紧急停车继电器 empElectronic := component.EmpElectronicType.Get(entry) if isX { sta.Empj = getRelayXqVal(empElectronic.XEMPJ) } else { sta.Empj = getRelayXqVal(empElectronic.SEMPJ) } } if entry.HasComponent(component.EmpElectronicType) { // SPKS继电器 spkElectronic := component.SpkElectronicType.Get(entry) if isX { sta.SpksState = append(sta.SpksState, &state.ReplyState{Code: "SPKSX旁路", Xh: getRelayXqVal(spkElectronic.SPKSXPLAJ)}) sta.SpksState = append(sta.SpksState, &state.ReplyState{Code: "SPKS1", Xh: getRelayXqVal(spkElectronic.SPKSX1J)}) sta.SpksState = append(sta.SpksState, &state.ReplyState{Code: "SPKS3", Xh: getRelayXqVal(spkElectronic.SPKSX3J)}) } else { sta.SpksState = append(sta.SpksState, &state.ReplyState{Code: "SPKSS旁路", Xh: getRelayXqVal(spkElectronic.SPKSSPLAJ)}) sta.SpksState = append(sta.SpksState, &state.ReplyState{Code: "SPKS2", Xh: getRelayXqVal(spkElectronic.SPKSS2J)}) sta.SpksState = append(sta.SpksState, &state.ReplyState{Code: "SPKS4", Xh: getRelayXqVal(spkElectronic.SPKSS4J)}) } } psdId := platformScreenDoorMap[platform.Common.Id] if psdId != "" { psdUid, ok := uidsMap.PsdIds[psdId] if !ok { continue } psdEntry, ok := entity.GetEntityByUid(ms.vs.World, psdUid.Uid) if !ok { return nil, fmt.Errorf("屏蔽门实体不存在: World id=%d, uid=%s", ms.vs.World.Id(), psdUid.Uid) } if psdEntry.HasComponent(component.PlatformMkxCircuitType) { mkxCircuit := component.PlatformMkxCircuitType.Get(psdEntry) mkxj := &state.MkxJState{Code: psdUid.Code} if mkxCircuit.PABJ != nil { mkxj.ReplyState = append(mkxj.ReplyState, &state.ReplyState{Code: "站台确认继电器", Xh: getRelayXqVal(mkxCircuit.PABJ)}) } if mkxCircuit.PCBJ != nil { mkxj.ReplyState = append(mkxj.ReplyState, &state.ReplyState{Code: "站台关门继电器", Xh: getRelayXqVal(mkxCircuit.PCBJ)}) } if mkxCircuit.POBJ != nil { mkxj.ReplyState = append(mkxj.ReplyState, &state.ReplyState{Code: "站台开门继电器", Xh: getRelayXqVal(mkxCircuit.POBJ)}) } sta.MkxJState = mkxj } } states = append(states, sta) } return states, nil } // 将屏蔽门关联到站台 func wrapScreenDoorToPlatform(mapData *graphicData.RtssGraphicStorage) map[string]string { platformMap := make(map[string]string, len(mapData.Platforms)) for _, s := range mapData.ScreenDoors { platformMap[s.RefPlatformId] = s.Common.Id } return platformMap } // 获取继电器吸起状态 func getRelayXqVal(entry *ecs.Entry) bool { relay := component.BitStateType.Get(entry) return relay.Val }