调整公里标概念,添加注释说明
完善道岔区段与道岔关系构建检查 添加Link位置和link范围工具类 完善example道岔区段关系构建
This commit is contained in:
parent
97445763fd
commit
1a610c01ba
|
@ -17,49 +17,10 @@ func main() {
|
|||
rtssGraphicStorage := data_proto.GetXian6STYG()
|
||||
dataMapping := repository.NewDataMapping("1", rtssGraphicStorage)
|
||||
repo1.DataMapping["1"] = dataMapping
|
||||
// log.Println(rtssGraphicStorage.Stations)
|
||||
// log.Println(rtssGraphicStorage.Section)
|
||||
// log.Println(rtssGraphicStorage.Turnouts)
|
||||
for _, station := range rtssGraphicStorage.Stations {
|
||||
dataMapping.StationDataMap[station.Common.Id] = station
|
||||
uid := station.Code
|
||||
dataMapping.AddIdMapping(repository.NewIdMapping(station.Common.Id, uid))
|
||||
stationModel := model.NewStation(uid, station.ConcentrationStations)
|
||||
repo1.StationMap[uid] = &modelimpl.Station{StationImpl: stationModel}
|
||||
}
|
||||
for _, section := range rtssGraphicStorage.Section {
|
||||
if section.CentralizedStations == nil || len(section.CentralizedStations) == 0 {
|
||||
continue
|
||||
}
|
||||
belongStation := dataMapping.StationDataMap[section.CentralizedStations[0]]
|
||||
if belongStation == nil {
|
||||
continue
|
||||
}
|
||||
dataMapping.SectionDataMap[section.Common.Id] = section
|
||||
uid := getSectionUid(section, belongStation, dataMapping.GetLineInfo())
|
||||
dataMapping.AddIdMapping(repository.NewIdMapping(section.Common.Id, uid))
|
||||
if section.SectionType == data_proto.Section_Physical {
|
||||
sectionModel := model.NewPhysicalSection(uid)
|
||||
repo1.PhysicalSectionMap[uid] = &modelimpl.PhysicalSection{PhysicalSectionImpl: sectionModel}
|
||||
} else {
|
||||
tsModel := model.NewTurnoutSection(uid)
|
||||
repo1.TurnoutSectionMap[uid] = tsModel
|
||||
}
|
||||
}
|
||||
for _, turnout := range rtssGraphicStorage.Turnouts {
|
||||
if turnout.CentralizedStations == nil || len(turnout.CentralizedStations) == 0 {
|
||||
continue
|
||||
}
|
||||
belongStation := dataMapping.StationDataMap[turnout.CentralizedStations[0]]
|
||||
if belongStation == nil {
|
||||
continue
|
||||
}
|
||||
dataMapping.TurnoutDataMap[turnout.Common.Id] = turnout
|
||||
uid := getTurnoutUid(turnout, belongStation, dataMapping.GetLineInfo())
|
||||
dataMapping.AddIdMapping(repository.NewIdMapping(turnout.Common.Id, uid))
|
||||
turnoutModel := model.NewTurnout(uid)
|
||||
repo1.TurnoutMap[uid] = &modelimpl.Turnout{TurnoutImpl: turnoutModel}
|
||||
}
|
||||
basicDataMappingAndModelBuild(dataMapping, repo1)
|
||||
|
||||
// 构建公里标转换器
|
||||
buildKilometerMarkConverters(dataMapping, repo1)
|
||||
|
||||
// 构建区段关系
|
||||
buildSectionRelationships(dataMapping, repo1)
|
||||
|
@ -92,14 +53,80 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
func basicDataMappingAndModelBuild(dataMapping *repository.DataMapping, repo1 *repository.Repository) {
|
||||
// 车站
|
||||
for _, station := range dataMapping.Stations {
|
||||
dataMapping.StationDataMap[station.Common.Id] = station
|
||||
uid := station.Code
|
||||
dataMapping.AddIdMapping(repository.NewIdMapping(station.Common.Id, uid))
|
||||
stationModel := model.NewStation(uid, station.ConcentrationStations)
|
||||
repo1.StationMap[uid] = &modelimpl.Station{StationImpl: stationModel}
|
||||
}
|
||||
// 物理区段
|
||||
for _, section := range dataMapping.Section {
|
||||
if section.CentralizedStations == nil || len(section.CentralizedStations) == 0 {
|
||||
continue
|
||||
}
|
||||
belongStation := dataMapping.StationDataMap[section.CentralizedStations[0]]
|
||||
if belongStation == nil {
|
||||
continue
|
||||
}
|
||||
dataMapping.SectionDataMap[section.Common.Id] = section
|
||||
uid := getSectionUid(section, belongStation, dataMapping.GetLineInfo())
|
||||
dataMapping.AddIdMapping(repository.NewIdMapping(section.Common.Id, uid))
|
||||
if section.SectionType == data_proto.Section_Physical {
|
||||
sectionModel := model.NewPhysicalSection(uid)
|
||||
repo1.PhysicalSectionMap[uid] = &modelimpl.PhysicalSection{PhysicalSectionImpl: sectionModel}
|
||||
} else {
|
||||
tsModel := model.NewTurnoutSection(uid)
|
||||
repo1.TurnoutSectionMap[uid] = tsModel
|
||||
}
|
||||
}
|
||||
// 道岔
|
||||
for _, turnout := range dataMapping.Turnouts {
|
||||
if turnout.CentralizedStations == nil || len(turnout.CentralizedStations) == 0 {
|
||||
continue
|
||||
}
|
||||
belongStation := dataMapping.StationDataMap[turnout.CentralizedStations[0]]
|
||||
if belongStation == nil {
|
||||
continue
|
||||
}
|
||||
dataMapping.TurnoutDataMap[turnout.Common.Id] = turnout
|
||||
uid := getTurnoutUid(turnout, belongStation, dataMapping.GetLineInfo())
|
||||
dataMapping.AddIdMapping(repository.NewIdMapping(turnout.Common.Id, uid))
|
||||
turnoutModel := model.NewTurnout(uid)
|
||||
repo1.TurnoutMap[uid] = &modelimpl.Turnout{TurnoutImpl: turnoutModel}
|
||||
}
|
||||
// 区段检测点
|
||||
for _, axleCounting := range dataMapping.AxleCountings {
|
||||
dataMapping.SectionCheckPointMap[axleCounting.Common.Id] = axleCounting
|
||||
}
|
||||
}
|
||||
|
||||
func buildKilometerMarkConverters(dataMapping *repository.DataMapping, repo1 *repository.Repository) {
|
||||
if dataMapping.KilometerConvertList == nil || len(dataMapping.KilometerConvertList) == 0 {
|
||||
slog.Info("没有公里标转换数据")
|
||||
return
|
||||
}
|
||||
for _, kmConverter := range dataMapping.KilometerConvertList {
|
||||
km1 := convertKs2Km(kmConverter.KmA)
|
||||
km2 := convertKs2Km(kmConverter.KmB)
|
||||
repo1.KilometerMarkConverters = append(repo1.KilometerMarkConverters,
|
||||
model.NewKilometerMarkConverter(km1, km2, kmConverter.SameTrend))
|
||||
}
|
||||
}
|
||||
|
||||
func convertKs2Km(ks *data_proto.KilometerSystem) *model.KilometerMark {
|
||||
return model.NewKilometerMark(ks.CoordinateSystem, ks.Kilometer)
|
||||
}
|
||||
|
||||
// 构建区段、道岔公里标
|
||||
func buildKilometerMark(dataMapping *repository.DataMapping, repo1 *repository.Repository) {
|
||||
for _, turnout := range dataMapping.TurnoutDataMap {
|
||||
if len(turnout.KilometerSystem) == 0 {
|
||||
repo1.BuildErrorInfos = append(repo1.BuildErrorInfos, fmt.Errorf("构建区段、道岔公里标数据错误:道岔[id=%d]未关联任何公里标", turnout.Common.Id))
|
||||
} else {
|
||||
tks := turnout.KilometerSystem[0]
|
||||
km := model.NewKilometerMark(tks.CoordinateSystem, convertKmDirection(tks.Direction), tks.Kilometer)
|
||||
km := convertKs2Km(turnout.KilometerSystem[0])
|
||||
turnoutModel := repo1.TurnoutMap[dataMapping.IdMappingMap[turnout.Common.Id].Uid]
|
||||
turnoutModel.(*modelimpl.Turnout).Km = km
|
||||
}
|
||||
|
@ -120,7 +147,7 @@ func buildKilometerMark(dataMapping *repository.DataMapping, repo1 *repository.R
|
|||
if !ok {
|
||||
panic(fmt.Errorf("构建区段、道岔公里标数据错误:检测点[id=%d]关联的区段模型[uid=%s]不存在", checkpoint.Common.Id, idmapping.Uid))
|
||||
}
|
||||
km := model.NewKilometerMark(checkpoint.KilometerSystem.CoordinateSystem, convertKmDirection(checkpoint.KilometerSystem.Direction), checkpoint.KilometerSystem.Kilometer)
|
||||
km := convertKs2Km(checkpoint.KilometerSystem)
|
||||
if linkship.DevicePort == data_proto.RelatedRef_A {
|
||||
sectionModel.(*modelimpl.PhysicalSection).PaKm = km
|
||||
} else if linkship.DevicePort == data_proto.RelatedRef_B {
|
||||
|
@ -133,7 +160,7 @@ func buildKilometerMark(dataMapping *repository.DataMapping, repo1 *repository.R
|
|||
if !ok {
|
||||
panic(fmt.Errorf("构建区段、道岔公里标数据错误:检测点[id=%d]关联的道岔模型[uid=%s]不存在", checkpoint.Common.Id, idmapping.Uid))
|
||||
}
|
||||
km := model.NewKilometerMark(checkpoint.KilometerSystem.CoordinateSystem, convertKmDirection(checkpoint.KilometerSystem.Direction), checkpoint.KilometerSystem.Kilometer)
|
||||
km := convertKs2Km(checkpoint.KilometerSystem)
|
||||
if linkship.DevicePort == data_proto.RelatedRef_A {
|
||||
turnoutModel.(*modelimpl.Turnout).PaKm = km
|
||||
} else if linkship.DevicePort == data_proto.RelatedRef_B {
|
||||
|
@ -150,16 +177,6 @@ func buildKilometerMark(dataMapping *repository.DataMapping, repo1 *repository.R
|
|||
}
|
||||
}
|
||||
|
||||
func convertKmDirection(kddata data_proto.KilometerSystem_Direction) model.OperationDirection {
|
||||
if kddata == data_proto.KilometerSystem_LEFT {
|
||||
return model.OperationDirectionDown
|
||||
}
|
||||
if kddata == data_proto.KilometerSystem_RIGHT {
|
||||
return model.OperationDirectionUp
|
||||
}
|
||||
panic(fmt.Errorf("未知的公里标方向类型: %v", kddata))
|
||||
}
|
||||
|
||||
func buildTurnoutRelationships(dataMapping *repository.DataMapping, repo1 *repository.Repository) {
|
||||
for _, turnout := range dataMapping.TurnoutDataMap {
|
||||
idmapping := dataMapping.IdMappingMap[turnout.Common.Id]
|
||||
|
@ -175,13 +192,41 @@ func buildTurnoutRelationships(dataMapping *repository.DataMapping, repo1 *repos
|
|||
|
||||
func buildSectionRelationships(dataMapping *repository.DataMapping, repo1 *repository.Repository) {
|
||||
for _, section := range dataMapping.SectionDataMap {
|
||||
idmapping := dataMapping.IdMappingMap[section.Common.Id]
|
||||
if idmapping == nil {
|
||||
panic(fmt.Errorf("构建区段关系错误:idmapping异常为空"))
|
||||
}
|
||||
idmapping := dataMapping.GetIdMapping(section.Common.Id)
|
||||
if section.SectionType == data_proto.Section_Physical {
|
||||
sectionModel := repo1.PhysicalSectionMap[idmapping.Uid]
|
||||
buildSectionPortLinkRelation(section.PaRef, sectionModel, model.PipePortA, repo1, dataMapping)
|
||||
buildSectionPortLinkRelation(section.PbRef, sectionModel, model.PipePortB, repo1, dataMapping)
|
||||
} else {
|
||||
tsModel := repo1.TurnoutSectionMap[idmapping.Uid]
|
||||
turnoutPorts := make(map[uint32]map[model.PipePort]struct{})
|
||||
for _, axleId := range section.AxleCountings {
|
||||
slog.Debug("区段关联检测点", "sectionId", section.Common.Id, "axleId", axleId)
|
||||
axleCounting := dataMapping.SectionCheckPointMap[axleId]
|
||||
if axleCounting == nil {
|
||||
repo1.BuildErrorInfos = append(repo1.BuildErrorInfos, fmt.Errorf("构建区段关系错误:区段[id=%d]关联的检测点[id=%d]不存在", section.Common.Id, axleId))
|
||||
continue
|
||||
}
|
||||
for _, ref := range axleCounting.AxleCountingRef {
|
||||
if ref.DeviceType == data_proto.RelatedRef_Turnout {
|
||||
portMap := turnoutPorts[ref.Id]
|
||||
if portMap == nil {
|
||||
portMap = make(map[model.PipePort]struct{})
|
||||
turnoutPorts[ref.Id] = portMap
|
||||
}
|
||||
portMap[convertPort(ref.DevicePort)] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
slog.Debug("区段关联检测点关联道岔", "sectionId", section.Common.Id, "turnoutPorts", turnoutPorts)
|
||||
for turnoutId, portMap := range turnoutPorts {
|
||||
if len(portMap) == 3 {
|
||||
turnoutModel := repo1.TurnoutMap[dataMapping.GetIdMapping(turnoutId).Uid]
|
||||
tsModel.AddTurnout(turnoutModel.(*modelimpl.Turnout))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,10 +26,19 @@ type DataMapping struct {
|
|||
StationDataMap map[uint32]*data_proto.Station
|
||||
SectionDataMap map[uint32]*data_proto.Section
|
||||
TurnoutDataMap map[uint32]*data_proto.Turnout
|
||||
SectionCheckPointMap map[uint32]*data_proto.AxleCounting
|
||||
IdMappingMap map[uint32]*IdMapping
|
||||
UidMappingMap map[string]*IdMapping
|
||||
}
|
||||
|
||||
func (d *DataMapping) GetIdMapping(graphicId uint32) *IdMapping {
|
||||
mapping, ok := d.IdMappingMap[graphicId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("不存在{graphicId=%v}的IdMapping数据", graphicId))
|
||||
}
|
||||
return mapping
|
||||
}
|
||||
|
||||
func (d *DataMapping) AddIdMapping(idMapping *IdMapping) {
|
||||
d.IdMappingMap[idMapping.GraphicId] = idMapping
|
||||
d.UidMappingMap[idMapping.Uid] = idMapping
|
||||
|
@ -47,6 +56,7 @@ func NewDataMapping(id string, storage *data_proto.RtssGraphicStorage) *DataMapp
|
|||
StationDataMap: make(map[uint32]*data_proto.Station),
|
||||
SectionDataMap: make(map[uint32]*data_proto.Section),
|
||||
TurnoutDataMap: make(map[uint32]*data_proto.Turnout),
|
||||
SectionCheckPointMap: make(map[uint32]*data_proto.AxleCounting),
|
||||
IdMappingMap: make(map[uint32]*IdMapping),
|
||||
UidMappingMap: make(map[string]*IdMapping),
|
||||
}
|
||||
|
|
|
@ -5,29 +5,22 @@ import (
|
|||
"strconv"
|
||||
)
|
||||
|
||||
// 运营方向(上行/下行)
|
||||
type OperationDirection int8
|
||||
|
||||
const (
|
||||
// 上行
|
||||
OperationDirectionUp OperationDirection = 1
|
||||
// 下行
|
||||
OperationDirectionDown OperationDirection = 0
|
||||
)
|
||||
|
||||
// 公里标
|
||||
// 类似地铁线路一般正线都是双线,公里标一般以类似YDK/ZDK开头,
|
||||
// 虽然从信号布置图上看,YDK/ZDK相近位置的值似乎是一样的,以为他们是同一个坐标系,
|
||||
// 但这种情况的原因其实是因为两条线的起始点很接近,他们依然是两个独立的坐标系。
|
||||
// 左线和右线中间有渡线可以相互切换,但公里标如何转换暂时不清楚,
|
||||
// 暂时考虑先通过添加转换配置来实现(比如转换可以配置0或根据道岔处勾股定理大概估算,虽然不同道岔处可能不一样,但暂时不考虑复杂情况)。
|
||||
type KilometerMark struct {
|
||||
// 公里标坐标系
|
||||
coordinate string
|
||||
// 公里标上下行方向
|
||||
direction OperationDirection
|
||||
// 公里标值
|
||||
value int64
|
||||
}
|
||||
|
||||
func NewKilometerMark(coordinate string, direction OperationDirection, value int64) *KilometerMark {
|
||||
func NewKilometerMark(coordinate string, value int64) *KilometerMark {
|
||||
return &KilometerMark{
|
||||
coordinate: coordinate,
|
||||
direction: direction,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
@ -47,12 +40,23 @@ func (km *KilometerMark) IsCoordinateEqual(coordinate string) bool {
|
|||
|
||||
// 公里标转换配置
|
||||
type KilometerMarkConverter struct {
|
||||
km1 KilometerMark
|
||||
km2 KilometerMark
|
||||
km1 *KilometerMark
|
||||
km2 *KilometerMark
|
||||
// 趋势是否相同
|
||||
trendSame bool
|
||||
}
|
||||
|
||||
func NewKilometerMarkConverter(km1, km2 *KilometerMark, trendSame bool) *KilometerMarkConverter {
|
||||
if km1 == nil || km2 == nil {
|
||||
panic("km1 or km2 is nil")
|
||||
}
|
||||
return &KilometerMarkConverter{
|
||||
km1: km1,
|
||||
km2: km2,
|
||||
trendSame: trendSame,
|
||||
}
|
||||
}
|
||||
|
||||
func (kmc *KilometerMarkConverter) Debug() string {
|
||||
return fmt.Sprintf("{%s<->%s(%s)}", kmc.km1.coordinate, kmc.km2.coordinate, strconv.FormatBool(kmc.trendSame))
|
||||
}
|
||||
|
|
|
@ -6,19 +6,11 @@ import (
|
|||
|
||||
func TestConvertKilometerMark(t *testing.T) {
|
||||
// 1. 配置公里标转换关系
|
||||
km1 := NewKilometerMark("DBCSK", OperationDirectionUp, 0)
|
||||
km2 := NewKilometerMark("DCSK", OperationDirectionUp, 200)
|
||||
kmc1 := &KilometerMarkConverter{
|
||||
km1: *km1,
|
||||
km2: *km2,
|
||||
trendSame: true,
|
||||
}
|
||||
km1 := NewKilometerMark("YDK", 0)
|
||||
km2 := NewKilometerMark("ZDK", 200)
|
||||
kmc1 := NewKilometerMarkConverter(km1, km2, true)
|
||||
t.Log(kmc1.Debug())
|
||||
kmc2 := &KilometerMarkConverter{
|
||||
km1: *km1,
|
||||
km2: *km2,
|
||||
trendSame: false,
|
||||
}
|
||||
kmc2 := *NewKilometerMarkConverter(km1, km2, false)
|
||||
t.Log(kmc2.Debug())
|
||||
|
||||
// 2. 验证公里标转换关系
|
||||
|
@ -28,12 +20,12 @@ func TestConvertKilometerMark(t *testing.T) {
|
|||
expect1 int64
|
||||
expect2 int64
|
||||
}{
|
||||
{km: NewKilometerMark("DBCSK", OperationDirectionUp, 0), coordinate: km2.Coordinate(), expect1: 200, expect2: 200},
|
||||
{km: NewKilometerMark("DBCSK", OperationDirectionUp, 100), coordinate: km2.Coordinate(), expect1: 300, expect2: 100},
|
||||
{km: NewKilometerMark("DBCSK", OperationDirectionUp, -100), coordinate: km2.Coordinate(), expect1: 100, expect2: 300},
|
||||
{km: NewKilometerMark("DCSK", OperationDirectionUp, 200), coordinate: km1.Coordinate(), expect1: 0, expect2: 0},
|
||||
{km: NewKilometerMark("DCSK", OperationDirectionUp, 300), coordinate: km1.Coordinate(), expect1: 100, expect2: -100},
|
||||
{km: NewKilometerMark("DCSK", OperationDirectionUp, 100), coordinate: km1.Coordinate(), expect1: -100, expect2: 100},
|
||||
{km: NewKilometerMark("YDK", 0), coordinate: km2.Coordinate(), expect1: 200, expect2: 200},
|
||||
{km: NewKilometerMark("YDK", 100), coordinate: km2.Coordinate(), expect1: 300, expect2: 100},
|
||||
{km: NewKilometerMark("YDK", -100), coordinate: km2.Coordinate(), expect1: 100, expect2: 300},
|
||||
{km: NewKilometerMark("ZDK", 200), coordinate: km1.Coordinate(), expect1: 0, expect2: 0},
|
||||
{km: NewKilometerMark("ZDK", 300), coordinate: km1.Coordinate(), expect1: 100, expect2: -100},
|
||||
{km: NewKilometerMark("ZDK", 100), coordinate: km1.Coordinate(), expect1: -100, expect2: 100},
|
||||
}
|
||||
for _, test := range tests {
|
||||
result1 := kmc1.Convert(test.km, test.coordinate)
|
||||
|
|
|
@ -3,27 +3,105 @@ package model
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
)
|
||||
|
||||
type RR = io.Writer
|
||||
|
||||
// 轨道
|
||||
// 默认坐标系为以A端为起点,B端为终点的单轴坐标系
|
||||
type Link interface {
|
||||
TwoPortsPipeElement
|
||||
IsEquals(pla *PipeLink, plb *PipeLink) bool
|
||||
// 获取轨道长度,单位:毫米mm
|
||||
GetLength() int64
|
||||
}
|
||||
|
||||
// 轨道连接点
|
||||
// 轨道连接点(使用的是道岔岔心),然后有三个方向A/B/C,还是使用PipePort枚举,但表示的含义是方向,而不是道岔的三个端口
|
||||
type LinkNode interface {
|
||||
Turnout() Turnout
|
||||
ThreePortsPipeElement
|
||||
}
|
||||
|
||||
type LinkDirection int
|
||||
|
||||
const (
|
||||
LinkDirectionB2A LinkDirection = -1
|
||||
LinkDirectionA2B LinkDirection = 1
|
||||
)
|
||||
|
||||
// link位置
|
||||
type LinkPosition struct {
|
||||
link Link
|
||||
pos int64
|
||||
}
|
||||
|
||||
func NewLinkPosition(link Link, pos int64) *LinkPosition {
|
||||
return &LinkPosition{
|
||||
link: link,
|
||||
pos: pos,
|
||||
}
|
||||
}
|
||||
|
||||
func (lp *LinkPosition) Link() Link {
|
||||
return lp.link
|
||||
}
|
||||
|
||||
func (lp *LinkPosition) Position() int64 {
|
||||
return lp.pos
|
||||
}
|
||||
|
||||
func (lp *LinkPosition) IsIn(linkRange LinkRange) bool {
|
||||
return lp.link.Uid() == linkRange.link.Uid() && lp.IsInRange(linkRange.start, linkRange.end)
|
||||
}
|
||||
|
||||
func (lp *LinkPosition) IsInRange(a int64, b int64) bool {
|
||||
start := int64(math.Min(float64(a), float64(b)))
|
||||
end := int64(math.Max(float64(a), float64(b)))
|
||||
return lp.pos >= start && lp.pos <= end
|
||||
}
|
||||
|
||||
// 移动位置
|
||||
func (lp *LinkPosition) Move(len uint32, direction LinkDirection) (overflow bool) {
|
||||
lp.pos += int64(len) * int64(direction)
|
||||
if lp.pos > lp.link.GetLength() || lp.pos < 0 {
|
||||
overflow = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// link范围
|
||||
type LinkRange struct {
|
||||
link Link
|
||||
start int64
|
||||
end int64
|
||||
}
|
||||
|
||||
// 构造link范围
|
||||
// link: 轨道 不能为nil
|
||||
// a: 起始位置,b: 结束位置,单位:毫米mm,a和b不能相等
|
||||
func NewLinkRange(link Link, a int64, b int64) *LinkRange {
|
||||
if link == nil {
|
||||
panic("构造LinkRange错误: link不能为空")
|
||||
}
|
||||
if a == b {
|
||||
panic("构造LinkRange错误: a和b不能相等")
|
||||
}
|
||||
start := int64(math.Min(float64(a), float64(b)))
|
||||
end := int64(math.Max(float64(a), float64(b)))
|
||||
return &LinkRange{
|
||||
link: link,
|
||||
start: start,
|
||||
end: end,
|
||||
}
|
||||
}
|
||||
|
||||
var _ Link = (*LinkImpl)(nil)
|
||||
var _ LinkNode = (*LinkNodeImpl)(nil)
|
||||
|
||||
type LinkImpl struct {
|
||||
*TwoPortsPipeElementImpl
|
||||
len int64
|
||||
}
|
||||
|
||||
type LinkNodeImpl struct {
|
||||
|
@ -56,6 +134,14 @@ func (l *LinkImpl) IsEquals(pla *PipeLink, plb *PipeLink) bool {
|
|||
return l.uid == buildLinkUid(pla, plb) || l.uid == buildLinkUid(plb, pla)
|
||||
}
|
||||
|
||||
func (l *LinkImpl) GetLength() int64 {
|
||||
return l.len
|
||||
}
|
||||
|
||||
func (l *LinkImpl) SetLength(len int64) {
|
||||
l.len = len
|
||||
}
|
||||
|
||||
func (n *LinkNodeImpl) Turnout() Turnout {
|
||||
return n.turnout
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
package model_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"joylink.club/rtss-core/model"
|
||||
)
|
||||
|
||||
func TestLinkPositionInRange(t *testing.T) {
|
||||
// 1. 配置连接关系
|
||||
turnout := model.NewTurnout("1")
|
||||
ln1 := model.NewLinkNode(turnout)
|
||||
pla := model.NewPipeLink(ln1, model.PipePortA)
|
||||
link1 := model.NewLink(pla, nil)
|
||||
link1.SetLength(1000)
|
||||
linkPos1 := model.NewLinkPosition(link1, 100)
|
||||
// 2. 验证连接关系
|
||||
inRangeTests := []struct {
|
||||
start int64
|
||||
end int64
|
||||
expect bool
|
||||
}{
|
||||
{start: 0, end: 50, expect: false},
|
||||
{start: 0, end: 100, expect: true},
|
||||
{start: 100, end: 101, expect: true},
|
||||
{start: 100, end: 100, expect: true},
|
||||
{start: 101, end: 100, expect: true},
|
||||
{start: 101, end: 200, expect: false},
|
||||
}
|
||||
for _, test := range inRangeTests {
|
||||
in := linkPos1.IsInRange(test.start, test.end)
|
||||
if in != test.expect {
|
||||
t.Errorf("expect: %v, but got: %v", test.expect, in)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinkPositionAdd(t *testing.T) {
|
||||
// 1. 配置连接关系
|
||||
turnout := model.NewTurnout("1")
|
||||
ln1 := model.NewLinkNode(turnout)
|
||||
pla := model.NewPipeLink(ln1, model.PipePortA)
|
||||
link1 := model.NewLink(pla, nil)
|
||||
link1.SetLength(1000)
|
||||
// 2. 验证连接关系
|
||||
addTests := []struct {
|
||||
len uint32
|
||||
direction model.LinkDirection
|
||||
expect bool
|
||||
expectValue int64
|
||||
}{
|
||||
{len: 0, direction: model.LinkDirectionA2B, expect: false, expectValue: 100},
|
||||
{len: 10, direction: model.LinkDirectionA2B, expect: false, expectValue: 110},
|
||||
{len: 10, direction: model.LinkDirectionB2A, expect: false, expectValue: 90},
|
||||
{len: 1000, direction: model.LinkDirectionA2B, expect: true, expectValue: 1100},
|
||||
{len: 1000, direction: model.LinkDirectionB2A, expect: true, expectValue: -900},
|
||||
}
|
||||
for _, test := range addTests {
|
||||
linkPos1 := model.NewLinkPosition(link1, 100)
|
||||
overflow := linkPos1.Move(test.len, test.direction)
|
||||
if overflow != test.expect {
|
||||
t.Errorf("expect: %v, but got: %v", test.expect, overflow)
|
||||
}
|
||||
if linkPos1.Position() != test.expectValue {
|
||||
t.Errorf("expect: %v, but got: %v", test.expectValue, linkPos1.Position())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -80,7 +80,7 @@ type ThreePortsPipeElement interface {
|
|||
|
||||
func checkPipePortLink(pe PipeElement, port PipePort, pipeLink *PipeLink) error {
|
||||
if pipeLink == nil {
|
||||
slog.Debug("检查通道端口连接关系", "通道端口", DebugPipeLink(pe, port), "连接关系", "未连接")
|
||||
slog.Debug("检查通道端口连接关系", "通道端口", DebugPipeLink(pe, port), "连接关系", "无连接")
|
||||
return nil
|
||||
}
|
||||
slog.Debug("检查通道端口连接关系", "通道端口", DebugPipeLink(pe, port), "连接关系", pipeLink.Debug())
|
||||
|
|
|
@ -1,28 +1,45 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// 区段
|
||||
type Section interface {
|
||||
RtssModel
|
||||
}
|
||||
|
||||
// 物理区段(非道岔区段)
|
||||
// 用于和道岔连接构成轨道网络
|
||||
// 然后使用轨道网络构建link和linkNode形成新的以道岔岔心为节点的网络,用于路径计算等
|
||||
type PhysicalSection interface {
|
||||
TwoPortsPipeElement
|
||||
// 获取A端和B端的公里标
|
||||
GetPaKm() *KilometerMark
|
||||
GetPbKm() *KilometerMark
|
||||
// 计算各个端口到岔心的距离
|
||||
CalculateDistance(calculateKmDistance func(km1, km2 *KilometerMark) (int64, error)) error
|
||||
GetLength() int64
|
||||
}
|
||||
|
||||
// 道岔区段
|
||||
// 道岔区段(道岔物理区段)
|
||||
// 道岔处的由至少3个及以上的计轴或绝缘节所确定的区段
|
||||
type TurnoutSection interface {
|
||||
Section
|
||||
// 获取关联的道岔列表
|
||||
GetTurnouts() []Turnout
|
||||
// 添加关联道岔
|
||||
AddTurnout(turnout Turnout)
|
||||
}
|
||||
|
||||
// 逻辑区段
|
||||
// 是在物理区段基础上进一步细分的区段,为了相对更精细的列车追踪和进路触发等控制
|
||||
// 最终映射到link上,做相应的逻辑处理
|
||||
type LogicalSection interface {
|
||||
Section
|
||||
// 所属物理区段(可能是一般物理区段也可能是道岔区段)
|
||||
BelongSection() Section
|
||||
// 获取A端和B端的公里标
|
||||
GetPaKm() *KilometerMark
|
||||
GetPbKm() *KilometerMark
|
||||
|
@ -32,6 +49,7 @@ type PhysicalSectionImpl struct {
|
|||
*TwoPortsPipeElementImpl
|
||||
PaKm *KilometerMark
|
||||
PbKm *KilometerMark
|
||||
len int64
|
||||
}
|
||||
|
||||
var _ PhysicalSection = (*PhysicalSectionImpl)(nil)
|
||||
|
@ -44,6 +62,20 @@ func NewPhysicalSection(uid string) *PhysicalSectionImpl {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *PhysicalSectionImpl) GetLength() int64 {
|
||||
return s.len
|
||||
}
|
||||
|
||||
func (s *PhysicalSectionImpl) CalculateDistance(calculateKmDistance func(km1, km2 *KilometerMark) (int64, error)) error {
|
||||
if s.PaKm == nil || s.PbKm == nil {
|
||||
return fmt.Errorf("区段公里标不能为空")
|
||||
}
|
||||
var err error
|
||||
s.len, err = calculateKmDistance(s.PaKm, s.PbKm)
|
||||
slog.Debug("计算物理区段公里标距离", "uid", s.Uid(), "length", s.len)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PhysicalSectionImpl) GetPaKm() *KilometerMark {
|
||||
return s.PaKm
|
||||
}
|
||||
|
@ -77,6 +109,11 @@ func NewTurnoutSection(uid string) *TurnoutSectionImpl {
|
|||
}
|
||||
|
||||
func (t *TurnoutSectionImpl) AddTurnout(turnout Turnout) {
|
||||
for _, exist := range t.Turnouts {
|
||||
if exist.Uid() == turnout.Uid() {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Turnouts = append(t.Turnouts, turnout)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -15,6 +17,14 @@ type Turnout interface {
|
|||
GetPbKm() *KilometerMark
|
||||
// 获取道岔C端公里标
|
||||
GetPcKm() *KilometerMark
|
||||
// 计算各个端口到岔心的距离
|
||||
CalculateDistances(calculateKmDistance func(km1, km2 *KilometerMark) (int64, error)) error
|
||||
// 获取到指定端口的长度
|
||||
GetLength(port PipePort) int64
|
||||
// 获取A端到B端的距离
|
||||
GetPaPbLength() int64
|
||||
// 获取A端到C端的距离
|
||||
GetPaPcLength() int64
|
||||
}
|
||||
|
||||
var _ Turnout = (*TurnoutImpl)(nil)
|
||||
|
@ -25,6 +35,9 @@ type TurnoutImpl struct {
|
|||
PaKm *KilometerMark
|
||||
PbKm *KilometerMark
|
||||
PcKm *KilometerMark
|
||||
paLen int64
|
||||
pbLen int64
|
||||
pcLen int64
|
||||
}
|
||||
|
||||
func NewTurnout(uid string) *TurnoutImpl {
|
||||
|
@ -38,6 +51,47 @@ func NewTurnout(uid string) *TurnoutImpl {
|
|||
}
|
||||
}
|
||||
|
||||
func (t *TurnoutImpl) GetLength(port PipePort) int64 {
|
||||
switch port {
|
||||
case PipePortA:
|
||||
return t.paLen
|
||||
case PipePortB:
|
||||
return t.pbLen
|
||||
case PipePortC:
|
||||
return t.pcLen
|
||||
}
|
||||
panic(fmt.Sprintf("获取道岔指定端口长度异常:错误的端口参数'%s'", port))
|
||||
}
|
||||
|
||||
func (t *TurnoutImpl) GetPaPbLength() int64 {
|
||||
return t.pbLen + t.paLen
|
||||
}
|
||||
|
||||
func (t *TurnoutImpl) GetPaPcLength() int64 {
|
||||
return t.pcLen + t.paLen
|
||||
}
|
||||
|
||||
func (t *TurnoutImpl) CalculateDistances(calculateKmDistance func(km1, km2 *KilometerMark) (int64, error)) error {
|
||||
if t.Km == nil || t.PaKm == nil || t.PbKm == nil || t.PcKm == nil {
|
||||
return fmt.Errorf("道岔公里标不能为空")
|
||||
}
|
||||
var err error
|
||||
t.paLen, err = calculateKmDistance(t.Km, t.PaKm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.pbLen, err = calculateKmDistance(t.Km, t.PbKm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.pcLen, err = calculateKmDistance(t.Km, t.PcKm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
slog.Debug("计算道岔公里标距离", "uid", t.Uid(), "A端距离", t.paLen, "B端距离", t.pbLen, "C端距离", t.pcLen)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TurnoutImpl) GetKm() *KilometerMark {
|
||||
return t.Km
|
||||
}
|
||||
|
|
81
repo/repo.go
81
repo/repo.go
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"joylink.club/rtss-core/model"
|
||||
|
@ -23,6 +24,8 @@ type Repo interface {
|
|||
CheckLinkAndLinkNodePipeLink() error
|
||||
// 转换公里标
|
||||
ConvertKilometerMark(km *model.KilometerMark, targetCoordinate string) (int64, error)
|
||||
// 计算公里标间距离
|
||||
CalculateKmDistance(km1 *model.KilometerMark, km2 *model.KilometerMark) (int64, error)
|
||||
}
|
||||
|
||||
var _ Repo = (*RepoImpl)(nil)
|
||||
|
@ -36,7 +39,7 @@ type RepoImpl struct {
|
|||
TurnoutMap map[string]model.Turnout
|
||||
LinkNodeMap map[string]model.LinkNode
|
||||
LinkMap map[string]model.Link
|
||||
KilometerMarkConverters []model.KilometerMarkConverter
|
||||
KilometerMarkConverters []*model.KilometerMarkConverter
|
||||
}
|
||||
|
||||
func NewRepo(id string) *RepoImpl {
|
||||
|
@ -48,7 +51,7 @@ func NewRepo(id string) *RepoImpl {
|
|||
TurnoutMap: make(map[string]model.Turnout),
|
||||
LinkNodeMap: make(map[string]model.LinkNode),
|
||||
LinkMap: make(map[string]model.Link),
|
||||
KilometerMarkConverters: make([]model.KilometerMarkConverter, 0),
|
||||
KilometerMarkConverters: make([]*model.KilometerMarkConverter, 0),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,6 +71,18 @@ func (r *RepoImpl) ConvertKilometerMark(km *model.KilometerMark, targetCoordinat
|
|||
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())
|
||||
|
@ -77,6 +92,9 @@ func (r *RepoImpl) CheckSectionAndTurnoutPortKms() error {
|
|||
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())
|
||||
|
@ -92,6 +110,9 @@ func (r *RepoImpl) CheckSectionAndTurnoutPortKms() error {
|
|||
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...)
|
||||
|
@ -107,6 +128,16 @@ func (r *RepoImpl) CheckSectionAndTurnoutPipeLink() error {
|
|||
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 {
|
||||
|
@ -185,29 +216,51 @@ func walkOverTurnouts(turnout model.Turnout, repo1 *RepoImpl) {
|
|||
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 {
|
||||
link, exist := NewLinkNodeAndLinkAndBuildLinkship(turnout, port, nil, model.PipePortA, repo1)
|
||||
if !exist {
|
||||
repo1.LinkMap[link.Uid()] = link
|
||||
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)
|
||||
}
|
||||
slog.Debug("构建Link已存在", "LinkUid", link.Uid())
|
||||
return nil
|
||||
}
|
||||
if ple.Pipe.IsThreePorts() {
|
||||
nextTurnout := ple.Pipe.(model.Turnout)
|
||||
nextPort := 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
|
||||
repo1.LinkMap[link.Uid()] = link // 添加到仓库
|
||||
// 构建link的长度和道岔以及区段的映射
|
||||
buildLinkLengthAndElementMappings(link.(*model.LinkImpl), pathSections, repo1)
|
||||
return nextTurnout
|
||||
} else {
|
||||
ple = ple.Pipe.(model.PhysicalSection).OppositePipeLink(ple.Port)
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package repo_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"joylink.club/rtss-core/model"
|
||||
"joylink.club/rtss-core/repo"
|
||||
)
|
||||
|
||||
func TestRepositoryConvertKilometerMark(t *testing.T) {
|
||||
// 1. 配置公里标转换关系
|
||||
km1 := model.NewKilometerMark("YDK", 0)
|
||||
km2 := model.NewKilometerMark("ZDK", 200)
|
||||
kmc1 := model.NewKilometerMarkConverter(km1, km2, true)
|
||||
t.Log(kmc1.Debug())
|
||||
repo1 := repo.NewRepo("Test")
|
||||
repo1.KilometerMarkConverters = append(repo1.KilometerMarkConverters, kmc1)
|
||||
|
||||
// 2. 验证公里标转换关系
|
||||
tests := []struct {
|
||||
km *model.KilometerMark
|
||||
targetCoordinate string
|
||||
expect int64
|
||||
expectError bool
|
||||
}{
|
||||
{km: model.NewKilometerMark("YDK", 0), targetCoordinate: km2.Coordinate(), expect: 200, expectError: false},
|
||||
{km: model.NewKilometerMark("ZDK", 0), targetCoordinate: km1.Coordinate(), expect: -200, expectError: false},
|
||||
{km: model.NewKilometerMark("YDK", 100), targetCoordinate: "Other", expect: 0, expectError: true},
|
||||
}
|
||||
t.Logf("tests: %v", tests)
|
||||
for _, test := range tests {
|
||||
result, err := repo1.ConvertKilometerMark(test.km, test.targetCoordinate)
|
||||
if test.expectError {
|
||||
if err == nil {
|
||||
t.Errorf("expect error, but got nil")
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("expect %d, but got error: %v", test.expect, err)
|
||||
}
|
||||
if result != test.expect {
|
||||
t.Errorf("expect: %d, but got: %d", test.expect, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue