2023-09-20 15:14:38 +08:00
|
|
|
|
package repository
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2023-10-17 15:05:13 +08:00
|
|
|
|
"log/slog"
|
2023-09-27 14:51:51 +08:00
|
|
|
|
"math"
|
|
|
|
|
"strconv"
|
2023-12-06 16:43:29 +08:00
|
|
|
|
"strings"
|
2023-11-09 14:26:08 +08:00
|
|
|
|
|
|
|
|
|
"joylink.club/rtsssimulation/repository/model/proto"
|
|
|
|
|
"joylink.club/rtsssimulation/util/number"
|
2023-09-20 15:14:38 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var repositoryMap = make(map[string]*Repository)
|
|
|
|
|
|
|
|
|
|
func BuildRepository(source *proto.Repository) (*Repository, error) {
|
2023-09-21 16:28:13 +08:00
|
|
|
|
errMsg := baseCheck(source)
|
|
|
|
|
if len(errMsg) != 0 {
|
2024-01-29 14:42:09 +08:00
|
|
|
|
return nil, errors.New(strings.Join(errMsg, "\n"))
|
2023-09-21 16:28:13 +08:00
|
|
|
|
}
|
2023-09-20 15:14:38 +08:00
|
|
|
|
repository := newRepository(source.Id, source.Version)
|
2023-10-20 15:05:56 +08:00
|
|
|
|
err := buildModels(source, repository)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2023-10-20 16:12:42 +08:00
|
|
|
|
slog.Info("基础模型构建完毕")
|
2023-10-20 15:05:56 +08:00
|
|
|
|
err = buildModelRelationship(source, repository)
|
2023-10-20 16:12:42 +08:00
|
|
|
|
slog.Info("模型关系构建完毕")
|
2023-09-20 15:14:38 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
err = buildLinksAndRelate(repository)
|
2023-10-20 16:12:42 +08:00
|
|
|
|
slog.Info("构建Link并与模型关联完毕")
|
2023-09-20 15:14:38 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2023-12-26 14:45:41 +08:00
|
|
|
|
err = buildIscsModelRelationship(source, repository)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
//
|
2023-09-20 15:14:38 +08:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-20 15:05:56 +08:00
|
|
|
|
func buildModels(source *proto.Repository, repository *Repository) error {
|
2023-09-20 15:14:38 +08:00
|
|
|
|
for _, protoData := range source.KilometerConverts {
|
|
|
|
|
repository.addKilometerConvert(protoData)
|
|
|
|
|
}
|
2023-11-01 16:55:58 +08:00
|
|
|
|
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
|
|
|
|
|
}
|
2023-09-20 15:14:38 +08:00
|
|
|
|
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 {
|
2023-09-21 16:28:13 +08:00
|
|
|
|
m := NewTurnout(protoData.Id, protoData.Km, protoData.SwitchMachineType)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
repository.turnoutMap[m.Id()] = m
|
|
|
|
|
}
|
|
|
|
|
for _, protoData := range source.Signals {
|
2023-10-20 09:41:49 +08:00
|
|
|
|
m := NewSignal(protoData.Id, protoData.Km, protoData.Code, protoData.Model)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
repository.signalMap[m.Id()] = m
|
|
|
|
|
}
|
|
|
|
|
for _, protoData := range source.Transponders {
|
2024-01-22 11:06:16 +08:00
|
|
|
|
m := NewTransponder(protoData.Id, protoData.Km, protoData.FixedTelegram, protoData.FixedUserTelegram, protoData.Type)
|
2024-01-10 11:12:19 +08:00
|
|
|
|
repository.responderMap[m.Id()] = m
|
2023-09-20 15:14:38 +08:00
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
}
|
2023-09-22 15:13:24 +08:00
|
|
|
|
for _, protoData := range source.Relays {
|
2023-11-01 16:55:58 +08:00
|
|
|
|
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, s)
|
2023-09-22 15:13:24 +08:00
|
|
|
|
repository.relayMap[m.Id()] = m
|
|
|
|
|
}
|
2023-09-28 14:10:15 +08:00
|
|
|
|
for _, protoData := range source.PhaseFailureProtectors {
|
|
|
|
|
m := newPhaseFailureProtector(protoData.Id, protoData.Code)
|
|
|
|
|
repository.phaseFailureProtectorMap[m.Id()] = m
|
|
|
|
|
}
|
2023-10-10 11:05:26 +08:00
|
|
|
|
for _, protoData := range source.Buttons {
|
2023-10-12 17:46:43 +08:00
|
|
|
|
m := NewButton(protoData.Id, protoData.Code, protoData.ButtonType)
|
2023-10-18 14:08:10 +08:00
|
|
|
|
if protoData.HasLight {
|
2023-11-02 13:08:31 +08:00
|
|
|
|
m.SetLight()
|
2023-10-18 14:08:10 +08:00
|
|
|
|
}
|
2023-10-10 11:05:26 +08:00
|
|
|
|
repository.buttonMap[m.Id()] = m
|
|
|
|
|
}
|
2023-10-12 17:55:40 +08:00
|
|
|
|
for _, protoData := range source.Psds {
|
2023-11-01 16:52:03 +08:00
|
|
|
|
m := newPsd(protoData.Id, protoData.AsdAmount, protoData.AsdGroups)
|
2023-10-12 17:55:40 +08:00
|
|
|
|
repository.psdMap[m.Id()] = m
|
|
|
|
|
}
|
2023-10-17 15:05:13 +08:00
|
|
|
|
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
|
|
|
|
|
}
|
2023-10-17 10:26:31 +08:00
|
|
|
|
for _, protoData := range source.Mkxs {
|
2023-11-01 16:52:03 +08:00
|
|
|
|
m := NewMkx(protoData.Id)
|
2023-10-17 10:26:31 +08:00
|
|
|
|
repository.mkxMap[m.Id()] = m
|
|
|
|
|
}
|
2023-10-20 13:13:44 +08:00
|
|
|
|
for _, protoData := range source.Keys {
|
|
|
|
|
m := NewKey(protoData.Id, protoData.Code, protoData.Gear)
|
|
|
|
|
repository.keyMap[m.Id()] = m
|
|
|
|
|
}
|
2023-11-07 14:30:07 +08:00
|
|
|
|
for _, protoData := range source.Platforms {
|
|
|
|
|
m := NewPlatform(protoData.Id, protoData.Code)
|
|
|
|
|
repository.platformMap[m.Id()] = m
|
|
|
|
|
}
|
2024-03-07 17:40:51 +08:00
|
|
|
|
for _, protoData := range source.Ckms {
|
|
|
|
|
m := NewCkm(protoData.Id)
|
|
|
|
|
repository.ckmMap[m.Id()] = m
|
|
|
|
|
}
|
|
|
|
|
for _, protoData := range source.Xcjs {
|
|
|
|
|
m := NewXcj(protoData.Id)
|
|
|
|
|
repository.xcjMap[m.Id()] = m
|
|
|
|
|
}
|
2023-10-20 15:05:56 +08:00
|
|
|
|
err := repository.generateCoordinateInfo(source.MainCoordinateSystem)
|
2023-12-26 14:45:41 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2023-10-31 08:55:29 +08:00
|
|
|
|
for _, protoData := range source.CentralizedStationRefs {
|
|
|
|
|
repository.centralizedMap[protoData.StationId] = protoData
|
|
|
|
|
}
|
2023-12-26 14:45:41 +08:00
|
|
|
|
//
|
|
|
|
|
err = buildIscsModels(source, repository)
|
|
|
|
|
//
|
2023-10-20 15:05:56 +08:00
|
|
|
|
return err
|
2023-09-20 15:14:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
2023-10-17 10:26:31 +08:00
|
|
|
|
err = buildPsdRelationShip(source, repository)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2023-10-19 17:09:47 +08:00
|
|
|
|
err = buildMkxRelationShip(source, repository)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2023-09-25 14:38:13 +08:00
|
|
|
|
err = completeTurnoutPortKm(repository)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2023-10-17 15:05:13 +08:00
|
|
|
|
err = buildStationRelationShip(source, repository)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2023-11-07 14:30:07 +08:00
|
|
|
|
err = buildPlatformRelationShip(source, repository)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2023-11-07 17:47:41 +08:00
|
|
|
|
err = buildCentralizedStationRelationShip(source, repository)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2024-03-07 17:40:51 +08:00
|
|
|
|
err = buildCkmRelationShip(source, repository)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
err = buildXcjRelationShip(source, repository)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2023-09-20 15:14:38 +08:00
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-07 17:40:51 +08:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-07 14:30:07 +08:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-07 17:47:41 +08:00
|
|
|
|
func buildCentralizedStationRelationShip(source *proto.Repository, repo *Repository) error {
|
|
|
|
|
for _, stationRef := range source.CentralizedStationRefs {
|
|
|
|
|
stationId := stationRef.StationId
|
|
|
|
|
repo.centralizedMap[stationId] = stationRef
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2023-10-17 10:26:31 +08:00
|
|
|
|
func buildMkxRelationShip(source *proto.Repository, repo *Repository) error {
|
2023-10-19 17:09:47 +08:00
|
|
|
|
for _, protoData := range source.Mkxs {
|
2023-10-17 10:26:31 +08:00
|
|
|
|
mkx := repo.mkxMap[protoData.Id]
|
2024-02-18 18:26:12 +08:00
|
|
|
|
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]
|
2023-10-17 10:26:31 +08:00
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func buildPsdRelationShip(source *proto.Repository, repo *Repository) error {
|
|
|
|
|
for _, protoData := range source.Psds {
|
|
|
|
|
psd := repo.psdMap[protoData.Id]
|
2023-11-07 14:30:07 +08:00
|
|
|
|
platform := repo.platformMap[protoData.PlatformId]
|
|
|
|
|
if platform == nil {
|
|
|
|
|
return fmt.Errorf("屏蔽门[id:%s]关联的站台[id:%s]不存在", psd.Id(), protoData.PlatformId)
|
|
|
|
|
}
|
|
|
|
|
psd.platform = platform
|
2023-10-17 10:26:31 +08:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-25 14:38:13 +08:00
|
|
|
|
// 补全道岔端口的公里标
|
|
|
|
|
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()]
|
2024-01-10 14:06:00 +08:00
|
|
|
|
otherKm, err := ConvertKilometer(repo, otherTurnout.km, turnout.km.CoordinateSystem)
|
2023-09-25 14:38:13 +08:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-20 15:14:38 +08:00
|
|
|
|
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 {
|
2024-01-10 11:12:19 +08:00
|
|
|
|
responder := repository.responderMap[protoData.Id]
|
2023-09-20 15:14:38 +08:00
|
|
|
|
//应答器和区段相互关联
|
|
|
|
|
if protoData.SectionId != "" {
|
|
|
|
|
interrelateResponderAndPhysicalSection(responder, repository.physicalSectionMap[protoData.SectionId])
|
|
|
|
|
}
|
|
|
|
|
//应答器和道岔相互关联
|
|
|
|
|
tp := protoData.TurnoutPort
|
|
|
|
|
if tp != nil {
|
|
|
|
|
if tp.DeviceType != proto.DeviceType_DeviceType_Turnout {
|
2023-10-17 15:08:30 +08:00
|
|
|
|
return fmt.Errorf("应答器[%s]关联的[%s-%s-%s]并非道岔端口", responder.Id(), tp.DeviceId, tp.DeviceType, tp.Port)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
}
|
|
|
|
|
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 {
|
2023-10-17 15:08:30 +08:00
|
|
|
|
return fmt.Errorf("信号机[%s]关联的[%s-%s-%s]并非道岔端口", signal.Id(), tp.DeviceId, tp.DeviceType, tp.Port)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
}
|
|
|
|
|
interrelateSignalAndTurnout(signal, repository.turnoutMap[tp.DeviceId], tp.Port)
|
|
|
|
|
}
|
2023-10-10 16:51:07 +08:00
|
|
|
|
//关联继电器组
|
|
|
|
|
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,
|
|
|
|
|
})
|
|
|
|
|
}
|
2023-09-20 15:14:38 +08:00
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func buildTurnoutRelationShip(source *proto.Repository, repo *Repository) error {
|
|
|
|
|
for _, protoData := range source.Turnouts {
|
|
|
|
|
turnout := repo.turnoutMap[protoData.Id]
|
2023-12-05 13:54:49 +08:00
|
|
|
|
if protoData.ADevicePort == nil && protoData.BDevicePort == nil && protoData.CDevicePort == nil {
|
|
|
|
|
return fmt.Errorf("道岔[%s]无连接设备", protoData.Id)
|
|
|
|
|
}
|
2023-09-20 15:14:38 +08:00
|
|
|
|
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:13:24 +08:00
|
|
|
|
//关联继电器组
|
2023-09-28 14:10:15 +08:00
|
|
|
|
for _, group := range protoData.ElectronicComponentGroups {
|
2023-09-28 14:34:00 +08:00
|
|
|
|
var components []IGroupedElectronicComponent
|
2023-09-28 14:10:15 +08:00
|
|
|
|
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)
|
2023-09-26 13:45:00 +08:00
|
|
|
|
}
|
2023-09-22 15:38:24 +08:00
|
|
|
|
}
|
2023-09-28 14:10:15 +08:00
|
|
|
|
turnout.componentGroups = append(turnout.componentGroups, &ElectronicComponentGroup{
|
|
|
|
|
code: group.Code,
|
|
|
|
|
components: components,
|
2023-09-22 15:38:24 +08:00
|
|
|
|
})
|
|
|
|
|
}
|
2023-09-20 15:14:38 +08:00
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func buildTurnoutPortRelation(repo *Repository, turnout *Turnout, port proto.Port, protoDp *proto.DevicePort) error {
|
2023-12-05 13:54:49 +08:00
|
|
|
|
if protoDp == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2023-09-26 10:25:00 +08:00
|
|
|
|
model, err := repo.FindModel(protoDp.DeviceId, protoDp.DeviceType)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
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 {
|
2023-10-17 15:08:30 +08:00
|
|
|
|
return fmt.Errorf("id[%s]的道岔不存在", turnoutId)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
}
|
|
|
|
|
section.bindTurnouts(turnout)
|
|
|
|
|
turnout.section = section
|
|
|
|
|
}
|
2023-10-31 16:53:37 +08:00
|
|
|
|
//关联联锁集中站
|
|
|
|
|
section.centralizedStation = protoData.CentralizedStation
|
2023-09-20 15:14:38 +08:00
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 构建物理区段指定端口的关联关系。区段{section}的{port}端口关联{protoDp}
|
|
|
|
|
func buildSectionPortRelation(repo *Repository, section *PhysicalSection, port proto.Port, protoDp *proto.DevicePort) error {
|
2023-09-26 10:25:00 +08:00
|
|
|
|
model, err := repo.FindModel(protoDp.DeviceId, protoDp.DeviceType)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
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:
|
2023-10-17 15:08:30 +08:00
|
|
|
|
return nil, fmt.Errorf("[%s]类型设备没有端口", device.Type())
|
2023-09-20 15:14:38 +08:00
|
|
|
|
}
|
|
|
|
|
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()
|
2023-09-27 14:51:51 +08:00
|
|
|
|
linkIdGenerator := 1
|
2023-09-20 15:14:38 +08:00
|
|
|
|
for {
|
|
|
|
|
//找一个未遍历过的道岔端口
|
|
|
|
|
startTp := getATurnoutPort(allTurnouts, visitedTurnoutPortMap)
|
|
|
|
|
if startTp == nil {
|
|
|
|
|
break
|
|
|
|
|
}
|
2023-11-10 15:07:17 +08:00
|
|
|
|
//以始端道岔的公里标作为Link零点的公里标
|
|
|
|
|
linkZeroKm := startTp.turnout.km
|
2023-09-20 15:14:38 +08:00
|
|
|
|
//创建基础Link
|
2024-01-08 17:45:15 +08:00
|
|
|
|
link := NewLink(strconv.Itoa(linkIdGenerator)) //由于发给动力学的link的id是数字,所以这里也用数字
|
2023-09-20 15:14:38 +08:00
|
|
|
|
linkIdGenerator++
|
|
|
|
|
//以此道岔端口作为Link的A端节点
|
2023-11-10 15:07:17 +08:00
|
|
|
|
interrelateLinkAndTurnout(repo, linkZeroKm, startTp, &LinkPort{link, proto.Port_A})
|
2023-09-20 15:14:38 +08:00
|
|
|
|
//沿着道岔端口方向,一直寻找到轨道尽头或者下一个道岔端口。构建并关联中间的设备在link上的位置
|
2023-11-10 15:07:17 +08:00
|
|
|
|
endTp, endKm, err := findEndTurnoutPortOrEndKm(repo, link, startTp, linkZeroKm)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
//以下一个道岔端口作为Link的B端节点
|
|
|
|
|
if endTp != nil {
|
|
|
|
|
visitedTurnoutPortMap[buildTurnoutPortKey(endTp)] = true
|
|
|
|
|
endKm = endTp.turnout.km
|
2023-11-10 15:07:17 +08:00
|
|
|
|
interrelateLinkAndTurnout(repo, linkZeroKm, endTp, &LinkPort{link, proto.Port_B})
|
2023-09-20 15:14:38 +08:00
|
|
|
|
} else {
|
|
|
|
|
link.bindKm(endKm, proto.Port_B)
|
|
|
|
|
}
|
|
|
|
|
//计算Link长度
|
2024-01-10 14:06:00 +08:00
|
|
|
|
convertedKm, err := ConvertKilometer(repo, endKm, linkZeroKm.CoordinateSystem)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2023-11-10 15:07:17 +08:00
|
|
|
|
link.length = int64(math.Abs(float64(convertedKm.Value - linkZeroKm.Value)))
|
|
|
|
|
//构建Link两端物理道岔区段在Link上的范围
|
|
|
|
|
buildLinkRangeOfTurnoutPhysicalSection(link)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
|
|
|
|
|
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)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
for {
|
|
|
|
|
//遍历设备并构建、关联其在Link上的位置
|
2023-09-20 18:20:56 +08:00
|
|
|
|
err = relateDevicesAndLink(repo, link, baseKm, visitedModelMap, devices...)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
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)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
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())
|
2023-09-20 15:14:38 +08:00
|
|
|
|
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...)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if nextIsTp {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return endTp, endKm, err
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 15:07:17 +08:00
|
|
|
|
// 关联正常(非道岔)物理区段与Link
|
2023-09-20 18:20:56 +08:00
|
|
|
|
func relatePhysicalSectionAndLink(repo *Repository, section *PhysicalSection, link *Link, baseKm *proto.Kilometer) {
|
|
|
|
|
link.bindPhysicalSections(section)
|
2024-01-10 14:06:00 +08:00
|
|
|
|
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)
|
2023-09-22 10:29:35 +08:00
|
|
|
|
section.aLinkPosition = &LinkPosition{
|
2023-09-20 18:20:56 +08:00
|
|
|
|
link: link,
|
2023-09-22 10:29:35 +08:00
|
|
|
|
offset: aOffset,
|
|
|
|
|
}
|
|
|
|
|
section.bLinkPosition = &LinkPosition{
|
2023-09-20 18:20:56 +08:00
|
|
|
|
link: link,
|
2023-09-22 10:29:35 +08:00
|
|
|
|
offset: bOffset,
|
|
|
|
|
}
|
2023-11-10 15:26:35 +08:00
|
|
|
|
section.bindLinkRange(link, aOffset, bOffset)
|
2023-09-20 18:20:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-20 15:14:38 +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 {
|
2023-09-25 14:38:13 +08:00
|
|
|
|
return fmt.Sprintf("%s-%s", tp.turnout.Id(), tp.port)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-20 18:20:56 +08:00
|
|
|
|
func relateDevicesAndLink(repo *Repository, link *Link, startKm *proto.Kilometer, visitedModelMap map[string]bool, devices ...Identity) error {
|
2023-09-20 15:14:38 +08:00
|
|
|
|
for _, device := range devices {
|
|
|
|
|
if visitedModelMap[device.Id()] {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
km := findModelKm(device)
|
2023-09-25 14:38:13 +08:00
|
|
|
|
if km == nil || km.CoordinateSystem == "" {
|
2023-09-20 15:14:38 +08:00
|
|
|
|
continue
|
|
|
|
|
}
|
2024-01-10 14:06:00 +08:00
|
|
|
|
convertedKm, err := ConvertKilometer(repo, km, startKm.CoordinateSystem)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
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)
|
|
|
|
|
}
|
2024-01-11 10:59:02 +08:00
|
|
|
|
link.bindDevices(device)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 15:07:17 +08:00
|
|
|
|
// 互相关联Link和道岔及道岔区段
|
|
|
|
|
func interrelateLinkAndTurnout(repo *Repository, linkZeroKm *proto.Kilometer, tp *TurnoutPort, linkPort *LinkPort) {
|
|
|
|
|
//关联Link和道岔
|
2023-09-20 15:14:38 +08:00
|
|
|
|
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)
|
2024-01-10 14:06:00 +08:00
|
|
|
|
tpKm, _ = ConvertKilometer(repo, tpKm, linkZeroKm.CoordinateSystem)
|
2023-11-10 15:07:17 +08:00
|
|
|
|
offset := number.Abs(tpKm.Value - linkZeroKm.Value)
|
2023-09-20 18:20:56 +08:00
|
|
|
|
tp.turnout.bindLinkPosition(tp.port, &LinkPosition{
|
|
|
|
|
link: linkPort.link,
|
|
|
|
|
offset: offset,
|
|
|
|
|
})
|
2023-09-20 15:14:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 15:07:17 +08:00
|
|
|
|
// 构建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
|
2023-11-10 15:26:35 +08:00
|
|
|
|
section.bindLinkRange(link, 0, link.length)
|
2023-11-10 15:07:17 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if link.aRelation != nil {
|
|
|
|
|
aSection := link.aRelation.turnout.section //Link A端的关联的道岔物理区段
|
|
|
|
|
aLinkPosition := link.aRelation.turnout.FindLinkPositionByPort(link.aRelation.port) //Link A端道岔端点在Link上的位置
|
2023-11-10 15:26:35 +08:00
|
|
|
|
aSection.bindLinkRange(link, 0, aLinkPosition.offset)
|
2023-11-10 15:07:17 +08:00
|
|
|
|
}
|
|
|
|
|
if link.bRelation != nil {
|
|
|
|
|
bSection := link.bRelation.turnout.section //Link A端的关联的道岔物理区段
|
|
|
|
|
bLinkPosition := link.bRelation.turnout.FindLinkPositionByPort(link.bRelation.port) //Link A端道岔端点在Link上的位置
|
2023-11-10 15:26:35 +08:00
|
|
|
|
bSection.bindLinkRange(link, bLinkPosition.offset, link.length)
|
2023-11-10 15:07:17 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-20 15:14:38 +08:00
|
|
|
|
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) {
|
|
|
|
|
|
2024-01-10 14:06:00 +08:00
|
|
|
|
endKm, err := ConvertKilometer(repo, endKm, startKm.CoordinateSystem)
|
2023-09-20 15:14:38 +08:00
|
|
|
|
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同坐标系的公里标
|
2023-09-25 14:38:13 +08:00
|
|
|
|
linkAKm := link.aKm
|
|
|
|
|
linkBKm := link.bKm
|
|
|
|
|
if linkAKm.CoordinateSystem != startKm.CoordinateSystem || linkAKm.Direction != startKm.Direction {
|
2023-09-20 15:14:38 +08:00
|
|
|
|
continue
|
|
|
|
|
}
|
2023-09-25 14:38:13 +08:00
|
|
|
|
if linkBKm.CoordinateSystem != startKm.CoordinateSystem || linkBKm.Direction != startKm.Direction {
|
2023-09-20 15:14:38 +08:00
|
|
|
|
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
|
|
|
|
|
}
|
2023-10-17 15:05:13 +08:00
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
}
|
2023-12-06 16:43:29 +08:00
|
|
|
|
for _, group := range protoData.ElectronicGroup {
|
|
|
|
|
if group.Code == "SPKS" {
|
2023-12-06 17:43:08 +08:00
|
|
|
|
station.spksComponents = append(station.spksComponents, buildRelComponent(repo, group.Components)...)
|
2023-12-06 16:43:29 +08:00
|
|
|
|
} 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)
|
2023-10-17 15:05:13 +08:00
|
|
|
|
}
|
2023-12-06 16:43:29 +08:00
|
|
|
|
|
2023-10-17 15:05:13 +08:00
|
|
|
|
}
|
2023-11-09 14:26:08 +08:00
|
|
|
|
if len(protoData.Deccs) > 0 { // 暂时让逻辑运行起来,后需重构
|
|
|
|
|
station.deviceEcc = protoData.Deccs
|
|
|
|
|
}
|
2023-10-17 15:05:13 +08:00
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-06 16:43:29 +08:00
|
|
|
|
func buildRelComponent(repo *Repository, comps []*proto.ElectronicComponent) []IGroupedElectronicComponent {
|
|
|
|
|
var components []IGroupedElectronicComponent
|
2023-10-17 15:05:13 +08:00
|
|
|
|
for _, data := range comps {
|
|
|
|
|
switch data.DeviceType {
|
|
|
|
|
case proto.DeviceType_DeviceType_Alarm:
|
2023-12-06 16:43:29 +08:00
|
|
|
|
components = append(components, repo.alarmMap[data.Id])
|
2023-10-17 15:05:13 +08:00
|
|
|
|
case proto.DeviceType_DeviceType_Button:
|
2023-12-06 16:43:29 +08:00
|
|
|
|
components = append(components, repo.buttonMap[data.Id])
|
2023-10-17 15:05:13 +08:00
|
|
|
|
case proto.DeviceType_DeviceType_Relay:
|
2023-12-06 16:43:29 +08:00
|
|
|
|
components = append(components, repo.relayMap[data.Id])
|
2023-10-17 17:47:19 +08:00
|
|
|
|
case proto.DeviceType_DeviceType_Light:
|
2023-12-06 16:43:29 +08:00
|
|
|
|
components = append(components, repo.lightMap[data.Id])
|
2023-10-20 13:13:44 +08:00
|
|
|
|
case proto.DeviceType_DeviceType_Key:
|
2023-12-06 16:43:29 +08:00
|
|
|
|
components = append(components, repo.keyMap[data.Id])
|
2023-10-17 15:05:13 +08:00
|
|
|
|
default:
|
|
|
|
|
slog.Warn("IBP未绑定【%s】类型设备", data.DeviceType.String())
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-06 16:43:29 +08:00
|
|
|
|
return components
|
2023-10-17 15:05:13 +08:00
|
|
|
|
}
|