LtTwinkie

Twinkie's Docking Bay v1.2

Jul 14th, 2023
1,126
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 23.72 KB | None | 0 0
  1. /*
  2.  * Twinkie Industries™ Automation Presents
  3.  * Docking Bay v1.2
  4.  *
  5.  *
  6.  * This script will allow you to set up docking bays with vents and lights that will operate properly with a single button press or sensor trigger.
  7.  *
  8.  * On the vent and any lights add the following to CustomData
  9.  * [DockingBay]
  10.  * Name=dockingbayname
  11.  *
  12.  * Also add it to the doors but include if the door is the outer bay door or not. Other doors will be closed and disabled while the outer doors are open.
  13.  * IsOuterDoor=true
  14.  *
  15.  * When you run the script using the name of the docking bay as the argument it will properly cycle the bay.
  16.  */
  17.  
  18. // SETTINGS ---------------------------------------------------------------------------------------------------
  19.  
  20. // If you set a value for GRID_NAME, each block will require it in the name. This is used to prevent blocks from
  21. // other grids being added.
  22. public const string GRID_NAME = "";
  23.  
  24. // This timeout prevents the docking bay getting stuck because the vent can't depressurize. For example, if there is
  25. // no free space in an oxygen tank or no oxygen tank connected the vent won't be able to remove the air.
  26. public const double VENT_TIMEOUT_SECONDS = 20.0;
  27.  
  28. // Enable or disable docking bays closing on their own after a delay
  29. public const bool AUTO_CLOSE = true;
  30.  
  31. // How long to delay closing docking bays
  32. public const double CLOSE_DELAY_SECONDS = 30.0;
  33.  
  34. // END SETTINGS. DO NOT EDIT BELOW ----------------------------------------------------------------------------
  35.  
  36. private Dictionary<string, DockingBay> _bays = new Dictionary<string, DockingBay>();
  37. private List<string> _invalidBays = new List<string>();
  38.  
  39. public Program()
  40. {
  41.     if (!string.IsNullOrWhiteSpace(Storage))
  42.         Load();
  43.  
  44.     Refresh();
  45.  
  46.     Runtime.UpdateFrequency = UpdateFrequency.Update100;
  47. }
  48.  
  49. public void Main(string argument, UpdateType updateSource)
  50. {
  51.     if (_invalidBays.Count != 0)
  52.     {
  53.         Echo("Invalid docking bays:");
  54.         foreach (string bay in _invalidBays)
  55.         {
  56.             Echo(bay);
  57.         }
  58.     }
  59.  
  60.     if (string.IsNullOrWhiteSpace(argument))
  61.     {
  62.         // Regular update loop
  63.  
  64.         bool runFast = false;
  65.         foreach (KeyValuePair<string, DockingBay> bay in _bays)
  66.         {
  67.             bay.Value.Update(Echo, bay.Key);
  68.  
  69.             if (bay.Value.State != DockingBay.BayState.Idle)
  70.                 runFast = true;
  71.         }
  72.  
  73.         Runtime.UpdateFrequency = runFast ? UpdateFrequency.Update10 : UpdateFrequency.Update100;
  74.     }
  75.     else if (StringUtil.Equality(argument, "refresh"))
  76.     {
  77.         // Refresh command
  78.         Refresh();
  79.     }
  80.     else if (_bays.ContainsKey(argument.ToLowerInvariant()))
  81.     {
  82.         // Cycle command
  83.         Echo($"Cycling docking bay: {argument}");
  84.         _bays[argument.ToLowerInvariant()].Cycle();
  85.         Save();
  86.  
  87.         Runtime.UpdateFrequency = UpdateFrequency.Update10;
  88.     }
  89.     else
  90.     {
  91.         Echo($"Unknown argument {argument}");
  92.     }
  93. }
  94.  
  95. private void Refresh()
  96. {
  97.     foreach (var bay in _bays.Values)
  98.     {
  99.         bay.Clear();
  100.     }
  101.  
  102.     var blocks = new List<IMyTerminalBlock>();
  103.     GridTerminalSystem.GetBlocksOfType(blocks,
  104.         block => StringUtil.Contains(block.CustomName, GRID_NAME) && CustomData.HasSection(block));
  105.  
  106.     foreach (var block in blocks)
  107.     {
  108.         CustomData data;
  109.         if (!CustomData.TryGetData(block, Echo, out data))
  110.             continue;
  111.  
  112.         string name = data.Name.ToLowerInvariant();
  113.  
  114.         DockingBay bay;
  115.         if (!_bays.TryGetValue(name, out bay))
  116.         {
  117.             _bays[name] = bay = new DockingBay();
  118.         }
  119.  
  120.         bay.Add(block, data);
  121.     }
  122.  
  123.     foreach (KeyValuePair<string, DockingBay> bay in _bays)
  124.     {
  125.         if (!bay.Value.IsValid)
  126.         {
  127.             _invalidBays.Add(bay.Key);
  128.             _bays.Remove(bay.Key);
  129.         }
  130.     }
  131.  
  132.     foreach (KeyValuePair<string, DockingBay> bay in _bays)
  133.     {
  134.         Echo($"Resettings docking bay: {bay.Key}");
  135.         bay.Value.Reset(Echo);
  136.     }
  137.  
  138.     Save();
  139. }
  140.  
  141. public void Save()
  142. {
  143.     var sb = new StringBuilder();
  144.     sb.Append($"{_bays.Count};");
  145.     foreach (KeyValuePair<string, DockingBay> bay in _bays)
  146.     {
  147.         sb.Append($"{bay.Key};");
  148.         sb.Append($"{bay.Value.Open};");
  149.     }
  150.     Storage = sb.ToString();
  151. }
  152.  
  153. private void Load()
  154. {
  155.     string[] s = Storage.Split(';');
  156.     int pos = 0;
  157.  
  158.     int count = int.Parse(s[pos++]);
  159.     for (int i = 0; i < count; i++)
  160.     {
  161.         string name = s[pos++];
  162.         bool isOpen = bool.Parse(s[pos++]);
  163.  
  164.         _bays.Add(name, new DockingBay(isOpen));
  165.     }
  166.  
  167.     Storage = null;
  168. }
  169.  
  170. public class CustomData
  171. {
  172.     private const string DOCKING_BAY = "DockingBay";
  173.     private const string NAME = "Name";
  174.     private const string IS_OUTER_DOOR = "IsOuterDoor";
  175.  
  176.     public MyIni Ini { get; } = new MyIni();
  177.  
  178.     public string Name => Get(NAME).ToString();
  179.     public bool IsOuterDoor => Get(IS_OUTER_DOOR).ToBoolean();
  180.  
  181.     public CustomData(IMyTerminalBlock block)
  182.     {
  183.         MyIniParseResult parseResult;
  184.         if (!Ini.TryParse(block.CustomData, out parseResult))
  185.         {
  186.             throw new Exception($"Failed to parse ini {parseResult}");
  187.         }
  188.  
  189.         Check(NAME);
  190.  
  191.         if (block is IMyDoor)
  192.             Check(IS_OUTER_DOOR);
  193.     }
  194.  
  195.     public static bool TryGetData(IMyTerminalBlock block, Action<string> echo, out CustomData data)
  196.     {
  197.         try
  198.         {
  199.             data = new CustomData(block);
  200.         }
  201.         catch (Exception e)
  202.         {
  203.             echo?.Invoke(e.Message);
  204.             data = null;
  205.             return false;
  206.         }
  207.  
  208.         return true;
  209.     }
  210.  
  211.     public static bool HasSection(IMyTerminalBlock block)
  212.     {
  213.         return MyIni.HasSection(block.CustomData, DOCKING_BAY);
  214.     }
  215.  
  216.     public MyIniValue Get(string key) => Ini.Get(DOCKING_BAY, key);
  217.  
  218.     private void Check(string entry)
  219.     {
  220.         var nameResult = Get(entry);
  221.         if (nameResult.IsEmpty)
  222.         {
  223.             string message = $"Can't find {entry} in CustomData";
  224.             throw new Exception(message);
  225.         }
  226.     }
  227. }
  228.  
  229. public class DockingBay
  230. {
  231.     private DateTime _nextAction;
  232.  
  233.     public enum BayState
  234.     {
  235.         /// <summary> Setting doors, vents, and lights to the current bay condition </summary>
  236.         Resetting,
  237.  
  238.         /// <summary> Nothing happening </summary>
  239.         Idle,
  240.  
  241.         /// <summary> Waiting for the door to close </summary>
  242.         Closing,
  243.  
  244.         /// <summary> Waiting for venting </summary>
  245.         Venting,
  246.  
  247.         /// <summary> Waiting for the door to open </summary>
  248.         Opening,
  249.  
  250.         /// <summary> Waiting to close the docking bay </summary>
  251.         Waiting,
  252.     }
  253.  
  254.     public BayState State { get; private set; }
  255.  
  256.     private static readonly BayState[] UNINTERRUPTIBLE_STATES =
  257.     {
  258.         BayState.Resetting,
  259.         BayState.Closing,
  260.         BayState.Venting,
  261.         BayState.Opening,
  262.     };
  263.  
  264.     public bool Open { get; private set; }
  265.     private List<Door> OuterDoors { get; } = new List<Door>();
  266.     private List<Door> InnerDoors { get; } = new List<Door>();
  267.     private List<Vent> Vents { get; } = new List<Vent>();
  268.     private List<Light> Lights { get; } = new List<Light>();
  269.  
  270.     public bool IsValid => OuterDoors.Count != 0 && Vents.Count != 0;
  271.  
  272.     public DockingBay() { }
  273.  
  274.     public DockingBay(bool open)
  275.     {
  276.         Open = open;
  277.     }
  278.  
  279.     public void Update(Action<string> echo, string name)
  280.     {
  281.         if (State == BayState.Idle)
  282.         {
  283.             return;
  284.         }
  285.  
  286.         // Un-timed updates
  287.         bool isTimedOut = DateTime.UtcNow > _nextAction;
  288.         if (State == BayState.Venting)
  289.         {
  290.             if (Open && Vent.AllDepressurized(Vents) ||
  291.                 !Open && Vent.AllPressurized(Vents) ||
  292.                 isTimedOut) // timeout
  293.             {
  294.                 OpenDoors();
  295.                 RefreshLights();
  296.  
  297.                 string bayState = Open ? "Opening" : "Unlocking";
  298.                 string condition = isTimedOut ? " (Timed Out)" : "";
  299.                 echo($"{bayState} docking bay: {name}{condition}");
  300.  
  301.                 State = BayState.Opening;
  302.             }
  303.  
  304.             return;
  305.         }
  306.         else if (State == BayState.Resetting)
  307.         {
  308.             if (Open && Vent.AllDepressurized(Vents) ||
  309.                 !Open && Vent.AllPressurized(Vents) ||
  310.                 isTimedOut)
  311.             {
  312.                 PowerOff();
  313.                 echo($"Docking bay reset: {name}");
  314.                 State = BayState.Idle;
  315.             }
  316.         }
  317.  
  318.         if (DateTime.UtcNow < _nextAction)
  319.             return;
  320.  
  321.         // Timed updates
  322.         switch (State)
  323.         {
  324.             case BayState.Closing:
  325.                 ActivateVents();
  326.                 RefreshLights();
  327.                 PowerOff();
  328.                 echo($"Docking bay venting: {name}");
  329.                 State = BayState.Venting;
  330.                 break;
  331.  
  332.             case BayState.Opening:
  333.                 RefreshLights();
  334.                 PowerOff();
  335.                 string bayState = Open ? "opened" : "closed";
  336.                 echo($"Docking bay {bayState}: {name}");
  337.                 if (Open && AUTO_CLOSE)
  338.                 {
  339.                     Wait();
  340.                     echo($"Docking bay waiting to close at {_nextAction:T}");
  341.                     State = BayState.Waiting;
  342.                     break;
  343.                 }
  344.                 echo("Docking bay idle");
  345.                 State = BayState.Idle;
  346.                 break;
  347.  
  348.             case BayState.Waiting:
  349.                 Cycle();
  350.                 break;
  351.         }
  352.     }
  353.  
  354.     public void Cycle()
  355.     {
  356.         if (UNINTERRUPTIBLE_STATES.Contains(State))
  357.             return;
  358.  
  359.         Open = !Open;
  360.  
  361.         CloseDoors();
  362.         RefreshLights();
  363.         State = BayState.Closing;
  364.     }
  365.  
  366.     public void Add(IMyTerminalBlock block, CustomData data)
  367.     {
  368.         if (block is IMyDoor)
  369.         {
  370.             if (data.IsOuterDoor)
  371.             {
  372.                 OuterDoors.Add(new Door(block as IMyDoor));
  373.             }
  374.             else
  375.             {
  376.                 InnerDoors.Add(new Door(block as IMyDoor));
  377.             }
  378.         }
  379.         else if (block is IMyAirVent)
  380.         {
  381.             Vents.Add(new Vent(block as IMyAirVent));
  382.         }
  383.         else if (block is IMyLightingBlock)
  384.         {
  385.             Lights.Add(new Light(block as IMyLightingBlock));
  386.         }
  387.         else
  388.         {
  389.             throw new Exception($"Unknown block type {block.GetType()}");
  390.         }
  391.     }
  392.  
  393.     public void Reset(Action<string> echo)
  394.     {
  395.         DateTime ventTime = TimeUtil.OffsetSeconds(VENT_TIMEOUT_SECONDS);
  396.         if (Open)
  397.         {
  398.             echo("Bay is open");
  399.             DateTime closed = Door.CloseDoors(InnerDoors);
  400.             _nextAction = ventTime + TimeUtil.TimeSpanFromUtcNow(closed);
  401.         }
  402.         else
  403.         {
  404.             echo("Bay is closed");
  405.             DateTime closed = Door.CloseDoors(OuterDoors);
  406.             _nextAction = ventTime + TimeUtil.TimeSpanFromUtcNow(closed);
  407.         }
  408.         OpenDoors();
  409.         Vent.SetDepressurize(Vents, Open);
  410.         Light.SetEnabled(Lights, Open);
  411.         State = BayState.Resetting;
  412.     }
  413.  
  414.     public void Clear()
  415.     {
  416.         InnerDoors.Clear();
  417.         OuterDoors.Clear();
  418.         Vents.Clear();
  419.         Lights.Clear();
  420.     }
  421.  
  422.     private void CloseDoors()
  423.     {
  424.         _nextAction = Door.CloseDoors(Open ? InnerDoors : OuterDoors);
  425.     }
  426.  
  427.     private void ActivateVents()
  428.     {
  429.         Vent.SetDepressurize(Vents, Open);
  430.         _nextAction = TimeUtil.OffsetSeconds(VENT_TIMEOUT_SECONDS);
  431.     }
  432.  
  433.     private void OpenDoors()
  434.     {
  435.         if (Open)
  436.         {
  437.             _nextAction = Door.OpenDoors(OuterDoors);
  438.         }
  439.         else
  440.         {
  441.             Door.SetEnabled(InnerDoors, true); // only enable, not open
  442.         }
  443.     }
  444.  
  445.     private void PowerOff()
  446.     {
  447.         Door.SetEnabled(OuterDoors, false);
  448.  
  449.         if (Open)
  450.         {
  451.             Door.SetEnabled(InnerDoors, false);
  452.         }
  453.     }
  454.  
  455.     private void RefreshLights()
  456.     {
  457.         bool turnOnLights = Vents[0].Status != VentStatus.Pressurized || Vents[0].Depressurize;
  458.         Light.SetEnabled(Lights, turnOnLights);
  459.     }
  460.  
  461.     private void Wait()
  462.     {
  463.         _nextAction = TimeUtil.OffsetSeconds(CLOSE_DELAY_SECONDS);
  464.     }
  465. }
  466.  
  467. // Mixin
  468. public class Door
  469. {
  470.     public const double DELAY_STANDARD_MILLIS = 1.0;
  471.     public const double DELAY_HANGAR_MILLIS = 10.0;
  472.     private double _customDelay;
  473.  
  474.     private bool _isHangar;
  475.  
  476.     public IMyDoor Block { get; }
  477.  
  478.     public bool Enabled { get { return Block.Enabled; } set { Block.Enabled = value; } }
  479.     public DoorStatus Status => Block.Status;
  480.     public float OpenRatio => Block.OpenRatio;
  481.     public bool IsOpen => Block.Status == DoorStatus.Open;
  482.     public bool IsClosed => Block.Status == DoorStatus.Closed;
  483.     public bool IsTransitioning => !IsOpen && !IsClosed;
  484.  
  485.     public Door(IMyDoor block, double customDelay = 0.0)
  486.     {
  487.         Block = block;
  488.         _customDelay = customDelay;
  489.         _isHangar = block is IMyAirtightHangarDoor;
  490.     }
  491.  
  492.     public static void SetEnabled(List<Door> doors, bool value)
  493.     {
  494.         foreach (Door door in doors)
  495.         {
  496.             door.Enabled = value;
  497.         }
  498.     }
  499.  
  500.     public static bool AnyOpened(List<Door> doors)
  501.     {
  502.         foreach (var door in doors)
  503.         {
  504.             if (door.Status == DoorStatus.Open)
  505.                 return true;
  506.         }
  507.         return false;
  508.     }
  509.  
  510.     public static bool AnyClosed(List<Door> doors)
  511.     {
  512.         foreach (var door in doors)
  513.         {
  514.             if (door.Status == DoorStatus.Closed)
  515.                 return true;
  516.         }
  517.         return false;
  518.     }
  519.  
  520.     public static DateTime OpenDoors(List<Door> doors)
  521.     {
  522.         DateTime maxDelay = DateTime.MinValue;
  523.         foreach (Door door in doors)
  524.         {
  525.             maxDelay = Max(maxDelay, door.OpenDoor());
  526.         }
  527.  
  528.         return maxDelay;
  529.     }
  530.  
  531.     public static DateTime CloseDoors(List<Door> doors)
  532.     {
  533.         DateTime maxDelay = DateTime.MinValue;
  534.         foreach (Door door in doors)
  535.         {
  536.             maxDelay = Max(maxDelay, door.CloseDoor());
  537.         }
  538.  
  539.         return maxDelay;
  540.     }
  541.  
  542.     public DateTime OpenDoor()
  543.     {
  544.         if (Block.Status == DoorStatus.Open)
  545.             return DateTime.MinValue;
  546.  
  547.         Block.Enabled = true;
  548.         Block.OpenDoor();
  549.         return Delay();
  550.  
  551.     }
  552.  
  553.     public DateTime CloseDoor()
  554.     {
  555.         if (Block.Status == DoorStatus.Closed)
  556.             return DateTime.MinValue;
  557.  
  558.         Block.Enabled = true;
  559.         Block.CloseDoor();
  560.         return Delay();
  561.  
  562.     }
  563.  
  564.     public DateTime ToggleDoor()
  565.     {
  566.         return Block.Status == DoorStatus.Closed || Block.Status == DoorStatus.Closing
  567.                 ? OpenDoor()
  568.                 : CloseDoor();
  569.     }
  570.  
  571.     private DateTime Delay()
  572.     {
  573.         double standardDelay = _isHangar ? DELAY_HANGAR_MILLIS : DELAY_STANDARD_MILLIS;
  574.         double delay = _customDelay != 0.0 ? _customDelay : standardDelay;
  575.         return DateTime.UtcNow + TimeSpan.FromSeconds(delay);
  576.     }
  577.  
  578.     private static DateTime Max(DateTime a, DateTime b)
  579.     {
  580.         return a > b ? a : b;
  581.     }
  582. }
  583.  
  584. // Mixin
  585. public class Light
  586. {
  587.     public static readonly Color SODIUM = new Color(255, 183, 76);
  588.     public static readonly Color INCANDESCENT = new Color(255, 255, 210);
  589.     public static readonly Color FLORESCENT_WARM = new Color(230, 255, 255);
  590.     public static readonly Color FLORESCENT_COOL = new Color(175, 215, 255);
  591.  
  592.     public IMyLightingBlock Block { get; private set; }
  593.  
  594.     public bool OriginalEnabled { get; private set; }
  595.     public float OriginalRadius { get; private set; }
  596.     public float OriginalIntensity { get; private set; }
  597.     public float OriginalFalloff { get; private set; }
  598.     public float OriginalBlinkInterval { get; private set; }
  599.     public float OriginalBlinkLength { get; private set; }
  600.     public float OriginalBlinkOffset { get; private set; }
  601.     public Color OriginalColor { get; private set; }
  602.  
  603.     public bool Enabled { get { return Block.Enabled;} set { Block.Enabled = value; } }
  604.     public float Radius { get { return Block.Radius; } set { Block.Radius = value; } }
  605.     float Intensity { get { return Block.Intensity; } set { Block.Intensity = value; } }
  606.     float Falloff { get { return Block.Falloff; } set { Block.Falloff = value; } }
  607.     float BlinkIntervalSeconds { get { return Block.BlinkIntervalSeconds; } set { Block.BlinkIntervalSeconds = value; } }
  608.     float BlinkLength { get { return Block.BlinkLength; } set { Block.BlinkLength = value; } }
  609.     float BlinkOffset { get { return Block.BlinkOffset; } set { Block.BlinkOffset = value; } }
  610.     Color Color { get { return Block.Color; } set { Block.Color = value; } }
  611.  
  612.     public Light(IMyLightingBlock block)
  613.     {
  614.         Block = block;
  615.         OriginalEnabled = Block.Enabled;
  616.         OriginalRadius = Block.Radius;
  617.         OriginalIntensity = Block.Intensity;
  618.         OriginalFalloff = Block.Falloff;
  619.         OriginalBlinkInterval = Block.BlinkIntervalSeconds;
  620.         OriginalBlinkLength = Block.BlinkLength;
  621.         OriginalBlinkOffset = Block.BlinkOffset;
  622.         OriginalColor = Block.Color;
  623.     }
  624.  
  625.     public static void SetEnabled(List<Light> lights, bool value)
  626.     {
  627.         foreach (var light in lights)
  628.         {
  629.             light.Enabled = value;
  630.         }
  631.     }
  632.  
  633.     public static void SetColor(List<Light> lights, Color color)
  634.     {
  635.         foreach (var light in lights)
  636.         {
  637.             light.Color = color;
  638.         }
  639.     }
  640.  
  641.     public void Reset()
  642.     {
  643.         ResetEnabled();
  644.         ResetRadius();
  645.         ResetIntensity();
  646.         ResetFalloff();
  647.         ResetBlinkInterval();
  648.         ResetBlinkLength();
  649.         ResetBlinkOffset();
  650.         ResetColor();
  651.     }
  652.  
  653.     public void ResetEnabled() => Block.Enabled = OriginalEnabled;
  654.     public void ResetRadius() => Block.Radius = OriginalRadius;
  655.     public void ResetIntensity() => Block.Intensity = OriginalIntensity;
  656.     public void ResetFalloff() => Block.Falloff = OriginalFalloff;
  657.     public void ResetBlinkInterval() => Block.BlinkIntervalSeconds = OriginalBlinkInterval;
  658.     public void ResetBlinkLength() => Block.BlinkLength = OriginalBlinkLength;
  659.     public void ResetBlinkOffset() => Block.BlinkOffset = OriginalBlinkOffset;
  660.     public void ResetColor() => Block.Color = OriginalColor;
  661. }
  662.  
  663. // Mixin
  664. public static class StringUtil
  665. {
  666.     public static bool Contains(string text, string testSequence, bool useInvariant = true)
  667.     {
  668.         return useInvariant
  669.             ? text.ToLowerInvariant().Contains(testSequence.ToLowerInvariant())
  670.             : text.ToLower().Contains(testSequence.ToLower());
  671.     }
  672.  
  673.     public static bool Equality(string a, string b, bool useInvariant = true)
  674.     {
  675.         return useInvariant
  676.             ? a.ToLowerInvariant() == b.ToLowerInvariant()
  677.             : a.ToLower() == b.ToLower();
  678.     }
  679.  
  680.     public static bool ContainsKey<T>(Dictionary<string, T> dict, string key)
  681.     {
  682.         if (dict.ContainsKey(key)) return true;
  683.         if (dict.ContainsKey(key.ToLowerInvariant())) return true;
  684.         if (dict.ContainsKey(key.ToLower())) return true;
  685.  
  686.         return false;
  687.     }
  688.  
  689.     public static bool TryGetValue<T>(Dictionary<string, T> dict, string key, out T value)
  690.     {
  691.         if (dict.TryGetValue(key, out value)) return true;
  692.         if (dict.TryGetValue(key.ToLowerInvariant(), out value)) return true;
  693.         if (dict.TryGetValue(key.ToLower(), out value)) return true;
  694.  
  695.         return false;
  696.     }
  697.  
  698.     public static bool HasSection(IMyTerminalBlock block, string key)
  699.     {
  700.         if (MyIni.HasSection(block.CustomData, key)) return true;
  701.         if (MyIni.HasSection(block.CustomData, key.ToLowerInvariant())) return true;
  702.         if (MyIni.HasSection(block.CustomData, key.ToLower())) return true;
  703.  
  704.         return false;
  705.     }
  706. }
  707.  
  708. // Mixin
  709. public static class TimeUtil
  710. {
  711.     public static DateTime OffsetSeconds(double seconds)
  712.     {
  713.         return DateTime.UtcNow +TimeSpan.FromSeconds(seconds);
  714.     }
  715.  
  716.     public static TimeSpan TimeSpanFromUtcNow(DateTime time)
  717.     {
  718.         if (DateTime.UtcNow > time)
  719.             return TimeSpan.Zero;
  720.  
  721.         return DateTime.UtcNow - time;
  722.     }
  723. }
  724.  
  725. // Mixin
  726. public class Vent
  727. {
  728.     private const int DELAY_MILLIS = 2000;
  729.  
  730.     private int _customDelay;
  731.  
  732.     public IMyAirVent Block { get; }
  733.  
  734.     public bool Enabled { get { return Block.Enabled; } set { Block.Enabled = value; } }
  735.     public bool CanPressurize => Block.CanPressurize;
  736.     public float OxygenLevel => Block.GetOxygenLevel();
  737.     public VentStatus Status => Block.Status;
  738.     public bool PressurizationEnabled => Block.PressurizationEnabled;
  739.  
  740.     public bool Depressurize => Block.Depressurize;
  741.  
  742.     public bool IsPressurized => Block.Status == VentStatus.Pressurized;
  743.     public bool IsDepressurized => Block.Status == VentStatus.Depressurized;
  744.     public bool IsPressureUncertain => !IsPressurized && !IsDepressurized;
  745.  
  746.     public Vent(IMyAirVent block, int customDelay = 0)
  747.     {
  748.         Block = block;
  749.         _customDelay = customDelay;
  750.     }
  751.  
  752.     public static void SetEnabled(List<Door> doors, bool value)
  753.     {
  754.         foreach (Door door in doors)
  755.         {
  756.             door.Enabled = value;
  757.         }
  758.     }
  759.  
  760.     public static float OxygenLevelMin(List<Vent> vents)
  761.     {
  762.         float level = float.MaxValue;
  763.         foreach (var vent in vents)
  764.         {
  765.             if (vent.OxygenLevel < level)
  766.                 level = vent.OxygenLevel;
  767.         }
  768.         return level;
  769.     }
  770.  
  771.     public static float OxygenLevelMax(List<Vent> vents)
  772.     {
  773.         float level = float.MinValue;
  774.         foreach (var vent in vents)
  775.         {
  776.             if (vent.OxygenLevel > level)
  777.                 level = vent.OxygenLevel;
  778.         }
  779.         return level;
  780.     }
  781.  
  782.     public static float OxygenLevelAve(List<Vent> vents)
  783.     {
  784.         float level = 0f;
  785.         foreach (var vent in vents)
  786.         {
  787.             level += vent.OxygenLevel;
  788.         }
  789.         return level / vents.Count;
  790.     }
  791.  
  792.     public static bool AnyDepressurizing(List<Vent> vents)
  793.     {
  794.         foreach (var vent in vents)
  795.         {
  796.             if (vent.Depressurize)
  797.                 return true;
  798.         }
  799.         return false;
  800.     }
  801.  
  802.     public static DateTime SetDepressurize(List<Vent> vents, bool value)
  803.     {
  804.         DateTime maxDelay = DateTime.MinValue;
  805.         for (int i = 0; i < vents.Count; i++)
  806.         {
  807.             if (vents[i]?.Block == null)
  808.                 continue;
  809.  
  810.             maxDelay = Max(maxDelay, vents[i].SetDepressurize(value));
  811.         }
  812.  
  813.         return maxDelay;
  814.     }
  815.  
  816.     public static bool AllPressurized(List<Vent> vents)
  817.     {
  818.         foreach (var vent in vents)
  819.         {
  820.             if (vent.Status != VentStatus.Pressurized)
  821.                 return false;
  822.         }
  823.  
  824.         return true;
  825.     }
  826.  
  827.     public static bool AllDepressurized(List<Vent> vents)
  828.     {
  829.         foreach (var vent in vents)
  830.         {
  831.             if (vent.Status != VentStatus.Depressurized)
  832.                 return false;
  833.         }
  834.  
  835.         return true;
  836.     }
  837.  
  838.     public DateTime SetDepressurize(bool value)
  839.     {
  840.         if (Block.Depressurize == value)
  841.             return DateTime.MinValue;
  842.  
  843.         Block.Depressurize = value;
  844.         return Delay();
  845.  
  846.     }
  847.  
  848.     private DateTime Delay()
  849.     {
  850.         int delay = _customDelay != 0 ? _customDelay : DELAY_MILLIS;
  851.         return DateTime.UtcNow + TimeSpan.FromMilliseconds(delay);
  852.     }
  853.  
  854.     private static DateTime Max(DateTime a, DateTime b)
  855.     {
  856.         return a > b ? a : b;
  857.     }
  858. }
Advertisement
Add Comment
Please, Sign In to add comment