This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
|
|
||||||
[plc]
|
[plc]
|
||||||
port = COM1
|
port = COM1
|
||||||
|
sensor_delay_open = 0 ; seconds
|
||||||
|
sensor_delay_close = 2 ; seconds
|
||||||
|
|
||||||
[update]
|
[update]
|
||||||
url = https://elwig.at/files/pamhagen-sysctrl/latest
|
url = https://elwig.at/files/pamhagen-sysctrl/latest
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
private readonly string FileName;
|
private readonly string FileName;
|
||||||
|
|
||||||
public string PlcPort = "COM1";
|
public string PlcPort = "COM1";
|
||||||
|
public int PlcSensorDelayOpen = 0;
|
||||||
|
public int PlcSensorDelayClose = 0;
|
||||||
public string? UpdateUrl;
|
public string? UpdateUrl;
|
||||||
public bool UpdateAuto;
|
public bool UpdateAuto;
|
||||||
|
|
||||||
@@ -24,6 +26,8 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
try {
|
try {
|
||||||
var config = new ConfigurationBuilder().AddIniFile(FileName).Build();
|
var config = new ConfigurationBuilder().AddIniFile(FileName).Build();
|
||||||
PlcPort = config["plc:port"] ?? "COM1";
|
PlcPort = config["plc:port"] ?? "COM1";
|
||||||
|
PlcSensorDelayOpen = int.Parse(config["plc:sensor_delay_open"] ?? "0");
|
||||||
|
PlcSensorDelayClose = int.Parse(config["plc:sensor_delay_close"] ?? "0");
|
||||||
UpdateUrl = config["update:url"];
|
UpdateUrl = config["update:url"];
|
||||||
UpdateAuto = TrueValues.Contains(config["update:auto"]?.ToLower());
|
UpdateAuto = TrueValues.Contains(config["update:auto"]?.ToLower());
|
||||||
var xbe24 = config.GetSection("xbe24");
|
var xbe24 = config.GetSection("xbe24");
|
||||||
|
|||||||
@@ -488,7 +488,7 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected ControlledValve CreateValve(int n, double x, double y, int o) {
|
protected ControlledValve CreateValve(int n, double x, double y, int o) {
|
||||||
return new(n, x, y, o, () => App.Plant?.State.Valve_IsOpen(n) ?? false, (v) => App.Plant?.ManualControlMode ?? false ? App.Plant.State.Valve_WantOpen(n) : SelectedPaths.Any(p => p.Contains(v)));
|
return new(n, x, y, o, () => App.Plant?.State.Valve_OpenState(n), (v) => App.Plant?.ManualControlMode ?? false ? App.Plant.State.Valve_WantOpen(n) : SelectedPaths.Any(p => p.Contains(v)));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static Tank CreateTank(int n, double x, double y, int capacity) {
|
protected static Tank CreateTank(int n, double x, double y, int capacity) {
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
PlcThread = null;
|
PlcThread = null;
|
||||||
PamhagenPlc? tmp = null;
|
PamhagenPlc? tmp = null;
|
||||||
try {
|
try {
|
||||||
tmp = new PamhagenPlc(PlcComPort);
|
tmp = new PamhagenPlc(PlcComPort, App.Config.PlcSensorDelayOpen * 1000, App.Config.PlcSensorDelayClose * 1000);
|
||||||
await tmp.Initialize();
|
await tmp.Initialize();
|
||||||
Plc = tmp;
|
Plc = tmp;
|
||||||
PlcConnectionError = null;
|
PlcConnectionError = null;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
|
|||||||
|
|
||||||
protected Func<Valve, bool> CallbackWantOpen;
|
protected Func<Valve, bool> CallbackWantOpen;
|
||||||
|
|
||||||
public ControlledValve(int nr, double x, double y, int orientation, Func<bool> cbOpen, Func<Valve, bool> cbWant) :
|
public ControlledValve(int nr, double x, double y, int orientation, Func<bool?> cbOpen, Func<Valve, bool> cbWant) :
|
||||||
base(x, y, orientation, cbOpen) {
|
base(x, y, orientation, cbOpen) {
|
||||||
Nr = nr;
|
Nr = nr;
|
||||||
CallbackWantOpen = cbWant;
|
CallbackWantOpen = cbWant;
|
||||||
@@ -23,9 +23,9 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
|
|||||||
if (App.Plant?.ManualControlMode ?? false) {
|
if (App.Plant?.ManualControlMode ?? false) {
|
||||||
_text?.Visibility = Visibility.Visible;
|
_text?.Visibility = Visibility.Visible;
|
||||||
_triangles?.Fill =
|
_triangles?.Fill =
|
||||||
!WantOpen && !IsOpen && !isHovering ? PamhagenBrushes.Red :
|
!WantOpen && IsClosed && !isHovering ? PamhagenBrushes.Red :
|
||||||
!WantOpen && !IsOpen ? PamhagenBrushes.Green :
|
!WantOpen && IsClosed ? PamhagenBrushes.Green :
|
||||||
!WantOpen && IsOpen ? PamhagenBrushes.DimRed :
|
!WantOpen && !IsClosed ? PamhagenBrushes.DimRed :
|
||||||
WantOpen && !IsOpen ? PamhagenBrushes.DimGreen :
|
WantOpen && !IsOpen ? PamhagenBrushes.DimGreen :
|
||||||
WantOpen && IsOpen && !isHovering ? PamhagenBrushes.Green :
|
WantOpen && IsOpen && !isHovering ? PamhagenBrushes.Green :
|
||||||
WantOpen && IsOpen ? PamhagenBrushes.Red :
|
WantOpen && IsOpen ? PamhagenBrushes.Red :
|
||||||
@@ -33,13 +33,13 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
|
|||||||
} else {
|
} else {
|
||||||
_text?.Visibility = isHovering ? Visibility.Visible : Visibility.Hidden;
|
_text?.Visibility = isHovering ? Visibility.Visible : Visibility.Hidden;
|
||||||
_triangles?.Fill =
|
_triangles?.Fill =
|
||||||
IsLocked && !WantOpen && !IsOpen ? PamhagenBrushes.DimOrange :
|
IsLocked && !WantOpen && IsClosed ? PamhagenBrushes.DimOrange :
|
||||||
IsLocked && !WantOpen && IsOpen ? PamhagenBrushes.Red :
|
IsLocked && !WantOpen && !IsClosed ? PamhagenBrushes.Red :
|
||||||
IsLocked && WantOpen && !IsOpen ? PamhagenBrushes.Yellow :
|
IsLocked && WantOpen && !IsOpen ? PamhagenBrushes.Yellow :
|
||||||
IsLocked && WantOpen && IsOpen ? PamhagenBrushes.Green :
|
IsLocked && WantOpen && IsOpen ? PamhagenBrushes.Green :
|
||||||
Highlight ?? (isHovering ? Brushes.LightGray : Brushes.DarkGray);
|
Highlight ?? (isHovering ? Brushes.LightGray : Brushes.DarkGray);
|
||||||
}
|
}
|
||||||
if (IsOpen) {
|
if (!IsClosed) {
|
||||||
_topLine?.Points[1] = _middlePoints.Open.Top;
|
_topLine?.Points[1] = _middlePoints.Open.Top;
|
||||||
_bottomLine?.Points[1] = _middlePoints.Open.Bottom;
|
_bottomLine?.Points[1] = _middlePoints.Open.Bottom;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
|
|||||||
|
|
||||||
public override string Label { get; }
|
public override string Label { get; }
|
||||||
|
|
||||||
public ExternalValve(string label, double x, double y, int orientation, Func<bool> cbOpen) :
|
public ExternalValve(string label, double x, double y, int orientation, Func<bool?> cbOpen) :
|
||||||
base(x, y, orientation, cbOpen) {
|
base(x, y, orientation, cbOpen) {
|
||||||
Label = label;
|
Label = label;
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
|
|||||||
!IsOpen ? PamhagenBrushes.Red :
|
!IsOpen ? PamhagenBrushes.Red :
|
||||||
IsOpen ? PamhagenBrushes.Green :
|
IsOpen ? PamhagenBrushes.Green :
|
||||||
Highlight ?? Brushes.DarkGray;
|
Highlight ?? Brushes.DarkGray;
|
||||||
if (IsOpen) {
|
if (!IsClosed) {
|
||||||
_topLine?.Points[1] = _middlePoints.Open.Top;
|
_topLine?.Points[1] = _middlePoints.Open.Top;
|
||||||
_bottomLine?.Points[1] = _middlePoints.Open.Bottom;
|
_bottomLine?.Points[1] = _middlePoints.Open.Bottom;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
namespace PamhagenSysCtrl.Helpers.Pipeline {
|
namespace PamhagenSysCtrl.Helpers.Pipeline {
|
||||||
public record struct Path(
|
public record struct Path(
|
||||||
INode Start,
|
INode Start,
|
||||||
IEnumerable<(IEdge Edge, INode Node)> Hops
|
(IEdge Edge, INode Node)[] Hops
|
||||||
) {
|
) {
|
||||||
public readonly int Bends => Hops.Sum(h => (h.Node is not Valve && h.Node is not PipeJoin ? 1 : 0) + (h.Edge.Start.CenterX != h.Edge.End.CenterX && h.Edge.Start.CenterY != h.Edge.End.CenterY ? 1 : 0));
|
public readonly int Bends => Hops.Sum(h => (h.Node is not Valve && h.Node is not PipeJoin ? 1 : 0) + (h.Edge.Start.CenterX != h.Edge.End.CenterX && h.Edge.Start.CenterY != h.Edge.End.CenterY ? 1 : 0));
|
||||||
public readonly double Length => Hops.Sum(h => Math.Abs(h.Edge.Start.CenterX - h.Edge.End.CenterX) + Math.Abs(h.Edge.Start.CenterY - h.Edge.End.CenterY));
|
public readonly double Length => Hops.Sum(h => Math.Abs(h.Edge.Start.CenterX - h.Edge.End.CenterX) + Math.Abs(h.Edge.Start.CenterY - h.Edge.End.CenterY));
|
||||||
@@ -26,7 +26,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
|
|||||||
public readonly IEnumerable<ControlledValve> BorderValves => InnerAndBorderValves.Except(InnerValves);
|
public readonly IEnumerable<ControlledValve> BorderValves => InnerAndBorderValves.Except(InnerValves);
|
||||||
|
|
||||||
public readonly bool HasActivePump => Nodes.Any(n => n is Pump p && p.IsActive);
|
public readonly bool HasActivePump => Nodes.Any(n => n is Pump p && p.IsActive);
|
||||||
public readonly bool IsReady => InnerValves.All(v => v.IsOpen && v.WantOpen && v.IsLocked) && BorderValves.All(v => !v.IsOpen && !v.WantOpen && v.IsLocked);
|
public readonly bool IsReady => InnerValves.All(v => v.IsOpen && v.WantOpen && v.IsLocked) && BorderValves.All(v => v.IsClosed && !v.WantOpen && v.IsLocked);
|
||||||
|
|
||||||
public readonly void LockValves() {
|
public readonly void LockValves() {
|
||||||
foreach (var v in InnerAndBorderValves) {
|
foreach (var v in InnerAndBorderValves) {
|
||||||
|
|||||||
@@ -16,11 +16,13 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
|
|||||||
|
|
||||||
public int Orientation { get; set; }
|
public int Orientation { get; set; }
|
||||||
|
|
||||||
public bool IsOpen => CallbackIsOpen();
|
public bool IsOpen => CallbackIsOpen() == true;
|
||||||
|
public bool IsClosed => CallbackIsOpen() == false;
|
||||||
|
public bool IsTransitioning => CallbackIsOpen() == null;
|
||||||
public ISet<IEdge> Inputs { get; init; }
|
public ISet<IEdge> Inputs { get; init; }
|
||||||
public ISet<IEdge> Outputs { get; init; }
|
public ISet<IEdge> Outputs { get; init; }
|
||||||
|
|
||||||
protected Func<bool> CallbackIsOpen;
|
protected Func<bool?> CallbackIsOpen;
|
||||||
|
|
||||||
protected Rectangle? _background;
|
protected Rectangle? _background;
|
||||||
protected Polygon? _triangles;
|
protected Polygon? _triangles;
|
||||||
@@ -29,7 +31,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
|
|||||||
protected ((Point Top, Point Bottom) Closed, (Point Top, Point Bottom) Open) _middlePoints;
|
protected ((Point Top, Point Bottom) Closed, (Point Top, Point Bottom) Open) _middlePoints;
|
||||||
protected TextBlock? _text;
|
protected TextBlock? _text;
|
||||||
|
|
||||||
public Valve(double x, double y, int orientation, Func<bool> cbOpen) {
|
public Valve(double x, double y, int orientation, Func<bool?> cbOpen) {
|
||||||
CenterX = x;
|
CenterX = x;
|
||||||
CenterY = y;
|
CenterY = y;
|
||||||
CallbackIsOpen = cbOpen;
|
CallbackIsOpen = cbOpen;
|
||||||
|
|||||||
@@ -27,12 +27,16 @@ namespace PamhagenSysCtrl.Helpers.Plc {
|
|||||||
|
|
||||||
internal LegacyPamhagenPlc.ActuatorStates Actuators;
|
internal LegacyPamhagenPlc.ActuatorStates Actuators;
|
||||||
internal LegacyPamhagenPlc.ActuatorStates LastActuators;
|
internal LegacyPamhagenPlc.ActuatorStates LastActuators;
|
||||||
internal LegacyPamhagenPlc.SensorStates Sensors = new(default, default, default, default, default, default, default, [
|
internal LegacyPamhagenPlc.SensorStates[] LastSensors = [];
|
||||||
FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown
|
|
||||||
], default);
|
|
||||||
|
|
||||||
public PamhagenPlc(string comPort, Parity parity = Parity.Odd) {
|
internal readonly int ConsiderLastSensorsOpen;
|
||||||
|
internal readonly int ConsiderLastSensorsClose;
|
||||||
|
|
||||||
|
public PamhagenPlc(string comPort, int sensorDelayOpenMs, int sensorDelayCloseMs, Parity parity = Parity.Odd) {
|
||||||
Plc = new(comPort, parity);
|
Plc = new(comPort, parity);
|
||||||
|
ConsiderLastSensorsOpen = sensorDelayOpenMs / 125 + 1;
|
||||||
|
ConsiderLastSensorsClose = sensorDelayCloseMs / 125 + 1;
|
||||||
|
LastSensors = new LegacyPamhagenPlc.SensorStates[Math.Max(ConsiderLastSensorsOpen, ConsiderLastSensorsClose)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
@@ -44,8 +48,9 @@ namespace PamhagenSysCtrl.Helpers.Plc {
|
|||||||
await Plc.TestConnection();
|
await Plc.TestConnection();
|
||||||
Actuators = await Plc.ReadOutputs();
|
Actuators = await Plc.ReadOutputs();
|
||||||
LastActuators = Actuators;
|
LastActuators = Actuators;
|
||||||
Sensors = await Plc.ReadInputs();
|
var sensors = await Plc.ReadInputs();
|
||||||
State = new(Sensors, this);
|
LastSensors[0] = sensors;
|
||||||
|
State = new(LastSensors, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Shutdown() {
|
public async Task Shutdown() {
|
||||||
@@ -59,8 +64,10 @@ namespace PamhagenSysCtrl.Helpers.Plc {
|
|||||||
LastActuators = Actuators;
|
LastActuators = Actuators;
|
||||||
await Plc.WriteOutputs(LastActuators);
|
await Plc.WriteOutputs(LastActuators);
|
||||||
}
|
}
|
||||||
Sensors = await Plc.ReadInputs();
|
var sensors = await Plc.ReadInputs();
|
||||||
State = new(Sensors, this);
|
Array.Copy(LastSensors, 0, LastSensors, 1, LastSensors.Length - 1);
|
||||||
|
LastSensors[0] = sensors;
|
||||||
|
State = new(LastSensors, this);
|
||||||
|
|
||||||
UpdateInterlocks();
|
UpdateInterlocks();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ namespace PamhagenSysCtrl.Helpers.Plc {
|
|||||||
public bool OverrideDryRunClearances => Plc != null && Plc.OverrideDryRunClearances;
|
public bool OverrideDryRunClearances => Plc != null && Plc.OverrideDryRunClearances;
|
||||||
|
|
||||||
public long Valves => Actuators.Valves;
|
public long Valves => Actuators.Valves;
|
||||||
public long PressureSensors => Sensors.PressureSensors;
|
public readonly long OpenValves { get; }
|
||||||
|
public readonly long ClosedValves { get; }
|
||||||
|
|
||||||
public bool MW1_IsSelected => Sensors.TroughSelector == 1;
|
public bool MW1_IsSelected => Sensors.TroughSelector == 1;
|
||||||
public bool MW2_IsSelected => Sensors.TroughSelector == 2;
|
public bool MW2_IsSelected => Sensors.TroughSelector == 2;
|
||||||
@@ -114,10 +115,13 @@ namespace PamhagenSysCtrl.Helpers.Plc {
|
|||||||
public bool P5_HasClearance => P4_HasDryRunClearance && P5_HasPathClearance;
|
public bool P5_HasClearance => P4_HasDryRunClearance && P5_HasPathClearance;
|
||||||
public bool P6_HasClearance => P6_HasDryRunClearance && P6_HasPathClearance;
|
public bool P6_HasClearance => P6_HasDryRunClearance && P6_HasPathClearance;
|
||||||
|
|
||||||
public PamhagenState(LegacyPamhagenPlc.SensorStates sensors, PamhagenPlc plc) {
|
public PamhagenState(IEnumerable<LegacyPamhagenPlc.SensorStates> sensors, PamhagenPlc plc) {
|
||||||
Sensors = sensors;
|
Sensors = sensors.First();
|
||||||
Plc = plc;
|
Plc = plc;
|
||||||
|
|
||||||
|
OpenValves = sensors.Take(Plc.ConsiderLastSensorsOpen).Aggregate(-1L, (m, s) => m & ~s.PressureSensors);
|
||||||
|
ClosedValves = sensors.Take(Plc.ConsiderLastSensorsClose).Aggregate(-1L, (m, s) => m & s.PressureSensors);
|
||||||
|
|
||||||
MP1_WantsPathClearance = WantsPathClearnace(this, Paths.MP1);
|
MP1_WantsPathClearance = WantsPathClearnace(this, Paths.MP1);
|
||||||
MP2_WantsPathClearance = WantsPathClearnace(this, Paths.MP2);
|
MP2_WantsPathClearance = WantsPathClearnace(this, Paths.MP2);
|
||||||
P12_WantsPathClearance = WantsPathClearnace(this, Paths.P12);
|
P12_WantsPathClearance = WantsPathClearnace(this, Paths.P12);
|
||||||
@@ -143,8 +147,10 @@ namespace PamhagenSysCtrl.Helpers.Plc {
|
|||||||
P6_DryRunClearance = true;
|
P6_DryRunClearance = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Valve_IsClosed(int n) => Sensors.GetDS(n);
|
public bool Valve_IsClosed(int n) => (ClosedValves & (1L << (n - 1))) != 0;
|
||||||
public bool Valve_IsOpen(int n) => !Valve_IsClosed(n);
|
public bool Valve_IsTransitioning(int n) => !Valve_IsClosed(n) && !Valve_IsOpen(n);
|
||||||
|
public bool Valve_IsOpen(int n) => (OpenValves & (1L << (n - 1))) != 0;
|
||||||
|
public bool? Valve_OpenState(int n) => Valve_IsOpen(n) ? true : Valve_IsClosed(n) ? false : null;
|
||||||
public bool Valve_WantOpen(int n) => Actuators.GetV(n);
|
public bool Valve_WantOpen(int n) => Actuators.GetV(n);
|
||||||
public bool Valve_WantClosed(int n) => !Valve_WantOpen(n);
|
public bool Valve_WantClosed(int n) => !Valve_WantOpen(n);
|
||||||
|
|
||||||
@@ -164,11 +170,11 @@ namespace PamhagenSysCtrl.Helpers.Plc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static bool HasSinglePathClearance(PamhagenState state, IEnumerable<ValveStateMask> masks) {
|
private static bool HasSinglePathClearance(PamhagenState state, IEnumerable<ValveStateMask> masks) {
|
||||||
return masks.Any(m => state.Valves == m && ~state.PressureSensors == m);
|
return masks.Any(m => state.Valves == m && state.OpenValves == m && ~state.ClosedValves == m);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HasAnyPathClearance(PamhagenState state, IEnumerable<ValveStateMask> masks) {
|
private static bool HasAnyPathClearance(PamhagenState state, IEnumerable<ValveStateMask> masks) {
|
||||||
return masks.Any(m => state.Valves >= m && ~state.PressureSensors >= m);
|
return masks.Any(m => state.Valves >= m && state.OpenValves >= m && ~state.ClosedValves >= m);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool WantsSinglePathClearance(PamhagenState state, IEnumerable<ValveStateMask> masks) {
|
private static bool WantsSinglePathClearance(PamhagenState state, IEnumerable<ValveStateMask> masks) {
|
||||||
|
|||||||
Reference in New Issue
Block a user