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 { query *ecs.Query //流体驱动源 drivePathMap map[string][]*SearchedPath //key pipePortId,value 驱动源驱动流体的路径 } func NewFluidDriverSystem() *FluidDriverSystem { return &FluidDriverSystem{ query: ecs.NewQuery(filter.Contains(component.UidType, component.FluidDriverType)), } } // 获取流体流动路径 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) fluidDriverOn := fd.On fdModel := wd.Repo.FindById(fdId) if fdModel == nil { fmt.Printf("==>>FluidDriver[%s]模型不存在\n", fdId) } else { 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) 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() } }