ecs 测试

This commit is contained in:
xzb 2023-08-08 13:52:19 +08:00
parent 283dac24a4
commit 57deaec338
15 changed files with 570 additions and 0 deletions

5
examples/rtss-cg/go.mod Normal file
View File

@ -0,0 +1,5 @@
module joylink.club/rtss
go 1.20
require github.com/yohamta/donburi v1.3.8

1
examples/rtss-cg/go.sum Normal file
View File

@ -0,0 +1 @@
github.com/yohamta/donburi v1.3.8 h1:ca4NuhzJ8Jeb6GAEf6ecksa+l8JWaAnr0WLqG20TimU=

View File

@ -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
}

127
examples/rtss-cg/main.go Normal file
View File

@ -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}
}

View File

@ -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
}

View File

@ -0,0 +1,13 @@
package mwayside
//长link只与道岔相连
type LinkModel struct {
//轨道基本信息
DeviceModel
//轨道长度,单位mm
Len int64
//轨道A端连接的道岔
PortA *SwitchRef
//轨道B端连接的道岔
PortB *SwitchRef
}

View File

@ -0,0 +1,12 @@
package mwayside
type SwitchModel struct {
//道岔基本信息
DeviceModel
//道岔A端连接的轨道
PortA *LinkRef
//道岔B端连接的轨道
PortB *LinkRef
//道岔C端连接的轨道
PortC *LinkRef
}

View File

@ -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]()

View File

@ -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
}

View File

@ -0,0 +1,10 @@
package state
//轨道状态
//模拟测试区段状态
type LinkState struct {
//轨道id
Id string
//是否有车占用
Occupied bool
}

View File

@ -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
}

View File

@ -0,0 +1,13 @@
package state
//列车状态
type TrainState struct {
//列车id
Id string
//列车所在linkid
LinkId string
//列车在link上的偏移量(A端为起点)
LinkOffset int64
//运行方向,true-由左向右
Up bool
}

View File

@ -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)),
}
}

View File

@ -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))}
}

View File

@ -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)
}