Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f076919c7 | ||
|
|
947d581c11 | ||
|
|
334f201a32 | ||
|
|
24beafd9e5 |
No files matched your search
@@ -36,15 +36,11 @@ namespace PamhagenSysCtrl {
|
|||||||
protected override async void OnStartup(StartupEventArgs evt) {
|
protected override async void OnStartup(StartupEventArgs evt) {
|
||||||
Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0");
|
Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0");
|
||||||
|
|
||||||
|
Plant = new(Config.PlcPort);
|
||||||
try {
|
try {
|
||||||
Plant = new(Config.PlcPort);
|
|
||||||
await Plant.StartThread();
|
await Plant.StartThread();
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
Plant?.Dispose();
|
// TODO
|
||||||
Plant = null;
|
|
||||||
var str = $"Beim Aufbauen einer Verbindung zur SPS ist ein Fehler aufgetreten:\n\n{exc.GetType().Name}: {exc.Message}";
|
|
||||||
if (exc.InnerException != null) str += $"\n\n{exc.InnerException.GetType().Name}: {exc.InnerException.Message}";
|
|
||||||
MessageBox.Show(str, "SPS-Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnStartup(evt);
|
base.OnStartup(evt);
|
||||||
|
|||||||
@@ -512,7 +512,6 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
HashSet<IEdge> visited = [];
|
HashSet<IEdge> visited = [];
|
||||||
foreach (var (edge, node) in path.Hops) {
|
foreach (var (edge, node) in path.Hops) {
|
||||||
edge.Highlight = color1;
|
edge.Highlight = color1;
|
||||||
(edge.Start as PipeJoin)?.Highlight = color1;
|
|
||||||
if (flowArrows > MotorState.Halt) {
|
if (flowArrows > MotorState.Halt) {
|
||||||
var dir = edge.End == node;
|
var dir = edge.End == node;
|
||||||
if (flowArrows == MotorState.Backward) dir = !dir;
|
if (flowArrows == MotorState.Backward) dir = !dir;
|
||||||
@@ -521,10 +520,7 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
}
|
}
|
||||||
visited.Add(edge);
|
visited.Add(edge);
|
||||||
if (node is Pump) color = color2;
|
if (node is Pump) color = color2;
|
||||||
TraverseSubgraph(node, (n) => n is Valve || n is ISink || n is Pump, visited, edgeAction: (s, r, e) => {
|
TraverseSubgraph(node, (n) => n is Valve || n is ISink || n is Pump, visited, edgeAction: (s, r, e) => e.Highlight = color);
|
||||||
(s as PipeJoin)?.Highlight = color;
|
|
||||||
e.Highlight = color;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,8 +530,6 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
edgeAction: (s, r, e) => {
|
edgeAction: (s, r, e) => {
|
||||||
if (((e.Start as Valve)?.IsOpen ?? true) && ((e.End as Valve)?.IsOpen ?? true)) {
|
if (((e.Start as Valve)?.IsOpen ?? true) && ((e.End as Valve)?.IsOpen ?? true)) {
|
||||||
e.Highlight = color1;
|
e.Highlight = color1;
|
||||||
(e.Start as PipeJoin)?.Highlight = color1;
|
|
||||||
(e.End as PipeJoin)?.Highlight = color1;
|
|
||||||
if (flowArrows == MotorState.Forward || flowArrows == MotorState.Backward) {
|
if (flowArrows == MotorState.Forward || flowArrows == MotorState.Backward) {
|
||||||
var dir = e.Start == s;
|
var dir = e.Start == s;
|
||||||
if (r) dir = !dir;
|
if (r) dir = !dir;
|
||||||
|
|||||||
@@ -1,10 +1,35 @@
|
|||||||
|
using PamhagenSysCtrl.Helpers.Pipeline;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
namespace PamhagenSysCtrl.Helpers {
|
namespace PamhagenSysCtrl.Helpers {
|
||||||
public class PamhagenPlant : IDisposable {
|
public class PamhagenPlant : IDisposable {
|
||||||
|
|
||||||
|
public enum Xbe24Key {
|
||||||
|
START_REBLER = 6, STOP_REBLER = 7,
|
||||||
|
START_TROUGH_AUGER = 12, STOP_TROUGH_AUGER = 13,
|
||||||
|
SELECT_MW1 = 14, SELECT_MW2 = 8,
|
||||||
|
STIRRER_MW1 = 15, STIRRER_MW2 = 9,
|
||||||
|
START_MP1 = 16, STOP_MP1 = 17,
|
||||||
|
START_MP2 = 10, STOP_MP2 = 11,
|
||||||
|
FAST_SELECT_MW1_1 = 18,
|
||||||
|
FAST_SELECT_MW1_2 = 19,
|
||||||
|
FAST_SELECT_MW1_3 = 20,
|
||||||
|
FAST_SELECT_MW1_4 = 21,
|
||||||
|
FAST_SELECT_MW1_5 = 22,
|
||||||
|
FAST_SELECT_MW1_6 = 23,
|
||||||
|
FAST_SELECT_MW2_1 = 0,
|
||||||
|
FAST_SELECT_MW2_2 = 1,
|
||||||
|
FAST_SELECT_MW2_3 = 2,
|
||||||
|
FAST_SELECT_MW2_4 = 3,
|
||||||
|
FAST_SELECT_MW2_5 = 4,
|
||||||
|
FAST_SELECT_MW2_6 = 5,
|
||||||
|
}
|
||||||
|
|
||||||
protected PamhagenPlc? Plc;
|
protected PamhagenPlc? Plc;
|
||||||
|
protected Xbe24Keyboard? Keyboard;
|
||||||
|
public PamhagenGraph Graph;
|
||||||
|
|
||||||
protected bool IsRunning = true;
|
protected bool IsRunning = true;
|
||||||
protected Thread? BackgroundThread;
|
protected Thread? BackgroundThread;
|
||||||
@@ -21,6 +46,11 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown
|
FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown
|
||||||
], default);
|
], default);
|
||||||
|
|
||||||
|
public string? PlcPortName => Plc?.PortName;
|
||||||
|
public T1.Status? LastPlcStatus => Plc?.LastPlcStatus;
|
||||||
|
public T1Error? LastPlcError => Plc?.LastPlcError;
|
||||||
|
public long? LastRttNs { get; private set; }
|
||||||
|
|
||||||
public bool IsValveOpen(int n) => !IsValveClosed(n);
|
public bool IsValveOpen(int n) => !IsValveClosed(n);
|
||||||
public bool IsValveClosed(int n) => Sensors.GetDS(n);
|
public bool IsValveClosed(int n) => Sensors.GetDS(n);
|
||||||
public bool WantValveClosed(int n) => !WantValveOpen(n);
|
public bool WantValveClosed(int n) => !WantValveOpen(n);
|
||||||
@@ -73,8 +103,25 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
public bool IsP6Active => Actuators.P6 == MotorState.Forward;
|
public bool IsP6Active => Actuators.P6 == MotorState.Forward;
|
||||||
|
|
||||||
public PamhagenPlant(string portName) {
|
public PamhagenPlant(string portName) {
|
||||||
Plc = new(portName);
|
try {
|
||||||
|
Plc = new(portName);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
var str = $"Beim Aufbauen einer Verbindung zur SPS ist ein Fehler aufgetreten:\n\n{exc.GetType().Name}: {exc.Message}";
|
||||||
|
if (exc.InnerException != null) str += $"\n\n{exc.InnerException.GetType().Name}: {exc.InnerException.Message}";
|
||||||
|
MessageBox.Show(str, "SPS-Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
Graph = new();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Keyboard = new();
|
||||||
|
Keyboard.KeyDown += Keyboard_KeyDown;
|
||||||
|
Keyboard.KeysChanged += Keyboard_KeysChanged;
|
||||||
|
Keyboard.SetFashFrequency(250);
|
||||||
|
Keyboard.SetBacklight(Color.FromRgb(4, 4, 4));
|
||||||
|
} catch { }
|
||||||
|
|
||||||
Update += OnUpdate;
|
Update += OnUpdate;
|
||||||
|
Update += UpdateKeyboardBacklights;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void RaiseUpdateEvent(PlantEventArgs evt) {
|
protected virtual void RaiseUpdateEvent(PlantEventArgs evt) {
|
||||||
@@ -91,6 +138,10 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
if (p.IsAuger2Active && !p.IsAuger3Active) p.StartAuger3();
|
if (p.IsAuger2Active && !p.IsAuger3Active) p.StartAuger3();
|
||||||
if (p.IsTroughAugerActive && !p.TroughAugerHasClearance)
|
if (p.IsTroughAugerActive && !p.TroughAugerHasClearance)
|
||||||
p.StopTroughAuger();
|
p.StopTroughAuger();
|
||||||
|
if (!p.IsPresseQuerSchneckeActive && !p.IsAuger1Active) {
|
||||||
|
p.StopAuger2();
|
||||||
|
p.StopAuger3();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p.IsMP1Active && !p.MP1HasClearance) p.StopMP1();
|
if (p.IsMP1Active && !p.MP1HasClearance) p.StopMP1();
|
||||||
@@ -116,6 +167,66 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
if (p.IsP6Active && !p.P6HasClearance) p.StopP6();
|
if (p.IsP6Active && !p.P6HasClearance) p.StopP6();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void UpdateKeyboardBacklights(object? sender, PlantEventArgs evt) {
|
||||||
|
if (sender is not PamhagenPlant plant || plant.Keyboard is not Xbe24Keyboard k) return;
|
||||||
|
|
||||||
|
k.SetBacklight((int)Xbe24Key.START_REBLER, plant.IsReblerActive ? Color.FromRgb(0, 255, 0) : plant.IsAuger1Active ? Color.FromRgb(255, 255, 0) : plant.IsAuger2Active || plant.IsAuger3Active ? Color.FromRgb(32, 32, 4) : Color.FromRgb(4, 32, 4));
|
||||||
|
k.SetBacklight((int)Xbe24Key.STOP_REBLER, !plant.IsReblerActive ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4));
|
||||||
|
|
||||||
|
k.SetBacklight((int)Xbe24Key.START_TROUGH_AUGER, !plant.TroughAugerHasClearance ? k.IsKeyPressed((int)Xbe24Key.START_TROUGH_AUGER) ? Color.FromRgb(255, 32, 0) : Color.FromRgb(32, 4, 4) : plant.IsTroughAugerActive ? Color.FromRgb(0, 255, 0) : Color.FromRgb(4, 32, 4));
|
||||||
|
k.SetBacklight((int)Xbe24Key.STOP_TROUGH_AUGER, !plant.IsTroughAugerActive ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4));
|
||||||
|
|
||||||
|
k.SetBacklight((int)Xbe24Key.SELECT_MW2, plant.WantMW2Selected || plant.IsMW2Selected ? Color.FromRgb(0, 0, 255) : Color.FromRgb(4, 4, 16), plant.WantMW2Selected && !plant.IsMW2Selected);
|
||||||
|
k.SetBacklight((int)Xbe24Key.SELECT_MW1, plant.WantMW1Selected || plant.IsMW1Selected ? Color.FromRgb(0, 0, 255) : Color.FromRgb(4, 4, 16), plant.WantMW1Selected && !plant.IsMW1Selected);
|
||||||
|
|
||||||
|
var buttons = new Dictionary<Xbe24Key, (ISource Source, Pump Pump, ISink Sink)> {
|
||||||
|
[Xbe24Key.FAST_SELECT_MW1_1] = (Graph.MW1, Graph.MP1, Graph.Press1),
|
||||||
|
[Xbe24Key.FAST_SELECT_MW2_1] = (Graph.MW2, Graph.MP2, Graph.Press1),
|
||||||
|
[Xbe24Key.FAST_SELECT_MW1_2] = (Graph.MW1, Graph.MP1, Graph.Press2),
|
||||||
|
[Xbe24Key.FAST_SELECT_MW2_2] = (Graph.MW2, Graph.MP2, Graph.Press2),
|
||||||
|
[Xbe24Key.FAST_SELECT_MW1_3] = (Graph.MW1, Graph.MP1, Graph.Tank1),
|
||||||
|
[Xbe24Key.FAST_SELECT_MW2_3] = (Graph.MW2, Graph.MP2, Graph.Tank1),
|
||||||
|
[Xbe24Key.FAST_SELECT_MW1_4] = (Graph.MW1, Graph.MP1, Graph.Tank2),
|
||||||
|
[Xbe24Key.FAST_SELECT_MW2_4] = (Graph.MW2, Graph.MP2, Graph.Tank2),
|
||||||
|
[Xbe24Key.FAST_SELECT_MW1_5] = (Graph.MW1, Graph.MP1, Graph.Tank3),
|
||||||
|
[Xbe24Key.FAST_SELECT_MW2_5] = (Graph.MW2, Graph.MP2, Graph.Tank3),
|
||||||
|
[Xbe24Key.FAST_SELECT_MW1_6] = (Graph.MW1, Graph.MP1, Graph.Tank4),
|
||||||
|
[Xbe24Key.FAST_SELECT_MW2_6] = (Graph.MW2, Graph.MP2, Graph.Tank4),
|
||||||
|
};
|
||||||
|
|
||||||
|
bool preparingMP1 = false, preparingMP2 = false;
|
||||||
|
bool highlightOffMP1 = false, highlightOffMP2 = false;
|
||||||
|
foreach (var (idx, d) in buttons) {
|
||||||
|
var isRunning = d.Pump.IsActive;
|
||||||
|
if (plant.Graph.SelectedPaths.Select(p => (Pipeline.Path?)p).FirstOrDefault(p => p?.Start == d.Source && p?.Hops.Last().Node == d.Sink) is Pipeline.Path path) {
|
||||||
|
// TODO
|
||||||
|
var isReady = path.Hops.All(h => h.Node is not ControlledValve v || v.IsOpen == v.WantOpen);
|
||||||
|
if (!isReady && d.Pump == Graph.MP1) preparingMP1 = true;
|
||||||
|
if (!isReady && d.Pump == Graph.MP2) preparingMP2 = true;
|
||||||
|
var isDefault = !path.Hops.SkipLast(1).Any(h => h.Node is not Valve && h.Node is not PipeJoin && h.Node is not Pump);
|
||||||
|
k.SetBacklight((int)idx, isRunning ? isDefault ? Color.FromRgb(0, 255, 255) : Color.FromRgb(255, 64, 255) : isDefault ? Color.FromRgb(0, 0, 255) : Color.FromRgb(128, 0, 128), !isReady);
|
||||||
|
} else {
|
||||||
|
var isFree = Graph.GetFreePath([d.Source, d.Sink], overrideStart: true) != null;
|
||||||
|
if (isRunning && k.IsKeyPressed((int)idx) && d.Pump == Graph.MP1) highlightOffMP1 = true;
|
||||||
|
if (isRunning && k.IsKeyPressed((int)idx) && d.Pump == Graph.MP2) highlightOffMP2 = true;
|
||||||
|
k.SetBacklight((int)idx, isRunning ? k.IsKeyPressed((int)idx) ? Color.FromRgb(255, 16, 0) : Color.FromRgb(4, 4, 4) : isFree ? Color.FromRgb(8, 8, 32) : k.IsKeyPressed((int)idx) ? Color.FromRgb(255, 32, 0) : Color.FromRgb(4, 4, 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!plant.MP2HasPathClearance && plant.MP2HasDryRunClearance && preparingMP2) {
|
||||||
|
k.SetBacklight((int)Xbe24Key.START_MP2, Color.FromRgb(4, 32, 4), true);
|
||||||
|
} else {
|
||||||
|
k.SetBacklight((int)Xbe24Key.START_MP2, !plant.MP2HasClearance ? k.IsKeyPressed((int)Xbe24Key.START_MP2) ? Color.FromRgb(255, 32, 0) : Color.FromRgb(32, 4, 4) : plant.IsMP2Active ? Color.FromRgb(0, 255, 0) : Color.FromRgb(4, 32, 4));
|
||||||
|
}
|
||||||
|
if (!plant.MP1HasPathClearance && plant.MP1HasDryRunClearance && preparingMP1) {
|
||||||
|
k.SetBacklight((int)Xbe24Key.START_MP1, Color.FromRgb(4, 32, 4), true);
|
||||||
|
} else {
|
||||||
|
k.SetBacklight((int)Xbe24Key.START_MP1, !plant.MP1HasClearance ? k.IsKeyPressed((int)Xbe24Key.START_MP1) ? Color.FromRgb(255, 32, 0) : Color.FromRgb(32, 4, 4) : plant.IsMP1Active ? Color.FromRgb(0, 255, 0) : Color.FromRgb(4, 32, 4));
|
||||||
|
}
|
||||||
|
k.SetBacklight((int)Xbe24Key.STOP_MP2, highlightOffMP2 ? Color.FromRgb(255, 32, 0) : !plant.IsMP2Active ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4));
|
||||||
|
k.SetBacklight((int)Xbe24Key.STOP_MP1, highlightOffMP1 ? Color.FromRgb(255, 32, 0) : !plant.IsMP1Active ? Color.FromRgb(255, 0, 0) : Color.FromRgb(32, 4, 4));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task StartThread() {
|
public async Task StartThread() {
|
||||||
if (Plc == null) throw new Exception();
|
if (Plc == null) throw new Exception();
|
||||||
await Plc.TestConnection();
|
await Plc.TestConnection();
|
||||||
@@ -128,19 +239,23 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
IsRunning = false;
|
IsRunning = false;
|
||||||
BackgroundThread?.Join();
|
BackgroundThread?.Join();
|
||||||
Plc?.Dispose();
|
Plc?.Dispose();
|
||||||
|
Keyboard?.Dispose();
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async void BackgroundLoop(object? parameters) {
|
protected async void BackgroundLoop(object? parameters) {
|
||||||
while (IsRunning && Plc != null) {
|
while (IsRunning && Plc != null) {
|
||||||
|
var start = DateTime.Now;
|
||||||
try {
|
try {
|
||||||
if (ActuatorsChanged) {
|
if (ActuatorsChanged) {
|
||||||
await Plc.WriteOutputs(Actuators);
|
await Plc.WriteOutputs(Actuators);
|
||||||
ActuatorsChanged = false;
|
ActuatorsChanged = false;
|
||||||
}
|
}
|
||||||
Sensors = await Plc.ReadInputs();
|
Sensors = await Plc.ReadInputs();
|
||||||
|
LastRttNs = (long)(DateTime.Now - start).TotalNanoseconds;
|
||||||
RaiseUpdateEvent(new());
|
RaiseUpdateEvent(new());
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
|
LastRttNs = null;
|
||||||
var str = "Bei der SPS ist ein Fehler aufgetreten:\n\n" + exc.Message;
|
var str = "Bei der SPS ist ein Fehler aufgetreten:\n\n" + exc.Message;
|
||||||
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
||||||
MessageBox.Show(str, "SPS-Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show(str, "SPS-Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
@@ -163,6 +278,115 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void InitializePaths() {
|
||||||
|
var add = ReconstructSelectedPaths().ToList();
|
||||||
|
var remove = Graph.SelectedPaths.ToList();
|
||||||
|
foreach (var p in remove) Graph.RemovePath(p);
|
||||||
|
for (int i = 1; i <= 57; i++) {
|
||||||
|
App.Plant?.CloseV(i);
|
||||||
|
}
|
||||||
|
foreach (var p in add) {
|
||||||
|
if (p.Hops.Any(h => h.Node is ControlledValve v && v.IsLocked)) continue;
|
||||||
|
Graph.AddPath(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Pipeline.Path> ReconstructSelectedPaths() {
|
||||||
|
if (App.Plant is not PamhagenPlant p) yield break;
|
||||||
|
var sources = Graph.Nodes.Where(n => n is ISource).ToList();
|
||||||
|
var sinks = Graph.Nodes.Where(n => n is ISink).ToList();
|
||||||
|
foreach (var src in sources) {
|
||||||
|
var paths = new List<Pipeline.Path>();
|
||||||
|
foreach (var sink in sinks) {
|
||||||
|
paths.AddRange(Graph.GetPaths(src, sink, null, (n) => n is not ISink && (n is not ControlledValve v || p.WantValveOpen(v.Nr))));
|
||||||
|
}
|
||||||
|
if (paths.Count == 1)
|
||||||
|
yield return paths.First();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FastSelect(ISource source, ISink sink) {
|
||||||
|
if (Graph.SelectedPaths.Select(p => (Pipeline.Path?)p).FirstOrDefault(p => p?.Start == source, null) is Pipeline.Path p1) {
|
||||||
|
if (p1.Hops.Any(h => h.Node is Pump p && p.IsActive))
|
||||||
|
throw new NoClearanceException("Pumpe aktiv");
|
||||||
|
Graph.RemovePath(p1);
|
||||||
|
if (p1.Hops.Last().Node == sink)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Graph.GetFreePath([source, sink]) is Pipeline.Path p2) {
|
||||||
|
Graph.AddPath(p2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Keyboard_KeyDown(object? sender, int keyIndex) {
|
||||||
|
var key = (Xbe24Key)keyIndex;
|
||||||
|
App.MainDispatcher?.BeginInvoke(() => {
|
||||||
|
try {
|
||||||
|
if (key == Xbe24Key.START_REBLER) {
|
||||||
|
StartRebler();
|
||||||
|
} else if (key == Xbe24Key.STOP_REBLER) {
|
||||||
|
if (IsReblerActive) {
|
||||||
|
StopRebler();
|
||||||
|
} else {
|
||||||
|
StopAuger1();
|
||||||
|
if (!IsPresseQuerSchneckeActive) {
|
||||||
|
StopAuger2();
|
||||||
|
StopAuger3();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (key == Xbe24Key.START_TROUGH_AUGER) {
|
||||||
|
StartTroughAuger();
|
||||||
|
} else if (key == Xbe24Key.STOP_TROUGH_AUGER) {
|
||||||
|
StopTroughAuger();
|
||||||
|
} else if (key == Xbe24Key.SELECT_MW2) {
|
||||||
|
SelectMW2();
|
||||||
|
} else if (key == Xbe24Key.SELECT_MW1) {
|
||||||
|
SelectMW1();
|
||||||
|
} else if (key == Xbe24Key.START_MP2) {
|
||||||
|
StartMP2Forward();
|
||||||
|
} else if (key == Xbe24Key.STOP_MP2) {
|
||||||
|
StopMP2();
|
||||||
|
} else if (key == Xbe24Key.START_MP1) {
|
||||||
|
StartMP1Forward();
|
||||||
|
} else if (key == Xbe24Key.STOP_MP1) {
|
||||||
|
StopMP1();
|
||||||
|
} else if (key == Xbe24Key.FAST_SELECT_MW1_1) {
|
||||||
|
FastSelect(Graph.MW1, Graph.Press1);
|
||||||
|
} else if (key == Xbe24Key.FAST_SELECT_MW1_2) {
|
||||||
|
FastSelect(Graph.MW1, Graph.Press2);
|
||||||
|
} else if (key == Xbe24Key.FAST_SELECT_MW1_3) {
|
||||||
|
FastSelect(Graph.MW1, Graph.Tank1);
|
||||||
|
} else if (key == Xbe24Key.FAST_SELECT_MW1_4) {
|
||||||
|
FastSelect(Graph.MW1, Graph.Tank2);
|
||||||
|
} else if (key == Xbe24Key.FAST_SELECT_MW1_5) {
|
||||||
|
FastSelect(Graph.MW1, Graph.Tank3);
|
||||||
|
} else if (key == Xbe24Key.FAST_SELECT_MW1_6) {
|
||||||
|
FastSelect(Graph.MW1, Graph.Tank4);
|
||||||
|
} else if (key == Xbe24Key.FAST_SELECT_MW2_1) {
|
||||||
|
FastSelect(Graph.MW2, Graph.Press1);
|
||||||
|
} else if (key == Xbe24Key.FAST_SELECT_MW2_2) {
|
||||||
|
FastSelect(Graph.MW2, Graph.Press2);
|
||||||
|
} else if (key == Xbe24Key.FAST_SELECT_MW2_3) {
|
||||||
|
FastSelect(Graph.MW2, Graph.Tank1);
|
||||||
|
} else if (key == Xbe24Key.FAST_SELECT_MW2_4) {
|
||||||
|
FastSelect(Graph.MW2, Graph.Tank2);
|
||||||
|
} else if (key == Xbe24Key.FAST_SELECT_MW2_5) {
|
||||||
|
FastSelect(Graph.MW2, Graph.Tank3);
|
||||||
|
} else if (key == Xbe24Key.FAST_SELECT_MW2_6) {
|
||||||
|
FastSelect(Graph.MW2, Graph.Tank4);
|
||||||
|
}
|
||||||
|
} catch (NoClearanceException) {
|
||||||
|
// Keyboard?.SetBacklight(keyIndex, Color.FromRgb(255, 0, 0));
|
||||||
|
} catch (Exception) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Keyboard_KeysChanged(object? sender, Xbe24Keyboard.KeyState state) {
|
||||||
|
RaiseUpdateEvent(new());
|
||||||
|
}
|
||||||
|
|
||||||
protected bool HasWTClearanceAA =>
|
protected bool HasWTClearanceAA =>
|
||||||
(AreValvesOpen(12) && AreValvesClosed(15, 16, 20)) || // bypass
|
(AreValvesOpen(12) && AreValvesClosed(15, 16, 20)) || // bypass
|
||||||
(AreValvesOpen(15, 18, 16) && AreValvesClosed(11, 12, 14, 17, 19, 20)) || // WT1
|
(AreValvesOpen(15, 18, 16) && AreValvesClosed(11, 12, 14, 17, 19, 20)) || // WT1
|
||||||
@@ -233,7 +457,9 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
(a && ((HasWTClearanceAA && HasT9ClearanceA) || (HasWTClearanceAB && HasT9ClearanceB))) ||
|
(a && ((HasWTClearanceAA && HasT9ClearanceA) || (HasWTClearanceAB && HasT9ClearanceB))) ||
|
||||||
(b && ((HasWTClearanceBA && HasT9ClearanceA) || (HasWTClearanceBB && HasT9ClearanceB)));
|
(b && ((HasWTClearanceBA && HasT9ClearanceA) || (HasWTClearanceBB && HasT9ClearanceB)));
|
||||||
|
|
||||||
public bool MP1HasClearance => (OverrideDryRunClearances || FillLevelMW1 != FillState.Empty) && (
|
public bool MP1HasClearance => MP1HasDryRunClearance && MP1HasPathClearance;
|
||||||
|
public bool MP1HasDryRunClearance => (OverrideDryRunClearances || FillLevelMW1 != FillState.Empty);
|
||||||
|
public bool MP1HasPathClearance => (
|
||||||
OverridePathClearances ||
|
OverridePathClearances ||
|
||||||
(MP1HasClearancePress1 && Press1HasClearance) ||
|
(MP1HasClearancePress1 && Press1HasClearance) ||
|
||||||
(MP1HasClearancePress2 && Press2HasClearance) ||
|
(MP1HasClearancePress2 && Press2HasClearance) ||
|
||||||
@@ -261,7 +487,9 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
set => Actuators.MP1 = (value != MotorState.Halt && value != MotorState.Forward && value != MotorState.Backward) ? throw new ArgumentException("Invalid state for MP1") : value;
|
set => Actuators.MP1 = (value != MotorState.Halt && value != MotorState.Forward && value != MotorState.Backward) ? throw new ArgumentException("Invalid state for MP1") : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool MP2HasClearance => (OverrideDryRunClearances || FillLevelMW2 != FillState.Empty) && (
|
public bool MP2HasClearance => MP2HasDryRunClearance && MP2HasPathClearance;
|
||||||
|
public bool MP2HasDryRunClearance => (OverrideDryRunClearances || FillLevelMW2 != FillState.Empty);
|
||||||
|
public bool MP2HasPathClearance => (
|
||||||
OverridePathClearances ||
|
OverridePathClearances ||
|
||||||
(MP2HasClearancePress1 && Press1HasClearance) ||
|
(MP2HasClearancePress1 && Press1HasClearance) ||
|
||||||
(MP2HasClearancePress2 && Press2HasClearance) ||
|
(MP2HasClearancePress2 && Press2HasClearance) ||
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
|
|
||||||
protected readonly T1 Plc;
|
protected readonly T1 Plc;
|
||||||
|
|
||||||
|
public string PortName => Plc.PortName;
|
||||||
|
public T1.Status LastPlcStatus => Plc.LastStatus;
|
||||||
|
public T1Error LastPlcError => Plc.LastError;
|
||||||
|
|
||||||
public PamhagenPlc(string portName, Parity parity = Parity.Odd) {
|
public PamhagenPlc(string portName, Parity parity = Parity.Odd) {
|
||||||
Plc = new T1(portName, parity);
|
Plc = new T1(portName, parity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
namespace PamhagenSysCtrl.Helpers.Pipeline {
|
namespace PamhagenSysCtrl.Helpers.Pipeline {
|
||||||
public class PipeJoin : INode {
|
public class PipeJoin : INode {
|
||||||
@@ -11,12 +12,11 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
|
|||||||
public double CenterX { get; set; }
|
public double CenterX { get; set; }
|
||||||
public double CenterY { get; set; }
|
public double CenterY { get; set; }
|
||||||
public string Label => "";
|
public string Label => "";
|
||||||
public Brush? Highlight { get; set; }
|
|
||||||
|
|
||||||
public ISet<IEdge> Inputs { get; init; }
|
public ISet<IEdge> Inputs { get; init; }
|
||||||
public ISet<IEdge> Outputs { get; init; }
|
public ISet<IEdge> Outputs { get; init; }
|
||||||
|
|
||||||
private System.Windows.Shapes.Path? _rect;
|
private Polygon? _rect;
|
||||||
|
|
||||||
public PipeJoin(double x, double y) {
|
public PipeJoin(double x, double y) {
|
||||||
CenterX = x;
|
CenterX = x;
|
||||||
@@ -26,8 +26,8 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Draw(Canvas canvas) {
|
public void Draw(Canvas canvas) {
|
||||||
_rect = new System.Windows.Shapes.Path() {
|
_rect = new Polygon() {
|
||||||
Fill = Brushes.DarkGray,
|
Fill = Brushes.Black,
|
||||||
Stroke = Brushes.Black,
|
Stroke = Brushes.Black,
|
||||||
};
|
};
|
||||||
canvas.Children.Add(_rect);
|
canvas.Children.Add(_rect);
|
||||||
@@ -43,66 +43,43 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
|
|||||||
var il = Inputs.Any(e => (e.Start.CenterX < CenterX && (e.Orientation || e.Start.CenterY == CenterY)) || (e.IsTwoWay && e.End.CenterX < CenterX && (e.Orientation || e.End.CenterY == CenterY)));
|
var il = Inputs.Any(e => (e.Start.CenterX < CenterX && (e.Orientation || e.Start.CenterY == CenterY)) || (e.IsTwoWay && e.End.CenterX < CenterX && (e.Orientation || e.End.CenterY == CenterY)));
|
||||||
var ol = Outputs.Any(e => (e.End.CenterX < CenterX && (!e.Orientation || e.End.CenterY == CenterY)) || (e.IsTwoWay && e.Start.CenterX < CenterX && (!e.Orientation || e.Start.CenterY == CenterY)));
|
var ol = Outputs.Any(e => (e.End.CenterX < CenterX && (!e.Orientation || e.End.CenterY == CenterY)) || (e.IsTwoWay && e.Start.CenterX < CenterX && (!e.Orientation || e.Start.CenterY == CenterY)));
|
||||||
|
|
||||||
var size = new Size(SIZE / 2 * scale, SIZE / 2 * scale);
|
var points = new List<Point> {
|
||||||
var geo = new StreamGeometry();
|
new(0, 0)
|
||||||
using (var ctx = geo.Open()) {
|
};
|
||||||
var points = new List<Point> {
|
if (ot && it) {
|
||||||
new(0, 0)
|
points.AddRange([new(SIZE / 2 - POINT, 0), new(SIZE / 2, -POINT / 2), new(SIZE / 2, POINT / 2), new(SIZE / 2 + POINT, 0)]);
|
||||||
};
|
} else if (ot) {
|
||||||
|
points.AddRange([new(SIZE / 2 - POINT, 0), new(SIZE / 2, -POINT), new(SIZE / 2 + POINT, 0)]);
|
||||||
ctx.BeginFigure(new((SIZE / 2 - POINT) * scale, 0), true, false);
|
} else if (it) {
|
||||||
if (ot && it) {
|
points.AddRange([new(SIZE / 2 - POINT, 0), new(SIZE / 2, POINT), new(SIZE / 2 + POINT, 0)]);
|
||||||
points.AddRange([new(SIZE / 2, -POINT / 2), new(SIZE / 2, POINT / 2), new(SIZE / 2 + POINT, 0)]);
|
}
|
||||||
} else if (ot) {
|
points.Add(new(SIZE, 0));
|
||||||
ctx.PolyLineTo([new(SIZE / 2 * scale, -POINT * scale), new((SIZE / 2 + POINT) * scale, 0)], true, false);
|
if (or && ir) {
|
||||||
} else if (it) {
|
points.AddRange([new(SIZE, SIZE / 2 - POINT), new(SIZE + POINT / 2, SIZE / 2), new(SIZE - POINT / 2, SIZE / 2), new(SIZE, SIZE / 2 + POINT)]);
|
||||||
ctx.PolyLineTo([new(SIZE / 2 * scale, POINT * scale), new((SIZE / 2 + POINT) * scale, 0)], true, false);
|
} else if (or) {
|
||||||
} else {
|
points.AddRange([new(SIZE, SIZE / 2 - POINT), new(SIZE + POINT, SIZE / 2), new(SIZE, SIZE / 2 + POINT)]);
|
||||||
ctx.ArcTo(new((SIZE / 2 + POINT) * scale, 0), size, 0, false, SweepDirection.Clockwise, true, false);
|
} else if (ir) {
|
||||||
}
|
points.AddRange([new(SIZE, SIZE / 2 - POINT), new(SIZE - POINT, SIZE / 2), new(SIZE, SIZE / 2 + POINT)]);
|
||||||
|
}
|
||||||
ctx.ArcTo(new(SIZE * scale, (SIZE / 2 - POINT) * scale), size, 0, false, SweepDirection.Clockwise, true, false);
|
points.Add(new(SIZE, SIZE));
|
||||||
points.Add(new(SIZE, 0));
|
if (ob && ib) {
|
||||||
if (or && ir) {
|
points.AddRange([new(SIZE / 2 + POINT, SIZE), new(SIZE / 2, SIZE + POINT / 2), new(SIZE / 2, SIZE - POINT / 2), new(SIZE / 2 - POINT, SIZE)]);
|
||||||
points.AddRange([new(SIZE, SIZE / 2 - POINT), new(SIZE + POINT / 2, SIZE / 2), new(SIZE - POINT / 2, SIZE / 2), new(SIZE, SIZE / 2 + POINT)]);
|
} else if (ob) {
|
||||||
} else if (or) {
|
points.AddRange([new(SIZE / 2 + POINT, SIZE), new(SIZE / 2, SIZE + POINT), new(SIZE / 2 - POINT, SIZE)]);
|
||||||
ctx.PolyLineTo([new((SIZE + POINT) * scale, SIZE / 2 * scale), new(SIZE * scale, (SIZE / 2 + POINT) * scale)], true, false);
|
} else if (ib) {
|
||||||
} else if (ir) {
|
points.AddRange([new(SIZE / 2 + POINT, SIZE), new(SIZE / 2, SIZE - POINT), new(SIZE / 2 - POINT, SIZE)]);
|
||||||
ctx.PolyLineTo([new((SIZE - POINT) * scale, SIZE / 2 * scale), new(SIZE * scale, (SIZE / 2 + POINT) * scale)], true, false);
|
}
|
||||||
} else {
|
points.Add(new(0, SIZE));
|
||||||
ctx.ArcTo(new(SIZE * scale, (SIZE / 2 + POINT) * scale), size, 0, false, SweepDirection.Clockwise, true, false);
|
if (ol && il) {
|
||||||
}
|
points.AddRange([new(0, SIZE / 2 + POINT), new(-POINT / 2, SIZE / 2), new(POINT / 2, SIZE / 2), new(0, SIZE / 2 - POINT)]);
|
||||||
|
} else if (ol) {
|
||||||
ctx.ArcTo(new((SIZE / 2 + POINT) * scale, SIZE * scale), size, 0, false, SweepDirection.Clockwise, true, false);
|
points.AddRange([new(0, SIZE / 2 + POINT), new(-POINT, SIZE / 2), new(0, SIZE / 2 - POINT)]);
|
||||||
points.Add(new(SIZE, SIZE));
|
} else if (il) {
|
||||||
if (ob && ib) {
|
points.AddRange([new(0, SIZE / 2 + POINT), new(POINT, SIZE / 2), new(0, SIZE / 2 - POINT)]);
|
||||||
points.AddRange([new(SIZE / 2 + POINT, SIZE), new(SIZE / 2, SIZE + POINT / 2), new(SIZE / 2, SIZE - POINT / 2), new(SIZE / 2 - POINT, SIZE)]);
|
|
||||||
} else if (ob) {
|
|
||||||
ctx.PolyLineTo([new(SIZE / 2 * scale, (SIZE + POINT) * scale), new((SIZE / 2 - POINT) * scale, SIZE * scale)], true, false);
|
|
||||||
} else if (ib) {
|
|
||||||
ctx.PolyLineTo([new(SIZE / 2 * scale, (SIZE - POINT) * scale), new((SIZE / 2 - POINT) * scale, SIZE * scale)], true, false);
|
|
||||||
} else {
|
|
||||||
ctx.ArcTo(new((SIZE / 2 - POINT) * scale, SIZE * scale), size, 0, false, SweepDirection.Clockwise, true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.ArcTo(new(0, (SIZE / 2 + POINT) * scale), size, 0, false, SweepDirection.Clockwise, true, false);
|
|
||||||
points.Add(new(0, SIZE));
|
|
||||||
if (ol && il) {
|
|
||||||
points.AddRange([new(0, SIZE / 2 + POINT), new(-POINT / 2, SIZE / 2), new(POINT / 2, SIZE / 2), new(0, SIZE / 2 - POINT)]);
|
|
||||||
} else if (ol) {
|
|
||||||
ctx.PolyLineTo([new(-POINT * scale, SIZE / 2 * scale), new(0, (SIZE / 2 - POINT) * scale)], true, false);
|
|
||||||
} else if (il) {
|
|
||||||
ctx.PolyLineTo([new(POINT * scale, SIZE / 2 * scale), new(0, (SIZE / 2 - POINT) * scale)], true, false);
|
|
||||||
} else {
|
|
||||||
ctx.ArcTo(new(0, (SIZE / 2 - POINT) * scale), size, 0, false, SweepDirection.Clockwise, true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.ArcTo(new((SIZE / 2 - POINT) * scale, 0), size, 0, false, SweepDirection.Clockwise, true, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_rect?.Data = geo;
|
_rect?.Points = [.. points.Select(p => new Point(p.X * scale, p.Y * scale))];
|
||||||
// [.. points.Select(p => new Point(p.X * scale, p.Y * scale))];
|
_rect?.StrokeThickness = 2;
|
||||||
_rect?.StrokeThickness = Graph.ToBorder(scale);
|
|
||||||
_rect?.SetValue(Canvas.LeftProperty, (CenterX - SIZE / 2) * scale);
|
_rect?.SetValue(Canvas.LeftProperty, (CenterX - SIZE / 2) * scale);
|
||||||
_rect?.SetValue(Canvas.TopProperty, (CenterY - SIZE / 2) * scale);
|
_rect?.SetValue(Canvas.TopProperty, (CenterY - SIZE / 2) * scale);
|
||||||
}
|
}
|
||||||
@@ -112,7 +89,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Update(bool isHovering, bool isPressed) {
|
public void Update(bool isHovering, bool isPressed) {
|
||||||
_rect?.Fill = Highlight ?? Brushes.DarkGray;
|
_rect?.Stroke = isHovering ? Brushes.Black : Brushes.Black;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,12 +10,16 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
|
|
||||||
protected SerialPort Serial;
|
protected SerialPort Serial;
|
||||||
|
|
||||||
|
public string PortName => Serial.PortName;
|
||||||
|
public Status LastStatus { get; private set; }
|
||||||
|
public T1Error LastError { get; private set; }
|
||||||
|
|
||||||
public T1(string portName, Parity parity = Parity.Odd) {
|
public T1(string portName, Parity parity = Parity.Odd) {
|
||||||
Serial = new SerialPort(portName, 9600, parity, 8, StopBits.One) {
|
Serial = new SerialPort(portName, 9600, parity, 8, StopBits.One) {
|
||||||
Handshake = Handshake.None,
|
Handshake = Handshake.None,
|
||||||
NewLine = "\r",
|
NewLine = "\r",
|
||||||
ReadTimeout = 1000,
|
ReadTimeout = 500,
|
||||||
WriteTimeout = 1000,
|
WriteTimeout = 500,
|
||||||
DtrEnable = true,
|
DtrEnable = true,
|
||||||
RtsEnable = true,
|
RtsEnable = true,
|
||||||
Encoding = Encoding.ASCII,
|
Encoding = Encoding.ASCII,
|
||||||
@@ -97,14 +101,16 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
public async Task<Status> StatusRead() {
|
public async Task<Status> StatusRead() {
|
||||||
await SendCommand("ST", checksum: false);
|
await SendCommand("ST", checksum: false);
|
||||||
var (_, res) = await ReceiveResponse("ST");
|
var (_, res) = await ReceiveResponse("ST");
|
||||||
return new Status((Mode)(res[3] - '0'), ((res[0] - '0') << 4) | (res[1] - '0'));
|
LastStatus = new Status((Mode)(res[3] - '0'), ((res[0] - '0') << 4) | (res[1] - '0'));
|
||||||
|
return LastStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<T1Error> ErrorStatusRead() {
|
public async Task<T1Error> ErrorStatusRead() {
|
||||||
await SendCommand("ER", checksum: false);
|
await SendCommand("ER", checksum: false);
|
||||||
var (_, res) = await ReceiveResponse("ER");
|
var (_, res) = await ReceiveResponse("ER");
|
||||||
var code = Convert.ToInt32(res, 10);
|
var code = Convert.ToInt32(res, 10);
|
||||||
return Enum.IsDefined(typeof(T1Error), code) ? (T1Error)code : T1Error.UNKNOWN_ERROR;
|
LastError = Enum.IsDefined(typeof(T1Error), code) ? (T1Error)code : T1Error.UNKNOWN_ERROR;
|
||||||
|
return LastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<ushort[]> DataRead(string register, int length) {
|
public Task<ushort[]> DataRead(string register, int length) {
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
using PIEHidNetCore;
|
||||||
|
using System.IO;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
|
namespace PamhagenSysCtrl.Helpers {
|
||||||
|
public class Xbe24Keyboard : IDisposable, PIEDataHandler, PIEErrorHandler {
|
||||||
|
|
||||||
|
public enum LedBank { UPPER = 0, LOWER = 1 }
|
||||||
|
|
||||||
|
public record struct KeyState(byte[] Rows) {
|
||||||
|
public readonly bool GetKey(int n) => (Rows[n / 6] & (1 << (n % 6))) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected readonly PIEDevice XbeDevice;
|
||||||
|
|
||||||
|
private KeyState LastKeyState = new(new byte[4]);
|
||||||
|
private readonly (Color Color, bool Flashing)[] LastStates = new (Color, bool)[48];
|
||||||
|
|
||||||
|
public bool IsKeyPressed(int keyIndex) => LastKeyState.GetKey(keyIndex);
|
||||||
|
|
||||||
|
public event EventHandler<int>? KeyDown;
|
||||||
|
public event EventHandler<int>? KeyUp;
|
||||||
|
public event EventHandler<KeyState>? KeysChanged;
|
||||||
|
|
||||||
|
public Xbe24Keyboard(int pid = 0x0555) {
|
||||||
|
foreach (var device in PIEDevice.EnumeratePIE()) {
|
||||||
|
if (device.Pid == pid && device.HidUsagePage == 0x0C && device.HidUsage == 1 && device.WriteLength > 1) {
|
||||||
|
XbeDevice = device;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (XbeDevice == null)
|
||||||
|
throw new ArgumentException($"No XBE keyboard with id 05f3:{pid:x4}");
|
||||||
|
|
||||||
|
int res;
|
||||||
|
if ((res = XbeDevice.SetupInterface()) != 0)
|
||||||
|
throw new IOException($"Unable to SetupInterface for XBE keyboard: {res}");
|
||||||
|
if ((res = XbeDevice.SetDataCallback(this)) != 0)
|
||||||
|
throw new IOException($"Unable to SetDataCallback for XBE keyboard: {res}");
|
||||||
|
if ((res = XbeDevice.SetErrorCallback(this)) != 0)
|
||||||
|
throw new IOException($"Unable to SetErrorCallback for XBE keyboard: {res}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
XbeDevice?.CloseInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandlePIEHidData(byte[] data, PIEDevice sourceDevice, int error) {
|
||||||
|
if (sourceDevice != XbeDevice)
|
||||||
|
return;
|
||||||
|
var current = new KeyState(data[3..8]);
|
||||||
|
var last = LastKeyState;
|
||||||
|
LastKeyState = current;
|
||||||
|
HandleKeyStateChange(current, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandlePIEHidError(PIEDevice sourceDevice, int error) {
|
||||||
|
if (sourceDevice != XbeDevice)
|
||||||
|
return;
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void HandleKeyStateChange(KeyState current, KeyState last) {
|
||||||
|
for (int i = 0; i < 24; i++) {
|
||||||
|
if (!last.GetKey(i) && current.GetKey(i)) {
|
||||||
|
KeyDown?.Invoke(this, i);
|
||||||
|
} else if (last.GetKey(i) && !current.GetKey(i)) {
|
||||||
|
KeyUp?.Invoke(this, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeysChanged?.Invoke(this, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void SendCommand(byte command, byte[] payload) {
|
||||||
|
byte[] data = new byte[XbeDevice.WriteLength];
|
||||||
|
data[1] = command;
|
||||||
|
Array.Copy(payload, 0, data, 2, Math.Min(payload.Length, data.Length - 2));
|
||||||
|
|
||||||
|
int res;
|
||||||
|
do {
|
||||||
|
res = XbeDevice.WriteData(data);
|
||||||
|
} while (res == 404);
|
||||||
|
if (res != 0)
|
||||||
|
throw new IOException($"Unable to WriteData for XBE keyboard: {res}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBacklight(int keyIndex, Color color, bool flashing = false) {
|
||||||
|
SetBacklight(keyIndex, LedBank.UPPER, color, flashing);
|
||||||
|
SetBacklight(keyIndex, LedBank.LOWER, color, flashing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBacklight(int keyIndex, LedBank bank, Color color, bool flashing = false) {
|
||||||
|
if (LastStates[keyIndex * 2 + (int)bank] == (color, flashing))
|
||||||
|
return;
|
||||||
|
SendCommand(165, [(byte)keyIndex, (byte)bank, color.R, color.G, color.B, (byte)(flashing ? 1 : 0)]);
|
||||||
|
LastStates[keyIndex * 2 + (int)bank] = (color, flashing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBacklight(Color color) {
|
||||||
|
SetBacklight(LedBank.UPPER, color);
|
||||||
|
SetBacklight(LedBank.LOWER, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBacklight(LedBank bank, Color color) {
|
||||||
|
SendCommand(166, [(byte)bank, color.R, color.G, color.B]);
|
||||||
|
for (int i = 0; i < LastStates.Length; i++)
|
||||||
|
LastStates[i] = (color, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFashFrequency(int ms) {
|
||||||
|
SendCommand(180, [(byte)(ms / 16)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -18,6 +18,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.11" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.11" />
|
||||||
<PackageReference Include="System.IO.Ports" Version="10.0.11" />
|
<PackageReference Include="System.IO.Ports" Version="10.0.11" />
|
||||||
|
<Reference Include="PIEHidNetCore">
|
||||||
|
<HintPath>PIEHidNetCore.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -227,7 +227,44 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<StatusBar Grid.Row="2" BorderThickness="0,1,0,0" BorderBrush="Gray">
|
<StatusBar Grid.Row="2" BorderThickness="0,1,0,0" BorderBrush="Gray">
|
||||||
|
<StatusBar.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="2*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="3*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="1*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="2*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
</Grid>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</StatusBar.ItemsPanel>
|
||||||
|
<StatusBarItem>
|
||||||
|
<TextBlock>
|
||||||
|
SPS-Verbindung: <Run x:Name="Status_Connection" Text="-"/>
|
||||||
|
</TextBlock>
|
||||||
|
</StatusBarItem>
|
||||||
|
<Separator Grid.Column="1"/>
|
||||||
|
<StatusBarItem Grid.Column="2">
|
||||||
|
<TextBlock>
|
||||||
|
SPS-Status: <Run x:Name="Status_Plc" Text="-"/>
|
||||||
|
</TextBlock>
|
||||||
|
</StatusBarItem>
|
||||||
|
<Separator Grid.Column="3"/>
|
||||||
|
<StatusBarItem Grid.Column="4">
|
||||||
|
<TextBlock>
|
||||||
|
Druckluft: <Run x:Name="Status_CompressedAir" Text="-"/>
|
||||||
|
</TextBlock>
|
||||||
|
</StatusBarItem>
|
||||||
|
<Separator Grid.Column="5"/>
|
||||||
|
<StatusBarItem Grid.Column="6">
|
||||||
|
<TextBlock>
|
||||||
|
Ventile offen: <Run x:Name="Status_ValvesOpen" Text="-"/>
|
||||||
|
</TextBlock>
|
||||||
|
</StatusBarItem>
|
||||||
</StatusBar>
|
</StatusBar>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Window>
|
</Window>
|
||||||
@@ -11,7 +11,7 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
|
|
||||||
private const double _minMove = 50;
|
private const double _minMove = 50;
|
||||||
|
|
||||||
private readonly PamhagenGraph Graph;
|
private static PamhagenGraph Graph => App.Plant!.Graph;
|
||||||
|
|
||||||
private Point? _lastMousePos;
|
private Point? _lastMousePos;
|
||||||
private bool _mouseDragging;
|
private bool _mouseDragging;
|
||||||
@@ -50,26 +50,25 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
public PlantSchemeWindow() {
|
public PlantSchemeWindow() {
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
Menu_Help_CheckForUpdates.IsEnabled = App.Config.UpdateUrl != null;
|
Menu_Help_CheckForUpdates.IsEnabled = App.Config.UpdateUrl != null;
|
||||||
Graph = new();
|
|
||||||
Graph.Draw(SchemeCanvas, RoundedScale);
|
Graph.Draw(SchemeCanvas, RoundedScale);
|
||||||
FastSelectPaths = new Dictionary<Button, (ISource, Pump, ISink)> {
|
FastSelectPaths = new Dictionary<Button, (ISource, Pump, ISink)> {
|
||||||
[FastSelectButton_MW1_Press1] = (Graph.MW1, Graph.MP1, Graph.Press1),
|
[FastSelectButton_MW1_Press1] = (Graph.MW1, Graph.MP1, Graph.Press1),
|
||||||
[FastSelectButton_MW2_Press1] = (Graph.MW2, Graph.MP2, Graph.Press1),
|
[FastSelectButton_MW2_Press1] = (Graph.MW2, Graph.MP2, Graph.Press1),
|
||||||
[FastSelectButton_MW1_Press2] = (Graph.MW1, Graph.MP1, Graph.Press2),
|
[FastSelectButton_MW1_Press2] = (Graph.MW1, Graph.MP1, Graph.Press2),
|
||||||
[FastSelectButton_MW2_Press2] = (Graph.MW2, Graph.MP2, Graph.Press2),
|
[FastSelectButton_MW2_Press2] = (Graph.MW2, Graph.MP2, Graph.Press2),
|
||||||
[FastSelectButton_MW1_Tank1] = (Graph.MW1, Graph.MP1, Graph.Tank1),
|
[FastSelectButton_MW1_Tank1] = (Graph.MW1, Graph.MP1, Graph.Tank1),
|
||||||
[FastSelectButton_MW2_Tank1] = (Graph.MW2, Graph.MP2, Graph.Tank1),
|
[FastSelectButton_MW2_Tank1] = (Graph.MW2, Graph.MP2, Graph.Tank1),
|
||||||
[FastSelectButton_MW1_Tank2] = (Graph.MW1, Graph.MP1, Graph.Tank2),
|
[FastSelectButton_MW1_Tank2] = (Graph.MW1, Graph.MP1, Graph.Tank2),
|
||||||
[FastSelectButton_MW2_Tank2] = (Graph.MW2, Graph.MP2, Graph.Tank2),
|
[FastSelectButton_MW2_Tank2] = (Graph.MW2, Graph.MP2, Graph.Tank2),
|
||||||
[FastSelectButton_MW1_Tank3] = (Graph.MW1, Graph.MP1, Graph.Tank3),
|
[FastSelectButton_MW1_Tank3] = (Graph.MW1, Graph.MP1, Graph.Tank3),
|
||||||
[FastSelectButton_MW2_Tank3] = (Graph.MW2, Graph.MP2, Graph.Tank3),
|
[FastSelectButton_MW2_Tank3] = (Graph.MW2, Graph.MP2, Graph.Tank3),
|
||||||
[FastSelectButton_MW1_Tank4] = (Graph.MW1, Graph.MP1, Graph.Tank4),
|
[FastSelectButton_MW1_Tank4] = (Graph.MW1, Graph.MP1, Graph.Tank4),
|
||||||
[FastSelectButton_MW2_Tank4] = (Graph.MW2, Graph.MP2, Graph.Tank4),
|
[FastSelectButton_MW2_Tank4] = (Graph.MW2, Graph.MP2, Graph.Tank4),
|
||||||
[FastSelectButton_MW1_Tank5] = (Graph.MW1, Graph.MP1, Graph.Tank5),
|
[FastSelectButton_MW1_Tank5] = (Graph.MW1, Graph.MP1, Graph.Tank5),
|
||||||
[FastSelectButton_MW2_Tank5] = (Graph.MW2, Graph.MP2, Graph.Tank5),
|
[FastSelectButton_MW2_Tank5] = (Graph.MW2, Graph.MP2, Graph.Tank5),
|
||||||
};
|
};
|
||||||
App.Plant?.Update += OnUpdate;
|
App.Plant?.Update += OnUpdate;
|
||||||
InitializePaths();
|
App.Plant?.InitializePaths();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClosed(object sender, EventArgs evt) {
|
private void OnClosed(object sender, EventArgs evt) {
|
||||||
@@ -96,7 +95,7 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
Menu_Settings_OverridePathClearances.IsChecked = false;
|
Menu_Settings_OverridePathClearances.IsChecked = false;
|
||||||
Menu_Settings_OverrideDryRunClearances.IsChecked = false;
|
Menu_Settings_OverrideDryRunClearances.IsChecked = false;
|
||||||
|
|
||||||
InitializePaths();
|
App.Plant?.InitializePaths();
|
||||||
}
|
}
|
||||||
OnUpdate(null, null);
|
OnUpdate(null, null);
|
||||||
}
|
}
|
||||||
@@ -211,19 +210,12 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
UpdateScheme(evt.GetPosition(SchemeCanvas), evt.LeftButton == MouseButtonState.Pressed);
|
UpdateScheme(evt.GetPosition(SchemeCanvas), evt.LeftButton == MouseButtonState.Pressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FastSelectButton_Click(object sender, RoutedEventArgs evt) {
|
private void FastSelectButton_Click(object sender, RoutedEventArgs? evt) {
|
||||||
if (sender is not Button b || !FastSelectPaths.TryGetValue(b, out var val))
|
if (sender is not Button b || !FastSelectPaths.TryGetValue(b, out var val))
|
||||||
return;
|
return;
|
||||||
|
App.Plant?.FastSelect(val.Source, val.Sink);
|
||||||
if (Graph.SelectedPaths.Select(p => (Path?)p).FirstOrDefault(p => p?.Start == val.Source, null) is Path p1) {
|
OnUpdate(null, null);
|
||||||
Graph.RemovePath(p1);
|
_lastSource = null;
|
||||||
if (p1.Hops.Last().Node == val.Sink)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Graph.GetFreePath([val.Source, val.Sink]) is Path p2) {
|
|
||||||
Graph.AddPath(p2);
|
|
||||||
_lastSource = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSizeChanged(object sender, SizeChangedEventArgs evt) {
|
private void OnSizeChanged(object sender, SizeChangedEventArgs evt) {
|
||||||
@@ -502,8 +494,6 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
foreach (var n in Graph.Nodes) {
|
foreach (var n in Graph.Nodes) {
|
||||||
if (n is Pump p) {
|
if (n is Pump p) {
|
||||||
p.Highlight = null;
|
p.Highlight = null;
|
||||||
} else if (n is PipeJoin j) {
|
|
||||||
j.Highlight = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,9 +505,6 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
SetFastSelectButton(FastSelectButton_MP1_Stop, App.Plant?.MP1 == MotorState.Halt ? 2 : 0);
|
SetFastSelectButton(FastSelectButton_MP1_Stop, App.Plant?.MP1 == MotorState.Halt ? 2 : 0);
|
||||||
SetFastSelectButton(FastSelectButton_MP2_Forward, App.Plant?.MP2 == MotorState.Forward ? 1 : 0, !(App.Plant?.MP2HasClearance ?? false));
|
SetFastSelectButton(FastSelectButton_MP2_Forward, App.Plant?.MP2 == MotorState.Forward ? 1 : 0, !(App.Plant?.MP2HasClearance ?? false));
|
||||||
SetFastSelectButton(FastSelectButton_MP2_Stop, App.Plant?.MP2 == MotorState.Halt ? 2 : 0);
|
SetFastSelectButton(FastSelectButton_MP2_Stop, App.Plant?.MP2 == MotorState.Halt ? 2 : 0);
|
||||||
foreach (var b in FastSelectPaths) {
|
|
||||||
SetFastSelectButton(b.Key, 0, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_hover = Graph.GetHover(pos.X, pos.Y, RoundedScale);
|
_hover = Graph.GetHover(pos.X, pos.Y, RoundedScale);
|
||||||
var hoverButtons = FastSelectPaths.Where(b => b.Key.IsMouseOver).ToDictionary();
|
var hoverButtons = FastSelectPaths.Where(b => b.Key.IsMouseOver).ToDictionary();
|
||||||
@@ -586,8 +573,8 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
|
|
||||||
var paths = Graph.SelectedPaths;
|
var paths = Graph.SelectedPaths;
|
||||||
if (HoveringPath is Path hp) paths = paths.Append(hp);
|
if (HoveringPath is Path hp) paths = paths.Append(hp);
|
||||||
foreach (var b in FastSelectPaths.Keys) {
|
foreach (var b in FastSelectPaths) {
|
||||||
UpdateFastSelectPathButton(paths, hoverPathDeactivate, b);
|
UpdateFastSelectPathButton(paths, hoverPathDeactivate, b.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
Graph.Update(_hover, down);
|
Graph.Update(_hover, down);
|
||||||
@@ -595,7 +582,12 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void OnUpdate(object? sender, PlantEventArgs? evt) {
|
public void OnUpdate(object? sender, PlantEventArgs? evt) {
|
||||||
if (sender is not PamhagenPlant plant) return;
|
if (App.Plant is PamhagenPlant p) {
|
||||||
|
Status_Connection.Text = $"Ja ({p.PlcPortName}" + (p.LastRttNs != null ? $", {p.LastRttNs / 1000000.0:N0} ms" : "") + ")";
|
||||||
|
Status_Plc.Text = $"{p.LastPlcStatus?.Mode} ({p.LastPlcStatus?.Flags:X3}) / {p.LastPlcError?.GetName()} ({(int?)p.LastPlcError:0000})";
|
||||||
|
Status_CompressedAir.Text = Enumerable.Range(1, 57).Any(p.IsValveClosed) ? "Ein" : Enumerable.Range(1, 57).Any(p.WantValveClosed) ? "Aus" : "?";
|
||||||
|
Status_ValvesOpen.Text = Enumerable.Range(1, 57).Count(p.IsValveOpen) + " / " + Enumerable.Range(1, 57).Count(p.WantValveOpen) + " / 57";
|
||||||
|
}
|
||||||
UpdateScheme(Mouse.GetPosition(SchemeCanvas), Mouse.LeftButton == MouseButtonState.Pressed);
|
UpdateScheme(Mouse.GetPosition(SchemeCanvas), Mouse.LeftButton == MouseButtonState.Pressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Executable
+233
@@ -0,0 +1,233 @@
|
|||||||
|
#!/bin/env python3
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
|
mw1 = 0
|
||||||
|
mw2 = 0
|
||||||
|
t1 = 0
|
||||||
|
t2 = 0
|
||||||
|
t3 = 0
|
||||||
|
t4 = 0
|
||||||
|
t5 = 0
|
||||||
|
t6 = 0
|
||||||
|
t7 = 0
|
||||||
|
t8 = 0
|
||||||
|
t9 = 0
|
||||||
|
pr1 = 0
|
||||||
|
pr2 = 0
|
||||||
|
|
||||||
|
mw1_max = 5000
|
||||||
|
mw2_max = 5000
|
||||||
|
t_max = 35000
|
||||||
|
pr1_max = 20000
|
||||||
|
pr2_max = 15000
|
||||||
|
|
||||||
|
ENTRY_RATE = 20 / 20
|
||||||
|
PUMP_RATE = 5 / 20
|
||||||
|
|
||||||
|
rw = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
|
||||||
|
|
||||||
|
def are_valves_open(valves, *ns):
|
||||||
|
mask = 0
|
||||||
|
for n in ns:
|
||||||
|
mask |= 1 << (n - 1)
|
||||||
|
return (~valves & mask) == mask
|
||||||
|
|
||||||
|
|
||||||
|
def send_cmd(cmd: str, data: str) -> None:
|
||||||
|
msg = f'(A01{cmd}{data}&'
|
||||||
|
chk = sum(c for c in msg.encode('ascii')) & 0xFF
|
||||||
|
msg += f'{chk:02X})\r'
|
||||||
|
time.sleep(len(msg) * 8 / 9600 * 2)
|
||||||
|
print(f'-> {msg}')
|
||||||
|
client.send(msg.encode('ascii'))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('socket')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
|
client.connect(args.socket)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
line = b''
|
||||||
|
while b'\r' not in line:
|
||||||
|
line += client.recv(256)
|
||||||
|
cmds = line.split(b'\r')
|
||||||
|
line = cmds[-1]
|
||||||
|
cmds = [c.decode('ascii') for c in cmds[:-1]]
|
||||||
|
for cmd in cmds:
|
||||||
|
print(f'<- {cmd}')
|
||||||
|
time.sleep(len(cmd) * 8 / 9600 * 2)
|
||||||
|
if not cmd.startswith('(A01') or not cmd.endswith(')') or len(cmd) < 7:
|
||||||
|
send_cmd('CE', '02')
|
||||||
|
continue
|
||||||
|
c, d = cmd[4:6], cmd[6:-1]
|
||||||
|
if cmd[-4] == '&':
|
||||||
|
chk1 = int(cmd[-3:-1], 16)
|
||||||
|
chk2 = sum(c for c in cmd[:-3].encode('ascii')) & 0xFF
|
||||||
|
if chk1 != chk2:
|
||||||
|
send_cmd('CE', '03')
|
||||||
|
continue
|
||||||
|
d = d[:-3]
|
||||||
|
if c == 'TS':
|
||||||
|
send_cmd('TS', d)
|
||||||
|
elif c == 'ST':
|
||||||
|
send_cmd('ST', '0002')
|
||||||
|
elif c == 'DR':
|
||||||
|
# FIXME
|
||||||
|
d = d.split(',')
|
||||||
|
s = int(d[0][2:])
|
||||||
|
l = int(d[1])
|
||||||
|
send_cmd('DR', ''.join(f'{r:04X}' for r in rw[s:s+l]))
|
||||||
|
elif c == 'DW':
|
||||||
|
# FIXME
|
||||||
|
d = d.split(',')
|
||||||
|
s = int(d[0][2:])
|
||||||
|
l = int(d[1])
|
||||||
|
for i in range(l):
|
||||||
|
rw[s + i] = int(d[2 + i], 16)
|
||||||
|
send_cmd('ST', '0002')
|
||||||
|
else:
|
||||||
|
send_cmd('CE', '01')
|
||||||
|
|
||||||
|
# tick - valves
|
||||||
|
for i in range(0, 60):
|
||||||
|
want_open = rw[i // 16] & (1 << (i % 16)) == 0
|
||||||
|
is_open = rw[10 + i // 16] & (1 << (i % 16)) != 0
|
||||||
|
if want_open and not is_open:
|
||||||
|
if random.random() > 0.875:
|
||||||
|
rw[10 + i // 16] |= 1 << (i % 16)
|
||||||
|
elif not want_open and is_open:
|
||||||
|
if random.random() > 0.875:
|
||||||
|
rw[10 + i // 16] &= ~(1 << (i % 16))
|
||||||
|
|
||||||
|
# tick - wannen
|
||||||
|
want_a1_selected = (rw[5] & 0x0004) != 0
|
||||||
|
want_a2_selected = not want_a1_selected
|
||||||
|
is_a1_selected = (rw[14] & 0x0200) != 0
|
||||||
|
is_a2_selected = (rw[14] & 0x0100) != 0
|
||||||
|
if is_a1_selected and not want_a1_selected:
|
||||||
|
rw[14] &= 0xFDFF
|
||||||
|
elif not is_a1_selected and want_a1_selected:
|
||||||
|
if random.random() > 0.875:
|
||||||
|
rw[14] |= 0x0200
|
||||||
|
if is_a2_selected and not want_a2_selected:
|
||||||
|
rw[14] &= 0xFEFF
|
||||||
|
elif not is_a2_selected and want_a2_selected:
|
||||||
|
if random.random() > 0.875:
|
||||||
|
rw[14] |= 0x0100
|
||||||
|
|
||||||
|
# tick - pipeline
|
||||||
|
valves = (rw[13] << 48) | (rw[12] << 32) | (rw[11] << 16) | rw[10]
|
||||||
|
wannen_schnecke = (rw[5] & 0x1) != 0
|
||||||
|
mp1 = (rw[4] & 0x002) != 0
|
||||||
|
mp2 = (rw[4] & 0x004) != 0
|
||||||
|
p12 = (rw[4] & 0x010) != 0
|
||||||
|
p3 = (rw[4] & 0x020) != 0
|
||||||
|
p4 = (rw[4] & 0x040) != 0
|
||||||
|
p5 = (rw[4] & 0x080) != 0
|
||||||
|
p6 = (rw[4] & 0x100) != 0
|
||||||
|
if wannen_schnecke and is_a1_selected:
|
||||||
|
mw1 += ENTRY_RATE
|
||||||
|
elif wannen_schnecke and is_a2_selected:
|
||||||
|
mw2 += ENTRY_RATE
|
||||||
|
|
||||||
|
if mp1 and mw1 > 0:
|
||||||
|
mw1 -= PUMP_RATE
|
||||||
|
if are_valves_open(valves, 1, 6, 51) or are_valves_open(valves, 4, 8, 52):
|
||||||
|
pr1 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 1, 6, 55) or are_valves_open(valves, 4, 8, 56):
|
||||||
|
pr2 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 1, 5, 12, 22) or are_valves_open(valves, 4, 7, 9, 13, 23):
|
||||||
|
t1 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 1, 5, 12, 24) or are_valves_open(valves, 4, 7, 9, 13, 25):
|
||||||
|
t2 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 1, 5, 12, 26) or are_valves_open(valves, 4, 7, 9, 13, 27):
|
||||||
|
t3 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 1, 5, 12, 28, 30) or are_valves_open(valves, 4, 7, 9, 13, 29, 31):
|
||||||
|
t4 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 1, 5, 12, 28, 32) or are_valves_open(valves, 4, 7, 9, 13, 29, 33):
|
||||||
|
t5 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 1, 5, 12, 28, 34, 37) or are_valves_open(valves, 4, 7, 9, 13, 29, 35, 36):
|
||||||
|
t6 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 1, 5, 12, 28, 34, 39) or are_valves_open(valves, 4, 7, 9, 13, 29, 35, 38):
|
||||||
|
t7 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 1, 5, 12, 28, 34, 41) or are_valves_open(valves, 4, 7, 9, 13, 29, 35, 40):
|
||||||
|
t8 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 1, 5, 12, 28, 34, 43) or are_valves_open(valves, 4, 7, 9, 13, 29, 35, 42):
|
||||||
|
t9 += PUMP_RATE
|
||||||
|
|
||||||
|
if mp2 and mw2 > 0:
|
||||||
|
mw2 -= PUMP_RATE
|
||||||
|
if are_valves_open(valves, 2, 8, 52) or are_valves_open(valves, 3, 6, 51):
|
||||||
|
pr1 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 2, 8, 56) or are_valves_open(valves, 3, 6, 55):
|
||||||
|
pr2 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 3, 5, 12, 22) or are_valves_open(valves, 2, 7, 9, 13, 23):
|
||||||
|
t1 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 3, 5, 12, 24) or are_valves_open(valves, 2, 7, 9, 13, 25):
|
||||||
|
t2 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 3, 5, 12, 26) or are_valves_open(valves, 2, 7, 9, 13, 27):
|
||||||
|
t3 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 3, 5, 12, 28, 30) or are_valves_open(valves, 2, 7, 9, 13, 29, 31):
|
||||||
|
t4 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 3, 5, 12, 28, 32) or are_valves_open(valves, 2, 7, 9, 13, 29, 33):
|
||||||
|
t5 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 3, 5, 12, 28, 34, 37) or are_valves_open(valves, 2, 7, 9, 13, 29, 35, 36):
|
||||||
|
t6 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 3, 5, 12, 28, 34, 39) or are_valves_open(valves, 2, 7, 9, 13, 29, 35, 38):
|
||||||
|
t7 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 3, 5, 12, 28, 34, 41) or are_valves_open(valves, 2, 7, 9, 13, 29, 35, 40):
|
||||||
|
t8 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 3, 5, 12, 28, 34, 43) or are_valves_open(valves, 2, 7, 9, 13, 29, 35, 42):
|
||||||
|
t9 += PUMP_RATE
|
||||||
|
|
||||||
|
if p6 and (t6 > 0 or t7 > 0 or t8 > 0 or t9 > 0):
|
||||||
|
if t6 > 0:
|
||||||
|
t6 -= PUMP_RATE
|
||||||
|
if t7 > 0:
|
||||||
|
t7 -= PUMP_RATE
|
||||||
|
if t8 > 0:
|
||||||
|
t8 -= PUMP_RATE
|
||||||
|
if t9 > 0:
|
||||||
|
t9 -= PUMP_RATE
|
||||||
|
if are_valves_open(valves, 53):
|
||||||
|
pr1 += PUMP_RATE
|
||||||
|
elif are_valves_open(valves, 57):
|
||||||
|
pr2 += PUMP_RATE
|
||||||
|
|
||||||
|
# tick - fill levels
|
||||||
|
rw[14] &= 0xFF00
|
||||||
|
rw[14] |= 0x0004 if mw1 > 50 else 0
|
||||||
|
rw[14] |= 0x0002 if mw1 > mw1_max / 2 else 0
|
||||||
|
rw[14] |= 0x0001 if mw1 > mw1_max * 0.875 else 0
|
||||||
|
rw[14] |= 0x0020 if mw2 > 50 else 0
|
||||||
|
rw[14] |= 0x0010 if mw2 > mw2_max / 2 else 0
|
||||||
|
rw[14] |= 0x0008 if mw2 > mw2_max * 0.875 else 0
|
||||||
|
rw[14] |= 0x0040 if pr1 > pr1_max * 0.875 else 0
|
||||||
|
rw[14] |= 0x0080 if pr2 > pr2_max * 0.875 else 0
|
||||||
|
rw[16] &= 0xC000
|
||||||
|
rw[16] |= 0x0001 if t1 > 50 else 0
|
||||||
|
rw[16] |= 0x0002 if t1 > t_max * 0.875 else 0
|
||||||
|
rw[16] |= 0x0004 if t2 > 50 else 0
|
||||||
|
rw[16] |= 0x0008 if t2 > t_max * 0.875 else 0
|
||||||
|
rw[16] |= 0x0010 if t3 > 50 else 0
|
||||||
|
rw[16] |= 0x0020 if t3 > t_max * 0.875 else 0
|
||||||
|
rw[16] |= 0x0040 if t4 > 50 else 0
|
||||||
|
rw[16] |= 0x0080 if t4 > t_max * 0.875 else 0
|
||||||
|
rw[16] |= 0x0100 if t5 > 50 else 0
|
||||||
|
rw[16] |= 0x0200 if t5 > t_max * 0.875 else 0
|
||||||
|
rw[16] |= 0x0400 if t6 > t_max * 0.875 else 0
|
||||||
|
rw[16] |= 0x0800 if t7 > t_max * 0.875 else 0
|
||||||
|
rw[16] |= 0x1000 if t8 > t_max * 0.875 else 0
|
||||||
|
rw[16] |= 0x2000 if t9 > t_max * 0.875 else 0
|
||||||
|
|
||||||
|
print(mw1, mw2, t1, t2, t3, t4, t5, t6, t7, t8, t9, pr1, pr2)
|
||||||
Binary file not shown.
+216
@@ -0,0 +1,216 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="78mm" height="117mm" viewBox="0 0 780 1170">
|
||||||
|
|
||||||
|
<title>XBE-24 Key Legend Pamhagen</title>
|
||||||
|
|
||||||
|
<defs>
|
||||||
|
<path id="key" d="M 5,5 l 150,0 l 0,130 q -75,35 -150,0 Z"
|
||||||
|
stroke-width="10" stroke="#808080" paint-order="stroke"/>
|
||||||
|
<path id="start" d="M 0,-40 l 40,40 l -40,40 l -40,-40 Z M 0,-20 l 0,40 Z"
|
||||||
|
stroke-width="7" stroke="#004000" fill-opacity="0"
|
||||||
|
stroke-linejoin="round"/>
|
||||||
|
<path id="stop" d="M 0,40 a 40,40 180 0,0 0,-80 a 40,40 180 1,0 0,80 m 0,-5 l -30.310889,-52.5 l 60.621778,0 Z"
|
||||||
|
stroke-width="7" stroke="#400000" fill-opacity="0"/>
|
||||||
|
<path id="on-off" d="M 0,40 a 40,40 180 0,0 0,-80 a 40,40 180 1,0 0,80 Z M 0,-20 l 0,40 Z"
|
||||||
|
stroke-width="7" stroke="#000000" fill-opacity="0"
|
||||||
|
stroke-linejoin="round"/>
|
||||||
|
<path id="arrow-down" d="M 0,-35 l 15,0 l 0,50 l 10,0 l -25,25 l -25,-25 l 10,0 l 0,-50 Z"
|
||||||
|
fill="#000040"/>
|
||||||
|
<use id="arrow-left-down" href="#arrow-down" transform="rotate(30)"/>
|
||||||
|
<path id="arrow-from-mw1" d="M -70,-20 l 80,0 l 0,30 l 10,0 l -20,20 l -20,-20 l 10,0 l 0,-10 l -60,0 Z"
|
||||||
|
fill="#000040"/>
|
||||||
|
<use id="arrow-from-mw2" href="#arrow-from-mw1" transform="scale(-1,1)"/>
|
||||||
|
<path id="press" d="M 35,0 l 10,0 l 0,55 l -20,0 l 0,-10 l -50,0 l 0,10 l -20,0 l 0,-55 l 10,0 a 35,35 180 0,1 70,0 a 35,35 180 0,1 -70,0"
|
||||||
|
stroke-width="5" stroke="#000000" fill-opacity="0"/>
|
||||||
|
<!--path id="tank" d="M 35,-32 l 0,90 l -10,0 l 0,-23 l 10,-9 l -30,25 l -10,0 l -30,-25 l 10,9 l 0,23 l -10,0 l 0,-90 Z"
|
||||||
|
stroke-width="5" stroke="#000000" fill-opacity="0"/-->
|
||||||
|
<path id="tank" d="M 35,-32 l 0,90 l -10,0 l 0,-23 l -20,16 l -10,0 l -20,-16 l 0,23 l -10,0 l 0,-90 Z"
|
||||||
|
stroke-width="5" stroke="#000000" fill-opacity="0"/>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect x="0" y="0" width="780" height="1170" stroke="#000000" fill="#FFFFFF" stroke-width="5"/>
|
||||||
|
|
||||||
|
<g id="k0">
|
||||||
|
<use href="#key" x="25" y="30" fill="#E8E8FF"/>
|
||||||
|
<use href="#press" x="105" y="110"/>
|
||||||
|
<text x="105" y="110" font-size="35" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">P1</text>
|
||||||
|
<use href="#arrow-from-mw2" x="105" y="60"/>
|
||||||
|
<text x="148" y="50" font-size="20" text-anchor="middle" dominant-baseline="central" fill="#FFFFFF"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MW2</text>
|
||||||
|
</g>
|
||||||
|
<g id="k1">
|
||||||
|
<use href="#key" x="25" y="220" fill="#E8E8FF"/>
|
||||||
|
<use href="#press" x="105" y="300"/>
|
||||||
|
<text x="105" y="300" font-size="35" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">P2</text>
|
||||||
|
<use href="#arrow-from-mw2" x="105" y="250"/>
|
||||||
|
<text x="148" y="240" font-size="20" text-anchor="middle" dominant-baseline="central" fill="#FFFFFF"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MW2</text>
|
||||||
|
</g>
|
||||||
|
<g id="k2">
|
||||||
|
<use href="#key" x="25" y="410" fill="#E8E8FF"/>
|
||||||
|
<use href="#tank" x="105" y="490"/>
|
||||||
|
<text x="105" y="490" font-size="30" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">RT1</text>
|
||||||
|
<use href="#arrow-from-mw2" x="105" y="440"/>
|
||||||
|
<text x="148" y="430" font-size="20" text-anchor="middle" dominant-baseline="central" fill="#FFFFFF"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MW2</text>
|
||||||
|
</g>
|
||||||
|
<g id="k3">
|
||||||
|
<use href="#key" x="25" y="600" fill="#E8E8FF"/>
|
||||||
|
<use href="#tank" x="105" y="680"/>
|
||||||
|
<text x="105" y="680" font-size="30" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">RT2</text>
|
||||||
|
<use href="#arrow-from-mw2" x="105" y="630"/>
|
||||||
|
<text x="148" y="620" font-size="20" text-anchor="middle" dominant-baseline="central" fill="#FFFFFF"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MW2</text>
|
||||||
|
</g>
|
||||||
|
<g id="k4">
|
||||||
|
<use href="#key" x="25" y="790" fill="#E8E8FF"/>
|
||||||
|
<use href="#tank" x="105" y="870"/>
|
||||||
|
<text x="105" y="870" font-size="30" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">RT3</text>
|
||||||
|
<use href="#arrow-from-mw2" x="105" y="820"/>
|
||||||
|
<text x="148" y="810" font-size="20" text-anchor="middle" dominant-baseline="central" fill="#FFFFFF"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MW2</text>
|
||||||
|
</g>
|
||||||
|
<g id="k5">
|
||||||
|
<use href="#key" x="25" y="980" fill="#E8E8FF"/>
|
||||||
|
<use href="#tank" x="105" y="1060"/>
|
||||||
|
<text x="105" y="1060" font-size="30" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">RT4</text>
|
||||||
|
<use href="#arrow-from-mw2" x="105" y="1010"/>
|
||||||
|
<text x="148" y="1000" font-size="20" text-anchor="middle" dominant-baseline="central" fill="#FFFFFF"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MW2</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g id="k6">
|
||||||
|
<use href="#key" x="215" y="30" fill="#E8FFE8"/>
|
||||||
|
<use href="#start" x="295" y="85"/>
|
||||||
|
<text x="295" y="150" font-size="40" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">Rebler</text>
|
||||||
|
</g>
|
||||||
|
<g id="k7">
|
||||||
|
<use href="#key" x="215" y="220" fill="#FFE8E8"/>
|
||||||
|
<use href="#stop" x="295" y="275"/>
|
||||||
|
<text x="295" y="340" font-size="40" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">Rebler</text>
|
||||||
|
</g>
|
||||||
|
<g id="k8">
|
||||||
|
<use href="#key" x="215" y="410" fill="#E8E8FF"/>
|
||||||
|
<use href="#arrow-left-down" x="300" y="465"/>
|
||||||
|
<text x="295" y="530" font-size="45" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MW2</text>
|
||||||
|
</g>
|
||||||
|
<g id="k9">
|
||||||
|
<use href="#key" x="215" y="600" fill="#FFFFFF"/>
|
||||||
|
<use href="#on-off" x="295" y="655"/>
|
||||||
|
<text x="295" y="710" font-size="25" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;"><tspan>Rührer</tspan><tspan font-size="30" dx="-2.5em" dy="0.875em">MW2</tspan></text>
|
||||||
|
</g>
|
||||||
|
<g id="k10">
|
||||||
|
<use href="#key" x="215" y="790" fill="#E8FFE8"/>
|
||||||
|
<use href="#start" x="295" y="845"/>
|
||||||
|
<text x="295" y="913" font-size="45" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MP2</text>
|
||||||
|
</g>
|
||||||
|
<g id="k11">
|
||||||
|
<use href="#key" x="215" y="980" fill="#FFE8E8"/>
|
||||||
|
<use href="#stop" x="295" y="1035"/>
|
||||||
|
<text x="295" y="1103" font-size="45" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MP2</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g id="k12">
|
||||||
|
<use href="#key" x="405" y="30" fill="#E8FFE8"/>
|
||||||
|
<use href="#start" x="485" y="85"/>
|
||||||
|
<text x="485" y="140" font-size="25" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;"><tspan>Trog-</tspan><tspan dx="-3.5em" dy="0.875em">schnecke</tspan></text>
|
||||||
|
</g>
|
||||||
|
<g id="k13">
|
||||||
|
<use href="#key" x="405" y="220" fill="#FFE8E8"/>
|
||||||
|
<use href="#stop" x="485" y="275"/>
|
||||||
|
<text x="485" y="330" font-size="25" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;"><tspan>Trog-</tspan><tspan dx="-3.5em" dy="0.875em">schnecke</tspan></text>
|
||||||
|
</g>
|
||||||
|
<g id="k14">
|
||||||
|
<use href="#key" x="405" y="410" fill="#E8E8FF"/>
|
||||||
|
<use href="#arrow-down" x="485" y="465"/>
|
||||||
|
<text x="485" y="530" font-size="45" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MW1</text>
|
||||||
|
</g>
|
||||||
|
<g id="k15">
|
||||||
|
<use href="#key" x="405" y="600" fill="#FFFFFF"/>
|
||||||
|
<use href="#on-off" x="485" y="655"/>
|
||||||
|
<text x="485" y="710" font-size="25" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;"><tspan>Rührer</tspan><tspan font-size="30" dx="-2.5em" dy="0.875em">MW1</tspan></text>
|
||||||
|
</g>
|
||||||
|
<g id="k16">
|
||||||
|
<use href="#key" x="405" y="790" fill="#E8FFE8"/>
|
||||||
|
<use href="#start" x="485" y="845"/>
|
||||||
|
<text x="485" y="913" font-size="45" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MP1</text>
|
||||||
|
</g>
|
||||||
|
<g id="k17">
|
||||||
|
<use href="#key" x="405" y="980" fill="#FFE8E8"/>
|
||||||
|
<use href="#stop" x="485" y="1035"/>
|
||||||
|
<text x="485" y="1103" font-size="45" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MP1</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g id="k18">
|
||||||
|
<use href="#key" x="595" y="30" fill="#E8E8FF"/>
|
||||||
|
<use href="#press" x="675" y="110"/>
|
||||||
|
<text x="675" y="110" font-size="35" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">P1</text>
|
||||||
|
<use href="#arrow-from-mw1" x="675" y="60"/>
|
||||||
|
<text x="632" y="50" font-size="20" text-anchor="middle" dominant-baseline="central" fill="#FFFFFF"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MW1</text>
|
||||||
|
</g>
|
||||||
|
<g id="k19">
|
||||||
|
<use href="#key" x="595" y="220" fill="#E8E8FF"/>
|
||||||
|
<use href="#press" x="675" y="300"/>
|
||||||
|
<text x="675" y="300" font-size="35" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">P2</text>
|
||||||
|
<use href="#arrow-from-mw1" x="675" y="250"/>
|
||||||
|
<text x="632" y="240" font-size="20" text-anchor="middle" dominant-baseline="central" fill="#FFFFFF"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MW1</text>
|
||||||
|
</g>
|
||||||
|
<g id="k20">
|
||||||
|
<use href="#key" x="595" y="410" fill="#E8E8FF"/>
|
||||||
|
<use href="#tank" x="675" y="490"/>
|
||||||
|
<text x="675" y="490" font-size="30" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">RT1</text>
|
||||||
|
<use href="#arrow-from-mw1" x="675" y="440"/>
|
||||||
|
<text x="632" y="430" font-size="20" text-anchor="middle" dominant-baseline="central" fill="#FFFFFF"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MW1</text>
|
||||||
|
</g>
|
||||||
|
<g id="k21">
|
||||||
|
<use href="#key" x="595" y="600" fill="#E8E8FF"/>
|
||||||
|
<use href="#tank" x="675" y="680"/>
|
||||||
|
<text x="675" y="680" font-size="30" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">RT2</text>
|
||||||
|
<use href="#arrow-from-mw1" x="675" y="630"/>
|
||||||
|
<text x="632" y="620" font-size="20" text-anchor="middle" dominant-baseline="central" fill="#FFFFFF"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MW1</text>
|
||||||
|
</g>
|
||||||
|
<g id="k22">
|
||||||
|
<use href="#key" x="595" y="790" fill="#E8E8FF"/>
|
||||||
|
<use href="#tank" x="675" y="870"/>
|
||||||
|
<text x="675" y="870" font-size="30" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">RT3</text>
|
||||||
|
<use href="#arrow-from-mw1" x="675" y="820"/>
|
||||||
|
<text x="632" y="810" font-size="20" text-anchor="middle" dominant-baseline="central" fill="#FFFFFF"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MW1</text>
|
||||||
|
</g>
|
||||||
|
<g id="k23">
|
||||||
|
<use href="#key" x="595" y="980" fill="#E8E8FF"/>
|
||||||
|
<use href="#tank" x="675" y="1060"/>
|
||||||
|
<text x="675" y="1060" font-size="30" text-anchor="middle" dominant-baseline="central"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">RT4</text>
|
||||||
|
<use href="#arrow-from-mw1" x="675" y="1010"/>
|
||||||
|
<text x="632" y="1000" font-size="20" text-anchor="middle" dominant-baseline="central" fill="#FFFFFF"
|
||||||
|
style="font-family:Arial,sans-serif; font-weight:bold;">MW1</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
Reference in new issue
Block a user