enum AirlockState { Off, DoorsClosing, Cycling, DoorOpening, Cooldown, } enum AirlockLightState { Off, Cycling, Cooldown, Error, } string airlockName; IEnumerator state; float targetOxygenLevel = 0.0F; // Blocks we need to track IMyDoor innerDoor; IMyDoor outerDoor; List lights; IMyAirVent airVent; IMyAirVent airSensor; // TODO: we don't use this yet IMyGasTank oxygenTank; public Program() { airlockName = Me.CustomName.Split(' ')[0]; state = null; // Find the correct objects InitDoors(); InitVents(); InitLights(); InitOxygen(); } private void InitDoors() { List onlyDoors = new List(); GridTerminalSystem.GetBlocksOfType(onlyDoors); foreach (IMyDoor door in onlyDoors) { if (door.CustomName.StartsWith(airlockName)) { if (door.CustomName.Contains("Inner")) { innerDoor = door; } else if (door.CustomName.Contains("Outer")) { outerDoor = door; } } if (innerDoor != null && outerDoor != null) { break; } } } private void InitVents() { List onlyFans = new List(); GridTerminalSystem.GetBlocksOfType(onlyFans); foreach (IMyAirVent vent in onlyFans) { if (vent.CustomName.StartsWith(airlockName)) { if (vent.CustomName.Contains("Main")) { airVent = vent; } else if (vent.CustomName.Contains("Reference")) { airSensor = vent; } } // 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; } } } private void InitLights() { lights = new List(); List allLights = new List(); GridTerminalSystem.GetBlocksOfType(allLights); foreach (IMyLightingBlock light in allLights) { if (light.CustomName.StartsWith(airlockName)) { lights.Add(light); } } } private void InitOxygen() { List allTanks = new List(); GridTerminalSystem.GetBlocksOfType(allTanks); foreach (IMyGasTank tank in allTanks) { if (tank.CustomName.StartsWith(airlockName)) { oxygenTank = tank; break; } } } public void Main(string argument, UpdateType updateSource) { if (state == null) { state = CycleAirlock(); Runtime.UpdateFrequency = UpdateFrequency.Update1; return; } if (!state.MoveNext()) { state.Dispose(); state = null; Runtime.UpdateFrequency = UpdateFrequency.None; } } private IEnumerator CycleAirlock() { SetLights(AirlockLightState.Cycling); CloseDoors(); while(!DoorsClosed()) { yield return true; } LockDoors(); if (!airVent.CanPressurize) { Error("Airlock is not airtight."); yield return false; } 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 Runtime.UpdateFrequency = UpdateFrequency.Update100; yield return true; Runtime.UpdateFrequency = UpdateFrequency.Update1; SetLights(AirlockLightState.Off); } private void CloseDoors() { Echo("DEBUG: Closing Doors"); // close the doors innerDoor.Enabled = true; outerDoor.Enabled = true; innerDoor.CloseDoor(); outerDoor.CloseDoor(); } private bool DoorsClosed() { return innerDoor.Status == DoorStatus.Closed && outerDoor.Status == DoorStatus.Closed; } private void PressurizeDepressurize() { Echo("DEBUG: 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(); Echo($"Set depressurization target to {targetOxygenLevel}"); } } // TODO: this should check for depressurizing to the *reference* amount private bool PressureStabilized() { return ((airVent.Depressurize && airVent.GetOxygenLevel() <= targetOxygenLevel) || (!airVent.Depressurize && airVent.Status == VentStatus.Pressurized)); } // Open the appropriate door based on pressure state. private void OpenDoor() { Echo("DEBUG: Opening Door"); if (airVent.Status == VentStatus.Pressurized) { innerDoor.Enabled = true; innerDoor.OpenDoor(); } else { outerDoor.Enabled = true; outerDoor.OpenDoor(); } } private bool DoorOpened() { 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; } Echo("DEBUG: Balancing Oxygen Tank"); // Configure the vent to suck in Oxygen. airVent.Depressurize = true; airVent.Enabled = true; return true; } private bool OxygenBalanced() { return oxygenTank.FilledRatio > 0.25; } // 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; } foreach (IMyLightingBlock light in lights) { light.Enabled = true; light.BlinkIntervalSeconds = blinkInterval; light.BlinkLength = blinkLength; light.Color = color; } } private void LockDoors() { innerDoor.Enabled = false; outerDoor.Enabled = false; } private void Error(string error) { Echo(error); SetLights(AirlockLightState.Error); }