Advertisement
LtTwinkie

Twinkie's Airlock Automation v2.1

Jul 11th, 2023
772
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 19.84 KB | None | 0 0
  1. /*
  2.  * Twinkie Industries™ Automation Presents
  3.  * Airlock Automation v2.1
  4.  *
  5.  * This script will allow you to set up airlocks with vents and lights that will operate properly with a single button press or sensor trigger.
  6.  *
  7.  * On the vent and any lights add the following to CustomData
  8.  * [Airlock]
  9.  * Name=airlockName
  10.  *
  11.  * Also add it to the doors but include if the door is the outside door or not
  12.  * [Airlock]
  13.  * Name=airlockName
  14.  * IsOutside=true
  15.  *
  16.  * When you run the script using the name of the airlock as the argument it will properly cycle the airlock.
  17.  */
  18.  
  19. // SETTINGS ---------------------------------------------------------------------------------------------------
  20.  
  21. // If you set a value for GRID_NAME, each block will require it in the name. This is used to prevent blocks from
  22. // other grids being added.
  23. public const string GRID_NAME = "";
  24.  
  25. // This timeout prevents the airlock getting stuck because the vent can't depressurize. For example, if there is
  26. // no free space in an oxygen tank or no oxygen tank connected the vent won't be able to remove the air.
  27. public const double VENT_TIMEOUT_SECONDS = 4.0;
  28.  
  29. // The normal color for the airlock light when it is pressurized
  30. public static readonly Color PRESSURIZED_COLOR = Light.FLORESCENT_WARM;
  31.  
  32. // The color for the airlock light when it is depressurized
  33. public static readonly Color UNPRESSURIZED_COLOR = Color.Red;
  34.  
  35.  
  36. // END SETTINGS - PLEASE DO NOT EDIT BELOW --------------------------------------------------------------------
  37.  
  38. private Dictionary<string, Airlock> _airlocks = new Dictionary<string, Airlock>();
  39. private List<string> _invalidAirlocks = new List<string>();
  40.  
  41. public Program()
  42. {
  43.     Refresh();
  44.     Runtime.UpdateFrequency = UpdateFrequency.Update10;
  45. }
  46.  
  47. public void Main(string argument, UpdateType updateSource)
  48. {
  49.     if (_invalidAirlocks.Count != 0)
  50.     {
  51.         Echo("Invalid airlocks:");
  52.         foreach (string invalidAirlock in _invalidAirlocks)
  53.         {
  54.             Echo(invalidAirlock);
  55.         }
  56.         _invalidAirlocks.Clear();
  57.     }
  58.  
  59.     if (string.IsNullOrWhiteSpace(argument))
  60.     {
  61.         foreach (var airlock in _airlocks.Values)
  62.         {
  63.             airlock.Update();
  64.         }
  65.  
  66.         return;
  67.     }
  68.  
  69.     string lower = argument.ToLowerInvariant();
  70.  
  71.     if (lower == "lock")
  72.     {
  73.         foreach (var airlock in _airlocks)
  74.         {
  75.             Echo($"Locking {airlock.Key}");
  76.             airlock.Value.Lock();
  77.         }
  78.  
  79.         return;
  80.     }
  81.  
  82.     if (lower == "reset")
  83.     {
  84.         foreach (var airlock in _airlocks)
  85.         {
  86.             Echo($"Resetting {airlock.Key}");
  87.             airlock.Value.Reset();
  88.         }
  89.  
  90.         return;
  91.     }
  92.  
  93.     if (lower == "refresh")
  94.     {
  95.         Echo($"Refresh arg");
  96.         Refresh();
  97.         return;
  98.     }
  99.  
  100.     if (_airlocks.ContainsKey(argument))
  101.     {
  102.         Airlock airlock;
  103.         if (_airlocks.TryGetValue(argument, out airlock))
  104.         {
  105.             Echo($"Cycling {argument}");
  106.             airlock.Cycle();
  107.         }
  108.         else
  109.         {
  110.             Echo($"{argument} not found");
  111.         }
  112.     }
  113. }
  114.  
  115. public void Refresh()
  116. {
  117.     _airlocks.Clear();
  118.  
  119.     var airlockBlocks = new List<IMyTerminalBlock>();
  120.     GridTerminalSystem.GetBlocksOfType(airlockBlocks, block => block.CustomName.Contains(GRID_NAME) && MyIni.HasSection(block.CustomData, CustomData.AIRLOCK));
  121.  
  122.     foreach (IMyTerminalBlock block in airlockBlocks)
  123.     {
  124.         CustomData customData;
  125.         try
  126.         {
  127.             customData = new CustomData(block, Echo);
  128.         }
  129.         catch (Exception)
  130.         {
  131.             continue;
  132.         }
  133.  
  134.         string airlockName = customData.Name;
  135.         Airlock airlock;
  136.         if (!_airlocks.TryGetValue(airlockName, out airlock))
  137.         {
  138.             airlock = _airlocks[airlockName] = new Airlock();
  139.         }
  140.  
  141.         airlock.Add(block, customData);
  142.     }
  143.  
  144.     foreach (KeyValuePair<string, Airlock> airlock in _airlocks)
  145.     {
  146.         if (!airlock.Value.IsFunctional)
  147.         {
  148.             _airlocks.Remove(airlock.Key);
  149.             _invalidAirlocks.Add(airlock.Key);
  150.         }
  151.         else
  152.         {
  153.             airlock.Value.Reset();
  154.         }
  155.     }
  156. }
  157.  
  158. /* Twinkie Industries™ Automation Presents
  159.          * Airlock Automation v1.0*/
  160.  
  161. /*
  162.          * This script will allow you to set up airlocks with vents and lights that will operate properly with a single button press or sensor trigger.
  163.          *
  164.          * On the vent and any lights add the following to CustomData
  165.          * [Airlock]
  166.          * Name=airlockName
  167.          *
  168.          * Also add it to the doors but include if the door is the outside door or not
  169.          * [Airlock]
  170.          * Name=airlockName
  171.          * IsOutside=true
  172.          *
  173.          * When you run the script using the name of the airlock as the argument it will properly cycle the airlock.
  174.          */
  175.  
  176. private class Airlock
  177. {
  178.     private enum State
  179.     {
  180.         /// <summary> Waiting for the doors to close </summary>
  181.         Locking,
  182.  
  183.         /// <summary> Airlock is offline </summary>
  184.         Locked,
  185.  
  186.         /// <summary> Closing both doors and pressurizing </summary>
  187.         Resetting,
  188.  
  189.         /// <summary> Nothing happening </summary>
  190.         Idle,
  191.  
  192.         /// <summary> Waiting for the door to close </summary>
  193.         Closing,
  194.  
  195.         /// <summary> Waiting for venting </summary>
  196.         Venting,
  197.  
  198.         /// <summary> Waiting for the door to open </summary>
  199.         Opening,
  200.     }
  201.  
  202.     public List<Door> DoorsIn = new List<Door>();
  203.     public List<Door> DoorsOut = new List<Door>();
  204.     public List<Vent> AirVents = new List<Vent>();
  205.     public List<Light> Lights = new List<Light>();
  206.  
  207.     private State _state = State.Idle;
  208.     private bool _openOut;
  209.     private DateTime _nextAction;
  210.  
  211.     public bool IsFunctional => DoorsIn.Count != 0 && DoorsOut.Count != 0 && AirVents.Count != 0;
  212.  
  213.     public List<Door> AllDoors
  214.     {
  215.         get
  216.         {
  217.             var rv = new List<Door>(DoorsIn.Count + DoorsOut.Count);
  218.             rv.AddRange(DoorsIn);
  219.             rv.AddRange(DoorsOut);
  220.             return rv;
  221.         }
  222.     }
  223.  
  224.     public void Add(IMyTerminalBlock block, CustomData customData)
  225.     {
  226.         if (block is IMyDoor)
  227.         {
  228.             MyIniValue isOutside = customData.Get(CustomData.IS_OUTSIDE);
  229.             if (isOutside.IsEmpty)
  230.                 return;
  231.  
  232.             var door = new Door(block as IMyDoor);
  233.             if (isOutside.ToBoolean())
  234.             {
  235.                 DoorsOut.Add(door);
  236.             }
  237.             else
  238.             {
  239.                 DoorsIn.Add(door);
  240.             }
  241.         }
  242.         else if (block is IMyAirVent)
  243.         {
  244.             AirVents.Add(new Vent(block as IMyAirVent));
  245.         }
  246.         else if (block is IMyLightingBlock)
  247.         {
  248.             Lights.Add(new Light(block as IMyLightingBlock));
  249.         }
  250.     }
  251.  
  252.     public void Update()
  253.     {
  254.         if (_state == State.Idle || _state == State.Locked)
  255.             return;
  256.  
  257.         // Un-timed states
  258.         if (_state == State.Venting)
  259.         {
  260.             if (_openOut && Vent.AllDepressurized(AirVents) ||
  261.                 !_openOut && Vent.AllPressurized(AirVents) ||
  262.                 DateTime.UtcNow > _nextAction) // timeout
  263.             {
  264.                 _nextAction = Door.OpenDoors(_openOut ? DoorsOut : DoorsIn);
  265.                 RefreshLights();
  266.                 _state = State.Opening;
  267.             }
  268.  
  269.             return;
  270.         }
  271.  
  272.         if (DateTime.UtcNow < _nextAction)
  273.             return;
  274.  
  275.         // Timed states
  276.         switch (_state)
  277.         {
  278.             // Cycle
  279.             case State.Closing:
  280.             {
  281.                 PowerOff();
  282.                 Vent.SetDepressurize(AirVents, _openOut);
  283.                 _nextAction = DateTime.UtcNow + TimeSpan.FromSeconds(VENT_TIMEOUT_SECONDS);
  284.                 _state = State.Venting;
  285.                 break;
  286.             }
  287.             case State.Opening:
  288.             {
  289.                 PowerOff();
  290.                 RefreshLights();
  291.                 _state = State.Idle;
  292.                 break;
  293.             }
  294.  
  295.             // Lock
  296.             case State.Locking:
  297.             {
  298.                 PowerOff();
  299.                 _state = State.Locked;
  300.                 break;
  301.             }
  302.  
  303.             // Reset
  304.             case State.Resetting:
  305.             {
  306.                 Light.SetEnabled(Lights, true);
  307.                 RefreshLights();
  308.                 _state = State.Idle;
  309.                 break;
  310.             }
  311.         }
  312.     }
  313.  
  314.     public void Cycle()
  315.     {
  316.         if (_state != State.Idle)
  317.             return;
  318.  
  319.         _openOut = !_openOut;
  320.         _nextAction = Door.CloseDoors(AllDoors);
  321.         Light.SetColor(Lights, Color.Red);
  322.         _state = State.Closing;
  323.     }
  324.  
  325.     public void Reset()
  326.     {
  327.         _nextAction = Door.CloseDoors(AllDoors);
  328.         Light.SetEnabled(Lights, true);
  329.         _state = State.Resetting;
  330.     }
  331.  
  332.     public void Lock()
  333.     {
  334.         _nextAction = Door.CloseDoors(AllDoors);
  335.         Light.SetEnabled(Lights, false);
  336.         _state = State.Locking;
  337.     }
  338.  
  339.     private void PowerOff()
  340.     {
  341.         Door.SetEnabled(AllDoors, false);
  342.     }
  343.  
  344.     private void RefreshLights()
  345.     {
  346.         Color color = Vent.AllPressurized(AirVents) ? PRESSURIZED_COLOR : UNPRESSURIZED_COLOR;
  347.         Light.SetColor(Lights, color);
  348.     }
  349. }
  350.  
  351. private class CustomData
  352. {
  353.     public const string AIRLOCK = "Airlock";
  354.     public const string IS_OUTSIDE = "IsOutside";
  355.     private const string NAME = "Name";
  356.  
  357.     public MyIni Ini { get; } = new MyIni();
  358.  
  359.     public string Name => Ini.Get(AIRLOCK, NAME).ToString();
  360.  
  361.     public CustomData(IMyTerminalBlock block, Action<string> echo)
  362.     {
  363.         MyIniParseResult parseResult;
  364.         if (!Ini.TryParse(block.CustomData, out parseResult))
  365.         {
  366.             echo?.Invoke("Failed to parse ini");
  367.             throw new Exception(parseResult.ToString());
  368.         }
  369.  
  370.         MyIniValue nameResult = Get(NAME);
  371.         if (nameResult.IsEmpty)
  372.         {
  373.             string message = $"Can't find {NAME} setting for {block.CustomName}";
  374.             echo?.Invoke(message);
  375.             throw new Exception(message);
  376.         }
  377.     }
  378.  
  379.     public MyIniValue Get(string key) => Ini.Get(AIRLOCK, key);
  380. }
  381.  
  382. // Mixin
  383. public class Door
  384. {
  385.     public const int DELAY_STANDARD_MILLIS = 1000;
  386.     public const int DELAY_HANGAR_MILLIS = 10000;
  387.     private int _customDelay;
  388.  
  389.     private bool _isHangar;
  390.  
  391.     public IMyDoor Block { get; }
  392.  
  393.     public bool Enabled { get { return Block.Enabled; } set { Block.Enabled = value; } }
  394.     public DoorStatus Status => Block.Status;
  395.     public float OpenRatio => Block.OpenRatio;
  396.     public bool IsOpen => Block.Status == DoorStatus.Open;
  397.     public bool IsClosed => Block.Status == DoorStatus.Closed;
  398.     public bool IsTransitioning => !IsOpen && !IsClosed;
  399.  
  400.     public Door(IMyDoor block, int customDelay = 0)
  401.     {
  402.         Block = block;
  403.         _customDelay = customDelay;
  404.         _isHangar = block is IMyAirtightHangarDoor;
  405.     }
  406.  
  407.     public static void SetEnabled(List<Door> doors, bool value)
  408.     {
  409.         foreach (Door door in doors)
  410.         {
  411.             door.Enabled = value;
  412.         }
  413.     }
  414.  
  415.     public static bool AnyOpened(List<Door> doors)
  416.     {
  417.         foreach (var door in doors)
  418.         {
  419.             if (door.Status == DoorStatus.Open)
  420.                 return true;
  421.         }
  422.         return false;
  423.     }
  424.  
  425.     public static bool AnyClosed(List<Door> doors)
  426.     {
  427.         foreach (var door in doors)
  428.         {
  429.             if (door.Status == DoorStatus.Closed)
  430.                 return true;
  431.         }
  432.         return false;
  433.     }
  434.  
  435.     public static DateTime OpenDoors(List<Door> doors)
  436.     {
  437.         DateTime maxDelay = DateTime.MinValue;
  438.         foreach (Door door in doors)
  439.         {
  440.             DateTime delay = door.OpenDoor();
  441.  
  442.             if (delay > maxDelay)
  443.                 maxDelay = delay;
  444.         }
  445.  
  446.         return maxDelay;
  447.     }
  448.  
  449.     public static DateTime CloseDoors(List<Door> doors)
  450.     {
  451.         DateTime maxDelay = DateTime.MinValue;
  452.         foreach (Door door in doors)
  453.         {
  454.             DateTime delay = door.CloseDoor();
  455.  
  456.             if (delay > maxDelay)
  457.                 maxDelay = delay;
  458.         }
  459.  
  460.         return maxDelay;
  461.     }
  462.  
  463.     public DateTime OpenDoor()
  464.     {
  465.         if (Block.Status != DoorStatus.Open)
  466.         {
  467.             Block.Enabled = true;
  468.             Block.OpenDoor();
  469.             return Delay();
  470.         }
  471.  
  472.         return DateTime.MinValue;
  473.     }
  474.  
  475.     public DateTime CloseDoor()
  476.     {
  477.         if (Block.Status != DoorStatus.Closed)
  478.         {
  479.             Block.Enabled = true;
  480.             Block.CloseDoor();
  481.             return Delay();
  482.         }
  483.  
  484.         return DateTime.MinValue;
  485.     }
  486.  
  487.     public DateTime ToggleDoor()
  488.     {
  489.         if (Block.Status == DoorStatus.Closed ||
  490.             Block.Status == DoorStatus.Closing)
  491.         {
  492.             return OpenDoor();
  493.         }
  494.  
  495.         return CloseDoor();
  496.     }
  497.  
  498.     private DateTime Delay()
  499.     {
  500.         int standardDelay = _isHangar ? DELAY_HANGAR_MILLIS : DELAY_STANDARD_MILLIS;
  501.         int delay = _customDelay != 0 ? _customDelay : standardDelay;
  502.         return DateTime.UtcNow + TimeSpan.FromMilliseconds(delay);
  503.     }
  504. }
  505.  
  506. // Mixin
  507. public class Light
  508. {
  509.     public static readonly Color SODIUM = new Color(255, 183, 76);
  510.     public static readonly Color INCANDESCENT = new Color(255, 255, 210);
  511.     public static readonly Color FLORESCENT_WARM = new Color(230, 255, 255);
  512.     public static readonly Color FLORESCENT_COOL = new Color(175, 215, 255);
  513.  
  514.     public IMyLightingBlock Block { get; private set; }
  515.  
  516.     public bool OriginalEnabled { get; private set; }
  517.     public float OriginalRadius { get; private set; }
  518.     public float OriginalIntensity { get; private set; }
  519.     public float OriginalFalloff { get; private set; }
  520.     public float OriginalBlinkInterval { get; private set; }
  521.     public float OriginalBlinkLength { get; private set; }
  522.     public float OriginalBlinkOffset { get; private set; }
  523.     public Color OriginalColor { get; private set; }
  524.  
  525.     public bool Enabled { get { return Block.Enabled;} set { Block.Enabled = value; } }
  526.     public float Radius { get { return Block.Radius; } set { Block.Radius = value; } }
  527.     float Intensity { get { return Block.Intensity; } set { Block.Intensity = value; } }
  528.     float Falloff { get { return Block.Falloff; } set { Block.Falloff = value; } }
  529.     float BlinkIntervalSeconds { get { return Block.BlinkIntervalSeconds; } set { Block.BlinkIntervalSeconds = value; } }
  530.     float BlinkLength { get { return Block.BlinkLength; } set { Block.BlinkLength = value; } }
  531.     float BlinkOffset { get { return Block.BlinkOffset; } set { Block.BlinkOffset = value; } }
  532.     Color Color { get { return Block.Color; } set { Block.Color = value; } }
  533.  
  534.     public Light(IMyLightingBlock block)
  535.     {
  536.         Block = block;
  537.         OriginalEnabled = Block.Enabled;
  538.         OriginalRadius = Block.Radius;
  539.         OriginalIntensity = Block.Intensity;
  540.         OriginalFalloff = Block.Falloff;
  541.         OriginalBlinkInterval = Block.BlinkIntervalSeconds;
  542.         OriginalBlinkLength = Block.BlinkLength;
  543.         OriginalBlinkOffset = Block.BlinkOffset;
  544.         OriginalColor = Block.Color;
  545.     }
  546.  
  547.     public static void SetEnabled(List<Light> lights, bool value)
  548.     {
  549.         foreach (var light in lights)
  550.         {
  551.             light.Enabled = value;
  552.         }
  553.     }
  554.  
  555.     public static void SetColor(List<Light> lights, Color color)
  556.     {
  557.         foreach (var light in lights)
  558.         {
  559.             light.Color = color;
  560.         }
  561.     }
  562.  
  563.     public void Reset()
  564.     {
  565.         ResetEnabled();
  566.         ResetRadius();
  567.         ResetIntensity();
  568.         ResetFalloff();
  569.         ResetBlinkInterval();
  570.         ResetBlinkLength();
  571.         ResetBlinkOffset();
  572.         ResetColor();
  573.     }
  574.  
  575.     public void ResetEnabled() => Block.Enabled = OriginalEnabled;
  576.     public void ResetRadius() => Block.Radius = OriginalRadius;
  577.     public void ResetIntensity() => Block.Intensity = OriginalIntensity;
  578.     public void ResetFalloff() => Block.Falloff = OriginalFalloff;
  579.     public void ResetBlinkInterval() => Block.BlinkIntervalSeconds = OriginalBlinkInterval;
  580.     public void ResetBlinkLength() => Block.BlinkLength = OriginalBlinkLength;
  581.     public void ResetBlinkOffset() => Block.BlinkOffset = OriginalBlinkOffset;
  582.     public void ResetColor() => Block.Color = OriginalColor;
  583. }
  584.  
  585. // Mixin
  586. public class Vent
  587. {
  588.     private const int DELAY_MILLIS = 2000;
  589.  
  590.     private int _customDelay;
  591.  
  592.     public IMyAirVent Block { get; }
  593.  
  594.     public bool Enabled { get { return Block.Enabled; } set { Block.Enabled = value; } }
  595.     public bool CanPressurize => Block.CanPressurize;
  596.     public float OxygenLevel => Block.GetOxygenLevel();
  597.     public VentStatus Status => Block.Status;
  598.     public bool PressurizationEnabled => Block.PressurizationEnabled;
  599.  
  600.     public bool Depressurize => Block.Depressurize;
  601.  
  602.     public bool IsPressurized => Block.Status == VentStatus.Pressurized;
  603.     public bool IsDepressurized => Block.Status == VentStatus.Depressurized;
  604.     public bool IsPressureUncertain => !IsPressurized && !IsDepressurized;
  605.  
  606.     public Vent(IMyAirVent block, int customDelay = 0)
  607.     {
  608.         Block = block;
  609.         _customDelay = customDelay;
  610.     }
  611.  
  612.     public static void SetEnabled(List<Door> doors, bool value)
  613.     {
  614.         foreach (Door door in doors)
  615.         {
  616.             door.Enabled = value;
  617.         }
  618.     }
  619.  
  620.     public static float OxygenLevelMin(List<Vent> vents)
  621.     {
  622.         float level = float.MaxValue;
  623.         foreach (var vent in vents)
  624.         {
  625.             if (vent.OxygenLevel < level)
  626.                 level = vent.OxygenLevel;
  627.         }
  628.         return level;
  629.     }
  630.  
  631.     public static float OxygenLevelMax(List<Vent> vents)
  632.     {
  633.         float level = float.MinValue;
  634.         foreach (var vent in vents)
  635.         {
  636.             if (vent.OxygenLevel > level)
  637.                 level = vent.OxygenLevel;
  638.         }
  639.         return level;
  640.     }
  641.  
  642.     public static float OxygenLevelAve(List<Vent> vents)
  643.     {
  644.         float level = 0;
  645.         foreach (var vent in vents)
  646.         {
  647.             level += vent.OxygenLevel;
  648.         }
  649.         return level / (float)vents.Count;
  650.     }
  651.  
  652.     public static bool AnyDepressurizing(List<Vent> vents)
  653.     {
  654.         foreach (var vent in vents)
  655.         {
  656.             if (vent.Depressurize)
  657.                 return true;
  658.         }
  659.         return false;
  660.     }
  661.  
  662.     public static DateTime SetDepressurize(List<Vent> vents, bool value)
  663.     {
  664.         DateTime maxDelay = DateTime.MinValue;
  665.         for (int i = 0; i < vents.Count; i++)
  666.         {
  667.             if (vents[i]?.Block == null)
  668.                 continue;
  669.  
  670.             DateTime delay = vents[i].SetDepressurize(value);
  671.  
  672.             if (delay > maxDelay)
  673.                 maxDelay = delay;
  674.         }
  675.  
  676.         return maxDelay;
  677.     }
  678.  
  679.     public static bool AllPressurized(List<Vent> vents)
  680.     {
  681.         foreach (var vent in vents)
  682.         {
  683.             if (vent.Status != VentStatus.Pressurized)
  684.                 return false;
  685.         }
  686.  
  687.         return true;
  688.     }
  689.  
  690.     public static bool AllDepressurized(List<Vent> vents)
  691.     {
  692.         foreach (var vent in vents)
  693.         {
  694.             if (vent.Status != VentStatus.Depressurized)
  695.                 return false;
  696.         }
  697.  
  698.         return true;
  699.     }
  700.  
  701.     public DateTime SetDepressurize(bool value)
  702.     {
  703.         if (Block.Depressurize != value)
  704.         {
  705.             Block.Depressurize = value;
  706.             return Delay();
  707.         }
  708.  
  709.         return DateTime.MinValue;
  710.     }
  711.  
  712.     private DateTime Delay()
  713.     {
  714.         int delay = _customDelay != 0 ? _customDelay : DELAY_MILLIS;
  715.         return DateTime.UtcNow + TimeSpan.FromMilliseconds(delay);
  716.     }
  717. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement