Rename and genericize door control script.
It can now handle multiple doors each made of multiple hinges, open and close them separately, and handle multiple simultaneous invocations.
This commit is contained in:
parent
445c1205f9
commit
96b4c5861e
|
@ -1,144 +0,0 @@
|
|||
// Script for opening maintence panels on a ship.
|
||||
//
|
||||
// Currently this simply opens and closes *all* of the panels attached to hinges
|
||||
// whose names start with "Maintenance". Default behavior is to open.
|
||||
// Pass the argument -close to close the panels.
|
||||
//
|
||||
// By default "open" is assumed to be 90 degrees and "closed" is assumed to be 0 degrees.
|
||||
// Each hinge can have its behavior configured via custom data. A sample config
|
||||
// (with the default values):
|
||||
//
|
||||
// OpenAngle=90
|
||||
// ClosedAngle=0
|
||||
// Velocity=5
|
||||
|
||||
|
||||
MyCommandLine cli;
|
||||
IEnumerator<bool> state;
|
||||
List<Panel> panels;
|
||||
|
||||
public class Panel {
|
||||
public Panel(IMyMotorStator hinge) {
|
||||
Hinge = hinge;
|
||||
ParseConfig();
|
||||
}
|
||||
|
||||
// For these two functions, IMyMotorStator.Angle reports radians, but
|
||||
// IMyMotorStator.RotateToAngle() expects degrees...
|
||||
public void OpenPanel() {
|
||||
Hinge.RotorLock = false;
|
||||
TargetAngle = DegToRad(OpenAngle);
|
||||
Hinge.RotateToAngle(MyRotationDirection.AUTO, OpenAngle, Velocity);
|
||||
}
|
||||
|
||||
public void ClosePanel() {
|
||||
Hinge.RotorLock = false;
|
||||
TargetAngle = DegToRad(ClosedAngle);
|
||||
Hinge.RotateToAngle(MyRotationDirection.AUTO, ClosedAngle, Velocity);
|
||||
}
|
||||
|
||||
// Call this function every tick after OpenPanel or ClosePanel.
|
||||
// It will return true when the panel has finished moving.
|
||||
public bool Monitor() {
|
||||
if (Math.Abs(Hinge.Angle - TargetAngle) < 0.001) {
|
||||
Hinge.RotorLock = true;
|
||||
}
|
||||
return Hinge.RotorLock;
|
||||
}
|
||||
|
||||
// TODO: these are public for debugging
|
||||
public IMyMotorStator Hinge { get; set; }
|
||||
public float TargetAngle { get; set; }
|
||||
private float OpenAngle { get; set; } = 90F;
|
||||
private float ClosedAngle { get; set; } = 0F;
|
||||
private float Velocity { get; set; } = 5F;
|
||||
|
||||
private void ParseConfig() {
|
||||
string[] lines = Hinge.CustomData.Split('\n');
|
||||
foreach (string line in lines) {
|
||||
string[] tokens = line.Split('=');
|
||||
if (tokens.Length != 2) continue;
|
||||
switch(tokens[0]) {
|
||||
case "OpenAngle":
|
||||
OpenAngle = float.Parse(tokens[1]);
|
||||
break;
|
||||
case "ClosedAngle":
|
||||
ClosedAngle = float.Parse(tokens[1]);
|
||||
break;
|
||||
case "Velocity":
|
||||
Velocity = float.Parse(tokens[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: a utility class or function would be lovely...
|
||||
// In general, the encapsulation feels a little screwy here.
|
||||
private float DegToRad(float degrees) {
|
||||
return degrees * ((float)Math.PI / 180F);
|
||||
}
|
||||
}
|
||||
|
||||
public Program() {
|
||||
cli = new MyCommandLine();
|
||||
state = null;
|
||||
|
||||
panels = new List<Panel>();
|
||||
List<IMyMotorStator> allHinges = new List<IMyMotorStator>();
|
||||
GridTerminalSystem.GetBlocksOfType(allHinges);
|
||||
foreach(IMyMotorStator hinge in allHinges) {
|
||||
if (hinge.CustomName.StartsWith("Maintenance")) {
|
||||
panels.Add(new Panel(hinge));
|
||||
}
|
||||
}
|
||||
|
||||
Echo($"Found {panels.Count} panels.");
|
||||
}
|
||||
|
||||
public void Main(string argument, UpdateType updateSource) {
|
||||
if (state == null) {
|
||||
cli.TryParse(argument);
|
||||
if (cli.Switch("close")) state = ClosePanels();
|
||||
else state = OpenPanels();
|
||||
Runtime.UpdateFrequency = UpdateFrequency.Update1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state.MoveNext()) {
|
||||
Echo("Operation Complete.");
|
||||
state.Dispose();
|
||||
state = null;
|
||||
Runtime.UpdateFrequency = UpdateFrequency.None;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator<bool> OpenPanels() {
|
||||
Echo("Opening panels.");
|
||||
foreach (Panel panel in panels) {
|
||||
panel.OpenPanel();
|
||||
}
|
||||
return MonitorPanels();
|
||||
}
|
||||
|
||||
private IEnumerator<bool> ClosePanels() {
|
||||
Echo("Closing panels.");
|
||||
foreach (Panel panel in panels) {
|
||||
panel.ClosePanel();
|
||||
}
|
||||
return MonitorPanels();
|
||||
}
|
||||
|
||||
private IEnumerator<bool> MonitorPanels() {
|
||||
while (true) {
|
||||
Echo("Monitoring panels.");
|
||||
bool done = true; // assume we've finished, then falsify it below
|
||||
foreach (Panel panel in panels) {
|
||||
if (!panel.Monitor()) {
|
||||
Echo($"{panel.TargetAngle} {panel.Hinge.Angle}");
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
if (done) yield break;
|
||||
yield return true;
|
||||
}
|
||||
}
|
228
mechanical_door_controller.cs
Normal file
228
mechanical_door_controller.cs
Normal file
|
@ -0,0 +1,228 @@
|
|||
// Script for controlling "mechanical" doors, aka custom doors using hinges.
|
||||
//
|
||||
// Future features:
|
||||
// * Rotor and piston support.
|
||||
// * "Multi-stage doors", e.g. actuate a hinge, then a piston.
|
||||
//
|
||||
// Currently this operates on hinges whose names start with "DoorX", where X is an arbitrary identifier.
|
||||
// Usage:
|
||||
// Script argument should be a space-separated list of identifiers and either
|
||||
// `-open` (default behavior) or `-close`.
|
||||
// Failing to pass any identifiers will cause the action to happen to all doors on the grid.
|
||||
//
|
||||
// By default "open" is assumed to be 90 degrees and "closed" is assumed to be 0 degrees.
|
||||
// However, each hinge can have this behavior configured via custom data. A sample config
|
||||
// (with the default values):
|
||||
//
|
||||
// OpenAngle=90
|
||||
// ClosedAngle=0
|
||||
// Velocity=5
|
||||
|
||||
|
||||
MyCommandLine cli;
|
||||
List<IEnumerator<bool>> jobs;
|
||||
Map<string, Door> doors;
|
||||
|
||||
public class DoorHinge {
|
||||
public DoorHinge(Program p, IMyMotorStator hinge) {
|
||||
P = p;
|
||||
Hinge = hinge;
|
||||
}
|
||||
|
||||
// For these two functions, IMyMotorStator.Angle reports radians, but
|
||||
// IMyMotorStator.RotateToAngle() expects degrees...
|
||||
public void OpenDoorHinge() {
|
||||
foreach(IMyMotorStator hinge in Hinges) {
|
||||
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.
|
||||
public bool Actuate() {
|
||||
if (Math.Abs(Hinge.Angle - TargetAngle) < 0.001) {
|
||||
Hinge.RotorLock = true;
|
||||
}
|
||||
return Locked();
|
||||
}
|
||||
|
||||
public bool Locked() {
|
||||
return Hinge.RotorLock;
|
||||
}
|
||||
|
||||
private Program P; // for access to Echo, etc.
|
||||
private IMyMotorStator Hinge { get; set; }
|
||||
private float TargetAngle { get; set; }
|
||||
private float OpenAngle { get; set; } = 90F;
|
||||
private float ClosedAngle { get; set; } = 0F;
|
||||
private float Velocity { get; set; } = 5F;
|
||||
|
||||
private void ParseConfig() {
|
||||
string[] lines = Hinge.CustomData.Split('\n');
|
||||
foreach (string line in lines) {
|
||||
string[] tokens = line.Split('=');
|
||||
if (tokens.Length != 2) continue;
|
||||
switch(tokens[0]) {
|
||||
case "OpenAngle":
|
||||
OpenAngle = float.Parse(tokens[1]);
|
||||
break;
|
||||
case "ClosedAngle":
|
||||
ClosedAngle = float.Parse(tokens[1]);
|
||||
break;
|
||||
case "Velocity":
|
||||
Velocity = float.Parse(tokens[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: a utility class or function would be lovely...
|
||||
// In general, the encapsulation feels a little screwy here.
|
||||
private float DegToRad(float degrees) {
|
||||
return degrees * ((float)Math.PI / 180F);
|
||||
}
|
||||
}
|
||||
|
||||
public class Door {
|
||||
public Door(Program p) {
|
||||
P = p;
|
||||
Hinges = new List<DoorHinge>();
|
||||
}
|
||||
|
||||
// Add a hinge to the door
|
||||
public void AddHinge(IMyMotorStator hinge) {
|
||||
Hinges.Add(P, new DoorHinge(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;
|
||||
}
|
||||
|
||||
public bool Locked() {
|
||||
foreach (DoorHinge hinge in Hinges) {
|
||||
if (!hinge.Locked()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<DoorHinge> hinges;
|
||||
private Program P;
|
||||
}
|
||||
|
||||
public Program() {
|
||||
cli = new MyCommandLine();
|
||||
jobs = new List<IEnumerator<bool>>();
|
||||
|
||||
doors = new Map<string, Door>();
|
||||
|
||||
List<IMyMotorStator> allHinges = new List<IMyMotorStator>();
|
||||
GridTerminalSystem.GetBlocksOfType(allHinges);
|
||||
foreach(IMyMotorStator hinge in allHinges) {
|
||||
if (hinge.CustomName.StartsWith("Door")) {
|
||||
string doorName = hinge.CustomName;
|
||||
if (!hingeMap.ContainsKey(doorName)) {
|
||||
hingeMap[doorName] = new Door(this);
|
||||
}
|
||||
hingeMap[doorName].AddHinge(hinge);
|
||||
}
|
||||
}
|
||||
|
||||
Echo($"Found {doors.Keys.Count} doors.");
|
||||
}
|
||||
|
||||
public void Main(string argument, UpdateType updateSource) {
|
||||
if (updateSource & (UpdateType.Trigger | UpdateType.Terminal)) {
|
||||
// Create a new job
|
||||
cli.TryParse(argument);
|
||||
|
||||
List<Door> doorsToControl = new List<Door>();
|
||||
foreach (string arg in argument) {
|
||||
if (!doors.ContainsKey($"Door{arg}")) {
|
||||
Echo($"Door with identifier {arg} not found. Skipping.");
|
||||
continue;
|
||||
}
|
||||
if (!doors[arg].Locked()) {
|
||||
Echo($"Door {arg} already moving. Skipping.");
|
||||
continue;
|
||||
}
|
||||
doorsToControl.Add(doors[arg]);
|
||||
}
|
||||
|
||||
if (doorsToControl.IsEmpty) {
|
||||
Echo("No doors found. Not creating new job.");
|
||||
} else {
|
||||
Echo("Creating new job.");
|
||||
if (cli.Switch("close")) jobs.Add(CloseDoors(doorsToControl));
|
||||
else jobs.Add(OpenDoors(doorsToControl));
|
||||
Runtime.UpdateFrequency = UpdateFrequency.Update1;
|
||||
}
|
||||
}
|
||||
|
||||
// Process running jobs
|
||||
for (int i=0; i < jobs.Count; i++) {
|
||||
if (!jobs[i].MoveNext()) {
|
||||
jobs[i].Dispose();
|
||||
jobs.Remove(jobs[i]);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (jobs.IsEmpty) {
|
||||
Runtime.UpdateFrequency = UpdateFrequency.None;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator<bool> OpenDoors(List<Door> doorsToControl) {
|
||||
Echo("Opening doors.");
|
||||
foreach (Door door in doors) {
|
||||
door.OpenDoor();
|
||||
}
|
||||
return ActuateDoors(doorsToCotrol);
|
||||
}
|
||||
|
||||
private IEnumerator<bool> CloseDoors(List<Door> doorsToControl) {
|
||||
Echo("Closing doors.");
|
||||
foreach (Door door in doorsToControl) {
|
||||
panel.CloseDoor();
|
||||
}
|
||||
return ActuateDoors(doorsToControl);
|
||||
}
|
||||
|
||||
private IEnumerator<bool> ActuateDoors(doorsToControl) {
|
||||
while (true) {
|
||||
Echo("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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user