rts-sim-module/repository/repository_manager.go

734 lines
21 KiB
Go
Raw Normal View History

package repository
import (
"errors"
"fmt"
"go.uber.org/zap"
"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)
zap.S().Debug("基础模型构建完毕")
err := buildModelRelationship(source, repository)
zap.S().Debug("模型关系构建完毕")
if err != nil {
return nil, err
}
err = buildLinksAndRelate(repository)
zap.S().Debug("构建Link并与模型关联完毕")
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
}
err = completeTurnoutPortKm(repository)
if err != nil {
return err
}
return err
}
// 补全道岔端口的公里标
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 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
}
//关联继电器组
2023-09-22 15:38:24 +08:00
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端节点
2023-09-20 18:20:56 +08:00
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
2023-09-20 18:20:56 +08:00
interrelateLinkAndTurnout(repo, baseKm, endTp, &LinkPort{link, proto.Port_B})
} else {
link.bindKm(endKm, proto.Port_B)
}
//计算Link长度
convertedKm, err := convertKilometer(repo, endKm, baseKm.CoordinateSystem)
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
2023-09-20 18:20:56 +08:00
devices := startTp.turnout.findDevicesByPort(startTp.port)
for {
//遍历设备并构建、关联其在Link上的位置
2023-09-20 18:20:56 +08:00
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)
2023-09-20 18:20:56 +08:00
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)
2023-09-20 18:20:56 +08:00
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)
2023-09-20 18:20:56 +08:00
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
}
2023-09-20 18:20:56 +08:00
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)
2023-09-20 18:20:56 +08:00
aOffset := number.Abs(aKm.Value - baseKm.Value)
bOffset := number.Abs(bKm.Value - baseKm.Value)
section.aLinkPosition = &LinkPosition{
2023-09-20 18:20:56 +08:00
link: link,
offset: aOffset,
}
section.bLinkPosition = &LinkPosition{
2023-09-20 18:20:56 +08:00
link: link,
offset: bOffset,
}
if aOffset <= bOffset {
section.startLinkPosition = section.aLinkPosition
section.endLinkPosition = section.bLinkPosition
} else {
section.startLinkPosition = section.bLinkPosition
section.endLinkPosition = section.aLinkPosition
}
2023-09-20 18:20:56 +08:00
}
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)
}
2023-09-20 18:20:56 +08:00
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)
}
}
return nil
}
2023-09-20 18:20:56 +08:00
func interrelateLinkAndTurnout(repo *Repository, baseKm *proto.Kilometer, tp *TurnoutPort, linkPort *LinkPort) {
tp.turnout.bindLinkPort(tp.port, linkPort)
2023-09-20 18:20:56 +08:00
linkPort.link.bindTurnoutPort(linkPort.port, tp)
tpKm := tp.turnout.findBoundaryKmByPort(tp.port)
tpKm, _ = convertKilometer(repo, tpKm, baseKm.CoordinateSystem)
2023-09-20 18:20:56 +08:00
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)
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
}