package repository import ( "errors" "fmt" "log/slog" "math" "strconv" "strings" "joylink.club/rtsssimulation/repository/model/proto" "joylink.club/rtsssimulation/util/number" ) var repositoryMap = make(map[string]*Repository) func BuildRepository(source *proto.Repository) (*Repository, error) { errMsg := baseCheck(source) if len(errMsg) != 0 { return nil, errors.New(strings.Join(errMsg, "\n")) } repository := newRepository(source.Id, source.Version) err := buildModels(source, repository) if err != nil { return nil, err } slog.Info("基础模型构建完毕") err = buildModelRelationship(source, repository) slog.Info("模型关系构建完毕") if err != nil { return nil, err } err = buildLinksAndRelate(repository) slog.Info("构建Link并与模型关联完毕") if err != nil { return nil, err } err = buildIscsModelRelationship(source, repository) if err != nil { return nil, err } // repositoryMap[buildRepositoryKey(source.Id, source.Version)] = repository return repository, err } func FindRepository(id string, version string) *Repository { return repositoryMap[buildRepositoryKey(id, version)] } func buildRepositoryKey(id string, version string) string { return id + "_" + version } func buildModels(source *proto.Repository, repository *Repository) error { for _, protoData := range source.KilometerConverts { repository.addKilometerConvert(protoData) } for _, protoData := range source.Stations { m := NewStation(protoData.Id, protoData.Code) _, ok := repository.stationMap[m.Id()] if ok { return fmt.Errorf("id=%s的车站数据重复", protoData.Id) } repository.stationMap[m.Id()] = m } for _, protoData := range source.PhysicalSections { m := NewPhysicalSection(protoData.Id) repository.physicalSectionMap[m.Id()] = m } for _, protoData := range source.CheckPoints { m := NewCheckPoint(protoData.Id, protoData.Km, protoData.Type) repository.checkPointMap[m.Id()] = m } for _, protoData := range source.Turnouts { m := NewTurnout(protoData.Id, protoData.Km, protoData.SwitchMachineType) repository.turnoutMap[m.Id()] = m } for _, protoData := range source.Signals { m := NewSignal(protoData.Id, protoData.Km, protoData.Code, protoData.Model) repository.signalMap[m.Id()] = m } for _, protoData := range source.Transponders { m := NewTransponder(protoData.Id, protoData.Km, protoData.FixedTelegram, protoData.FixedUserTelegram, protoData.Type) repository.responderMap[m.Id()] = m } for _, protoData := range source.Slopes { m := NewSlope(protoData.Id, protoData.Kms, protoData.Degree) repository.slopeMap[m.Id()] = m } for _, protoData := range source.SectionalCurvatures { m := NewSectionalCurvature(protoData.Id, protoData.Kms, protoData.Radius) repository.sectionalCurvatureMap[m.Id()] = m } for _, protoData := range source.Relays { s, ok := repository.stationMap[protoData.StationId] if !ok { return fmt.Errorf("id=%s的继电器所属车站不存在,车站id=%s", protoData.Id, protoData.StationId) } m := newRelay(protoData.Id, protoData.Code, protoData.Model, s) repository.relayMap[m.Id()] = m } for _, protoData := range source.PhaseFailureProtectors { m := newPhaseFailureProtector(protoData.Id, protoData.Code) repository.phaseFailureProtectorMap[m.Id()] = m } for _, protoData := range source.Buttons { m := NewButton(protoData.Id, protoData.Code, protoData.ButtonType) if protoData.HasLight { m.SetLight() } repository.buttonMap[m.Id()] = m } for _, protoData := range source.Psds { m := newPsd(protoData.Id, protoData.AsdAmount, protoData.AsdGroups) repository.psdMap[m.Id()] = m } for _, protoData := range source.Lights { m := NewLight(protoData.Id, protoData.Code) repository.lightMap[m.Id()] = m } for _, protoData := range source.Alarms { m := NewAlarm(protoData.Id, protoData.Code) repository.alarmMap[m.Id()] = m } for _, protoData := range source.Mkxs { m := NewMkx(protoData.Id) repository.mkxMap[m.Id()] = m } for _, protoData := range source.Keys { m := NewKey(protoData.Id, protoData.Code, protoData.Gear) repository.keyMap[m.Id()] = m } for _, protoData := range source.Platforms { m := NewPlatform(protoData.Id, protoData.Code) repository.platformMap[m.Id()] = m } err := repository.generateCoordinateInfo(source.MainCoordinateSystem) if err != nil { return err } for _, protoData := range source.CentralizedStationRefs { repository.centralizedMap[protoData.StationId] = protoData } // err = buildIscsModels(source, repository) // return err } func buildModelRelationship(source *proto.Repository, repository *Repository) error { err := buildCheckPointRelationShip(source, repository) if err != nil { return err } err = buildPhysicalSectionRelationShip(source, repository) if err != nil { return err } err = buildTurnoutRelationShip(source, repository) if err != nil { return err } err = buildSignalRelationShip(source, repository) if err != nil { return err } err = buildResponderRelationShip(source, repository) if err != nil { return err } err = buildPsdRelationShip(source, repository) if err != nil { return err } err = buildMkxRelationShip(source, repository) if err != nil { return err } err = completeTurnoutPortKm(repository) if err != nil { return err } err = buildStationRelationShip(source, repository) if err != nil { return err } err = buildPlatformRelationShip(source, repository) if err != nil { return err } err = buildCentralizedStationRelationShip(source, repository) if err != nil { return err } return err } func buildPlatformRelationShip(source *proto.Repository, repo *Repository) error { for _, protoData := range source.Platforms { platform := repo.platformMap[protoData.Id] station := repo.stationMap[protoData.StationId] if station == nil { return fmt.Errorf("站台[id:%s]关联的车站[id:%s]不存在", platform.Id(), protoData.StationId) } platform.station = station } return nil } func buildCentralizedStationRelationShip(source *proto.Repository, repo *Repository) error { for _, stationRef := range source.CentralizedStationRefs { stationId := stationRef.StationId repo.centralizedMap[stationId] = stationRef } return nil } func buildMkxRelationShip(source *proto.Repository, repo *Repository) error { for _, protoData := range source.Mkxs { mkx := repo.mkxMap[protoData.Id] mkx.psd = repo.psdMap[protoData.GetPsdId()] mkx.pcb = repo.buttonMap[protoData.GetPcbButtonId()] mkx.pob = repo.buttonMap[protoData.GetPobButtonId()] mkx.pab = repo.buttonMap[protoData.GetPabButtonId()] mkx.pcbj = repo.relayMap[protoData.GetPcbjId()] mkx.pobj = repo.relayMap[protoData.GetPobjId()] mkx.pabj = repo.relayMap[protoData.GetPabjId()] } return nil } func buildPsdRelationShip(source *proto.Repository, repo *Repository) error { for _, protoData := range source.Psds { psd := repo.psdMap[protoData.Id] platform := repo.platformMap[protoData.PlatformId] if platform == nil { return fmt.Errorf("屏蔽门[id:%s]关联的站台[id:%s]不存在", psd.Id(), protoData.PlatformId) } psd.platform = platform for _, group := range protoData.ElectronicComponentGroups { var components []IGroupedElectronicComponent for _, id := range group.GetComponentIds() { if relay := repo.relayMap[id]; relay != nil { components = append(components, relay) } } psd.componentGroups = append(psd.componentGroups, &ElectronicComponentGroup{ code: group.Code, components: components, }) } } return nil } // 补全道岔端口的公里标 func completeTurnoutPortKm(repo *Repository) error { ports := []proto.Port{proto.Port_A, proto.Port_B, proto.Port_C} for _, turnout := range repo.turnoutMap { for _, port := range ports { tpKm := turnout.findBoundaryKmByPort(port) if tpKm == nil { dp := turnout.findDevicePortByPort(port) otherTurnout := repo.turnoutMap[dp.Device().Id()] otherKm, err := ConvertKilometer(repo, otherTurnout.km, turnout.km.CoordinateSystem) if err != nil { return err } km := &proto.Kilometer{ Value: (turnout.km.Value + otherKm.Value) / 2, CoordinateSystem: turnout.km.CoordinateSystem, Direction: turnout.km.Direction, } err = turnout.bindBoundaryKm(km, port) if err != nil { return err } err = otherTurnout.bindBoundaryKm(km, dp.Port()) if err != nil { return err } } } } return nil } func buildCheckPointRelationShip(source *proto.Repository, repo *Repository) error { for _, protoData := range source.CheckPoints { cp := repo.checkPointMap[protoData.Id] isBoundary := protoData.Type == proto.CheckPointType_Boundary for _, protoDp := range protoData.DevicePorts { switch protoDp.DeviceType { case proto.DeviceType_DeviceType_PhysicalSection: section := repo.physicalSectionMap[protoDp.DeviceId] if isBoundary { //是区段边界 err := section.bindBoundaryKm(cp.km, protoDp.Port) if err != nil { return err } } else { //检测点关联区段端口 cp.bindDevicePort(&PhysicalSectionPort{ section: section, port: protoDp.Port, }) //区段关联检测点 section.bindDevices(cp) //如果区段边界未设置 if section.findBoundaryKmByPort(protoDp.Port) == nil { if err := section.bindBoundaryKm(cp.km, protoDp.Port); err != nil { return err } } } case proto.DeviceType_DeviceType_Turnout: turnout := repo.turnoutMap[protoDp.DeviceId] if isBoundary { //是区段边界 err := turnout.bindBoundaryKm(cp.km, protoDp.Port) if err != nil { return err } } else { //检测点关联道岔端口 cp.bindDevicePort(&TurnoutPort{ turnout: turnout, port: protoDp.Port, }) //道岔关联检测点 turnout.bindDevice(cp, protoDp.Port) //如果区段边界未设置 if turnout.findBoundaryKmByPort(protoDp.Port) == nil { if err := turnout.bindBoundaryKm(cp.km, protoDp.Port); err != nil { return err } } } } } } return nil } func buildResponderRelationShip(source *proto.Repository, repository *Repository) error { for _, protoData := range source.Transponders { responder := repository.responderMap[protoData.Id] //应答器和区段相互关联 if protoData.SectionId != "" { interrelateResponderAndPhysicalSection(responder, repository.physicalSectionMap[protoData.SectionId]) } //应答器和道岔相互关联 tp := protoData.TurnoutPort if tp != nil { if tp.DeviceType != proto.DeviceType_DeviceType_Turnout { return fmt.Errorf("应答器[%s]关联的[%s-%s-%s]并非道岔端口", responder.Id(), tp.DeviceId, tp.DeviceType, tp.Port) } interrelateResponderAndTurnout(responder, repository.turnoutMap[tp.DeviceId], tp.Port) } } return nil } func buildSignalRelationShip(source *proto.Repository, repository *Repository) error { for _, protoData := range source.Signals { signal := repository.signalMap[protoData.Id] //信号机和区段相互关联 if protoData.SectionId != "" { interrelateSignalAndPhysicalSection(signal, repository.physicalSectionMap[protoData.SectionId]) } //信号机和道岔相互关联 tp := protoData.TurnoutPort if tp != nil { if tp.DeviceType != proto.DeviceType_DeviceType_Turnout { return fmt.Errorf("信号机[%s]关联的[%s-%s-%s]并非道岔端口", signal.Id(), tp.DeviceId, tp.DeviceType, tp.Port) } interrelateSignalAndTurnout(signal, repository.turnoutMap[tp.DeviceId], tp.Port) } //关联继电器组 for _, group := range protoData.ElectronicComponentGroups { var components []IGroupedElectronicComponent for _, id := range group.GetComponentIds() { if relay := repository.relayMap[id]; relay != nil { components = append(components, relay) } } signal.componentGroups = append(signal.componentGroups, &ElectronicComponentGroup{ code: group.Code, components: components, }) } } return nil } func buildTurnoutRelationShip(source *proto.Repository, repo *Repository) error { for _, protoData := range source.Turnouts { turnout := repo.turnoutMap[protoData.Id] if protoData.ADevicePort == nil && protoData.BDevicePort == nil && protoData.CDevicePort == nil { return fmt.Errorf("道岔[%s]无连接设备", protoData.Id) } err := buildTurnoutPortRelation(repo, turnout, proto.Port_A, protoData.ADevicePort) if err != nil { return err } err = buildTurnoutPortRelation(repo, turnout, proto.Port_B, protoData.BDevicePort) if err != nil { return err } err = buildTurnoutPortRelation(repo, turnout, proto.Port_C, protoData.CDevicePort) if err != nil { return err } //关联继电器组 for _, group := range protoData.ElectronicComponentGroups { var components []IGroupedElectronicComponent for _, id := range group.GetComponentIds() { if relay := repo.relayMap[id]; relay != nil { components = append(components, relay) } else if pfp := repo.phaseFailureProtectorMap[id]; pfp != nil { components = append(components, pfp) } } turnout.componentGroups = append(turnout.componentGroups, &ElectronicComponentGroup{ code: group.Code, components: components, }) } } return nil } func buildTurnoutPortRelation(repo *Repository, turnout *Turnout, port proto.Port, protoDp *proto.DevicePort) error { if protoDp == nil { return nil } model, err := repo.FindModel(protoDp.DeviceId, protoDp.DeviceType) if err != nil { return err } dp, err := buildDevicePort(model, protoDp.Port) if err != nil { return err } err = turnout.bindDevicePort(port, dp) return err } func buildPhysicalSectionRelationShip(source *proto.Repository, repository *Repository) error { for _, protoData := range source.PhysicalSections { section := repository.physicalSectionMap[protoData.Id] //A端关联 if protoData.ADevicePort != nil { err := buildSectionPortRelation(repository, section, proto.Port_A, protoData.ADevicePort) if err != nil { return err } } //B端关联 if protoData.BDevicePort != nil { err := buildSectionPortRelation(repository, section, proto.Port_B, protoData.BDevicePort) if err != nil { return err } } //道岔关联 for _, turnoutId := range protoData.TurnoutIds { turnout := repository.turnoutMap[turnoutId] if turnout == nil { return fmt.Errorf("id[%s]的道岔不存在", turnoutId) } section.bindTurnouts(turnout) turnout.section = section } //关联联锁集中站 section.centralizedStation = protoData.CentralizedStation } return nil } // 构建物理区段指定端口的关联关系。区段{section}的{port}端口关联{protoDp} func buildSectionPortRelation(repo *Repository, section *PhysicalSection, port proto.Port, protoDp *proto.DevicePort) error { model, err := repo.FindModel(protoDp.DeviceId, protoDp.DeviceType) if err != nil { return err } dp, err := buildDevicePort(model, protoDp.Port) if err != nil { return err } err = section.bindDevicePort(port, dp) if err != nil { return err } return nil } func buildDevicePort(device Identity, port proto.Port) (DevicePort, error) { var dp DevicePort switch device.Type() { case proto.DeviceType_DeviceType_PhysicalSection: section := device.(*PhysicalSection) dp = &PhysicalSectionPort{ section: section, port: port, } case proto.DeviceType_DeviceType_Turnout: turnout := device.(*Turnout) dp = &TurnoutPort{ turnout: turnout, port: port, } case proto.DeviceType_DeviceType_Link: link := device.(*Link) dp = &LinkPort{ link: link, port: port, } default: return nil, fmt.Errorf("[%s]类型设备没有端口", device.Type()) } return dp, nil } // 相互关联道岔和检测点 func interrelateTurnoutAndCheckPoints(turnout *Turnout, point *CheckPoint, port proto.Port) { turnout.bindDevice(point, port) point.bindDevicePort(&TurnoutPort{ turnout: turnout, port: port, }) } // 相互关联物理区段和信号机 func interrelateSignalAndPhysicalSection(signal *Signal, section *PhysicalSection) { section.bindDevices(signal) //signal.bindSection(section) } // 相互关联信号机和道岔 func interrelateSignalAndTurnout(signal *Signal, turnout *Turnout, port proto.Port) { //signal.bindTurnoutPort(TurnoutPort{ // turnout: turnout, // port: port, //}) turnout.bindDevice(signal, port) } // 相互关联应答器和物理区段 func interrelateResponderAndPhysicalSection(responder *Transponder, section *PhysicalSection) { //responder.bindSection(section) section.bindDevices(responder) } // 相互关联应答器和道岔 func interrelateResponderAndTurnout(responder *Transponder, turnout *Turnout, port proto.Port) { //responder.bindTurnoutPort(TurnoutPort{ // turnout: turnout, // port: port, //}) turnout.bindDevice(responder, port) } func buildLinksAndRelate(repo *Repository) error { //构建link err := buildLinks(repo) if err != nil { return err } //Slope关联Link err = slopeRelateLink(repo) if err != nil { return err } //SectionalCurvature关联Link err = sectionalCurvatureRelateLink(repo) if err != nil { return err } return nil } func buildLinks(repo *Repository) error { visitedTurnoutPortMap := make(map[string]bool) allTurnouts := repo.TurnoutList() linkIdGenerator := 1 for { //找一个未遍历过的道岔端口 startTp := getATurnoutPort(allTurnouts, visitedTurnoutPortMap) if startTp == nil { break } //以始端道岔的公里标作为Link零点的公里标 linkZeroKm := startTp.turnout.km //创建基础Link link := NewLink(strconv.Itoa(linkIdGenerator)) //由于发给动力学的link的id是数字,所以这里也用数字 linkIdGenerator++ //以此道岔端口作为Link的A端节点 interrelateLinkAndTurnout(repo, linkZeroKm, startTp, &LinkPort{link, proto.Port_A}) //沿着道岔端口方向,一直寻找到轨道尽头或者下一个道岔端口。构建并关联中间的设备在link上的位置 endTp, endKm, err := findEndTurnoutPortOrEndKm(repo, link, startTp, linkZeroKm) if err != nil { return err } //以下一个道岔端口作为Link的B端节点 if endTp != nil { visitedTurnoutPortMap[buildTurnoutPortKey(endTp)] = true endKm = endTp.turnout.km interrelateLinkAndTurnout(repo, linkZeroKm, endTp, &LinkPort{link, proto.Port_B}) } else { link.bindKm(endKm, proto.Port_B) } //计算Link长度 convertedKm, err := ConvertKilometer(repo, endKm, linkZeroKm.CoordinateSystem) if err != nil { return err } link.length = int64(math.Abs(float64(convertedKm.Value - linkZeroKm.Value))) //构建Link两端物理道岔区段在Link上的范围 buildLinkRangeOfTurnoutPhysicalSection(link) repo.linkMap[link.Id()] = link } return nil } func findEndTurnoutPortOrEndKm(repo *Repository, link *Link, startTp *TurnoutPort, baseKm *proto.Kilometer) (*TurnoutPort, *proto.Kilometer, error) { var endTp *TurnoutPort var endKm *proto.Kilometer var err error visitedModelMap := make(map[string]bool) var currentDp DevicePort = startTp devices := startTp.turnout.findDevicesByPort(startTp.port) for { //遍历设备并构建、关联其在Link上的位置 err = relateDevicesAndLink(repo, link, baseKm, visitedModelMap, devices...) if err != nil { return nil, nil, err } //顺着端口寻找下一个设备端口 var nextDp DevicePort switch currentDp.Device().Type() { case proto.DeviceType_DeviceType_PhysicalSection: section := currentDp.Device().(*PhysicalSection) relatePhysicalSectionAndLink(repo, section, link, baseKm) nextDp = section.findOtherDevicePort(currentDp.Port()) if nextDp == nil { endKm = section.findOtherBoundaryKmByPort(currentDp.Port()) } case proto.DeviceType_DeviceType_Turnout: turnout := currentDp.Device().(*Turnout) nextDp = turnout.findDevicePortByPort(currentDp.Port()) if nextDp == nil { endKm = turnout.findBoundaryKmByPort(currentDp.Port()) } } //根据下一个端口设备的信息决定是否结束循环 if nextDp == nil { break } currentDp = nextDp var nextIsTp bool switch nextDp.Device().Type() { case proto.DeviceType_DeviceType_PhysicalSection: devices = nextDp.Device().(*PhysicalSection).devices case proto.DeviceType_DeviceType_Turnout: nextIsTp = true endTp = nextDp.(*TurnoutPort) devices = nextDp.Device().(*Turnout).findDevicesByPort(nextDp.Port()) err = relateDevicesAndLink(repo, link, baseKm, visitedModelMap, devices...) if err != nil { return nil, nil, err } } if nextIsTp { break } } return endTp, endKm, err } // 关联正常(非道岔)物理区段与Link func relatePhysicalSectionAndLink(repo *Repository, section *PhysicalSection, link *Link, baseKm *proto.Kilometer) { link.bindPhysicalSections(section) aKm, _ := ConvertKilometer(repo, section.aKm, baseKm.CoordinateSystem) bKm, _ := ConvertKilometer(repo, section.bKm, baseKm.CoordinateSystem) aOffset := number.Abs(aKm.Value - baseKm.Value) bOffset := number.Abs(bKm.Value - baseKm.Value) section.aLinkPosition = &LinkPosition{ link: link, offset: aOffset, } section.bLinkPosition = &LinkPosition{ link: link, offset: bOffset, } section.bindLinkRange(link, aOffset, bOffset) } func getATurnoutPort(turnouts []*Turnout, visitedTurnoutMap map[string]bool) *TurnoutPort { portSlice := []proto.Port{proto.Port_A, proto.Port_B, proto.Port_C} for _, turnout := range turnouts { for _, port := range portSlice { tp := &TurnoutPort{ turnout: turnout, port: port, } key := buildTurnoutPortKey(tp) if !visitedTurnoutMap[key] { visitedTurnoutMap[key] = true return tp } } } return nil } func buildTurnoutPortKey(tp *TurnoutPort) string { return fmt.Sprintf("%s-%s", tp.turnout.Id(), tp.port) } func relateDevicesAndLink(repo *Repository, link *Link, startKm *proto.Kilometer, visitedModelMap map[string]bool, devices ...Identity) error { for _, device := range devices { if visitedModelMap[device.Id()] { continue } km := findModelKm(device) if km == nil || km.CoordinateSystem == "" { continue } convertedKm, err := ConvertKilometer(repo, km, startKm.CoordinateSystem) if err != nil { return err } offset := int64(math.Abs(float64(convertedKm.Value - startKm.Value))) linkPosition := &LinkPosition{ link: link, offset: offset, } switch device.Type() { case proto.DeviceType_DeviceType_CheckPoint: device.(*CheckPoint).bindLinkPosition(linkPosition) case proto.DeviceType_DeviceType_Signal: device.(*Signal).bindLinkPosition(linkPosition) case proto.DeviceType_DeviceType_Transponder: device.(*Transponder).bindLinkPosition(linkPosition) } link.bindDevices(device) } return nil } // 互相关联Link和道岔及道岔区段 func interrelateLinkAndTurnout(repo *Repository, linkZeroKm *proto.Kilometer, tp *TurnoutPort, linkPort *LinkPort) { //关联Link和道岔 tp.turnout.bindLinkPort(tp.port, linkPort) linkPort.link.bindTurnoutPort(linkPort.port, tp) tpKm := tp.turnout.findBoundaryKmByPort(tp.port) tpKm, _ = ConvertKilometer(repo, tpKm, linkZeroKm.CoordinateSystem) offset := number.Abs(tpKm.Value - linkZeroKm.Value) tp.turnout.bindLinkPosition(tp.port, &LinkPosition{ link: linkPort.link, offset: offset, }) } // 构建Link关联道岔物理区段在Link上的范围 func buildLinkRangeOfTurnoutPhysicalSection(link *Link) { //解决[两道岔的道岔物理区段,两道岔间的Link会产生两个LinkRange]的问题 if link.aRelation != nil && link.bRelation != nil && link.aRelation.turnout.section == link.bRelation.turnout.section { section := link.aRelation.turnout.section section.bindLinkRange(link, 0, link.length) return } if link.aRelation != nil { aSection := link.aRelation.turnout.section //Link A端的关联的道岔物理区段 aLinkPosition := link.aRelation.turnout.FindLinkPositionByPort(link.aRelation.port) //Link A端道岔端点在Link上的位置 aSection.bindLinkRange(link, 0, aLinkPosition.offset) } if link.bRelation != nil { bSection := link.bRelation.turnout.section //Link A端的关联的道岔物理区段 bLinkPosition := link.bRelation.turnout.FindLinkPositionByPort(link.bRelation.port) //Link A端道岔端点在Link上的位置 bSection.bindLinkRange(link, bLinkPosition.offset, link.length) } } func findModelKm(model Identity) *proto.Kilometer { switch model.Type() { case proto.DeviceType_DeviceType_Signal: return model.(*Signal).km case proto.DeviceType_DeviceType_Transponder: return model.(*Transponder).km case proto.DeviceType_DeviceType_CheckPoint: return model.(*CheckPoint).km case proto.DeviceType_DeviceType_Turnout: return model.(*Turnout).km } return nil } func slopeRelateLink(repo *Repository) error { slopeMap := repo.slopeMap for _, slope := range slopeMap { start, end, err := calculateLinkSegment(repo, slope.kms[0], slope.kms[1]) if err != nil { return err } slope.bindStartLinkPosition(start) slope.bindEndLinkPosition(end) } return nil } func calculateLinkSegment(repo *Repository, startKm *proto.Kilometer, endKm *proto.Kilometer) (*LinkPosition, *LinkPosition, error) { endKm, err := ConvertKilometer(repo, endKm, startKm.CoordinateSystem) if err != nil { return nil, nil, err } var start *LinkPosition var end *LinkPosition var minKmValue float64 var minKmLink *Link var maxKmValue float64 var maxKmLink *Link for _, link := range repo.linkMap { //将link两端的公里标转为aKm同坐标系的公里标 linkAKm := link.aKm linkBKm := link.bKm if linkAKm.CoordinateSystem != startKm.CoordinateSystem || linkAKm.Direction != startKm.Direction { continue } if linkBKm.CoordinateSystem != startKm.CoordinateSystem || linkBKm.Direction != startKm.Direction { continue } //记录公里标最小/最大的Link newMinKmValue := math.Min(float64(linkAKm.Value), float64(linkBKm.Value)) newMaxKmValue := math.Max(float64(linkAKm.Value), float64(linkBKm.Value)) if minKmLink == nil || newMinKmValue < minKmValue { minKmValue = newMinKmValue minKmLink = link } if maxKmLink == nil || newMaxKmValue > maxKmValue { maxKmValue = newMaxKmValue maxKmLink = link } //若start/end公里标落在link上,构建LinkPosition if number.IsBetween(startKm.Value, linkAKm.Value, linkBKm.Value) { start = &LinkPosition{ link: link, offset: int64(math.Abs(float64(startKm.Value - linkAKm.Value))), } } if number.IsBetween(endKm.Value, linkAKm.Value, linkBKm.Value) { end = &LinkPosition{ link: link, offset: int64(math.Abs(float64(endKm.Value - linkAKm.Value))), } } if start != nil && end != nil { break } } //处理start/end公里标没落在Link上的情况 if start == nil { if startKm.Value < endKm.Value { offset, _ := minKmLink.findEndOffset() start = &LinkPosition{ link: minKmLink, offset: offset, } } else { offset, _ := maxKmLink.findEndOffset() start = &LinkPosition{ link: maxKmLink, offset: offset, } } } if end == nil { if endKm.Value < startKm.Value { offset, _ := minKmLink.findEndOffset() end = &LinkPosition{ link: minKmLink, offset: offset, } } else { offset, _ := maxKmLink.findEndOffset() end = &LinkPosition{ link: maxKmLink, offset: offset, } } } return start, end, nil } func sectionalCurvatureRelateLink(repo *Repository) error { for _, curvature := range repo.sectionalCurvatureMap { start, end, err := calculateLinkSegment(repo, curvature.kms[0], curvature.kms[1]) if err != nil { return err } curvature.bindStartLinkPosition(start) curvature.bindEndLinkPosition(end) } return nil } func buildStationRelationShip(source *proto.Repository, repo *Repository) error { for _, protoData := range source.Stations { station, ok := repo.stationMap[protoData.Id] if !ok { return fmt.Errorf("缺失车站[%s]", protoData.Id) } for _, group := range protoData.ElectronicGroup { if group.Code == "SPKS" { station.spksComponents = append(station.spksComponents, buildRelComponent(repo, group.Components)...) } else if group.Code == "EMP" || strings.HasSuffix(group.Code, "EMPJ") { station.empGroups = append(station.empGroups, &ElectronicComponentGroup{ code: group.Code, components: buildRelComponent(repo, group.Components), }) } else { slog.Warn("未知的车站组合code", group.Code) } } if len(protoData.Deccs) > 0 { // 暂时让逻辑运行起来,后需重构 station.deviceEcc = protoData.Deccs } } return nil } func buildRelComponent(repo *Repository, comps []*proto.ElectronicComponent) []IGroupedElectronicComponent { var components []IGroupedElectronicComponent for _, data := range comps { switch data.DeviceType { case proto.DeviceType_DeviceType_Alarm: components = append(components, repo.alarmMap[data.Id]) case proto.DeviceType_DeviceType_Button: components = append(components, repo.buttonMap[data.Id]) case proto.DeviceType_DeviceType_Relay: components = append(components, repo.relayMap[data.Id]) case proto.DeviceType_DeviceType_Light: components = append(components, repo.lightMap[data.Id]) case proto.DeviceType_DeviceType_Key: components = append(components, repo.keyMap[data.Id]) default: slog.Warn("IBP未绑定【%s】类型设备", data.DeviceType.String()) } } return components }