Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * Twinkie Industries™ Automation Presents
- * Docking Bay v1.2
- *
- *
- * 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.
- *
- * On the vent and any lights add the following to CustomData
- * [DockingBay]
- * Name=dockingbayname
- *
- * 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.
- * IsOuterDoor=true
- *
- * When you run the script using the name of the docking bay as the argument it will properly cycle the bay.
- */
- // SETTINGS ---------------------------------------------------------------------------------------------------
- // If you set a value for GRID_NAME, each block will require it in the name. This is used to prevent blocks from
- // other grids being added.
- public const string GRID_NAME = "";
- // This timeout prevents the docking bay getting stuck because the vent can't depressurize. For example, if there is
- // no free space in an oxygen tank or no oxygen tank connected the vent won't be able to remove the air.
- public const double VENT_TIMEOUT_SECONDS = 20.0;
- // Enable or disable docking bays closing on their own after a delay
- public const bool AUTO_CLOSE = true;
- // How long to delay closing docking bays
- public const double CLOSE_DELAY_SECONDS = 30.0;
- // END SETTINGS. DO NOT EDIT BELOW ----------------------------------------------------------------------------
- private Dictionary<string, DockingBay> _bays = new Dictionary<string, DockingBay>();
- private List<string> _invalidBays = new List<string>();
- public Program()
- {
- if (!string.IsNullOrWhiteSpace(Storage))
- Load();
- Refresh();
- Runtime.UpdateFrequency = UpdateFrequency.Update100;
- }
- public void Main(string argument, UpdateType updateSource)
- {
- if (_invalidBays.Count != 0)
- {
- Echo("Invalid docking bays:");
- foreach (string bay in _invalidBays)
- {
- Echo(bay);
- }
- }
- if (string.IsNullOrWhiteSpace(argument))
- {
- // Regular update loop
- bool runFast = false;
- foreach (KeyValuePair<string, DockingBay> bay in _bays)
- {
- bay.Value.Update(Echo, bay.Key);
- if (bay.Value.State != DockingBay.BayState.Idle)
- runFast = true;
- }
- Runtime.UpdateFrequency = runFast ? UpdateFrequency.Update10 : UpdateFrequency.Update100;
- }
- else if (StringUtil.Equality(argument, "refresh"))
- {
- // Refresh command
- Refresh();
- }
- else if (_bays.ContainsKey(argument.ToLowerInvariant()))
- {
- // Cycle command
- Echo($"Cycling docking bay: {argument}");
- _bays[argument.ToLowerInvariant()].Cycle();
- Save();
- Runtime.UpdateFrequency = UpdateFrequency.Update10;
- }
- else
- {
- Echo($"Unknown argument {argument}");
- }
- }
- private void Refresh()
- {
- foreach (var bay in _bays.Values)
- {
- bay.Clear();
- }
- var blocks = new List<IMyTerminalBlock>();
- GridTerminalSystem.GetBlocksOfType(blocks,
- block => StringUtil.Contains(block.CustomName, GRID_NAME) && CustomData.HasSection(block));
- foreach (var block in blocks)
- {
- CustomData data;
- if (!CustomData.TryGetData(block, Echo, out data))
- continue;
- string name = data.Name.ToLowerInvariant();
- DockingBay bay;
- if (!_bays.TryGetValue(name, out bay))
- {
- _bays[name] = bay = new DockingBay();
- }
- bay.Add(block, data);
- }
- foreach (KeyValuePair<string, DockingBay> bay in _bays)
- {
- if (!bay.Value.IsValid)
- {
- _invalidBays.Add(bay.Key);
- _bays.Remove(bay.Key);
- }
- }
- foreach (KeyValuePair<string, DockingBay> bay in _bays)
- {
- Echo($"Resettings docking bay: {bay.Key}");
- bay.Value.Reset(Echo);
- }
- Save();
- }
- public void Save()
- {
- var sb = new StringBuilder();
- sb.Append($"{_bays.Count};");
- foreach (KeyValuePair<string, DockingBay> bay in _bays)
- {
- sb.Append($"{bay.Key};");
- sb.Append($"{bay.Value.Open};");
- }
- Storage = sb.ToString();
- }
- private void Load()
- {
- string[] s = Storage.Split(';');
- int pos = 0;
- int count = int.Parse(s[pos++]);
- for (int i = 0; i < count; i++)
- {
- string name = s[pos++];
- bool isOpen = bool.Parse(s[pos++]);
- _bays.Add(name, new DockingBay(isOpen));
- }
- Storage = null;
- }
- public class CustomData
- {
- private const string DOCKING_BAY = "DockingBay";
- private const string NAME = "Name";
- private const string IS_OUTER_DOOR = "IsOuterDoor";
- public MyIni Ini { get; } = new MyIni();
- public string Name => Get(NAME).ToString();
- public bool IsOuterDoor => Get(IS_OUTER_DOOR).ToBoolean();
- public CustomData(IMyTerminalBlock block)
- {
- MyIniParseResult parseResult;
- if (!Ini.TryParse(block.CustomData, out parseResult))
- {
- throw new Exception($"Failed to parse ini {parseResult}");
- }
- Check(NAME);
- if (block is IMyDoor)
- Check(IS_OUTER_DOOR);
- }
- public static bool TryGetData(IMyTerminalBlock block, Action<string> echo, out CustomData data)
- {
- try
- {
- data = new CustomData(block);
- }
- catch (Exception e)
- {
- echo?.Invoke(e.Message);
- data = null;
- return false;
- }
- return true;
- }
- public static bool HasSection(IMyTerminalBlock block)
- {
- return MyIni.HasSection(block.CustomData, DOCKING_BAY);
- }
- public MyIniValue Get(string key) => Ini.Get(DOCKING_BAY, key);
- private void Check(string entry)
- {
- var nameResult = Get(entry);
- if (nameResult.IsEmpty)
- {
- string message = $"Can't find {entry} in CustomData";
- throw new Exception(message);
- }
- }
- }
- public class DockingBay
- {
- private DateTime _nextAction;
- public enum BayState
- {
- /// <summary> Setting doors, vents, and lights to the current bay condition </summary>
- Resetting,
- /// <summary> Nothing happening </summary>
- Idle,
- /// <summary> Waiting for the door to close </summary>
- Closing,
- /// <summary> Waiting for venting </summary>
- Venting,
- /// <summary> Waiting for the door to open </summary>
- Opening,
- /// <summary> Waiting to close the docking bay </summary>
- Waiting,
- }
- public BayState State { get; private set; }
- private static readonly BayState[] UNINTERRUPTIBLE_STATES =
- {
- BayState.Resetting,
- BayState.Closing,
- BayState.Venting,
- BayState.Opening,
- };
- public bool Open { get; private set; }
- private List<Door> OuterDoors { get; } = new List<Door>();
- private List<Door> InnerDoors { get; } = new List<Door>();
- private List<Vent> Vents { get; } = new List<Vent>();
- private List<Light> Lights { get; } = new List<Light>();
- public bool IsValid => OuterDoors.Count != 0 && Vents.Count != 0;
- public DockingBay() { }
- public DockingBay(bool open)
- {
- Open = open;
- }
- public void Update(Action<string> echo, string name)
- {
- if (State == BayState.Idle)
- {
- return;
- }
- // Un-timed updates
- bool isTimedOut = DateTime.UtcNow > _nextAction;
- if (State == BayState.Venting)
- {
- if (Open && Vent.AllDepressurized(Vents) ||
- !Open && Vent.AllPressurized(Vents) ||
- isTimedOut) // timeout
- {
- OpenDoors();
- RefreshLights();
- string bayState = Open ? "Opening" : "Unlocking";
- string condition = isTimedOut ? " (Timed Out)" : "";
- echo($"{bayState} docking bay: {name}{condition}");
- State = BayState.Opening;
- }
- return;
- }
- else if (State == BayState.Resetting)
- {
- if (Open && Vent.AllDepressurized(Vents) ||
- !Open && Vent.AllPressurized(Vents) ||
- isTimedOut)
- {
- PowerOff();
- echo($"Docking bay reset: {name}");
- State = BayState.Idle;
- }
- }
- if (DateTime.UtcNow < _nextAction)
- return;
- // Timed updates
- switch (State)
- {
- case BayState.Closing:
- ActivateVents();
- RefreshLights();
- PowerOff();
- echo($"Docking bay venting: {name}");
- State = BayState.Venting;
- break;
- case BayState.Opening:
- RefreshLights();
- PowerOff();
- string bayState = Open ? "opened" : "closed";
- echo($"Docking bay {bayState}: {name}");
- if (Open && AUTO_CLOSE)
- {
- Wait();
- echo($"Docking bay waiting to close at {_nextAction:T}");
- State = BayState.Waiting;
- break;
- }
- echo("Docking bay idle");
- State = BayState.Idle;
- break;
- case BayState.Waiting:
- Cycle();
- break;
- }
- }
- public void Cycle()
- {
- if (UNINTERRUPTIBLE_STATES.Contains(State))
- return;
- Open = !Open;
- CloseDoors();
- RefreshLights();
- State = BayState.Closing;
- }
- public void Add(IMyTerminalBlock block, CustomData data)
- {
- if (block is IMyDoor)
- {
- if (data.IsOuterDoor)
- {
- OuterDoors.Add(new Door(block as IMyDoor));
- }
- else
- {
- InnerDoors.Add(new Door(block as IMyDoor));
- }
- }
- else if (block is IMyAirVent)
- {
- Vents.Add(new Vent(block as IMyAirVent));
- }
- else if (block is IMyLightingBlock)
- {
- Lights.Add(new Light(block as IMyLightingBlock));
- }
- else
- {
- throw new Exception($"Unknown block type {block.GetType()}");
- }
- }
- public void Reset(Action<string> echo)
- {
- DateTime ventTime = TimeUtil.OffsetSeconds(VENT_TIMEOUT_SECONDS);
- if (Open)
- {
- echo("Bay is open");
- DateTime closed = Door.CloseDoors(InnerDoors);
- _nextAction = ventTime + TimeUtil.TimeSpanFromUtcNow(closed);
- }
- else
- {
- echo("Bay is closed");
- DateTime closed = Door.CloseDoors(OuterDoors);
- _nextAction = ventTime + TimeUtil.TimeSpanFromUtcNow(closed);
- }
- OpenDoors();
- Vent.SetDepressurize(Vents, Open);
- Light.SetEnabled(Lights, Open);
- State = BayState.Resetting;
- }
- public void Clear()
- {
- InnerDoors.Clear();
- OuterDoors.Clear();
- Vents.Clear();
- Lights.Clear();
- }
- private void CloseDoors()
- {
- _nextAction = Door.CloseDoors(Open ? InnerDoors : OuterDoors);
- }
- private void ActivateVents()
- {
- Vent.SetDepressurize(Vents, Open);
- _nextAction = TimeUtil.OffsetSeconds(VENT_TIMEOUT_SECONDS);
- }
- private void OpenDoors()
- {
- if (Open)
- {
- _nextAction = Door.OpenDoors(OuterDoors);
- }
- else
- {
- Door.SetEnabled(InnerDoors, true); // only enable, not open
- }
- }
- private void PowerOff()
- {
- Door.SetEnabled(OuterDoors, false);
- if (Open)
- {
- Door.SetEnabled(InnerDoors, false);
- }
- }
- private void RefreshLights()
- {
- bool turnOnLights = Vents[0].Status != VentStatus.Pressurized || Vents[0].Depressurize;
- Light.SetEnabled(Lights, turnOnLights);
- }
- private void Wait()
- {
- _nextAction = TimeUtil.OffsetSeconds(CLOSE_DELAY_SECONDS);
- }
- }
- // Mixin
- public class Door
- {
- public const double DELAY_STANDARD_MILLIS = 1.0;
- public const double DELAY_HANGAR_MILLIS = 10.0;
- private double _customDelay;
- private bool _isHangar;
- public IMyDoor Block { get; }
- public bool Enabled { get { return Block.Enabled; } set { Block.Enabled = value; } }
- public DoorStatus Status => Block.Status;
- public float OpenRatio => Block.OpenRatio;
- public bool IsOpen => Block.Status == DoorStatus.Open;
- public bool IsClosed => Block.Status == DoorStatus.Closed;
- public bool IsTransitioning => !IsOpen && !IsClosed;
- public Door(IMyDoor block, double customDelay = 0.0)
- {
- Block = block;
- _customDelay = customDelay;
- _isHangar = block is IMyAirtightHangarDoor;
- }
- public static void SetEnabled(List<Door> doors, bool value)
- {
- foreach (Door door in doors)
- {
- door.Enabled = value;
- }
- }
- public static bool AnyOpened(List<Door> doors)
- {
- foreach (var door in doors)
- {
- if (door.Status == DoorStatus.Open)
- return true;
- }
- return false;
- }
- public static bool AnyClosed(List<Door> doors)
- {
- foreach (var door in doors)
- {
- if (door.Status == DoorStatus.Closed)
- return true;
- }
- return false;
- }
- public static DateTime OpenDoors(List<Door> doors)
- {
- DateTime maxDelay = DateTime.MinValue;
- foreach (Door door in doors)
- {
- maxDelay = Max(maxDelay, door.OpenDoor());
- }
- return maxDelay;
- }
- public static DateTime CloseDoors(List<Door> doors)
- {
- DateTime maxDelay = DateTime.MinValue;
- foreach (Door door in doors)
- {
- maxDelay = Max(maxDelay, door.CloseDoor());
- }
- return maxDelay;
- }
- public DateTime OpenDoor()
- {
- if (Block.Status == DoorStatus.Open)
- return DateTime.MinValue;
- Block.Enabled = true;
- Block.OpenDoor();
- return Delay();
- }
- public DateTime CloseDoor()
- {
- if (Block.Status == DoorStatus.Closed)
- return DateTime.MinValue;
- Block.Enabled = true;
- Block.CloseDoor();
- return Delay();
- }
- public DateTime ToggleDoor()
- {
- return Block.Status == DoorStatus.Closed || Block.Status == DoorStatus.Closing
- ? OpenDoor()
- : CloseDoor();
- }
- private DateTime Delay()
- {
- double standardDelay = _isHangar ? DELAY_HANGAR_MILLIS : DELAY_STANDARD_MILLIS;
- double delay = _customDelay != 0.0 ? _customDelay : standardDelay;
- return DateTime.UtcNow + TimeSpan.FromSeconds(delay);
- }
- private static DateTime Max(DateTime a, DateTime b)
- {
- return a > b ? a : b;
- }
- }
- // Mixin
- public class Light
- {
- public static readonly Color SODIUM = new Color(255, 183, 76);
- public static readonly Color INCANDESCENT = new Color(255, 255, 210);
- public static readonly Color FLORESCENT_WARM = new Color(230, 255, 255);
- public static readonly Color FLORESCENT_COOL = new Color(175, 215, 255);
- public IMyLightingBlock Block { get; private set; }
- public bool OriginalEnabled { get; private set; }
- public float OriginalRadius { get; private set; }
- public float OriginalIntensity { get; private set; }
- public float OriginalFalloff { get; private set; }
- public float OriginalBlinkInterval { get; private set; }
- public float OriginalBlinkLength { get; private set; }
- public float OriginalBlinkOffset { get; private set; }
- public Color OriginalColor { get; private set; }
- public bool Enabled { get { return Block.Enabled;} set { Block.Enabled = value; } }
- public float Radius { get { return Block.Radius; } set { Block.Radius = value; } }
- float Intensity { get { return Block.Intensity; } set { Block.Intensity = value; } }
- float Falloff { get { return Block.Falloff; } set { Block.Falloff = value; } }
- float BlinkIntervalSeconds { get { return Block.BlinkIntervalSeconds; } set { Block.BlinkIntervalSeconds = value; } }
- float BlinkLength { get { return Block.BlinkLength; } set { Block.BlinkLength = value; } }
- float BlinkOffset { get { return Block.BlinkOffset; } set { Block.BlinkOffset = value; } }
- Color Color { get { return Block.Color; } set { Block.Color = value; } }
- public Light(IMyLightingBlock block)
- {
- Block = block;
- OriginalEnabled = Block.Enabled;
- OriginalRadius = Block.Radius;
- OriginalIntensity = Block.Intensity;
- OriginalFalloff = Block.Falloff;
- OriginalBlinkInterval = Block.BlinkIntervalSeconds;
- OriginalBlinkLength = Block.BlinkLength;
- OriginalBlinkOffset = Block.BlinkOffset;
- OriginalColor = Block.Color;
- }
- public static void SetEnabled(List<Light> lights, bool value)
- {
- foreach (var light in lights)
- {
- light.Enabled = value;
- }
- }
- public static void SetColor(List<Light> lights, Color color)
- {
- foreach (var light in lights)
- {
- light.Color = color;
- }
- }
- public void Reset()
- {
- ResetEnabled();
- ResetRadius();
- ResetIntensity();
- ResetFalloff();
- ResetBlinkInterval();
- ResetBlinkLength();
- ResetBlinkOffset();
- ResetColor();
- }
- public void ResetEnabled() => Block.Enabled = OriginalEnabled;
- public void ResetRadius() => Block.Radius = OriginalRadius;
- public void ResetIntensity() => Block.Intensity = OriginalIntensity;
- public void ResetFalloff() => Block.Falloff = OriginalFalloff;
- public void ResetBlinkInterval() => Block.BlinkIntervalSeconds = OriginalBlinkInterval;
- public void ResetBlinkLength() => Block.BlinkLength = OriginalBlinkLength;
- public void ResetBlinkOffset() => Block.BlinkOffset = OriginalBlinkOffset;
- public void ResetColor() => Block.Color = OriginalColor;
- }
- // Mixin
- public static class StringUtil
- {
- public static bool Contains(string text, string testSequence, bool useInvariant = true)
- {
- return useInvariant
- ? text.ToLowerInvariant().Contains(testSequence.ToLowerInvariant())
- : text.ToLower().Contains(testSequence.ToLower());
- }
- public static bool Equality(string a, string b, bool useInvariant = true)
- {
- return useInvariant
- ? a.ToLowerInvariant() == b.ToLowerInvariant()
- : a.ToLower() == b.ToLower();
- }
- public static bool ContainsKey<T>(Dictionary<string, T> dict, string key)
- {
- if (dict.ContainsKey(key)) return true;
- if (dict.ContainsKey(key.ToLowerInvariant())) return true;
- if (dict.ContainsKey(key.ToLower())) return true;
- return false;
- }
- public static bool TryGetValue<T>(Dictionary<string, T> dict, string key, out T value)
- {
- if (dict.TryGetValue(key, out value)) return true;
- if (dict.TryGetValue(key.ToLowerInvariant(), out value)) return true;
- if (dict.TryGetValue(key.ToLower(), out value)) return true;
- return false;
- }
- public static bool HasSection(IMyTerminalBlock block, string key)
- {
- if (MyIni.HasSection(block.CustomData, key)) return true;
- if (MyIni.HasSection(block.CustomData, key.ToLowerInvariant())) return true;
- if (MyIni.HasSection(block.CustomData, key.ToLower())) return true;
- return false;
- }
- }
- // Mixin
- public static class TimeUtil
- {
- public static DateTime OffsetSeconds(double seconds)
- {
- return DateTime.UtcNow +TimeSpan.FromSeconds(seconds);
- }
- public static TimeSpan TimeSpanFromUtcNow(DateTime time)
- {
- if (DateTime.UtcNow > time)
- return TimeSpan.Zero;
- return DateTime.UtcNow - time;
- }
- }
- // Mixin
- public class Vent
- {
- private const int DELAY_MILLIS = 2000;
- private int _customDelay;
- public IMyAirVent Block { get; }
- public bool Enabled { get { return Block.Enabled; } set { Block.Enabled = value; } }
- public bool CanPressurize => Block.CanPressurize;
- public float OxygenLevel => Block.GetOxygenLevel();
- public VentStatus Status => Block.Status;
- public bool PressurizationEnabled => Block.PressurizationEnabled;
- public bool Depressurize => Block.Depressurize;
- public bool IsPressurized => Block.Status == VentStatus.Pressurized;
- public bool IsDepressurized => Block.Status == VentStatus.Depressurized;
- public bool IsPressureUncertain => !IsPressurized && !IsDepressurized;
- public Vent(IMyAirVent block, int customDelay = 0)
- {
- Block = block;
- _customDelay = customDelay;
- }
- public static void SetEnabled(List<Door> doors, bool value)
- {
- foreach (Door door in doors)
- {
- door.Enabled = value;
- }
- }
- public static float OxygenLevelMin(List<Vent> vents)
- {
- float level = float.MaxValue;
- foreach (var vent in vents)
- {
- if (vent.OxygenLevel < level)
- level = vent.OxygenLevel;
- }
- return level;
- }
- public static float OxygenLevelMax(List<Vent> vents)
- {
- float level = float.MinValue;
- foreach (var vent in vents)
- {
- if (vent.OxygenLevel > level)
- level = vent.OxygenLevel;
- }
- return level;
- }
- public static float OxygenLevelAve(List<Vent> vents)
- {
- float level = 0f;
- foreach (var vent in vents)
- {
- level += vent.OxygenLevel;
- }
- return level / vents.Count;
- }
- public static bool AnyDepressurizing(List<Vent> vents)
- {
- foreach (var vent in vents)
- {
- if (vent.Depressurize)
- return true;
- }
- return false;
- }
- public static DateTime SetDepressurize(List<Vent> vents, bool value)
- {
- DateTime maxDelay = DateTime.MinValue;
- for (int i = 0; i < vents.Count; i++)
- {
- if (vents[i]?.Block == null)
- continue;
- maxDelay = Max(maxDelay, vents[i].SetDepressurize(value));
- }
- return maxDelay;
- }
- public static bool AllPressurized(List<Vent> vents)
- {
- foreach (var vent in vents)
- {
- if (vent.Status != VentStatus.Pressurized)
- return false;
- }
- return true;
- }
- public static bool AllDepressurized(List<Vent> vents)
- {
- foreach (var vent in vents)
- {
- if (vent.Status != VentStatus.Depressurized)
- return false;
- }
- return true;
- }
- public DateTime SetDepressurize(bool value)
- {
- if (Block.Depressurize == value)
- return DateTime.MinValue;
- Block.Depressurize = value;
- return Delay();
- }
- private DateTime Delay()
- {
- int delay = _customDelay != 0 ? _customDelay : DELAY_MILLIS;
- return DateTime.UtcNow + TimeSpan.FromMilliseconds(delay);
- }
- private static DateTime Max(DateTime a, DateTime b)
- {
- return a > b ? a : b;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment