package memory import ( "fmt" "sort" "strconv" "strings" "sync" "joylink.club/bj-rtsts-server/ats/verify/protos/graphicData" "joylink.club/bj-rtsts-server/ats/verify/protos/state" "joylink.club/bj-rtsts-server/dto" "joylink.club/ecs" "joylink.club/rtsssimulation/repository" "joylink.club/rtsssimulation/repository/model/proto" "joylink.club/rtsssimulation/simulation" "joylink.club/rtsssimulation/system" ) // 轨旁仿真定义 type VerifySimulation struct { //地图id MapIds []int32 // 项目ID ProjectId int32 //仿真id SimulationId string //仿真内存数据 Memory *WaysideMemory //模型仓库 Repo *repository.Repository //Rtss仿真世界的id WorldId int } // 轨旁仿真内存模型 type WaysideMemory struct { //可变状态数据:轨旁仿真模型状态(全量数据) Status *VerifyStatus // 要变更的状态:用户操作过的状态记录在这里,增量推送次数据 ChangeStatus *ChangeVerifyStatus //状态保护锁 rwLock *sync.RWMutex } // 轨旁仿真模型状态 type VerifyStatus struct { //道岔状态,key为道岔id即索引 state.SwitchState SwitchStateMap sync.Map //轨道状态,key为轨道id即索引 state.LinkState LinkStateMap sync.Map //列车状态,key为列车id即索引 state.TrainState TrainStateMap sync.Map //计轴区段状态,key为计轴区段的id即索引 state.SectionState AxleSectionStateMap sync.Map //物理区段状态,key为物理区段id即索引 state.SectionState PhysicalSectionStateMap sync.Map //逻辑区段状态,key为逻辑区段id即索引 state.SectionState LogicSectionStateMap sync.Map //信号机状态,key为信号机id,即索引 state.SignalState SignalStateMap sync.Map } // 轨旁仿真模型状态(变更) type ChangeVerifyStatus struct { VerifyStatus //删除的列车ID列表 RemoveTrainId []string } func NewWaysideMemory() *WaysideMemory { return &WaysideMemory{ Status: &VerifyStatus{}, ChangeStatus: &ChangeVerifyStatus{}, rwLock: &sync.RWMutex{}, } } // 创建仿真对象 func CreateSimulation(projectId int32, mapIds []int32) (*VerifySimulation, error) { //构建Repository sort.Slice(mapIds, func(i, j int) bool { return mapIds[i] < mapIds[j] }) var mapIdStrSlice []string for _, id := range mapIds { mapIdStrSlice = append(mapIdStrSlice, strconv.Itoa(int(id))) } repoId := strings.Join(mapIdStrSlice, "|") repoVersion := "0.1" repo := repository.FindRepository(repoId, repoVersion) if repo == nil { //var storages []*graphicData.RtssGraphicStorage //var rmapIds []int32 //for _, mapId := range mapIds { // storage := QueryGraphicStorage(mapId) // if storage == nil { // continue // } // storages = append(storages, storage) // rmapIds = append(rmapIds, mapId) //} protoRepo, err := buildProtoRepository(mapIds) if err != nil { return nil, err } newRepo, err := repository.BuildRepository(protoRepo) if err != nil { return nil, err } repo = newRepo } // 目前用本地构建状态 worldMemory := NewWaysideMemory() initWorldDeviceState(worldMemory.Status, repo) //创建仿真 worldId := simulation.CreateSimulation(repo, system.SWITCH_ZDJ9_2) verifySimulation := &VerifySimulation{ MapIds: mapIds, ProjectId: projectId, Memory: worldMemory, Repo: repo, WorldId: worldId, } return verifySimulation, nil } // 获取全量状态 func (s *VerifySimulation) GetAllState(mapId int32) *state.PushedDevicesStatus { return &state.PushedDevicesStatus{ All: true, AllStatus: &state.AllDevicesStatus{ SwitchState: GetMapAllTurnoutState(s, mapId), TrainState: GetAllTrainState(s), }, } } // 获取仿真世界信息 func (s *VerifySimulation) GetSimulationWorld() ecs.World { ecsSimulation := simulation.FindSimulation(ecs.WorldId(s.WorldId)) if ecsSimulation == nil { panic(&dto.ErrorDto{Code: dto.DataNotExist, Message: "ecs 仿真不存在"}) } return ecsSimulation.GetWorld() } func buildProtoRepository(mapIds []int32) (*proto.Repository, error) { repo := &proto.Repository{} var exceptStationGiMapIds []int32 //创建设备 for _, mapId := range mapIds { giType := QueryGiType(mapId) if giType == graphicData.PictureType_StationLayout { stationGi := QueryGiData[*graphicData.RtssGraphicStorage](mapId) fillProtoRepository(repo, stationGi, mapId) } else { exceptStationGiMapIds = append(exceptStationGiMapIds, mapId) } } //将继电器与设备关联 for _, mapId := range exceptStationGiMapIds { giType := QueryGiType(mapId) if giType == graphicData.PictureType_RelayCabinetLayout { relayGi := QueryGiData[*graphicData.RelayCabinetGraphicStorage](mapId) relateRelay(repo, relayGi) } } return repo, nil } func relateRelay(repo *proto.Repository, relayGi *graphicData.RelayCabinetGraphicStorage, mapId int32) { city := relayGi.UniqueIdPrefix.City lineId := relayGi.UniqueIdPrefix.LineId station := relayGi.UniqueIdPrefix.BelongsConcentrationStation uidsMap := queryUidStructure[*relayUidStructure](mapId) for _, relay := range relayGi.Relays { repo.Relays = append(repo.Relays, &proto.Relay{ Id: uidsMap.RelayIds[relay.Common.Id].Uid, Code: relay.Code, Model: relay.Model, }) } turnoutMap := make(map[string]*proto.Turnout) for _, turnout := range repo.Turnouts { turnoutMap[turnout.Id] = turnout } signalMap := make(map[string]*proto.Signal) for _, signal := range repo.Signals { signalMap[signal.Id] = signal } for _, relationship := range relayGi.DeviceRelateRelayList { switch relationship.Type { case "Turnout": turnout := turnoutMap[GenerateElementUid(city, lineId, []string{station}, relationship.Code)] for _, group := range relationship.Combinationtypes { turnout.RelayGroups = append(turnout.RelayGroups, &proto.RelayGroup{ Code: group.Code, RelayIds: group.RefRelays, }) } case "Signal": signal := signalMap[GenerateElementUid(city, lineId, []string{station}, relationship.Code)] for _, group := range relationship.Combinationtypes { signal.RelayGroups = append(signal.RelayGroups, &proto.RelayGroup{ Code: group.Code, RelayIds: group.RefRelays, }) } } } } func fillProtoRepository(repo *proto.Repository, storage *graphicData.RtssGraphicStorage, mapId int32) { axleCountingMap := make(map[string]*graphicData.AxleCounting) uidsMap := queryUidStructure[*stationUidStructure](mapId) for _, data := range storage.AxleCountings { axleCountingMap[data.Common.Id] = data cpType := proto.CheckPointType_AxleCounter if data.Type == graphicData.AxleCounting_SectionBoundary { cpType = proto.CheckPointType_Boundary } cp := &proto.CheckPoint{ Id: data.Common.Id, Km: convertKm(data.KilometerSystem), Type: cpType, DevicePorts: convertDevicePorts(data.AxleCountingRef), } repo.CheckPoints = append(repo.CheckPoints, converCheckPointUid(cp, uidsMap)) } for _, data := range storage.Section { var turnoutIds []string if data.SectionType == graphicData.Section_TurnoutPhysical { turnoutIds = findTurnoutIds(axleCountingMap, data.AxleCountings) } physicalSection := &proto.PhysicalSection{ Id: data.Common.Id, ADevicePort: convertDevicePort(data.PaRef), BDevicePort: convertDevicePort(data.PbRef), TurnoutIds: turnoutIds, } repo.PhysicalSections = append(repo.PhysicalSections, converSectionUid(physicalSection, uidsMap)) } for _, data := range storage.Turnouts { var km *proto.Kilometer for _, ks := range data.KilometerSystem { if ks.Kilometer != 0 { km = convertKm(ks) break } } repo.KilometerConverts = append(repo.KilometerConverts, buildKmConverts(data.KilometerSystem)...) turnout := &proto.Turnout{ Id: data.Common.Id, Km: km, ADevicePort: convertDevicePort(data.PaRef), BDevicePort: convertDevicePort(data.PbRef), CDevicePort: convertDevicePort(data.PcRef), } repo.Turnouts = append(repo.Turnouts, converTurnoutUid(turnout, uidsMap)) } for _, data := range storage.Signals { var sectionId string var turnoutPort *proto.DevicePort switch data.RefDev.DeviceType { case graphicData.RelatedRef_Section: sectionId = data.RefDev.Id case graphicData.RelatedRef_Turnout: turnoutPort = convertDevicePort(data.RefDev) } signal := &proto.Signal{ Id: data.Common.Id, Km: convertKm(data.KilometerSystem), SectionId: sectionId, TurnoutPort: turnoutPort, } repo.Signals = append(repo.Signals, converSignalUid(signal, uidsMap)) } for _, data := range storage.Transponders { var sectionId string var turnoutPort *proto.DevicePort switch data.TransponderRef.DeviceType { case graphicData.RelatedRef_Section: sectionId = data.TransponderRef.Id case graphicData.RelatedRef_Turnout: turnoutPort = convertDevicePort(data.TransponderRef) } responder := &proto.Transponder{ Id: data.Common.Id, Km: convertKm(data.KilometerSystem), SectionId: sectionId, TurnoutPort: turnoutPort, } repo.Transponders = append(repo.Transponders, converTransponderUid(responder, uidsMap)) } slopeKsMap := make(map[string]*graphicData.SlopeKiloMarker) for _, data := range storage.SlopeKiloMarker { slopeKsMap[data.Common.Id] = data repo.KilometerConverts = append(repo.KilometerConverts, buildKmConverts(data.KilometerSystem)...) } curveKsMap := make(map[string]*graphicData.CurvatureKiloMarker) for _, data := range storage.CurvatureKiloMarker { curveKsMap[data.Common.Id] = data repo.KilometerConverts = append(repo.KilometerConverts, buildKmConverts(data.KilometerSystem)...) } for _, data := range storage.Slopes { var kms []*proto.Kilometer for _, id := range data.RefDeviceId { kms = append(kms, convertKm(slopeKsMap[id].KilometerSystem[0])) } slope := &proto.Slope{ Id: data.Common.Id, Kms: kms, Degree: data.SlopeNumber, } repo.Slopes = append(repo.Slopes, converSlopeUid(slope, uidsMap)) } for _, data := range storage.Curvatures { var kms []*proto.Kilometer for _, id := range data.RefDeviceId { kms = append(kms, convertKm(curveKsMap[id].KilometerSystem[0])) } slope := &proto.SectionalCurvature{ Id: data.Common.Id, Kms: kms, Radius: data.CurvatureNumber, } repo.SectionalCurvatures = append(repo.SectionalCurvatures, converCurvatureUid(slope, uidsMap)) } } func converCheckPointUid(data *proto.CheckPoint, uidsMap *stationUidStructure) *proto.CheckPoint { data.Id = uidsMap.AxlePointIds[data.Id].Uid for _, c := range data.DevicePorts { c.DeviceId = converRefUid(c.DeviceId, c.DeviceType, uidsMap) } return data } func converSectionUid(data *proto.PhysicalSection, uidsMap *stationUidStructure) *proto.PhysicalSection { data.Id = uidsMap.PhysicalSectionIds[data.Id].Uid if data.ADevicePort != nil { data.ADevicePort.DeviceId = converRefUid(data.ADevicePort.DeviceId, data.ADevicePort.DeviceType, uidsMap) } if data.BDevicePort != nil { data.BDevicePort.DeviceId = converRefUid(data.BDevicePort.DeviceId, data.BDevicePort.DeviceType, uidsMap) } tids := make([]string, len(data.TurnoutIds)) for i, tid := range data.TurnoutIds { tids[i] = converRefUid(tid, proto.DeviceType_DeviceType_Turnout, uidsMap) } data.TurnoutIds = tids return data } func converTurnoutUid(data *proto.Turnout, uidsMap *stationUidStructure) *proto.Turnout { data.Id = uidsMap.TurnoutIds[data.Id].Uid if data.ADevicePort != nil { data.ADevicePort.DeviceId = converRefUid(data.ADevicePort.DeviceId, data.ADevicePort.DeviceType, uidsMap) } if data.BDevicePort != nil { data.BDevicePort.DeviceId = converRefUid(data.BDevicePort.DeviceId, data.BDevicePort.DeviceType, uidsMap) } if data.CDevicePort != nil { data.CDevicePort.DeviceId = converRefUid(data.CDevicePort.DeviceId, data.CDevicePort.DeviceType, uidsMap) } return data } func converSignalUid(data *proto.Signal, uidsMap *stationUidStructure) *proto.Signal { data.Id = uidsMap.SignalIds[data.Id].Uid if data.SectionId != "" { data.SectionId = converRefUid(data.SectionId, proto.DeviceType_DeviceType_PhysicalSection, uidsMap) } if data.TurnoutPort != nil { data.TurnoutPort.DeviceId = converRefUid(data.TurnoutPort.DeviceId, data.TurnoutPort.DeviceType, uidsMap) } return data } func converTransponderUid(data *proto.Transponder, uidsMap *stationUidStructure) *proto.Transponder { data.Id = uidsMap.TransponderIds[data.Id].Uid if data.SectionId != "" { data.SectionId = converRefUid(data.SectionId, proto.DeviceType_DeviceType_PhysicalSection, uidsMap) } if data.TurnoutPort != nil { data.TurnoutPort.DeviceId = converRefUid(data.TurnoutPort.DeviceId, data.TurnoutPort.DeviceType, uidsMap) } return data } func converSlopeUid(data *proto.Slope, uidsMap *stationUidStructure) *proto.Slope { data.Id = uidsMap.SlopeIds[data.Id].Uid return data } func converCurvatureUid(data *proto.SectionalCurvature, uidsMap *stationUidStructure) *proto.SectionalCurvature { data.Id = uidsMap.CurvatureIds[data.Id].Uid return data } func converRefUid(id string, d proto.DeviceType, uidsMap *stationUidStructure) string { var elementId *elementIdStructure switch d { case proto.DeviceType_DeviceType_CheckPoint: elementId = uidsMap.AxlePointIds[id] case proto.DeviceType_DeviceType_PhysicalSection: elementId = uidsMap.PhysicalSectionIds[id] case proto.DeviceType_DeviceType_SectionalCurvature: elementId = uidsMap.CurvatureIds[id] case proto.DeviceType_DeviceType_Signal: elementId = uidsMap.SignalIds[id] case proto.DeviceType_DeviceType_Slope: elementId = uidsMap.SlopeIds[id] case proto.DeviceType_DeviceType_Transponder: elementId = uidsMap.TransponderIds[id] case proto.DeviceType_DeviceType_Turnout: elementId = uidsMap.TurnoutIds[id] default: panic(&dto.ErrorDto{Code: dto.ArgumentParseError, Message: "异常的设备类型-" + d.String()}) } return elementId.Uid } func convertKm(ks *graphicData.KilometerSystem) *proto.Kilometer { var dir proto.Direction switch ks.Direction { case graphicData.Direction_LEFT: dir = proto.Direction_LEFT case graphicData.Direction_RIGHT: dir = proto.Direction_RIGHT } return &proto.Kilometer{ Value: ks.Kilometer, CoordinateSystem: ks.CoordinateSystem, Direction: dir, } } func convertDevicePort(ref *graphicData.RelatedRef) *proto.DevicePort { if ref == nil { return nil } var deviceType proto.DeviceType var port proto.Port switch ref.DevicePort { case graphicData.RelatedRef_A: port = proto.Port_A case graphicData.RelatedRef_B: port = proto.Port_B case graphicData.RelatedRef_C: port = proto.Port_C } switch ref.DeviceType { case graphicData.RelatedRef_Section: deviceType = proto.DeviceType_DeviceType_PhysicalSection case graphicData.RelatedRef_Turnout: deviceType = proto.DeviceType_DeviceType_Turnout default: panic(fmt.Sprintf("异常的设备类型-%s", ref.DeviceType)) } return &proto.DevicePort{ DeviceId: ref.Id, DeviceType: deviceType, Port: port, } } func convertDevicePorts(refList []*graphicData.RelatedRef) []*proto.DevicePort { var dps []*proto.DevicePort for _, ref := range refList { dps = append(dps, convertDevicePort(ref)) } return dps } func findTurnoutIds(axleCountingMap map[string]*graphicData.AxleCounting, axleIds []string) []string { if len(axleIds) <= 2 { return nil } turnoutMap := make(map[string]bool) for _, axleId := range axleIds { axle := axleCountingMap[axleId] relTurnoutCount := 0 var turnoutId string for _, ref := range axle.AxleCountingRef { if ref.DeviceType == graphicData.RelatedRef_Turnout { relTurnoutCount++ turnoutId = ref.Id } } if relTurnoutCount == 1 { turnoutMap[turnoutId] = true } } var turnoutIds []string for id := range turnoutMap { turnoutIds = append(turnoutIds, id) } return turnoutIds } func buildKmConverts(ksList []*graphicData.KilometerSystem) []*proto.KilometerConvert { var kmConverts []*proto.KilometerConvert for i, ks := range ksList { if ks.Kilometer == 0 { continue } for j := i + 1; j < len(ksList); j++ { if ks.Kilometer == 0 { continue } kmConverts = append(kmConverts, buildKmConvert(ks, ksList[j])) } } return kmConverts } func buildKmConvert(ks1 *graphicData.KilometerSystem, ks2 *graphicData.KilometerSystem) *proto.KilometerConvert { return &proto.KilometerConvert{ KmA: convertKm(ks1), KmB: convertKm(ks2), SameTrend: false, } } func initWorldDeviceState(status *VerifyStatus, repo *repository.Repository) { initWorldTurnoutState(status, repo) initWorldPhysicalSectionState(status, repo) } // 初始化道岔状态 func initWorldTurnoutState(status *VerifyStatus, repo *repository.Repository) { for _, turnout := range repo.TurnoutList() { id := turnout.Identity.Id() status.SwitchStateMap.Store(id, &state.SwitchState{Id: id, Normal: true, Reverse: false}) } } // 初始化物理区段状态 func initWorldPhysicalSectionState(status *VerifyStatus, repo *repository.Repository) { for _, section := range repo.PhysicalSectionList() { id := section.Identity.Id() status.PhysicalSectionStateMap.Store(id, &state.SectionState{Id: id, Occupied: false, Type: state.SectionType_Physic}) } }