diff --git a/component/cbtc_atp_ground.go b/component/cbtc_atp_ground.go new file mode 100644 index 0000000..60b3eb6 --- /dev/null +++ b/component/cbtc_atp_ground.go @@ -0,0 +1,168 @@ +package component + +import ( + "time" + + "joylink.club/ecs" +) + +// 控制模式 +type ControlMode uint8 + +const ( + ControlMode_None ControlMode = iota // 交出未被接收 + ControlMode_Center // 中控 + ControlMode_Local // 站控/自律站控 + ControlMode_Emergency // 紧急站控 + ControlMode_Interlock // 联锁控/非常站控 +) + +// 控制模式 +type ControlState struct { + Model ControlMode // 当前控制模式 +} + +var ControlStateType = ecs.NewComponentType[ControlState]() // 控制模式 + +// 限速组件 +type SpeedLimit struct { + Val float32 // 限速值 +} + +var SpeedLimitType = ecs.NewComponentType[SpeedLimit]() // 限速组件 + +// 占用类型 +type OccupiedType uint8 + +const ( + Occupied_Ct OccupiedType = iota // 通信车占用 + Occupied_Nct // 非通信车占用 + Occupied_Fault // 故障占用 +) + +// 占用状态 +type OccupiedStatus struct { + Type OccupiedType +} + +var OccupiedStatusType = ecs.NewComponentType[OccupiedStatus]() // 占用状态组件 + +// 进路锁闭状态 +type RouteLock struct { + Route *ecs.Entry +} + +var RouteLockType = ecs.NewComponentType[RouteLock]() + +// 故障锁闭状态 +type FaultLock struct { + Fault any // 故障信息 +} + +var FaultLockType = ecs.NewComponentType[FaultLock]() // 故障锁闭状态组件 + +// 封锁状态 +type BlockadeStatus struct { +} + +var BlockadeStatusType = ecs.NewComponentType[BlockadeStatus]() // 封锁状态组件 + +// 解锁状态 +type UnLockStatus struct { + Delay bool // 延迟状态 + DelayTime time.Duration // 延迟解锁时间 + TriggerDevice *ecs.Entry // 触发解锁设备 +} + +var UnLockStatusType = ecs.NewComponentType[UnLockStatus]() // 解锁状态组件 + +// 关闭状态 +type ClosedStatus struct { +} + +var ClosedStatusType = ecs.NewComponentType[ClosedStatus]() // 关闭状态组件 + +// 站台跳停状态 +type PlatformSkipStatus struct { + AllSkip bool // 全部跳停 + Trains []*ecs.Entry // 跳停列车 +} + +var PlatformSkipStatusType = ecs.NewComponentType[PlatformSkipStatus]() // 站台跳停状态 + +// 站台停靠发车状态 +type PlatformParkStatus struct { + Park bool // 列车停靠状态 + ParkTime time.Duration // 停靠时间 + Depart bool // 列车发车状态 +} + +var PlatformParkStatusType = ecs.NewComponentType[PlatformParkStatus]() // 站台停靠发车状态 + +// 扣车状态 +type PlatformHoldStatus struct { +} + +var PlatformHoldStatusType = ecs.NewComponentType[PlatformHoldStatus]() // 扣车状态组件 + +// 信号机状态 +type SignalStatus struct { + ApproachLock bool // 接近锁闭 + OverlapLock bool // 延续保护锁闭 +} + +var SignalStatusType = ecs.NewComponentType[SignalStatus]() // 信号机状态组件 + +// 进路状态 +type RouteStatus struct { + AtsControl bool // ats自动控制 + Setting bool // 进路是否排列中 + Lock bool // 已锁闭 +} + +var RouteStatusType = ecs.NewComponentType[RouteStatus]() // 进路状态组件 + +// 列车运行级别 +type TrainRunLevel uint8 + +const ( + TrainRunLevel_CBTC TrainRunLevel = iota // cbtc级别 + TrainRunLevel_ITC // 点式通信 + TrainRunLevel_IL // 联锁级 +) + +// 列车运行模式 +type DriveMode uint8 + +const ( + DriveMode_AM DriveMode = iota // 列车自动驾驶模式 + DriveMode_CM // ATP防护下的人工驾驶模式 + DriveMode_RM // 限制人工驾驶模式 + DriveMode_NRM // 无限制人工驾驶模式 +) + +// 位置:目标位置、占用位置 +type DevicePosition struct { + Device *ecs.Entry // 设备 + Offset int64 // 在设备上的相对a位置偏移 + Direction bool // 从a到b为true;从b到a为false +} + +var DevicePositionType = ecs.NewComponentType[DevicePosition]() + +// 列车运行状态 +type TrainRunStatus struct { + RunLevel TrainRunLevel // 列车运行级别 + DriveMode DriveMode // 驾驶模式 + Speed float32 // 运行速度 + SpeedMax float32 // 最大速度 + HeadPosition *DevicePosition // 车头位置 + TailPosition *DevicePosition // 车尾位置 + Hold bool // 扣车 +} + +var TrainRunStatusType = ecs.NewComponentType[TrainRunStatus]() // 列车位置状态组件 + +var ( + TrainSpeedMax float32 = 80 / 3.6 +) diff --git a/component/iscs_bas_pipe.go b/component/iscs_bas_pipe.go index 59a83b6..b087c95 100644 --- a/component/iscs_bas_pipe.go +++ b/component/iscs_bas_pipe.go @@ -3,16 +3,22 @@ package component import "joylink.club/ecs" // FluidPipe 流体管线 -// -// 管线内流体流动条件:1、管线内有流体;2、管线两端有压差;3、管线畅通 type FluidPipe struct { - FlowVelocity int32 //流速,大于零从管线A端向B端流动,小于零从管线B端向A端流动,等于零没有流动 + Direction PipeFlowDirection //流动方向 } +// PipeFlowDirection 管线内流体流动方向定义 +type PipeFlowDirection int8 + +const ( + PipeFlowNon PipeFlowDirection = iota //流体未流动 + PipeFlowAb //流体从管线的A->B + PipeFlowBa //流体从管线的B->A +) + // FluidDriver 流体驱动器 type FluidDriver struct { - On bool //true-输出流体驱动力;false-未输出流体驱动力 - Forward bool //true-正转;false-反转 + On bool //true-输出流体驱动力;false-未输出流体驱动力 } var ( diff --git a/entity/iscs_bas.go b/entity/iscs_bas.go index 203248c..8633648 100644 --- a/entity/iscs_bas.go +++ b/entity/iscs_bas.go @@ -101,7 +101,7 @@ func newAirConditioningEntity(w ecs.World, id string, tag *ecs.ComponentType[str wd := GetWorldData(w) e, ok := wd.EntityMap[id] if !ok { - e = w.Entry(w.Create(component.UidType, component.MotorType, component.AirConditioningType, component.FluidDriverType, component.DeviceExceptionType, tag)) + e = w.Entry(w.Create(component.UidType, component.MotorType, component.AirConditioningType, component.DeviceExceptionType, tag)) component.UidType.SetValue(e, component.Uid{Id: id}) wd.EntityMap[id] = e } diff --git a/entity/iscs_load.go b/entity/iscs_load.go index 21f7982..7382cbb 100644 --- a/entity/iscs_load.go +++ b/entity/iscs_load.go @@ -84,7 +84,7 @@ func LoadIscs(w ecs.World) error { NewAirCurtainEntity(w, ac.Id()) } //气体环境 - for _, gasEnv := range data.Repo.GasEnvironmentMap { + for _, gasEnv := range data.Repo.EnvironmentMap { NewGasEnvironmentEntity(w, gasEnv.Id()) } //风机 diff --git a/proto/src/model.proto b/proto/src/model.proto index 6b95055..d457adc 100644 --- a/proto/src/model.proto +++ b/proto/src/model.proto @@ -27,6 +27,7 @@ message Repository { string mainCoordinateSystem = 20; repeated Key Keys = 21; repeated CentralizedStationRef CentralizedStationRefs = 22; + repeated Route routes = 23; //ISCS 编号[300,500] //ISCS管线 repeated Pipe pipes = 300; @@ -353,8 +354,8 @@ enum DeviceType { DeviceType_CombinationAirConditioner = 390; //ISCS 净化装置 DeviceType_AirPurificationDevice = 391; - //ISCS 气体环境(正常空气+有害烟雾) - DeviceType_GasEnvironment = 392; + //ISCS 环境(正常空气、有害烟雾、温度、湿度等) + DeviceType_Environment = 392; } @@ -501,10 +502,16 @@ message Mkx { } //站台 message Platform { + enum PlatformDirection { + Unknown = 0; + Up = 1; // 上行 + Down = 2; // 下行 + } string id = 1; string code = 2; string stationId = 3; string physicalSectionId = 4; + PlatformDirection direction = 5; } // 报警器 @@ -558,6 +565,29 @@ message CiSectionCodePoint{ string sectionId = 2;//物理区段id } +// 进路信息 +message Route { + enum RouteType { + RECEIVING = 0; + DEPARTURE = 1; + SHUNTING = 2; + PASS = 3; + LONG_SHUNTING = 4; + } + string id = 1; + string name = 2; + // 开始信号机 + string startId = 3; + // 目的信号机 + string destinationId = 4; + // 关联设备ID + repeated string deviceIds = 5; + // 敌对进路ID + repeated string conflictingRouteIds = 6; + // 进路类型 + RouteType routeType = 7; +} + //////////////////////////ISCS/////////////////////////////////// @@ -737,3 +767,4 @@ message GasEnvironment{ string id = 1; string code = 2; } + diff --git a/repository/iscs_bas_dxt.go b/repository/iscs_bas_dxt.go index 49ebf33..66d3b2c 100644 --- a/repository/iscs_bas_dxt.go +++ b/repository/iscs_bas_dxt.go @@ -45,8 +45,8 @@ type Valve struct { Identity Code string ValveType proto.Valve_Type //阀门子类型 - PortA DevicePort //阀门A端口连接的设备 - PortB DevicePort //阀门B端口连接的设备 + PortA *PipePort //阀门A端口连接的设备 + PortB *PipePort //阀门B端口连接的设备 } func NewValve(id string, code string, valveType proto.Valve_Type) *Valve { @@ -59,6 +59,16 @@ func NewValve(id string, code string, valveType proto.Valve_Type) *Valve { func (p *Valve) PortNum() int { return 2 } +func (p *Valve) Ports() []*PipePort { + var ports []*PipePort + if p.PortA != nil { + ports = append(ports, p.PortA) + } + if p.PortB != nil { + ports = append(ports, p.PortB) + } + return ports +} // ValvePort 阀门端口 // @@ -81,11 +91,9 @@ func (p *ValvePort) Device() PortedDevice { // 有四个端口,ABC三个端口为输入口,D端口为输出口 type GasMixingChamber struct { Identity - Code string - PortA DevicePort //A端口连接的设备 - PortB DevicePort //B端口连接的设备 - PortC DevicePort //C端口连接的设备 - PortD DevicePort //D端口连接的设备 + Code string + PortsA []*PipePort //A端多个输入口 + PortsB []*PipePort //B端多个输出口 } func NewGasMixingChamber(id string, code string) *GasMixingChamber { @@ -97,6 +105,14 @@ func NewGasMixingChamber(id string, code string) *GasMixingChamber { func (p *GasMixingChamber) PortNum() int { return 4 } +func (p *GasMixingChamber) IsInput(pipeId string) bool { + for _, portA := range p.PortsA { + if pipeId == portA.pipe.Id() { + return true + } + } + return false +} // GasMixingChamberPort 混合室静压箱端口 // @@ -121,10 +137,10 @@ func (p *GasMixingChamberPort) Device() PortedDevice { type CombinationAirConditioner struct { Identity Code string - PortA DevicePort //新风输入A端口连接的设备 - PortB DevicePort //工作风输出B端口连接的设备 - PortC DevicePort //C端口连接的设备 - PortD DevicePort //D端口连接的设备 + PortA *PipePort //新风输入A端口连接的设备 + PortB *PipePort //工作风输出B端口连接的设备 + PortC *PipePort //C端口连接的设备 + PortD *PipePort //D端口连接的设备 } func NewCombinationAirConditioner(id string, code string) *CombinationAirConditioner { @@ -136,25 +152,21 @@ func NewCombinationAirConditioner(id string, code string) *CombinationAirConditi func (p *CombinationAirConditioner) PortNum() int { return 4 } -func (p *CombinationAirConditioner) Outputs() []DevicePort { - var out []DevicePort +func (p *CombinationAirConditioner) Ports() []*PipePort { + var ports []*PipePort + if p.PortA != nil { + ports = append(ports, p.PortA) + } if p.PortB != nil { - out = append(out, p.PortB) + ports = append(ports, p.PortB) } if p.PortC != nil { - out = append(out, p.PortC) - } - return out -} -func (p *CombinationAirConditioner) Inputs() []DevicePort { - var in []DevicePort - if p.PortA != nil { - in = append(in, p.PortA) + ports = append(ports, p.PortC) } if p.PortD != nil { - in = append(in, p.PortD) + ports = append(ports, p.PortD) } - return in + return ports } // CombinationAirConditionerPort 组合式空调端口 @@ -179,8 +191,8 @@ func (p *CombinationAirConditionerPort) Device() PortedDevice { type AirPurificationDevice struct { Identity Code string - PortA DevicePort //A端口连接的设备 - PortB DevicePort //B端口连接的设备 + PortA *PipePort //A端口连接的设备 + PortB *PipePort //B端口连接的设备 } func NewAirPurificationDevice(id string, code string) *AirPurificationDevice { @@ -192,6 +204,16 @@ func NewAirPurificationDevice(id string, code string) *AirPurificationDevice { func (p *AirPurificationDevice) PortNum() int { return 2 } +func (p *AirPurificationDevice) Ports() []*PipePort { + var ports []*PipePort + if p.PortA != nil { + ports = append(ports, p.PortA) + } + if p.PortB != nil { + ports = append(ports, p.PortB) + } + return ports +} // AirPurificationDevicePort 净化装置端口 // @@ -231,8 +253,8 @@ type Fan struct { Code string FanType proto.Fan_Type *fanRunningModel - PortA DevicePort //A端口连接的设备,风机出风口即排风口,输出端口 - PortB DevicePort //B端口连接的设备,风机进风口,输入端口 + PortA *PipePort //A端口连接的设备,风机出风口即排风口,输出端口 + PortB *PipePort //B端口连接的设备,风机进风口,输入端口 } func NewFan(id string, code string, fanType proto.Fan_Type) *Fan { @@ -246,18 +268,6 @@ func NewFan(id string, code string, fanType proto.Fan_Type) *Fan { func (p *Fan) PortNum() int { return 2 } -func (p *Fan) Outputs() []DevicePort { - if p.PortA == nil { - return nil - } - return []DevicePort{p.PortA} -} -func (p *Fan) Inputs() []DevicePort { - if p.PortB == nil { - return nil - } - return []DevicePort{p.PortB} -} // FanPort 风机端口 // @@ -302,38 +312,37 @@ func newFanRunningModel(fanType proto.Fan_Type) *fanRunningModel { //////////////////////////////////////////////// -// GasEnvironment 气体环境(正常空气+有害烟雾) -// 有多个输入口,统一为端口A,用来为环境补充新鲜空气; -// 有多个输出口,统一为端口B,用户将环境的混浊气体排除; -// 排出气体动力源为该环境。 -type GasEnvironment struct { +// Environment 环境 +// 有多个输入口,统一为端口A +// 有多个输出口,统一为端口B +type Environment struct { Identity Code string PortsA []DevicePort // 有多个输入口,统一为端口A,用来为环境补充新鲜空气; PortsB []DevicePort // 有多个输出口,统一为端口B,用户将环境的混浊气体排除; } -func NewGasEnvironment(id string, code string) *GasEnvironment { - return &GasEnvironment{ - Identity: &identity{id: id, deviceType: proto.DeviceType_DeviceType_GasEnvironment}, +func NewEnvironment(id string, code string) *Environment { + return &Environment{ + Identity: &identity{id: id, deviceType: proto.DeviceType_DeviceType_Environment}, Code: code, } } -func (p *GasEnvironment) PortNum() int { +func (p *Environment) PortNum() int { return len(p.PortsA) + len(p.PortsB) } -// GasEnvironmentPort 气体环境端口 +// EnvironmentPort 气体环境端口 // // implement DevicePort -type GasEnvironmentPort struct { +type EnvironmentPort struct { port proto.Port - device *GasEnvironment + device *Environment } -func (p *GasEnvironmentPort) Port() proto.Port { +func (p *EnvironmentPort) Port() proto.Port { return p.port } -func (p *GasEnvironmentPort) Device() PortedDevice { +func (p *EnvironmentPort) Device() PortedDevice { return p.device } diff --git a/repository/iscs_pscada_yc.go b/repository/iscs_pscada_yc.go index 5ee3b60..0bdbbf1 100644 --- a/repository/iscs_pscada_yc.go +++ b/repository/iscs_pscada_yc.go @@ -1,6 +1,9 @@ package repository -import "joylink.club/rtsssimulation/repository/model/proto" +import ( + "fmt" + "joylink.club/rtsssimulation/repository/model/proto" +) //电力监控系统车站一次图中相关设备模型 @@ -20,6 +23,18 @@ func (p *Pipe) PortNum() int { return 2 } +// PortDevice 获取管线对应端口连接的设备 +func (p *Pipe) PortDevice(port proto.Port) DevicePort { + switch port { + case proto.Port_A: + return p.PortA + case proto.Port_B: + return p.PortB + default: + panic("管线端口只有A或B") + } +} + // PipePort 管线端口 // // implement DevicePort @@ -34,6 +49,21 @@ func (p *PipePort) Port() proto.Port { func (p *PipePort) Device() PortedDevice { return p.pipe } +func (p *PipePort) PipePortId() string { + return fmt.Sprintf("%s-%d", p.pipe.Id(), p.port) +} + +// ToOtherPort 获取当前管线端口所在管线的另一端 +func (p *PipePort) ToOtherPort() PipePort { + switch p.port { + case proto.Port_A: + return PipePort{port: proto.Port_B, pipe: p.pipe} + case proto.Port_B: + return PipePort{port: proto.Port_A, pipe: p.pipe} + default: + panic("管线的端口只能为A或B") + } +} /////////////////////////////////////////////////// diff --git a/repository/model/proto/model.pb.go b/repository/model/proto/model.pb.go index 8d6a206..d8bc73e 100644 --- a/repository/model/proto/model.pb.go +++ b/repository/model/proto/model.pb.go @@ -185,8 +185,8 @@ const ( DeviceType_DeviceType_CombinationAirConditioner DeviceType = 390 // ISCS 净化装置 DeviceType_DeviceType_AirPurificationDevice DeviceType = 391 - // ISCS 气体环境(正常空气+有害烟雾) - DeviceType_DeviceType_GasEnvironment DeviceType = 392 + // ISCS 环境(正常空气、有害烟雾、温度、湿度等) + DeviceType_DeviceType_Environment DeviceType = 392 ) // Enum value maps for DeviceType. @@ -283,7 +283,7 @@ var ( 389: "DeviceType_GasMixingChamber", 390: "DeviceType_CombinationAirConditioner", 391: "DeviceType_AirPurificationDevice", - 392: "DeviceType_GasEnvironment", + 392: "DeviceType_Environment", } DeviceType_value = map[string]int32{ "DeviceType_Unknown": 0, @@ -377,7 +377,7 @@ var ( "DeviceType_GasMixingChamber": 389, "DeviceType_CombinationAirConditioner": 390, "DeviceType_AirPurificationDevice": 391, - "DeviceType_GasEnvironment": 392, + "DeviceType_Environment": 392, } ) @@ -5570,7 +5570,7 @@ var file_model_proto_rawDesc = []byte{ 0x0a, 0x0e, 0x47, 0x61, 0x73, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x63, 0x6f, 0x64, 0x65, 0x2a, 0xee, 0x15, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, + 0x63, 0x6f, 0x64, 0x65, 0x2a, 0xeb, 0x15, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, @@ -5743,20 +5743,20 @@ var file_model_proto_rawDesc = []byte{ 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x10, 0x86, 0x03, 0x12, 0x25, 0x0a, 0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x41, 0x69, 0x72, 0x50, 0x75, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x10, 0x87, 0x03, 0x12, 0x1e, 0x0a, 0x19, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, - 0x79, 0x70, 0x65, 0x5f, 0x47, 0x61, 0x73, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, - 0x6e, 0x74, 0x10, 0x88, 0x03, 0x2a, 0x2c, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x08, 0x0a, - 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x05, 0x0a, 0x01, 0x41, 0x10, 0x01, 0x12, 0x05, - 0x0a, 0x01, 0x42, 0x10, 0x02, 0x12, 0x05, 0x0a, 0x01, 0x43, 0x10, 0x03, 0x12, 0x05, 0x0a, 0x01, - 0x44, 0x10, 0x04, 0x2a, 0x20, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x45, 0x46, 0x54, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x49, - 0x47, 0x48, 0x54, 0x10, 0x01, 0x2a, 0x43, 0x0a, 0x0e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x50, 0x6f, - 0x69, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x42, 0x6f, 0x75, 0x6e, 0x64, - 0x61, 0x72, 0x79, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x78, 0x6c, 0x65, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x6e, 0x73, 0x75, 0x6c, 0x61, - 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x69, 0x6e, 0x74, 0x10, 0x02, 0x42, 0x1a, 0x5a, 0x18, 0x2e, 0x2f, - 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x65, 0x10, 0x87, 0x03, 0x12, 0x1b, 0x0a, 0x16, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x5f, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x10, + 0x88, 0x03, 0x2a, 0x2c, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, + 0x6e, 0x65, 0x10, 0x00, 0x12, 0x05, 0x0a, 0x01, 0x41, 0x10, 0x01, 0x12, 0x05, 0x0a, 0x01, 0x42, + 0x10, 0x02, 0x12, 0x05, 0x0a, 0x01, 0x43, 0x10, 0x03, 0x12, 0x05, 0x0a, 0x01, 0x44, 0x10, 0x04, + 0x2a, 0x20, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x08, 0x0a, + 0x04, 0x4c, 0x45, 0x46, 0x54, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x49, 0x47, 0x48, 0x54, + 0x10, 0x01, 0x2a, 0x43, 0x0a, 0x0e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x50, 0x6f, 0x69, 0x6e, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, + 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x78, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x6e, 0x73, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x64, + 0x4a, 0x6f, 0x69, 0x6e, 0x74, 0x10, 0x02, 0x42, 0x1a, 0x5a, 0x18, 0x2e, 0x2f, 0x72, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/repository/repository.go b/repository/repository.go index 68918de..eddf03a 100644 --- a/repository/repository.go +++ b/repository/repository.go @@ -51,7 +51,7 @@ type Repository struct { AirPurificationDeviceMap map[string]*AirPurificationDevice //ISCS净化装置 AirCurtainMap map[string]*AirCurtain //ISCS空气幕 FanMap map[string]*Fan //ISCS风机 - GasEnvironmentMap map[string]*GasEnvironment //ISCS气体环境 + EnvironmentMap map[string]*Environment //ISCS气体环境 } func newRepository(id string, version string) *Repository { @@ -98,7 +98,7 @@ func newRepository(id string, version string) *Repository { AirPurificationDeviceMap: make(map[string]*AirPurificationDevice), //ISCS净化装置 AirCurtainMap: make(map[string]*AirCurtain), //ISCS空气幕 FanMap: make(map[string]*Fan), //ISCS风机 - GasEnvironmentMap: make(map[string]*GasEnvironment), //ISCS气体环境 + EnvironmentMap: make(map[string]*Environment), //ISCS气体环境 } } @@ -189,7 +189,7 @@ func (repo *Repository) FindById(id string) Identity { if md, ok := repo.FanMap[id]; ok { return md } - if md, ok := repo.GasEnvironmentMap[id]; ok { + if md, ok := repo.EnvironmentMap[id]; ok { return md } //ISCS-end @@ -385,8 +385,8 @@ func (repo *Repository) FindModel(deviceId string, deviceType proto.DeviceType) return repo.AirCurtainMap[deviceId], nil case proto.DeviceType_DeviceType_Fan: return repo.FanMap[deviceId], nil - case proto.DeviceType_DeviceType_GasEnvironment: - return repo.GasEnvironmentMap[deviceId], nil + case proto.DeviceType_DeviceType_Environment: + return repo.EnvironmentMap[deviceId], nil default: return nil, fmt.Errorf("仓库中不存在[%s]类型的模型", deviceType) } diff --git a/repository/repository_iscs.go b/repository/repository_iscs.go index a96b8cc..d7c1f3b 100644 --- a/repository/repository_iscs.go +++ b/repository/repository_iscs.go @@ -104,10 +104,10 @@ func buildIscsModels(source *proto.Repository, repository *Repository) error { m := NewFan(protoData.Id, protoData.Code, protoData.FanType) repository.FanMap[m.Id()] = m } - //ISCS气体环境 + //ISCS环境 for _, protoData := range source.GasEnvironments { - m := NewGasEnvironment(protoData.Id, protoData.Code) - repository.GasEnvironmentMap[m.Id()] = m + m := NewEnvironment(protoData.Id, protoData.Code) + repository.EnvironmentMap[m.Id()] = m } // return nil @@ -160,13 +160,9 @@ func buildIscsPipeModelRelationship(source *proto.Repository, repository *Reposi pipePortRelatedDevice = &GasMixingChamberPort{port: portRelatedDevice.Port, device: gasMixingChamber} switch portRelatedDevice.Port { case proto.Port_A: - gasMixingChamber.PortA = &PipePort{port: pipePort, pipe: pipeModel} + gasMixingChamber.PortsA = append(gasMixingChamber.PortsA, &PipePort{port: pipePort, pipe: pipeModel}) case proto.Port_B: - gasMixingChamber.PortB = &PipePort{port: pipePort, pipe: pipeModel} - case proto.Port_C: - gasMixingChamber.PortC = &PipePort{port: pipePort, pipe: pipeModel} - case proto.Port_D: - gasMixingChamber.PortD = &PipePort{port: pipePort, pipe: pipeModel} + gasMixingChamber.PortsB = append(gasMixingChamber.PortsB, &PipePort{port: pipePort, pipe: pipeModel}) } } case proto.DeviceType_DeviceType_CombinationAirConditioner: @@ -206,10 +202,10 @@ func buildIscsPipeModelRelationship(source *proto.Repository, repository *Reposi fan.PortB = &PipePort{port: pipePort, pipe: pipeModel} } } - case proto.DeviceType_DeviceType_GasEnvironment: + case proto.DeviceType_DeviceType_Environment: { - gasEnvironment := relatedDevice.(*GasEnvironment) - pipePortRelatedDevice = &GasEnvironmentPort{port: portRelatedDevice.Port, device: gasEnvironment} + gasEnvironment := relatedDevice.(*Environment) + pipePortRelatedDevice = &EnvironmentPort{port: portRelatedDevice.Port, device: gasEnvironment} switch portRelatedDevice.Port { case proto.Port_A: gasEnvironment.PortsA = append(gasEnvironment.PortsA, &PipePort{port: pipePort, pipe: pipeModel}) diff --git a/sys/cbtc_sys/train_run_control.go b/sys/cbtc_sys/train_run_control.go new file mode 100644 index 0000000..b4d3322 --- /dev/null +++ b/sys/cbtc_sys/train_run_control.go @@ -0,0 +1,33 @@ +package cbtc_sys + +import ( + "github.com/yohamta/donburi" + "joylink.club/ecs" + "joylink.club/ecs/filter" + "joylink.club/rtsssimulation/component" +) + +// 列车运行控制组件,给列车增加限制组件 +type TrainRunControlSys struct { + trainQuery *ecs.Query +} + +func NewTrainRunControlSys() *TrainRunControlSys { + return &TrainRunControlSys{ + trainQuery: ecs.NewQuery(filter.Contains(component.TrainRunStatusType)), + } +} + +func (sls *TrainRunControlSys) Update(w ecs.World) { + sls.trainQuery.Each(w, func(e *donburi.Entry) { + runStatus := component.TrainRunStatusType.Get(e) + headDevice := runStatus.HeadPosition.Device + // 限速赋值 + if headDevice.HasComponent(component.SpeedLimitType) { // 车头所在设备有限速组件 + speedLimit := component.SpeedLimitType.Get(headDevice) + runStatus.SpeedMax = speedLimit.Val + } else { + runStatus.SpeedMax = component.TrainSpeedMax + } + }) +} diff --git a/sys/iscs_sys/iscs_bas_air_conditioner.go b/sys/iscs_sys/iscs_bas_air_conditioner.go index 8624317..1978bfa 100644 --- a/sys/iscs_sys/iscs_bas_air_conditioner.go +++ b/sys/iscs_sys/iscs_bas_air_conditioner.go @@ -13,17 +13,14 @@ type AirConditionerSystem struct { func NewAirConditionerSystem() *AirConditionerSystem { return &AirConditionerSystem{ - query: ecs.NewQuery(filter.Contains(component.UidType, component.MotorType, component.AirConditioningType, component.FluidDriverType)), + query: ecs.NewQuery(filter.Contains(component.UidType, component.MotorType, component.AirConditioningType)), } } func (s *AirConditionerSystem) Update(w ecs.World) { s.query.Each(w, func(entry *ecs.Entry) { motor := component.MotorType.Get(entry) - fluidDriver := component.FluidDriverType.Get(entry) air := component.AirConditioningType.Get(entry) // air.Running = motor.Speed > 0 - fluidDriver.On = air.Running - fluidDriver.Forward = motor.Forward }) } diff --git a/sys/iscs_sys/iscs_bas_fan.go b/sys/iscs_sys/iscs_bas_fan.go index 5add54d..ab5adba 100644 --- a/sys/iscs_sys/iscs_bas_fan.go +++ b/sys/iscs_sys/iscs_bas_fan.go @@ -50,6 +50,5 @@ func (s *FanSystem) Update(w ecs.World) { } // fluidDriver.On = fan.Speed > 0 - fluidDriver.Forward = fan.Forward }) } diff --git a/sys/iscs_sys/iscs_bas_fluid.go b/sys/iscs_sys/iscs_bas_fluid.go index ac87051..e8c0b4f 100644 --- a/sys/iscs_sys/iscs_bas_fluid.go +++ b/sys/iscs_sys/iscs_bas_fluid.go @@ -8,12 +8,15 @@ import ( "joylink.club/rtsssimulation/entity" "joylink.club/rtsssimulation/repository" "joylink.club/rtsssimulation/repository/model/proto" + "log/slog" ) // FluidDriverSystem 流体驱动系统 // 实现流体在设备、管线中流动 +// 流体驱动源(风机、送风亭、泵) type FluidDriverSystem struct { - query *ecs.Query + query *ecs.Query //流体驱动源 + drivePathMap map[string][]*SearchedPath //key pipePortId,value 驱动源驱动流体的路径 } func NewFluidDriverSystem() *FluidDriverSystem { @@ -22,30 +25,275 @@ func NewFluidDriverSystem() *FluidDriverSystem { } } -// 流体驱动系统依赖的数据 -type fluidDriverModel interface { - Outputs() []repository.DevicePort - Inputs() []repository.DevicePort +// 获取流体流动路径 +func (s *FluidDriverSystem) findFluidPath(fromDevice repository.Identity, fromDevicePortPipe repository.PipePort, fromIsStart bool) []*SearchedPath { + pipePortId := fromDevicePortPipe.PipePortId() + sp, ok := s.drivePathMap[pipePortId] + if !ok { + sp = newFluidDriverPathSearcher(fromDevice, fromDevicePortPipe, fromIsStart).search() + s.drivePathMap[pipePortId] = sp + } + return sp } - func (s *FluidDriverSystem) Update(w ecs.World) { wd := entity.GetWorldData(w) s.query.Each(w, func(entry *ecs.Entry) { fdId := component.UidType.Get(entry).Id fd := component.FluidDriverType.Get(entry) - fdModel, fdModelOk := wd.Repo.FindById(fdId).(fluidDriverModel) - if !fdModelOk { - fmt.Printf("==>>FluidDriverSystem.Update FluidDriver[%s]未实现接口fluidDriverModel\n", fdId) + fluidDriverOn := fd.On + fdModel := wd.Repo.FindById(fdId) + if fdModel == nil { + fmt.Printf("==>>FluidDriver[%s]模型不存在\n", fdId) } else { - s.fluidDrive(entry, fd, fdModel) + switch fdModel.Type() { + case proto.DeviceType_DeviceType_Fan: //风机 + { + fanModel := fdModel.(*repository.Fan) + fanOutPort := *fanModel.PortA + fanInPort := *fanModel.PortB + fanOutPath := s.findFluidPath(fanModel, fanOutPort, true) + fanInPath := s.findFluidPath(fanModel, fanInPort, false) + // + _ = fanOutPath + _ = fanInPath + _ = fluidDriverOn + } + case proto.DeviceType_DeviceType_AirPavilion: //风亭,其中送风亭为动力源 + + } } }) } -func (s *FluidDriverSystem) fluidDrive(entry *ecs.Entry, fd *component.FluidDriver, fdModel fluidDriverModel) { - for _, out := range fdModel.Outputs() { - switch out.Device().Type() { - case proto.DeviceType_DeviceType_Pipe: +// 计算流体在管线中的流动参数 +func (s *FluidDriverSystem) calculateFluid(fluidDriverOn bool, outPath []*SearchedPath, inPath []*SearchedPath) { + +} + +// 判断路径是否畅通 +func (s *FluidDriverSystem) isFluidPathUnblocked(fp *SearchedPath, w ecs.World) bool { + for i := 0; i < len(fp.ViaPipes); i++ { + viaPipe := fp.ViaPipes[i] + viaDevice := viaPipe.Device().(*repository.Pipe).PortDevice(viaPipe.Port()) + switch viaDevice.Device().Type() { + case proto.DeviceType_DeviceType_Environment: //默认畅通 + case proto.DeviceType_DeviceType_PipeFitting: //默认畅通 + case proto.DeviceType_DeviceType_AirPurificationDevice: //默认畅通 + case proto.DeviceType_DeviceType_GasMixingChamber: //默认畅通 + case proto.DeviceType_DeviceType_Valve: + if s.getValveOpenRate(w, viaDevice.Device().Id()) <= 0 { //阀门未开,路径阻塞 + return false + } + case proto.DeviceType_DeviceType_CombinationAirConditioner: + if !s.isCombinationAirConditionerRunning(w, viaDevice.Device().Id()) { //组合式空调未开,路径阻塞 + return false + } + } + } + return true +} + +// 组合式空调是否在运行 +func (s *FluidDriverSystem) isCombinationAirConditionerRunning(w ecs.World, id string) bool { + wd := entity.GetWorldData(w) + entry, ok := wd.EntityMap[id] + if ok { + air := component.AirConditioningType.Get(entry) + return air.Running + } else { + slog.Warn(fmt.Sprintf("World中未找到组合式空调[%s]的实体", id)) + } + + return false +} + +// 获取阀门开度 +func (s *FluidDriverSystem) getValveOpenRate(w ecs.World, valveId string) uint8 { + wd := entity.GetWorldData(w) + entry, ok := wd.EntityMap[valveId] + if ok { + return component.ValveType.Get(entry).OpenRate + } else { + slog.Warn(fmt.Sprintf("World中未找到阀门[%s]的实体", valveId)) + } + return 0 +} + +//////////////////////////流体路径相关///////////////////////////// + +type SearchedPath struct { + ViaPipes []repository.PipePort //路径所经过的有向管线 +} +type fluidDriverPathSearcher struct { + fromDevice repository.Identity //起点设备 + fromDevicePortPipe repository.PipePort //从起点设备的某个端口连接的管线的某个端口开始 + fromIsStart bool //true-以设备输出口为起点;false-以设备输入口为起点 + searchedPaths []*fluidPath //搜索到的路径 + excludePipeIds []string //排除经过这些管线的路径 +} + +func newFluidDriverPathSearcher(fromDevice repository.Identity, fromDevicePortPipe repository.PipePort, fromIsStart bool) *fluidDriverPathSearcher { + return &fluidDriverPathSearcher{fromDevice: fromDevice, fromDevicePortPipe: fromDevicePortPipe, fromIsStart: fromIsStart} +} + +func (s *fluidDriverPathSearcher) search() []*SearchedPath { + var rt []*SearchedPath + if !s.exclude(s.fromDevicePortPipe.Device().Id()) { + searchContext := &fluidPath{} + searchContext.addPipe(s.fromDevicePortPipe) + s.doSearch(searchContext) + } else { + return rt + } + // + for _, fp := range s.searchedPaths { + fpLen := len(fp.viaPipes) + path := &SearchedPath{} + if !s.fromIsStart { //以输入口为起点,须反转搜索结果 + for i := fpLen - 1; i >= 0; i-- { + viaPipe := fp.viaPipes[i] + path.ViaPipes = append(path.ViaPipes, viaPipe.ToOtherPort()) + } + } else { + for _, viaPipe := range fp.viaPipes { + path.ViaPipes = append(path.ViaPipes, viaPipe) + } + } + rt = append(rt, path) + } + + // + return rt +} + +func (s *fluidDriverPathSearcher) exclude(pipeId string) bool { + for _, excludePipeId := range s.excludePipeIds { + if excludePipeId == pipeId { + return true + } + } + return false +} +func (s *fluidDriverPathSearcher) addSearchedPath(searchContext *fluidPath) { + s.searchedPaths = append(s.searchedPaths, searchContext) +} + +// 递归搜索 +func (s *fluidDriverPathSearcher) doSearch(searchContext *fluidPath) { + nextViaPipes := searchContext.nextViaPathPipes() + lenNextPipes := len(nextViaPipes) + if lenNextPipes == 0 { + s.addSearchedPath(searchContext) + } else if lenNextPipes == 1 { + if !searchContext.viaPipe(nextViaPipes[0].Device().Id()) && !s.exclude(nextViaPipes[0].Device().Id()) { + searchContext.addPipe(nextViaPipes[0]) + s.doSearch(searchContext) + } + } else { + for _, nextViaPipe := range nextViaPipes { + if searchContext.viaPipe(nextViaPipe.Device().Id()) { //舍弃回头路径 + continue + } + if s.exclude(nextViaPipe.Device().Id()) { //舍弃排除路径 + continue + } + // + cp := searchContext.clone() + cp.addPipe(nextViaPipe) + s.doSearch(cp) } } } + +func (p *fluidPath) nextViaPathPipes() []repository.PipePort { + nextDevice := p.nextDevice() + switch nextDevice.Type() { + case proto.DeviceType_DeviceType_PipeFitting: + return p.nextDeviceViaPipes(nextDevice.(*repository.PipeFitting).Ports(), p.curPipe) + case proto.DeviceType_DeviceType_Valve: + return p.nextDeviceViaPipes(nextDevice.(*repository.Valve).Ports(), p.curPipe) + case proto.DeviceType_DeviceType_GasMixingChamber: + return p.nextGasMixingChamberViaPipes(nextDevice.(*repository.GasMixingChamber), p.curPipe) + case proto.DeviceType_DeviceType_CombinationAirConditioner: + return p.nextCombinationAirConditionerViaPipes(nextDevice.(*repository.CombinationAirConditioner), p.curPipe) + case proto.DeviceType_DeviceType_AirPurificationDevice: + return p.nextDeviceViaPipes(nextDevice.(*repository.AirPurificationDevice).Ports(), p.curPipe) + default: + slog.Warn("管线路径中的设备[%d]无法处理", nextDevice.Type()) + } + return nil +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +func (p *fluidPath) nextDeviceViaPipes(deviceAllPipePorts []*repository.PipePort, preViaPipe repository.PipePort) []repository.PipePort { + var rt []repository.PipePort + findPreViaPipe := false + for _, pfPort := range deviceAllPipePorts { + if preViaPipe.Device().Id() != pfPort.Device().Id() { + rt = append(rt, *pfPort) + } else { + findPreViaPipe = true + } + } + if !findPreViaPipe { //校验数据 + panic("preViaPipe没有与设备连接") + } + return rt +} + +// 组合式空调 +func (p *fluidPath) nextCombinationAirConditionerViaPipes(nextDevice *repository.CombinationAirConditioner, preViaPipe repository.PipePort) []repository.PipePort { + var rt []repository.PipePort + if preViaPipe.Device().Id() == nextDevice.PortA.Device().Id() { //从组合式空调的A口进入 + rt = append(rt, *nextDevice.PortB) + if nextDevice.PortC != nil { + rt = append(rt, *nextDevice.PortC) + } + return rt + } else if preViaPipe.Device().Id() == nextDevice.PortD.Device().Id() { //从组合式空调的D口进入 + return rt + } + return rt +} + +// 静压箱 +func (p *fluidPath) nextGasMixingChamberViaPipes(nextDevice *repository.GasMixingChamber, preViaPipe repository.PipePort) []repository.PipePort { + var rt []repository.PipePort + if nextDevice.IsInput(preViaPipe.Device().Id()) { + for _, out := range nextDevice.PortsB { + rt = append(rt, *out) + } + } + return rt +} + +type fluidPath struct { + curPipe repository.PipePort //路径中当前有向管线 + viaPipes []repository.PipePort //路径所经过的有向管线 +} + +func (p *fluidPath) addPipe(viaPipe repository.PipePort) { + p.viaPipes = append(p.viaPipes, viaPipe) + p.curPipe = viaPipe +} +func (p *fluidPath) viaPipe(pipeId string) bool { + for _, vp := range p.viaPipes { + if vp.Device().Id() == pipeId { + return true + } + } + return false +} +func (p *fluidPath) clone() *fluidPath { + cp := &fluidPath{viaPipes: make([]repository.PipePort, 0, len(p.viaPipes)), curPipe: p.curPipe} + copy(cp.viaPipes, p.viaPipes) + return cp +} +func (p *fluidPath) nextDevice() repository.Identity { + if p.curPipe.Port() == proto.Port_A { + return p.curPipe.Device().(*repository.Pipe).PortB.Device() + } else { + return p.curPipe.Device().(*repository.Pipe).PortA.Device() + } +} diff --git a/sys/iscs_sys/iscs_bas_valve.go b/sys/iscs_sys/iscs_bas_valve.go index c288be4..1d0ad86 100644 --- a/sys/iscs_sys/iscs_bas_valve.go +++ b/sys/iscs_sys/iscs_bas_valve.go @@ -20,6 +20,7 @@ func NewValveSystem() *ValveSystem { query: ecs.NewQuery(filter.Contains(component.UidType, component.ValveType, component.ValveControllerType, component.FixedPositionTransformType)), } } + func (s *ValveSystem) Update(w ecs.World) { wd := entity.GetWorldData(w) s.query.Each(w, func(entry *ecs.Entry) {