package memory import ( "fmt" "log/slog" "math" "runtime" "sort" "strconv" "strings" "sync" "time" "joylink.club/rtsssimulation/component" "joylink.club/rtsssimulation/entity" rtss_simulation "joylink.club/rtsssimulation" "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/bj-rtsts-server/sys_error" "joylink.club/bj-rtsts-server/third_party/dynamics" "joylink.club/bj-rtsts-server/third_party/message" "joylink.club/bj-rtsts-server/third_party/semi_physical_train" "joylink.club/ecs" "joylink.club/rtsssimulation/repository" "joylink.club/rtsssimulation/repository/model/proto" ) // 轨旁仿真定义 type VerifySimulation struct { //地图id MapIds []int32 // 项目ID ProjectId int32 //仿真id SimulationId string //仿真内存数据 Memory *WaysideMemory //模型仓库 Repo *repository.Repository //Rtss仿真世界的id WorldId ecs.WorldId World ecs.World //设备UID映射 uidMap map[string]*elementIdStructure } // 轨旁仿真内存模型 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 { protoRepo, err := buildProtoRepository(mapIds) if err != nil { return nil, sys_error.New("数据错误", err) } protoRepo.Id, protoRepo.Version = repoId, repoVersion newRepo, err := repository.BuildRepository(protoRepo) if err != nil { return nil, sys_error.New("数据错误", err) } repo = newRepo } // 构建所有UID映射关系, allUidMap := buildRepositoryAllUidsMap(mapIds, repo) //创建仿真 w, err := rtss_simulation.NewSimulation(repo) if err != nil { return nil, sys_error.New("仿真创建失败", err) } verifySimulation := &VerifySimulation{ MapIds: mapIds, ProjectId: projectId, Memory: NewWaysideMemory(), Repo: repo, World: w, WorldId: w.Id(), uidMap: allUidMap, } // 保证World关闭 runtime.SetFinalizer(verifySimulation, func(verifySimulation *VerifySimulation) { slog.Info("---关闭仿真World---") verifySimulation.World.Close() }) return verifySimulation, nil } // 获取全量状态 func (s *VerifySimulation) GetAllState(mapId int32) *state.PushedDevicesStatus { giType := QueryGiType(mapId) switch giType { case graphicData.PictureType_StationLayout: return &state.PushedDevicesStatus{ All: true, AllStatus: &state.AllDevicesStatus{ SwitchState: GetMapAllTurnoutState(s, mapId), TrainState: GetAllTrainState(s), SignalState: GetMapAllSignalState(s, mapId), ButtonState: GetMapAllStationButtonState(s, mapId), PsdState: GetMapAllPsdState(s, mapId), }, } case graphicData.PictureType_RelayCabinetLayout: return &state.PushedDevicesStatus{ All: true, AllStatus: &state.AllDevicesStatus{ ReplyState: GetMapAllRelayState(s, mapId), }, } } return &state.PushedDevicesStatus{} } // 获取门控箱状态 func (s *VerifySimulation) GetAllPSLState(mapId int32, boxId string) *state.PushedDevicesStatus { world := s.GetSimulationWorld() uidStructure := queryUidStructure[*stationUidStructure](mapId) boxUid := uidStructure.GateBoxIds[boxId].Uid mkxEntry, ok := entity.GetEntityByUid(world, boxUid) var buttonStateArr []*state.ButtonState if ok { mkxCircuit := component.MkxCircuitType.Get(mkxEntry) var boxArr []*ecs.Entry boxArr = append(boxArr, mkxCircuit.PcbList...) boxArr = append(boxArr, mkxCircuit.PobList...) boxArr = append(boxArr, mkxCircuit.PabList...) for _, mkxBoxEntry := range boxArr { mkxBox := component.MkxBoxType.Get(mkxBoxEntry) uid := component.UidType.Get(mkxBox.Btn).Id down := component.BitStateType.Get(mkxBox.Btn).Val buttonStateArr = append(buttonStateArr, &state.ButtonState{ Id: uid, Down: down, }) } } return &state.PushedDevicesStatus{ All: true, AllStatus: &state.AllDevicesStatus{ ButtonState: buttonStateArr, }, } } // 获取车站IBP状态 func (s *VerifySimulation) GetAllIBPState(mapId int32, stationId string, ibpMapCode string) *state.PushedDevicesStatus { return &state.PushedDevicesStatus{ All: true, AllStatus: GetMapAllIBPState(s, mapId, stationId, ibpMapCode), } } // 获取仿真世界信息 func (s *VerifySimulation) GetSimulationWorld() ecs.World { return s.World } // 获取仿真世界信息 func (s *VerifySimulation) GetComIdByUid(uid string) string { es := s.uidMap if es == nil { panic(&dto.ErrorDto{Code: dto.DataNotExist, Message: "无映射信息"}) } if es[uid] == nil { panic(&dto.ErrorDto{Code: dto.DataNotExist, Message: "无【uid】映射信息"}) } return es[uid].CommonId } func (sim *VerifySimulation) BuildLineBaseInfo() *message.LineBaseInfo { info := &message.LineBaseInfo{} for _, model := range sim.Repo.LinkList() { id, _ := strconv.Atoi(model.Id()) link := &message.Link{ ID: int32(id), Len: int32(model.Length()), } info.LinkList = append(info.LinkList, link) if model.ARelation() != nil { turnoutId, _ := strconv.Atoi(sim.GetComIdByUid(model.ARelation().Device().Id())) link.ARelTurnoutId = int32(turnoutId) switch model.ARelation().Port() { case proto.Port_A: link.ARelTurnoutPoint = "A" case proto.Port_B: link.ARelTurnoutPoint = "B" case proto.Port_C: link.ARelTurnoutPoint = "C" } } if model.BRelation() != nil { turnoutId, _ := strconv.Atoi(sim.GetComIdByUid(model.BRelation().Device().Id())) link.BRelTurnoutId = int32(turnoutId) switch model.BRelation().Port() { case proto.Port_A: link.BRelTurnoutPoint = "A" case proto.Port_B: link.BRelTurnoutPoint = "B" case proto.Port_C: link.BRelTurnoutPoint = "C" } } } for _, model := range sim.Repo.SlopeList() { id, _ := strconv.Atoi(sim.GetComIdByUid(model.Id())) slope := &message.Slope{ ID: int32(id), StartLinkOffset: int32(model.StartLinkPosition().Offset()), EndLinkOffset: int32(model.EndLinkPosition().Offset()), DegreeTrig: model.Degree(), } info.SlopeList = append(info.SlopeList, slope) startLinkId, _ := strconv.Atoi(model.StartLinkPosition().Link().Id()) slope.StartLinkId = int32(startLinkId) endLinkId, _ := strconv.Atoi(model.EndLinkPosition().Link().Id()) slope.EndLinkId = int32(endLinkId) } for _, model := range sim.Repo.SectionalCurvatureList() { id, _ := strconv.Atoi(sim.GetComIdByUid(model.Id())) curve := &message.Curve{ ID: int32(id), StartLinkOffset: int32(model.StartLinkPosition().Offset()), EndLinkOffset: int32(model.EndLinkPosition().Offset()), Curvature: model.Radius(), } info.CurveList = append(info.CurveList, curve) startLinkId, _ := strconv.Atoi(model.StartLinkPosition().Link().Id()) curve.StartLinkId = int32(startLinkId) endLinkId, _ := strconv.Atoi(model.EndLinkPosition().Link().Id()) curve.EndLinkId = int32(endLinkId) } return info } // 采集动力学道岔状态 func (s *VerifySimulation) CollectDynamicsTurnoutInfo() []*message.DynamicsTurnoutInfo { stateSlice := GetAllTurnoutState(s) var turnoutStates []*message.DynamicsTurnoutInfo for _, sta := range stateSlice { code64, err := strconv.ParseUint(sta.Id, 10, 16) if err != nil { slog.Error("id转uint16报错", err) } info := message.DynamicsTurnoutInfo{ Code: uint16(code64), NPosition: sta.Dw, RPosition: sta.Fw, } turnoutStates = append(turnoutStates, &info) } return turnoutStates } // 处理动力学列车速度消息 func (s *VerifySimulation) HandleDynamicsTrainInfo(info *message.DynamicsTrainInfo) { sta, ok := s.Memory.Status.TrainStateMap.Load(strconv.Itoa(int(info.Number))) if !ok { return } // 给半实物仿真发送速度 semi_physical_train.Default().SendTrainControlMessage(info) // 更新列车状态 UpdateTrainState(s, convert(info, sta.(*state.TrainState), s)) } func convert(info *message.DynamicsTrainInfo, sta *state.TrainState, simulation *VerifySimulation) *state.TrainState { delayTime := time.Now().UnixMilli() - sta.VobcState.UpdateTime sta.ControlDelayTime = (int64(sta.VobcState.LifeSignal)-int64(info.VobcLifeSignal))*20 + delayTime slog.Debug("收到动力学原始消息", "Number", info.Number, "Link", info.Link, "LinkOffset", info.LinkOffset) id, port, offset, runDirection, pointTo, kilometer := QueryDeviceByCalcLink(simulation.Repo, strconv.Itoa(int(info.Link)), int64(info.LinkOffset), info.Up) slog.Debug("处理动力学转换后的消息", "number", info.Number, "车头位置", id, "偏移", offset, "是否上行", runDirection, "是否ab", pointTo) sta.HeadDeviceId = simulation.GetComIdByUid(id) sta.DevicePort = port sta.HeadOffset = offset sta.PointTo = pointTo sta.TrainKilometer = kilometer sta.RunDirection = runDirection //判定车头方向 sta.HeadDirection = runDirection if sta.VobcState != nil { if sta.VobcState.DirectionForward { sta.HeadDirection = runDirection } else if sta.VobcState.DirectionBackward { sta.HeadDirection = !runDirection } } if info.Speed < 0 { sta.RunDirection = !sta.RunDirection } // 赋值动力学信息 sta.DynamicState.Heartbeat = int32(info.LifeSignal) sta.DynamicState.HeadLinkId = strconv.Itoa(int(info.Link)) sta.DynamicState.HeadLinkOffset = int64(info.LinkOffset) sta.DynamicState.Slope = int32(info.Slope) sta.DynamicState.Upslope = info.UpSlope sta.DynamicState.RunningUp = info.Up sta.DynamicState.RunningResistanceSum = float32(info.TotalResistance) / 1000 sta.DynamicState.AirResistance = float32(info.AirResistance) / 1000 sta.DynamicState.RampResistance = float32(info.SlopeResistance) / 1000 sta.DynamicState.CurveResistance = float32(info.CurveResistance) / 1000 sta.DynamicState.Speed = speedParse(info.Speed) sta.DynamicState.HeadSensorSpeed1 = speedParse(info.HeadSpeed1) sta.DynamicState.HeadSensorSpeed2 = speedParse(info.HeadSpeed2) sta.DynamicState.TailSensorSpeed1 = speedParse(info.TailSpeed1) sta.DynamicState.TailSensorSpeed2 = speedParse(info.TailSpeed2) sta.DynamicState.HeadRadarSpeed = speedParse(info.HeadRadarSpeed) sta.DynamicState.TailRadarSpeed = speedParse(info.TailRadarSpeed) sta.DynamicState.Acceleration = info.Acceleration return sta } // 发送给前端的速度格式化 func speedParse(speed float32) int32 { return int32(math.Abs(float64(speed * 3.6 * 100))) } // 处理半实物仿真列车控制消息 func (s *VerifySimulation) HandleSemiPhysicalTrainControlMsg(b []byte) { s.Memory.Status.TrainStateMap.Range(func(_, value any) bool { train := value.(*state.TrainState) if !train.Show { // 下线列车 return false } trainId, err := strconv.Atoi(train.Id) if err != nil { panic(dto.ErrorDto{Code: dto.ArgumentParseError, Message: err.Error()}) } d := append(b, uint8(trainId)) // 发送给动力学 dynamics.Default().SendTrainControlMessage(d) // 存放至列车中 controlMessage := &message.TrainControlMsg{} controlMessage.Decode(b) train.VobcState = controlMessage.ControlInfo return true }) } // 处理接到的联锁消息 func (s *VerifySimulation) HandleDriverInfo(b []byte) { } // 采集联锁中的继电器消息 func (s *VerifySimulation) CollectRelayInfo() *message.InterlockSendMsgPkg { return nil } 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) buildAndRelateElectronicComponent(repo, relayGi, mapId) } } return repo, nil } func buildAndRelateElectronicComponent(repo *proto.Repository, relayGi *graphicData.RelayCabinetGraphicStorage, mapId int32) { uidsMap := queryUidStructure[*relayUidStructure](mapId) city := relayGi.UniqueIdPrefix.City lineId := relayGi.UniqueIdPrefix.LineId station := relayGi.UniqueIdPrefix.BelongsConcentrationStation relayMap := make(map[string]*proto.Relay) for _, relay := range relayGi.Relays { repoRelay := &proto.Relay{ Id: uidsMap.RelayIds[relay.Common.Id].Uid, Code: relay.Code, Model: convertRelayModel(relay.NewModel), } repo.Relays = append(repo.Relays, repoRelay) relayMap[repoRelay.Id] = repoRelay } for _, pfp := range relayGi.PhaseFailureProtectors { repo.PhaseFailureProtectors = append(repo.PhaseFailureProtectors, &proto.PhaseFailureProtector{ Id: uidsMap.RelayIds[pfp.Common.Id].Uid, Code: pfp.Code, }) } 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 } stationMap := make(map[string]*proto.Station) for _, station := range repo.Stations { stationMap[station.Id] = station } psdMap := make(map[string]*proto.Psd) for _, psd := range repo.Psds { psdMap[psd.Id] = psd } platformMap := make(map[string]*proto.Platform) for _, platform := range repo.Platforms { platformMap[platform.Id] = platform } for _, relationship := range relayGi.DeviceRelateRelayList { switch relationship.DeviceType { case graphicData.RelatedRef_Turnout: turnout := turnoutMap[GenerateElementUid(city, lineId, []string{station}, relationship.Code)] if turnout == nil { continue } for _, group := range relationship.Combinationtypes { var componentIds []string for _, relayId := range group.RefRelays { if uidsMap.RelayIds[relayId] == nil { continue } componentIds = append(componentIds, uidsMap.RelayIds[relayId].Uid) } turnout.ElectronicComponentGroups = append(turnout.ElectronicComponentGroups, &proto.ElectronicComponentGroup{ Code: group.Code, ComponentIds: componentIds, }) } case graphicData.RelatedRef_signal: signal := signalMap[GenerateElementUid(city, lineId, []string{station}, relationship.Code)] if signal == nil { continue } //信号机只有一个组合类型 if len(relationship.Combinationtypes) != 1 { panic(fmt.Sprintf("信号机[%s]须有一个组合类型", signal.Id)) } group := relationship.Combinationtypes[0] var componentIds []string for _, relayId := range group.RefRelays { if uidsMap.RelayIds[relayId] == nil { continue } componentIds = append(componentIds, uidsMap.RelayIds[relayId].Uid) } signal.Code = group.Code signal.ElectronicComponentGroups = append(signal.ElectronicComponentGroups, &proto.ElectronicComponentGroup{ Code: group.Code, ComponentIds: componentIds, }) case graphicData.RelatedRef_station: station := stationMap[GenerateElementUid(city, lineId, nil, relationship.Code)] if station == nil { continue } for _, group := range relationship.Combinationtypes { d := &proto.ElectronicGroup{Code: group.Code} for _, relayId := range group.RefRelays { if uidsMap.RelayIds[relayId] == nil { continue } d.Components = append(d.Components, &proto.ElectronicComponent{ Id: uidsMap.RelayIds[relayId].Uid, DeviceType: proto.DeviceType_DeviceType_Relay, }) } station.ElectronicGroup = append(station.ElectronicGroup, d) } case graphicData.RelatedRef_ScreenDoor: psd, ok := psdMap[GenerateElementUid(city, lineId, nil, relationship.Code)] if !ok { continue } for _, group := range relationship.Combinationtypes { var componentIds []string for _, relayId := range group.RefRelays { if uidsMap.RelayIds[relayId] == nil { continue } componentIds = append(componentIds, uidsMap.RelayIds[relayId].Uid) } psd.ElectronicComponentGroups = append(psd.ElectronicComponentGroups, &proto.ElectronicComponentGroup{ Code: group.Code, ComponentIds: componentIds, }) } } } //门控箱 for _, mkx := range repo.Mkxs { platform := platformMap[psdMap[mkx.PsdId].GetPlatformId()] station := stationMap[platform.GetStationId()] var s string if strings.Contains(platform.GetCode(), "上行") { s = "S" } else if strings.Contains(platform.GetCode(), "下行") { s = "X" } else { continue } for _, group := range station.ElectronicGroup { if group.Code == "MKX" { var componentIds []string for _, component := range group.Components { relay := relayMap[component.Id] if strings.Contains(relay.GetCode(), s) { componentIds = append(componentIds, relay.Id) } } mkx.ElectronicComponentGroups = append(mkx.ElectronicComponentGroups, &proto.ElectronicComponentGroup{ Code: group.Code, ComponentIds: componentIds, }) } } } } func convertRelayModel(modelType graphicData.Relay_ModelType) proto.Relay_Model { switch modelType { case graphicData.Relay_Unknown: return proto.Relay_Unknown case graphicData.Relay_JPXC_1000: return proto.Relay_JPXC_1000 case graphicData.Relay_JPXC_1700: return proto.Relay_JPXC_1700 case graphicData.Relay_JWJXC_480: return proto.Relay_JWJXC_480 case graphicData.Relay_JWJXC_H125_80: return proto.Relay_JWJXC_H125_80 case graphicData.Relay_JWXC_1700: return proto.Relay_JWXC_1700 case graphicData.Relay_JWXC_H340: return proto.Relay_JWXC_H340 case graphicData.Relay_JYJXC_160_260: return proto.Relay_JYJXC_160_260 case graphicData.Relay_JZXC_H18: return proto.Relay_JZXC_H18 default: panic(fmt.Sprintf("意料之外的继电器型号:%s", modelType)) } } func fillProtoRepository(repo *proto.Repository, storage *graphicData.RtssGraphicStorage, mapId int32) { repo.MainCoordinateSystem = storage.UniqueIdPrefix.MainCoordinateSystem 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 } } var switchMachineType proto.Turnout_SwitchMachineType switch data.SwitchMachineType { case graphicData.Turnout_ZDJ9_Single: switchMachineType = proto.Turnout_ZDJ9_Single case graphicData.Turnout_ZDJ9_Double: switchMachineType = proto.Turnout_ZDJ9_Double } turnout := &proto.Turnout{ Id: data.Common.Id, Km: km, ADevicePort: convertDevicePort(data.PaRef), BDevicePort: convertDevicePort(data.PbRef), CDevicePort: convertDevicePort(data.PcRef), SwitchMachineType: switchMachineType, } 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, Model: convertToProtoSignalModel(data.Mt), } 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 } curveKsMap := make(map[string]*graphicData.CurvatureKiloMarker) for _, data := range storage.CurvatureKiloMarker { curveKsMap[data.Common.Id] = data } 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)) } for _, data := range storage.KilometerConvertList { repo.KilometerConverts = append(repo.KilometerConverts, &proto.KilometerConvert{ KmA: convertKm(data.KmA), KmB: convertKm(data.KmB), SameTrend: data.SameTrend, }) } // 初始化站场图按钮 for _, data := range storage.EsbButtons { repo.Buttons = append(repo.Buttons, &proto.Button{ Id: uidsMap.ButtonIds[data.Common.Id].Uid, Code: data.Code, ButtonType: proto.Button_Reset_Press, }) } // 车站关联关系 relateMap := make(map[string]*graphicData.StationRelateDevice) for _, data := range storage.StationRelateDeviceList { relateMap[data.Code] = data } // 处理车站信息 for _, data := range storage.Stations { station := &proto.Station{ Id: uidsMap.StationIds[data.Common.Id].Uid, Code: data.StationName, } // 关联车站的设备 refs, ok := relateMap[data.Code] if ok { for _, c := range refs.Combinationtypes { group := &proto.ElectronicGroup{Code: c.Code} for _, d := range c.RefDevices { var comp *proto.ElectronicComponent if uidsMap.ButtonIds[d] != nil { // 目前只处理按钮 comp = &proto.ElectronicComponent{ Id: uidsMap.ButtonIds[d].Uid, DeviceType: proto.DeviceType_DeviceType_Button, } } else { continue } group.Components = append(group.Components, comp) } station.ElectronicGroup = append(station.ElectronicGroup, group) } } // 处理车站关联IBP的设备 handlerIBPDeviceToStation(station, repo, data.RefIbpMapCode) repo.Stations = append(repo.Stations, station) } //门控箱 gateBoxMap := make(map[string]*proto.Mkx) for _, data := range storage.GateBoxs { mkx, ok := gateBoxMap[data.RefScreenDoor] if !ok { mkx = &proto.Mkx{ Id: uidsMap.GateBoxIds[data.Common.Id].Uid, PsdId: uidsMap.PsdIds[data.RefScreenDoor].Uid, } repo.Mkxs = append(repo.Mkxs, mkx) gateBoxMap[data.RefScreenDoor] = mkx } pslMapId := QueryGiId(data.RefGatedBoxMapCode) pslStorage := QueryGiData[*graphicData.PslGraphicStorage](pslMapId) for _, button := range pslStorage.PslButtons { repoButton := &proto.Button{ Id: uidsMap.GateBoxIds[data.Common.Id].Uid + "_" + button.Code, Code: button.Code, ButtonType: proto.Button_Reset_Press, HasLight: true, } repo.Buttons = append(repo.Buttons, repoButton) switch button.Code { case "PCB": mkx.PcbButtonIds = append(mkx.PcbButtonIds, repoButton.Id) case "POB": mkx.PobButtonIds = append(mkx.PobButtonIds, repoButton.Id) case "PAB": mkx.PabButtonIds = append(mkx.PabButtonIds, repoButton.Id) } } } //站台 platformMap := make(map[string]*graphicData.Platform) for _, data := range storage.Platforms { platformMap[data.Common.Id] = data platform := &proto.Platform{ Id: uidsMap.PlatformIds[data.Common.Id].Uid, Code: data.Code, } repo.Platforms = append(repo.Platforms, platform) platform.StationId = uidsMap.StationIds[data.RefStationId].Uid platform.PhysicalSectionId = uidsMap.PhysicalSectionIds[data.RefSectionId].Uid } //屏蔽门 for _, data := range storage.ScreenDoors { psd := &proto.Psd{ Id: uidsMap.PsdIds[data.Common.Id].Uid, PlatformId: uidsMap.PlatformIds[data.RefPlatformId].Uid, } repo.Psds = append(repo.Psds, psd) } } // 将IBP的设备关联到车站中 func handlerIBPDeviceToStation(station *proto.Station, repo *proto.Repository, ibpMap string) { storage := getStorageIBPMapData(ibpMap) if storage == nil { return } deviceMap := make(map[string]*proto.ElectronicComponent) // 对应的设备类型 for _, data := range storage.IbpButtons { // 处理按钮 buttonType := proto.Button_NO_Reset_Press if data.IsSelfReset { buttonType = proto.Button_Reset_Press } b := &proto.Button{ Id: station.Id + "_button_" + data.Code, Code: data.Code, ButtonType: buttonType, HasLight: data.HasLight, } deviceMap[data.Common.Id] = &proto.ElectronicComponent{ Id: b.Id, DeviceType: proto.DeviceType_DeviceType_Button, } repo.Buttons = append(repo.Buttons, b) } for _, data := range storage.IbpKeys { // 钥匙 b := &proto.Key{ Id: station.Id + "_key_" + data.Code, Code: data.Code, Gear: 2, } deviceMap[data.Common.Id] = &proto.ElectronicComponent{ Id: b.Id, DeviceType: proto.DeviceType_DeviceType_Key, } repo.Keys = append(repo.Keys, b) } for _, data := range storage.IbpAlarms { // 报警器 b := &proto.Alarm{ Id: station.Id + "_alarm_" + data.Code, Code: data.Code, } deviceMap[data.Common.Id] = &proto.ElectronicComponent{ Id: b.Id, DeviceType: proto.DeviceType_DeviceType_Alarm, } repo.Alarms = append(repo.Alarms, b) } empGroup := &proto.ElectronicGroup{Code: "EMP"} spksGroup := &proto.ElectronicGroup{Code: "SPKS"} for _, data := range storage.IbpLights { // 指示灯, b := &proto.Light{ Id: station.Id + "_light_" + data.Code, Code: data.Code, } repo.Lights = append(repo.Lights, b) // 存入组合类型的 c := &proto.ElectronicComponent{ Id: b.Id, DeviceType: proto.DeviceType_DeviceType_Light, } switch data.Code { case "X紧急关闭", "S紧急关闭": empGroup.Components = append(empGroup.Components, c) case "启用状态_SPKS1", "启用状态_SPKS3", "启用状态_SPKS2", "启用状态_SPKS4": spksGroup.Components = append(spksGroup.Components, c) } } for _, data := range storage.IbpRelatedDevices { // 组信息 for _, c := range data.Combinationtypes { var group *proto.ElectronicGroup switch c.Code { case "EMP": group = empGroup case "SPKS": group = spksGroup default: continue } for _, d := range c.RefDevices { deviceType, ok := deviceMap[d] if !ok { slog.Debug("IBP组合类型类型不确定id:%s", d) continue } group.Components = append(group.Components, deviceType) } } } station.ElectronicGroup = append(station.ElectronicGroup, empGroup, spksGroup) } 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 convertToProtoSignalModel(gSmt graphicData.Signal_Model) proto.Signal_Model { switch gSmt { case graphicData.Signal_HLU: return proto.Signal_HLU case graphicData.Signal_HL: return proto.Signal_HL case graphicData.Signal_HLU_FU: return proto.Signal_HLU_FU case graphicData.Signal_HLU_FL: return proto.Signal_HLU_FL case graphicData.Signal_AB: return proto.Signal_AB case graphicData.Signal_HBU: return proto.Signal_HBU default: panic(fmt.Sprintf("graphicData.Signal_Model[%d]无法映射到proto.Signal_Model", gSmt)) } }