Add (and use) a Sequencer library for orchestrating the mechanical door; this will eventually have wide reuse applicability.

This commit is contained in:
2025-02-09 20:26:00 -05:00
parent 74331a95ba
commit 37511ac473
10 changed files with 250 additions and 189 deletions

View File

@ -1,62 +0,0 @@
using Sandbox.ModAPI.Ingame;
using System.Collections.Generic;
namespace IngameScript
{
public class Door
{
private PrefixedConsole _console;
private List<DoorHinge> _hinges;
public bool Locked
{
get
{
foreach (DoorHinge hinge in _hinges)
{
if (!hinge.Locked) return false;
}
return true;
}
}
public Door(Console console, string name)
{
_console = new PrefixedConsole(console, name);
_hinges = new List<DoorHinge>();
}
// Add a hinge to the door
public void AddHinge(IMyMotorStator hinge)
{
_hinges.Add(new DoorHinge(_console, hinge));
}
public void OpenDoor()
{
foreach (DoorHinge hinge in _hinges)
{
hinge.OpenDoorHinge();
}
}
public void CloseDoor()
{
foreach (DoorHinge hinge in _hinges)
{
hinge.CloseDoorHinge();
}
}
// Process the door's movement. This will return true when the door is done moving.
public bool Actuate()
{
bool done = true;
foreach (DoorHinge hinge in _hinges)
{
if (!hinge.Actuate()) done = false;
}
return done;
}
}
}

View File

@ -1,64 +0,0 @@
using Sandbox.ModAPI.Ingame;
using System.Collections.Generic;
using System;
namespace IngameScript
{
public class DoorHinge
{
public bool Locked { get { return _hinge.RotorLock; } }
private PrefixedConsole _console;
private IMyMotorStator _hinge;
private float _targetAngle;
private float _lastAngle;
private float _openAngle = 90F;
private float _closedAngle = 0F;
private float _velocity = 5F;
public DoorHinge(PrefixedConsole console, IMyMotorStator hinge)
{
_hinge = hinge;
_console = console;
ConfigParser config = new ConfigParser(_hinge);
_openAngle = config.GetValue("OpenAngle", 90F);
_closedAngle = config.GetValue("ClosedAngle", 0F);
_velocity = config.GetValue("Velocity", 5F);
}
// For these two functions, IMyMotorStator.Angle reports radians, but
// IMyMotorStator.RotateToAngle() expects degrees...
public void OpenDoorHinge()
{
_hinge.RotorLock = false;
_targetAngle = degToRad(_openAngle);
_hinge.RotateToAngle(MyRotationDirection.AUTO, _openAngle, _velocity);
}
public void CloseDoorHinge()
{
_hinge.RotorLock = false;
_targetAngle = degToRad(_closedAngle);
_hinge.RotateToAngle(MyRotationDirection.AUTO, _closedAngle, _velocity);
}
// Process the hinge's movement.
// It will return true when the panel has finished moving.
// TODO: Add a mechanism to determine when a door gets stuck or can't reach the target angle.
public bool Actuate()
{
if (Math.Abs(_hinge.Angle - _targetAngle) < 0.1 && _hinge.Angle == _lastAngle)
{
_hinge.RotorLock = true;
}
_lastAngle = _hinge.Angle;
return Locked;
}
private float degToRad(float degrees)
{
return degrees * ((float)Math.PI / 180F);
}
}
}

View File

@ -25,4 +25,6 @@
</ItemGroup>
<Import Project="..\Mixins\Console\Console.projitems" Label="shared" />
<Import Project="..\Mixins\ConfigParser\ConfigParser.projitems" Label="shared" />
<Import Project="..\Mixins\Sequencer\Sequencer.projitems" Label="shared" />
<Import Project="..\Mixins\Utils\Utils.projitems" Label="shared" />
</Project>

View File

@ -1,4 +1,5 @@
using Sandbox.ModAPI.Ingame;
using SpaceEngineers.Game.ModAPI.Ingame;
using System.Collections.Generic;
using VRage.Game.ModAPI.Ingame.Utilities;
@ -9,7 +10,7 @@ namespace IngameScript
private MyCommandLine _cli;
private Console _console;
private List<IEnumerator<bool>> _jobs;
private Dictionary<string, Door> _doors;
private Dictionary<string, Sequencer> _doors;
private int _tickCount;
public Program()
@ -19,21 +20,25 @@ namespace IngameScript
_console = new Console(this);
_jobs = new List<IEnumerator<bool>>();
_doors = new Dictionary<string, Door>();
_doors = new Dictionary<string, Sequencer>();
List<IMyMotorStator> allHinges = new List<IMyMotorStator>();
GridTerminalSystem.GetBlocksOfType(allHinges);
foreach (IMyMotorStator hinge in allHinges)
List<IMyTerminalBlock> doorParts = Utils.GetBlocksNameContains(GridTerminalSystem, "!Door");
foreach (IMyTerminalBlock block in doorParts)
{
if (hinge.CustomName.StartsWith("Door"))
string doorName = Utils.ExtractTag(block, "!Door");
// Create the door if this is a new tag
if (!_doors.ContainsKey(doorName))
{
string doorName = hinge.CustomName.Split(' ')[0];
if (!_doors.ContainsKey(doorName))
{
_doors[doorName] = new Door(_console, doorName);
}
_doors[doorName].AddHinge(hinge);
_doors[doorName] = new Sequencer(doorName, new PrefixedConsole(_console, doorName));
}
// Add the part; the Door object handles typing and sequencing.
int defaultStep = 1;
if (block is IMyShipMergeBlock) defaultStep = 0;
ISequenceable wrapped = SequenceableFactory.MakeSequenceable(block, defaultStep);
if (wrapped == null) { _console.Print($"Tried to add incompatible block '{block.CustomName}'"); continue; }
_doors[doorName].AddBlock(wrapped);
}
_console.Print($"Found {_doors.Keys.Count} doors.");
@ -47,17 +52,14 @@ namespace IngameScript
{
// Create a new job
_cli.TryParse(argument);
List<Door> doorsToControl = new List<Door>();
List<Sequencer> doorsToControl = new List<Sequencer>();
if (_cli.ArgumentCount == 0)
{
_console.Print("No arguments passed. Controlling all doors.");
foreach (Door door in _doors.Values)
foreach (Sequencer door in _doors.Values)
{
if (!door.Locked)
{
continue;
}
if (door.Running) continue;
doorsToControl.Add(door);
}
}
@ -67,12 +69,12 @@ namespace IngameScript
string key = "Door" + _cli.Argument(i);
if (!_doors.ContainsKey(key))
{
_console.Print($"Door with identifier {key} not found. Skipping.");
_console.Print($"Door '{key}' not found. Skipping.");
continue;
}
if (!_doors[key].Locked)
if (_doors[key].Running)
{
_console.Print($"Door {key} already moving. Skipping.");
_console.Print($"Door '{key}' already moving. Skipping.");
continue;
}
doorsToControl.Add(_doors[key]);
@ -84,10 +86,13 @@ namespace IngameScript
}
else
{
_console.Print("Creating new job.");
if (_cli.Switch("close")) _jobs.Add(closeDoors(doorsToControl));
else _jobs.Add(openDoors(doorsToControl));
Runtime.UpdateFrequency = UpdateFrequency.Update1;
_console.Print("Creating new job(s).");
bool close = _cli.Switch("close");
foreach (Sequencer door in doorsToControl)
{
_jobs.Add(door.RunSequence(close));
}
Runtime.UpdateFrequency |= UpdateFrequency.Update1;
}
}
@ -108,43 +113,5 @@ namespace IngameScript
Runtime.UpdateFrequency = UpdateFrequency.None;
}
}
private IEnumerator<bool> openDoors(List<Door> doorsToControl)
{
_console.Print("Opening doors.");
foreach (Door door in doorsToControl)
{
door.OpenDoor();
}
return actuateDoors(doorsToControl);
}
private IEnumerator<bool> closeDoors(List<Door> doorsToControl)
{
_console.Print("Closing doors.");
foreach (Door door in doorsToControl)
{
door.CloseDoor();
}
return actuateDoors(doorsToControl);
}
private IEnumerator<bool> actuateDoors(List<Door> doorsToControl)
{
while (true)
{
_console.Print("Actuating doors.");
bool done = true; // assume we've finished, then falsify it below
foreach (Door door in doorsToControl)
{
if (!door.Actuate())
{
done = false;
}
}
if (done) yield break;
yield return true;
}
}
}
}