package repository import ( "errors" "fmt" "joylink.club/rtsssimulation/repository/model/proto" "joylink.club/rtsssimulation/util/number" "math" "strconv" ) var repositoryMap = make(map[string]*Repository) func BuildRepository(source *proto.Repository) (*Repository, error) { errMsg := baseCheck(source) if len(errMsg) != 0 { println("-------------------- 数据异常信息 --------------------") for _, s := range errMsg { println(s) } return nil, errors.New("数据校验未通过") } repository := newRepository(source.Id, source.Version) buildModels(source, repository) err := buildModelRelationship(source, repository) if err != nil { return nil, err } err = buildLinksAndRelate(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) { for _, protoData := range source.KilometerConverts { repository.addKilometerConvert(protoData) } 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) repository.signalMap[m.Id()] = m } for _, protoData := range source.Transponders { m := NewTransponder(protoData.Id, protoData.Km) 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 { m := newRelay(protoData.Id, protoData.Code, protoData.Model) repository.relayMap[m.Id()] = m } } 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 } return err } 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 errors.New(fmt.Sprintf("应答器[%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 errors.New(fmt.Sprintf("信号机[%s]关联的[%s-%s-%s]并非道岔端口", signal.Id(), tp.DeviceId, tp.DeviceType, tp.Port)) } interrelateSignalAndTurnout(signal, repository.turnoutMap[tp.DeviceId], tp.Port) } } return nil } func buildTurnoutRelationShip(source *proto.Repository, repo *Repository) error { for _, protoData := range source.Turnouts { turnout := repo.turnoutMap[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.RelayGroups { var relays []*Relay for _, id := range group.RelayIds { relays = append(relays, repo.relayMap[id]) } turnout.relayGroups = append(turnout.relayGroups, &RelayGroup{ code: group.Code, relays: relays, }) } } return nil } func buildTurnoutPortRelation(repo *Repository, turnout *Turnout, port proto.Port, protoDp *proto.DevicePort) error { model, err := repo.getModel(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 errors.New(fmt.Sprintf("id[%s]的道岔不存在", turnoutId)) } section.bindTurnouts(turnout) turnout.section = section } } return nil } // 构建物理区段指定端口的关联关系。区段{section}的{port}端口关联{protoDp} func buildSectionPortRelation(repo *Repository, section *PhysicalSection, port proto.Port, protoDp *proto.DevicePort) error { model, err := repo.getModel(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, errors.New(fmt.Sprintf("[%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 := 10000 for { //找一个未遍历过的道岔端口 startTp := getATurnoutPort(allTurnouts, visitedTurnoutPortMap) if startTp == nil { break } //以始端道岔的公里标作为公里标换算的基准 baseKm := startTp.turnout.km //创建基础Link link := &Link{ Identity: identity{ id: strconv.Itoa(linkIdGenerator), //由于发给动力学的link的id是数字,所以这里也用数字 deviceType: proto.DeviceType_DeviceType_Link, }} linkIdGenerator++ //以此道岔端口作为Link的A端节点 interrelateLinkAndTurnout(repo, baseKm, startTp, &LinkPort{link, proto.Port_A}) //沿着道岔端口方向,一直寻找到轨道尽头或者下一个道岔端口。构建并关联中间的设备在link上的位置 endTp, endKm, err := findEndTurnoutPortOrEndKm(repo, link, startTp, baseKm) if err != nil { return err } //以下一个道岔端口作为Link的B端节点 if endTp != nil { visitedTurnoutPortMap[buildTurnoutPortKey(endTp)] = true endKm = endTp.turnout.km interrelateLinkAndTurnout(repo, baseKm, endTp, &LinkPort{link, proto.Port_B}) } else { link.bindKm(endKm, proto.Port_B) } //计算Link长度 convertedKm, err := convertKilometer(repo, endKm, baseKm.CoordinateSystem, baseKm.Direction) if err != nil { return err } link.length = int64(math.Abs(float64(convertedKm.Value - baseKm.Value))) 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 } func relatePhysicalSectionAndLink(repo *Repository, section *PhysicalSection, link *Link, baseKm *proto.Kilometer) { link.bindPhysicalSections(section) aKm, _ := convertKilometer(repo, section.aKm, baseKm.CoordinateSystem, baseKm.Direction) bKm, _ := convertKilometer(repo, section.bKm, baseKm.CoordinateSystem, baseKm.Direction) 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, } if aOffset <= bOffset { section.startLinkPosition = section.aLinkPosition section.endLinkPosition = section.bLinkPosition } else { section.startLinkPosition = section.bLinkPosition section.endLinkPosition = section.aLinkPosition } } 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("%v-%s", tp.turnout, 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 { continue } convertedKm, err := convertKilometer(repo, km, startKm.CoordinateSystem, startKm.Direction) 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) } } return nil } func interrelateLinkAndTurnout(repo *Repository, baseKm *proto.Kilometer, tp *TurnoutPort, linkPort *LinkPort) { tp.turnout.bindLinkPort(tp.port, linkPort) linkPort.link.bindTurnoutPort(linkPort.port, tp) tpKm := tp.turnout.findBoundaryKmByPort(tp.port) tpKm, _ = convertKilometer(repo, tpKm, baseKm.CoordinateSystem, baseKm.Direction) offset := number.Abs(tpKm.Value - baseKm.Value) tp.turnout.bindLinkPosition(tp.port, &LinkPosition{ link: linkPort.link, offset: offset, }) } 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, startKm.Direction) 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, err := convertKilometer(repo, link.aKm, startKm.CoordinateSystem, startKm.Direction) if err != nil || linkAKm.Direction != startKm.Direction { continue } linkBKm, err := convertKilometer(repo, link.bKm, startKm.CoordinateSystem, startKm.Direction) if err != nil || 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 }