diff --git a/AirMonitor/AirDoor.cs b/AirMonitor/AirDoor.cs new file mode 100644 index 0000000..0667a80 --- /dev/null +++ b/AirMonitor/AirDoor.cs @@ -0,0 +1,19 @@ +using Sandbox.ModAPI.Ingame; +using System.Collections.Generic; + +namespace IngameScript +{ + partial class Program + { + public class AirDoor + { + public bool Triggered { get; set; } = false; + public IMyDoor Door { get; private set; } + + public AirDoor(Program program, IMyDoor door) + { + Door = door; + } + } + } +} \ No newline at end of file diff --git a/AirMonitor/AirZone.cs b/AirMonitor/AirZone.cs index ab9f003..df91bb7 100644 --- a/AirMonitor/AirZone.cs +++ b/AirMonitor/AirZone.cs @@ -10,13 +10,14 @@ namespace IngameScript public class AirZone { public string Name { get; private set; } + public bool Triggered { get { foreach (IMyAirVent vent in Vents) { - if (vent.GetOxygenLevel() < TriggerLevel) return true; + if (vent.GetOxygenLevel() < TriggerLevel || vent.Status == VentStatus.Depressurized) return true; } return false; } @@ -24,9 +25,10 @@ namespace IngameScript public List Vents { get; } = new List(); private Program _program; - private List _doors = new List(); - private List _displays = new List(); + + // private List _displays = new List(); // TODO: add single-zone displays private List _lights = new List(); + private List _doors = new List(); private PrefixedConsole _console; private Sequencer _sequencer; @@ -40,25 +42,27 @@ namespace IngameScript _sequencer = new Sequencer(_program, Name); } + public void AddDoor(AirDoor door) + { + _doors.Add(door); + IMyDoor doorBlock = door.Door; + SequenceableDoor wrapped = SequenceableFactory.MakeSequenceable( + _program, + doorBlock as IMyDoor, + "airMonitor") as SequenceableDoor; + wrapped.Step = 0; + wrapped.DeployOpen = false; + wrapped.LockOpen = false; + wrapped.LockClosed = true; + _sequencer.AddBlock(wrapped); + } + public void AddBlock(IMyTerminalBlock block) { if (block is IMyAirVent) { Vents.Add(block as IMyAirVent); } - else if (block is IMyDoor) - { - _doors.Add(block as IMyDoor); - SequenceableDoor wrapped = SequenceableFactory.MakeSequenceable( - _program, - block as IMyDoor, - "airMonitor") as SequenceableDoor; - wrapped.Step = 0; - wrapped.DeployOpen = false; - wrapped.LockOpen = false; - wrapped.LockClosed = true; - _sequencer.AddBlock(wrapped); - } else if (block is IMyLightingBlock) { _lights.Add(block as IMyLightingBlock); @@ -78,54 +82,34 @@ namespace IngameScript if (Triggered == true) { _console.Print($"Low pressure alarm triggered."); - // close the doors IEnumerator job = _sequencer.RunSequence(true); while (job.MoveNext()) { // It would be nice if the API had UpdateFrequency.Once10, e.g. "re-run in 10 ticks and then clear the flag" // We'll just monitor every tick instead. + setTriggeredStates(); _program.Runtime.UpdateFrequency |= UpdateFrequency.Once; yield return true; } job.Dispose(); - while (Triggered) { yield return true; } - - // unlock the doors, but we'll leave them closed. - foreach (IMyDoor door in _doors) { door.Enabled = true; } - - foreach (IMyLightingBlock light in _lights) + while (Triggered) { - light.Enabled = true; - light.Color = Color.White; + setTriggeredStates(); + yield return true; } + _console.Print("Air pressure returned to safe levels."); } yield return true; } } - public void SetLights() + private void setTriggeredStates() { - bool warning = false; - foreach (IMyAirVent vent in Vents) - { - if (vent.GetOxygenLevel() < TriggerLevel) - { - warning = true; - break; - } - } - foreach (IMyLightingBlock light in _lights) - { - if (warning) - { - light.Enabled = true; - light.Color = Color.Red; - } - else light.Color = Color.White; - } + foreach (IMyLightingBlock light in _lights) light.Color = Color.Red; + foreach (AirDoor door in _doors) door.Triggered = true; } } } diff --git a/AirMonitor/Program.cs b/AirMonitor/Program.cs index f4865dd..b5e493a 100644 --- a/AirMonitor/Program.cs +++ b/AirMonitor/Program.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using VRage.Game.ModAPI.Ingame.Utilities; +using VRageMath; namespace IngameScript { @@ -13,12 +14,17 @@ namespace IngameScript public MyIni Ini { get; } = new MyIni(); public IConsole Console { get; private set; } - private Dictionary _zones = new Dictionary(); private List> _jobs = new List>(); + + private Dictionary _zones = new Dictionary(); + private List _doors = new List(); + private List _lights = new List(); + private List _displays = new List(); - private StringBuilder _displayBuffer = new StringBuilder(); private List _oxygenTanks = new List(); + private StringBuilder _displayBuffer = new StringBuilder(); + public Program() { Console = new MainConsole(this, "Air Pressure Monitor"); @@ -31,13 +37,34 @@ namespace IngameScript Ini.TryParse(block.CustomData); string[] zones = new string[] { }; - if (block is IMyTextSurface) + if (Ini.Get("airMonitor", "zones").ToString() != "") { + zones = Ini.Get("airMonitor", "zones").ToString().Split(','); + } + else if (Ini.Get("airMonitor", "zone").ToString() != "") + { + zones = new string[] { Ini.Get("airMonitor", "zone").ToString() }; + } + foreach (string zone in zones) + { + if (!_zones.ContainsKey(zone)) _zones[zone] = new AirZone(this, zone); + } + + if (block is IMyGasTank) + { + _oxygenTanks.Add(block as IMyGasTank); + } + + else if (block is IMyTextSurface) + { + // TODO: behave differently if zone is defined Console.Print($"Adding monitoring display '{block.CustomName}'"); _displays.Add(block as IMyTextSurface); } + else if (Ini.Get("airMonitor", "display").ToString() != "") { + // TODO: behave differently if zone is defined int displayIndex = Ini.Get("airMonitor", "display").ToInt32(); IMyTextSurfaceProvider provider = block as IMyTextSurfaceProvider; if (provider.SurfaceCount <= displayIndex) @@ -49,29 +76,26 @@ namespace IngameScript _displays.Add(provider.GetSurface(displayIndex)); } } - else if (block is IMyGasTank) + + else if (block is IMyDoor) { - _oxygenTanks.Add(block as IMyGasTank); - } - else if (Ini.Get("airMonitor", "zones").ToString() != "") - { - zones = Ini.Get("airMonitor", "zones").ToString().Split(','); - } - else if (Ini.Get("airMonitor", "zone").ToString() != "") - { - zones = new string[] { Ini.Get("airMonitor", "zone").ToString() }; - } - else { - Console.Print($"Failed to add block {block.CustomName}"); + AirDoor door = new AirDoor(this, block as IMyDoor); + _doors.Add(door); + foreach (string zone in zones) _zones[zone].AddDoor(door); } - foreach (string zone in zones) + else if (block is IMyLightingBlock) { - if (!_zones.ContainsKey(zone)) + _lights.Add(block as IMyLightingBlock); + foreach (string zone in zones) _zones[zone].AddBlock(block); + } + + else + { + foreach (string zone in zones) { - _zones[zone] = new AirZone(this, zone); + _zones[zone].AddBlock(block); } - _zones[zone].AddBlock(block); } } @@ -82,6 +106,7 @@ namespace IngameScript Console.Print(kvp.Key); _jobs.Add(zone.Monitor()); } + Runtime.UpdateFrequency = UpdateFrequency.Update100; Console.UpdateTickCount(); } @@ -90,20 +115,32 @@ namespace IngameScript { Console.UpdateTickCount(); + // Light indicators should be set/unset independent of the triggered states and actions. + foreach (IMyLightingBlock light in _lights) light.Color = Color.White; + foreach (AirDoor door in _doors) door.Triggered = false; + foreach (IEnumerator job in _jobs) { - if (job.MoveNext()) continue; - Console.Print("WARNING: Monitoring job exited. Zone no longer being monitored."); + // Run the monitoring jobs, which will update the lights and door states + if (!job.MoveNext()) Console.Print("WARNING: Monitoring job exited. Zone no longer being monitored."); } - // Light indicators should be set/unset independent of the triggered states and actions. - foreach (AirZone zone in _zones.Values) zone.SetLights(); + // Unlock all safe doors, but leave them closed. + // Note that this will make it difficult to manually lock/disable these doors. + foreach (AirDoor door in _doors) + { + if (door.Door.Enabled == false && !door.Triggered) + { + Console.Print($"DEBUG: {door.Door.CustomName} re-enabled"); + door.Door.Enabled = true; + } + } - _updateDisplays(); + updateDisplays(); } // write diagnostics to any configured display screens - private void _updateDisplays() + private void updateDisplays() { if (_displays.Count == 0) return;