rts-sim-module/repository/simulation.go

388 lines
10 KiB
Go
Raw Normal View History

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
}