323 lines
10 KiB
Go
323 lines
10 KiB
Go
package repo
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"math"
|
|
"strings"
|
|
|
|
"joylink.club/rtss-core/model"
|
|
)
|
|
|
|
type Repo interface {
|
|
// 模型仓库id
|
|
Id() string
|
|
GetStationByUid(uid string) model.Station
|
|
GetSectionByUid(uid string) model.Section
|
|
GetTurnoutByUid(uid string) model.Turnout
|
|
// 检查区段、道岔通道连接关系
|
|
CheckSectionAndTurnoutPipeLink() error
|
|
// 检查区段、道岔公里标
|
|
CheckSectionAndTurnoutPortKms() error
|
|
// 检查Link、LinkNode通道连接关系
|
|
CheckLinkAndLinkNodePipeLink() error
|
|
// 转换公里标
|
|
ConvertKilometerMark(km *model.KilometerMark, targetCoordinate string) (int64, error)
|
|
// 计算公里标间距离
|
|
CalculateKmDistance(km1 *model.KilometerMark, km2 *model.KilometerMark) (int64, error)
|
|
}
|
|
|
|
var _ Repo = (*RepoImpl)(nil)
|
|
|
|
type RepoImpl struct {
|
|
id string
|
|
BuildErrorInfos []error
|
|
StationMap map[string]model.Station
|
|
PhysicalSectionMap map[string]model.PhysicalSection
|
|
TurnoutSectionMap map[string]model.TurnoutSection
|
|
TurnoutMap map[string]model.Turnout
|
|
LinkNodeMap map[string]model.LinkNode
|
|
LinkMap map[string]model.Link
|
|
KilometerMarkConverters []*model.KilometerMarkConverter
|
|
}
|
|
|
|
func NewRepo(id string) *RepoImpl {
|
|
return &RepoImpl{
|
|
id: id,
|
|
StationMap: make(map[string]model.Station),
|
|
PhysicalSectionMap: make(map[string]model.PhysicalSection),
|
|
TurnoutSectionMap: make(map[string]model.TurnoutSection),
|
|
TurnoutMap: make(map[string]model.Turnout),
|
|
LinkNodeMap: make(map[string]model.LinkNode),
|
|
LinkMap: make(map[string]model.Link),
|
|
KilometerMarkConverters: make([]*model.KilometerMarkConverter, 0),
|
|
}
|
|
}
|
|
|
|
func (r *RepoImpl) ConvertKilometerMark(km *model.KilometerMark, targetCoordinate string) (int64, error) {
|
|
if km.Coordinate() == targetCoordinate {
|
|
return km.Value(), nil
|
|
}
|
|
for _, converter := range r.KilometerMarkConverters {
|
|
if converter.IsMatch(km, targetCoordinate) {
|
|
return converter.Convert(km, targetCoordinate), nil
|
|
}
|
|
}
|
|
existConfigs := make([]string, 0)
|
|
for _, converter := range r.KilometerMarkConverters {
|
|
existConfigs = append(existConfigs, converter.Debug())
|
|
}
|
|
return 0, fmt.Errorf("未找到公里标转换配置: %s<->%s, 全部配置项为: %s", km.Coordinate(), targetCoordinate, strings.Join(existConfigs, ","))
|
|
}
|
|
|
|
func (r *RepoImpl) CalculateKmDistance(km1 *model.KilometerMark, km2 *model.KilometerMark) (int64, error) {
|
|
if km1.Coordinate() == km2.Coordinate() {
|
|
return int64(math.Abs(float64(km2.Value() - km1.Value()))), nil
|
|
} else {
|
|
km1Value, err := r.ConvertKilometerMark(km1, km2.Coordinate())
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return int64(math.Abs(float64(km2.Value() - km1Value))), nil
|
|
}
|
|
}
|
|
|
|
func (r *RepoImpl) CheckSectionAndTurnoutPortKms() error {
|
|
for _, section := range r.PhysicalSectionMap {
|
|
slog.Debug("检查区段公里标", "uid", section.Uid(), "A端公里标", section.GetPaKm(), "B端公里标", section.GetPbKm())
|
|
if section.GetPaKm() == nil {
|
|
r.BuildErrorInfos = append(r.BuildErrorInfos, fmt.Errorf("区段[uid=%s]无A端公里标", section.Uid()))
|
|
}
|
|
if section.GetPbKm() == nil {
|
|
r.BuildErrorInfos = append(r.BuildErrorInfos, fmt.Errorf("区段[uid=%s]无B端公里标", section.Uid()))
|
|
}
|
|
if section.GetPaKm() != nil && section.GetPbKm() != nil {
|
|
section.CalculateDistance(r.CalculateKmDistance)
|
|
}
|
|
}
|
|
for _, turnout := range r.TurnoutMap {
|
|
slog.Debug("检查道岔公里标", "uid", turnout.Uid(), "公里标", turnout.GetKm(), "A端公里标", turnout.GetPaKm(), "B端公里标", turnout.GetPbKm(), "C端公里标", turnout.GetPcKm())
|
|
if turnout.GetKm() == nil {
|
|
r.BuildErrorInfos = append(r.BuildErrorInfos, fmt.Errorf("道岔[uid=%s]无公里标", turnout.Uid()))
|
|
}
|
|
if turnout.GetPaKm() == nil {
|
|
r.BuildErrorInfos = append(r.BuildErrorInfos, fmt.Errorf("道岔[uid=%s]无A端公里标", turnout.Uid()))
|
|
}
|
|
if turnout.GetPbKm() == nil {
|
|
r.BuildErrorInfos = append(r.BuildErrorInfos, fmt.Errorf("道岔[uid=%s]无B端公里标", turnout.Uid()))
|
|
}
|
|
if turnout.GetPcKm() == nil {
|
|
r.BuildErrorInfos = append(r.BuildErrorInfos, fmt.Errorf("道岔[uid=%s]无C端公里标", turnout.Uid()))
|
|
}
|
|
if turnout.GetKm() != nil && turnout.GetPaKm() != nil && turnout.GetPbKm() != nil && turnout.GetPcKm() != nil {
|
|
turnout.CalculateDistances(r.CalculateKmDistance)
|
|
}
|
|
}
|
|
if len(r.BuildErrorInfos) > 0 {
|
|
return errors.Join(r.BuildErrorInfos...)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CheckSectionAndTurnoutPipeLink implements Repo.
|
|
func (r *RepoImpl) CheckSectionAndTurnoutPipeLink() error {
|
|
for _, section := range r.PhysicalSectionMap {
|
|
err := section.CheckPipeLink()
|
|
if err != nil {
|
|
r.BuildErrorInfos = append(r.BuildErrorInfos, err)
|
|
}
|
|
}
|
|
for _, tsModel := range r.TurnoutSectionMap {
|
|
turnouts := make([]string, 0)
|
|
for _, turnout := range tsModel.GetTurnouts() {
|
|
turnouts = append(turnouts, turnout.Uid())
|
|
}
|
|
slog.Debug("检查道岔区段关联道岔", "uid", tsModel.Uid(), "关联道岔", fmt.Sprintf("[%s]", strings.Join(turnouts, ",")))
|
|
if len(tsModel.GetTurnouts()) == 0 {
|
|
r.BuildErrorInfos = append(r.BuildErrorInfos, fmt.Errorf("道岔区段[uid=%s]未关联道岔", tsModel.Uid()))
|
|
}
|
|
}
|
|
for _, turnout := range r.TurnoutMap {
|
|
err := turnout.CheckPipeLink()
|
|
if err != nil {
|
|
r.BuildErrorInfos = append(r.BuildErrorInfos, err)
|
|
}
|
|
}
|
|
if len(r.BuildErrorInfos) > 0 {
|
|
return errors.Join(r.BuildErrorInfos...)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CheckLinkAndLinkNodePipeLink implements Repo.
|
|
func (r *RepoImpl) CheckLinkAndLinkNodePipeLink() error {
|
|
slog.Debug("检查通道连接关系", "link数量", len(r.LinkMap))
|
|
for _, link := range r.LinkMap {
|
|
err := link.CheckPipeLink()
|
|
if err != nil {
|
|
r.BuildErrorInfos = append(r.BuildErrorInfos, err)
|
|
}
|
|
}
|
|
for _, linkNode := range r.LinkNodeMap {
|
|
err := linkNode.CheckPipeLink()
|
|
if err != nil {
|
|
r.BuildErrorInfos = append(r.BuildErrorInfos, err)
|
|
}
|
|
}
|
|
if len(r.BuildErrorInfos) > 0 {
|
|
return errors.Join(r.BuildErrorInfos...)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetSectionByUid implements Repo.
|
|
func (r *RepoImpl) GetSectionByUid(uid string) model.Section {
|
|
return r.PhysicalSectionMap[uid]
|
|
}
|
|
|
|
// GetStationByUid implements Repo.
|
|
func (r *RepoImpl) GetStationByUid(uid string) model.Station {
|
|
return r.StationMap[uid]
|
|
}
|
|
|
|
// GetTurnoutByUid implements Repo.
|
|
func (r *RepoImpl) GetTurnoutByUid(uid string) model.Turnout {
|
|
return r.TurnoutMap[uid]
|
|
}
|
|
|
|
// Id implements Repo.
|
|
func (r *RepoImpl) Id() string {
|
|
return r.id
|
|
}
|
|
|
|
func (r *RepoImpl) BuildLinks() {
|
|
for _, turnout := range r.TurnoutMap {
|
|
walkOverTurnouts(turnout, r)
|
|
break // 从一个道岔遍历所有区段、道岔
|
|
}
|
|
}
|
|
|
|
func walkOverTurnouts(turnout model.Turnout, repo1 *RepoImpl) {
|
|
paNext := walkFromTurnoutPortToNextAndBuildLink(turnout, model.PipePortA, repo1)
|
|
if paNext != nil {
|
|
walkOverTurnouts(paNext, repo1)
|
|
}
|
|
pbNext := walkFromTurnoutPortToNextAndBuildLink(turnout, model.PipePortB, repo1)
|
|
if pbNext != nil {
|
|
walkOverTurnouts(pbNext, repo1)
|
|
}
|
|
pcNext := walkFromTurnoutPortToNextAndBuildLink(turnout, model.PipePortC, repo1)
|
|
if pcNext != nil {
|
|
walkOverTurnouts(pcNext, repo1)
|
|
}
|
|
}
|
|
|
|
func walkFromTurnoutPortToNextAndBuildLink(turnout model.Turnout, port model.PipePort, repo1 *RepoImpl) model.Turnout {
|
|
slog.Debug("walkFromTurnoutPortToNextAndBuildLink", "道岔id", turnout.Uid(), "端口", port)
|
|
ple := turnout.GetLinkedElement(port)
|
|
pathSections := make([]model.PhysicalSection, 0)
|
|
var nextTurnout model.Turnout = nil
|
|
var nextPort model.PipePort
|
|
for {
|
|
if ple == nil { // 到尽头
|
|
nextPort = model.PipePortA
|
|
break
|
|
}
|
|
if ple.Pipe.IsThreePorts() { // 到下一个道岔
|
|
nextTurnout = ple.Pipe.(model.Turnout)
|
|
nextPort = ple.Port
|
|
break
|
|
} else { // 到下一个区段
|
|
section := ple.Pipe.(model.PhysicalSection)
|
|
pathSections = append(pathSections, section)
|
|
ple = section.OppositePipeLink(ple.Port)
|
|
}
|
|
}
|
|
link, exist := NewLinkNodeAndLinkAndBuildLinkship(turnout, port, nextTurnout, nextPort, repo1)
|
|
if exist {
|
|
slog.Debug("构建Link已存在", "LinkUid", link.Uid())
|
|
return nil
|
|
}
|
|
repo1.LinkMap[link.Uid()] = link // 添加到仓库
|
|
// 构建link的长度和道岔以及区段的映射
|
|
buildLinkLengthAndElementMappings(link.(*model.LinkImpl), pathSections, repo1)
|
|
return nextTurnout
|
|
}
|
|
|
|
func buildLinkLengthAndElementMappings(link *model.LinkImpl, pathSections []model.PhysicalSection, repo1 *RepoImpl) {
|
|
len := int64(0)
|
|
pale := link.GetLinkedElement(model.PipePortA)
|
|
if pale == nil {
|
|
panic("构建Link长度错误: A端通道连接关系不能为空")
|
|
}
|
|
len += pale.Pipe.(model.LinkNode).Turnout().GetLength(pale.Port)
|
|
pble := link.GetLinkedElement(model.PipePortB)
|
|
if pble != nil {
|
|
len += pble.Pipe.(model.LinkNode).Turnout().GetLength(pble.Port)
|
|
}
|
|
for _, section := range pathSections {
|
|
len += section.GetLength()
|
|
}
|
|
link.SetLength(len)
|
|
slog.Debug("构建Link长度", "LinkUid", link.Uid(), "长度(m)", len/1000)
|
|
}
|
|
|
|
func (r *RepoImpl) getOrBuildLinkNode(turnout model.Turnout) model.LinkNode {
|
|
node := r.LinkNodeMap[turnout.Uid()]
|
|
if node == nil {
|
|
node = model.NewLinkNode(turnout)
|
|
r.LinkNodeMap[turnout.Uid()] = node
|
|
}
|
|
return node
|
|
}
|
|
|
|
func (r *RepoImpl) isLinkExist(pla *model.PipeLink, plb *model.PipeLink) (bool, model.Link) {
|
|
for _, link := range r.LinkMap {
|
|
if link.IsEquals(pla, plb) {
|
|
return true, link
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func NewLinkNodeAndLinkAndBuildLinkship(ta model.Turnout, tap model.PipePort, tb model.Turnout, tbp model.PipePort, repo1 *RepoImpl) (link model.Link, exist bool) {
|
|
if ta == nil && tb == nil {
|
|
panic("构造LinkNode错误: 道岔不能都为空")
|
|
}
|
|
var nodea model.LinkNode
|
|
var nodeb model.LinkNode
|
|
var pla *model.PipeLink
|
|
var plb *model.PipeLink
|
|
if ta == nil {
|
|
nodea = nil
|
|
nodeb = repo1.getOrBuildLinkNode(tb)
|
|
pla = nil
|
|
plb = model.NewPipeLink(nodeb, tbp)
|
|
} else if tb == nil {
|
|
nodea = repo1.getOrBuildLinkNode(ta)
|
|
nodeb = nil
|
|
pla = model.NewPipeLink(nodea, tap)
|
|
plb = nil
|
|
} else {
|
|
nodea = repo1.getOrBuildLinkNode(ta)
|
|
nodeb = repo1.getOrBuildLinkNode(tb)
|
|
pla = model.NewPipeLink(nodea, tap)
|
|
plb = model.NewPipeLink(nodeb, tbp)
|
|
}
|
|
exist, link = repo1.isLinkExist(pla, plb)
|
|
if exist {
|
|
return
|
|
}
|
|
link = model.NewLink(pla, plb)
|
|
link.SetLinkedElement(model.PipePortA, pla)
|
|
link.SetLinkedElement(model.PipePortB, plb)
|
|
if nodea != nil {
|
|
nodea.SetLinkedElement(tap, model.NewPipeLink(link, model.PipePortA))
|
|
}
|
|
if nodeb != nil {
|
|
nodeb.SetLinkedElement(tbp, model.NewPipeLink(link, model.PipePortB))
|
|
}
|
|
return
|
|
}
|