rts-sim-module/sys/iscs_sys/iscs_bas_fluid.go
2024-01-09 09:39:03 +08:00

294 lines
9.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package iscs_sys
import (
"fmt"
"joylink.club/ecs"
"joylink.club/ecs/filter"
"joylink.club/rtsssimulation/component"
"joylink.club/rtsssimulation/entity"
"joylink.club/rtsssimulation/repository"
"joylink.club/rtsssimulation/repository/model/proto"
"log/slog"
)
// FluidDriverSystem 流体驱动系统
// 实现流体在设备、管线中流动
// 流体驱动源(风机、泵、组合式空调)
type FluidDriverSystem struct {
queryFluidDriver *ecs.Query //流体驱动源
drivePathMap map[string][]*SearchedPath //key pipePortId,value 驱动源驱动流体的路径
queryFluidPipe *ecs.Query
}
func NewFluidDriverSystem() *FluidDriverSystem {
return &FluidDriverSystem{
queryFluidDriver: ecs.NewQuery(filter.Contains(component.UidType, component.FluidDriverType)),
queryFluidPipe: ecs.NewQuery(filter.Contains(component.UidType, component.PipeFluidType)),
}
}
// 获取流体流动路径
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) {
s.queryFluidDriver.Each(w, func(entry *ecs.Entry) {
//fdId := component.UidType.Get(entry).Id
})
}
// 判断路径是否畅通
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{viaPipeFittings: make(map[string]string)}
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, end := searchContext.nextViaPathPipes()
lenNextPipes := len(nextViaPipes)
if end {
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)
}
}
}
}
// 从当前管线找下一个管线
// 泵、风机、空调 提供驱动力,承接进出
// 返回bool : true-搜索达终点
func (p *fluidPath) nextViaPathPipes() ([]repository.PipePort, bool) {
nextDevice := p.nextDevice()
//
switch nextDevice.Type() {
case proto.DeviceType_DeviceType_PipeFitting:
{
if _, has := p.viaPipeFittings[nextDevice.Id()]; has { //排除已经经过的管件的路径
return nil, false
} else {
p.viaPipeFittings[nextDevice.Id()] = nextDevice.Id()
return p.nextDeviceViaPipes(nextDevice.(*repository.PipeFitting).Ports(), p.curPipe), false
}
}
case proto.DeviceType_DeviceType_Valve:
return p.nextDeviceViaPipes(nextDevice.(*repository.Valve).Ports(), p.curPipe), false
case proto.DeviceType_DeviceType_GasMixingChamber: //静压箱
return p.nextGasMixingChamberViaPipes(nextDevice.(*repository.GasMixingChamber), p.curPipe), false
case proto.DeviceType_DeviceType_AirPurificationDevice: //净化装置
return p.nextDeviceViaPipes(nextDevice.(*repository.AirPurificationDevice).Ports(), p.curPipe), false
case proto.DeviceType_DeviceType_Environment: //环境
fallthrough
case proto.DeviceType_DeviceType_AirPavilion: //风亭
fallthrough
case proto.DeviceType_DeviceType_CombinationAirConditioner: //组合式空调
return nil, true //路径搜索终点
default:
slog.Warn("管线路径中的设备[%d]无法处理", nextDevice.Type())
}
return nil, false //排除(舍弃)该路径搜索
}
///////////////////////////////////////////////////////////////////////////////////////////
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 //路径所经过的有向管线
viaPipeFittings map[string]string //经过的管件用于排除闭环路径key和value均为deviceId
}
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, viaPipeFittings: make(map[string]string)}
copy(cp.viaPipes, p.viaPipes)
for k, v := range p.viaPipeFittings {
cp.viaPipeFittings[k] = v
}
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()
}
}