16 Commits
Author SHA1 Message Date
thomas.hilscher 2d43509afc Update Über page
Test / Run tests (push) Successful in 37s
2026-08-08 00:19:49 +02:00
thomas.hilscher 1da9354c67 Add auto updater 2026-08-08 00:09:06 +02:00
lorenz.stechauner c82adcc054 Handle windows for manual control correctly
Test / Run tests (push) Successful in 13s
2026-08-07 16:15:00 +02:00
lorenz.stechauner 28aaa6b1f3 Use ManualControlMode to disable automatic behaviour
Test / Run tests (push) Successful in 13s
2026-08-07 16:03:56 +02:00
lorenz.stechauner 0ceab368ce Add Handbetrieb support in scheme window
Test / Run tests (push) Successful in 33s
2026-08-07 15:55:22 +02:00
lorenz.stechauner 7c3233361f Add automatic stirrers
Test / Run tests (push) Successful in 14s
2026-08-07 13:08:45 +02:00
lorenz.stechauner e45c398c68 Update manual control invocation
Test / Run tests (push) Successful in 13s
2026-08-07 12:58:55 +02:00
lorenz.stechauner d3dea3e9a0 Add ability to override clearances 2026-08-07 12:48:51 +02:00
lorenz.stechauner dbcf807154 Add pipe labels
Test / Run tests (push) Successful in 13s
2026-08-07 12:30:11 +02:00
lorenz.stechauner 96bb51807d Rearrange graph
Test / Run tests (push) Successful in 15s
2026-08-07 11:49:38 +02:00
lorenz.stechauner 4f8c401cae Adjust clearances to new pipe schema
Test / Run tests (push) Successful in 32s
2026-08-07 10:30:37 +02:00
lorenz.stechauner 06dcdd8569 Add CenterValves to presses
Test / Run tests (push) Successful in 14s
2026-08-07 01:10:29 +02:00
lorenz.stechauner e6b97ccf33 Fix belts/augers
Test / Run tests (push) Successful in 32s
2026-08-06 22:08:44 +02:00
lorenz.stechauner b85d1d8afd Update press and tank capacity 2026-08-06 21:47:16 +02:00
lorenz.stechauner b485bcfb19 deploy.yaml: Shorten Gitea release name 2026-08-06 10:04:51 +02:00
thomas.hilscher 122ac8efd7 Change assembly title to show correct name in task manager
Test / Run tests (push) Successful in 33s
2026-08-05 20:07:37 +02:00
27 changed files with 873 additions and 295 deletions
+1 -1
View File
@@ -40,7 +40,7 @@ jobs:
- name: Create release - name: Create release
uses: akkuman/gitea-release-action@v1 uses: akkuman/gitea-release-action@v1
with: with:
name: Anlagensteuerung Pamhagen ${{ env.APP_VERSION }} name: Anl.-Strg. Pamhagen ${{ env.APP_VERSION }}
body: "**[Changelog](src/branch/main/CHANGELOG.md#v${{ env.APP_VERSION }})**" body: "**[Changelog](src/branch/main/CHANGELOG.md#v${{ env.APP_VERSION }})**"
files: |- files: |-
Installer/bin/Release/de-AT/PamhagenSysCtrl-${{ env.APP_VERSION }}.msi Installer/bin/Release/de-AT/PamhagenSysCtrl-${{ env.APP_VERSION }}.msi
+4
View File
@@ -1,3 +1,7 @@
[plc] [plc]
port = COM1 port = COM1
[update]
url = https://elwig.at/files/pamhagen-sysctrl/?format=json
auto = true
+69
View File
@@ -1,3 +1,4 @@
using PamhagenSysCtrl.Dialogs;
using PamhagenSysCtrl.Helpers; using PamhagenSysCtrl.Helpers;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
@@ -10,6 +11,9 @@ namespace PamhagenSysCtrl {
public static PamhagenPlant? Plant; public static PamhagenPlant? Plant;
public static Dispatcher? MainDispatcher; public static Dispatcher? MainDispatcher;
private readonly DispatcherTimer AutoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) };
private bool IsCheckingForUpdates;
public static readonly string DataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Anlagensteuerung Pamhagen"); public static readonly string DataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Anlagensteuerung Pamhagen");
public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini"); public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini");
public static readonly string InstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Anlagensteuerung Pamhagen"); public static readonly string InstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Anlagensteuerung Pamhagen");
@@ -45,6 +49,71 @@ namespace PamhagenSysCtrl {
} }
base.OnStartup(evt); base.OnStartup(evt);
if (Config.UpdateAuto && Config.UpdateUrl != null) {
AutoUpdateTimer.Tick += async (_, _) => await CheckForUpdates();
AutoUpdateTimer.Start();
_ = Dispatcher.BeginInvoke(async () => {
await Task.Delay(1500);
await CheckForUpdates();
});
}
}
public async Task CheckForUpdates(bool showResult = false) {
if (IsCheckingForUpdates) {
if (showResult) {
MessageBox.Show("Es wird bereits nach Updates gesucht.", "Nach Updates suchen", MessageBoxButton.OK, MessageBoxImage.Information);
}
return;
}
if (Config.UpdateUrl == null) {
if (showResult) {
MessageBox.Show("Die automatische Update-Suche ist deaktiviert.", "Nach Updates suchen", MessageBoxButton.OK, MessageBoxImage.Information);
}
return;
}
IsCheckingForUpdates = true;
try {
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(15));
var latest = await UpdateService.GetLatestInstallerAsync(Config.UpdateUrl, timeout.Token);
if (latest.Version > Version) {
var dialog = new UpdateDialog(latest) {
Owner = MainWindow,
};
if (dialog.ShowDialog() == true) {
Shutdown();
}
} else if (showResult) {
MessageBox.Show(
$"Die Anlagensteuerung ist auf dem aktuellen Stand. (Version {Version})",
"Nach Updates suchen",
MessageBoxButton.OK,
MessageBoxImage.Information
);
}
} catch (OperationCanceledException) {
if (showResult) {
MessageBox.Show(
"Zeitüberschreitung beim Abrufen der Update-Informationen.",
"Nach Updates suchen",
MessageBoxButton.OK,
MessageBoxImage.Error
);
}
} catch (Exception exc) {
if (showResult) {
MessageBox.Show(
$"Die Update-Informationen konnten nicht abgerufen werden:\n\n{exc.Message}",
"Nach Updates suchen",
MessageBoxButton.OK,
MessageBoxImage.Error
);
}
} finally {
IsCheckingForUpdates = false;
}
} }
protected async void Application_Exit(object sender, ExitEventArgs evt) { protected async void Application_Exit(object sender, ExitEventArgs evt) {
+37
View File
@@ -0,0 +1,37 @@
<Window x:Class="PamhagenSysCtrl.Dialogs.UpdateDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True"
WindowStartupLocation="CenterOwner"
Title="Update verfügbar - Anlagensteuerung Pamhagen"
Height="220" Width="460"
Closed="OnClosed">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock x:Name="Description" FontSize="14" TextAlignment="Center"
HorizontalAlignment="Center" VerticalAlignment="Center">
Version <Run x:Name="VersionText" FontWeight="Bold">0.0.0</Run> ist verfügbar.<LineBreak/>
Soll das Update heruntergeladen und installiert werden?<LineBreak/>
(ca. <Run x:Name="SizeText">0</Run> MB)<LineBreak/><LineBreak/>
<Run FontWeight="Bold">Hinweis:</Run> Die Anlagensteuerung wird zur Installation geschlossen.
</TextBlock>
<StackPanel x:Name="DownloadPanel" Grid.Row="0" VerticalAlignment="Center" Visibility="Collapsed">
<TextBlock x:Name="StatusText" Text="Update wird heruntergeladen..."
FontSize="14" TextAlignment="Center" Margin="0,0,0,10"/>
<ProgressBar x:Name="DownloadProgress" Height="24" Minimum="0" Maximum="100"/>
</StackPanel>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,18,0,0">
<Button x:Name="InstallButton" Content="Installieren" IsDefault="True"
Width="110" Height="30" Margin="0,0,12,0" Click="InstallButton_Click"/>
<Button x:Name="CancelButton" Content="Abbrechen" IsCancel="True"
Width="110" Height="30"/>
</StackPanel>
</Grid>
</Window>
@@ -0,0 +1,49 @@
using PamhagenSysCtrl.Helpers;
using System.Windows;
namespace PamhagenSysCtrl.Dialogs {
public partial class UpdateDialog : Window {
private readonly UpdateInstaller Installer;
private readonly CancellationTokenSource Cancellation = new();
public UpdateDialog(UpdateInstaller installer) {
Installer = installer;
InitializeComponent();
VersionText.Text = installer.Version.ToString();
SizeText.Text = Math.Ceiling(installer.Size / 1024d / 1024d).ToString("N0");
}
private void OnClosed(object? sender, EventArgs evt) {
Cancellation.Cancel();
Cancellation.Dispose();
}
private async void InstallButton_Click(object sender, RoutedEventArgs evt) {
Description.Visibility = Visibility.Collapsed;
DownloadPanel.Visibility = Visibility.Visible;
InstallButton.IsEnabled = false;
try {
var progress = new Progress<double>(value => DownloadProgress.Value = value * 100);
var fileName = await UpdateService.DownloadInstallerAsync(Installer, App.TempPath, progress, Cancellation.Token);
StatusText.Text = "Installer wird gestartet...";
UpdateService.StartInstaller(fileName);
DialogResult = true;
} catch (OperationCanceledException) {
// Closing the dialog cancels an active download.
} catch (Exception exc) {
MessageBox.Show(
this,
$"Das Update konnte nicht installiert werden:\n\n{exc.Message}",
"Update installieren",
MessageBoxButton.OK,
MessageBoxImage.Error
);
Description.Visibility = Visibility.Visible;
DownloadPanel.Visibility = Visibility.Collapsed;
InstallButton.IsEnabled = true;
}
}
}
}
+6 -1
View File
@@ -9,6 +9,8 @@ namespace PamhagenSysCtrl.Helpers {
private readonly string FileName; private readonly string FileName;
public string PlcPort = "COM1"; public string PlcPort = "COM1";
public string? UpdateUrl = "https://elwig.at/files/pamhagen-sysctrl/?format=json";
public bool UpdateAuto = true;
public Config(string filename) { public Config(string filename) {
FileName = filename; FileName = filename;
@@ -19,9 +21,12 @@ 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";
UpdateUrl = config["update:url"] ?? UpdateUrl;
var updateAuto = config["update:auto"];
UpdateAuto = updateAuto == null || TrueValues.Contains(updateAuto.ToLower());
} catch (Exception exc) { } catch (Exception exc) {
MessageBox.Show($"Die Konfigurationsdatei konnte nicht gelesen werden:\n\n{exc.Message}", "Konfigurationsdatei lesen", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show($"Die Konfigurationsdatei konnte nicht gelesen werden:\n\n{exc.Message}", "Konfigurationsdatei lesen", MessageBoxButton.OK, MessageBoxImage.Error);
App.Current.Shutdown(); Application.Current.Shutdown();
} }
} }
} }
+6 -4
View File
@@ -5,10 +5,12 @@ namespace PamhagenSysCtrl.Helpers {
public const double TRANSITION = 1.0 / 4096; public const double TRANSITION = 1.0 / 4096;
public static readonly SolidColorBrush Green = new SolidColorBrush(Color.FromRgb(0x20, 0xE0, 0x20)); public static readonly SolidColorBrush Green = new(Color.FromRgb(0x20, 0xE0, 0x20));
public static readonly SolidColorBrush DimOrange = new SolidColorBrush(Color.FromRgb(0xFF, 0xC0, 0x80)); public static readonly SolidColorBrush DimGreen = new(Color.FromRgb(0xC0, 0xE0, 0xC0));
public static readonly SolidColorBrush Yellow = new SolidColorBrush(Color.FromRgb(0xFF, 0xE0, 0x10)); public static readonly SolidColorBrush DimOrange = new(Color.FromRgb(0xFF, 0xC0, 0x80));
public static readonly SolidColorBrush Red = new SolidColorBrush(Color.FromRgb(0xFF, 0x20, 0x20)); public static readonly SolidColorBrush Yellow = new(Color.FromRgb(0xFF, 0xE0, 0x10));
public static readonly SolidColorBrush Red = new(Color.FromRgb(0xFF, 0x20, 0x20));
public static readonly SolidColorBrush DimRed = new(Color.FromRgb(0xFF, 0xC0, 0xC0));
public static Brush ForFillState(FillState state, Color background) { public static Brush ForFillState(FillState state, Color background) {
return state switch { return state switch {
+131 -90
View File
@@ -17,7 +17,7 @@ namespace PamhagenSysCtrl.Helpers {
public readonly Pump P5; public readonly Pump P5;
public readonly Pump P6; public readonly Pump P6;
public readonly EncasedAuger Auger1; public readonly EncasedAuger Auger1;
public readonly EncasedAuger Auger2; public readonly EncasedAuger Auger3;
public PamhagenGraph() { public PamhagenGraph() {
double mw2X = 180; double mw2X = 180;
@@ -34,6 +34,7 @@ namespace PamhagenSysCtrl.Helpers {
double wtX = ltg1X - 180; double wtX = ltg1X - 180;
double wt2Y = 225; double wt2Y = 225;
double wt1Y = wt2Y + 135; double wt1Y = wt2Y + 135;
double x1X = wtX + 40;
double t1X = 735; double t1X = 735;
double t2X = t1X + 80; double t2X = t1X + 80;
double t3X = t2X + 80; double t3X = t2X + 80;
@@ -43,21 +44,21 @@ namespace PamhagenSysCtrl.Helpers {
double t7X = t6X + 80; double t7X = t6X + 80;
double t8X = t7X + 80; double t8X = t7X + 80;
double t9X = t8X + 80; double t9X = t8X + 80;
var prY = 780; var prY = 720;
var pr1X = 750; var pr1X = 730;
var pr2X = 900; var pr2X = 920;
var trough = new EntryTrough("Auffangwanne", mw1X + 70, 100, () => App.Plant?.IsTroughAugerActive ?? false); var trough = new EntryTrough("Auffangwanne", mw1X + 70, 100, () => App.Plant?.IsTroughAugerActive ?? false);
Nodes.Add(trough); Nodes.Add(trough);
Auger1 = new EncasedAuger("", (mw1X + mw2X) / 2 - 100, 200, () => App.Plant?.IsAuger1Active ?? false); Auger1 = new EncasedAuger("", (mw1X + mw2X) / 2 - 100, 200, () => App.Plant?.IsAuger1Active ?? false);
var auger3 = new EncasedAuger("Pressen-\nQuerschnecke", 40, 200, () => App.Plant?.IsPresseQuerSchneckeActive ?? false, 180, clickable: false); var auger0 = new EncasedAuger("Pressen-\nQuerschnecke", 40, 200, () => App.Plant?.IsPresseQuerSchneckeActive ?? false, 180, clickable: false);
Auger2 = new EncasedAuger("", 120, 80, () => App.Plant?.IsAuger2Active ?? false, 135); Auger3 = new EncasedAuger("", 120, 80, () => App.Plant?.IsAuger3Active ?? false, 135);
var belt = new ConveyorBelt("", 90, 160, () => App.Plant?.IsBeltActive ?? false); var belt = new ConveyorBelt("", 90, 160, () => App.Plant?.IsAuger2Active ?? false);
Nodes.Add(belt); Nodes.Add(belt);
Nodes.Add(Auger1); Nodes.Add(Auger1);
Nodes.Add(auger3); Nodes.Add(auger0);
Nodes.Add(Auger2); Nodes.Add(Auger3);
var rebler = new Rebler("Rebler", (mw1X + mw2X) / 2, 200, () => App.Plant?.IsReblerActive ?? false); var rebler = new Rebler("Rebler", (mw1X + mw2X) / 2, 200, () => App.Plant?.IsReblerActive ?? false);
Nodes.Add(rebler); Nodes.Add(rebler);
var switcher = new Switcher("", (mw1X + mw2X) / 2, 260, () => App.Plant?.TroughSelector ?? 0, () => App.Plant == null ? 0 : App.Plant.WantMW1Selected ? 1 : 2); var switcher = new Switcher("", (mw1X + mw2X) / 2, 260, () => App.Plant?.TroughSelector ?? 0, () => App.Plant == null ? 0 : App.Plant.WantMW1Selected ? 1 : 2);
@@ -65,8 +66,8 @@ namespace PamhagenSysCtrl.Helpers {
MW1 = new Trough("MW1", mw1X, mpY - 70, () => App.Plant?.FillLevelMW1 ?? FillState.Unknown); MW1 = new Trough("MW1", mw1X, mpY - 70, () => App.Plant?.FillLevelMW1 ?? FillState.Unknown);
MW2 = new Trough("MW2", mw2X, mpY - 70, () => App.Plant?.FillLevelMW2 ?? FillState.Unknown); MW2 = new Trough("MW2", mw2X, mpY - 70, () => App.Plant?.FillLevelMW2 ?? FillState.Unknown);
MP1 = new Pump("MP1", mw1X, mpY, () => App.Plant?.IsMP1Active ?? false, () => App.Plant?.MP1HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); MP1 = new Pump("MP1", mw1X, mpY, () => App.Plant?.MP1 ?? default, () => App.Plant?.MP1HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x)));
MP2 = new Pump("MP2", mw2X, mpY, () => App.Plant?.IsMP2Active ?? false, () => App.Plant?.MP2HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); MP2 = new Pump("MP2", mw2X, mpY, () => App.Plant?.MP2 ?? default, () => App.Plant?.MP2HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x)));
var x1 = new PipeJoin(mw1X, mpY + 40); var x1 = new PipeJoin(mw1X, mpY + 40);
var x2 = new PipeJoin(mw2X, mpY + 40); var x2 = new PipeJoin(mw2X, mpY + 40);
@@ -86,9 +87,9 @@ namespace PamhagenSysCtrl.Helpers {
CreatePipe(x2, v3); CreatePipe(x2, v3);
var x3 = new PipeJoin(mw1X, mpY + 100); var x3 = new PipeJoin(mw1X, mpY + 100);
var x4 = new PipeJoin(mw1X, ltg1Y); var x4 = new PipeJoin(x1X, ltg1Y);
var x5 = new PipeJoin(mw2X, mpY + 130); var x5 = new PipeJoin(mw2X, mpY + 130);
var x6 = new PipeJoin(mw2X, ltg2Y); var x6 = new PipeJoin(x1X - 30, ltg2Y);
CreatePipe(v1, x3, true); CreatePipe(v1, x3, true);
CreatePipe(v3, x3, true); CreatePipe(v3, x3, true);
@@ -97,23 +98,23 @@ namespace PamhagenSysCtrl.Helpers {
CreatePipe(v4, x5, true); CreatePipe(v4, x5, true);
CreatePipe(x5, x6, true); CreatePipe(x5, x6, true);
var v5 = CreateValve(5, mw1X + 50, ltg1Y); var v5 = CreateValve(5, x1X + 30, ltg1Y);
var v6 = CreateValve(6, mw1X, ltg2Y + 40); var v6 = CreateValve(6, x1X + 30, ltg2Y + 40);
var v7 = CreateValve(7, (mw2X + mw1X) / 2, ltg2Y); var v7 = CreateValve(7, x1X + 30, ltg2Y);
var v8 = CreateValve(8, mw2X, ltg2Y + 40); var v8 = CreateValve(8, x1X - 30, ltg2Y + 40);
var v9 = CreateValve(9, ltg2X, 500); var v9 = CreateValve(9, ltg2X, 500);
CreatePipe(x4, v5); CreatePipe(x4, v5);
CreatePipe(x4, v6); CreatePipe(x4, v6, true);
CreatePipe(x6, v7); CreatePipe(x6, v7);
CreatePipe(x6, v8); CreatePipe(x6, v8);
CreatePipe(v7, v9); CreatePipe(v7, v9, label: "Ltg. 2", labelOffsetX: 30);
var xv15 = new PipeJoin(ltg1X, wt1Y + 40); var xv15 = new PipeJoin(ltg1X, wt1Y + 40);
var xv11 = new PipeJoin(ltg2X, wt1Y + 70); var xv11 = new PipeJoin(ltg2X, wt1Y + 70);
var x7 = new PipeJoin(ltg2X, 460); var x7 = new PipeJoin(ltg2X, 460);
CreatePipe(v5, xv15); CreatePipe(v5, xv15, label: "Ltg. 1", labelOffsetX: 30);
CreatePipe(v9, x7); CreatePipe(v9, x7);
CreatePipe(x7, xv11); CreatePipe(x7, xv11);
@@ -179,9 +180,9 @@ namespace PamhagenSysCtrl.Helpers {
var xv23 = new PipeJoin(t1X + 15, ltg6Y); var xv23 = new PipeJoin(t1X + 15, ltg6Y);
var v22 = CreateValve(22, t1X - 15, tValveY); var v22 = CreateValve(22, t1X - 15, tValveY);
var v23 = CreateValve(23, t1X + 15, tValveY); var v23 = CreateValve(23, t1X + 15, tValveY);
var t1 = CreateTank(1, t1X, tanksY, 100000); var t1 = CreateTank(1, t1X, tanksY, 35000);
CreatePipe(xv20, xv22, true); CreatePipe(xv20, xv22, true, label: "Ltg. 7", labelOffsetX: 50);
CreatePipe(xv21, xv23, true); CreatePipe(xv21, xv23, true, label: "Ltg. 6", labelOffsetX: 20);
CreatePipe(xv22, v22); CreatePipe(xv22, v22);
CreatePipe(xv23, v23); CreatePipe(xv23, v23);
CreateInlet(v22, t1); CreateInlet(v22, t1);
@@ -191,7 +192,7 @@ namespace PamhagenSysCtrl.Helpers {
var xv25 = new PipeJoin(t2X + 15, ltg6Y); var xv25 = new PipeJoin(t2X + 15, ltg6Y);
var v24 = CreateValve(24, t2X - 15, tValveY); var v24 = CreateValve(24, t2X - 15, tValveY);
var v25 = CreateValve(25, t2X + 15, tValveY); var v25 = CreateValve(25, t2X + 15, tValveY);
var t2 = CreateTank(2, t2X, tanksY, 100000); var t2 = CreateTank(2, t2X, tanksY, 35000);
CreatePipe(xv22, xv24); CreatePipe(xv22, xv24);
CreatePipe(xv23, xv25); CreatePipe(xv23, xv25);
CreatePipe(xv24, v24); CreatePipe(xv24, v24);
@@ -203,7 +204,7 @@ namespace PamhagenSysCtrl.Helpers {
var xv27 = new PipeJoin(t3X + 15, ltg6Y); var xv27 = new PipeJoin(t3X + 15, ltg6Y);
var v26 = CreateValve(26, t3X - 15, tValveY); var v26 = CreateValve(26, t3X - 15, tValveY);
var v27 = CreateValve(27, t3X + 15, tValveY); var v27 = CreateValve(27, t3X + 15, tValveY);
var t3 = CreateTank(3, t3X, tanksY, 100000); var t3 = CreateTank(3, t3X, tanksY, 35000);
CreatePipe(xv24, xv26); CreatePipe(xv24, xv26);
CreatePipe(xv25, xv27); CreatePipe(xv25, xv27);
CreatePipe(xv26, v26); CreatePipe(xv26, v26);
@@ -220,7 +221,7 @@ namespace PamhagenSysCtrl.Helpers {
var xv31 = new PipeJoin(t4X + 15, ltg6Y); var xv31 = new PipeJoin(t4X + 15, ltg6Y);
var v30 = CreateValve(30, t4X - 15, tValveY); var v30 = CreateValve(30, t4X - 15, tValveY);
var v31 = CreateValve(31, t4X + 15, tValveY); var v31 = CreateValve(31, t4X + 15, tValveY);
var t4 = CreateTank(4, t4X, tanksY, 100000); var t4 = CreateTank(4, t4X, tanksY, 35000);
CreatePipe(v28, xv30); CreatePipe(v28, xv30);
CreatePipe(v29, xv31); CreatePipe(v29, xv31);
CreatePipe(xv30, v30); CreatePipe(xv30, v30);
@@ -232,9 +233,9 @@ namespace PamhagenSysCtrl.Helpers {
var xv33 = new PipeJoin(t5X + 15, ltg6Y); var xv33 = new PipeJoin(t5X + 15, ltg6Y);
var v32 = CreateValve(32, t5X - 15, tValveY); var v32 = CreateValve(32, t5X - 15, tValveY);
var v33 = CreateValve(33, t5X + 15, tValveY); var v33 = CreateValve(33, t5X + 15, tValveY);
var t5 = CreateTank(5, t5X, tanksY, 100000); var t5 = CreateTank(5, t5X, tanksY, 35000);
CreatePipe(xv30, xv32); CreatePipe(xv30, xv32, label: "Ltg. 7", labelOffsetX: 30);
CreatePipe(xv31, xv33); CreatePipe(xv31, xv33, label: "Ltg. 6", labelOffsetX: 0);
CreatePipe(xv32, v32); CreatePipe(xv32, v32);
CreatePipe(xv33, v33); CreatePipe(xv33, v33);
CreateInlet(v32, t5); CreateInlet(v32, t5);
@@ -249,7 +250,7 @@ namespace PamhagenSysCtrl.Helpers {
var xv37 = new PipeJoin(t6X - 15, ltg7Y); var xv37 = new PipeJoin(t6X - 15, ltg7Y);
var v36 = CreateValve(36, t6X + 15, tValveY); var v36 = CreateValve(36, t6X + 15, tValveY);
var v37 = CreateValve(37, t6X - 15, tValveY); var v37 = CreateValve(37, t6X - 15, tValveY);
var t6 = CreateTank(6, t6X, tanksY, 100000); var t6 = CreateTank(6, t6X, tanksY, 35000);
CreatePipe(v34, xv37); CreatePipe(v34, xv37);
CreatePipe(v35, xv36); CreatePipe(v35, xv36);
CreatePipe(xv37, v37); CreatePipe(xv37, v37);
@@ -261,7 +262,7 @@ namespace PamhagenSysCtrl.Helpers {
var xv39 = new PipeJoin(t7X - 15, ltg7Y); var xv39 = new PipeJoin(t7X - 15, ltg7Y);
var v38 = CreateValve(38, t7X + 15, tValveY); var v38 = CreateValve(38, t7X + 15, tValveY);
var v39 = CreateValve(39, t7X - 15, tValveY); var v39 = CreateValve(39, t7X - 15, tValveY);
var t7 = CreateTank(7, t7X, tanksY, 100000); var t7 = CreateTank(7, t7X, tanksY, 35000);
CreatePipe(xv37, xv39); CreatePipe(xv37, xv39);
CreatePipe(xv36, xv38); CreatePipe(xv36, xv38);
CreatePipe(xv38, v38); CreatePipe(xv38, v38);
@@ -273,9 +274,9 @@ namespace PamhagenSysCtrl.Helpers {
var xv41 = new PipeJoin(t8X - 15, ltg7Y); var xv41 = new PipeJoin(t8X - 15, ltg7Y);
var v40 = CreateValve(40, t8X + 15, tValveY); var v40 = CreateValve(40, t8X + 15, tValveY);
var v41 = CreateValve(41, t8X - 15, tValveY); var v41 = CreateValve(41, t8X - 15, tValveY);
var t8 = CreateTank(8, t8X, tanksY, 100000); var t8 = CreateTank(8, t8X, tanksY, 35000);
CreatePipe(xv38, xv40); CreatePipe(xv38, xv40, label: "Ltg. 6", labelOffsetX: 0);
CreatePipe(xv39, xv41); CreatePipe(xv39, xv41, label: "Ltg. 7", labelOffsetX: 30);
CreatePipe(xv40, v40); CreatePipe(xv40, v40);
CreatePipe(xv41, v41); CreatePipe(xv41, v41);
CreateInlet(v40, t8); CreateInlet(v40, t8);
@@ -283,21 +284,21 @@ namespace PamhagenSysCtrl.Helpers {
var v42 = CreateValve(42, t9X + 15, tValveY); var v42 = CreateValve(42, t9X + 15, tValveY);
var v43 = CreateValve(43, t9X - 15, tValveY); var v43 = CreateValve(43, t9X - 15, tValveY);
var t9 = CreateTank(9, t9X, tanksY, 100000); var t9 = CreateTank(9, t9X, tanksY, 35000);
CreatePipe(xv40, v42); CreatePipe(xv40, v42);
CreatePipe(xv41, v43); CreatePipe(xv41, v43);
CreateInlet(v42, t9); CreateInlet(v42, t9);
CreateInlet(v43, t9); CreateInlet(v43, t9);
var xp12 = new PipeJoin((t1X + t2X) / 2, tanksY + 80); var xp12 = new PipeJoin((t1X + t2X) / 2, tanksY + 80);
P12 = new Pump("P1/2", (t1X + t2X) / 2, tanksY + 110, () => App.Plant?.IsP12Active ?? false, () => App.Plant?.P12HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); P12 = new Pump("P1/2", (t1X + t2X) / 2, tanksY + 110, () => App.Plant?.P12 ?? default, () => App.Plant?.P12HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x)));
CreateOutlet(t1, xp12); CreateOutlet(t1, xp12);
CreateOutlet(t2, xp12); CreateOutlet(t2, xp12);
CreatePipe(xp12, P12); CreatePipe(xp12, P12);
P3 = new Pump("P3", t3X, tanksY + 110, () => App.Plant?.IsP3Active ?? false, () => App.Plant?.P3HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); P3 = new Pump("P3", t3X, tanksY + 110, () => App.Plant?.P3 ?? default, () => App.Plant?.P3HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x)));
P4 = new Pump("P4", t4X, tanksY + 110, () => App.Plant?.IsP4Active ?? false, () => App.Plant?.P4HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); P4 = new Pump("P4", t4X, tanksY + 110, () => App.Plant?.P4 ?? default, () => App.Plant?.P4HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x)));
P5 = new Pump("P5", t5X, tanksY + 110, () => App.Plant?.IsP5Active ?? false, () => App.Plant?.P5HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); P5 = new Pump("P5", t5X, tanksY + 110, () => App.Plant?.P5 ?? default, () => App.Plant?.P5HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x)));
CreateOutlet(t3, P3); CreateOutlet(t3, P3);
CreateOutlet(t4, P4); CreateOutlet(t4, P4);
CreateOutlet(t5, P5); CreateOutlet(t5, P5);
@@ -318,7 +319,7 @@ namespace PamhagenSysCtrl.Helpers {
CreatePipe(v45, xv45); CreatePipe(v45, xv45);
CreatePipe(v47, xv47); CreatePipe(v47, xv47);
CreatePipe(xv44, v10); CreatePipe(xv44, v10, label: "Ltg. 5");
CreatePipe(v48, xv47, true); CreatePipe(v48, xv47, true);
CreateTwoWayPipe(xv47, v46); CreateTwoWayPipe(xv47, v46);
CreateTwoWayPipe(v46, xv45); CreateTwoWayPipe(v46, xv45);
@@ -327,7 +328,7 @@ namespace PamhagenSysCtrl.Helpers {
var xt6 = new PipeJoin(t6X, tanksY + 110); var xt6 = new PipeJoin(t6X, tanksY + 110);
var xt7 = new PipeJoin(t7X, tanksY + 110); var xt7 = new PipeJoin(t7X, tanksY + 110);
var xt8 = new PipeJoin(t8X, tanksY + 110); var xt8 = new PipeJoin(t8X, tanksY + 110);
P6 = new Pump("P6", t6X, tanksY + 150, () => App.Plant?.IsP6Active ?? false, () => App.Plant?.P6HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x))); P6 = new Pump("P6", t6X, tanksY + 150, () => App.Plant?.P6 ?? default, () => App.Plant?.P6HasClearance ?? false, (x) => SelectedPaths.Any(p => p.Hops.Any(h => h.Node == x)));
CreateOutlet(t6, xt6); CreateOutlet(t6, xt6);
CreateOutlet(t7, xt7); CreateOutlet(t7, xt7);
CreateOutlet(t8, xt8); CreateOutlet(t8, xt8);
@@ -339,73 +340,90 @@ namespace PamhagenSysCtrl.Helpers {
var v49 = CreateValve(49, t4X, tanksY + 220); var v49 = CreateValve(49, t4X, tanksY + 220);
CreatePipe(xv47, v49); CreatePipe(xv47, v49);
var press1 = new Press("Presse 1", pr1X, prY, 200000, () => App.Plant?.FillLevelPress1 ?? FillState.Unknown); var prX = (pr1X + pr2X) / 2;
var press2 = new Press("Presse 2", pr2X, prY, 150000, () => App.Plant?.FillLevelPress2 ?? FillState.Unknown); var pr1cX = prX - 15;
var v50 = CreateValve(50, pr1X - 45, prY - 100); var pr2cX = prX + 15;
var v51 = CreateValve(51, pr1X - 15, prY - 100); var press1 = new Press("Presse 1", pr1X, prY, 20000, () => FillState.Unknown);
var v52 = CreateValve(52, pr1X + 15, prY - 100); var press2 = new Press("Presse 2", pr2X, prY, 15000, () => FillState.Unknown);
var v53 = CreateValve(53, pr1X + 45, prY - 100); var v50 = CreateValve(50, pr2cX + 30, 580);
var v54 = CreateValve(54, pr2X - 45, prY - 100); var v51 = CreateValve(51, pr1cX - 30, 580);
var v55 = CreateValve(55, pr2X - 15, prY - 100); var v52 = CreateValve(52, pr1cX - 30, 610);
var v56 = CreateValve(56, pr2X + 15, prY - 100); var v53 = CreateValve(53, pr2cX + 30, 610);
var v57 = CreateValve(57, pr2X + 45, prY - 100); var v54 = CreateValve(54, pr2cX + 30, 550);
var xp1 = new PipeJoin(pr1X - 15, 610); var v55 = CreateValve(55, pr1cX - 30, 550);
var xp2 = new PipeJoin(pr1X + 15, 640); var v56 = CreateValve(56, pr1cX - 30, 640);
var xp3 = new PipeJoin(pr2X - 45, 550); var v57 = CreateValve(57, pr2cX + 30, 640);
var xp4 = new PipeJoin(pr2X + 45, 580); var xp1 = new PipeJoin(pr1cX - 70, 580);
var xp2 = new PipeJoin(pr1cX - 70, 610);
var xp3 = new PipeJoin(pr2cX + 70, 550);
var xp4 = new PipeJoin(pr2cX + 70, 640);
var xp5 = new PipeJoin(pr1cX, 580);
var xp6 = new PipeJoin(pr1cX, 610);
var xp7 = new PipeJoin(pr2cX, 550);
var xp8 = new PipeJoin(pr2cX, 640);
CreatePipe(v6, xp1, true); CreatePipe(v6, xp1, true, label: "Ltg. 3", labelOffsetX: 30);
CreatePipe(v8, xp2, true); CreatePipe(v8, xp2, true, label: "Ltg. 4", labelOffsetX: 90);
CreatePipe(v49, xp3, true); CreatePipe(v49, xp3, true, label: "Ltg. 8");
CreatePipe(P6, xp4, true); CreatePipe(P6, xp4, true, label: "Ltg. 9");
CreatePipe(xp1, v51); CreatePipe(xp1, v51, true);
CreatePipe(xp1, v55); CreatePipe(xp1, v55, true);
CreatePipe(xp2, v52); CreatePipe(xp2, v52, true);
CreatePipe(xp2, v56); CreatePipe(xp2, v56, true);
CreatePipe(xp3, v50); CreatePipe(xp3, v50, true);
CreatePipe(xp3, v54); CreatePipe(xp3, v54, true);
CreatePipe(xp4, v53); CreatePipe(xp4, v53, true);
CreatePipe(xp4, v57); CreatePipe(xp4, v57, true);
CreateInlet(v50, press1); CreatePipe(v50, xp5);
CreateInlet(v51, press1); CreatePipe(v51, xp5);
CreateInlet(v52, press1); CreatePipe(v52, xp6);
CreateInlet(v53, press1); CreatePipe(v53, xp6);
CreateInlet(v54, press2); CreatePipe(v54, xp7);
CreateInlet(v55, press2); CreatePipe(v55, xp7);
CreateInlet(v56, press2); CreatePipe(v56, xp8);
CreateInlet(v57, press2); CreatePipe(v57, xp8);
CreatePipe(xp5, xp6);
CreatePipe(xp7, xp8);
var zv1 = new CenterValve("ZV1", pr1cX, prY - 40, () => App.Plant?.Press1HasClearance ?? false);
var zv2 = new CenterValve("ZV2", pr2cX, prY - 40, () => App.Plant?.Press2HasClearance ?? false);
CreatePipe(xp6, zv1, true);
CreatePipe(xp8, zv2, true);
CreateInlet(zv1, press1, true);
CreateInlet(zv2, press2, true);
} }
protected static Valve CreateValve(int n, double x, double y) { protected Valve CreateValve(int n, double x, double y) {
return new(n, x, y, () => App.Plant?.IsValveOpen(n) ?? false, () => App.Plant?.WantValveOpen(n) ?? false); return new(n, x, y, () => App.Plant?.IsValveOpen(n) ?? false, (v) => App.Plant?.ManualControlMode ?? false ? App.Plant.WantValveOpen(n) : SelectedPaths.Any(p => p.Hops.Any(h => h.Node == 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) {
return new($"Tank {n}", x, y, capacity, () => App.Plant?.TankFillLevels[n - 1] ?? FillState.Unknown); return new($"Tank {n}", x, y, capacity, () => App.Plant?.TankFillLevels[n - 1] ?? FillState.Unknown);
} }
public void TraverseSubgraph(INode start, HashSet<IEdge>? visited = null, Action<IEdge>? edgeAction = null, Action<Valve>? valveAction = null) { public void TraverseSubgraph(INode start, Func<INode, bool> invalid, HashSet<IEdge>? _visited = null, Action<INode, bool, IEdge>? edgeAction = null, Action<Valve>? valveAction = null, bool reverse = false) {
visited ??= []; var visited = _visited ?? [];
if (start is Valve v) { if (start is Valve v) {
valveAction?.Invoke(v); valveAction?.Invoke(v);
return; }
} else if (start is ISink || start is Pump) { if (invalid(start)) {
return; return;
} }
foreach (var edge in start.Inputs) { foreach (var edge in start.Inputs) {
if (visited.Contains(edge)) continue; if (visited.Contains(edge)) continue;
var o = edge.IsTwoWay && edge.Start == start ? edge.End : edge.Start; var o = edge.IsTwoWay && edge.Start == start ? edge.End : edge.Start;
edgeAction?.Invoke(edge); edgeAction?.Invoke(start, reverse || _visited == null, edge);
visited.Add(edge); visited.Add(edge);
TraverseSubgraph(o, visited, edgeAction, valveAction); TraverseSubgraph(o, invalid, visited, edgeAction, valveAction, reverse || _visited == null);
} }
foreach (var edge in start.Outputs) { foreach (var edge in start.Outputs) {
if (visited.Contains(edge)) continue; if (visited.Contains(edge)) continue;
var o = edge.IsTwoWay && edge.End == start ? edge.Start : edge.End; var o = edge.IsTwoWay && edge.End == start ? edge.Start : edge.End;
edgeAction?.Invoke(edge); edgeAction?.Invoke(start, reverse, edge);
visited.Add(edge); visited.Add(edge);
TraverseSubgraph(o, visited, edgeAction, valveAction); TraverseSubgraph(o, invalid, visited, edgeAction, valveAction, reverse);
} }
} }
@@ -436,7 +454,7 @@ namespace PamhagenSysCtrl.Helpers {
private void UpdateValvesAdjacientToPath(Path path, bool locked, bool? wantOpen = null) { private void UpdateValvesAdjacientToPath(Path path, bool locked, bool? wantOpen = null) {
foreach (var (edge, node) in path.Hops) { foreach (var (edge, node) in path.Hops) {
TraverseSubgraph(node, valveAction: (v) => { TraverseSubgraph(node, (n) => n is Valve || n is ISink || n is Pump, valveAction: (v) => {
v.IsLocked = locked; v.IsLocked = locked;
if (wantOpen.HasValue) { if (wantOpen.HasValue) {
if (wantOpen.Value) { if (wantOpen.Value) {
@@ -449,18 +467,41 @@ namespace PamhagenSysCtrl.Helpers {
} }
} }
public void ColorPipesAdjacientToPath(Path path, Brush? color1, bool flowArrows, Brush? color2) { public void ColorPipesAdjacientToPath(Path path, Brush? color1, MotorState flowArrows, Brush? color2) {
var color = color1; var color = color1;
var flowPathOffset = 0.0; var flowPathOffset = 0.0;
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.Flow = flowArrows ? (edge.End == node, flowPathOffset) : null; if (flowArrows > MotorState.Halt) {
if (edge.Flow != null) flowPathOffset += edge.FlowLength; var dir = edge.End == node;
if (flowArrows == MotorState.Backward) dir = !dir;
edge.Flow = (dir, flowPathOffset);
flowPathOffset += edge.FlowLength;
}
visited.Add(edge); visited.Add(edge);
if (node is Pump) color = color2; if (node is Pump) color = color2;
TraverseSubgraph(node, visited, edgeAction: (e) => e.Highlight = color); TraverseSubgraph(node, (n) => n is Valve || n is ISink || n is Pump, visited, edgeAction: (s, r, e) => e.Highlight = color);
} }
} }
public void ColorPipesByValves(INode start, Brush? color1, MotorState flowArrows, Brush? color2) {
var flowPathOffset = 0.0;
TraverseSubgraph(start, (n) => n is ISink || n is Outlet || !((n as Valve)?.IsOpen ?? (n as CenterValve)?.IsOpen ?? true), null,
edgeAction: (s, r, e) => {
if (((e.Start as Valve)?.IsOpen ?? true) && ((e.End as Valve)?.IsOpen ?? true)) {
e.Highlight = color1;
if (flowArrows == MotorState.Forward || flowArrows == MotorState.Backward) {
var dir = e.Start == s;
if (r) dir = !dir;
if (flowArrows == MotorState.Backward) dir = !dir;
e.Flow = (dir, flowPathOffset);
flowPathOffset += e.FlowLength;
}
} else {
e.Highlight = color2;
}
});
}
} }
} }
+115 -81
View File
@@ -9,11 +9,15 @@ namespace PamhagenSysCtrl.Helpers {
protected bool IsRunning = true; protected bool IsRunning = true;
protected Thread? BackgroundThread; protected Thread? BackgroundThread;
public bool ManualControlMode { get; set; }
public bool OverridePathClearances { get; set; }
public bool OverrideDryRunClearances { get; set; }
public event EventHandler<PlantEventArgs>? Update; public event EventHandler<PlantEventArgs>? Update;
protected bool ActuatorsChanged = false; protected bool ActuatorsChanged = false;
protected PamhagenPlc.ActuatorStates Actuators; protected PamhagenPlc.ActuatorStates Actuators;
protected PamhagenPlc.SensorStates Sensors = new(default, default, default, default, [ protected PamhagenPlc.SensorStates Sensors = new(default, default, default, default, default, default, default, [
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);
@@ -28,10 +32,10 @@ namespace PamhagenSysCtrl.Helpers {
public int TroughSelector => Sensors.TroughSelector; public int TroughSelector => Sensors.TroughSelector;
public bool IsMWSelected => Sensors.TroughSelector == 1; public bool IsMWSelected => Sensors.TroughSelector == 1;
public bool IsMW2Selected => Sensors.TroughSelector == 2; public bool IsMW2Selected => Sensors.TroughSelector == 2;
public FillState FillLevelMW1 => Sensors.FillingLevels.MW1; public FillState FillLevelMW1 => Sensors.FillingLevelMW1;
public FillState FillLevelMW2 => Sensors.FillingLevels.MW2; public FillState FillLevelMW2 => Sensors.FillingLevelMW2;
public FillState FillLevelPress1 => Sensors.FillingLevels.Press1; public bool Press1HasClearance => Sensors.ClearancePress1;
public FillState FillLevelPress2 => Sensors.FillingLevels.Press2; public bool Press2HasClearance => Sensors.ClearancePress2;
public FillState FillLevelT1 => Sensors.FillingLevelTanks[0]; public FillState FillLevelT1 => Sensors.FillingLevelTanks[0];
public FillState FillLevelT2 => Sensors.FillingLevelTanks[1]; public FillState FillLevelT2 => Sensors.FillingLevelTanks[1];
public FillState FillLevelT3 => Sensors.FillingLevelTanks[2]; public FillState FillLevelT3 => Sensors.FillingLevelTanks[2];
@@ -53,10 +57,10 @@ namespace PamhagenSysCtrl.Helpers {
public bool IsReblerActive => Actuators.Rebler; public bool IsReblerActive => Actuators.Rebler;
public bool IsStirrerMW1Active => Actuators.StirrerMW1; public bool IsStirrerMW1Active => Actuators.StirrerMW1;
public bool IsStirrerMW2Active => Actuators.StirrerMW2; public bool IsStirrerMW2Active => Actuators.StirrerMW2;
public bool IsBeltActive => Actuators.Belt;
public bool IsAuger1Active => Actuators.Auger1; public bool IsAuger1Active => Actuators.Auger1;
public bool IsAuger2Active => Actuators.Auger2; public bool IsAuger2Active => Actuators.Auger2;
public bool IsWasteDisposalActive => IsBeltActive || IsAuger1Active || IsAuger2Active; public bool IsAuger3Active => Actuators.Auger3;
public bool IsWasteDisposalActive => IsAuger1Active || IsAuger2Active || IsAuger3Active;
public bool IsGradationPumpActive => Actuators.GradationPump; public bool IsGradationPumpActive => Actuators.GradationPump;
public bool IsMP1Active => Actuators.MP1 == MotorState.Forward || Actuators.MP1 == MotorState.Backward; public bool IsMP1Active => Actuators.MP1 == MotorState.Forward || Actuators.MP1 == MotorState.Backward;
@@ -78,14 +82,32 @@ namespace PamhagenSysCtrl.Helpers {
protected static void OnUpdate(object? sender, PlantEventArgs evt) { protected static void OnUpdate(object? sender, PlantEventArgs evt) {
if (sender is not PamhagenPlant p) return; if (sender is not PamhagenPlant p) return;
if (p.IsPresseQuerSchneckeActive && !p.IsBeltActive) p.StartBelt();
if (p.IsReblerActive && !p.IsAuger1Active) p.StartAuger1(); if (!p.ManualControlMode) {
if (p.IsAuger1Active && !p.IsBeltActive) p.StartBelt(); if (p.IsPresseQuerSchneckeActive && !p.IsAuger2Active) p.StartAuger2();
if (p.IsBeltActive && !p.IsAuger2Active) p.StartAuger2(); if (p.IsReblerActive && !p.IsAuger1Active) p.StartAuger1();
if (p.IsTroughAugerActive && (!p.IsReblerActive || (p.IsMWSelected && p.FillLevelMW1 == FillState.Full) || (p.IsMW2Selected && p.FillLevelMW2 == FillState.Full))) if (p.IsAuger1Active && !p.IsAuger2Active) p.StartAuger2();
p.StopTroughAuger(); if (p.IsAuger2Active && !p.IsAuger3Active) p.StartAuger3();
if (p.IsTroughAugerActive && (!p.IsReblerActive || (p.IsMWSelected && p.FillLevelMW1 == FillState.Full) || (p.IsMW2Selected && p.FillLevelMW2 == FillState.Full)))
p.StopTroughAuger();
}
if (p.IsMP1Active && !p.MP1HasClearance) p.StopMP1(); if (p.IsMP1Active && !p.MP1HasClearance) p.StopMP1();
if (p.IsMP2Active && !p.MP2HasClearance) p.StopMP2(); if (p.IsMP2Active && !p.MP2HasClearance) p.StopMP2();
if (!p.ManualControlMode) {
if (p.MP1 == MotorState.Forward && p.FillLevelMW1 >= FillState.Half && !p.IsStirrerMW1Active) {
p.StartStirrerMW1();
} else if (p.IsStirrerMW1Active && (!p.IsMP1Active || p.FillLevelMW1 < FillState.Half)) {
p.StopStirrerMW1();
}
if (p.MP2 == MotorState.Forward && p.FillLevelMW2 >= FillState.Half && !p.IsStirrerMW2Active) {
p.StartStirrerMW2();
} else if (p.IsStirrerMW2Active && (!p.IsMP2Active || p.FillLevelMW2 < FillState.Half)) {
p.StopStirrerMW2();
}
}
if (p.IsP12Active && !p.P12HasClearance) p.StopP12(); if (p.IsP12Active && !p.P12HasClearance) p.StopP12();
if (p.IsP3Active && !p.P3HasClearance) p.StopP3(); if (p.IsP3Active && !p.P3HasClearance) p.StopP3();
if (p.IsP4Active && !p.P4HasClearance) p.StopP4(); if (p.IsP4Active && !p.P4HasClearance) p.StopP4();
@@ -210,25 +232,26 @@ 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 => FillLevelMW1 != FillState.Empty && ( public bool MP1HasClearance => (OverrideDryRunClearances || FillLevelMW1 != FillState.Empty) && (
(MP1HasClearancePress1 && FillLevelPress1 != FillState.Full) || OverridePathClearances ||
(MP1HasClearancePress2 && FillLevelPress2 != FillState.Full) || (MP1HasClearancePress1 && Press1HasClearance) ||
(HasClearanceTank1(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT1 != FillState.Full) || (MP1HasClearancePress2 && Press2HasClearance) ||
(HasClearanceTank2(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT2 != FillState.Full) || (HasClearanceTank1(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT1 != FillState.Full) ||
(HasClearanceTank3(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT3 != FillState.Full) || (HasClearanceTank2(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT2 != FillState.Full) ||
(HasClearanceTank4(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT4 != FillState.Full) || (HasClearanceTank3(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT3 != FillState.Full) ||
(HasClearanceTank5(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT5 != FillState.Full) || (HasClearanceTank4(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT4 != FillState.Full) ||
(HasClearanceTank6(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT6 != FillState.Full) || (HasClearanceTank5(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT5 != FillState.Full) ||
(HasClearanceTank7(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT7 != FillState.Full) || (HasClearanceTank6(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT6 != FillState.Full) ||
(HasClearanceTank8(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT8 != FillState.Full) || (HasClearanceTank7(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT7 != FillState.Full) ||
(HasClearanceTank9(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT9 != FillState.Full) || (HasClearanceTank8(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT8 != FillState.Full) ||
false); (HasClearanceTank9(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT9 != FillState.Full) ||
false);
protected bool MP1HasClearancePress1 => protected bool MP1HasClearancePress1 =>
(AreValvesOpen(1, 6, 51) && AreValvesClosed(3, 4, 5, 55)) || (AreValvesOpen(1, 6, 51) && AreValvesClosed(3, 4, 5, 50, 52, 53, 55)) ||
(AreValvesOpen(4, 8, 52) && AreValvesClosed(1, 2, 7, 56)); (AreValvesOpen(4, 8, 52) && AreValvesClosed(1, 2, 7, 50, 51, 53, 56));
protected bool MP1HasClearancePress2 => protected bool MP1HasClearancePress2 =>
(AreValvesOpen(1, 6, 55) && AreValvesClosed(3, 4, 5, 51)) || (AreValvesOpen(1, 6, 55) && AreValvesClosed(3, 4, 5, 51, 54, 56, 57)) ||
(AreValvesOpen(4, 8, 56) && AreValvesClosed(1, 2, 7, 52)); (AreValvesOpen(4, 8, 56) && AreValvesClosed(1, 2, 7, 52, 54, 55, 57));
protected bool MP1HasClearanceTanksA => AreValvesOpen(1, 5) && AreValvesClosed(3, 4, 6); protected bool MP1HasClearanceTanksA => AreValvesOpen(1, 5) && AreValvesClosed(3, 4, 6);
protected bool MP1HasClearanceTanksB => AreValvesOpen(4, 7, 9) && AreValvesClosed(1, 2, 8, 10); protected bool MP1HasClearanceTanksB => AreValvesOpen(4, 7, 9) && AreValvesClosed(1, 2, 8, 10);
@@ -237,9 +260,10 @@ 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 => FillLevelMW2 != FillState.Empty && ( public bool MP2HasClearance => (OverrideDryRunClearances || FillLevelMW2 != FillState.Empty) && (
(MP2HasClearancePress1 && FillLevelPress1 != FillState.Full) || OverridePathClearances ||
(MP2HasClearancePress2 && FillLevelPress2 != FillState.Full) || (MP2HasClearancePress1 && Press1HasClearance) ||
(MP2HasClearancePress2 && Press2HasClearance) ||
(HasClearanceTank1(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT1 != FillState.Full) || (HasClearanceTank1(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT1 != FillState.Full) ||
(HasClearanceTank2(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT2 != FillState.Full) || (HasClearanceTank2(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT2 != FillState.Full) ||
(HasClearanceTank3(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT3 != FillState.Full) || (HasClearanceTank3(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT3 != FillState.Full) ||
@@ -251,11 +275,11 @@ namespace PamhagenSysCtrl.Helpers {
(HasClearanceTank9(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT9 != FillState.Full) || (HasClearanceTank9(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT9 != FillState.Full) ||
false); false);
protected bool MP2HasClearancePress1 => protected bool MP2HasClearancePress1 =>
(AreValvesOpen(2, 8, 52) && AreValvesClosed(3, 4, 7, 56)) || (AreValvesOpen(2, 8, 52) && AreValvesClosed(3, 4, 7, 50, 51, 53, 56)) ||
(AreValvesOpen(3, 6, 51) && AreValvesClosed(1, 2, 5, 55)); (AreValvesOpen(3, 6, 51) && AreValvesClosed(1, 2, 5, 50, 52, 53, 55));
protected bool MP2HasClearancePress2 => protected bool MP2HasClearancePress2 =>
(AreValvesOpen(2, 8, 56) && AreValvesClosed(3, 4, 7, 52)) || (AreValvesOpen(2, 8, 56) && AreValvesClosed(3, 4, 7, 52, 54, 55, 57)) ||
(AreValvesOpen(3, 6, 55) && AreValvesClosed(2, 1, 5, 51)); (AreValvesOpen(3, 6, 55) && AreValvesClosed(2, 1, 5, 51, 54, 56, 57));
protected bool MP2HasClearanceTanksA => AreValvesOpen(3, 5) && AreValvesClosed(1, 2, 6); protected bool MP2HasClearanceTanksA => AreValvesOpen(3, 5) && AreValvesClosed(1, 2, 6);
protected bool MP2HasClearanceTanksB => AreValvesOpen(2, 7, 9) && AreValvesClosed(3, 4, 8, 10); protected bool MP2HasClearanceTanksB => AreValvesOpen(2, 7, 9) && AreValvesClosed(3, 4, 8, 10);
@@ -264,9 +288,10 @@ namespace PamhagenSysCtrl.Helpers {
set => Actuators.MP2 = (value != MotorState.Halt && value != MotorState.Forward && value != MotorState.Backward) ? throw new ArgumentException("Invalid state for MP2") : value; set => Actuators.MP2 = (value != MotorState.Halt && value != MotorState.Forward && value != MotorState.Backward) ? throw new ArgumentException("Invalid state for MP2") : value;
} }
public bool P12HasClearance => (FillLevelT1 != FillState.Empty || FillLevelT2 != FillState.Empty) && ( public bool P12HasClearance => (OverrideDryRunClearances || FillLevelT1 != FillState.Empty || FillLevelT2 != FillState.Empty) && (
(P12HasClearancePress1 && FillLevelPress1 != FillState.Full) || OverridePathClearances ||
(P12HasClearancePress2 && FillLevelPress2 != FillState.Full) || (P12HasClearancePress1 && Press1HasClearance) ||
(P12HasClearancePress2 && Press2HasClearance) ||
(HasClearanceTank1(false, P12HasClearanceTanksB) && FillLevelT1 != FillState.Full) || (HasClearanceTank1(false, P12HasClearanceTanksB) && FillLevelT1 != FillState.Full) ||
(HasClearanceTank2(false, P12HasClearanceTanksB) && FillLevelT2 != FillState.Full) || (HasClearanceTank2(false, P12HasClearanceTanksB) && FillLevelT2 != FillState.Full) ||
(HasClearanceTank3(false, P12HasClearanceTanksB) && FillLevelT3 != FillState.Full) || (HasClearanceTank3(false, P12HasClearanceTanksB) && FillLevelT3 != FillState.Full) ||
@@ -277,8 +302,8 @@ namespace PamhagenSysCtrl.Helpers {
(HasClearanceTank8(false, P12HasClearanceTanksB) && FillLevelT8 != FillState.Full) || (HasClearanceTank8(false, P12HasClearanceTanksB) && FillLevelT8 != FillState.Full) ||
(HasClearanceTank9(false, P12HasClearanceTanksB) && FillLevelT9 != FillState.Full) || (HasClearanceTank9(false, P12HasClearanceTanksB) && FillLevelT9 != FillState.Full) ||
false); false);
protected bool P12HasClearancePress1 => AreValvesOpen(44, 46, 49, 50) && AreValvesClosed(10, 45, 47, 48, 54); protected bool P12HasClearancePress1 => AreValvesOpen(44, 46, 49, 50) && AreValvesClosed(10, 45, 47, 48, 54, 51, 52, 53);
protected bool P12HasClearancePress2 => AreValvesOpen(44, 46, 49, 54) && AreValvesClosed(10, 45, 47, 48, 50); protected bool P12HasClearancePress2 => AreValvesOpen(44, 46, 49, 54) && AreValvesClosed(10, 45, 47, 48, 50, 55, 56, 57);
protected bool P12HasClearanceTanksB => AreValvesOpen(44, 10) && AreValvesClosed(9, 45, 46); protected bool P12HasClearanceTanksB => AreValvesOpen(44, 10) && AreValvesClosed(9, 45, 46);
public MotorState P12 { public MotorState P12 {
@@ -286,9 +311,10 @@ namespace PamhagenSysCtrl.Helpers {
set => Actuators.P12 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P12") : value; set => Actuators.P12 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P12") : value;
} }
public bool P3HasClearance => FillLevelT3 != FillState.Empty && ( public bool P3HasClearance => (OverrideDryRunClearances || FillLevelT3 != FillState.Empty) && (
(P3HasClearancePress1 && FillLevelPress1 != FillState.Full) || OverridePathClearances ||
(P3HasClearancePress2 && FillLevelPress2 != FillState.Full) || (P3HasClearancePress1 && Press1HasClearance) ||
(P3HasClearancePress2 && Press2HasClearance) ||
(HasClearanceTank1(false, P3HasClearanceTanksB) && FillLevelT1 != FillState.Full) || (HasClearanceTank1(false, P3HasClearanceTanksB) && FillLevelT1 != FillState.Full) ||
(HasClearanceTank2(false, P3HasClearanceTanksB) && FillLevelT2 != FillState.Full) || (HasClearanceTank2(false, P3HasClearanceTanksB) && FillLevelT2 != FillState.Full) ||
(HasClearanceTank3(false, P3HasClearanceTanksB) && FillLevelT3 != FillState.Full) || (HasClearanceTank3(false, P3HasClearanceTanksB) && FillLevelT3 != FillState.Full) ||
@@ -299,8 +325,8 @@ namespace PamhagenSysCtrl.Helpers {
(HasClearanceTank8(false, P3HasClearanceTanksB) && FillLevelT8 != FillState.Full) || (HasClearanceTank8(false, P3HasClearanceTanksB) && FillLevelT8 != FillState.Full) ||
(HasClearanceTank9(false, P3HasClearanceTanksB) && FillLevelT9 != FillState.Full) || (HasClearanceTank9(false, P3HasClearanceTanksB) && FillLevelT9 != FillState.Full) ||
false); false);
protected bool P3HasClearancePress1 => AreValvesOpen(45, 46, 49, 50) && AreValvesClosed(10, 44, 47, 48, 54); protected bool P3HasClearancePress1 => AreValvesOpen(45, 46, 49, 50) && AreValvesClosed(10, 44, 47, 48, 51, 52, 53, 54);
protected bool P3HasClearancePress2 => AreValvesOpen(45, 46, 49, 54) && AreValvesClosed(10, 44, 47, 48, 50); protected bool P3HasClearancePress2 => AreValvesOpen(45, 46, 49, 54) && AreValvesClosed(10, 44, 47, 48, 50, 55, 56, 57);
protected bool P3HasClearanceTanksB => AreValvesOpen(45, 10) && AreValvesClosed(9, 44, 46); protected bool P3HasClearanceTanksB => AreValvesOpen(45, 10) && AreValvesClosed(9, 44, 46);
public MotorState P3 { public MotorState P3 {
@@ -308,9 +334,10 @@ namespace PamhagenSysCtrl.Helpers {
set => Actuators.P3 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P3") : value; set => Actuators.P3 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P3") : value;
} }
public bool P4HasClearance => FillLevelT4 != FillState.Empty && ( public bool P4HasClearance => (OverrideDryRunClearances || FillLevelT4 != FillState.Empty) && (
(P4HasClearancePress1 && FillLevelPress1 != FillState.Full) || OverridePathClearances ||
(P4HasClearancePress2 && FillLevelPress2 != FillState.Full) || (P4HasClearancePress1 && Press1HasClearance) ||
(P4HasClearancePress2 && Press2HasClearance) ||
(HasClearanceTank1(false, P4HasClearanceTanksB) && FillLevelT1 != FillState.Full) || (HasClearanceTank1(false, P4HasClearanceTanksB) && FillLevelT1 != FillState.Full) ||
(HasClearanceTank2(false, P4HasClearanceTanksB) && FillLevelT2 != FillState.Full) || (HasClearanceTank2(false, P4HasClearanceTanksB) && FillLevelT2 != FillState.Full) ||
(HasClearanceTank3(false, P4HasClearanceTanksB) && FillLevelT3 != FillState.Full) || (HasClearanceTank3(false, P4HasClearanceTanksB) && FillLevelT3 != FillState.Full) ||
@@ -321,8 +348,8 @@ namespace PamhagenSysCtrl.Helpers {
(HasClearanceTank8(false, P4HasClearanceTanksB) && FillLevelT8 != FillState.Full) || (HasClearanceTank8(false, P4HasClearanceTanksB) && FillLevelT8 != FillState.Full) ||
(HasClearanceTank9(false, P4HasClearanceTanksB) && FillLevelT9 != FillState.Full) || (HasClearanceTank9(false, P4HasClearanceTanksB) && FillLevelT9 != FillState.Full) ||
false); false);
protected bool P4HasClearancePress1 => AreValvesOpen(47, 49, 50) && AreValvesClosed(46, 48, 54); protected bool P4HasClearancePress1 => AreValvesOpen(47, 49, 50) && AreValvesClosed(46, 48, 51, 52, 53, 54);
protected bool P4HasClearancePress2 => AreValvesOpen(47, 49, 54) && AreValvesClosed(46, 48, 50); protected bool P4HasClearancePress2 => AreValvesOpen(47, 49, 54) && AreValvesClosed(46, 48, 50, 55, 56, 57);
protected bool P4HasClearanceTanksB => AreValvesOpen(47, 46, 10) && AreValvesClosed(9, 44, 45, 48, 49); protected bool P4HasClearanceTanksB => AreValvesOpen(47, 46, 10) && AreValvesClosed(9, 44, 45, 48, 49);
public MotorState P4 { public MotorState P4 {
@@ -330,9 +357,10 @@ namespace PamhagenSysCtrl.Helpers {
set => Actuators.P4 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P4") : value; set => Actuators.P4 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P4") : value;
} }
public bool P5HasClearance => FillLevelT5 != FillState.Empty && ( public bool P5HasClearance => (OverrideDryRunClearances || FillLevelT5 != FillState.Empty) && (
(P5HasClearancePress1 && FillLevelPress1 != FillState.Full) || OverridePathClearances ||
(P5HasClearancePress2 && FillLevelPress2 != FillState.Full) || (P5HasClearancePress1 && Press1HasClearance) ||
(P5HasClearancePress2 && Press2HasClearance) ||
(HasClearanceTank1(false, P5HasClearanceTanksB) && FillLevelT1 != FillState.Full) || (HasClearanceTank1(false, P5HasClearanceTanksB) && FillLevelT1 != FillState.Full) ||
(HasClearanceTank2(false, P5HasClearanceTanksB) && FillLevelT2 != FillState.Full) || (HasClearanceTank2(false, P5HasClearanceTanksB) && FillLevelT2 != FillState.Full) ||
(HasClearanceTank3(false, P5HasClearanceTanksB) && FillLevelT3 != FillState.Full) || (HasClearanceTank3(false, P5HasClearanceTanksB) && FillLevelT3 != FillState.Full) ||
@@ -343,8 +371,8 @@ namespace PamhagenSysCtrl.Helpers {
(HasClearanceTank8(false, P5HasClearanceTanksB) && FillLevelT8 != FillState.Full) || (HasClearanceTank8(false, P5HasClearanceTanksB) && FillLevelT8 != FillState.Full) ||
(HasClearanceTank9(false, P5HasClearanceTanksB) && FillLevelT9 != FillState.Full) || (HasClearanceTank9(false, P5HasClearanceTanksB) && FillLevelT9 != FillState.Full) ||
false); false);
protected bool P5HasClearancePress1 => AreValvesOpen(48, 49, 50) && AreValvesClosed(46, 47, 54); protected bool P5HasClearancePress1 => AreValvesOpen(48, 49, 50) && AreValvesClosed(46, 47, 51, 52, 53, 54);
protected bool P5HasClearancePress2 => AreValvesOpen(48, 49, 54) && AreValvesClosed(46, 47, 50); protected bool P5HasClearancePress2 => AreValvesOpen(48, 49, 54) && AreValvesClosed(46, 47, 50, 55, 56, 57);
protected bool P5HasClearanceTanksB => AreValvesOpen(48, 46, 10) && AreValvesClosed(9, 44, 45, 47, 49); protected bool P5HasClearanceTanksB => AreValvesOpen(48, 46, 10) && AreValvesClosed(9, 44, 45, 47, 49);
public MotorState P5 { public MotorState P5 {
@@ -352,9 +380,9 @@ namespace PamhagenSysCtrl.Helpers {
set => Actuators.P5 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P5") : value; set => Actuators.P5 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P5") : value;
} }
public bool P6HasClearance => public bool P6HasClearance => OverridePathClearances ||
(IsValveOpen(53) && IsValveClosed(57) && FillLevelPress1 != FillState.Full) || (AreValvesOpen(53) && AreValvesClosed(50, 51, 52, 57) && Press1HasClearance) ||
(IsValveOpen(57) && IsValveClosed(53) && FillLevelPress2 != FillState.Full); (AreValvesOpen(57) && AreValvesClosed(53, 54, 55, 56) && Press2HasClearance);
public MotorState P6 { public MotorState P6 {
get => Actuators.P6; get => Actuators.P6;
@@ -373,18 +401,16 @@ namespace PamhagenSysCtrl.Helpers {
public void SelectMW1() { public void SelectMW1() {
Actuators.SetV(58, true); Actuators.SetV(58, true);
//if (IsReblerActive) Actuators.StirrerMW1 = true;
ActuatorsChanged = true; ActuatorsChanged = true;
} }
public void SelectMW2() { public void SelectMW2() {
Actuators.SetV(58, false); Actuators.SetV(58, false);
//if (IsReblerActive) Actuators.StirrerMW2 = true;
ActuatorsChanged = true; ActuatorsChanged = true;
} }
public void StartTroughAuger() { public void StartTroughAuger() {
if (!IsReblerActive || (IsMWSelected && FillLevelMW1 == FillState.Full) || (IsMW2Selected && FillLevelMW2 == FillState.Full)) if (!ManualControlMode && (!IsReblerActive || (IsMWSelected && FillLevelMW1 == FillState.Full) || (IsMW2Selected && FillLevelMW2 == FillState.Full)))
throw new NoClearanceException("Schnecke Auffangwanne hat keine Freigabe"); throw new NoClearanceException("Schnecke Auffangwanne hat keine Freigabe");
Actuators.TroughAuger = true; Actuators.TroughAuger = true;
ActuatorsChanged = true; ActuatorsChanged = true;
@@ -397,12 +423,12 @@ namespace PamhagenSysCtrl.Helpers {
public void StartRebler() { public void StartRebler() {
Actuators.Rebler = true; Actuators.Rebler = true;
StartAuger1(); if (!ManualControlMode) StartAuger1();
ActuatorsChanged = true; ActuatorsChanged = true;
} }
public void StopRebler() { public void StopRebler() {
StopTroughAuger(); if (!ManualControlMode) StopTroughAuger();
Actuators.Rebler = false; Actuators.Rebler = false;
ActuatorsChanged = true; ActuatorsChanged = true;
} }
@@ -427,42 +453,42 @@ namespace PamhagenSysCtrl.Helpers {
ActuatorsChanged = true; ActuatorsChanged = true;
} }
public void StartBelt() {
Actuators.Belt = true;
StartAuger2();
ActuatorsChanged = true;
}
public void StopBelt() {
if (IsPresseQuerSchneckeActive) throw new NoClearanceException("Presse-Querschnecke aktiv");
if (IsAuger1Active) throw new NoClearanceException("Förderschnecke 1 aktiv");
Actuators.Belt = false;
ActuatorsChanged = true;
}
public void StartAuger1() { public void StartAuger1() {
Actuators.Auger1 = true; Actuators.Auger1 = true;
StartBelt(); if (!ManualControlMode) StartAuger2();
ActuatorsChanged = true; ActuatorsChanged = true;
} }
public void StopAuger1() { public void StopAuger1() {
if (IsReblerActive) throw new NoClearanceException("Rebler aktiv"); if (!ManualControlMode && IsReblerActive) throw new NoClearanceException("Rebler aktiv");
Actuators.Auger1 = false; Actuators.Auger1 = false;
ActuatorsChanged = true; ActuatorsChanged = true;
} }
public void StartAuger2() { public void StartAuger2() {
Actuators.Auger2 = true; Actuators.Auger2 = true;
if (!ManualControlMode) StartAuger3();
ActuatorsChanged = true; ActuatorsChanged = true;
} }
public void StopAuger2() { public void StopAuger2() {
if (IsBeltActive) throw new NoClearanceException("Förderband aktiv"); if (!ManualControlMode && IsPresseQuerSchneckeActive) throw new NoClearanceException("Presse-Querschnecke aktiv");
if (!ManualControlMode && IsAuger1Active) throw new NoClearanceException("Förderschnecke 1 aktiv");
Actuators.Auger2 = false; Actuators.Auger2 = false;
ActuatorsChanged = true; ActuatorsChanged = true;
} }
public void StartAuger3() {
Actuators.Auger3 = true;
ActuatorsChanged = true;
}
public void StopAuger3() {
if (!ManualControlMode && IsAuger2Active) throw new NoClearanceException("Förderschnecke 2 aktiv");
Actuators.Auger3 = false;
ActuatorsChanged = true;
}
public void StartGradationPump() { public void StartGradationPump() {
Actuators.GradationPump = true; Actuators.GradationPump = true;
ActuatorsChanged = true; ActuatorsChanged = true;
@@ -475,35 +501,43 @@ namespace PamhagenSysCtrl.Helpers {
public void StartMP1Forward() { public void StartMP1Forward() {
if (!MP1HasClearance) throw new NoClearanceException("MP1 hat keine Freigabe"); if (!MP1HasClearance) throw new NoClearanceException("MP1 hat keine Freigabe");
if (Actuators.MP1 == MotorState.Backward) throw new NoClearanceException("MP1 läuft bereits (zurück)");
Actuators.MP1 = MotorState.Forward; Actuators.MP1 = MotorState.Forward;
if (FillLevelMW1 >= FillState.Half) StartStirrerMW1();
ActuatorsChanged = true; ActuatorsChanged = true;
} }
public void StartMP1Backward() { public void StartMP1Backward() {
if (!MP1HasClearance) throw new NoClearanceException("MP1 hat keine Freigabe"); if (!MP1HasClearance) throw new NoClearanceException("MP1 hat keine Freigabe");
if (Actuators.MP1 == MotorState.Forward) throw new NoClearanceException("MP1 läuft bereits (vor)");
Actuators.MP1 = MotorState.Backward; Actuators.MP1 = MotorState.Backward;
ActuatorsChanged = true; ActuatorsChanged = true;
} }
public void StopMP1() { public void StopMP1() {
Actuators.MP1 = MotorState.Halt; Actuators.MP1 = MotorState.Halt;
if (!ManualControlMode) StopStirrerMW1();
ActuatorsChanged = true; ActuatorsChanged = true;
} }
public void StartMP2Forward() { public void StartMP2Forward() {
if (!MP2HasClearance) throw new NoClearanceException("MP2 hat keine Freigabe"); if (!MP2HasClearance) throw new NoClearanceException("MP2 hat keine Freigabe");
if (Actuators.MP2 == MotorState.Backward) throw new NoClearanceException("MP2 läuft bereits (zurück)");
Actuators.MP2 = MotorState.Forward; Actuators.MP2 = MotorState.Forward;
if (FillLevelMW2 >= FillState.Half) StartStirrerMW2();
ActuatorsChanged = true; ActuatorsChanged = true;
} }
public void StartMP2Backward() { public void StartMP2Backward() {
if (!MP2HasClearance) throw new NoClearanceException("MP2 hat keine Freigabe"); if (!MP2HasClearance) throw new NoClearanceException("MP2 hat keine Freigabe");
if (Actuators.MP2 == MotorState.Forward) throw new NoClearanceException("MP2 läuft bereits (vor)");
Actuators.MP2 = MotorState.Backward; Actuators.MP2 = MotorState.Backward;
ActuatorsChanged = true; ActuatorsChanged = true;
} }
public void StopMP2() { public void StopMP2() {
Actuators.MP2 = MotorState.Halt; Actuators.MP2 = MotorState.Halt;
if (!ManualControlMode) StopStirrerMW1();
ActuatorsChanged = true; ActuatorsChanged = true;
} }
+11 -8
View File
@@ -5,7 +5,10 @@ namespace PamhagenSysCtrl.Helpers {
public record struct SensorStates( public record struct SensorStates(
long PressureSensors, long PressureSensors,
(FillState MW1, FillState MW2, FillState Press1, FillState Press2) FillingLevels, FillState FillingLevelMW1,
FillState FillingLevelMW2,
bool ClearancePress1,
bool ClearancePress2,
int TroughSelector, int TroughSelector,
double GradationOe, double GradationOe,
FillState[] FillingLevelTanks, FillState[] FillingLevelTanks,
@@ -17,7 +20,7 @@ namespace PamhagenSysCtrl.Helpers {
public record struct ActuatorStates( public record struct ActuatorStates(
long Valves, long Valves,
MotorState MP1, MotorState MP2, MotorState P12, MotorState P3, MotorState P4, MotorState P5, MotorState P6, MotorState MP1, MotorState MP2, MotorState P12, MotorState P3, MotorState P4, MotorState P5, MotorState P6,
bool TroughAuger, bool Rebler, bool StirrerMW1, bool StirrerMW2, bool Belt, bool Auger1, bool Auger2, bool GradationPump bool TroughAuger, bool Rebler, bool StirrerMW1, bool StirrerMW2, bool Auger2, bool Auger3, bool Auger1, bool GradationPump
) { ) {
public readonly bool GetV(int n) => (Valves & (1L << (n - 1))) != 0; public readonly bool GetV(int n) => (Valves & (1L << (n - 1))) != 0;
public void SetV(int n, bool val) { public void SetV(int n, bool val) {
@@ -109,8 +112,8 @@ namespace PamhagenSysCtrl.Helpers {
var mw1 = Int3ToFillState((f1 ) & 0x7); // 111 011 001 000 var mw1 = Int3ToFillState((f1 ) & 0x7); // 111 011 001 000
var mw2 = Int3ToFillState((f1 >> 3) & 0x7); // 111 011 001 000 var mw2 = Int3ToFillState((f1 >> 3) & 0x7); // 111 011 001 000
var p1 = Int1ToFillState((f1 >> 6) & 0x1); // 1 0 var p1 = ((f1 >> 6) & 0x1) == 0; // 1 0
var p2 = Int1ToFillState((f1 >> 7) & 0x1); // 1 0 var p2 = ((f1 >> 7) & 0x1) == 0; // 1 0
var wp = ((f1 >> 8) & 0x3); // 10 01 var wp = ((f1 >> 8) & 0x3); // 10 01
// Masseligerät : 4mA => 50 Oe // Masseligerät : 4mA => 50 Oe
@@ -137,7 +140,7 @@ namespace PamhagenSysCtrl.Helpers {
var t9 = Int1ToFillState((f2 >> 13) & 0x1); // 1 0 var t9 = Int1ToFillState((f2 >> 13) & 0x1); // 1 0
var q1 = Int1ToPumpState((f2 >> 15) & 0x1); // 1 0 var q1 = Int1ToPumpState((f2 >> 15) & 0x1); // 1 0
return new SensorStates(pressure, (mw1, mw2, p1, p2), wp == 3 ? -1 : wp == 1 ? 2 : wp == 2 ? 1 : 0, oe, [t1, t2, t3, t4, t5, t6, t7, t8, t9], q1); return new SensorStates(pressure, mw1, mw2, p1, p2, wp == 3 ? -1 : wp == 1 ? 2 : wp == 2 ? 1 : 0, oe, [t1, t2, t3, t4, t5, t6, t7, t8, t9], q1);
} }
protected async Task<(ushort RW000, ushort RW001, ushort RW002, ushort RW003, ushort RW004, ushort RW005, ushort RW006)> ReadOutputRegisters() { protected async Task<(ushort RW000, ushort RW001, ushort RW002, ushort RW003, ushort RW004, ushort RW005, ushort RW006)> ReadOutputRegisters() {
@@ -212,9 +215,9 @@ namespace PamhagenSysCtrl.Helpers {
(state.GetV(58) ? 0x04 : 0x00) | (state.GetV(58) ? 0x04 : 0x00) |
(state.StirrerMW1 ? 0x08 : 0x00) | (state.StirrerMW1 ? 0x08 : 0x00) |
(state.StirrerMW2 ? 0x10 : 0x00) | (state.StirrerMW2 ? 0x10 : 0x00) |
(state.Belt ? 0x20 : 0x00) | (state.Auger2 ? 0x20 : 0x00) |
(state.Auger1 ? 0x40 : 0x00) | (state.Auger3 ? 0x40 : 0x00) |
(state.Auger2 ? 0x80 : 0x00); (state.Auger1 ? 0x80 : 0x00);
int measuring = int measuring =
(state.GetV(59) ? 0x01 : 0x00) | (state.GetV(59) ? 0x01 : 0x00) |
@@ -0,0 +1,70 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace PamhagenSysCtrl.Helpers.Pipeline {
public class CenterValve : INode {
public const double DIAMETER = 24;
public double CenterX { get; set; }
public double CenterY { get; set; }
public string Label { get; set; }
public Brush? Highlight { get; set; }
public bool IsOpen => CallbackIsOpen();
public ISet<IEdge> Inputs { get; init; }
public ISet<IEdge> Outputs { get; init; }
protected Func<bool> CallbackIsOpen;
private Shape? _circle;
private TextBlock? _text;
public CenterValve(string label, double x, double y, Func<bool> cbOpen) {
CenterX = x;
CenterY = y;
Label = label;
CallbackIsOpen = cbOpen;
Inputs = new HashSet<IEdge>();
Outputs = new HashSet<IEdge>();
}
public void Draw(Canvas canvas) {
_circle = new Ellipse() {
Height = DIAMETER,
Width = DIAMETER,
Fill = Brushes.DarkGray,
Stroke = Brushes.Black,
StrokeThickness = 2,
};
_circle.SetValue(Canvas.LeftProperty, CenterX - DIAMETER / 2);
_circle.SetValue(Canvas.TopProperty, CenterY - DIAMETER / 2);
_text = new() {
Text = Label,
FontSize = 10,
Width = DIAMETER,
Height = DIAMETER,
TextAlignment = TextAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
SnapsToDevicePixels = true,
};
_text.SetValue(Canvas.LeftProperty, CenterX - DIAMETER / 2 + 0.5);
_text.SetValue(Canvas.TopProperty, CenterY - DIAMETER / 2 + 5.5);
canvas.Children.Add(_circle);
canvas.Children.Add(_text);
}
public bool IsInside(double x, double y) {
return Math.Sqrt(Math.Pow(CenterX - x, 2) + Math.Pow(CenterY - y, 2)) <= DIAMETER / 2;
}
public void Update(bool isHovering, bool isPressed) {
_circle?.Fill =
!IsOpen ? PamhagenBrushes.Red :
IsOpen ? PamhagenBrushes.Green :
Highlight ?? Brushes.DarkGray;
}
}
}
+5 -5
View File
@@ -12,10 +12,10 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
} }
public Pipe CreatePipe(INode start, INode end, bool verticalFirst = false) { public Pipe CreatePipe(INode start, INode end, bool verticalFirst = false, string? label = null, double? labelOffsetX = null) {
Nodes.Add(start); Nodes.Add(start);
Nodes.Add(end); Nodes.Add(end);
var p = new Pipe(start, end, verticalFirst); var p = new Pipe(start, end, label, verticalFirst, default, labelOffsetX);
start.Outputs.Add(p); start.Outputs.Add(p);
end.Inputs.Add(p); end.Inputs.Add(p);
Edges.Add(p); Edges.Add(p);
@@ -25,7 +25,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
public Pipe CreateTwoWayPipe(INode n1, INode n2, bool verticalFirst = false) { public Pipe CreateTwoWayPipe(INode n1, INode n2, bool verticalFirst = false) {
Nodes.Add(n1); Nodes.Add(n1);
Nodes.Add(n2); Nodes.Add(n2);
var p = new Pipe(n1, n2, verticalFirst, twoWay: true); var p = new Pipe(n1, n2, default, verticalFirst, twoWay: true);
n1.Inputs.Add(p); n1.Inputs.Add(p);
n1.Outputs.Add(p); n1.Outputs.Add(p);
n2.Inputs.Add(p); n2.Inputs.Add(p);
@@ -34,10 +34,10 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
return p; return p;
} }
public Inlet CreateInlet(INode start, ISink end) { public Inlet CreateInlet(INode start, ISink end, bool sideways = false) {
Nodes.Add(start); Nodes.Add(start);
Nodes.Add(end); Nodes.Add(end);
var p = new Inlet(start, end, start.CenterX > end.CenterX); var p = new Inlet(start, end, sideways);
start.Outputs.Add(p); start.Outputs.Add(p);
end.Inputs.Add(p); end.Inputs.Add(p);
Edges.Add(p); Edges.Add(p);
@@ -41,6 +41,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
TextAlignment = TextAlignment.Center, TextAlignment = TextAlignment.Center,
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
RenderTransform = new RotateTransform(270), RenderTransform = new RotateTransform(270),
SnapsToDevicePixels = true,
}; };
_rect.SetValue(Canvas.LeftProperty, CenterX - WIDTH / 2); _rect.SetValue(Canvas.LeftProperty, CenterX - WIDTH / 2);
_rect.SetValue(Canvas.TopProperty, CenterY - HEIGHT / 2); _rect.SetValue(Canvas.TopProperty, CenterY - HEIGHT / 2);
@@ -6,6 +6,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
public INode Start { get; } public INode Start { get; }
public INode End { get; } public INode End { get; }
public string? Label { get; }
public bool IsTwoWay { get; } public bool IsTwoWay { get; }
public bool Orientation { get; } public bool Orientation { get; }
public Brush? Highlight { get; set; } public Brush? Highlight { get; set; }
+4 -3
View File
@@ -8,6 +8,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
public INode Start { get; set; } public INode Start { get; set; }
public INode End => Sink; public INode End => Sink;
public ISink Sink { get; set; } public ISink Sink { get; set; }
public string? Label => null;
public bool IsTwoWay => false; public bool IsTwoWay => false;
public bool Orientation { get; set; } public bool Orientation { get; set; }
public Brush? Highlight { get; set; } public Brush? Highlight { get; set; }
@@ -30,9 +31,9 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
var x1 = Start.CenterX; var x1 = Start.CenterX;
var y1 = Start.CenterY; var y1 = Start.CenterY;
var x2 = Start.CenterX; var x2 = Start.CenterX;
var y2 = Sink.TopY - 10; var y2 = Orientation ? Sink.CenterY - 10 : Sink.TopY - 10;
var x3 = Start.CenterX + (Orientation ? -20 : 20); var x3 = Orientation ? Sink.CenterX : Start.CenterX + (Start.CenterX == End.CenterX ? 0 : Start.CenterX > End.CenterX ? -20 : 20);
var y3 = Sink.TopY + 20; var y3 = Orientation ? Sink.CenterY - 10 : Sink.TopY + 20;
_outer1 = new Line() { _outer1 = new Line() {
X1 = x1, X1 = x1,
Y1 = y1, Y1 = y1,
@@ -10,6 +10,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
public INode Start => Source; public INode Start => Source;
public INode End { get; set; } public INode End { get; set; }
public ISource Source { get; set; } public ISource Source { get; set; }
public string? Label => null;
public bool IsTwoWay => false; public bool IsTwoWay => false;
public bool Orientation { get; set; } public bool Orientation { get; set; }
public Brush? Highlight { get; set; } public Brush? Highlight { get; set; }
+21 -1
View File
@@ -1,3 +1,4 @@
using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Shapes; using System.Windows.Shapes;
@@ -7,6 +8,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
public INode Start { get; set; } public INode Start { get; set; }
public INode End { get; set; } public INode End { get; set; }
public string? Label { get; set; }
public bool IsTwoWay { get; set; } public bool IsTwoWay { get; set; }
public bool Orientation { get; set; } public bool Orientation { get; set; }
public Brush? Highlight { get; set; } public Brush? Highlight { get; set; }
@@ -15,11 +17,15 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
private Polyline? _outer; private Polyline? _outer;
private Polyline? _inner; private Polyline? _inner;
private TextBlock? _text;
private FlowArrows? _flowArrows; private FlowArrows? _flowArrows;
private double? _textOffsetX;
public Pipe(INode start, INode end, bool orientation = false, bool twoWay = false) { public Pipe(INode start, INode end, string? label = null, bool orientation = false, bool twoWay = false, double? textOffsetX = null) {
Start = start; Start = start;
End = end; End = end;
Label = label;
_textOffsetX = textOffsetX;
Orientation = orientation; Orientation = orientation;
IsTwoWay = twoWay; IsTwoWay = twoWay;
} }
@@ -49,6 +55,20 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
}; };
canvas.Children.Add(_outer); canvas.Children.Add(_outer);
canvas.Children.Add(_inner); canvas.Children.Add(_inner);
if (Label != null) {
_text = new() {
Text = Label,
FontSize = 12,
Width = 50,
TextAlignment = TextAlignment.Center,
VerticalAlignment = VerticalAlignment.Bottom,
HorizontalAlignment = HorizontalAlignment.Center,
SnapsToDevicePixels = true,
};
_text.SetValue(Canvas.LeftProperty, _textOffsetX != null ? (Orientation ? x2 : x1) + _textOffsetX : (Orientation ? (x2 + x3) / 2 : (x1 + x2) / 2) - 25);
_text.SetValue(Canvas.TopProperty, y2 - 22);
canvas.Children.Add(_text);
}
_flowArrows = FlowArrows.Create(canvas, _inner); _flowArrows = FlowArrows.Create(canvas, _inner);
} }
+21 -11
View File
@@ -16,11 +16,12 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
public ISet<IEdge> Outputs { get; init; } public ISet<IEdge> Outputs { get; init; }
public Brush? Highlight { get; set; } public Brush? Highlight { get; set; }
public bool IsActive => CallbackActive(); public bool IsActive => State == MotorState.Forward || State == MotorState.Backward;
public MotorState State => CallbackState();
public bool HasClearance => CallbackClearance(); public bool HasClearance => CallbackClearance();
public bool IsSelected => CallbackSelected(this); public bool IsSelected => CallbackSelected(this);
protected Func<bool> CallbackActive; protected Func<MotorState> CallbackState;
protected Func<bool> CallbackClearance; protected Func<bool> CallbackClearance;
protected Func<Pump, bool> CallbackSelected; protected Func<Pump, bool> CallbackSelected;
@@ -28,11 +29,11 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
private Shape? _triangle; private Shape? _triangle;
private TextBlock? _text; private TextBlock? _text;
public Pump(string label, double x, double y, Func<bool> cbActive, Func<bool> cbClearance, Func<Pump, bool> cbSelected) { public Pump(string label, double x, double y, Func<MotorState> cbState, Func<bool> cbClearance, Func<Pump, bool> cbSelected) {
CenterX = x; CenterX = x;
CenterY = y; CenterY = y;
Label = label; Label = label;
CallbackActive = cbActive; CallbackState = cbState;
CallbackClearance = cbClearance; CallbackClearance = cbClearance;
CallbackSelected = cbSelected; CallbackSelected = cbSelected;
Inputs = new HashSet<IEdge>(); Inputs = new HashSet<IEdge>();
@@ -75,13 +76,22 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
} }
public void Update(bool isHovering, bool isPresed) { public void Update(bool isHovering, bool isPresed) {
_circle?.Fill = Highlight ?? ( if (App.Plant?.ManualControlMode ?? false) {
IsActive && isHovering ? PamhagenBrushes.Red : _circle?.Fill = Highlight ?? (
IsActive ? PamhagenBrushes.Green : IsActive && isHovering ? PamhagenBrushes.Red :
HasClearance && IsSelected && isHovering ? PamhagenBrushes.Green : IsActive ? PamhagenBrushes.Green :
HasClearance && IsSelected ? PamhagenBrushes.Yellow : HasClearance && isHovering ? PamhagenBrushes.Green :
IsSelected ? PamhagenBrushes.Red : HasClearance ? PamhagenBrushes.Yellow :
isHovering ? Brushes.LightGray : Brushes.WhiteSmoke); PamhagenBrushes.Red);
} else {
_circle?.Fill = Highlight ?? (
IsActive && isHovering ? PamhagenBrushes.Red :
IsActive ? PamhagenBrushes.Green :
HasClearance && IsSelected && isHovering ? PamhagenBrushes.Green :
HasClearance && IsSelected ? PamhagenBrushes.Yellow :
IsSelected ? PamhagenBrushes.Red :
isHovering ? Brushes.LightGray : Brushes.WhiteSmoke);
}
} }
} }
} }
+22 -10
View File
@@ -16,17 +16,17 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
public bool IsLocked { get; set; } public bool IsLocked { get; set; }
public bool IsOpen => CallbackIsOpen(); public bool IsOpen => CallbackIsOpen();
public bool WantOpen => CallbackWantOpen(); public bool WantOpen => CallbackWantOpen(this);
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 Func<bool> CallbackWantOpen; protected Func<Valve, bool> CallbackWantOpen;
private Shape? _circle; private Shape? _circle;
private TextBlock? _text; private TextBlock? _text;
public Valve(int nr, double x, double y, Func<bool> cbOpen, Func<bool> cbWant) { public Valve(int nr, double x, double y, Func<bool> cbOpen, Func<Valve, bool> cbWant) {
CenterX = x; CenterX = x;
CenterY = y; CenterY = y;
Nr = nr; Nr = nr;
@@ -53,8 +53,9 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
Height = DIAMETER, Height = DIAMETER,
TextAlignment = TextAlignment.Center, TextAlignment = TextAlignment.Center,
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
SnapsToDevicePixels = true,
}; };
_text.SetValue(Canvas.LeftProperty, CenterX - DIAMETER / 2); _text.SetValue(Canvas.LeftProperty, CenterX - DIAMETER / 2 + 0.5);
_text.SetValue(Canvas.TopProperty, CenterY - DIAMETER / 2 + 5.5); _text.SetValue(Canvas.TopProperty, CenterY - DIAMETER / 2 + 5.5);
canvas.Children.Add(_circle); canvas.Children.Add(_circle);
canvas.Children.Add(_text); canvas.Children.Add(_text);
@@ -65,12 +66,23 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
} }
public void Update(bool isHovering, bool isPressed) { public void Update(bool isHovering, bool isPressed) {
_circle?.Fill = if (App.Plant?.ManualControlMode ?? false) {
IsLocked && !WantOpen && !IsOpen ? PamhagenBrushes.DimOrange : _circle?.Fill =
IsLocked && !WantOpen && IsOpen ? PamhagenBrushes.Red : !WantOpen && !IsOpen && !isHovering ? PamhagenBrushes.Red :
IsLocked && WantOpen && !IsOpen ? PamhagenBrushes.Yellow : !WantOpen && !IsOpen ? PamhagenBrushes.Green :
IsLocked && WantOpen && IsOpen ? PamhagenBrushes.Green : !WantOpen && IsOpen ? PamhagenBrushes.DimRed :
Highlight ?? (isHovering ? Brushes.LightGray : Brushes.DarkGray); WantOpen && !IsOpen ? PamhagenBrushes.DimGreen :
WantOpen && IsOpen && !isHovering ? PamhagenBrushes.Green :
WantOpen && IsOpen ? PamhagenBrushes.Red :
Highlight ?? (isHovering ? Brushes.LightGray : Brushes.DarkGray);
} else {
_circle?.Fill =
IsLocked && !WantOpen && !IsOpen ? PamhagenBrushes.DimOrange :
IsLocked && !WantOpen && IsOpen ? PamhagenBrushes.Red :
IsLocked && WantOpen && !IsOpen ? PamhagenBrushes.Yellow :
IsLocked && WantOpen && IsOpen ? PamhagenBrushes.Green :
Highlight ?? (isHovering ? Brushes.LightGray : Brushes.DarkGray);
}
} }
} }
} }
+74
View File
@@ -0,0 +1,74 @@
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Text.Json.Nodes;
namespace PamhagenSysCtrl.Helpers {
public sealed record UpdateInstaller(Version Version, Uri Url, long Size);
public static class UpdateService {
private static readonly HttpClient HttpClient = new() {
Timeout = Timeout.InfiniteTimeSpan,
};
public static async Task<UpdateInstaller> GetLatestInstallerAsync(string feedUrl, CancellationToken cancellationToken = default) {
using var response = await HttpClient.GetAsync(feedUrl, cancellationToken);
response.EnsureSuccessStatusCode();
var json = JsonNode.Parse(await response.Content.ReadAsStringAsync(cancellationToken));
var latest = json!["data"]!.AsArray()[^1]!;
return new(
new Version((string)latest["version"]!),
new Uri((string)latest["url"]!),
(long)latest["size"]!
);
}
public static async Task<string> DownloadInstallerAsync(UpdateInstaller installer, string targetDirectory, IProgress<double>? progress = null, CancellationToken cancellationToken = default) {
Directory.CreateDirectory(targetDirectory);
var fileName = Path.Combine(targetDirectory, $"PamhagenSysCtrl-{installer.Version}.msi");
try {
using var response = await HttpClient.GetAsync(installer.Url, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
response.EnsureSuccessStatusCode();
var contentLength = response.Content.Headers.ContentLength;
await using (var destination = new FileStream(fileName, FileMode.Create)) {
await using var source = await response.Content.ReadAsStreamAsync(cancellationToken);
var buffer = new byte[81920];
long downloaded = 0;
int read;
while ((read = await source.ReadAsync(buffer, cancellationToken)) != 0) {
await destination.WriteAsync(buffer.AsMemory(0, read), cancellationToken);
downloaded += read;
if (contentLength.HasValue) {
progress?.Report((double)downloaded / contentLength.Value);
}
}
}
progress?.Report(1);
return fileName;
} catch {
File.Delete(fileName);
throw;
}
}
public static void StartInstaller(string fileName) {
var startInfo = new ProcessStartInfo {
FileName = "msiexec.exe",
UseShellExecute = true,
Verb = "runas",
};
startInfo.ArgumentList.Add("/i");
startInfo.ArgumentList.Add(Path.GetFullPath(fileName));
if (Process.Start(startInfo) == null) {
throw new InvalidOperationException("Der Installer konnte nicht gestartet werden.");
}
}
}
}
+4 -2
View File
@@ -5,6 +5,8 @@
<TargetFramework>net10.0-windows</TargetFramework> <TargetFramework>net10.0-windows</TargetFramework>
<Version>0.0.1</Version> <Version>0.0.1</Version>
<Product>Anlagensteuerung Pamhagen</Product> <Product>Anlagensteuerung Pamhagen</Product>
<AssemblyTitle>Anlagensteuerung Pamhagen</AssemblyTitle>
<AssemblyName>PamhagenSysCtrl</AssemblyName>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
@@ -14,8 +16,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.10" /> <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.10" />
<PackageReference Include="System.IO.Ports" Version="10.0.10" /> <PackageReference Include="System.IO.Ports" Version="10.0.10" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+27
View File
@@ -0,0 +1,27 @@
<Window x:Class="PamhagenSysCtrl.Windows.AboutWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Über - Anlagensteuerung Pamhagen"
Height="300" Width="560" ResizeMode="NoResize"
WindowStartupLocation="CenterOwner">
<Grid>
<TextBlock Margin="20,10" FontSize="12">
<Bold>Produkt:</Bold> Anlagensteuerung Pamhagen<LineBreak/>
<Bold>Beschreibung:</Bold> Steuerung und Visualisierung der Anlage in Pamhagen<LineBreak/>
<Bold>Typ:</Bold> Industrielle Anlagensteuerung<LineBreak/>
<Bold>Version:</Bold> <Run x:Name="VersionText">0.0.0</Run><LineBreak/>
<Bold>Lizenz:</Bold> <Hyperlink NavigateUri="https://www.gnu.org/licenses/gpl-3.0.html" RequestNavigate="Hyperlink_RequestNavigate">GNU General Public License 3.0 (GPLv3)</Hyperlink><LineBreak/>
<Bold>Entwickler:</Bold> Lorenz Stechauner, Thomas Hilscher<LineBreak/>
<Bold>Kontakt:</Bold> <Hyperlink NavigateUri="mailto:lorenz.stechauner@necronda.net" RequestNavigate="Hyperlink_RequestNavigate">lorenz.stechauner@necronda.net</Hyperlink>, <Hyperlink NavigateUri="mailto:thomas.hilscher@gmail.com" RequestNavigate="Hyperlink_RequestNavigate">thomas.hilscher@gmail.com</Hyperlink><LineBreak/>
<Bold>Quellcode:</Bold> <Hyperlink NavigateUri="C:\Program Files\Anlagensteuerung Pamhagen\src" RequestNavigate="Hyperlink_RequestNavigate_Explorer">C:\Program Files\Anlagensteuerung Pamhagen\src</Hyperlink><LineBreak/>
<Hyperlink NavigateUri="https://git.necronda.net/winzer/pamhagen-sysctrl" RequestNavigate="Hyperlink_RequestNavigate">git.necronda.net/winzer/pamhagen-sysctrl</Hyperlink><LineBreak/>
<Bold>Entwicklungszeitraum:</Bold> 2026<LineBreak/>
<LineBreak/>
<Bold>Verwendete Technologien:</Bold><LineBreak/>
Programmiersprache: C#<LineBreak/>
Framework: Windows Presentation Framework (WPF)<LineBreak/>
Kommunikation: Serielle Schnittstelle<LineBreak/>
Paketierung: <Hyperlink NavigateUri="https://www.firegiant.com/wixtoolset/" RequestNavigate="Hyperlink_RequestNavigate">WiX Toolset</Hyperlink>
</TextBlock>
</Grid>
</Window>
@@ -0,0 +1,25 @@
using System.Diagnostics;
using System.Windows;
using System.Windows.Navigation;
namespace PamhagenSysCtrl.Windows {
public partial class AboutWindow : Window {
public AboutWindow() {
InitializeComponent();
VersionText.Text = App.Version.ToString();
}
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs evt) {
try {
Process.Start(new ProcessStartInfo(evt.Uri.AbsoluteUri) { UseShellExecute = true });
} catch { }
}
private void Hyperlink_RequestNavigate_Explorer(object sender, RequestNavigateEventArgs evt) {
try {
Process.Start("explorer.exe", evt.Uri.AbsoluteUri);
} catch { }
}
}
}
@@ -5,7 +5,8 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PamhagenSysCtrl" xmlns:local="clr-namespace:PamhagenSysCtrl"
mc:Ignorable="d" mc:Ignorable="d"
Title="Manuelle Steuerung - Anlagensteuerung Pamhagen" Height="550" Width="860"> Closed="OnClosed"
Title="Handbetrieb - Anlagensteuerung Pamhagen" Height="550" Width="860">
<Grid> <Grid>
<Grid x:Name="ValvePanel" Margin="5,5,5,5"> <Grid x:Name="ValvePanel" Margin="5,5,5,5">
</Grid> </Grid>
@@ -39,14 +40,14 @@
FontSize="14" BorderThickness="2" FontWeight="Bold" FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="135,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="135,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="Auger1Button_Click"/> Click="Auger1Button_Click"/>
<Button x:Name="BeltButton" Content="Förderband" Padding="2" Height="30" Width="150"
FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="135,45,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="BeltButton_Click"/>
<Button x:Name="Auger2Button" Content="Förderschnecke 2" Padding="2" Height="30" Width="150" <Button x:Name="Auger2Button" Content="Förderschnecke 2" Padding="2" Height="30" Width="150"
FontSize="14" BorderThickness="2" FontWeight="Bold" FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="135,80,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="135,45,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="Auger2Button_Click"/> Click="Auger2Button_Click"/>
<Button x:Name="Auger3Button" Content="Förderschnecke 3" Padding="2" Height="30" Width="150"
FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="135,80,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="Auger3Button_Click"/>
<Button x:Name="P6Button" Content="Pumpe 6" Padding="2" Height="30" Width="150" <Button x:Name="P6Button" Content="Pumpe 6" Padding="2" Height="30" Width="150"
FontSize="14" BorderThickness="2" FontWeight="Bold" FontSize="14" BorderThickness="2" FontWeight="Bold"
@@ -1,4 +1,5 @@
using PamhagenSysCtrl.Helpers; using PamhagenSysCtrl.Helpers;
using PamhagenSysCtrl.Windows;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
@@ -47,6 +48,14 @@ namespace PamhagenSysCtrl {
App.Plant?.Update += OnUpdate; App.Plant?.Update += OnUpdate;
} }
private void OnClosed(object sender, EventArgs evt) {
foreach (var win in Application.Current.Windows) {
if (win is PlantSchemeWindow w) {
w.Menu_Settings_ManualControl.IsChecked = false;
}
}
}
public void OnUpdate(object? sender, PlantEventArgs evt) { public void OnUpdate(object? sender, PlantEventArgs evt) {
if (sender is not PamhagenPlant plant) return; if (sender is not PamhagenPlant plant) return;
@@ -76,10 +85,6 @@ namespace PamhagenSysCtrl {
StirrerMW2Button.Foreground = plant.IsStirrerMW2Active ? Brushes.DarkGreen : Brushes.DarkRed; StirrerMW2Button.Foreground = plant.IsStirrerMW2Active ? Brushes.DarkGreen : Brushes.DarkRed;
StirrerMW2Button.Background = plant.IsStirrerMW2Active ? Brushes.MintCream : Brushes.MistyRose; StirrerMW2Button.Background = plant.IsStirrerMW2Active ? Brushes.MintCream : Brushes.MistyRose;
BeltButton.BorderBrush = plant.IsBeltActive ? Brushes.DarkGreen : Brushes.DarkRed;
BeltButton.Foreground = plant.IsBeltActive ? Brushes.DarkGreen : Brushes.DarkRed;
BeltButton.Background = plant.IsBeltActive ? Brushes.MintCream : Brushes.MistyRose;
Auger1Button.BorderBrush = plant.IsAuger1Active ? Brushes.DarkGreen : Brushes.DarkRed; Auger1Button.BorderBrush = plant.IsAuger1Active ? Brushes.DarkGreen : Brushes.DarkRed;
Auger1Button.Foreground = plant.IsAuger1Active ? Brushes.DarkGreen : Brushes.DarkRed; Auger1Button.Foreground = plant.IsAuger1Active ? Brushes.DarkGreen : Brushes.DarkRed;
Auger1Button.Background = plant.IsAuger1Active ? Brushes.MintCream : Brushes.MistyRose; Auger1Button.Background = plant.IsAuger1Active ? Brushes.MintCream : Brushes.MistyRose;
@@ -88,6 +93,10 @@ namespace PamhagenSysCtrl {
Auger2Button.Foreground = plant.IsAuger2Active ? Brushes.DarkGreen : Brushes.DarkRed; Auger2Button.Foreground = plant.IsAuger2Active ? Brushes.DarkGreen : Brushes.DarkRed;
Auger2Button.Background = plant.IsAuger2Active ? Brushes.MintCream : Brushes.MistyRose; Auger2Button.Background = plant.IsAuger2Active ? Brushes.MintCream : Brushes.MistyRose;
Auger3Button.BorderBrush = plant.IsAuger3Active ? Brushes.DarkGreen : Brushes.DarkRed;
Auger3Button.Foreground = plant.IsAuger3Active ? Brushes.DarkGreen : Brushes.DarkRed;
Auger3Button.Background = plant.IsAuger3Active ? Brushes.MintCream : Brushes.MistyRose;
MP1BackwardButton.BorderBrush = plant.MP1 == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray; MP1BackwardButton.BorderBrush = plant.MP1 == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray;
MP1BackwardButton.Foreground = plant.MP1 == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray; MP1BackwardButton.Foreground = plant.MP1 == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray;
MP1BackwardButton.Background = plant.MP1 == MotorState.Backward ? Brushes.MintCream : Brushes.WhiteSmoke; MP1BackwardButton.Background = plant.MP1 == MotorState.Backward ? Brushes.MintCream : Brushes.WhiteSmoke;
@@ -134,8 +143,8 @@ namespace PamhagenSysCtrl {
Füllstände: Füllstände:
Maischewanne 1: {plant.FillLevelMW1} (ausgewählt: {plant.WantMW1Selected}, Status: {plant.IsMWSelected}) Maischewanne 1: {plant.FillLevelMW1} (ausgewählt: {plant.WantMW1Selected}, Status: {plant.IsMWSelected})
Maischewanne 2: {plant.FillLevelMW2} (ausgewählt: {plant.WantMW2Selected}, Status: {plant.IsMW2Selected}) Maischewanne 2: {plant.FillLevelMW2} (ausgewählt: {plant.WantMW2Selected}, Status: {plant.IsMW2Selected})
Presse 1: {plant.FillLevelPress1} Presse 1: {plant.Press1HasClearance}
Presse 2: {plant.FillLevelPress2} Presse 2: {plant.Press2HasClearance}
Tank 1: {plant.FillLevelT1} Tank 1: {plant.FillLevelT1}
Tank 2: {plant.FillLevelT2} Tank 2: {plant.FillLevelT2}
Tank 3: {plant.FillLevelT3} Tank 3: {plant.FillLevelT3}
@@ -200,19 +209,6 @@ namespace PamhagenSysCtrl {
} }
} }
private void BeltButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
if (App.Plant.IsBeltActive) {
App.Plant.StopBelt();
} else {
App.Plant.StartBelt();
}
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void Auger1Button_Click(object? sender, RoutedEventArgs? evt) { private void Auger1Button_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return; if (App.Plant == null) return;
try { try {
@@ -239,6 +235,19 @@ namespace PamhagenSysCtrl {
} }
} }
private void Auger3Button_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
if (App.Plant.IsAuger3Active) {
App.Plant.StopAuger3();
} else {
App.Plant.StartAuger3();
}
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void MP1BackwardButton_Click(object? sender, RoutedEventArgs? evt) { private void MP1BackwardButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return; if (App.Plant == null) return;
try { try {
+20 -2
View File
@@ -4,6 +4,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PamhagenSysCtrl.Windows" xmlns:local="clr-namespace:PamhagenSysCtrl.Windows"
Closed="OnClosed"
Title="Anlagensteuerung Pamhagen" Height="950" Width="1600" MinWidth="1500" MinHeight="950"> Title="Anlagensteuerung Pamhagen" Height="950" Width="1600" MinWidth="1500" MinHeight="950">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
@@ -14,11 +15,23 @@
<Menu Grid.Row="0" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White"> <Menu Grid.Row="0" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Einstellungen"> <MenuItem Header="Einstellungen">
<MenuItem x:Name="Menu_Settings_ManualControl" Header="Manuelle Steuerung" Click="Menu_Settings_ManualControl_Click"> <MenuItem x:Name="Menu_Settings_ManualControl" Header="Handbetrieb" IsCheckable="True"
Checked="Menu_Settings_ManualControl_Changed"
Unchecked="Menu_Settings_ManualControl_Changed">
<MenuItem.Icon> <MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xF271;"/> <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xF271;"/>
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem x:Name="Menu_Settings_OverridePathClearances" Header="Pfad-Freigaben überbrücken" IsCheckable="True"
Checked="Menu_Settings_OverridePathClearances_Changed"
Unchecked="Menu_Settings_OverridePathClearances_Changed">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE785;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_Settings_OverrideDryRunClearances" Header="Trockenlauf-Freigaben überbrücken" IsCheckable="True"
Checked="Menu_Settings_OverrideDryRunClearances_Changed"
Unchecked="Menu_Settings_OverrideDryRunClearances_Changed"/>
<Separator/> <Separator/>
<MenuItem x:Name="Menu_Settings_Config" Header="Konfigurationsdatei öffnen..." Click="Menu_Settings_Config_Click"> <MenuItem x:Name="Menu_Settings_Config" Header="Konfigurationsdatei öffnen..." Click="Menu_Settings_Config_Click">
<MenuItem.Icon> <MenuItem.Icon>
@@ -32,11 +45,16 @@
</MenuItem> </MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Hilfe"> <MenuItem Header="Hilfe">
<MenuItem Header="Über"> <MenuItem Header="Über" Click="Menu_Help_About_Click">
<MenuItem.Icon> <MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE946;"/> <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE946;"/>
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Nach Updates suchen..." Click="Menu_Help_CheckForUpdates_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE895;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
</Menu> </Menu>
+113 -51
View File
@@ -21,9 +21,41 @@ namespace PamhagenSysCtrl.Windows {
Graph.Draw(SchemeCanvas); Graph.Draw(SchemeCanvas);
} }
private void Menu_Settings_ManualControl_Click(object sender, RoutedEventArgs evt) { private void OnClosed(object sender, EventArgs evt) {
var w = new ManualControlWindow(); Application.Current.Shutdown();
w.Show(); }
private void Menu_Settings_ManualControl_Changed(object sender, RoutedEventArgs evt) {
App.Plant?.ManualControlMode = Menu_Settings_ManualControl.IsChecked;
if (Menu_Settings_ManualControl.IsChecked) {
foreach (var win in Application.Current.Windows) {
if (win is ManualControlWindow w) {
w.Focus();
return;
}
}
var ctrl = new ManualControlWindow();
ctrl.Show();
} else {
foreach (var win in Application.Current.Windows) {
if (win is ManualControlWindow w) {
w.Close();
}
}
Menu_Settings_OverridePathClearances.IsChecked = false;
Menu_Settings_OverrideDryRunClearances.IsChecked = false;
}
OnUpdate(null, null);
}
private void Menu_Settings_OverridePathClearances_Changed(object sender, RoutedEventArgs evt) {
App.Plant?.OverridePathClearances = Menu_Settings_OverridePathClearances.IsChecked;
OnUpdate(null, null);
}
private void Menu_Settings_OverrideDryRunClearances_Changed(object sender, RoutedEventArgs evt) {
App.Plant?.OverrideDryRunClearances = Menu_Settings_OverrideDryRunClearances.IsChecked;
OnUpdate(null, null);
} }
private void Menu_Settings_Directory_Click(object sender, RoutedEventArgs evt) { private void Menu_Settings_Directory_Click(object sender, RoutedEventArgs evt) {
@@ -43,6 +75,19 @@ namespace PamhagenSysCtrl.Windows {
} catch { } } catch { }
} }
private async void Menu_Help_CheckForUpdates_Click(object sender, RoutedEventArgs evt) {
if (Application.Current is App app) {
await app.CheckForUpdates(true);
}
}
private void Menu_Help_About_Click(object sender, RoutedEventArgs evt) {
var window = new AboutWindow {
Owner = this,
};
window.Show();
}
private void SchemeCanvas_MouseMove(object sender, MouseEventArgs evt) { private void SchemeCanvas_MouseMove(object sender, MouseEventArgs evt) {
var p = evt.GetPosition(SchemeCanvas); var p = evt.GetPosition(SchemeCanvas);
UpdateScheme(evt.GetPosition(SchemeCanvas), evt.LeftButton == MouseButtonState.Pressed); UpdateScheme(evt.GetPosition(SchemeCanvas), evt.LeftButton == MouseButtonState.Pressed);
@@ -61,27 +106,37 @@ namespace PamhagenSysCtrl.Windows {
_lastSource = null; _lastSource = null;
} }
foreach (var c in clicked) { foreach (var c in clicked) {
if (_lastSource == null && Graph.SelectedPaths.Any(p => p.Start == c)) { if (App.Plant?.ManualControlMode ?? false) {
var path = Graph.SelectedPaths.First(p => p.Start == c); if (c is Valve v) {
if (!path.Hops.Any(h => (h.Node as Pump)?.IsActive ?? false)) { if (v.WantOpen) {
Graph.RemovePath(path); App.Plant.CloseV(v.Nr);
} else {
App.Plant.OpenV(v.Nr);
}
} }
} else if (_lastSource != null && c is ISink sink) { } else {
var res = Graph.GetPath([_lastSource, .._path, sink], (n) => n is not ISink && (n is not Valve v || !v.IsLocked)); if (_lastSource == null && Graph.SelectedPaths.Any(p => p.Start == c)) {
if (res is Path path) { var path = Graph.SelectedPaths.First(p => p.Start == c);
Graph.AddPath(path); if (!path.Hops.Any(h => (h.Node as Pump)?.IsActive ?? false)) {
Graph.RemovePath(path);
}
} else if (_lastSource != null && c is ISink sink) {
var res = Graph.GetPath([_lastSource, .. _path, sink], (n) => n is not ISink && (n is not Valve v || !v.IsLocked));
if (res is Path path) {
Graph.AddPath(path);
_lastSource = null;
}
} else if (_lastSource != null) {
var path = Graph.GetPath([_lastSource, .. _path, c], (n) => n is not ISink && (n is not Valve v || !v.IsLocked));
if (path != null) {
_path.Add(c);
}
} else if (c is ISource source) {
_lastSource = source;
_path = [];
} else {
_lastSource = null; _lastSource = null;
} }
} else if (_lastSource != null) {
var path = Graph.GetPath([_lastSource, .. _path, c], (n) => n is not ISink && (n is not Valve v || !v.IsLocked));
if (path != null) {
_path.Add(c);
}
} else if (c is ISource source) {
_lastSource = source;
_path = [];
} else {
_lastSource = null;
} }
try { try {
if (c is EntryTrough trough) { if (c is EntryTrough trough) {
@@ -109,18 +164,18 @@ namespace PamhagenSysCtrl.Windows {
} else { } else {
App.Plant?.StartAuger1(); App.Plant?.StartAuger1();
} }
} else if (auger == Graph.Auger2) { } else if (auger == Graph.Auger3) {
if (auger.IsActive) { if (auger.IsActive) {
App.Plant?.StopAuger2(); App.Plant?.StopAuger3();
} else { } else {
App.Plant?.StartAuger2(); App.Plant?.StartAuger3();
} }
} }
} else if (c is ConveyorBelt belt) { } else if (c is ConveyorBelt belt) {
if (belt.IsActive) { if (belt.IsActive) {
App.Plant?.StopBelt(); App.Plant?.StopAuger2();
} else { } else {
App.Plant?.StartBelt(); App.Plant?.StartAuger2();
} }
} else if (c is Pump pump) { } else if (c is Pump pump) {
if (pump == Graph.MP1) { if (pump == Graph.MP1) {
@@ -184,42 +239,49 @@ namespace PamhagenSysCtrl.Windows {
p.Highlight = null; p.Highlight = null;
} }
} }
foreach (var p in Graph.SelectedPaths) {
Graph.ColorPipesAdjacientToPath(p, PamhagenBrushes.Green, p.Hops.Any(h => (h.Node as Pump)?.IsActive ?? false), PamhagenBrushes.DimOrange);
}
_hover = Graph.GetHover(pos.X, pos.Y); _hover = Graph.GetHover(pos.X, pos.Y);
if (_lastSource != null && (_hover.Count > 0 || _path.Count > 0)) { if (App.Plant?.ManualControlMode ?? false) {
List<INode> points = [_lastSource, .. _path]; foreach (var src in Graph.Nodes.Where(n => n is Pump)) {
if (_hover.Count > 0 && !points.Contains(_hover.First())) { Graph.ColorPipesByValves(src, PamhagenBrushes.Green, (src as Pump)?.State ?? default, PamhagenBrushes.DimOrange);
points.Add(_hover.First());
} }
if (points.Count >= 2) { } else {
var res = Graph.GetPath(points, (n) => n is not ISink && (n is not Valve v || !v.IsLocked)); foreach (var p in Graph.SelectedPaths) {
if (res is Path path) { Graph.ColorPipesAdjacientToPath(p, PamhagenBrushes.Green, p.Hops.Select(h => (h.Node as Pump)?.State ?? default).FirstOrDefault(n => n != MotorState.Halt), PamhagenBrushes.DimOrange);
Graph.ColorPipesAdjacientToPath(path, PamhagenBrushes.Green, false, PamhagenBrushes.DimOrange); }
if (_lastSource != null && (_hover.Count > 0 || _path.Count > 0)) {
List<INode> points = [_lastSource, .. _path];
if (_hover.Count > 0 && !points.Contains(_hover.First())) {
points.Add(_hover.First());
}
if (points.Count >= 2) {
var res = Graph.GetPath(points, (n) => n is not ISink && (n is not Valve v || !v.IsLocked));
if (res is Path path) {
Graph.ColorPipesAdjacientToPath(path, PamhagenBrushes.Green, default, PamhagenBrushes.DimOrange);
}
}
} else if (_lastSource == null && _hover.Count > 0 && Graph.SelectedPaths.Any(p => p.Start == _hover.First())) {
var path = Graph.SelectedPaths.First(p => p.Start == _hover.First());
var pumpActive = path.Hops.Any(h => (h.Node as Pump)?.IsActive ?? false);
if (pumpActive) {
foreach (var pump in path.Hops.Select(h => h.Node as Pump).Where(p => p != null)) {
pump?.Highlight = PamhagenBrushes.Red;
}
} else {
Graph.ColorPipesAdjacientToPath(path, PamhagenBrushes.Red, default, null);
} }
} }
} else if (_lastSource == null && _hover.Count > 0 && Graph.SelectedPaths.Any(p => p.Start == _hover.First())) { if (_lastSource != null) {
var path = Graph.SelectedPaths.First(p => p.Start == _hover.First()); foreach (var edge in _lastSource.Outputs) {
var pumpActive = path.Hops.Any(h => (h.Node as Pump)?.IsActive ?? false); edge.Highlight = PamhagenBrushes.Green;
if (pumpActive) {
foreach (var pump in path.Hops.Select(h => h.Node as Pump).Where(p => p != null)) {
pump?.Highlight = PamhagenBrushes.Red;
} }
} else {
Graph.ColorPipesAdjacientToPath(path, PamhagenBrushes.Red, false, null);
}
}
if (_lastSource != null) {
foreach (var edge in _lastSource.Outputs) {
edge.Highlight = PamhagenBrushes.Green;
} }
} }
Graph.Update(_hover, down); Graph.Update(_hover, down);
return _hover; return _hover;
} }
public void OnUpdate(object? sender, PlantEventArgs evt) { public void OnUpdate(object? sender, PlantEventArgs? evt) {
if (sender is not PamhagenPlant plant) return; if (sender is not PamhagenPlant plant) return;
UpdateScheme(Mouse.GetPosition(SchemeCanvas), Mouse.LeftButton == MouseButtonState.Pressed); UpdateScheme(Mouse.GetPosition(SchemeCanvas), Mouse.LeftButton == MouseButtonState.Pressed);
} }