diff --git a/example/build_repo_example/main.go b/example/build_repo_example/main.go index 03cf613..9f33fd5 100644 --- a/example/build_repo_example/main.go +++ b/example/build_repo_example/main.go @@ -11,8 +11,8 @@ import ( ) func main() { - // slog.SetLogLoggerLevel(slog.LevelDebug) - slog.SetLogLoggerLevel(slog.LevelInfo) + slog.SetLogLoggerLevel(slog.LevelDebug) + // slog.SetLogLoggerLevel(slog.LevelInfo) repo1 := repository.NewRepository("test1") rtssGraphicStorage := data_proto.GetXian6STYG() dataMapping := repository.NewDataMapping("1", rtssGraphicStorage) @@ -38,8 +38,13 @@ func main() { dataMapping.SectionDataMap[section.Common.Id] = section uid := getSectionUid(section, belongStation, dataMapping.GetLineInfo()) dataMapping.AddIdMapping(repository.NewIdMapping(section.Common.Id, uid)) - sectionModel := model.NewSection(uid) - repo1.SectionMap[uid] = &modelimpl.Section{SectionImpl: sectionModel} + 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 { @@ -61,7 +66,7 @@ func main() { // 构建道岔关系 buildTurnoutRelationships(dataMapping, repo1) // 检查区段、道岔通道连接关系 - err := repo1.CheckPipeLink() + err := repo1.CheckSectionAndTurnoutPipeLink() if err != nil { slog.Error("区段道岔连接关系检查错误", "errMsg", err) return @@ -70,10 +75,15 @@ func main() { } // 构建区段、道岔公里标 buildKilometerMark(dataMapping, repo1) + err = repo1.CheckSectionAndTurnoutPortKms() + if err != nil { + slog.Error("区段道岔公里标检查错误", "errMsg", err) + return + } // 构建link/linknode repo1.BuildLinks() // 检查link/linknode - err = repo1.CheckPipeLink() + err = repo1.CheckLinkAndLinkNodePipeLink() if err != nil { slog.Error("link/linknode连接关系检查错误", "errMsg", err) return @@ -84,23 +94,57 @@ func main() { // 构建区段、道岔公里标 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) + turnoutModel := repo1.TurnoutMap[dataMapping.IdMappingMap[turnout.Common.Id].Uid] + turnoutModel.(*modelimpl.Turnout).Km = km + } + } for _, checkpoint := range dataMapping.AxleCountings { if checkpoint.AxleCountingRef == nil || len(checkpoint.AxleCountingRef) == 0 { - repo1.BuildErrorInfos = append(repo1.BuildErrorInfos, fmt.Errorf("构建区段、道岔公里标错误:检测点[id=%d]未关联任何区段、道岔", checkpoint.Common.Id)) + repo1.BuildErrorInfos = append(repo1.BuildErrorInfos, fmt.Errorf("构建区段、道岔公里标数据错误:检测点[id=%d]未关联任何区段、道岔", checkpoint.Common.Id)) } for _, linkship := range checkpoint.AxleCountingRef { - slog.Info("区段检测点关联数据", "code", checkpoint.Code, "linkship", fmt.Sprintf("{type=%s, id=%d, port=%s}", linkship.DeviceType, linkship.Id, linkship.DevicePort)) + slog.Debug("区段检测点关联数据", "code", checkpoint.Code, "linkship", fmt.Sprintf("{type=%s, id=%d, port=%s}", linkship.DeviceType, linkship.Id, linkship.DevicePort)) idmapping := dataMapping.IdMappingMap[linkship.Id] if idmapping == nil { - repo1.BuildErrorInfos = append(repo1.BuildErrorInfos, fmt.Errorf("构建区段、道岔公里标错误:检测点[id=%d]关联的{type=%s,id=%d}不存在", checkpoint.Common.Id, linkship.DeviceType, linkship.Id)) + repo1.BuildErrorInfos = append(repo1.BuildErrorInfos, fmt.Errorf("构建区段、道岔公里标数据错误:检测点[id=%d]关联的{type=%s,id=%d}不存在", checkpoint.Common.Id, linkship.DeviceType, linkship.Id)) continue } if linkship.DeviceType == data_proto.RelatedRef_Section { - sectionModel := repo1.SectionMap[idmapping.Uid] - if sectionModel == nil { - panic(fmt.Errorf("构建区段、道岔公里标错误:检测点[id=%d]关联的区段模型[uid=%s]不存在", checkpoint.Common.Id, idmapping.Uid)) + sectionModel, ok := repo1.PhysicalSectionMap[idmapping.Uid] + if !ok { + panic(fmt.Errorf("构建区段、道岔公里标数据错误:检测点[id=%d]关联的区段模型[uid=%s]不存在", checkpoint.Common.Id, idmapping.Uid)) } - sectionModel.(*modelimpl.Section).PaKm = model.NewKilometerMark(checkpoint.KilometerSystem.CoordinateSystem, convertKmDirection(checkpoint.KilometerSystem.Direction), checkpoint.KilometerSystem.Kilometer) + km := model.NewKilometerMark(checkpoint.KilometerSystem.CoordinateSystem, convertKmDirection(checkpoint.KilometerSystem.Direction), checkpoint.KilometerSystem.Kilometer) + if linkship.DevicePort == data_proto.RelatedRef_A { + sectionModel.(*modelimpl.PhysicalSection).PaKm = km + } else if linkship.DevicePort == data_proto.RelatedRef_B { + sectionModel.(*modelimpl.PhysicalSection).PbKm = km + } else { + panic(fmt.Errorf("构建区段、道岔公里标数据错误:检测点[id=%d]关联的区段模型[uid=%s]未知的端口类型: %v", checkpoint.Common.Id, idmapping.Uid, linkship.DevicePort)) + } + } else if linkship.DeviceType == data_proto.RelatedRef_Turnout { + turnoutModel, ok := repo1.TurnoutMap[idmapping.Uid] + 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) + if linkship.DevicePort == data_proto.RelatedRef_A { + turnoutModel.(*modelimpl.Turnout).PaKm = km + } else if linkship.DevicePort == data_proto.RelatedRef_B { + turnoutModel.(*modelimpl.Turnout).PbKm = km + } else if linkship.DevicePort == data_proto.RelatedRef_C { + turnoutModel.(*modelimpl.Turnout).PcKm = km + } else { + panic(fmt.Errorf("构建区段、道岔公里标数据错误:检测点[id=%d]关联的道岔模型[uid=%s]未知的端口类型: %v", checkpoint.Common.Id, idmapping.Uid, linkship.DevicePort)) + } + } else { + panic(fmt.Errorf("构建区段、道岔公里标数据错误:检测点[id=%d]未知的关联设备类型: %v", checkpoint.Common.Id, linkship.DeviceType)) } } } @@ -135,7 +179,7 @@ func buildSectionRelationships(dataMapping *repository.DataMapping, repo1 *repos if idmapping == nil { panic(fmt.Errorf("构建区段关系错误:idmapping异常为空")) } - sectionModel := repo1.SectionMap[idmapping.Uid] + sectionModel := repo1.PhysicalSectionMap[idmapping.Uid] buildSectionPortLinkRelation(section.PaRef, sectionModel, model.PipePortA, repo1, dataMapping) buildSectionPortLinkRelation(section.PbRef, sectionModel, model.PipePortB, repo1, dataMapping) } @@ -148,7 +192,7 @@ func buildTurnoutPortLinkship(pref *data_proto.RelatedRef, sourceModel model.Tur if pref.DeviceType == data_proto.RelatedRef_Section { idmapping2 := dataMapping.IdMappingMap[pref.Id] if idmapping2 != nil { - sectionModel := repo1.SectionMap[idmapping2.Uid] + sectionModel := repo1.PhysicalSectionMap[idmapping2.Uid] if sectionModel != nil { sourceModel.SetLinkedElement(port, model.NewPipeLink(sectionModel, convertPort(pref.DevicePort))) } @@ -165,14 +209,14 @@ func buildTurnoutPortLinkship(pref *data_proto.RelatedRef, sourceModel model.Tur } } -func buildSectionPortLinkRelation(pref *data_proto.RelatedRef, sourceModel model.Section, port model.PipePort, repo1 *repository.Repository, dataMapping *repository.DataMapping) { +func buildSectionPortLinkRelation(pref *data_proto.RelatedRef, sourceModel model.PhysicalSection, port model.PipePort, repo1 *repository.Repository, dataMapping *repository.DataMapping) { if pref == nil { return } if pref.DeviceType == data_proto.RelatedRef_Section { idmapping2 := dataMapping.IdMappingMap[pref.Id] if idmapping2 != nil { - sectionModel := repo1.SectionMap[idmapping2.Uid] + sectionModel := repo1.PhysicalSectionMap[idmapping2.Uid] if sectionModel != nil { sourceModel.SetLinkedElement(port, model.NewPipeLink(sectionModel, convertPort(pref.DevicePort))) } diff --git a/example/model_impl/section.go b/example/model_impl/section.go index 4ee0c0c..b1b34d1 100644 --- a/example/model_impl/section.go +++ b/example/model_impl/section.go @@ -2,6 +2,6 @@ package modelimpl import "joylink.club/rtss-core/model" -type Section struct { - *model.SectionImpl +type PhysicalSection struct { + *model.PhysicalSectionImpl } diff --git a/model/kilometer_mark.go b/model/kilometer_mark.go index 0bc8b69..25f926a 100644 --- a/model/kilometer_mark.go +++ b/model/kilometer_mark.go @@ -36,6 +36,10 @@ func (km *KilometerMark) Coordinate() string { return km.coordinate } +func (km *KilometerMark) Value() int64 { + return km.value +} + // 是否为同一坐标系 func (km *KilometerMark) IsCoordinateEqual(coordinate string) bool { return km.coordinate == coordinate diff --git a/model/model.go b/model/model.go index 74049ed..b60798d 100644 --- a/model/model.go +++ b/model/model.go @@ -138,21 +138,24 @@ func (t *TwoPortsPipeElementImpl) OppositePipeLink(port PipePort) *PipeLink { func (s *TwoPortsPipeElementImpl) SetLinkedElement(port PipePort, pipeLink *PipeLink) error { if port == PipePortA { if s.paPipeLink != nil { - return fmt.Errorf("区段uid=%s端口A已经设置关联元素: {uid=%s, port=%s}", s.uid, s.paPipeLink.Pipe.Uid(), s.paPipeLink.Port) + return fmt.Errorf("区段{uid=%s}端口A已经设置关联元素: {uid=%s, port=%s}", s.uid, s.paPipeLink.Pipe.Uid(), s.paPipeLink.Port) } s.paPipeLink = pipeLink } else if port == PipePortB { if s.pbPipeLink != nil { - return fmt.Errorf("区段uid=%s端口B已经设置关联元素: {uid=%s, port=%s}", s.uid, s.pbPipeLink.Pipe.Uid(), s.pbPipeLink.Port) + return fmt.Errorf("区段{uid=%s}端口B已经设置关联元素: {uid=%s, port=%s}", s.uid, s.pbPipeLink.Pipe.Uid(), s.pbPipeLink.Port) } s.pbPipeLink = pipeLink } else { - return fmt.Errorf("区段uid=%s设置关联元素端口错误,不支持C端口", s.uid) + return fmt.Errorf("区段{uid=%s}设置关联元素端口错误,不支持C端口", s.uid) } return nil } func (s *TwoPortsPipeElementImpl) CheckPipeLink() error { + if s.paPipeLink == nil && s.pbPipeLink == nil { + return fmt.Errorf("区段{uid=%s}两端都没有关联通道连接关系", s.uid) + } err := checkPipePortLink(s, PipePortA, s.paPipeLink) if err != nil { return err @@ -201,17 +204,17 @@ func (t *ThreePortsPipeElementImpl) GetLinkedElement(port PipePort) *PipeLink { func (t *ThreePortsPipeElementImpl) SetLinkedElement(port PipePort, pipeLink *PipeLink) error { if port == PipePortA { if t.paPipeLink != nil { - return fmt.Errorf("道岔uid=%s端口A已经设置关联元素: {uid=%s, port=%s}", t.uid, t.paPipeLink.Pipe.Uid(), t.paPipeLink.Port) + return fmt.Errorf("道岔{uid=%s}端口A已经设置关联元素: {uid=%s, port=%s}", t.uid, t.paPipeLink.Pipe.Uid(), t.paPipeLink.Port) } t.paPipeLink = pipeLink } else if port == PipePortB { if t.pbPipeLink != nil { - return fmt.Errorf("道岔uid=%s端口B已经设置关联元素: {uid=%s, port=%s}", t.uid, t.pbPipeLink.Pipe.Uid(), t.pbPipeLink.Port) + return fmt.Errorf("道岔{uid=%s}端口B已经设置关联元素: {uid=%s, port=%s}", t.uid, t.pbPipeLink.Pipe.Uid(), t.pbPipeLink.Port) } t.pbPipeLink = pipeLink } else if port == PipePortC { if t.pcPipeLink != nil { - return fmt.Errorf("道岔uid=%s端口C已经设置关联元素: {uid=%s, port=%s}", t.uid, t.pcPipeLink.Pipe.Uid(), t.pcPipeLink.Port) + return fmt.Errorf("道岔{uid=%s}端口C已经设置关联元素: {uid=%s, port=%s}", t.uid, t.pcPipeLink.Pipe.Uid(), t.pcPipeLink.Port) } t.pcPipeLink = pipeLink } diff --git a/model/section.go b/model/section.go index 705053a..f254262 100644 --- a/model/section.go +++ b/model/section.go @@ -2,32 +2,84 @@ package model // 区段 type Section interface { + RtssModel +} + +// 物理区段(非道岔区段) +type PhysicalSection interface { TwoPortsPipeElement // 获取A端和B端的公里标 GetPaKm() *KilometerMark GetPbKm() *KilometerMark } -type SectionImpl struct { +// 道岔区段 +type TurnoutSection interface { + Section + // 获取关联的道岔列表 + GetTurnouts() []Turnout +} + +// 逻辑区段 +type LogicalSection interface { + Section + // 获取A端和B端的公里标 + GetPaKm() *KilometerMark + GetPbKm() *KilometerMark +} + +type PhysicalSectionImpl struct { *TwoPortsPipeElementImpl PaKm *KilometerMark PbKm *KilometerMark } -var _ Section = (*SectionImpl)(nil) +var _ PhysicalSection = (*PhysicalSectionImpl)(nil) -func NewSection(uid string) *SectionImpl { - return &SectionImpl{ +func NewPhysicalSection(uid string) *PhysicalSectionImpl { + return &PhysicalSectionImpl{ TwoPortsPipeElementImpl: &TwoPortsPipeElementImpl{ uid: uid, }, } } -func (s *SectionImpl) GetPaKm() *KilometerMark { +func (s *PhysicalSectionImpl) GetPaKm() *KilometerMark { return s.PaKm } -func (s *SectionImpl) GetPbKm() *KilometerMark { +func (s *PhysicalSectionImpl) GetPbKm() *KilometerMark { return s.PbKm } + +type SectionImpl struct { + uid string +} + +func (s *SectionImpl) Uid() string { + return s.uid +} + +var _ TurnoutSection = (*TurnoutSectionImpl)(nil) + +type TurnoutSectionImpl struct { + *SectionImpl + Turnouts []Turnout +} + +func NewTurnoutSection(uid string) *TurnoutSectionImpl { + return &TurnoutSectionImpl{ + SectionImpl: &SectionImpl{ + uid: uid, + }, + Turnouts: make([]Turnout, 0), + } +} + +func (t *TurnoutSectionImpl) AddTurnout(turnout Turnout) { + t.Turnouts = append(t.Turnouts, turnout) +} + +func (t *TurnoutSectionImpl) GetTurnouts() []Turnout { + return t.Turnouts +} diff --git a/repo/repo.go b/repo/repo.go index c06c008..618f5f5 100644 --- a/repo/repo.go +++ b/repo/repo.go @@ -15,8 +15,12 @@ type Repo interface { GetStationByUid(uid string) model.Station GetSectionByUid(uid string) model.Section GetTurnoutByUid(uid string) model.Turnout - // 检查通道连接关系 - CheckPipeLink() error + // 检查区段、道岔通道连接关系 + CheckSectionAndTurnoutPipeLink() error + // 检查区段、道岔公里标 + CheckSectionAndTurnoutPortKms() error + // 检查Link、LinkNode通道连接关系 + CheckLinkAndLinkNodePipeLink() error // 转换公里标 ConvertKilometerMark(km *model.KilometerMark, targetCoordinate string) (int64, error) } @@ -27,7 +31,8 @@ type RepoImpl struct { id string BuildErrorInfos []error StationMap map[string]model.Station - SectionMap map[string]model.Section + 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 @@ -38,7 +43,8 @@ func NewRepo(id string) *RepoImpl { return &RepoImpl{ id: id, StationMap: make(map[string]model.Station), - SectionMap: make(map[string]model.Section), + 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), @@ -47,6 +53,9 @@ func NewRepo(id string) *RepoImpl { } 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 @@ -59,9 +68,40 @@ func (r *RepoImpl) ConvertKilometerMark(km *model.KilometerMark, targetCoordinat return 0, fmt.Errorf("未找到公里标转换配置: %s<->%s, 全部配置项为: %s", km.Coordinate(), targetCoordinate, strings.Join(existConfigs, ",")) } -// CheckPipeLink implements Repo. -func (r *RepoImpl) CheckPipeLink() error { - for _, section := range r.SectionMap { +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())) + } + } + 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 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) @@ -73,6 +113,14 @@ func (r *RepoImpl) CheckPipeLink() error { 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() @@ -94,7 +142,7 @@ func (r *RepoImpl) CheckPipeLink() error { // GetSectionByUid implements Repo. func (r *RepoImpl) GetSectionByUid(uid string) model.Section { - return r.SectionMap[uid] + return r.PhysicalSectionMap[uid] } // GetStationByUid implements Repo. @@ -157,7 +205,7 @@ func walkFromTurnoutPortToNextAndBuildLink(turnout model.Turnout, port model.Pip repo1.LinkMap[link.Uid()] = link return nextTurnout } else { - ple = ple.Pipe.(model.Section).OppositePipeLink(ple.Port) + ple = ple.Pipe.(model.PhysicalSection).OppositePipeLink(ple.Port) } } }