rts-sim-testing-service/message_server/sfp_ms.go
2023-11-09 18:13:32 +08:00

404 lines
13 KiB
Go

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("simulation-%s_%d-devices-status", 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
}