rtss-core/repo/repo.go
soul-walker 1a610c01ba 调整公里标概念,添加注释说明
完善道岔区段与道岔关系构建检查
添加Link位置和link范围工具类
完善example道岔区段关系构建
2024-07-10 18:09:16 +08:00

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
}