rts-sim-module/repository/repository_manager.go

1278 lines
40 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package repository
import (
"errors"
"fmt"
"joylink.club/rtsssimulation/repository/model/proto"
"joylink.club/rtsssimulation/util/number"
"log/slog"
"math"
"sort"
"strconv"
"strings"
)
var repositoryMap = make(map[string]*Repository)
func BuildRepository(source *proto.Repository) (*Repository, error) {
errMsg := baseCheck(source)
if len(errMsg) != 0 {
return nil, errors.New(strings.Join(errMsg, "\n"))
}
repository := newRepository(source.Id, source.Version)
err := buildModels(source, repository)
if err != nil {
return nil, err
}
slog.Info("基础模型构建完毕")
err = buildModelRelationship(source, repository)
slog.Info("模型关系构建完毕")
if err != nil {
return nil, err
}
err = buildLinksAndRelate(repository)
slog.Info("构建Link并与模型关联完毕")
if err != nil {
return nil, err
}
err = buildIscsModelRelationship(source, 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) error {
for _, protoData := range source.KilometerConverts {
repository.addKilometerConvert(protoData)
}
err := buildKilometerCalibration(source, repository)
if err != nil {
return err
}
for _, protoData := range source.Stations {
m := NewStation(protoData.Id, protoData.Code)
_, ok := repository.stationMap[m.Id()]
if ok {
return fmt.Errorf("id=%s的车站数据重复", protoData.Id)
}
repository.stationMap[m.Id()] = m
}
for _, protoData := range source.PhysicalSections {
m := NewPhysicalSection(protoData.Id)
repository.physicalSectionMap[m.Id()] = m
}
for _, protoData := range source.AxleCountingSections {
m := NewAxleCountingSection(protoData.Id, protoData.TurnoutAndPos)
repository.axleCountingSectionMap[m.Id()] = m
}
for _, protoData := range source.CheckPoints {
calibrationKilometer(protoData.Km, repository)
m := NewCheckPoint(protoData.Id, protoData.Km, protoData.Type)
repository.checkPointMap[m.Id()] = m
}
for _, protoData := range source.Turnouts {
calibrationKilometer(protoData.Km, repository)
m := NewTurnout(protoData.Id, protoData.Km, protoData.SwitchMachineType)
repository.turnoutMap[m.Id()] = m
}
for _, protoData := range source.Signals {
calibrationKilometer(protoData.Km, repository)
m := NewSignal(protoData.Id, protoData.Km, protoData.Code, protoData.Model)
repository.signalMap[m.Id()] = m
}
for _, protoData := range source.Transponders {
calibrationKilometer(protoData.Km, repository)
m := NewTransponder(protoData.Id, protoData.Km, protoData.FixedTelegram, protoData.FixedUserTelegram,
protoData.Type, protoData.LeuIndex, protoData.IndexInLeu)
repository.responderMap[m.Id()] = m
}
for _, sp := range source.StopPosition {
calibrationKilometer(sp.Km, repository)
t := NewStopPosition(sp.Id, sp.Km, sp.CoachNum)
repository.StopPosition[t.Id()] = t
}
for _, protoData := range source.Slopes {
for _, km := range protoData.Kms {
calibrationKilometer(km, repository)
}
m := NewSlope(protoData.Id, protoData.Kms, protoData.Degree)
repository.slopeMap[m.Id()] = m
}
for _, protoData := range source.SectionalCurvatures {
for _, km := range protoData.Kms {
calibrationKilometer(km, repository)
}
m := NewSectionalCurvature(protoData.Id, protoData.Kms, protoData.Radius)
repository.sectionalCurvatureMap[m.Id()] = m
}
for _, protoData := range source.Relays {
s, ok := repository.stationMap[protoData.StationId]
if !ok {
return fmt.Errorf("id=%s的继电器所属车站不存在,车站id=%s", protoData.Id, protoData.StationId)
}
m := newRelay(protoData.Id, protoData.Code, protoData.Model, protoData.DefaultPos, s)
repository.relayMap[m.Id()] = m
}
for _, protoData := range source.PhaseFailureProtectors {
m := newPhaseFailureProtector(protoData.Id, protoData.Code)
repository.phaseFailureProtectorMap[m.Id()] = m
}
for _, protoData := range source.Buttons {
m := NewButton(protoData.Id, protoData.Code, protoData.ButtonType)
if protoData.HasLight {
m.SetLight()
}
repository.buttonMap[m.Id()] = m
}
for _, protoData := range source.Psds {
m := newPsd(protoData.Id, protoData.AsdAmount, protoData.AsdGroups)
repository.psdMap[m.Id()] = m
}
for _, protoData := range source.Lights {
m := NewLight(protoData.Id, protoData.Code)
repository.lightMap[m.Id()] = m
}
for _, protoData := range source.Alarms {
m := NewAlarm(protoData.Id, protoData.Code)
repository.alarmMap[m.Id()] = m
}
for _, protoData := range source.Mkxs {
m := NewPsdPsl(protoData.Id)
repository.psdPslMap[m.Id()] = m
}
for _, protoData := range source.Keys {
m := NewKey(protoData.Id, protoData.Code, protoData.Gear)
repository.keyMap[m.Id()] = m
}
for _, protoData := range source.Platforms {
m := NewPlatform(protoData.Id, protoData.Code, protoData.Direction)
repository.platformMap[m.Id()] = m
}
for _, protoData := range source.Ckms {
m := NewCkm(protoData.Id)
repository.ckmMap[m.Id()] = m
}
for _, protoData := range source.Xcjs {
m := NewXcj(protoData.Id, protoData.NumSegments)
repository.xcjMap[m.Id()] = m
}
for _, protoData := range source.CkmPsls {
m := NewCkmPsl(protoData.Id)
repository.ckmPslMap[m.Id()] = m
}
err = repository.generateCoordinateInfo(source.MainCoordinateSystem)
if err != nil {
return err
}
for _, protoData := range source.CentralizedStationRefs {
repository.centralizedMap[protoData.StationId] = protoData
}
for _, protoData := range source.Esbs {
repository.esbMap[protoData.Id] = &Esb{Identity: identity{id: protoData.Id, deviceType: proto.DeviceType_DeviceType_Esb}}
}
for _, protoData := range source.Spkss {
repository.spksMap[protoData.Id] = &Spks{Identity: identity{id: protoData.Id, deviceType: proto.DeviceType_DeviceType_Spks}}
}
err = buildIscsModels(source, repository)
//
return err
}
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 = buildAxleCountingSectionRelationShip(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 = buildPsdRelationShip(source, repository)
if err != nil {
return err
}
err = buildMkxRelationShip(source, repository)
if err != nil {
return err
}
err = completeTurnoutPortKm(repository)
if err != nil {
return err
}
err = buildStationRelationShip(source, repository)
if err != nil {
return err
}
err = buildPlatformRelationShip(source, repository)
if err != nil {
return err
}
err = buildCentralizedStationRelationShip(source, repository)
if err != nil {
return err
}
err = buildCkmRelationShip(source, repository)
if err != nil {
return err
}
err = buildXcjRelationShip(source, repository)
if err != nil {
return err
}
err = buildCkmPslRelationShip(source, repository)
if err != nil {
return err
}
err = buildEsbRelationship(source, repository)
if err != nil {
return err
}
err = buildSpksRelationship(source, repository)
buildStopPositionRelationShip(source, repository)
return err
}
func calibrationKilometer(km *proto.Kilometer, repository *Repository) {
params := repository.kilometerCalibrationMap[km.CoordinateSystem+km.Direction.String()]
for _, param := range params {
if param.Design.Value <= km.Value {
km.Value += param.Actual.Value - param.Design.Value
break
}
}
}
func buildKilometerCalibration(source *proto.Repository, repository *Repository) error {
for _, protoData := range source.KilometerCalibrations {
key := protoData.Design.CoordinateSystem + protoData.Design.Direction.String()
repository.kilometerCalibrationMap[key] = append(repository.kilometerCalibrationMap[key], protoData)
}
for _, calibrations := range repository.kilometerCalibrationMap {
sort.Slice(calibrations, func(i, j int) bool {
return calibrations[i].Design.Value > calibrations[j].Design.Value
})
}
return nil
}
func buildAxleCountingSectionRelationShip(source *proto.Repository, repository *Repository) error {
turnout_physicalSection_map := make(map[string]*PhysicalSection)
for _, physicalSection := range repository.physicalSectionMap {
for _, turnout := range physicalSection.turnouts {
turnout_physicalSection_map[turnout.Id()] = physicalSection
}
}
for _, protoData := range source.AxleCountingSections {
axleCountingSection := repository.axleCountingSectionMap[protoData.Id]
if len(protoData.TurnoutAndPos) != 0 {
axleCountingSection.physicalSection = turnout_physicalSection_map[protoData.TurnoutAndPos[0].TurnoutId]
} else {
commonPhysicalSectionMap := make(map[*PhysicalSection]int)
for _, axleCountingId := range protoData.AxleCountingIds {
for _, dp := range repository.checkPointMap[axleCountingId].devicePorts {
physicalSection, ok := dp.Device().(*PhysicalSection)
if ok {
commonPhysicalSectionMap[physicalSection]++
}
}
}
for physicalSection, i := range commonPhysicalSectionMap {
if i > 1 {
axleCountingSection.physicalSection = physicalSection
break
}
}
if axleCountingSection.physicalSection == nil { //轨道尽头的区段
for section, _ := range commonPhysicalSectionMap {
if len(section.checkPoints) == 1 {
axleCountingSection.physicalSection = section
break
}
}
}
}
if axleCountingSection.physicalSection == nil {
return fmt.Errorf("计轴区段[%s]找不到对应的物理区段", protoData.Id)
}
}
return nil
}
func buildSpksRelationship(source *proto.Repository, repository *Repository) error {
for _, protoData := range source.Spkss {
platform := repository.platformMap[protoData.PlatformId]
var num byte
if len(protoData.Code) > 4 { //暂时默认Code为SPKS+数字SPKS1、SPKS3等
num = protoData.Code[4] //这一位应该是数字
}
var plajCode string
var relayCode string
switch platform.dir {
case proto.Platform_Up:
plajCode = "SPKSSPLAJ"
relayCode = fmt.Sprintf("SPKSS%cJ", num)
case proto.Platform_Down:
plajCode = "SPKSXPLAJ"
relayCode = fmt.Sprintf("SPKSX%cJ", num)
default:
panic(fmt.Sprintf("未知的站台方向:%s", platform.dir))
}
spks := repository.spksMap[protoData.Id]
station := platform.station
for _, component := range station.spksComponents {
if component.Code() == plajCode && component.Type() == proto.DeviceType_DeviceType_Relay {
spks.plaId = component.Id()
}
if component.Code() == relayCode && component.Type() == proto.DeviceType_DeviceType_Relay {
spks.relayId = component.Id()
}
}
if spks.plaId == "" || spks.relayId == "" {
return fmt.Errorf("SPKS[%s]未找到对应的旁路继电器或状态继电器", protoData.Id)
}
}
return nil
}
func buildEsbRelationship(source *proto.Repository, repository *Repository) error {
for _, protoData := range source.Esbs {
platform := repository.platformMap[protoData.PlatformId]
var plaCode string
var relayCode string
switch platform.dir {
case proto.Platform_Up:
plaCode = "SEMPFA"
relayCode = "SEMPJ"
case proto.Platform_Down:
plaCode = "XEMPFA"
relayCode = "XEMPJ"
default:
panic(fmt.Sprintf("站台[%s]的方向[%s]不正确", platform.Id(), platform.dir))
}
esb := repository.esbMap[protoData.Id]
station := platform.station
for _, group := range station.empGroups {
for _, component := range group.components {
if component.Code() == plaCode && component.Type() == proto.DeviceType_DeviceType_Button {
esb.plaId = component.Id()
}
if component.Code() == relayCode && component.Type() == proto.DeviceType_DeviceType_Relay {
esb.relayId = component.Id()
}
}
}
if esb.plaId == "" || esb.relayId == "" {
return fmt.Errorf("ESB[%s]未找到对应的旁路按钮或继电器", protoData.Id)
}
}
return nil
}
func buildCkmPslRelationShip(source *proto.Repository, repository *Repository) error {
for _, protoData := range source.CkmPsls {
psl := repository.ckmPslMap[protoData.Id]
psl.ckm = repository.ckmMap[protoData.CkmId]
psl.gma = repository.buttonMap[protoData.GmaId]
psl.kma = repository.buttonMap[protoData.KmaId]
psl.mpla = repository.buttonMap[protoData.MplaId]
psl.mmsa = repository.buttonMap[protoData.MmsaId]
}
return nil
}
func buildXcjRelationShip(source *proto.Repository, repo *Repository) error {
for _, protoData := range source.Ckms {
ckm := repo.ckmMap[protoData.Id]
for _, group := range protoData.ElectronicComponentGroups {
var components []IGroupedElectronicComponent
for _, id := range group.GetComponentIds() {
if relay := repo.relayMap[id]; relay != nil {
components = append(components, relay)
}
}
ckm.componentGroups = append(ckm.componentGroups, &ElectronicComponentGroup{
code: group.Code,
components: components,
})
}
}
return nil
}
func buildCkmRelationShip(source *proto.Repository, repo *Repository) error {
for _, protoData := range source.Xcjs {
xcj := repo.xcjMap[protoData.Id]
for _, group := range protoData.ElectronicComponentGroups {
var components []IGroupedElectronicComponent
for _, id := range group.GetComponentIds() {
if relay := repo.relayMap[id]; relay != nil {
components = append(components, relay)
}
}
xcj.componentGroups = append(xcj.componentGroups, &ElectronicComponentGroup{
code: group.Code,
components: components,
})
}
}
return nil
}
func buildPlatformRelationShip(source *proto.Repository, repo *Repository) error {
for _, protoData := range source.Platforms {
platform := repo.platformMap[protoData.Id]
station := repo.stationMap[protoData.StationId]
if station == nil {
return fmt.Errorf("站台[id:%s]关联的车站[id:%s]不存在", platform.Id(), protoData.StationId)
}
platform.station = station
for _, group := range protoData.ElectronicComponentGroups {
var components []IGroupedElectronicComponent
for _, id := range group.GetComponentIds() {
if relay := repo.relayMap[id]; relay != nil {
components = append(components, relay)
}
}
platform.componentGroups = append(platform.componentGroups, &ElectronicComponentGroup{
code: group.Code,
components: components,
})
}
}
return nil
}
func buildCentralizedStationRelationShip(source *proto.Repository, repo *Repository) error {
for _, stationRef := range source.CentralizedStationRefs {
stationId := stationRef.StationId
repo.centralizedMap[stationId] = stationRef
}
return nil
}
func buildMkxRelationShip(source *proto.Repository, repo *Repository) error {
for _, protoData := range source.Mkxs {
mkx := repo.psdPslMap[protoData.Id]
mkx.psd = repo.psdMap[protoData.PsdId]
mkx.pcb = repo.buttonMap[protoData.PcbaId]
mkx.pcbpl = repo.buttonMap[protoData.PcbplaId]
mkx.pcbj = repo.relayMap[protoData.PcbjId]
mkx.pob = repo.buttonMap[protoData.PobaId]
mkx.pobpl = repo.buttonMap[protoData.PobplaId]
mkx.pobj = repo.relayMap[protoData.PobjId]
mkx.pab = repo.buttonMap[protoData.PabaId]
mkx.pabpl = repo.buttonMap[protoData.PabplaId]
mkx.pabj = repo.relayMap[protoData.PabjId]
mkx.wrzf = repo.buttonMap[protoData.WrzfaId]
mkx.wrzfpl = repo.buttonMap[protoData.WrzfplaId]
mkx.wrzfj = repo.relayMap[protoData.WrzfjId]
mkx.qkqr = repo.buttonMap[protoData.QkqraId]
mkx.qkqrpl = repo.buttonMap[protoData.QkqrplaId]
mkx.qkqrj = repo.relayMap[protoData.QkqrjId]
mkx.mpl = repo.buttonMap[protoData.MplaId]
mkx.jxtcpl = repo.buttonMap[protoData.JxtcplaId]
}
return nil
}
func buildPsdRelationShip(source *proto.Repository, repo *Repository) error {
for _, protoData := range source.Psds {
psd := repo.psdMap[protoData.Id]
platform := repo.platformMap[protoData.PlatformId]
if platform == nil {
return fmt.Errorf("屏蔽门[id:%s]关联的站台[id:%s]不存在", psd.Id(), protoData.PlatformId)
}
psd.platform = platform
for _, group := range protoData.ElectronicComponentGroups {
var components []IGroupedElectronicComponent
for _, id := range group.GetComponentIds() {
if relay := repo.relayMap[id]; relay != nil {
components = append(components, relay)
}
}
psd.componentGroups = append(psd.componentGroups, &ElectronicComponentGroup{
code: group.Code,
components: components,
})
}
}
return nil
}
// 补全道岔端口的公里标
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 fmt.Errorf("应答器[%s]关联的[%s-%s-%s]并非道岔端口", responder.Id(), tp.DeviceId, tp.DeviceType, tp.Port)
}
interrelateResponderAndTurnout(responder, repository.turnoutMap[tp.DeviceId], tp.Port)
}
}
return nil
}
func buildStopPositionRelationShip(source *proto.Repository, repository *Repository) error {
for _, protoData := range source.StopPosition {
responder := repository.StopPosition[protoData.Id]
if protoData.SectionId != "" {
repository.physicalSectionMap[protoData.SectionId].bindDevices(responder)
}
}
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 fmt.Errorf("信号机[%s]关联的[%s-%s-%s]并非道岔端口", signal.Id(), tp.DeviceId, tp.DeviceType, tp.Port)
}
interrelateSignalAndTurnout(signal, repository.turnoutMap[tp.DeviceId], tp.Port)
}
//关联继电器组
for _, group := range protoData.ElectronicComponentGroups {
var components []IGroupedElectronicComponent
for _, id := range group.GetComponentIds() {
if relay := repository.relayMap[id]; relay != nil {
components = append(components, relay)
}
}
signal.componentGroups = append(signal.componentGroups, &ElectronicComponentGroup{
code: group.Code,
components: components,
})
}
}
return nil
}
func buildTurnoutRelationShip(source *proto.Repository, repo *Repository) error {
for _, protoData := range source.Turnouts {
turnout := repo.turnoutMap[protoData.Id]
if protoData.ADevicePort == nil && protoData.BDevicePort == nil && protoData.CDevicePort == nil {
return fmt.Errorf("道岔[%s]无连接设备", 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.ElectronicComponentGroups {
var components []IGroupedElectronicComponent
for _, id := range group.GetComponentIds() {
if relay := repo.relayMap[id]; relay != nil {
components = append(components, relay)
} else if pfp := repo.phaseFailureProtectorMap[id]; pfp != nil {
components = append(components, pfp)
}
}
turnout.componentGroups = append(turnout.componentGroups, &ElectronicComponentGroup{
code: group.Code,
components: components,
})
}
}
return nil
}
func buildTurnoutPortRelation(repo *Repository, turnout *Turnout, port proto.Port, protoDp *proto.DevicePort) error {
if protoDp == nil {
return nil
}
model, err := repo.FindModel(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, repo *Repository) error {
for _, protoData := range source.PhysicalSections {
section := repo.physicalSectionMap[protoData.Id]
//A端关联
if protoData.ADevicePort != nil {
err := buildSectionPortRelation(repo, section, proto.Port_A, protoData.ADevicePort)
if err != nil {
return err
}
}
//B端关联
if protoData.BDevicePort != nil {
err := buildSectionPortRelation(repo, section, proto.Port_B, protoData.BDevicePort)
if err != nil {
return err
}
}
//道岔关联
for _, turnoutId := range protoData.TurnoutIds {
turnout := repo.turnoutMap[turnoutId]
if turnout == nil {
return fmt.Errorf("id[%s]的道岔不存在", turnoutId)
}
section.bindTurnouts(turnout)
turnout.section = section
}
//关联联锁集中站
for _, stationId := range protoData.CentralizedStation {
section.centralizedStation = append(section.centralizedStation, repo.stationMap[stationId])
}
//关联电子元件
for _, group := range protoData.ElectronicComponentGroups {
var components []IGroupedElectronicComponent
for _, id := range group.GetComponentIds() {
if relay := repo.relayMap[id]; relay != nil {
components = append(components, relay)
} else if pfp := repo.phaseFailureProtectorMap[id]; pfp != nil {
components = append(components, pfp)
}
}
section.componentGroups = append(section.componentGroups, &ElectronicComponentGroup{
code: group.Code,
components: components,
})
}
}
return nil
}
// 构建物理区段指定端口的关联关系。区段{section}的{port}端口关联{protoDp}
func buildSectionPortRelation(repo *Repository, section *PhysicalSection, port proto.Port, protoDp *proto.DevicePort) error {
model, err := repo.FindModel(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, fmt.Errorf("[%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 stopPositionRelateLink(repo *Repository) {
for _, curvature := range repo {
start, end, err := calculateLinkSegment(repo, curvature.kms[0], curvature.kms[1])
if err != nil {
return err
}
curvature.bindStartLinkPosition(start)
curvature.bindEndLinkPosition(end)
}
return nil
}*/
func buildLinks(repo *Repository) error {
visitedTurnoutPortMap := make(map[string]bool)
allTurnouts := repo.TurnoutList()
linkIdGenerator := 1
for {
//找一个未遍历过的道岔端口
startTp := getATurnoutPort(allTurnouts, visitedTurnoutPortMap)
if startTp == nil {
break
}
//以始端道岔的公里标作为Link零点的公里标
linkZeroKm := startTp.turnout.km
//创建基础Link
link := NewLink(strconv.Itoa(linkIdGenerator)) //由于发给动力学的link的id是数字所以这里也用数字
linkIdGenerator++
//以此道岔端口作为Link的A端节点
interrelateLinkAndTurnout(repo, linkZeroKm, startTp, &LinkPort{link, proto.Port_A})
//沿着道岔端口方向一直寻找到轨道尽头或者下一个道岔端口。构建并关联中间的设备在link上的位置
endTp, endKm, err := findEndTurnoutPortOrEndKm(repo, link, startTp, linkZeroKm)
if err != nil {
return err
}
//以下一个道岔端口作为Link的B端节点
if endTp != nil {
visitedTurnoutPortMap[buildTurnoutPortKey(endTp)] = true
endKm = endTp.turnout.km
interrelateLinkAndTurnout(repo, linkZeroKm, endTp, &LinkPort{link, proto.Port_B})
} else {
link.bindKm(endKm, proto.Port_B)
}
//计算Link长度
convertedKm, err := ConvertKilometer(repo, endKm, linkZeroKm.CoordinateSystem)
if err != nil {
return err
}
link.length = int64(math.Abs(float64(convertedKm.Value - linkZeroKm.Value)))
//构建Link两端物理道岔区段在Link上的范围
buildLinkRangeOfTurnoutPhysicalSection(link)
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())
}
//case proto.DeviceType_deviceType_Stop_position:
// turnout := currentDp.Device().(*StopPosition)
}
//根据下一个端口设备的信息决定是否结束循环
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
}
// 关联正常非道岔物理区段与Link
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)
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,
}
section.bindLinkRange(link, aOffset, bOffset)
}
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)
}
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
}
linkPositionDevice, ok := device.(LinkPositionDevice)
if !ok {
return fmt.Errorf("device [%s:%s] not implements LinkPositionDevice", device.Id(), device.Type().String())
}
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,
}
linkPositionDevice.bindLinkPosition(linkPosition)
link.bindDevices(linkPositionDevice)
}
return nil
}
// 互相关联Link和道岔及道岔区段
func interrelateLinkAndTurnout(repo *Repository, linkZeroKm *proto.Kilometer, tp *TurnoutPort, linkPort *LinkPort) {
//关联Link和道岔
tp.turnout.bindLinkPort(tp.port, linkPort)
linkPort.link.bindTurnoutPort(linkPort.port, tp)
tpKm := tp.turnout.findBoundaryKmByPort(tp.port)
tpKm, _ = ConvertKilometer(repo, tpKm, linkZeroKm.CoordinateSystem)
offset := number.Abs(tpKm.Value - linkZeroKm.Value)
tp.turnout.bindLinkPosition(tp.port, &LinkPosition{
link: linkPort.link,
offset: offset,
})
}
// 构建Link关联道岔物理区段在Link上的范围
func buildLinkRangeOfTurnoutPhysicalSection(link *Link) {
//解决[两道岔的道岔物理区段两道岔间的Link会产生两个LinkRange]的问题
if link.aRelation != nil && link.bRelation != nil && link.aRelation.turnout.section == link.bRelation.turnout.section {
section := link.aRelation.turnout.section
section.bindLinkRange(link, 0, link.length)
return
}
if link.aRelation != nil {
aSection := link.aRelation.turnout.section //Link A端的关联的道岔物理区段
aLinkPosition := link.aRelation.turnout.FindLinkPositionByPort(link.aRelation.port) //Link A端道岔端点在Link上的位置
aSection.bindLinkRange(link, 0, aLinkPosition.offset)
}
if link.bRelation != nil {
bSection := link.bRelation.turnout.section //Link A端的关联的道岔物理区段
bLinkPosition := link.bRelation.turnout.FindLinkPositionByPort(link.bRelation.port) //Link A端道岔端点在Link上的位置
bSection.bindLinkRange(link, bLinkPosition.offset, link.length)
}
}
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
case proto.DeviceType_deviceType_Stop_position:
return model.(*StopPosition).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
}
func buildStationRelationShip(source *proto.Repository, repo *Repository) error {
for _, protoData := range source.Stations {
station, ok := repo.stationMap[protoData.Id]
if !ok {
return fmt.Errorf("缺失车站[%s]", protoData.Id)
}
for _, group := range protoData.ElectronicGroup {
if group.Code == "SPKS" {
station.spksComponents = append(station.spksComponents, buildRelComponent(repo, group.Components)...)
} else if group.Code == "EMP" || strings.HasSuffix(group.Code, "EMPJ") {
station.empGroups = append(station.empGroups, &ElectronicComponentGroup{
code: group.Code,
components: buildRelComponent(repo, group.Components),
})
} else {
slog.Warn("未知的车站组合code", group.Code)
}
}
if len(protoData.Deccs) > 0 { // 暂时让逻辑运行起来,后需重构
station.deviceEcc = protoData.Deccs
}
}
return nil
}
func buildRelComponent(repo *Repository, comps []*proto.ElectronicComponent) []IGroupedElectronicComponent {
var components []IGroupedElectronicComponent
for _, data := range comps {
switch data.DeviceType {
case proto.DeviceType_DeviceType_Alarm:
components = append(components, repo.alarmMap[data.Id])
case proto.DeviceType_DeviceType_Button:
components = append(components, repo.buttonMap[data.Id])
case proto.DeviceType_DeviceType_Relay:
components = append(components, repo.relayMap[data.Id])
case proto.DeviceType_DeviceType_Light:
components = append(components, repo.lightMap[data.Id])
case proto.DeviceType_DeviceType_Key:
components = append(components, repo.keyMap[data.Id])
default:
slog.Warn("IBP未绑定【%s】类型设备", data.DeviceType.String())
}
}
return components
}