diff --git a/examples/rtss-cg/go.mod b/examples/rtss-cg/go.mod new file mode 100644 index 0000000..461a18a --- /dev/null +++ b/examples/rtss-cg/go.mod @@ -0,0 +1,5 @@ +module joylink.club/rtss + +go 1.20 + +require github.com/yohamta/donburi v1.3.8 diff --git a/examples/rtss-cg/go.sum b/examples/rtss-cg/go.sum new file mode 100644 index 0000000..8cc54a0 --- /dev/null +++ b/examples/rtss-cg/go.sum @@ -0,0 +1 @@ +github.com/yohamta/donburi v1.3.8 h1:ca4NuhzJ8Jeb6GAEf6ecksa+l8JWaAnr0WLqG20TimU= diff --git a/examples/rtss-cg/iwayside/iwayside.go b/examples/rtss-cg/iwayside/iwayside.go new file mode 100644 index 0000000..eddaa45 --- /dev/null +++ b/examples/rtss-cg/iwayside/iwayside.go @@ -0,0 +1,45 @@ +package iwayside + +//设备类型枚举定义 +type DeviceType int + +const ( + //道岔 + Switch DeviceType = iota + //长轨道 + Link +) + +///////////////////////////////////// + +//设备端口定义 +type DevicePort int + +const ( + A DevicePort = iota + B + C +) + +//////////////////////////////////// + +type IDeviceModel interface { + GetType() DeviceType + GetId() string +} + +type ISwitchModel interface { + IDeviceModel +} + +type ILinkModel interface { + IDeviceModel +} + +////////////////////////////// + +//轨道上的位置定义 +type LinkLocation struct { + LinkId string + LinkOffset int64 +} diff --git a/examples/rtss-cg/main.go b/examples/rtss-cg/main.go new file mode 100644 index 0000000..0a46a0b --- /dev/null +++ b/examples/rtss-cg/main.go @@ -0,0 +1,127 @@ +package main + +import ( + "log" + "time" + + "github.com/yohamta/donburi/filter" + "joylink.club/ecs" + "joylink.club/rtss/iwayside" + "joylink.club/rtss/mwayside" + "joylink.club/rtss/simulation" + "joylink.club/rtss/state" + "joylink.club/rtss/system" +) + +func init() { + log.SetFlags(log.Lshortfile | log.Lmicroseconds | log.Ldate) +} + +// 1 world 须增加attach , 绑定与该world相关的信息 +// 2 事件的订阅、发布、处理是独立调用执行的,事件处理可以集中处理也可以只处理某类事件 +func main() { + // + simWorld := ecs.NewWorld(400) + // + initMemoryModel(simWorld) + //道岔 + initSwitchState(simWorld) + //轨道 + initLinkState(simWorld) + //添加列车 + initTrainState(simWorld, "train1", iwayside.LinkLocation{LinkId: "link4", LinkOffset: 3000 * 100 * 10}, false) + // + simWorld.AddSystem(system.NewSwitchSystem()) + simWorld.AddSystem(system.NewTrainSystem()) + simWorld.AddSystem(system.NewCiSystem()) + // + simWorld.StartUp() + // + time.Sleep(2 * time.Second) + // + operateSwitch(simWorld, "dc1") + operateSwitch(simWorld, "dc2") + time.Sleep(200 * time.Second) +} + +// 根据道岔id来搬动道岔 +func operateSwitch(world ecs.World, dcId string) { + query := ecs.NewQuery(filter.Contains(simulation.ComSwitchState, simulation.ComSwitchOperating)) + query.Each(world, func(e *ecs.Entry) { + dcState := simulation.ComSwitchState.Get(e) + if dcState.Id == dcId { + simulation.ComSwitchOperating.SetValue(e, state.SwitchOperating{EnableStart: true, StartTime: time.Now()}) + } + }) +} + +// 初始化memory +func initMemoryModel(world ecs.World) { + memoryEntry := world.Create(simulation.ComSimMemory) + simulation.ComSimMemory.SetValue(memoryEntry, *simulation.NewSimMemory()) + // + simMemory := simulation.ComSimMemory.Get(memoryEntry) + createModelsRelation(simMemory) +} + +// 初始化道岔状态 +func initSwitchState(world ecs.World) { + simMemory := simulation.GetMemoryModel(world) + for _, switchModel := range simMemory.Switchs { + dc1 := &state.SwitchState{Id: switchModel.GetId(), Normal: true, Reverse: false} + dc1Entry := world.Create(simulation.ComSwitchState, simulation.ComSwitchOperating) + simulation.ComSwitchState.SetValue(dc1Entry, *dc1) + } +} + +// 初始化轨道状态 +func initLinkState(world ecs.World) { + simMemory := simulation.GetMemoryModel(world) + for _, linkModel := range simMemory.Links { + linkState := &state.LinkState{Id: linkModel.GetId(), Occupied: false} + linkEntry := world.Create(simulation.ComLinkState) + simulation.ComLinkState.SetValue(linkEntry, *linkState) + } +} + +// 添加列车并初始化 +func initTrainState(world ecs.World, trainId string, linkLocation iwayside.LinkLocation, up bool) { + train := &state.TrainState{Id: trainId, LinkId: linkLocation.LinkId, LinkOffset: linkLocation.LinkOffset, Up: up} + trainEntry := world.Create(simulation.ComTrainState) + simulation.ComTrainState.SetValue(trainEntry, *train) +} + +// 构建模型关系 +func createModelsRelation(memory *simulation.SimMemory) { + // + link1Model := &mwayside.LinkModel{DeviceModel: mwayside.DeviceModel{Id: "link1", Type: iwayside.Link}, Len: 3000 * 100 * 10} + link2Model := &mwayside.LinkModel{DeviceModel: mwayside.DeviceModel{Id: "link2", Type: iwayside.Link}, Len: 3070 * 100 * 10} + link3Model := &mwayside.LinkModel{DeviceModel: mwayside.DeviceModel{Id: "link3", Type: iwayside.Link}, Len: 3070 * 100 * 10} + link4Model := &mwayside.LinkModel{DeviceModel: mwayside.DeviceModel{Id: "link4", Type: iwayside.Link}, Len: 3000 * 100 * 10} + link5Model := &mwayside.LinkModel{DeviceModel: mwayside.DeviceModel{Id: "link5", Type: iwayside.Link}, Len: 120 * 100 * 10} + memory.Links[link1Model.Id] = link1Model + memory.Links[link2Model.Id] = link2Model + memory.Links[link3Model.Id] = link3Model + memory.Links[link4Model.Id] = link4Model + memory.Links[link5Model.Id] = link5Model + // + switch1Model := &mwayside.SwitchModel{DeviceModel: mwayside.DeviceModel{Id: "dc1", Type: iwayside.Switch}} + switch2Model := &mwayside.SwitchModel{DeviceModel: mwayside.DeviceModel{Id: "dc2", Type: iwayside.Switch}} + memory.Switchs[switch1Model.Id] = switch1Model + memory.Switchs[switch2Model.Id] = switch2Model + // + link1Model.PortB = &mwayside.SwitchRef{Port: iwayside.A, Switch: switch1Model} + link2Model.PortA = &mwayside.SwitchRef{Port: iwayside.B, Switch: switch1Model} + link3Model.PortB = &mwayside.SwitchRef{Port: iwayside.B, Switch: switch2Model} + link4Model.PortA = &mwayside.SwitchRef{Port: iwayside.A, Switch: switch2Model} + link5Model.PortA = &mwayside.SwitchRef{Port: iwayside.C, Switch: switch1Model} + link5Model.PortB = &mwayside.SwitchRef{Port: iwayside.C, Switch: switch2Model} + // + switch1Model.PortA = &mwayside.LinkRef{Port: iwayside.B, Link: link1Model} + switch1Model.PortB = &mwayside.LinkRef{Port: iwayside.A, Link: link2Model} + switch1Model.PortC = &mwayside.LinkRef{Port: iwayside.A, Link: link5Model} + // + switch2Model.PortA = &mwayside.LinkRef{Port: iwayside.A, Link: link4Model} + switch2Model.PortB = &mwayside.LinkRef{Port: iwayside.B, Link: link3Model} + switch2Model.PortC = &mwayside.LinkRef{Port: iwayside.B, Link: link5Model} +} diff --git a/examples/rtss-cg/mwayside/common.go b/examples/rtss-cg/mwayside/common.go new file mode 100644 index 0000000..bcab498 --- /dev/null +++ b/examples/rtss-cg/mwayside/common.go @@ -0,0 +1,29 @@ +package mwayside + +import "joylink.club/rtss/iwayside" + +//设备基类定义 +type DeviceModel struct { + Id string + Type iwayside.DeviceType +} + +func (me *DeviceModel) GetType() iwayside.DeviceType { + return me.Type +} +func (me *DeviceModel) GetId() string { + return me.Id +} + +///////////////////////////////////////// +//长轨道端口引用 +type LinkRef struct { + Port iwayside.DevicePort + Link iwayside.ILinkModel +} + +//道岔端口引用 +type SwitchRef struct { + Port iwayside.DevicePort + Switch iwayside.ISwitchModel +} diff --git a/examples/rtss-cg/mwayside/link_model.go b/examples/rtss-cg/mwayside/link_model.go new file mode 100644 index 0000000..777b16a --- /dev/null +++ b/examples/rtss-cg/mwayside/link_model.go @@ -0,0 +1,13 @@ +package mwayside + +//长link只与道岔相连 +type LinkModel struct { + //轨道基本信息 + DeviceModel + //轨道长度,单位mm + Len int64 + //轨道A端连接的道岔 + PortA *SwitchRef + //轨道B端连接的道岔 + PortB *SwitchRef +} diff --git a/examples/rtss-cg/mwayside/switch_model.go b/examples/rtss-cg/mwayside/switch_model.go new file mode 100644 index 0000000..9684ec8 --- /dev/null +++ b/examples/rtss-cg/mwayside/switch_model.go @@ -0,0 +1,12 @@ +package mwayside + +type SwitchModel struct { + //道岔基本信息 + DeviceModel + //道岔A端连接的轨道 + PortA *LinkRef + //道岔B端连接的轨道 + PortB *LinkRef + //道岔C端连接的轨道 + PortC *LinkRef +} diff --git a/examples/rtss-cg/simulation/ecs_components.go b/examples/rtss-cg/simulation/ecs_components.go new file mode 100644 index 0000000..7c3df84 --- /dev/null +++ b/examples/rtss-cg/simulation/ecs_components.go @@ -0,0 +1,19 @@ +package simulation + +import ( + "joylink.club/ecs" + "joylink.club/rtss/state" +) + +// 道岔状态组件 +var ComSwitchState = ecs.NewComponentType[state.SwitchState]() +var ComSwitchOperating = ecs.NewComponentType[state.SwitchOperating]() + +// 轨道状态组件 +var ComLinkState = ecs.NewComponentType[state.LinkState]() + +// 列车状态组件 +var ComTrainState = ecs.NewComponentType[state.TrainState]() + +// 仿真模型组件 +var ComSimMemory = ecs.NewComponentType[SimMemory]() diff --git a/examples/rtss-cg/simulation/sim_memory.go b/examples/rtss-cg/simulation/sim_memory.go new file mode 100644 index 0000000..9c4b781 --- /dev/null +++ b/examples/rtss-cg/simulation/sim_memory.go @@ -0,0 +1,24 @@ +package simulation + +import ( + "github.com/yohamta/donburi/component" + "github.com/yohamta/donburi/filter" + "joylink.club/ecs" + "joylink.club/rtss/iwayside" +) + +type SimMemory struct { + Links map[string]iwayside.ILinkModel + Switchs map[string]iwayside.ISwitchModel +} + +func NewSimMemory() *SimMemory { + return &SimMemory{Links: make(map[string]iwayside.ILinkModel), Switchs: make(map[string]iwayside.ISwitchModel)} +} + +func GetMemoryModel(world ecs.World) *SimMemory { + if memoryEntry, ok := ecs.NewQuery(filter.Exact([]component.IComponentType{ComSimMemory})).First(world); ok { + return ComSimMemory.Get(memoryEntry) + } + return nil +} diff --git a/examples/rtss-cg/state/link_state.go b/examples/rtss-cg/state/link_state.go new file mode 100644 index 0000000..1950721 --- /dev/null +++ b/examples/rtss-cg/state/link_state.go @@ -0,0 +1,10 @@ +package state + +//轨道状态 +//模拟测试区段状态 +type LinkState struct { + //轨道id + Id string + //是否有车占用 + Occupied bool +} diff --git a/examples/rtss-cg/state/switch_state.go b/examples/rtss-cg/state/switch_state.go new file mode 100644 index 0000000..2b470ec --- /dev/null +++ b/examples/rtss-cg/state/switch_state.go @@ -0,0 +1,17 @@ +package state + +import "time" + +//道岔状态 +type SwitchState struct { + Id string + Normal bool + Reverse bool +} + +//执行道岔操作的执行时状态 +//如3秒内完成道岔的转换 +type SwitchOperating struct { + EnableStart bool + StartTime time.Time +} diff --git a/examples/rtss-cg/state/train_state.go b/examples/rtss-cg/state/train_state.go new file mode 100644 index 0000000..6c66f6d --- /dev/null +++ b/examples/rtss-cg/state/train_state.go @@ -0,0 +1,13 @@ +package state + +//列车状态 +type TrainState struct { + //列车id + Id string + //列车所在linkid + LinkId string + //列车在link上的偏移量(A端为起点) + LinkOffset int64 + //运行方向,true-由左向右 + Up bool +} diff --git a/examples/rtss-cg/system/ci_system.go b/examples/rtss-cg/system/ci_system.go new file mode 100644 index 0000000..9de7cdc --- /dev/null +++ b/examples/rtss-cg/system/ci_system.go @@ -0,0 +1,50 @@ +package system + +import ( + "fmt" + "strings" + + "github.com/yohamta/donburi/filter" + "joylink.club/ecs" + "joylink.club/rtss/simulation" +) + +// 把长轨道当成物理区段,列车当成一个点,来计算物理区段的占用空闲状态 +type CiSystem struct { + //轨道查询 + linkQuery *ecs.Query + //列车查询 + trainQuery *ecs.Query +} + +func (me *CiSystem) Update(w ecs.World) { + //key linkId,value bool + linkOccupyMap := make(map[string]bool) + me.trainQuery.Each(w, func(e *ecs.Entry) { + train := simulation.ComTrainState.Get(e) + + if len(strings.TrimSpace(train.LinkId)) > 0 { + linkOccupyMap[train.LinkId] = true + } + }) + //轨道是否被列车占用 + me.linkQuery.Each(w, func(e *ecs.Entry) { + link := simulation.ComLinkState.Get(e) + if linkOccupied, ok := linkOccupyMap[link.Id]; ok { + link.Occupied = linkOccupied + } else { + link.Occupied = false + } + }) + me.linkQuery.Each(w, func(e *ecs.Entry) { + link := simulation.ComLinkState.Get(e) + fmt.Println("==>>轨道(", link.Id, ") 上有车(", link.Occupied, ")") + }) +} + +func NewCiSystem() *CiSystem { + return &CiSystem{ + linkQuery: ecs.NewQuery(filter.Contains(simulation.ComLinkState)), + trainQuery: ecs.NewQuery(filter.Contains(simulation.ComTrainState)), + } +} diff --git a/examples/rtss-cg/system/switch_system.go b/examples/rtss-cg/system/switch_system.go new file mode 100644 index 0000000..ed6b747 --- /dev/null +++ b/examples/rtss-cg/system/switch_system.go @@ -0,0 +1,38 @@ +package system + +import ( + "fmt" + "time" + + "github.com/yohamta/donburi/filter" + "joylink.club/ecs" + "joylink.club/rtss/simulation" +) + +type SwitchSystem struct { + ComQuery *ecs.Query +} + +func (me *SwitchSystem) Update(w ecs.World) { + me.ComQuery.Each(w, func(e *ecs.Entry) { + state := simulation.ComSwitchState.Get(e) + fmt.Printf("==>>当前状态:道岔id=%s 定位=%t 反位=%t ", state.Id, state.Normal, state.Reverse) + opt := simulation.ComSwitchOperating.Get(e) + if nil != opt && opt.EnableStart { + if time.Duration(time.Now().Second()-opt.StartTime.Second()) <= 4 { + fmt.Println(" ... 扳道中 ....") + } else { + opt.EnableStart = false + fmt.Println(" ... 完成扳道...") + state.Normal = !state.Normal + state.Reverse = !state.Reverse + } + } else { + fmt.Println() + } + }) + +} +func NewSwitchSystem() *SwitchSystem { + return &SwitchSystem{ComQuery: ecs.NewQuery(filter.Contains(simulation.ComSwitchOperating))} +} diff --git a/examples/rtss-cg/system/train_system.go b/examples/rtss-cg/system/train_system.go new file mode 100644 index 0000000..c5d2300 --- /dev/null +++ b/examples/rtss-cg/system/train_system.go @@ -0,0 +1,167 @@ +package system + +import ( + "fmt" + + "github.com/yohamta/donburi/filter" + "joylink.club/ecs" + "joylink.club/rtss/iwayside" + "joylink.club/rtss/mwayside" + "joylink.club/rtss/simulation" + "joylink.club/rtss/state" +) + +type TrainSystem struct { + ComTrainQuery *ecs.Query + ComSwitchQuery *ecs.Query +} + +// 模拟列车运行,更新列车位置 +func (me *TrainSystem) Update(w ecs.World) { + me.ComTrainQuery.Each(w, func(e *ecs.Entry) { + train := simulation.ComTrainState.Get(e) + fmt.Println("==>>列车 id = ", train.Id, " linkId = ", train.LinkId, " offset = ", train.LinkOffset, " up = ", train.Up) + curLink := findTrainLinkModel(w, train.LinkId) + var offset int64 = train.LinkOffset + func() int64 { + if train.Up { + return 20 * 1000 + } else { + return -20 * 1000 + } + }() + if offset >= 0 && offset <= curLink.Len { + train.LinkOffset = offset + } else { + nextLink := findNextLink(w, me, curLink, train.Up) + if nil != nextLink { + train.LinkId = nextLink.Id + if train.Up { + train.LinkOffset = 0 + } else { + train.LinkOffset = nextLink.Len + } + } else { + fmt.Println("==>>列车 id = ", train.Id, " , 路到尽头") + } + } + + }) +} + +// 获取列车运行的下一个轨道 +func findNextLink(w ecs.World, ts *TrainSystem, curLink *mwayside.LinkModel, up bool) *mwayside.LinkModel { + if up { + curLinkPortBRef := curLink.PortB + if nil == curLinkPortBRef { + return nil + } + dcState, _ := findSwitchModel(w, ts, curLinkPortBRef.Switch.GetId()) + switch curLinkPortBRef.Port { + case iwayside.A: + if dcState.Normal { + nextLinkRef := curLinkPortBRef.Switch.(*mwayside.SwitchModel).PortB + if nil == nextLinkRef { + return nil + } + return nextLinkRef.Link.(*mwayside.LinkModel) + } else { + nextLinkRef := curLinkPortBRef.Switch.(*mwayside.SwitchModel).PortC + if nil == nextLinkRef { + return nil + } + return nextLinkRef.Link.(*mwayside.LinkModel) + } + case iwayside.B: + if dcState.Normal { + nextLinkRef := curLinkPortBRef.Switch.(*mwayside.SwitchModel).PortA + if nil == nextLinkRef { + return nil + } + return nextLinkRef.Link.(*mwayside.LinkModel) + } else { + fmt.Println("==>>从道岔B侧进入,则道岔须处于定位,但此时道岔处于反位 。。。") + return nil + } + case iwayside.C: + if dcState.Reverse { + nextLinkRef := curLinkPortBRef.Switch.(*mwayside.SwitchModel).PortA + if nil == nextLinkRef { + return nil + } + return nextLinkRef.Link.(*mwayside.LinkModel) + } else { + fmt.Println("==>>从道岔C侧进入,则道岔须处于反位,但此时道岔处于定位 。。。") + return nil + } + } + } else { + curLinkPortARef := curLink.PortA + if nil == curLinkPortARef { + return nil + } + dcState, _ := findSwitchModel(w, ts, curLinkPortARef.Switch.GetId()) + switch curLinkPortARef.Port { + case iwayside.A: + if dcState.Normal { + nextLinkRef := curLinkPortARef.Switch.(*mwayside.SwitchModel).PortB + if nil == nextLinkRef { + return nil + } + return nextLinkRef.Link.(*mwayside.LinkModel) + } else { + nextLinkRef := curLinkPortARef.Switch.(*mwayside.SwitchModel).PortC + if nil == nextLinkRef { + return nil + } + return nextLinkRef.Link.(*mwayside.LinkModel) + } + case iwayside.B: + if dcState.Normal { + nextLinkRef := curLinkPortARef.Switch.(*mwayside.SwitchModel).PortA + if nil == nextLinkRef { + return nil + } + return nextLinkRef.Link.(*mwayside.LinkModel) + } else { + fmt.Println("==>>从道岔B侧进入,则道岔须处于定位,但此时道岔处于反位 。。。") + } + case iwayside.C: + if dcState.Reverse { + nextLinkRef := curLinkPortARef.Switch.(*mwayside.SwitchModel).PortA + if nil == nextLinkRef { + return nil + } + return nextLinkRef.Link.(*mwayside.LinkModel) + } else { + fmt.Println("==>>从道岔C侧进入,则道岔须处于反位,但此时道岔处于定位 。。。") + } + } + } + return nil +} + +// 获取道岔 +func findSwitchModel(w ecs.World, ts *TrainSystem, switchId string) (*state.SwitchState, *mwayside.SwitchModel) { + var dcState *state.SwitchState = nil + ts.ComSwitchQuery.Each(w, func(e *ecs.Entry) { + switchState := simulation.ComSwitchState.Get(e) + if switchState.Id == switchId { + dcState = switchState + } + }) + if nil == dcState { + return nil, nil + } + return dcState, simulation.GetMemoryModel(w).Switchs[dcState.Id].(*mwayside.SwitchModel) +} +func NewTrainSystem() *TrainSystem { + return &TrainSystem{ + ComTrainQuery: ecs.NewQuery(filter.Contains(simulation.ComTrainState)), + ComSwitchQuery: ecs.NewQuery(filter.Contains(simulation.ComSwitchState)), + } +} + +// 获取列车当前所在轨道 +func findTrainLinkModel(w ecs.World, linkId string) *mwayside.LinkModel { + return simulation.GetMemoryModel(w).Links[linkId].(*mwayside.LinkModel) +}