Convert airlock into a multiplex controller and use MDK.
This commit is contained in:
parent
d2952a3111
commit
446aa29742
305
Airlock/Airlock.cs
Normal file
305
Airlock/Airlock.cs
Normal file
|
@ -0,0 +1,305 @@
|
||||||
|
using Sandbox.ModAPI.Ingame;
|
||||||
|
using SpaceEngineers.Game.ModAPI.Ingame;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using VRageMath;
|
||||||
|
|
||||||
|
namespace IngameScript
|
||||||
|
{
|
||||||
|
public class Airlock
|
||||||
|
{
|
||||||
|
enum AirlockLightState
|
||||||
|
{
|
||||||
|
Off,
|
||||||
|
Cycling,
|
||||||
|
Cooldown,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DoorsClosed
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return innerDoor.Status == DoorStatus.Closed && outerDoor.Status == DoorStatus.Closed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this should check for depressurizing to the *reference* amount
|
||||||
|
public bool PressureStabilized
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ((airVent.Depressurize && airVent.GetOxygenLevel() <= targetOxygenLevel) ||
|
||||||
|
(!airVent.Depressurize && airVent.Status == VentStatus.Pressurized));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OxygenBalanced
|
||||||
|
{
|
||||||
|
get { return oxygenTank.FilledRatio > 0.25; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Functional { get; private set; } = true;
|
||||||
|
|
||||||
|
public bool Cycling { get; private set; } = false;
|
||||||
|
|
||||||
|
private bool DoorOpened
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
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; }
|
||||||
|
|
||||||
|
_p.Echo("DEBUG: Balancing Oxygen Tank");
|
||||||
|
|
||||||
|
// Configure the vent to suck in Oxygen.
|
||||||
|
airVent.Depressurize = true;
|
||||||
|
airVent.Enabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _name;
|
||||||
|
private MyGridProgram _p;
|
||||||
|
private float targetOxygenLevel = 0.0F;
|
||||||
|
|
||||||
|
private IMyDoor innerDoor;
|
||||||
|
private IMyDoor outerDoor;
|
||||||
|
private List<IMyLightingBlock> lights;
|
||||||
|
private IMyGasTank oxygenTank;
|
||||||
|
private IMyAirVent airVent;
|
||||||
|
private IMyAirVent airSensor;
|
||||||
|
private const int CooldownTicks = 120;
|
||||||
|
|
||||||
|
public Airlock(MyGridProgram p, string name)
|
||||||
|
{
|
||||||
|
_p = p;
|
||||||
|
_name = name;
|
||||||
|
|
||||||
|
// Find the appropriate blocks given the airlock name
|
||||||
|
initDoors();
|
||||||
|
initLights();
|
||||||
|
initVents();
|
||||||
|
initOxygen();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDoors()
|
||||||
|
{
|
||||||
|
List<IMyDoor> onlyDoors = new List<IMyDoor>();
|
||||||
|
_p.GridTerminalSystem.GetBlocksOfType(onlyDoors);
|
||||||
|
foreach (IMyDoor door in onlyDoors)
|
||||||
|
{
|
||||||
|
if (!door.CustomName.StartsWith(_name)) continue;
|
||||||
|
|
||||||
|
if (door.CustomName.Contains("Inner"))
|
||||||
|
{
|
||||||
|
innerDoor = door;
|
||||||
|
}
|
||||||
|
else if (door.CustomName.Contains("Outer"))
|
||||||
|
{
|
||||||
|
outerDoor = door;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (innerDoor != null && outerDoor != null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Functional = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initVents()
|
||||||
|
{
|
||||||
|
List<IMyAirVent> onlyFans = new List<IMyAirVent>();
|
||||||
|
_p.GridTerminalSystem.GetBlocksOfType(onlyFans);
|
||||||
|
foreach (IMyAirVent vent in onlyFans)
|
||||||
|
{
|
||||||
|
if (!vent.CustomName.StartsWith(_name)) continue;
|
||||||
|
|
||||||
|
if (vent.CustomName.StartsWith(_name))
|
||||||
|
{
|
||||||
|
if (vent.CustomName.Contains("Main"))
|
||||||
|
{
|
||||||
|
airVent = vent;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (vent.CustomName.Contains("Reference"))
|
||||||
|
{
|
||||||
|
airSensor = vent;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (airVent == null) Functional = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initLights()
|
||||||
|
{
|
||||||
|
lights = new List<IMyLightingBlock>();
|
||||||
|
List<IMyLightingBlock> allLights = new List<IMyLightingBlock>();
|
||||||
|
_p.GridTerminalSystem.GetBlocksOfType(allLights);
|
||||||
|
foreach (IMyLightingBlock light in allLights)
|
||||||
|
{
|
||||||
|
if (!light.CustomName.StartsWith(_name)) continue;
|
||||||
|
lights.Add(light);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initOxygen()
|
||||||
|
{
|
||||||
|
List<IMyGasTank> allTanks = new List<IMyGasTank>();
|
||||||
|
_p.GridTerminalSystem.GetBlocksOfType(allTanks);
|
||||||
|
foreach (IMyGasTank tank in allTanks)
|
||||||
|
{
|
||||||
|
if (!tank.CustomName.StartsWith(_name)) continue;
|
||||||
|
oxygenTank = tank;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Functional = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<bool> CycleAirlock()
|
||||||
|
{
|
||||||
|
Cycling = true;
|
||||||
|
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
|
||||||
|
int cooldown = 0;
|
||||||
|
while(cooldown < CooldownTicks) {
|
||||||
|
cooldown++;
|
||||||
|
yield return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLights(AirlockLightState.Off);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeDoors()
|
||||||
|
{
|
||||||
|
_p.Echo("DEBUG: Closing Doors");
|
||||||
|
|
||||||
|
// close the doors
|
||||||
|
innerDoor.Enabled = true;
|
||||||
|
outerDoor.Enabled = true;
|
||||||
|
innerDoor.CloseDoor();
|
||||||
|
outerDoor.CloseDoor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pressurizeDepressurize()
|
||||||
|
{
|
||||||
|
_p.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();
|
||||||
|
_p.Echo($"Set depressurization target to {targetOxygenLevel}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the appropriate door based on pressure state.
|
||||||
|
private void openDoor()
|
||||||
|
{
|
||||||
|
_p.Echo("DEBUG: Opening Door");
|
||||||
|
|
||||||
|
if (airVent.Status == VentStatus.Pressurized)
|
||||||
|
{
|
||||||
|
innerDoor.Enabled = true;
|
||||||
|
innerDoor.OpenDoor();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
outerDoor.Enabled = true;
|
||||||
|
outerDoor.OpenDoor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
_p.Echo(error);
|
||||||
|
setLights(AirlockLightState.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
Airlock/Airlock.csproj
Normal file
26
Airlock/Airlock.csproj
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netframework48</TargetFramework>
|
||||||
|
<RootNamespace>IngameScript</RootNamespace>
|
||||||
|
<LangVersion>6</LangVersion>
|
||||||
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
|
<Configurations>Release;Debug</Configurations>
|
||||||
|
<Platforms>x64</Platforms>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Mal.Mdk2.PbAnalyzers" Version="2.1.11">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Mal.Mdk2.PbPackager" Version="2.1.1">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Mal.Mdk2.References" Version="2.2.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="Instructions.readme" />
|
||||||
|
<AdditionalFiles Include="Instructions.readme" />
|
||||||
|
<AdditionalFiles Include="thumb.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
22
Airlock/Airlock.mdk.ini
Normal file
22
Airlock/Airlock.mdk.ini
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
; This file is project specific and should be checked in to source control.
|
||||||
|
|
||||||
|
[mdk]
|
||||||
|
; This is a programmable block script project.
|
||||||
|
; You should not change this.
|
||||||
|
type=programmableblock
|
||||||
|
|
||||||
|
; Toggle trace (on|off) (verbose output)
|
||||||
|
trace=off
|
||||||
|
|
||||||
|
; What type of minification to use (none|trim|stripcomments|lite|full)
|
||||||
|
; none: No minification
|
||||||
|
; trim: Removes unused types (NOT members).
|
||||||
|
; stripcomments: trim + removes comments.
|
||||||
|
; lite: stripcomments + removes leading/trailing whitespace.
|
||||||
|
; full: lite + renames identifiers to shorter names.
|
||||||
|
minify=none
|
||||||
|
|
||||||
|
; A list of files and folder to ignore when creating the script.
|
||||||
|
; This is a comma separated list of glob patterns.
|
||||||
|
; See https://code.visualstudio.com/docs/editor/glob-patterns
|
||||||
|
ignores=obj/**/*,MDK/**/*,**/*.debug.cs
|
7
Airlock/Airlock.mdk.local.ini
Normal file
7
Airlock/Airlock.mdk.local.ini
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
; This file is _local_ to your machine and should not be checked in to source control.
|
||||||
|
|
||||||
|
[mdk]
|
||||||
|
; Where to output the script to (auto|specific path)
|
||||||
|
output=auto
|
||||||
|
; Override the default binary path (auto|specific path)
|
||||||
|
binarypath=auto
|
25
Airlock/Airlock.sln
Normal file
25
Airlock/Airlock.sln
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.5.002.0
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Airlock", "Airlock.csproj", "{B6B9BE35-0DAE-4066-AAE0-699803B5EFDB}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{B6B9BE35-0DAE-4066-AAE0-699803B5EFDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B6B9BE35-0DAE-4066-AAE0-699803B5EFDB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B6B9BE35-0DAE-4066-AAE0-699803B5EFDB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B6B9BE35-0DAE-4066-AAE0-699803B5EFDB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {9AE24D08-32D5-4BAA-819E-BC436D40C89D}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
12
Airlock/Instructions.readme
Normal file
12
Airlock/Instructions.readme
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
A script for controlling airlocks, designed so that a single Programmable Block can handle
|
||||||
|
every airlock on the grid.
|
||||||
|
|
||||||
|
Airlocks are configured via block naming. Each airlock block should have a name starting with
|
||||||
|
"AirlockX Function", where "X" is an arbitrary identifier and "Function" specifies the specific
|
||||||
|
function for that block.
|
||||||
|
|
||||||
|
Possible functions vary by block type:
|
||||||
|
|
||||||
|
Doors should be named "Inner" for the pressurized side of the door, and "Outer" for the vacuum side.
|
||||||
|
Vents are "Main" (for the vent that actually (de)pressurizes the lock) and "Reference" (optional, see below).
|
||||||
|
Lights and Oxygen tanks do not require function specification.
|
92
Airlock/Program.cs
Normal file
92
Airlock/Program.cs
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
using Sandbox.Game.EntityComponents;
|
||||||
|
using Sandbox.ModAPI.Ingame;
|
||||||
|
using Sandbox.ModAPI.Interfaces;
|
||||||
|
using SpaceEngineers.Game.ModAPI.Ingame;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using VRage;
|
||||||
|
using VRage.Collections;
|
||||||
|
using VRage.Game;
|
||||||
|
using VRage.Game.Components;
|
||||||
|
using VRage.Game.GUI.TextPanel;
|
||||||
|
using VRage.Game.ModAPI.Ingame;
|
||||||
|
using VRage.Game.ModAPI.Ingame.Utilities;
|
||||||
|
using VRage.Game.ObjectBuilders.Definitions;
|
||||||
|
using VRageMath;
|
||||||
|
|
||||||
|
namespace IngameScript
|
||||||
|
{
|
||||||
|
public partial class Program : MyGridProgram
|
||||||
|
{
|
||||||
|
private Dictionary<string, Airlock> _airlocks;
|
||||||
|
private List<IEnumerator<bool>> _jobs;
|
||||||
|
|
||||||
|
private int _tickCount = 0;
|
||||||
|
private MyCommandLine _cli;
|
||||||
|
|
||||||
|
public Program()
|
||||||
|
{
|
||||||
|
_cli = new MyCommandLine();
|
||||||
|
_jobs = new List<IEnumerator<bool>>();
|
||||||
|
_airlocks = new Dictionary<string, Airlock>();
|
||||||
|
|
||||||
|
List<IMyDoor> doors = new List<IMyDoor>();
|
||||||
|
GridTerminalSystem.GetBlocksOfType(doors);
|
||||||
|
foreach (IMyDoor door in doors)
|
||||||
|
{
|
||||||
|
if (!door.CustomName.StartsWith("Airlock")) continue;
|
||||||
|
string airlockName = door.CustomName.Split(' ')[0];
|
||||||
|
if (_airlocks.ContainsKey(airlockName)) continue;
|
||||||
|
Airlock newAirlock = new Airlock(this, airlockName);
|
||||||
|
if (!newAirlock.Functional)
|
||||||
|
{
|
||||||
|
Echo($"{airlockName} is missing one or more required blocks.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_airlocks[airlockName] = newAirlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Main(string argument, UpdateType updateSource)
|
||||||
|
{
|
||||||
|
Echo($"index: {_tickCount++}");
|
||||||
|
|
||||||
|
if (updateSource == UpdateType.Trigger || updateSource == UpdateType.Terminal)
|
||||||
|
{
|
||||||
|
_cli.TryParse(argument);
|
||||||
|
if (_cli.ArgumentCount == 0) { Echo("You must provide an airlock ID."); }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string airlockName = $"Airlock{_cli.Argument(0)}";
|
||||||
|
if (!_airlocks.ContainsKey(airlockName))
|
||||||
|
{
|
||||||
|
Echo($"Invalid airlock ID {_cli.Argument(0)}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_jobs.Add(_airlocks[airlockName].CycleAirlock());
|
||||||
|
Runtime.UpdateFrequency |= UpdateFrequency.Update1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _jobs.Count; i++)
|
||||||
|
{
|
||||||
|
IEnumerator<bool> job = _jobs[i];
|
||||||
|
if (!job.MoveNext())
|
||||||
|
{
|
||||||
|
job.Dispose();
|
||||||
|
_jobs.Remove(job);
|
||||||
|
i--;
|
||||||
|
Echo("Airlock Cycling Complete.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_jobs.Count == 0) Runtime.UpdateFrequency = UpdateFrequency.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
Airlock/thumb.png
Normal file
BIN
Airlock/thumb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 389 KiB |
258
airlock.cs
258
airlock.cs
|
@ -1,258 +0,0 @@
|
||||||
enum AirlockState {
|
|
||||||
Off,
|
|
||||||
DoorsClosing,
|
|
||||||
Cycling,
|
|
||||||
DoorOpening,
|
|
||||||
Cooldown,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AirlockLightState {
|
|
||||||
Off,
|
|
||||||
Cycling,
|
|
||||||
Cooldown,
|
|
||||||
Error,
|
|
||||||
}
|
|
||||||
|
|
||||||
string airlockName;
|
|
||||||
IEnumerator<bool> state;
|
|
||||||
float targetOxygenLevel = 0.0F;
|
|
||||||
|
|
||||||
// Blocks we need to track
|
|
||||||
IMyDoor innerDoor;
|
|
||||||
IMyDoor outerDoor;
|
|
||||||
List<IMyLightingBlock> 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<IMyDoor> onlyDoors = new List<IMyDoor>();
|
|
||||||
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<IMyAirVent> onlyFans = new List<IMyAirVent>();
|
|
||||||
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<IMyLightingBlock>();
|
|
||||||
List<IMyLightingBlock> allLights = new List<IMyLightingBlock>();
|
|
||||||
GridTerminalSystem.GetBlocksOfType(allLights);
|
|
||||||
foreach (IMyLightingBlock light in allLights) {
|
|
||||||
if (light.CustomName.StartsWith(airlockName)) {
|
|
||||||
lights.Add(light);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitOxygen() {
|
|
||||||
List<IMyGasTank> allTanks = new List<IMyGasTank>();
|
|
||||||
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<bool> 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);
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user