From 723b27298b4a80d880c96728d7d7dd8dafe5b24d Mon Sep 17 00:00:00 2001 From: annabunches Date: Sun, 9 Feb 2025 21:51:47 -0500 Subject: [PATCH] Refactor airlock for fewer lookups and in preparation for using a sequencer. --- Airlock/Airlock.cs | 612 +++++++++++++++++++++++------------------ Airlock/Airlock.csproj | 2 +- Airlock/Program.cs | 33 ++- 3 files changed, 363 insertions(+), 284 deletions(-) diff --git a/Airlock/Airlock.cs b/Airlock/Airlock.cs index 0be449d..baad440 100644 --- a/Airlock/Airlock.cs +++ b/Airlock/Airlock.cs @@ -5,307 +5,383 @@ using VRageMath; namespace IngameScript { - public class Airlock + partial class Program { - enum AirlockLightState + public class Airlock { - Off, - Cycling, - Cooldown, - Error, - } - - public bool DoorsClosed - { - get + enum AirlockLightState { - return innerDoor.Status == DoorStatus.Closed && outerDoor.Status == DoorStatus.Closed; + Off, + Cycling, + Cooldown, + Error, } - } - // TODO: this should check for depressurizing to the *reference* amount - public bool PressureStabilized - { - get + public bool DoorsClosed { - return ((airVent.Depressurize && airVent.GetOxygenLevel() <= targetOxygenLevel) || - (!airVent.Depressurize && airVent.Status == VentStatus.Pressurized)); - } - } - - public bool OxygenBalanced - { - get { return oxygenTank.FilledRatio > 0.25; } - } - - public bool Functional { get; private set; } = true; - - public bool Cycling { get; private set; } = false; - - private bool DoorOpened - { - get - { - return innerDoor.Status == DoorStatus.Open || outerDoor.Status == DoorStatus.Open; - } - } - - // Returns false if we are in a state where we can't or don't need to balance - private bool balanceOxygen() - { - if (innerDoor.Status == DoorStatus.Closed || outerDoor.Status == DoorStatus.Open || OxygenBalanced) { return false; } - - _console.Print($"{_name}: Balancing Oxygen Tank"); - - // Configure the vent to suck in Oxygen. - airVent.Depressurize = true; - airVent.Enabled = true; - return true; - } - - private string _name; - private PrefixedConsole _console; - private MyGridProgram _p; - private float targetOxygenLevel = 0.0F; - - private IMyDoor innerDoor; - private IMyDoor outerDoor; - private List lights; - private IMyGasTank oxygenTank; - private IMyAirVent airVent; - private IMyAirVent airSensor; - private const int CooldownTicks = 120; - - public Airlock(MyGridProgram program, Console console, string name) - { - _p = program; - _name = name; - _console = console.CreatePrefixedConsole(_name); - - // Find the appropriate blocks given the airlock name - initDoors(); - initLights(); - initVents(); - initOxygen(); - } - - private void initDoors() - { - List onlyDoors = new List(); - _p.GridTerminalSystem.GetBlocksOfType(onlyDoors); - foreach (IMyDoor door in onlyDoors) - { - if (!door.CustomName.StartsWith(_name)) continue; - - if (door.CustomName.Contains("Inner")) + get { - innerDoor = door; - } - else if (door.CustomName.Contains("Outer")) - { - outerDoor = door; + return innerDoor.Status == DoorStatus.Closed && outerDoor.Status == DoorStatus.Closed; } + } - if (innerDoor != null && outerDoor != null) + // TODO: this should check for depressurizing to the *reference* amount + public bool PressureStabilized + { + get { + return ((airVent.Depressurize && airVent.GetOxygenLevel() <= targetOxygenLevel) || + (!airVent.Depressurize && airVent.Status == VentStatus.Pressurized)); + } + } + + public bool OxygenBalanced + { + get { return oxygenTank.FilledRatio > 0.25; } + } + + public bool Functional + { + get + { + return (innerDoor != null && outerDoor != null && airVent != null && oxygenTank != null); + } + } + + public bool Cycling { get; private set; } = false; + + private bool DoorOpened + { + get + { + return innerDoor.Status == DoorStatus.Open || outerDoor.Status == DoorStatus.Open; + } + } + + // Returns false if we are in a state where we can't or don't need to balance + private bool balanceOxygen() + { + if (innerDoor.Status == DoorStatus.Closed || outerDoor.Status == DoorStatus.Open || OxygenBalanced) { return false; } + + _console.Print($"{_name}: Balancing Oxygen Tank"); + + // Configure the vent to suck in Oxygen. + airVent.Depressurize = true; + airVent.Enabled = true; + return true; + } + + private string _name; + private PrefixedConsole _console; + private MyGridProgram _p; + private float targetOxygenLevel = 0.0F; + + private IMyDoor innerDoor; + private IMyDoor outerDoor; + private List lights; + private IMyGasTank oxygenTank; + private IMyAirVent airVent; + private IMyAirVent airSensor; + private const int CooldownTicks = 120; + + public Airlock(MyGridProgram program, Console console, string name) + { + _p = program; + _name = name; + _console = console.CreatePrefixedConsole(_name); + lights = new List(); + + // Find the appropriate blocks given the airlock name + // initDoors(); + // initLights(); + // initVents(); + // initOxygen(); + } + + public void AddBlock(IMyTerminalBlock block) + { + if (block is IMyDoor) + { + addDoor(block as IMyDoor); return; } - } - - Functional = false; - } - - private void initVents() - { - List onlyFans = new List(); - _p.GridTerminalSystem.GetBlocksOfType(onlyFans); - foreach (IMyAirVent vent in onlyFans) - { - if (!vent.CustomName.StartsWith(_name)) continue; - - if (vent.CustomName.StartsWith(_name)) + if (block is IMyLightingBlock) { - if (vent.CustomName.Contains("Main")) - { - airVent = vent; - continue; - } - else if (vent.CustomName.Contains("Reference")) - { - airSensor = vent; - continue; - } + lights.Add(block as IMyLightingBlock); + return; + } + if (block is IMyAirVent) + { + addVent(block as IMyAirVent); + return; + } + if (block is IMyGasTank) + { + addOxygenTank(block as IMyGasTank); + return; } - // A global reference vent will be used if we don't have one specific to our airlock. - // A specific vent found later will overwrite this assignment. - if (vent.CustomName.StartsWith("Airlock Reference") && airSensor == null) + _console.Print($"Tried to add invalid block '{block.CustomName}'"); + } + + private void addDoor(IMyDoor door) + { + if (door.CustomName.Contains("Inner") && innerDoor == null) + { + innerDoor = door; + return; + } + if (door.CustomName.Contains("Outer") && outerDoor == null) + { + outerDoor = door; + return; + } + _console.Print($"Couldn't add door '{door.CustomName}'"); + } + + private void addVent(IMyAirVent vent) + { + if (vent.CustomName.Contains("Main") && airVent == null) + { + airVent = vent; + return; + } + if (vent.CustomName.Contains("Reference") && airSensor == null) { airSensor = vent; + return; + } + _console.Print($"Couldn't add air vent '{vent.CustomName}'"); + } + + private void addOxygenTank(IMyGasTank tank) + { + if (oxygenTank == null) + { + oxygenTank = tank; + return; + } + _console.Print($"Couldn't add oxygen tank '{tank.CustomName}'"); + } + + // private void initDoors() + // { + // List onlyDoors = new List(); + // _p.GridTerminalSystem.GetBlocksOfType(onlyDoors); + // foreach (IMyDoor door in onlyDoors) + // { + // if (!door.CustomName.StartsWith(_name)) continue; + + // if (door.CustomName.Contains("Inner")) + // { + // innerDoor = door; + // } + // else if (door.CustomName.Contains("Outer")) + // { + // outerDoor = door; + // } + + // if (innerDoor != null && outerDoor != null) + // { + // return; + // } + // } + + // Functional = false; + // } + + // private void initVents() + // { + // List onlyFans = new List(); + // _p.GridTerminalSystem.GetBlocksOfType(onlyFans); + // foreach (IMyAirVent vent in onlyFans) + // { + // if (!vent.CustomName.StartsWith(_name)) continue; + + // if (vent.CustomName.StartsWith(_name)) + // { + // if (vent.CustomName.Contains("Main")) + // { + // airVent = vent; + // continue; + // } + // else if (vent.CustomName.Contains("Reference")) + // { + // airSensor = vent; + // continue; + // } + // } + + // // A global reference vent will be used if we don't have one specific to our airlock. + // // A specific vent found later will overwrite this assignment. + // if (vent.CustomName.StartsWith("Airlock Reference") && airSensor == null) + // { + // airSensor = vent; + // } + // } + + // if (airVent == null) Functional = false; + // } + + // private void initLights() + // { + // lights = new List(); + // List allLights = new List(); + // _p.GridTerminalSystem.GetBlocksOfType(allLights); + // foreach (IMyLightingBlock light in allLights) + // { + // if (!light.CustomName.StartsWith(_name)) continue; + // lights.Add(light); + // } + // } + + // private void initOxygen() + // { + // List allTanks = new List(); + // _p.GridTerminalSystem.GetBlocksOfType(allTanks); + // foreach (IMyGasTank tank in allTanks) + // { + // if (!tank.CustomName.StartsWith(_name)) continue; + // oxygenTank = tank; + // return; + // } + // Functional = false; + // } + + public IEnumerator CycleAirlock() + { + Cycling = true; + setLights(AirlockLightState.Cycling); + + closeDoors(); + while (!DoorsClosed) { yield return true; } + lockDoors(); + + if (!airVent.CanPressurize) + { + error("Airlock is not airtight."); + Cycling = false; + yield break; + } + + pressurizeDepressurize(); + while (!PressureStabilized) { yield return true; } + airVent.Enabled = false; + + openDoor(); + while (!DoorOpened) { yield return true; } + lockDoors(); + + // Balance oxygen storage + setLights(AirlockLightState.Cooldown); + if (balanceOxygen()) + { + while (!OxygenBalanced) { yield return true; } + } + airVent.Enabled = false; + + // Cooldown period + int cooldown = 0; + while (cooldown < CooldownTicks) + { + cooldown++; + yield return true; + } + + setLights(AirlockLightState.Off); + _console.Print("Cycling Complete."); + Cycling = false; + } + + private void closeDoors() + { + _console.Print("Closing Doors"); + + // close the doors + innerDoor.Enabled = true; + outerDoor.Enabled = true; + innerDoor.CloseDoor(); + outerDoor.CloseDoor(); + } + + private void pressurizeDepressurize() + { + _console.Print("Cycling"); + + // toggle the current state + airVent.Depressurize = !airVent.Depressurize; + airVent.Enabled = true; + + // When depressurizing, check the external pressure and only depressurize to that value. + // TODO: test this for floating point errors + if (airVent.Depressurize && airSensor != null) + { + targetOxygenLevel = airSensor.GetOxygenLevel(); + _console.Print($"Set depressurization target to {targetOxygenLevel}"); } } - if (airVent == null) Functional = false; - } - - private void initLights() - { - lights = new List(); - List allLights = new List(); - _p.GridTerminalSystem.GetBlocksOfType(allLights); - foreach (IMyLightingBlock light in allLights) + // Open the appropriate door based on pressure state. + private void openDoor() { - if (!light.CustomName.StartsWith(_name)) continue; - lights.Add(light); - } - } + _console.Print($"Opening Door"); - private void initOxygen() - { - List allTanks = new List(); - _p.GridTerminalSystem.GetBlocksOfType(allTanks); - foreach (IMyGasTank tank in allTanks) - { - if (!tank.CustomName.StartsWith(_name)) continue; - oxygenTank = tank; - return; - } - Functional = false; - } - - public IEnumerator CycleAirlock() - { - Cycling = true; - setLights(AirlockLightState.Cycling); - - closeDoors(); - while (!DoorsClosed) { yield return true; } - lockDoors(); - - if (!airVent.CanPressurize) - { - error("Airlock is not airtight."); - Cycling = false; - yield break; + if (airVent.Status == VentStatus.Pressurized) + { + innerDoor.Enabled = true; + innerDoor.OpenDoor(); + } + else + { + outerDoor.Enabled = true; + outerDoor.OpenDoor(); + } } - pressurizeDepressurize(); - while (!PressureStabilized) { yield return true; } - airVent.Enabled = false; - - openDoor(); - while (!DoorOpened) { yield return true; } - lockDoors(); - - // Balance oxygen storage - setLights(AirlockLightState.Cooldown); - if (balanceOxygen()) + // TODO: blinkenlights are unsatisfying right now... + private void setLights(AirlockLightState lightState) { - while (!OxygenBalanced) { yield return true; } - } - airVent.Enabled = false; + float blinkLength = 1.0F; + float blinkInterval = 0.0F; + Color color = Color.Red; - // Cooldown period - int cooldown = 0; - while (cooldown < CooldownTicks) - { - cooldown++; - yield return true; + switch (lightState) + { + case AirlockLightState.Off: + color = Color.Green; + break; + case AirlockLightState.Cycling: + blinkInterval = 1.0F; + blinkLength = 0.75F; + break; + case AirlockLightState.Cooldown: + color = Color.Yellow; + break; + case AirlockLightState.Error: + // the defaults already set this correctly + break; + } + + foreach (IMyLightingBlock light in lights) + { + light.Enabled = true; + light.BlinkIntervalSeconds = blinkInterval; + light.BlinkLength = blinkLength; + light.Color = color; + } } - setLights(AirlockLightState.Off); - _console.Print("Cycling Complete."); - Cycling = false; - } - - private void closeDoors() - { - _console.Print("Closing Doors"); - - // close the doors - innerDoor.Enabled = true; - outerDoor.Enabled = true; - innerDoor.CloseDoor(); - outerDoor.CloseDoor(); - } - - private void pressurizeDepressurize() - { - _console.Print("Cycling"); - - // toggle the current state - airVent.Depressurize = !airVent.Depressurize; - airVent.Enabled = true; - - // When depressurizing, check the external pressure and only depressurize to that value. - // TODO: test this for floating point errors - if (airVent.Depressurize && airSensor != null) + private void lockDoors() { - targetOxygenLevel = airSensor.GetOxygenLevel(); - _console.Print($"Set depressurization target to {targetOxygenLevel}"); - } - } - - // Open the appropriate door based on pressure state. - private void openDoor() - { - _console.Print($"Opening Door"); - - if (airVent.Status == VentStatus.Pressurized) - { - innerDoor.Enabled = true; - innerDoor.OpenDoor(); - } - else - { - outerDoor.Enabled = true; - outerDoor.OpenDoor(); - } - } - - // TODO: blinkenlights are unsatisfying right now... - private void setLights(AirlockLightState lightState) - { - float blinkLength = 1.0F; - float blinkInterval = 0.0F; - Color color = Color.Red; - - switch (lightState) - { - case AirlockLightState.Off: - color = Color.Green; - break; - case AirlockLightState.Cycling: - blinkInterval = 1.0F; - blinkLength = 0.75F; - break; - case AirlockLightState.Cooldown: - color = Color.Yellow; - break; - case AirlockLightState.Error: - // the defaults already set this correctly - break; + innerDoor.Enabled = false; + outerDoor.Enabled = false; } - foreach (IMyLightingBlock light in lights) + private void error(string error) { - light.Enabled = true; - light.BlinkIntervalSeconds = blinkInterval; - light.BlinkLength = blinkLength; - light.Color = color; + _console.Print(error); + setLights(AirlockLightState.Error); } } - - private void lockDoors() - { - innerDoor.Enabled = false; - outerDoor.Enabled = false; - } - - private void error(string error) - { - _console.Print(error); - setLights(AirlockLightState.Error); - } } } \ No newline at end of file diff --git a/Airlock/Airlock.csproj b/Airlock/Airlock.csproj index 7b595e0..899617a 100644 --- a/Airlock/Airlock.csproj +++ b/Airlock/Airlock.csproj @@ -24,5 +24,5 @@ - + \ No newline at end of file diff --git a/Airlock/Program.cs b/Airlock/Program.cs index 2ebc5f3..e146fe6 100644 --- a/Airlock/Program.cs +++ b/Airlock/Program.cs @@ -12,30 +12,33 @@ namespace IngameScript private int _tickCount = 0; private MyCommandLine _cli; private Console _console; + private MyIni _ini; public Program() { - _console = new Console(this); + _ini = new MyIni(); + _console = new Console(this, _ini); _cli = new MyCommandLine(); _jobs = new List>(); _airlocks = new Dictionary(); - List doors = new List(); - GridTerminalSystem.GetBlocksOfType(doors); - foreach (IMyDoor door in doors) + List airlockBlocks = new List(); + GridTerminalSystem.GetBlocksOfType(airlockBlocks, block => block.CustomName.Contains("!Airlock")); + foreach (IMyTerminalBlock block in airlockBlocks) { - if (!door.CustomName.StartsWith("Airlock")) continue; - string airlockName = door.CustomName.Split(' ')[0]; - if (_airlocks.ContainsKey(airlockName)) continue; - Airlock newAirlock = new Airlock(this, _console, airlockName); - if (!newAirlock.Functional) { - _console.Print($"{airlockName} is missing one or more required blocks."); - continue; - } - _airlocks[airlockName] = newAirlock; - } + string airlockName = Utils.ExtractTag(block, "!Airlock"); + if (!_airlocks.ContainsKey(airlockName)) + { + _airlocks[airlockName] = new Airlock(this, _console, airlockName); + } + _airlocks[airlockName].AddBlock(block); + } + + // TODO: it would be most convenient to just delete non-functional Airlocks... but maybe they're worth + // keeping around for warnings. + } _console.Print($"Found {_airlocks.Count} airlocks."); } @@ -49,7 +52,7 @@ namespace IngameScript if (_cli.ArgumentCount == 0) { _console.Print("You must provide an airlock ID."); } else { - string airlockName = $"Airlock{_cli.Argument(0)}"; + string airlockName = $"!Airlock{_cli.Argument(0)}"; if (!_airlocks.ContainsKey(airlockName)) { _console.Print($"Invalid airlock ID {_cli.Argument(0)}");