package repository import ( "fmt" "joylink.club/rtsssimulation/repository/model/proto" "joylink.club/rtsssimulation/util" "math" "strconv" ) var simId = 1 func BuildSimulation(id string, version string) (*Simulation, error) { repository, err := getRepository(id, version) if err != nil { return nil, err } simulation := newSimulation(repository) //构建link err = buildLinks(simulation) if err != nil { return nil, err } //构建Slope的Link区间 err = buildSlopeLinkSegments(simulation) if err != nil { return nil, err } //构建SectionalCurvature的Link区间 err = buildSectionalCurvatureLinkSegments(simulation) if err != nil { return nil, err } return simulation, nil } func getASimulationId() string { id := strconv.Itoa(simId) simId++ return id } type Simulation struct { id string idGenerator int repository *Repository linkMap map[string]*Link devicePositionMap map[string]*DeviceLinkPosition //key为device的id linkNodeMap map[string]*LinkNode //LinkNode的id应该没什么意义,所以此处key用LinkNode中Turnout的id slopeLinkSegmentMap map[string]SlopeLinkSegment sectionalCurvatureLinkSegmentMap map[string]SectionalCurvatureLinkSegment } func newSimulation(repository *Repository) *Simulation { return &Simulation{ id: getASimulationId(), idGenerator: 1, repository: repository, linkMap: make(map[string]*Link), devicePositionMap: make(map[string]*DeviceLinkPosition), linkNodeMap: make(map[string]*LinkNode), slopeLinkSegmentMap: make(map[string]SlopeLinkSegment), sectionalCurvatureLinkSegmentMap: make(map[string]SectionalCurvatureLinkSegment), } } func (s *Simulation) getAId() string { id := strconv.Itoa(s.idGenerator) s.idGenerator++ return id } func buildLinks(sim *Simulation) error { visitedTurnoutPortMap := make(map[string]bool) allTurnouts := sim.repository.TurnoutList() for { //找一个未遍历过的道岔端口 startTp := getATurnoutPort(allTurnouts, visitedTurnoutPortMap) if startTp == nil { break } //以始端道岔的公里标作为公里标换算的基准 baseKm := startTp.turnout.km //创建基础Link link := &Link{Identity: identity{sim.getAId(), proto.DeviceType_DeviceType_Link}} //以此道岔端口作为Link的A端节点 err := buildAndRelateLinkNode(sim, startTp, link, proto.Port_A) if err != nil { return err } //沿着道岔端口方向,一直寻找到轨道尽头或者下一个道岔端口。构建并关联中间的设备在link上的位置 endTp, endKm, err := findEndTurnoutPortOrEndKm(sim, link, startTp, baseKm) if err != nil { return err } //以下一个道岔端口作为Link的B端节点 if endTp != nil { visitedTurnoutPortMap[buildTurnoutPortKey(endTp)] = true endKm = endTp.turnout.km err = buildAndRelateLinkNode(sim, endTp, link, proto.Port_B) if err != nil { return err } } //计算Link长度 convertedKm, err := convertKilometer(sim.repository, endKm, baseKm.CoordinateSystem, baseKm.Direction) if err != nil { return err } link.length = int64(math.Abs(float64(convertedKm.Value - baseKm.Value))) sim.linkMap[link.Id()] = link } return nil } func findEndTurnoutPortOrEndKm(sim *Simulation, 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.getDevicesByPort(startTp.port) for { //遍历设备并构建、关联其在Link上的位置 err = buildAndRelateDeviceLinkPositions(sim, 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) nextDp = section.findOtherDevicePort(currentDp.Port()) if nextDp == nil { endKm = section.findOtherBoundaryKmByPort(currentDp.Port()) } case proto.DeviceType_DeviceType_Turnout: turnout := currentDp.Device().(*Turnout) nextDp = turnout.getDevicePortByPort(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).getDevicesByPort(nextDp.Port()) err = buildAndRelateDeviceLinkPositions(sim, link, baseKm, visitedModelMap, devices...) if err != nil { return nil, nil, err } } if nextIsTp { break } } return endTp, endKm, err } 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 buildAndRelateDeviceLinkPositions(sim *Simulation, 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(sim.repository, km, startKm.CoordinateSystem, startKm.Direction) if err != nil { return err } offset := int64(math.Abs(float64(convertedKm.Value - startKm.Value))) dlps := &DeviceLinkPosition{ device: device, position: LinkPosition{ link: link, offset: offset, }, } sim.devicePositionMap[dlps.device.Id()] = dlps link.bindDevicesLinkPositions(dlps) } return nil } func buildAndRelateLinkNode(sim *Simulation, tp *TurnoutPort, link *Link, port proto.Port) error { ln := sim.linkNodeMap[tp.turnout.Id()] if ln == nil { ln = &LinkNode{ Identity: identity{sim.getAId(), proto.DeviceType_DeviceType_LinkNode}, turnout: tp.turnout, } } //LinkNode关联Link err := ln.bindDevicePort(port, &LinkPort{ link: link, port: port, }) if err != nil { return err } //Link关联LinkNode err = link.bindDevicePort(port, &LinkNodePort{ node: ln, port: tp.port, }) return err } func findModelKm(model Identity) *proto.Kilometer { signal, ok := model.(*Signal) if ok { return signal.km } responder, ok := model.(*Responder) if ok { return responder.km } cp, ok := model.(*CheckPoint) if ok { return cp.km } turnout, ok := model.(*Turnout) if ok { return turnout.km } return nil } func buildSlopeLinkSegments(simulation *Simulation) error { repo := simulation.repository slopeMap := repo.slopeMap for _, slope := range slopeMap { start, end, err := calculateLinkSegment(simulation, slope.kms[0], slope.kms[1]) if err != nil { return err } simulation.slopeLinkSegmentMap[slope.Id()] = SlopeLinkSegment{ slope: slope, start: start, end: end, } } return nil } func calculateLinkSegment(simulation *Simulation, startKm *proto.Kilometer, endKm *proto.Kilometer) (*LinkPosition, *LinkPosition, error) { repo := simulation.repository 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 simulation.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 util.IsBetween(startKm.Value, linkAKm.Value, linkBKm.Value) { start = &LinkPosition{ link: link, offset: int64(math.Abs(float64(startKm.Value - linkAKm.Value))), } } if util.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 buildSectionalCurvatureLinkSegments(sim *Simulation) error { repo := sim.repository for _, curvature := range repo.sectionalCurvatureMap { start, end, err := calculateLinkSegment(sim, curvature.kms[0], curvature.kms[1]) if err != nil { return err } sim.sectionalCurvatureLinkSegmentMap[curvature.Id()] = SectionalCurvatureLinkSegment{ sectionalCurvature: curvature, start: start, end: end, } } return nil }