Compare commits

...

2 Commits

Author SHA1 Message Date
8264941969 重构model抽象结构 2024-06-27 19:59:13 +08:00
58754c2b04 section 和 turnout模型关系构建和检查 2024-06-26 19:00:01 +08:00
10 changed files with 422 additions and 117 deletions

View File

@ -1,6 +1,9 @@
package main
import (
"fmt"
"log"
"joylink.club/rtss-core/example/data_proto"
modelimpl "joylink.club/rtss-core/example/model_impl"
"joylink.club/rtss-core/example/repository"
@ -8,27 +11,172 @@ import (
)
func main() {
repo := repository.NewRepository("test1").(*repository.Repository)
repo1 := repository.NewRepository("test1")
rtssGraphicStorage := data_proto.GetLineSytle1()
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 {
repo.StationDataMap[station.Common.Id] = station
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 {
belongStation := repo.SectionDataMap[section.CentralizedStations[0]]
if section.CentralizedStations == nil || len(section.CentralizedStations) == 0 || repo.SectionDataMap[section.CentralizedStations[0]] == nil {
if section.CentralizedStations == nil || len(section.CentralizedStations) == 0 {
continue
}
repo.SectionDataMap[section.Common.Id] = section
uid := getSectionUid(section, belongStation)
repo.IpMapping = append(repo.IpMapping, repository.NewIdMapping(section.Common.Id, uid))
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))
sectionModel := model.NewSection(uid)
repo.SectionMap[uid] = &modelimpl.Section{SectionImpl: sectionModel}
repo1.SectionMap[uid] = &modelimpl.Section{SectionImpl: sectionModel}
}
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)
// 构建道岔关系
buildTurnoutRelationships(dataMapping, repo1)
// 检查区段、道岔通道连接关系
err := repo1.CheckPipeLink()
if err != nil {
log.Println(err)
} else {
log.Println("区段、道岔连接关系检查通过")
}
// 构建link/linknode
buildLinkNode(repo1)
}
func buildLinkNode(repo1 *repository.Repository) {
passedSectionMap := make(map[string]struct{})
for _, turnout := range repo1.TurnoutMap {
walkToNextTurnout(turnout, model.PipePortA, passedSectionMap)
break // 从一个道岔遍历所有区段、道岔
}
}
func getSectionUid(section *data_proto.Section, belongStation *data_proto.Section) string {
return belongStation.Code + "-" + section.Code
func walkToNextTurnout(turnout *modelimpl.Turnout, port model.PipePort, passedSectionMap map[string]struct{}) (*modelimpl.Turnout, model.PipePort) {
plink := turnout.GetLinkedElement(port)
if plink == nil {
return nil, model.PipePortA
}
for {
if plink.Pipe.IsThreePorts() {
return plink.Pipe.(*modelimpl.Turnout), plink.Port
}
}
}
func buildTurnoutRelationships(dataMapping *repository.DataMapping, repo1 *repository.Repository) {
for _, turnout := range dataMapping.TurnoutDataMap {
idmapping := dataMapping.IdMappingMap[turnout.Common.Id]
if idmapping == nil {
panic(fmt.Errorf("构建道岔关系错误idmapping异常为空"))
}
turnoutModel := repo1.TurnoutMap[idmapping.Uid]
buildTurnoutPortLinkRelation(turnout.PaRef, turnoutModel, model.PipePortA, repo1, dataMapping)
buildTurnoutPortLinkRelation(turnout.PbRef, turnoutModel, model.PipePortB, repo1, dataMapping)
buildTurnoutPortLinkRelation(turnout.PcRef, turnoutModel, model.PipePortC, repo1, dataMapping)
}
}
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异常为空"))
}
sectionModel := repo1.SectionMap[idmapping.Uid]
buildSectionPortLinkRelation(section.PaRef, sectionModel, model.PipePortA, repo1, dataMapping)
buildSectionPortLinkRelation(section.PbRef, sectionModel, model.PipePortB, repo1, dataMapping)
}
}
func buildTurnoutPortLinkRelation(pref *data_proto.RelatedRef, sourceModel *modelimpl.Turnout, 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]
if sectionModel != nil {
sourceModel.SetLinkedElement(port, model.NewPipeLink(sectionModel, convertPort(pref.DevicePort)))
}
}
}
if pref.DeviceType == data_proto.RelatedRef_Turnout {
idmapping2 := dataMapping.IdMappingMap[pref.Id]
if idmapping2 != nil {
turnoutModel2 := repo1.TurnoutMap[idmapping2.Uid]
if turnoutModel2 != nil {
sourceModel.SetLinkedElement(port, model.NewPipeLink(turnoutModel2, convertPort(pref.DevicePort)))
}
}
}
}
func buildSectionPortLinkRelation(pref *data_proto.RelatedRef, sourceModel *modelimpl.Section, 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]
if sectionModel != nil {
sourceModel.SetLinkedElement(port, model.NewPipeLink(sectionModel, convertPort(pref.DevicePort)))
}
}
}
if pref.DeviceType == data_proto.RelatedRef_Turnout {
idmapping2 := dataMapping.IdMappingMap[pref.Id]
if idmapping2 != nil {
turnoutModel := repo1.TurnoutMap[idmapping2.Uid]
if turnoutModel != nil {
sourceModel.SetLinkedElement(port, model.NewPipeLink(turnoutModel, convertPort(pref.DevicePort)))
}
}
}
}
func convertPort(port data_proto.RelatedRef_DevicePort) model.PipePort {
if port == data_proto.RelatedRef_A {
return model.PipePortA
} else if port == data_proto.RelatedRef_B {
return model.PipePortB
} else if port == data_proto.RelatedRef_C {
return model.PipePortC
}
panic(fmt.Errorf("未知的数据关联端口类型: %v", port))
}
func getSectionUid(section *data_proto.Section, belongStation *data_proto.Station, lineInfo string) string {
return fmt.Sprintf("%s/%s/%s", lineInfo, belongStation.Code, section.Code)
}
func getTurnoutUid(turnout *data_proto.Turnout, belongStation *data_proto.Station, lineInfo string) string {
return fmt.Sprintf("%s/%s/%s", lineInfo, belongStation.Code, turnout.Code)
}

View File

@ -3,5 +3,5 @@ package modelimpl
import "joylink.club/rtss-core/model"
type Station struct {
model.StationImpl
*model.StationImpl
}

View File

@ -3,5 +3,5 @@ package modelimpl
import "joylink.club/rtss-core/model"
type Turnout struct {
model.TurnoutImpl
*model.TurnoutImpl
}

View File

@ -1,6 +1,9 @@
package repository
import (
"errors"
"fmt"
"joylink.club/rtss-core/example/data_proto"
modelimpl "joylink.club/rtss-core/example/model_impl"
"joylink.club/rtss-core/model"
@ -20,27 +23,57 @@ func NewIdMapping(graphicId uint32, uid string) *IdMapping {
}
}
type Repository struct {
id string
type DataMapping struct {
id string
*data_proto.RtssGraphicStorage
StationDataMap map[uint32]*data_proto.Station
SectionDataMap map[uint32]*data_proto.Section
TurnoutDataMap map[uint32]*data_proto.Turnout
IpMapping []*IdMapping
StationMap map[string]*modelimpl.Station
SectionMap map[string]*modelimpl.Section
TurnoutMap map[string]*modelimpl.Turnout
IdMappingMap map[uint32]*IdMapping
UidMappingMap map[string]*IdMapping
}
func NewRepository(id string) repo.Repo {
func (d *DataMapping) AddIdMapping(idMapping *IdMapping) {
d.IdMappingMap[idMapping.GraphicId] = idMapping
d.UidMappingMap[idMapping.Uid] = idMapping
}
func (d *DataMapping) GetLineInfo() string {
lineInfo := d.RtssGraphicStorage.UniqueIdPrefix
return fmt.Sprintf("%s%s", lineInfo.City, lineInfo.LineId)
}
func NewDataMapping(id string, storage *data_proto.RtssGraphicStorage) *DataMapping {
return &DataMapping{
id: id,
RtssGraphicStorage: storage,
StationDataMap: make(map[uint32]*data_proto.Station),
SectionDataMap: make(map[uint32]*data_proto.Section),
TurnoutDataMap: make(map[uint32]*data_proto.Turnout),
IdMappingMap: make(map[uint32]*IdMapping),
UidMappingMap: make(map[string]*IdMapping),
}
}
type Repository struct {
id string
BuildErrorInfos []error
DataMapping map[string]*DataMapping
StationMap map[string]*modelimpl.Station
SectionMap map[string]*modelimpl.Section
TurnoutMap map[string]*modelimpl.Turnout
}
var _ repo.Repo = &Repository{}
func NewRepository(id string) *Repository {
return &Repository{
id: id,
StationDataMap: make(map[uint32]*data_proto.Station),
SectionDataMap: make(map[uint32]*data_proto.Section),
TurnoutDataMap: make(map[uint32]*data_proto.Turnout),
IpMapping: make([]*IdMapping, 0),
StationMap: make(map[string]*modelimpl.Station),
SectionMap: make(map[string]*modelimpl.Section),
TurnoutMap: make(map[string]*modelimpl.Turnout),
id: id,
BuildErrorInfos: make([]error, 0),
DataMapping: make(map[string]*DataMapping),
StationMap: make(map[string]*modelimpl.Station),
SectionMap: make(map[string]*modelimpl.Section),
TurnoutMap: make(map[string]*modelimpl.Turnout),
}
}
@ -59,3 +92,22 @@ func (r *Repository) GetSectionByUid(uid string) model.Section {
func (r *Repository) GetTurnoutByUid(uid string) model.Turnout {
return r.TurnoutMap[uid]
}
func (r *Repository) CheckPipeLink() error {
for _, section := range r.SectionMap {
err := section.CheckPipeLink()
if err != nil {
r.BuildErrorInfos = append(r.BuildErrorInfos, err)
}
}
for _, turnout := range r.TurnoutMap {
err := turnout.CheckPipeLink()
if err != nil {
r.BuildErrorInfos = append(r.BuildErrorInfos, err)
}
}
if len(r.BuildErrorInfos) > 0 {
return errors.Join(r.BuildErrorInfos...)
}
return nil
}

View File

@ -6,7 +6,6 @@ type RR = io.Writer
// 轨道
type Link interface {
RtssModel
PipeElement
}
@ -20,67 +19,36 @@ var _ Link = (*LinkImpl)(nil)
var _ LinkNode = (*LinkNodeImpl)(nil)
type LinkImpl struct {
uid string
paLinkNode *PipeLink
pbLinkNode *PipeLink
*TwoPortsPipeElement
}
type LinkNodeImpl struct {
turnout Turnout
paPipeLink *PipeLink
pbPipeLink *PipeLink
pcPipeLink *PipeLink
turnout Turnout
*ThreePortsPipeElement
}
func NewLink(nodea LinkNode, nodeb LinkNode) Link {
uid := buildLinkUid(nodea, nodeb)
return &LinkImpl{
uid: uid,
TwoPortsPipeElement: &TwoPortsPipeElement{
uid: uid,
},
}
}
func NewLinkNode(turnout Turnout) LinkNode {
return &LinkNodeImpl{
turnout: turnout,
ThreePortsPipeElement: &ThreePortsPipeElement{
uid: turnout.Uid(),
},
}
}
func (l *LinkImpl) Uid() string {
return l.uid
}
func (l *LinkImpl) Ports() []PipePort {
return twoPorts
}
func (l *LinkImpl) GetLinkedElement(port PipePort) *PipeLink {
if port == PipePortA {
return l.paLinkNode
} else if port == PipePortB {
return l.pbLinkNode
}
return nil
}
func (n *LinkNodeImpl) Turnout() Turnout {
return n.turnout
}
func (n *LinkNodeImpl) Ports() []PipePort {
return threePorts
}
func (n *LinkNodeImpl) GetLinkedElement(port PipePort) *PipeLink {
if port == PipePortA {
return n.paPipeLink
} else if port == PipePortB {
return n.pbPipeLink
} else if port == PipePortC {
return n.pcPipeLink
}
return nil
}
func buildLinkUid(nodea LinkNode, nodeb LinkNode) string {
if nodea == nil && nodeb == nil {
panic("构造link Uid错误: nodea和nodeb不能同时为空")

View File

@ -1,5 +1,9 @@
package model
import (
"fmt"
)
type RtssModel interface {
// RtssModel unique id
Uid() string
@ -30,10 +34,178 @@ type PipeLink struct {
Port PipePort
}
func NewPipeLink(pipe PipeElement, port PipePort) *PipeLink {
if pipe == nil {
panic("pipe is nil")
}
return &PipeLink{
Pipe: pipe,
Port: port,
}
}
func (pl *PipeLink) Debug() string {
return "{" + "\"pipeUid\":" + pl.Pipe.Uid() + ", \"port\":" + pl.Port + "}"
}
// 通道元素
type PipeElement interface {
RtssModel
// 获取通道端口
Ports() []PipePort
IsTwoPorts() bool
IsThreePorts() bool
// 获取指定端口连接的通道连接关系
GetLinkedElement(port PipePort) *PipeLink
}
func checkPipePortLink(pe PipeElement, port PipePort, pipeLink *PipeLink) error {
if pipeLink == nil {
return nil
}
plink := pipeLink.Pipe.GetLinkedElement(pipeLink.Port)
if plink == nil {
return fmt.Errorf("通道端口{uid=%s, port=%s}关联通道端口{uid=%s, port=%s},但关联通道的对应端口却未关联通道端口", pe.Uid(), port, pipeLink.Pipe.Uid(), pipeLink.Port)
} else if plink.Pipe.Uid() != pe.Uid() || plink.Port != port {
return fmt.Errorf("通道端口{uid=%s, port=%s}关联通道端口{uid=%s, port=%s},但关联通道的对应端口却关联了其他通道端口: {uid=%s, port=%s}", pe.Uid(), port, pipeLink.Pipe.Uid(), pipeLink.Port,
plink.Pipe.Uid(), plink.Port)
}
return nil
}
var _ PipeElement = (*TwoPortsPipeElement)(nil)
// 两端口的线性通道元素
type TwoPortsPipeElement struct {
uid string
paPipeLink *PipeLink
pbPipeLink *PipeLink
}
func (t *TwoPortsPipeElement) Uid() string {
return t.uid
}
func (t *TwoPortsPipeElement) Ports() []PipePort {
return twoPorts
}
func (t *TwoPortsPipeElement) IsTwoPorts() bool {
return true
}
func (t *TwoPortsPipeElement) IsThreePorts() bool {
return false
}
func (s *TwoPortsPipeElement) GetLinkedElement(port PipePort) *PipeLink {
if port == PipePortA {
return s.paPipeLink
} else if port == PipePortB {
return s.pbPipeLink
}
return nil
}
func (t *TwoPortsPipeElement) OppositePipeLink(port PipePort) PipeLink {
if port == PipePortA {
return *t.pbPipeLink
}
return *t.paPipeLink
}
func (s *TwoPortsPipeElement) 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)
}
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)
}
s.pbPipeLink = pipeLink
} else {
return fmt.Errorf("区段uid=%s设置关联元素端口错误,不支持C端口", s.uid)
}
return nil
}
func (s *TwoPortsPipeElement) CheckPipeLink() error {
err := checkPipePortLink(s, PipePortA, s.paPipeLink)
if err != nil {
return err
}
err = checkPipePortLink(s, PipePortB, s.pbPipeLink)
return err
}
var _ PipeElement = (*ThreePortsPipeElement)(nil)
// 三端口的通道元素
type ThreePortsPipeElement struct {
uid string
paPipeLink *PipeLink
pbPipeLink *PipeLink
pcPipeLink *PipeLink
}
func (t *ThreePortsPipeElement) Uid() string {
return t.uid
}
func (t *ThreePortsPipeElement) Ports() []PipePort {
return threePorts
}
func (t *ThreePortsPipeElement) IsTwoPorts() bool {
return false
}
func (t *ThreePortsPipeElement) IsThreePorts() bool {
return true
}
func (t *ThreePortsPipeElement) GetLinkedElement(port PipePort) *PipeLink {
if port == PipePortA {
return t.paPipeLink
} else if port == PipePortB {
return t.pbPipeLink
} else if port == PipePortC {
return t.pcPipeLink
}
return nil
}
func (t *ThreePortsPipeElement) 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)
}
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)
}
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)
}
t.pcPipeLink = pipeLink
}
return nil
}
func (t *ThreePortsPipeElement) CheckPipeLink() error {
err := checkPipePortLink(t, PipePortA, t.paPipeLink)
if err != nil {
return err
}
err = checkPipePortLink(t, PipePortB, t.pbPipeLink)
if err != nil {
return err
}
err = checkPipePortLink(t, PipePortC, t.pcPipeLink)
return err
}

View File

@ -2,37 +2,19 @@ package model
// 区段
type Section interface {
RtssModel
PipeElement
}
type SectionImpl struct {
uid string
paPipeLink *PipeLink
pbPipeLink *PipeLink
*TwoPortsPipeElement
}
var _ Section = (*SectionImpl)(nil)
func NewSection(uid string) *SectionImpl {
return &SectionImpl{
uid: uid,
TwoPortsPipeElement: &TwoPortsPipeElement{
uid: uid,
},
}
}
func (s *SectionImpl) Uid() string {
return s.uid
}
func (s *SectionImpl) Ports() []PipePort {
return twoPorts
}
func (s *SectionImpl) GetLinkedElement(port PipePort) *PipeLink {
if port == PipePortA {
return s.paPipeLink
} else if port == PipePortB {
return s.pbPipeLink
}
return nil
}

View File

@ -12,7 +12,7 @@ type StationImpl struct {
centralized bool
}
func NewStation(uid string, centralized bool) Station {
func NewStation(uid string, centralized bool) *StationImpl {
return &StationImpl{
uid: uid,
centralized: centralized,

View File

@ -1,20 +1,18 @@
package model
import "strings"
import (
"strings"
)
// 道岔
type Turnout interface {
RtssModel
PipeElement
}
var _ Turnout = (*TurnoutImpl)(nil)
type TurnoutImpl struct {
uid string
paPipeLink *PipeLink
pbPipeLink *PipeLink
pcPipeLink *PipeLink
*ThreePortsPipeElement
}
func NewTurnout(uid string) *TurnoutImpl {
@ -22,25 +20,8 @@ func NewTurnout(uid string) *TurnoutImpl {
panic("Turnout uid is empty")
}
return &TurnoutImpl{
uid: uid,
ThreePortsPipeElement: &ThreePortsPipeElement{
uid: uid,
},
}
}
func (t *TurnoutImpl) Uid() string {
return t.uid
}
func (t *TurnoutImpl) Ports() []PipePort {
return threePorts
}
func (t *TurnoutImpl) GetLinkedElement(port PipePort) *PipeLink {
if port == PipePortA {
return t.paPipeLink
} else if port == PipePortB {
return t.pbPipeLink
} else if port == PipePortC {
return t.pcPipeLink
}
return nil
}

View File

@ -8,4 +8,6 @@ type Repo interface {
GetStationByUid(uid string) model.Station
GetSectionByUid(uid string) model.Section
GetTurnoutByUid(uid string) model.Turnout
// 检查通道连接关系
CheckPipeLink() error
}