调整公里标概念,添加注释说明
完善道岔区段与道岔关系构建检查 添加Link位置和link范围工具类 完善example道岔区段关系构建
This commit is contained in:
parent
97445763fd
commit
1a610c01ba
|
@ -17,49 +17,10 @@ func main() {
|
||||||
rtssGraphicStorage := data_proto.GetXian6STYG()
|
rtssGraphicStorage := data_proto.GetXian6STYG()
|
||||||
dataMapping := repository.NewDataMapping("1", rtssGraphicStorage)
|
dataMapping := repository.NewDataMapping("1", rtssGraphicStorage)
|
||||||
repo1.DataMapping["1"] = dataMapping
|
repo1.DataMapping["1"] = dataMapping
|
||||||
// log.Println(rtssGraphicStorage.Stations)
|
basicDataMappingAndModelBuild(dataMapping, repo1)
|
||||||
// log.Println(rtssGraphicStorage.Section)
|
|
||||||
// log.Println(rtssGraphicStorage.Turnouts)
|
// 构建公里标转换器
|
||||||
for _, station := range rtssGraphicStorage.Stations {
|
buildKilometerMarkConverters(dataMapping, repo1)
|
||||||
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}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建区段关系
|
// 构建区段关系
|
||||||
buildSectionRelationships(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) {
|
func buildKilometerMark(dataMapping *repository.DataMapping, repo1 *repository.Repository) {
|
||||||
for _, turnout := range dataMapping.TurnoutDataMap {
|
for _, turnout := range dataMapping.TurnoutDataMap {
|
||||||
if len(turnout.KilometerSystem) == 0 {
|
if len(turnout.KilometerSystem) == 0 {
|
||||||
repo1.BuildErrorInfos = append(repo1.BuildErrorInfos, fmt.Errorf("构建区段、道岔公里标数据错误:道岔[id=%d]未关联任何公里标", turnout.Common.Id))
|
repo1.BuildErrorInfos = append(repo1.BuildErrorInfos, fmt.Errorf("构建区段、道岔公里标数据错误:道岔[id=%d]未关联任何公里标", turnout.Common.Id))
|
||||||
} else {
|
} else {
|
||||||
tks := turnout.KilometerSystem[0]
|
km := convertKs2Km(turnout.KilometerSystem[0])
|
||||||
km := model.NewKilometerMark(tks.CoordinateSystem, convertKmDirection(tks.Direction), tks.Kilometer)
|
|
||||||
turnoutModel := repo1.TurnoutMap[dataMapping.IdMappingMap[turnout.Common.Id].Uid]
|
turnoutModel := repo1.TurnoutMap[dataMapping.IdMappingMap[turnout.Common.Id].Uid]
|
||||||
turnoutModel.(*modelimpl.Turnout).Km = km
|
turnoutModel.(*modelimpl.Turnout).Km = km
|
||||||
}
|
}
|
||||||
|
@ -120,7 +147,7 @@ func buildKilometerMark(dataMapping *repository.DataMapping, repo1 *repository.R
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(fmt.Errorf("构建区段、道岔公里标数据错误:检测点[id=%d]关联的区段模型[uid=%s]不存在", checkpoint.Common.Id, idmapping.Uid))
|
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 {
|
if linkship.DevicePort == data_proto.RelatedRef_A {
|
||||||
sectionModel.(*modelimpl.PhysicalSection).PaKm = km
|
sectionModel.(*modelimpl.PhysicalSection).PaKm = km
|
||||||
} else if linkship.DevicePort == data_proto.RelatedRef_B {
|
} else if linkship.DevicePort == data_proto.RelatedRef_B {
|
||||||
|
@ -133,7 +160,7 @@ func buildKilometerMark(dataMapping *repository.DataMapping, repo1 *repository.R
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(fmt.Errorf("构建区段、道岔公里标数据错误:检测点[id=%d]关联的道岔模型[uid=%s]不存在", checkpoint.Common.Id, idmapping.Uid))
|
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 {
|
if linkship.DevicePort == data_proto.RelatedRef_A {
|
||||||
turnoutModel.(*modelimpl.Turnout).PaKm = km
|
turnoutModel.(*modelimpl.Turnout).PaKm = km
|
||||||
} else if linkship.DevicePort == data_proto.RelatedRef_B {
|
} 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) {
|
func buildTurnoutRelationships(dataMapping *repository.DataMapping, repo1 *repository.Repository) {
|
||||||
for _, turnout := range dataMapping.TurnoutDataMap {
|
for _, turnout := range dataMapping.TurnoutDataMap {
|
||||||
idmapping := dataMapping.IdMappingMap[turnout.Common.Id]
|
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) {
|
func buildSectionRelationships(dataMapping *repository.DataMapping, repo1 *repository.Repository) {
|
||||||
for _, section := range dataMapping.SectionDataMap {
|
for _, section := range dataMapping.SectionDataMap {
|
||||||
idmapping := dataMapping.IdMappingMap[section.Common.Id]
|
idmapping := dataMapping.GetIdMapping(section.Common.Id)
|
||||||
if idmapping == nil {
|
if section.SectionType == data_proto.Section_Physical {
|
||||||
panic(fmt.Errorf("构建区段关系错误:idmapping异常为空"))
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sectionModel := repo1.PhysicalSectionMap[idmapping.Uid]
|
|
||||||
buildSectionPortLinkRelation(section.PaRef, sectionModel, model.PipePortA, repo1, dataMapping)
|
|
||||||
buildSectionPortLinkRelation(section.PbRef, sectionModel, model.PipePortB, repo1, dataMapping)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,11 +23,20 @@ func NewIdMapping(graphicId uint32, uid string) *IdMapping {
|
||||||
type DataMapping struct {
|
type DataMapping struct {
|
||||||
id string
|
id string
|
||||||
*data_proto.RtssGraphicStorage
|
*data_proto.RtssGraphicStorage
|
||||||
StationDataMap map[uint32]*data_proto.Station
|
StationDataMap map[uint32]*data_proto.Station
|
||||||
SectionDataMap map[uint32]*data_proto.Section
|
SectionDataMap map[uint32]*data_proto.Section
|
||||||
TurnoutDataMap map[uint32]*data_proto.Turnout
|
TurnoutDataMap map[uint32]*data_proto.Turnout
|
||||||
IdMappingMap map[uint32]*IdMapping
|
SectionCheckPointMap map[uint32]*data_proto.AxleCounting
|
||||||
UidMappingMap map[string]*IdMapping
|
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) {
|
func (d *DataMapping) AddIdMapping(idMapping *IdMapping) {
|
||||||
|
@ -42,13 +51,14 @@ func (d *DataMapping) GetLineInfo() string {
|
||||||
|
|
||||||
func NewDataMapping(id string, storage *data_proto.RtssGraphicStorage) *DataMapping {
|
func NewDataMapping(id string, storage *data_proto.RtssGraphicStorage) *DataMapping {
|
||||||
return &DataMapping{
|
return &DataMapping{
|
||||||
id: id,
|
id: id,
|
||||||
RtssGraphicStorage: storage,
|
RtssGraphicStorage: storage,
|
||||||
StationDataMap: make(map[uint32]*data_proto.Station),
|
StationDataMap: make(map[uint32]*data_proto.Station),
|
||||||
SectionDataMap: make(map[uint32]*data_proto.Section),
|
SectionDataMap: make(map[uint32]*data_proto.Section),
|
||||||
TurnoutDataMap: make(map[uint32]*data_proto.Turnout),
|
TurnoutDataMap: make(map[uint32]*data_proto.Turnout),
|
||||||
IdMappingMap: make(map[uint32]*IdMapping),
|
SectionCheckPointMap: make(map[uint32]*data_proto.AxleCounting),
|
||||||
UidMappingMap: make(map[string]*IdMapping),
|
IdMappingMap: make(map[uint32]*IdMapping),
|
||||||
|
UidMappingMap: make(map[string]*IdMapping),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,29 +5,22 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 运营方向(上行/下行)
|
// 公里标
|
||||||
type OperationDirection int8
|
// 类似地铁线路一般正线都是双线,公里标一般以类似YDK/ZDK开头,
|
||||||
|
// 虽然从信号布置图上看,YDK/ZDK相近位置的值似乎是一样的,以为他们是同一个坐标系,
|
||||||
const (
|
// 但这种情况的原因其实是因为两条线的起始点很接近,他们依然是两个独立的坐标系。
|
||||||
// 上行
|
// 左线和右线中间有渡线可以相互切换,但公里标如何转换暂时不清楚,
|
||||||
OperationDirectionUp OperationDirection = 1
|
// 暂时考虑先通过添加转换配置来实现(比如转换可以配置0或根据道岔处勾股定理大概估算,虽然不同道岔处可能不一样,但暂时不考虑复杂情况)。
|
||||||
// 下行
|
|
||||||
OperationDirectionDown OperationDirection = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
type KilometerMark struct {
|
type KilometerMark struct {
|
||||||
// 公里标坐标系
|
// 公里标坐标系
|
||||||
coordinate string
|
coordinate string
|
||||||
// 公里标上下行方向
|
|
||||||
direction OperationDirection
|
|
||||||
// 公里标值
|
// 公里标值
|
||||||
value int64
|
value int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewKilometerMark(coordinate string, direction OperationDirection, value int64) *KilometerMark {
|
func NewKilometerMark(coordinate string, value int64) *KilometerMark {
|
||||||
return &KilometerMark{
|
return &KilometerMark{
|
||||||
coordinate: coordinate,
|
coordinate: coordinate,
|
||||||
direction: direction,
|
|
||||||
value: value,
|
value: value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,12 +40,23 @@ func (km *KilometerMark) IsCoordinateEqual(coordinate string) bool {
|
||||||
|
|
||||||
// 公里标转换配置
|
// 公里标转换配置
|
||||||
type KilometerMarkConverter struct {
|
type KilometerMarkConverter struct {
|
||||||
km1 KilometerMark
|
km1 *KilometerMark
|
||||||
km2 KilometerMark
|
km2 *KilometerMark
|
||||||
// 趋势是否相同
|
// 趋势是否相同
|
||||||
trendSame bool
|
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 {
|
func (kmc *KilometerMarkConverter) Debug() string {
|
||||||
return fmt.Sprintf("{%s<->%s(%s)}", kmc.km1.coordinate, kmc.km2.coordinate, strconv.FormatBool(kmc.trendSame))
|
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) {
|
func TestConvertKilometerMark(t *testing.T) {
|
||||||
// 1. 配置公里标转换关系
|
// 1. 配置公里标转换关系
|
||||||
km1 := NewKilometerMark("DBCSK", OperationDirectionUp, 0)
|
km1 := NewKilometerMark("YDK", 0)
|
||||||
km2 := NewKilometerMark("DCSK", OperationDirectionUp, 200)
|
km2 := NewKilometerMark("ZDK", 200)
|
||||||
kmc1 := &KilometerMarkConverter{
|
kmc1 := NewKilometerMarkConverter(km1, km2, true)
|
||||||
km1: *km1,
|
|
||||||
km2: *km2,
|
|
||||||
trendSame: true,
|
|
||||||
}
|
|
||||||
t.Log(kmc1.Debug())
|
t.Log(kmc1.Debug())
|
||||||
kmc2 := &KilometerMarkConverter{
|
kmc2 := *NewKilometerMarkConverter(km1, km2, false)
|
||||||
km1: *km1,
|
|
||||||
km2: *km2,
|
|
||||||
trendSame: false,
|
|
||||||
}
|
|
||||||
t.Log(kmc2.Debug())
|
t.Log(kmc2.Debug())
|
||||||
|
|
||||||
// 2. 验证公里标转换关系
|
// 2. 验证公里标转换关系
|
||||||
|
@ -28,12 +20,12 @@ func TestConvertKilometerMark(t *testing.T) {
|
||||||
expect1 int64
|
expect1 int64
|
||||||
expect2 int64
|
expect2 int64
|
||||||
}{
|
}{
|
||||||
{km: NewKilometerMark("DBCSK", OperationDirectionUp, 0), coordinate: km2.Coordinate(), expect1: 200, expect2: 200},
|
{km: NewKilometerMark("YDK", 0), coordinate: km2.Coordinate(), expect1: 200, expect2: 200},
|
||||||
{km: NewKilometerMark("DBCSK", OperationDirectionUp, 100), coordinate: km2.Coordinate(), expect1: 300, expect2: 100},
|
{km: NewKilometerMark("YDK", 100), coordinate: km2.Coordinate(), expect1: 300, expect2: 100},
|
||||||
{km: NewKilometerMark("DBCSK", OperationDirectionUp, -100), coordinate: km2.Coordinate(), expect1: 100, expect2: 300},
|
{km: NewKilometerMark("YDK", -100), coordinate: km2.Coordinate(), expect1: 100, expect2: 300},
|
||||||
{km: NewKilometerMark("DCSK", OperationDirectionUp, 200), coordinate: km1.Coordinate(), expect1: 0, expect2: 0},
|
{km: NewKilometerMark("ZDK", 200), coordinate: km1.Coordinate(), expect1: 0, expect2: 0},
|
||||||
{km: NewKilometerMark("DCSK", OperationDirectionUp, 300), coordinate: km1.Coordinate(), expect1: 100, expect2: -100},
|
{km: NewKilometerMark("ZDK", 300), coordinate: km1.Coordinate(), expect1: 100, expect2: -100},
|
||||||
{km: NewKilometerMark("DCSK", OperationDirectionUp, 100), coordinate: km1.Coordinate(), expect1: -100, expect2: 100},
|
{km: NewKilometerMark("ZDK", 100), coordinate: km1.Coordinate(), expect1: -100, expect2: 100},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
result1 := kmc1.Convert(test.km, test.coordinate)
|
result1 := kmc1.Convert(test.km, test.coordinate)
|
||||||
|
|
|
@ -3,27 +3,105 @@ package model
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RR = io.Writer
|
type RR = io.Writer
|
||||||
|
|
||||||
// 轨道
|
// 轨道
|
||||||
|
// 默认坐标系为以A端为起点,B端为终点的单轴坐标系
|
||||||
type Link interface {
|
type Link interface {
|
||||||
TwoPortsPipeElement
|
TwoPortsPipeElement
|
||||||
IsEquals(pla *PipeLink, plb *PipeLink) bool
|
IsEquals(pla *PipeLink, plb *PipeLink) bool
|
||||||
|
// 获取轨道长度,单位:毫米mm
|
||||||
|
GetLength() int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// 轨道连接点
|
// 轨道连接点(使用的是道岔岔心),然后有三个方向A/B/C,还是使用PipePort枚举,但表示的含义是方向,而不是道岔的三个端口
|
||||||
type LinkNode interface {
|
type LinkNode interface {
|
||||||
Turnout() Turnout
|
Turnout() Turnout
|
||||||
ThreePortsPipeElement
|
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 _ Link = (*LinkImpl)(nil)
|
||||||
var _ LinkNode = (*LinkNodeImpl)(nil)
|
var _ LinkNode = (*LinkNodeImpl)(nil)
|
||||||
|
|
||||||
type LinkImpl struct {
|
type LinkImpl struct {
|
||||||
*TwoPortsPipeElementImpl
|
*TwoPortsPipeElementImpl
|
||||||
|
len int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type LinkNodeImpl struct {
|
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)
|
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 {
|
func (n *LinkNodeImpl) Turnout() Turnout {
|
||||||
return n.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 {
|
func checkPipePortLink(pe PipeElement, port PipePort, pipeLink *PipeLink) error {
|
||||||
if pipeLink == nil {
|
if pipeLink == nil {
|
||||||
slog.Debug("检查通道端口连接关系", "通道端口", DebugPipeLink(pe, port), "连接关系", "未连接")
|
slog.Debug("检查通道端口连接关系", "通道端口", DebugPipeLink(pe, port), "连接关系", "无连接")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
slog.Debug("检查通道端口连接关系", "通道端口", DebugPipeLink(pe, port), "连接关系", pipeLink.Debug())
|
slog.Debug("检查通道端口连接关系", "通道端口", DebugPipeLink(pe, port), "连接关系", pipeLink.Debug())
|
||||||
|
|
|
@ -1,28 +1,45 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
)
|
||||||
|
|
||||||
// 区段
|
// 区段
|
||||||
type Section interface {
|
type Section interface {
|
||||||
RtssModel
|
RtssModel
|
||||||
}
|
}
|
||||||
|
|
||||||
// 物理区段(非道岔区段)
|
// 物理区段(非道岔区段)
|
||||||
|
// 用于和道岔连接构成轨道网络
|
||||||
|
// 然后使用轨道网络构建link和linkNode形成新的以道岔岔心为节点的网络,用于路径计算等
|
||||||
type PhysicalSection interface {
|
type PhysicalSection interface {
|
||||||
TwoPortsPipeElement
|
TwoPortsPipeElement
|
||||||
// 获取A端和B端的公里标
|
// 获取A端和B端的公里标
|
||||||
GetPaKm() *KilometerMark
|
GetPaKm() *KilometerMark
|
||||||
GetPbKm() *KilometerMark
|
GetPbKm() *KilometerMark
|
||||||
|
// 计算各个端口到岔心的距离
|
||||||
|
CalculateDistance(calculateKmDistance func(km1, km2 *KilometerMark) (int64, error)) error
|
||||||
|
GetLength() int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// 道岔区段
|
// 道岔区段(道岔物理区段)
|
||||||
|
// 道岔处的由至少3个及以上的计轴或绝缘节所确定的区段
|
||||||
type TurnoutSection interface {
|
type TurnoutSection interface {
|
||||||
Section
|
Section
|
||||||
// 获取关联的道岔列表
|
// 获取关联的道岔列表
|
||||||
GetTurnouts() []Turnout
|
GetTurnouts() []Turnout
|
||||||
|
// 添加关联道岔
|
||||||
|
AddTurnout(turnout Turnout)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 逻辑区段
|
// 逻辑区段
|
||||||
|
// 是在物理区段基础上进一步细分的区段,为了相对更精细的列车追踪和进路触发等控制
|
||||||
|
// 最终映射到link上,做相应的逻辑处理
|
||||||
type LogicalSection interface {
|
type LogicalSection interface {
|
||||||
Section
|
Section
|
||||||
|
// 所属物理区段(可能是一般物理区段也可能是道岔区段)
|
||||||
|
BelongSection() Section
|
||||||
// 获取A端和B端的公里标
|
// 获取A端和B端的公里标
|
||||||
GetPaKm() *KilometerMark
|
GetPaKm() *KilometerMark
|
||||||
GetPbKm() *KilometerMark
|
GetPbKm() *KilometerMark
|
||||||
|
@ -32,6 +49,7 @@ type PhysicalSectionImpl struct {
|
||||||
*TwoPortsPipeElementImpl
|
*TwoPortsPipeElementImpl
|
||||||
PaKm *KilometerMark
|
PaKm *KilometerMark
|
||||||
PbKm *KilometerMark
|
PbKm *KilometerMark
|
||||||
|
len int64
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ PhysicalSection = (*PhysicalSectionImpl)(nil)
|
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 {
|
func (s *PhysicalSectionImpl) GetPaKm() *KilometerMark {
|
||||||
return s.PaKm
|
return s.PaKm
|
||||||
}
|
}
|
||||||
|
@ -77,6 +109,11 @@ func NewTurnoutSection(uid string) *TurnoutSectionImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TurnoutSectionImpl) AddTurnout(turnout Turnout) {
|
func (t *TurnoutSectionImpl) AddTurnout(turnout Turnout) {
|
||||||
|
for _, exist := range t.Turnouts {
|
||||||
|
if exist.Uid() == turnout.Uid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
t.Turnouts = append(t.Turnouts, turnout)
|
t.Turnouts = append(t.Turnouts, turnout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,16 +17,27 @@ type Turnout interface {
|
||||||
GetPbKm() *KilometerMark
|
GetPbKm() *KilometerMark
|
||||||
// 获取道岔C端公里标
|
// 获取道岔C端公里标
|
||||||
GetPcKm() *KilometerMark
|
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)
|
var _ Turnout = (*TurnoutImpl)(nil)
|
||||||
|
|
||||||
type TurnoutImpl struct {
|
type TurnoutImpl struct {
|
||||||
*ThreePortsPipeElementImpl
|
*ThreePortsPipeElementImpl
|
||||||
Km *KilometerMark
|
Km *KilometerMark
|
||||||
PaKm *KilometerMark
|
PaKm *KilometerMark
|
||||||
PbKm *KilometerMark
|
PbKm *KilometerMark
|
||||||
PcKm *KilometerMark
|
PcKm *KilometerMark
|
||||||
|
paLen int64
|
||||||
|
pbLen int64
|
||||||
|
pcLen int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTurnout(uid string) *TurnoutImpl {
|
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 {
|
func (t *TurnoutImpl) GetKm() *KilometerMark {
|
||||||
return t.Km
|
return t.Km
|
||||||
}
|
}
|
||||||
|
|
95
repo/repo.go
95
repo/repo.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"math"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"joylink.club/rtss-core/model"
|
"joylink.club/rtss-core/model"
|
||||||
|
@ -23,6 +24,8 @@ type Repo interface {
|
||||||
CheckLinkAndLinkNodePipeLink() error
|
CheckLinkAndLinkNodePipeLink() error
|
||||||
// 转换公里标
|
// 转换公里标
|
||||||
ConvertKilometerMark(km *model.KilometerMark, targetCoordinate string) (int64, error)
|
ConvertKilometerMark(km *model.KilometerMark, targetCoordinate string) (int64, error)
|
||||||
|
// 计算公里标间距离
|
||||||
|
CalculateKmDistance(km1 *model.KilometerMark, km2 *model.KilometerMark) (int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Repo = (*RepoImpl)(nil)
|
var _ Repo = (*RepoImpl)(nil)
|
||||||
|
@ -36,7 +39,7 @@ type RepoImpl struct {
|
||||||
TurnoutMap map[string]model.Turnout
|
TurnoutMap map[string]model.Turnout
|
||||||
LinkNodeMap map[string]model.LinkNode
|
LinkNodeMap map[string]model.LinkNode
|
||||||
LinkMap map[string]model.Link
|
LinkMap map[string]model.Link
|
||||||
KilometerMarkConverters []model.KilometerMarkConverter
|
KilometerMarkConverters []*model.KilometerMarkConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRepo(id string) *RepoImpl {
|
func NewRepo(id string) *RepoImpl {
|
||||||
|
@ -48,7 +51,7 @@ func NewRepo(id string) *RepoImpl {
|
||||||
TurnoutMap: make(map[string]model.Turnout),
|
TurnoutMap: make(map[string]model.Turnout),
|
||||||
LinkNodeMap: make(map[string]model.LinkNode),
|
LinkNodeMap: make(map[string]model.LinkNode),
|
||||||
LinkMap: make(map[string]model.Link),
|
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, ","))
|
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 {
|
func (r *RepoImpl) CheckSectionAndTurnoutPortKms() error {
|
||||||
for _, section := range r.PhysicalSectionMap {
|
for _, section := range r.PhysicalSectionMap {
|
||||||
slog.Debug("检查区段公里标", "uid", section.Uid(), "A端公里标", section.GetPaKm(), "B端公里标", section.GetPbKm())
|
slog.Debug("检查区段公里标", "uid", section.Uid(), "A端公里标", section.GetPaKm(), "B端公里标", section.GetPbKm())
|
||||||
|
@ -77,6 +92,9 @@ func (r *RepoImpl) CheckSectionAndTurnoutPortKms() error {
|
||||||
if section.GetPbKm() == nil {
|
if section.GetPbKm() == nil {
|
||||||
r.BuildErrorInfos = append(r.BuildErrorInfos, fmt.Errorf("区段[uid=%s]无B端公里标", section.Uid()))
|
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 {
|
for _, turnout := range r.TurnoutMap {
|
||||||
slog.Debug("检查道岔公里标", "uid", turnout.Uid(), "公里标", turnout.GetKm(), "A端公里标", turnout.GetPaKm(), "B端公里标", turnout.GetPbKm(), "C端公里标", turnout.GetPcKm())
|
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 {
|
if turnout.GetPcKm() == nil {
|
||||||
r.BuildErrorInfos = append(r.BuildErrorInfos, fmt.Errorf("道岔[uid=%s]无C端公里标", turnout.Uid()))
|
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 {
|
if len(r.BuildErrorInfos) > 0 {
|
||||||
return errors.Join(r.BuildErrorInfos...)
|
return errors.Join(r.BuildErrorInfos...)
|
||||||
|
@ -107,6 +128,16 @@ func (r *RepoImpl) CheckSectionAndTurnoutPipeLink() error {
|
||||||
r.BuildErrorInfos = append(r.BuildErrorInfos, err)
|
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 {
|
for _, turnout := range r.TurnoutMap {
|
||||||
err := turnout.CheckPipeLink()
|
err := turnout.CheckPipeLink()
|
||||||
if err != nil {
|
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 {
|
func walkFromTurnoutPortToNextAndBuildLink(turnout model.Turnout, port model.PipePort, repo1 *RepoImpl) model.Turnout {
|
||||||
slog.Debug("walkFromTurnoutPortToNextAndBuildLink", "道岔id", turnout.Uid(), "端口", port)
|
slog.Debug("walkFromTurnoutPortToNextAndBuildLink", "道岔id", turnout.Uid(), "端口", port)
|
||||||
ple := turnout.GetLinkedElement(port)
|
ple := turnout.GetLinkedElement(port)
|
||||||
|
pathSections := make([]model.PhysicalSection, 0)
|
||||||
|
var nextTurnout model.Turnout = nil
|
||||||
|
var nextPort model.PipePort
|
||||||
for {
|
for {
|
||||||
if ple == nil {
|
if ple == nil { // 到尽头
|
||||||
link, exist := NewLinkNodeAndLinkAndBuildLinkship(turnout, port, nil, model.PipePortA, repo1)
|
nextPort = model.PipePortA
|
||||||
if !exist {
|
break
|
||||||
repo1.LinkMap[link.Uid()] = link
|
|
||||||
}
|
|
||||||
slog.Debug("构建Link已存在", "LinkUid", link.Uid())
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
if ple.Pipe.IsThreePorts() {
|
if ple.Pipe.IsThreePorts() { // 到下一个道岔
|
||||||
nextTurnout := ple.Pipe.(model.Turnout)
|
nextTurnout = ple.Pipe.(model.Turnout)
|
||||||
nextPort := ple.Port
|
nextPort = ple.Port
|
||||||
link, exist := NewLinkNodeAndLinkAndBuildLinkship(turnout, port, nextTurnout, nextPort, repo1)
|
break
|
||||||
if exist {
|
} else { // 到下一个区段
|
||||||
slog.Debug("构建Link已存在", "LinkUid", link.Uid())
|
section := ple.Pipe.(model.PhysicalSection)
|
||||||
return nil
|
pathSections = append(pathSections, section)
|
||||||
}
|
ple = section.OppositePipeLink(ple.Port)
|
||||||
repo1.LinkMap[link.Uid()] = link
|
|
||||||
return nextTurnout
|
|
||||||
} else {
|
|
||||||
ple = ple.Pipe.(model.PhysicalSection).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 {
|
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