340 lines
9.1 KiB
Go
340 lines
9.1 KiB
Go
package repository
|
||
|
||
import (
|
||
"fmt"
|
||
"joylink.club/rtsssimulation/repository/model/proto"
|
||
"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, aKm *proto.Kilometer,
|
||
bKm *proto.Kilometer) (LinkPosition, LinkPosition, error) {
|
||
|
||
repo := simulation.repository
|
||
bKm, err := convertKilometer(repo, bKm, aKm.CoordinateSystem, aKm.Direction)
|
||
if err != nil {
|
||
return LinkPosition{}, LinkPosition{}, err
|
||
}
|
||
var start LinkPosition
|
||
var end LinkPosition
|
||
for _, link := range simulation.linkMap {
|
||
aTKm := link.aRelation.node.turnout.km
|
||
aTKm, err := convertKilometer(repo, aTKm, aKm.CoordinateSystem, aKm.Direction)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
bTKm := link.bRelation.node.turnout.km
|
||
bTKm, err = convertKilometer(repo, bTKm, aKm.CoordinateSystem, aKm.Direction)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
if aTKm.Value >= aTKm.Value && aTKm.Value <= bTKm.Value {
|
||
start = LinkPosition{
|
||
link: link,
|
||
offset: aTKm.Value - aTKm.Value,
|
||
}
|
||
}
|
||
if bKm.Value >= aTKm.Value && bKm.Value <= bTKm.Value {
|
||
end = LinkPosition{
|
||
link: link,
|
||
offset: bKm.Value - aTKm.Value,
|
||
}
|
||
}
|
||
if start.link != nil && end.link != link {
|
||
break
|
||
}
|
||
}
|
||
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
|
||
}
|