Author SHA1 Message Date
lorenz.stechauner 9c3cc8d4be Enhance Installer
Test / Run tests (push) Failing after 13s
2026-08-05 16:31:32 +02:00
thomas.hilscherandlorenz.stechauner bf8533c857 Add msi installer 2026-08-05 15:39:00 +02:00
lorenz.stechauner 47455d9291 Graph: Forbid paths trough already locked valves 2026-08-05 14:31:54 +02:00
lorenz.stechauner db5702c0a7 Add config file and refactor windows 2026-08-05 14:16:01 +02:00
lorenz.stechauner d9c1b79563 Add switcher 2026-08-04 23:56:32 +02:00
lorenz.stechauner ec07d91631 Add App.manifest 2026-08-04 19:14:58 +02:00
lorenz.stechauner 59707e427d Bugfixes and remove CommitChanges() 2026-08-04 16:39:09 +02:00
lorenz.stechauner 7b95cf4221 Add clearance logic to PamhagenPlant 2026-08-04 15:30:08 +02:00
34 changed files with 1468 additions and 475 deletions
+56
View File
@@ -0,0 +1,56 @@
name: Deploy
on:
push:
tags: ["v[0-9]+.[0-9]+.[0-9]+"]
jobs:
deploy:
name: Build and Deploy
runs-on: windows-latest
permissions:
contents: write
steps:
- name: Set APP_VERSION variable from tag
shell: powershell
run: |
$APP_VERSION = $env:GITHUB_REF -replace '^refs/tags/v', ''
Add-Content -Path $env:GITHUB_ENV -Value "APP_VERSION=$APP_VERSION"
- name: Checkout repository
uses: actions/checkout@v4
- name: Check version in project
shell: powershell
run: |
Select-String PamhagenSysCtrl/PamhagenSysCtrl.csproj -Pattern "<Version>"
$res = Select-String PamhagenSysCtrl/PamhagenSysCtrl.csproj -Pattern "<Version>${{ env.APP_VERSION }}</Version>"
if ($res -eq $null) {
exit 1
}
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1.1
- name: Setup NuGet
uses: nuget/setup-nuget@v1
- name: Restore NuGet packages
shell: powershell
run: $(& nuget restore PamhagenSysCtrl.sln; $a=$lastexitcode) | findstr x*; exit $a
- name: Build Installer
shell: powershell
run: $(& msbuild -verbosity:quiet Installer/Installer.wixproj -property:Configuration=Release -property:Platform=x64; $a=$lastexitcode) | findstr x*; exit $a
- name: Rename artifact
shell: powershell
run: Move-Item Installer/bin/x64/Release/PamhagenSysCtrl.msi Installer/bin/x64/Release/PamhagenSysCtrl-${{ env.APP_VERSION }}.msi
- name: Create release
uses: akkuman/gitea-release-action@v1
with:
name: Anlagensteuerung Pamhagen ${{ env.APP_VERSION }}
body: "**[Changelog](src/branch/main/CHANGELOG.md#v${{ env.APP_VERSION }})**"
files: |-
Installer/bin/x64/Release/PamhagenSysCtrl-${{ env.APP_VERSION }}.msi
- name: Upload to website
shell: powershell
run: |
$content = [System.IO.File]::ReadAllBytes("Installer/bin/x64/Release/PamhagenSysCtrl-${{ env.APP_VERSION }}.msi")
Invoke-WebRequest `
-Uri "https://elwig.at/files/PamhagenSysCtrl-${{ env.APP_VERSION }}.msi" `
-Method PUT `
-Body $content `
-Headers @{ Authorization = "${{ secrets.API_AUTHORIZATION }}" } `
-ContentType "application/octet-stream"
+25
View File
@@ -0,0 +1,25 @@
name: Test
on:
push:
branches: ["**"]
paths: ["PamhagenSysCtrl/**", ".gitea/workflows/test.yaml"]
jobs:
test:
name: Run tests
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check for Byte order marks
shell: powershell
run: |
$pattern = [char]::ConvertFromUtf32(0xFEFF)
$files = git grep -IEl "^$pattern"
if ( $lastexitcode -ne 1 ) {
echo "Files with BOM found:"
echo $files
exit 1
} else {
echo "No files with BOM found"
exit 0
}
+1
View File
@@ -4,3 +4,4 @@ bin/
.vs .vs
.idea .idea
*.exe *.exe
*.zip
+14
View File
@@ -0,0 +1,14 @@
Pamhagen
========
Source code
C:\Program Files\Anlagensteuerung Pamhagen\src\pamhagen-sysctrl.zip
https://git.necronda.net/winzer/pamhagen-sysctrl
Installation folder
C:\Program Files\Anlagensteuerung Pamhagen\
Data and configuration folder
C:\ProgramData\Anlagensteuerung Pamhagen\
- config.ini : main configuration file
+3
View File
@@ -0,0 +1,3 @@
[plc]
port = COM1
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<!-- C:\Program Files (x86)\Anlagensteuerung Pamhagen or C:\Program Files\Anlagensteuerung Pamhagen -->
<StandardDirectory Id="ProgramFiles64Folder">
<Directory Id="InstallFolder" Name="!(bind.Property.ProductName)">
<Directory Id="SourceFolder" Name="src" />
</Directory>
</StandardDirectory>
<!-- C:\ProgramData\Anlagensteuerung Pamhagen -->
<StandardDirectory Id="CommonAppDataFolder">
<Directory Id="ConfigFolder" Name="!(bind.Property.ProductName)" />
</StandardDirectory>
<!-- C:\ProgramData\Microsoft\Windows\Start Menu\Programs -->
<StandardDirectory Id="ProgramMenuFolder">
<Directory Id="StartMenuProgramsFolder" Name="!(bind.Property.ProductName)" />
</StandardDirectory>
<!-- C:\Users\{USERNAME}\Desktop -->
<StandardDirectory Id="DesktopFolder" />
</Fragment>
</Wix>
+43
View File
@@ -0,0 +1,43 @@
<Project Sdk="WixToolset.Sdk/7.0.0">
<PropertyGroup>
<InstallerPlatform>x64</InstallerPlatform>
<OutputName>PamhagenSysCtrl</OutputName>
<OutputPath>bin\$(Configuration)\</OutputPath>
<Cultures>de-AT</Cultures>
<AcceptEula>wix7</AcceptEula>
<SuppressValidation>false</SuppressValidation>
<PublishDirectory>$(MSBuildProjectDirectory)\..\PamhagenSysCtrl\bin\Publish</PublishDirectory>
</PropertyGroup>
<UsingTask TaskName="GetFileVersion" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<AssemblyPath ParameterType="System.String" Required="true" />
<Version ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Using Namespace="System.Diagnostics" />
<Code Type="Fragment" Language="cs"><![CDATA[
Version = FileVersionInfo.GetVersionInfo(AssemblyPath).ProductVersion.Split('+')[0];
]]></Code>
</Task>
</UsingTask>
<Target Name="PublishApplication" BeforeTargets="BeforeBuild">
<RemoveDir Directories="$(PublishDirectory)" Condition="Exists('$(PublishDirectory)')" />
<Exec Command="dotnet publish &quot;$(MSBuildProjectDirectory)\..\PamhagenSysCtrl\PamhagenSysCtrl.csproj&quot; --configuration &quot;$(Configuration)&quot; --runtime win-x64 --self-contained true --output &quot;$(PublishDirectory)&quot; --nologo" />
<GetFileVersion AssemblyPath="$(PublishDirectory)\PamhagenSysCtrl.exe">
<Output TaskParameter="Version" PropertyName="ProductVersion" />
</GetFileVersion>
<PropertyGroup>
<DefineConstants>ProductVersion=$(ProductVersion);BuildPath=$(PublishDirectory)</DefineConstants>
</PropertyGroup>
</Target>
<Target Name="CreateSourceArchive" BeforeTargets="BeforeBuild">
<Exec Command="git -C &quot;$(MSBuildProjectDirectory)\..&quot; archive --format=zip --output &quot;$(MSBuildProjectDirectory)\Files\pamhagen-sysctrl.zip&quot; HEAD" />
</Target>
<ItemGroup>
<ProjectReference Include="..\PamhagenSysCtrl\PamhagenSysCtrl.csproj" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup>
<None Include="Files\config.ini" />
<None Include="Files\README.txt" />
</ItemGroup>
</Project>
+22
View File
@@ -0,0 +1,22 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<ComponentGroup Id="MainComponents">
<Component Directory="InstallFolder">
<File Source="$(var.BuildPath)\PamhagenSysCtrl.exe" Id="PamhagenSysCtrl.exe"/>
</Component>
<Files Directory="InstallFolder" Include="$(var.BuildPath)\**">
<Exclude Files="$(var.BuildPath)\PamhagenSysCtrl.exe"/>
<Exclude Files="$(var.BuildPath)\**.pdb"/>
</Files>
<Component Directory="SourceFolder">
<File Source="$(ProjectDir)\Files\pamhagen-sysctrl.zip" Id="pamhagen_sysctrl.zip"/>
</Component>
<Component Directory="ConfigFolder" Permanent="true" NeverOverwrite="true">
<File Source="$(ProjectDir)\Files\config.ini" Id="config.ini"/>
</Component>
<Component Directory="ConfigFolder">
<File Source="$(ProjectDir)\Files\README.txt" Id="README.txt"/>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
+5
View File
@@ -0,0 +1,5 @@
<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl"
Culture="de-AT">
<String Id="DowngradeError"
Value="Eine neuere Version von [ProductName] ist bereits installiert." />
</WixLocalization>
+17
View File
@@ -0,0 +1,17 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Package Name="Anlagensteuerung Pamhagen"
Manufacturer="Anlagensteuerung Pamhagen"
Version="$(var.ProductVersion)"
Language="3079"
UpgradeCode="f214bfc6-1cd0-4971-807e-a4830e3b82a3"
Scope="perMachine"
Compressed="true">
<MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" AllowSameVersionUpgrades="no"/>
<MediaTemplate EmbedCab="true" CompressionLevel="high"/>
<Feature Id="Main" Title="Anlagensteuerung Pamhagen" Level="1">
<ComponentGroupRef Id="MainComponents"/>
<ComponentGroupRef Id="ShortcutComponents"/>
</Feature>
</Package>
</Wix>
+39
View File
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<ComponentGroup Id="ShortcutComponents">
<Component Id="StartMenuFolderShortcut"
Directory="StartMenuProgramsFolder"
Guid="6101b33f-9bc8-4d44-aad0-0f3eddc840c6">
<Shortcut Id="ApplicationStartMenuShortcut"
Name="!(bind.Property.ProductName)"
Description="!(bind.Property.ProductName)"
Target="[InstallFolder]PamhagenSysCtrl.exe"
WorkingDirectory="InstallFolder" />
<RemoveFolder Id="StartMenuProgramsFolder" On="uninstall" />
<RegistryValue Root="HKCU"
Key="Software\!(bind.Property.Manufacturer)\!(bind.Property.ProductName)"
Name="start_menu_installed"
Type="integer"
Value="1"
KeyPath="yes" />
</Component>
<Component Id="DesktopFolderShortcut"
Directory="DesktopFolder"
Guid="58b61147-4e5c-4581-8593-fc7bcb591048">
<Shortcut Id="DesktopShortcut"
Name="!(bind.Property.ProductName)"
Description="!(bind.Property.ProductName)"
Target="[InstallFolder]PamhagenSysCtrl.exe"
WorkingDirectory="InstallFolder" />
<RegistryValue Root="HKCU"
Key="Software\!(bind.Property.Manufacturer)\!(bind.Property.ProductName)"
Name="desktop_installed"
Type="integer"
Value="1"
KeyPath="yes" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
+7
View File
@@ -1,3 +1,10 @@
<Solution> <Solution>
<Configurations>
<Platform Name="Any CPU" />
</Configurations>
<Project Path="Installer/Installer.wixproj">
<Platform Solution="*|Any CPU" Project="x64" />
<Build Solution="*|Any CPU" Project="true" />
</Project>
<Project Path="PamhagenSysCtrl/PamhagenSysCtrl.csproj" /> <Project Path="PamhagenSysCtrl/PamhagenSysCtrl.csproj" />
</Solution> </Solution>
+22
View File
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
</dependentAssembly>
</dependency>
</assembly>
+1 -1
View File
@@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PamhagenSysCtrl" xmlns:local="clr-namespace:PamhagenSysCtrl"
StartupUri="Windows/MainWindow.xaml" StartupUri="Windows/PlantSchemeWindow.xaml"
Exit="Application_Exit"> Exit="Application_Exit">
<Application.Resources> <Application.Resources>
</Application.Resources> </Application.Resources>
+31 -1
View File
@@ -1,4 +1,6 @@
using PamhagenSysCtrl.Helpers; using PamhagenSysCtrl.Helpers;
using System.IO;
using System.Reflection;
using System.Windows; using System.Windows;
using System.Windows.Threading; using System.Windows.Threading;
@@ -6,14 +8,42 @@ namespace PamhagenSysCtrl {
public partial class App : Application { public partial class App : Application {
public static PamhagenPlant? Plant; public static PamhagenPlant? Plant;
public static Dispatcher MainDispatcher; public static Dispatcher? MainDispatcher;
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 InstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Anlagensteuerung Pamhagen");
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Anlagensteuerung Pamhagen");
public static Config Config { get; private set; } = new(ConfigPath);
public static Version Version { get; private set; } = new();
public App() : public App() :
base() { base() {
Directory.CreateDirectory(TempPath);
Directory.CreateDirectory(DataPath);
MainDispatcher = Dispatcher; MainDispatcher = Dispatcher;
var args = Environment.GetCommandLineArgs();
if (args.Length >= 2) {
Config = new(Path.GetFullPath(args[1]));
}
} }
protected override async void OnStartup(StartupEventArgs evt) { protected override async void OnStartup(StartupEventArgs evt) {
Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0");
try {
Plant = new(Config.PlcPort);
await Plant.StartThread();
} catch (Exception exc) {
Plant?.Dispose();
Plant = null;
var str = $"Beim Aufbauen einer Verbindung zur SPS ist ein Fehler aufgetreten:\n\n{exc.GetType().Name}: {exc.Message}";
if (exc.InnerException != null) str += $"\n\n{exc.InnerException.GetType().Name}: {exc.InnerException.Message}";
MessageBox.Show(str, "SPS-Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
base.OnStartup(evt); base.OnStartup(evt);
} }
+28
View File
@@ -0,0 +1,28 @@
using Microsoft.Extensions.Configuration;
using System.Windows;
namespace PamhagenSysCtrl.Helpers {
public class Config {
private static readonly string[] TrueValues = ["1", "true", "yes", "on"];
private readonly string FileName;
public string PlcPort = "COM1";
public Config(string filename) {
FileName = filename;
Read();
}
public void Read() {
try {
var config = new ConfigurationBuilder().AddIniFile(FileName).Build();
PlcPort = config["plc:port"] ?? "COM1";
} catch (Exception exc) {
MessageBox.Show($"Die Konfigurationsdatei konnte nicht gelesen werden:\n\n{exc.Message}", "Konfigurationsdatei lesen", MessageBoxButton.OK, MessageBoxImage.Error);
App.Current.Shutdown();
}
}
}
}
@@ -0,0 +1,4 @@
namespace PamhagenSysCtrl.Helpers {
public class NoClearanceException(string msg) : Exception(msg) {
}
}
+60 -42
View File
@@ -9,6 +9,15 @@ namespace PamhagenSysCtrl.Helpers {
public readonly Trough MW1; public readonly Trough MW1;
public readonly Trough MW2; public readonly Trough MW2;
public readonly Pump MP1;
public readonly Pump MP2;
public readonly Pump P12;
public readonly Pump P3;
public readonly Pump P4;
public readonly Pump P5;
public readonly Pump P6;
public readonly EncasedAuger Auger1;
public readonly EncasedAuger Auger2;
public PamhagenGraph() { public PamhagenGraph() {
double mw2X = 180; double mw2X = 180;
@@ -38,31 +47,33 @@ namespace PamhagenSysCtrl.Helpers {
var pr1X = 750; var pr1X = 750;
var pr2X = 900; var pr2X = 900;
var trough = new EntryTrough("Auffangwanne", mw1X + 70, 100); var trough = new EntryTrough("Auffangwanne", mw1X + 70, 100, () => App.Plant?.IsTroughAugerActive ?? false);
Nodes.Add(trough); Nodes.Add(trough);
var auger1 = new EncasedAuger("", (mw1X + mw2X) / 2 - 100, 200); Auger1 = new EncasedAuger("", (mw1X + mw2X) / 2 - 100, 200, () => App.Plant?.IsAuger1Active ?? false);
var auger2 = new EncasedAuger("Pressen-\nQuerschnecke", 40, 200, 180); var auger3 = new EncasedAuger("Pressen-\nQuerschnecke", 40, 200, () => App.Plant?.IsPresseQuerSchneckeActive ?? false, 180, clickable: false);
var auger3 = new EncasedAuger("", 120, 80, 135); Auger2 = new EncasedAuger("", 120, 80, () => App.Plant?.IsAuger2Active ?? false, 135);
var belt = new ConveyorBelt("", 90, 160); var belt = new ConveyorBelt("", 90, 160, () => App.Plant?.IsBeltActive ?? false);
Nodes.Add(belt); Nodes.Add(belt);
Nodes.Add(auger1); Nodes.Add(Auger1);
Nodes.Add(auger2);
Nodes.Add(auger3); Nodes.Add(auger3);
var rebler = new Rebler("Rebler", (mw1X + mw2X) / 2, 200); Nodes.Add(Auger2);
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);
Nodes.Add(switcher);
MW1 = new Trough("MW1", mw1X, mpY - 70, () => App.Plant?.FillLevelA1 ?? FillState.Unknown); MW1 = new Trough("MW1", mw1X, mpY - 70, () => App.Plant?.FillLevelMW1 ?? FillState.Unknown);
MW2 = new Trough("MW2", mw2X, mpY - 70, () => App.Plant?.FillLevelA1 ?? FillState.Unknown); MW2 = new Trough("MW2", mw2X, mpY - 70, () => App.Plant?.FillLevelMW2 ?? FillState.Unknown);
var mp1 = new Pump("MP1", mw1X, mpY); 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)));
var mp2 = new Pump("MP2", mw2X, mpY); 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)));
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);
CreateOutlet(MW1, mp1); CreateOutlet(MW1, MP1);
CreateOutlet(MW2, mp2); CreateOutlet(MW2, MP2);
CreatePipe(mp1, x1); CreatePipe(MP1, x1);
CreatePipe(mp2, x2); CreatePipe(MP2, x2);
var v1 = CreateValve(1, mw1X, mpY + 70); var v1 = CreateValve(1, mw1X, mpY + 70);
var v2 = CreateValve(2, mw2X, mpY + 70); var v2 = CreateValve(2, mw2X, mpY + 70);
@@ -279,17 +290,17 @@ namespace PamhagenSysCtrl.Helpers {
CreateInlet(v43, t9); CreateInlet(v43, t9);
var xp12 = new PipeJoin((t1X + t2X) / 2, tanksY + 80); var xp12 = new PipeJoin((t1X + t2X) / 2, tanksY + 80);
var p12 = new Pump("P1/2", (t1X + t2X) / 2, tanksY + 110); 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)));
CreateOutlet(t1, xp12); CreateOutlet(t1, xp12);
CreateOutlet(t2, xp12); CreateOutlet(t2, xp12);
CreatePipe(xp12, p12); CreatePipe(xp12, P12);
var p3 = new Pump("P3", t3X, tanksY + 110); 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)));
var p4 = new Pump("P4", t4X, tanksY + 110); 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)));
var p5 = new Pump("P5", t5X, tanksY + 110); 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)));
CreateOutlet(t3, p3); CreateOutlet(t3, P3);
CreateOutlet(t4, p4); CreateOutlet(t4, P4);
CreateOutlet(t5, p5); CreateOutlet(t5, P5);
var v44 = CreateValve(44, (t1X + t2X) / 2, tanksY + 150); var v44 = CreateValve(44, (t1X + t2X) / 2, tanksY + 150);
var v45 = CreateValve(45, t3X, tanksY + 150); var v45 = CreateValve(45, t3X, tanksY + 150);
@@ -299,10 +310,10 @@ namespace PamhagenSysCtrl.Helpers {
var xv44 = new PipeJoin((t1X + t2X) / 2, tanksY + 180); var xv44 = new PipeJoin((t1X + t2X) / 2, tanksY + 180);
var xv45 = new PipeJoin(t3X, tanksY + 180); var xv45 = new PipeJoin(t3X, tanksY + 180);
var xv47 = new PipeJoin(t4X, tanksY + 180); var xv47 = new PipeJoin(t4X, tanksY + 180);
CreatePipe(p12, v44); CreatePipe(P12, v44);
CreatePipe(p3, v45); CreatePipe(P3, v45);
CreatePipe(p4, v47); CreatePipe(P4, v47);
CreatePipe(p5, v48); CreatePipe(P5, v48);
CreatePipe(v44, xv44); CreatePipe(v44, xv44);
CreatePipe(v45, xv45); CreatePipe(v45, xv45);
CreatePipe(v47, xv47); CreatePipe(v47, xv47);
@@ -316,20 +327,20 @@ 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);
var p6 = new Pump("P6", t6X, tanksY + 150); 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)));
CreateOutlet(t6, xt6); CreateOutlet(t6, xt6);
CreateOutlet(t7, xt7); CreateOutlet(t7, xt7);
CreateOutlet(t8, xt8); CreateOutlet(t8, xt8);
CreateOutlet(t9, xt8); CreateOutlet(t9, xt8);
CreatePipe(xt8, xt7); CreatePipe(xt8, xt7);
CreatePipe(xt7, xt6); CreatePipe(xt7, xt6);
CreatePipe(xt6, p6); CreatePipe(xt6, P6);
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 press1 = new Press("Presse 1", pr1X, prY, 200000, () => App.Plant?.FillLevelPress1 ?? FillState.Unknown);
var press2 = new Press("Presse 2", pr2X, prY, 150000, () => App.Plant?.FillLevelPress1 ?? FillState.Unknown); var press2 = new Press("Presse 2", pr2X, prY, 150000, () => App.Plant?.FillLevelPress2 ?? FillState.Unknown);
var v50 = CreateValve(50, pr1X - 45, prY - 100); var v50 = CreateValve(50, pr1X - 45, prY - 100);
var v51 = CreateValve(51, pr1X - 15, prY - 100); var v51 = CreateValve(51, pr1X - 15, prY - 100);
var v52 = CreateValve(52, pr1X + 15, prY - 100); var v52 = CreateValve(52, pr1X + 15, prY - 100);
@@ -346,7 +357,7 @@ namespace PamhagenSysCtrl.Helpers {
CreatePipe(v6, xp1, true); CreatePipe(v6, xp1, true);
CreatePipe(v8, xp2, true); CreatePipe(v8, xp2, true);
CreatePipe(v49, xp3, true); CreatePipe(v49, xp3, true);
CreatePipe(p6, xp4, true); CreatePipe(P6, xp4, true);
CreatePipe(xp1, v51); CreatePipe(xp1, v51);
CreatePipe(xp1, v55); CreatePipe(xp1, v55);
CreatePipe(xp2, v52); CreatePipe(xp2, v52);
@@ -367,7 +378,7 @@ namespace PamhagenSysCtrl.Helpers {
} }
protected static Valve CreateValve(int n, double x, double y) { protected static Valve CreateValve(int n, double x, double y) {
return new(n, x, y, () => App.Plant?.IsValveOpen(4) ?? false, () => App.Plant?.WantValveOpen(4) ?? false); return new(n, x, y, () => App.Plant?.IsValveOpen(n) ?? false, () => App.Plant?.WantValveOpen(n) ?? false);
} }
protected static Tank CreateTank(int n, double x, double y, int capacity) { protected static Tank CreateTank(int n, double x, double y, int capacity) {
@@ -400,14 +411,13 @@ namespace PamhagenSysCtrl.Helpers {
public void AddPath(Path path) { public void AddPath(Path path) {
_selectedPaths.Add(path); _selectedPaths.Add(path);
UpdateValvesAdjacientToPath(path, true); UpdateValvesAdjacientToPath(path, true, false);
if (App.Plant is PamhagenPlant plant) { if (App.Plant is PamhagenPlant plant) {
foreach (var (edge, _) in path.Hops) { foreach (var (_, node) in path.Hops) {
if (edge is Valve v) { if (node is Valve v) {
plant.OpenV(v.Nr); plant.OpenV(v.Nr);
} }
} }
plant.CommitChanges();
} }
} }
@@ -416,18 +426,26 @@ namespace PamhagenSysCtrl.Helpers {
UpdateValvesAdjacientToPath(path, false); UpdateValvesAdjacientToPath(path, false);
foreach (var p in _selectedPaths) UpdateValvesAdjacientToPath(p, true); foreach (var p in _selectedPaths) UpdateValvesAdjacientToPath(p, true);
if (App.Plant is PamhagenPlant plant) { if (App.Plant is PamhagenPlant plant) {
foreach (var (edge, _) in path.Hops) { foreach (var (_, node) in path.Hops) {
if (edge is Valve v) { if (node is Valve v) {
plant.CloseV(v.Nr); plant.CloseV(v.Nr);
} }
} }
plant.CommitChanges();
} }
} }
private void UpdateValvesAdjacientToPath(Path path, bool locked) { 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) => v.IsLocked = locked); TraverseSubgraph(node, valveAction: (v) => {
v.IsLocked = locked;
if (wantOpen.HasValue) {
if (wantOpen.Value) {
App.Plant?.OpenV(v.Nr);
} else {
App.Plant?.CloseV(v.Nr);
}
}
});
} }
} }
+294 -38
View File
@@ -13,17 +13,23 @@ namespace PamhagenSysCtrl.Helpers {
protected bool ActuatorsChanged = false; protected bool ActuatorsChanged = false;
protected PamhagenPlc.ActuatorStates Actuators; protected PamhagenPlc.ActuatorStates Actuators;
protected PamhagenPlc.SensorStates Sensors; protected PamhagenPlc.SensorStates Sensors = new(default, default, default, default, [
FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown, FillState.Unknown
], default);
public bool IsValveOpen(int n) => !IsValveClosed(n); public bool IsValveOpen(int n) => !IsValveClosed(n);
public bool IsValveClosed(int n) => Sensors.GetDS(n); public bool IsValveClosed(int n) => Sensors.GetDS(n);
public bool WantValveClosed(int n) => !WantValveOpen(n); public bool WantValveClosed(int n) => !WantValveOpen(n);
public bool WantValveOpen(int n) => Actuators.GetV(n); public bool WantValveOpen(int n) => Actuators.GetV(n);
public bool IsA1Selected => Sensors.TroughSelector == 1; public bool AreValvesOpen(params int[] ns) => ns.All(IsValveOpen) && ns.All(WantValveOpen);
public bool IsA2Selected => Sensors.TroughSelector == 2; public bool AreValvesClosed(params int[] ns) => ns.All(IsValveClosed) && ns.All(WantValveClosed);
public FillState FillLevelA1 => Sensors.FillingLevels.A1;
public FillState FillLevelA2 => Sensors.FillingLevels.A2; public int TroughSelector => Sensors.TroughSelector;
public bool IsMWSelected => Sensors.TroughSelector == 1;
public bool IsMW2Selected => Sensors.TroughSelector == 2;
public FillState FillLevelMW1 => Sensors.FillingLevels.MW1;
public FillState FillLevelMW2 => Sensors.FillingLevels.MW2;
public FillState FillLevelPress1 => Sensors.FillingLevels.Press1; public FillState FillLevelPress1 => Sensors.FillingLevels.Press1;
public FillState FillLevelPress2 => Sensors.FillingLevels.Press2; public FillState FillLevelPress2 => Sensors.FillingLevels.Press2;
public FillState FillLevelT1 => Sensors.FillingLevelTanks[0]; public FillState FillLevelT1 => Sensors.FillingLevelTanks[0];
@@ -38,26 +44,53 @@ namespace PamhagenSysCtrl.Helpers {
public FillState[] TankFillLevels => Sensors.FillingLevelTanks; public FillState[] TankFillLevels => Sensors.FillingLevelTanks;
public double GradationOe => Sensors.GradationOe; public double GradationOe => Sensors.GradationOe;
public MotorState PresseQuerSchnecke => Sensors.PresseQuerSchnecke; public MotorState PresseQuerSchnecke => Sensors.PresseQuerSchnecke;
public bool IsPresseQuerSchneckeActive => PresseQuerSchnecke == MotorState.Forward;
public bool WantA1Selected => Actuators.GetV(58); public bool WantMW1Selected => Actuators.GetV(58);
public bool WantA2Selected => !Actuators.GetV(58); public bool WantMW2Selected => !Actuators.GetV(58);
public bool IsTroughAugerActive => Actuators.TroughAuger; public bool IsTroughAugerActive => Actuators.TroughAuger;
public bool IsReblerActive => Actuators.Rebler; public bool IsReblerActive => Actuators.Rebler;
public bool IsStirrerA1Active => Actuators.StirrerA1; public bool IsStirrerMW1Active => Actuators.StirrerMW1;
public bool IsStirrerA2Active => Actuators.StirrerA2; public bool IsStirrerMW2Active => Actuators.StirrerMW2;
public bool IsBeltActive => Actuators.Belt; 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 IsWasteDisposalActive => IsBeltActive || IsAuger1Active || IsAuger2Active;
public bool IsGradationPumpActive => Actuators.GradationPump; public bool IsGradationPumpActive => Actuators.GradationPump;
public bool IsMP1Active => Actuators.MP1 == MotorState.Forward || Actuators.MP1 == MotorState.Backward;
public bool IsMP2Active => Actuators.MP2 == MotorState.Forward || Actuators.MP2 == MotorState.Backward;
public bool IsP12Active => Actuators.P12 == MotorState.Forward;
public bool IsP3Active => Actuators.P3 == MotorState.Forward;
public bool IsP4Active => Actuators.P4 == MotorState.Forward;
public bool IsP5Active => Actuators.P5 == MotorState.Forward;
public bool IsP6Active => Actuators.P6 == MotorState.Forward;
public PamhagenPlant(string portName) { public PamhagenPlant(string portName) {
Plc = new(portName); Plc = new(portName);
Update += OnUpdate;
} }
protected virtual void RaiseUpdateEvent(PlantEventArgs evt) { protected virtual void RaiseUpdateEvent(PlantEventArgs evt) {
App.MainDispatcher.BeginInvoke(() => Update?.Invoke(this, evt)); App.MainDispatcher?.BeginInvoke(() => Update?.Invoke(this, evt));
}
protected static void OnUpdate(object? sender, PlantEventArgs evt) {
if (sender is not PamhagenPlant p) return;
if (p.IsPresseQuerSchneckeActive && !p.IsBeltActive) p.StartBelt();
if (p.IsReblerActive && !p.IsAuger1Active) p.StartAuger1();
if (p.IsAuger1Active && !p.IsBeltActive) p.StartBelt();
if (p.IsBeltActive && !p.IsAuger2Active) p.StartAuger2();
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.IsMP2Active && !p.MP2HasClearance) p.StopMP2();
if (p.IsP12Active && !p.P12HasClearance) p.StopP12();
if (p.IsP3Active && !p.P3HasClearance) p.StopP3();
if (p.IsP4Active && !p.P4HasClearance) p.StopP4();
if (p.IsP5Active && !p.P5HasClearance) p.StopP5();
if (p.IsP6Active && !p.P6HasClearance) p.StopP6();
} }
public async Task StartThread() { public async Task StartThread() {
@@ -107,40 +140,222 @@ namespace PamhagenSysCtrl.Helpers {
} }
} }
public void CommitChanges() { protected bool HasWTClearanceAA =>
ActuatorsChanged = true; (AreValvesOpen(12) && AreValvesClosed(15, 16, 20)) || // bypass
} (AreValvesOpen(15, 18, 16) && AreValvesClosed(11, 12, 14, 17, 19, 20)) || // WT1
(AreValvesOpen(15, 17, 20) && AreValvesClosed(11, 12, 18, 19, 21)) || // WT2
(AreValvesOpen(15, 18, 19, 20) && AreValvesClosed(11, 12, 14, 16, 17, 21)) || // WT1+WT2
false;
protected bool HasWTClearanceAB =>
(AreValvesOpen(15, 18, 14) && AreValvesClosed(11, 12, 13, 16, 17, 19, 21)) || // WT1
(AreValvesOpen(15, 17, 21) && AreValvesClosed(11, 12, 13, 18, 19, 20)) || // WT2
(AreValvesOpen(15, 18, 19, 21) && AreValvesClosed(11, 12, 13, 14, 16, 17, 20)) || // WT1+WT2
false;
protected bool HasWTClearanceBA =>
(AreValvesOpen(11, 18, 16) && AreValvesClosed(15, 12, 13, 14, 17, 19, 20)) || // WT1
(AreValvesOpen(11, 17, 20) && AreValvesClosed(15, 12, 13, 18, 19, 21)) || // WT2
(AreValvesOpen(11, 18, 19, 20) && AreValvesClosed(15, 12, 13, 14, 16, 17, 21)) || // WT1+WT2
false;
protected bool HasWTClearanceBB =>
(AreValvesOpen(13) && AreValvesClosed(11, 14, 21)) || // bypass
(AreValvesOpen(11, 18, 14) && AreValvesClosed(15, 13, 16, 17, 19, 21)) || // WT1
(AreValvesOpen(11, 17, 21) && AreValvesClosed(15, 13, 18, 19, 20)) || // WT2
(AreValvesOpen(11, 18, 19, 21) && AreValvesClosed(15, 13, 14, 16, 17, 20)) || // WT1+WT2
false;
protected bool HasT1ClearanceA => AreValvesOpen(22) && AreValvesClosed(24, 26, 28);
protected bool HasT1ClearanceB => AreValvesOpen(23) && AreValvesClosed(25, 27, 29);
protected bool HasT2ClearanceA => AreValvesOpen(24) && AreValvesClosed(22, 26, 28);
protected bool HasT2ClearanceB => AreValvesOpen(25) && AreValvesClosed(23, 27, 29);
protected bool HasT3ClearanceA => AreValvesOpen(26) && AreValvesClosed(22, 24, 28);
protected bool HasT3ClearanceB => AreValvesOpen(27) && AreValvesClosed(23, 25, 29);
protected bool HasT4ClearanceA => AreValvesOpen(28, 30) && AreValvesClosed(22, 24, 26, 32, 34);
protected bool HasT4ClearanceB => AreValvesOpen(29, 31) && AreValvesClosed(23, 25, 27, 33, 35);
protected bool HasT5ClearanceA => AreValvesOpen(28, 32) && AreValvesClosed(22, 24, 26, 30, 34);
protected bool HasT5ClearanceB => AreValvesOpen(29, 33) && AreValvesClosed(23, 25, 27, 31, 35);
protected bool HasT6ClearanceA => AreValvesOpen(28, 34, 37) && AreValvesClosed(22, 24, 26, 30, 32, 39, 41, 43);
protected bool HasT6ClearanceB => AreValvesOpen(29, 35, 36) && AreValvesClosed(23, 25, 27, 31, 33, 38, 40, 42);
protected bool HasT7ClearanceA => AreValvesOpen(28, 34, 39) && AreValvesClosed(22, 24, 26, 30, 32, 37, 41, 43);
protected bool HasT7ClearanceB => AreValvesOpen(29, 35, 38) && AreValvesClosed(23, 25, 27, 31, 33, 36, 40, 42);
protected bool HasT8ClearanceA => AreValvesOpen(28, 34, 41) && AreValvesClosed(22, 24, 26, 30, 32, 37, 39, 43);
protected bool HasT8ClearanceB => AreValvesOpen(29, 35, 40) && AreValvesClosed(23, 25, 27, 31, 33, 36, 38, 42);
protected bool HasT9ClearanceA => AreValvesOpen(28, 34, 43) && AreValvesClosed(22, 24, 26, 30, 32, 37, 39, 41);
protected bool HasT9ClearanceB => AreValvesOpen(29, 35, 42) && AreValvesClosed(23, 25, 27, 31, 33, 36, 38, 40);
protected bool HasClearanceTank1(bool a, bool b) =>
(a && ((HasWTClearanceAA && HasT1ClearanceA) || (HasWTClearanceAB && HasT1ClearanceB))) ||
(b && ((HasWTClearanceBA && HasT1ClearanceA) || (HasWTClearanceBB && HasT1ClearanceB)));
protected bool HasClearanceTank2(bool a, bool b) =>
(a && ((HasWTClearanceAA && HasT2ClearanceA) || (HasWTClearanceAB && HasT2ClearanceB))) ||
(b && ((HasWTClearanceBA && HasT2ClearanceA) || (HasWTClearanceBB && HasT2ClearanceB)));
protected bool HasClearanceTank3(bool a, bool b) =>
(a && ((HasWTClearanceAA && HasT3ClearanceA) || (HasWTClearanceAB && HasT3ClearanceB))) ||
(b && ((HasWTClearanceBA && HasT3ClearanceA) || (HasWTClearanceBB && HasT3ClearanceB)));
protected bool HasClearanceTank4(bool a, bool b) =>
(a && ((HasWTClearanceAA && HasT4ClearanceA) || (HasWTClearanceAB && HasT4ClearanceB))) ||
(b && ((HasWTClearanceBA && HasT4ClearanceA) || (HasWTClearanceBB && HasT4ClearanceB)));
protected bool HasClearanceTank5(bool a, bool b) =>
(a && ((HasWTClearanceAA && HasT5ClearanceA) || (HasWTClearanceAB && HasT5ClearanceB))) ||
(b && ((HasWTClearanceBA && HasT5ClearanceA) || (HasWTClearanceBB && HasT5ClearanceB)));
protected bool HasClearanceTank6(bool a, bool b) =>
(a && ((HasWTClearanceAA && HasT6ClearanceA) || (HasWTClearanceAB && HasT6ClearanceB))) ||
(b && ((HasWTClearanceBA && HasT6ClearanceA) || (HasWTClearanceBB && HasT6ClearanceB)));
protected bool HasClearanceTank7(bool a, bool b) =>
(a && ((HasWTClearanceAA && HasT7ClearanceA) || (HasWTClearanceAB && HasT7ClearanceB))) ||
(b && ((HasWTClearanceBA && HasT7ClearanceA) || (HasWTClearanceBB && HasT7ClearanceB)));
protected bool HasClearanceTank8(bool a, bool b) =>
(a && ((HasWTClearanceAA && HasT8ClearanceA) || (HasWTClearanceAB && HasT8ClearanceB))) ||
(b && ((HasWTClearanceBA && HasT8ClearanceA) || (HasWTClearanceBB && HasT8ClearanceB)));
protected bool HasClearanceTank9(bool a, bool b) =>
(a && ((HasWTClearanceAA && HasT9ClearanceA) || (HasWTClearanceAB && HasT9ClearanceB))) ||
(b && ((HasWTClearanceBA && HasT9ClearanceA) || (HasWTClearanceBB && HasT9ClearanceB)));
public bool MP1HasClearance => FillLevelMW1 != FillState.Empty && (
(MP1HasClearancePress1 && FillLevelPress1 != FillState.Full) ||
(MP1HasClearancePress2 && FillLevelPress2 != FillState.Full) ||
(HasClearanceTank1(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT1 != FillState.Full) ||
(HasClearanceTank2(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT2 != FillState.Full) ||
(HasClearanceTank3(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT3 != FillState.Full) ||
(HasClearanceTank4(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT4 != FillState.Full) ||
(HasClearanceTank5(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT5 != FillState.Full) ||
(HasClearanceTank6(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT6 != FillState.Full) ||
(HasClearanceTank7(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT7 != FillState.Full) ||
(HasClearanceTank8(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT8 != FillState.Full) ||
(HasClearanceTank9(MP1HasClearanceTanksA, MP1HasClearanceTanksB) && FillLevelT9 != FillState.Full) ||
false);
protected bool MP1HasClearancePress1 =>
(AreValvesOpen(1, 6, 51) && AreValvesClosed(3, 4, 5, 55)) ||
(AreValvesOpen(4, 8, 52) && AreValvesClosed(1, 2, 7, 56));
protected bool MP1HasClearancePress2 =>
(AreValvesOpen(1, 6, 55) && AreValvesClosed(3, 4, 5, 51)) ||
(AreValvesOpen(4, 8, 56) && AreValvesClosed(1, 2, 7, 52));
protected bool MP1HasClearanceTanksA => AreValvesOpen(1, 5) && AreValvesClosed(3, 4, 6);
protected bool MP1HasClearanceTanksB => AreValvesOpen(4, 7, 9) && AreValvesClosed(1, 2, 8, 10);
public MotorState MP1 { public MotorState MP1 {
get => Actuators.MP1; get => Actuators.MP1;
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 && (
(MP2HasClearancePress1 && FillLevelPress1 != FillState.Full) ||
(MP2HasClearancePress2 && FillLevelPress2 != FillState.Full) ||
(HasClearanceTank1(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT1 != FillState.Full) ||
(HasClearanceTank2(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT2 != FillState.Full) ||
(HasClearanceTank3(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT3 != FillState.Full) ||
(HasClearanceTank4(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT4 != FillState.Full) ||
(HasClearanceTank5(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT5 != FillState.Full) ||
(HasClearanceTank6(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT6 != FillState.Full) ||
(HasClearanceTank7(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT7 != FillState.Full) ||
(HasClearanceTank8(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT8 != FillState.Full) ||
(HasClearanceTank9(MP2HasClearanceTanksA, MP2HasClearanceTanksB) && FillLevelT9 != FillState.Full) ||
false);
protected bool MP2HasClearancePress1 =>
(AreValvesOpen(2, 8, 52) && AreValvesClosed(3, 4, 7, 56)) ||
(AreValvesOpen(3, 6, 51) && AreValvesClosed(1, 2, 5, 55));
protected bool MP2HasClearancePress2 =>
(AreValvesOpen(2, 8, 56) && AreValvesClosed(3, 4, 7, 52)) ||
(AreValvesOpen(3, 6, 55) && AreValvesClosed(2, 1, 5, 51));
protected bool MP2HasClearanceTanksA => AreValvesOpen(3, 5) && AreValvesClosed(1, 2, 6);
protected bool MP2HasClearanceTanksB => AreValvesOpen(2, 7, 9) && AreValvesClosed(3, 4, 8, 10);
public MotorState MP2 { public MotorState MP2 {
get => Actuators.MP2; get => Actuators.MP2;
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) && (
(P12HasClearancePress1 && FillLevelPress1 != FillState.Full) ||
(P12HasClearancePress2 && FillLevelPress2 != FillState.Full) ||
(HasClearanceTank1(false, P12HasClearanceTanksB) && FillLevelT1 != FillState.Full) ||
(HasClearanceTank2(false, P12HasClearanceTanksB) && FillLevelT2 != FillState.Full) ||
(HasClearanceTank3(false, P12HasClearanceTanksB) && FillLevelT3 != FillState.Full) ||
(HasClearanceTank4(false, P12HasClearanceTanksB) && FillLevelT4 != FillState.Full) ||
(HasClearanceTank5(false, P12HasClearanceTanksB) && FillLevelT5 != FillState.Full) ||
(HasClearanceTank6(false, P12HasClearanceTanksB) && FillLevelT6 != FillState.Full) ||
(HasClearanceTank7(false, P12HasClearanceTanksB) && FillLevelT7 != FillState.Full) ||
(HasClearanceTank8(false, P12HasClearanceTanksB) && FillLevelT8 != FillState.Full) ||
(HasClearanceTank9(false, P12HasClearanceTanksB) && FillLevelT9 != FillState.Full) ||
false);
protected bool P12HasClearancePress1 => AreValvesOpen(44, 46, 49, 50) && AreValvesClosed(10, 45, 47, 48, 54);
protected bool P12HasClearancePress2 => AreValvesOpen(44, 46, 49, 54) && AreValvesClosed(10, 45, 47, 48, 50);
protected bool P12HasClearanceTanksB => AreValvesOpen(44, 10) && AreValvesClosed(9, 45, 46);
public MotorState P12 { public MotorState P12 {
get => Actuators.P12; get => Actuators.P12;
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 && (
(P3HasClearancePress1 && FillLevelPress1 != FillState.Full) ||
(P3HasClearancePress2 && FillLevelPress2 != FillState.Full) ||
(HasClearanceTank1(false, P3HasClearanceTanksB) && FillLevelT1 != FillState.Full) ||
(HasClearanceTank2(false, P3HasClearanceTanksB) && FillLevelT2 != FillState.Full) ||
(HasClearanceTank3(false, P3HasClearanceTanksB) && FillLevelT3 != FillState.Full) ||
(HasClearanceTank4(false, P3HasClearanceTanksB) && FillLevelT4 != FillState.Full) ||
(HasClearanceTank5(false, P3HasClearanceTanksB) && FillLevelT5 != FillState.Full) ||
(HasClearanceTank6(false, P3HasClearanceTanksB) && FillLevelT6 != FillState.Full) ||
(HasClearanceTank7(false, P3HasClearanceTanksB) && FillLevelT7 != FillState.Full) ||
(HasClearanceTank8(false, P3HasClearanceTanksB) && FillLevelT8 != FillState.Full) ||
(HasClearanceTank9(false, P3HasClearanceTanksB) && FillLevelT9 != FillState.Full) ||
false);
protected bool P3HasClearancePress1 => AreValvesOpen(45, 46, 49, 50) && AreValvesClosed(10, 44, 47, 48, 54);
protected bool P3HasClearancePress2 => AreValvesOpen(45, 46, 49, 54) && AreValvesClosed(10, 44, 47, 48, 50);
protected bool P3HasClearanceTanksB => AreValvesOpen(45, 10) && AreValvesClosed(9, 44, 46);
public MotorState P3 { public MotorState P3 {
get => Actuators.P3; get => Actuators.P3;
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 && (
(P4HasClearancePress1 && FillLevelPress1 != FillState.Full) ||
(P4HasClearancePress2 && FillLevelPress2 != FillState.Full) ||
(HasClearanceTank1(false, P4HasClearanceTanksB) && FillLevelT1 != FillState.Full) ||
(HasClearanceTank2(false, P4HasClearanceTanksB) && FillLevelT2 != FillState.Full) ||
(HasClearanceTank3(false, P4HasClearanceTanksB) && FillLevelT3 != FillState.Full) ||
(HasClearanceTank4(false, P4HasClearanceTanksB) && FillLevelT4 != FillState.Full) ||
(HasClearanceTank5(false, P4HasClearanceTanksB) && FillLevelT5 != FillState.Full) ||
(HasClearanceTank6(false, P4HasClearanceTanksB) && FillLevelT6 != FillState.Full) ||
(HasClearanceTank7(false, P4HasClearanceTanksB) && FillLevelT7 != FillState.Full) ||
(HasClearanceTank8(false, P4HasClearanceTanksB) && FillLevelT8 != FillState.Full) ||
(HasClearanceTank9(false, P4HasClearanceTanksB) && FillLevelT9 != FillState.Full) ||
false);
protected bool P4HasClearancePress1 => AreValvesOpen(47, 49, 50) && AreValvesClosed(46, 48, 54);
protected bool P4HasClearancePress2 => AreValvesOpen(47, 49, 54) && AreValvesClosed(46, 48, 50);
protected bool P4HasClearanceTanksB => AreValvesOpen(47, 46, 10) && AreValvesClosed(9, 44, 45, 48, 49);
public MotorState P4 { public MotorState P4 {
get => Actuators.P4; get => Actuators.P4;
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 && (
(P5HasClearancePress1 && FillLevelPress1 != FillState.Full) ||
(P5HasClearancePress2 && FillLevelPress2 != FillState.Full) ||
(HasClearanceTank1(false, P5HasClearanceTanksB) && FillLevelT1 != FillState.Full) ||
(HasClearanceTank2(false, P5HasClearanceTanksB) && FillLevelT2 != FillState.Full) ||
(HasClearanceTank3(false, P5HasClearanceTanksB) && FillLevelT3 != FillState.Full) ||
(HasClearanceTank4(false, P5HasClearanceTanksB) && FillLevelT4 != FillState.Full) ||
(HasClearanceTank5(false, P5HasClearanceTanksB) && FillLevelT5 != FillState.Full) ||
(HasClearanceTank6(false, P5HasClearanceTanksB) && FillLevelT6 != FillState.Full) ||
(HasClearanceTank7(false, P5HasClearanceTanksB) && FillLevelT7 != FillState.Full) ||
(HasClearanceTank8(false, P5HasClearanceTanksB) && FillLevelT8 != FillState.Full) ||
(HasClearanceTank9(false, P5HasClearanceTanksB) && FillLevelT9 != FillState.Full) ||
false);
protected bool P5HasClearancePress1 => AreValvesOpen(48, 49, 50) && AreValvesClosed(46, 47, 54);
protected bool P5HasClearancePress2 => AreValvesOpen(48, 49, 54) && AreValvesClosed(46, 47, 50);
protected bool P5HasClearanceTanksB => AreValvesOpen(48, 46, 10) && AreValvesClosed(9, 44, 45, 47, 49);
public MotorState P5 { public MotorState P5 {
get => Actuators.P5; get => Actuators.P5;
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 =>
(IsValveOpen(53) && IsValveClosed(57) && FillLevelPress1 != FillState.Full) ||
(IsValveOpen(57) && IsValveClosed(53) && FillLevelPress2 != FillState.Full);
public MotorState P6 { public MotorState P6 {
get => Actuators.P6; get => Actuators.P6;
set => Actuators.P6 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P6") : value; set => Actuators.P6 = (value != MotorState.Halt && value != MotorState.Forward) ? throw new ArgumentException("Invalid state for P6") : value;
@@ -153,157 +368,198 @@ namespace PamhagenSysCtrl.Helpers {
protected void SetV(int n, bool val) { protected void SetV(int n, bool val) {
if (n < 1 || n > 57) throw new ArgumentException("Invalid value for n: 1 <= #V <= 57"); if (n < 1 || n > 57) throw new ArgumentException("Invalid value for n: 1 <= #V <= 57");
Actuators.SetV(n, val); Actuators.SetV(n, val);
ActuatorsChanged = true;
} }
public void SelectA1() { public void SelectMW1() {
Actuators.SetV(58, true); Actuators.SetV(58, true);
//if (IsReblerActive) Actuators.StirrerA1 = true; //if (IsReblerActive) Actuators.StirrerMW1 = true;
ActuatorsChanged = true;
} }
public void SelectA2() { public void SelectMW2() {
Actuators.SetV(58, false); Actuators.SetV(58, false);
//if (IsReblerActive) Actuators.StirrerA2 = true; //if (IsReblerActive) Actuators.StirrerMW2 = true;
ActuatorsChanged = true;
} }
public void StartTroughAuger() { public void StartTroughAuger() {
if (!IsReblerActive || (IsMWSelected && FillLevelMW1 == FillState.Full) || (IsMW2Selected && FillLevelMW2 == FillState.Full))
throw new NoClearanceException("Schnecke Auffangwanne hat keine Freigabe");
Actuators.TroughAuger = true; Actuators.TroughAuger = true;
ActuatorsChanged = true;
} }
public void StopTroughAuger() { public void StopTroughAuger() {
Actuators.TroughAuger = false; Actuators.TroughAuger = false;
ActuatorsChanged = true;
} }
public void StartRebler() { public void StartRebler() {
Actuators.Rebler = true; Actuators.Rebler = true;
StartAuger1();
ActuatorsChanged = true;
} }
public void StopRebler() { public void StopRebler() {
StopTroughAuger();
Actuators.Rebler = false; Actuators.Rebler = false;
ActuatorsChanged = true;
} }
public void StartStirrerA1() { public void StartStirrerMW1() {
Actuators.StirrerA1 = true; Actuators.StirrerMW1 = true;
ActuatorsChanged = true;
} }
public void StopStirrerA1() { public void StopStirrerMW1() {
Actuators.StirrerA1 = false; Actuators.StirrerMW1 = false;
ActuatorsChanged = true;
} }
public void StartStirrerA2() { public void StartStirrerMW2() {
Actuators.StirrerA2 = true; Actuators.StirrerMW2 = true;
ActuatorsChanged = true;
} }
public void StopStirrerA2() { public void StopStirrerMW2() {
Actuators.StirrerA2 = false; Actuators.StirrerMW2 = false;
ActuatorsChanged = true;
} }
public void StartBelt() { public void StartBelt() {
Actuators.Belt = true; Actuators.Belt = true;
StartAuger2();
ActuatorsChanged = true;
} }
public void StopBelt() { public void StopBelt() {
if (IsPresseQuerSchneckeActive) throw new NoClearanceException("Presse-Querschnecke aktiv");
if (IsAuger1Active) throw new NoClearanceException("Förderschnecke 1 aktiv");
Actuators.Belt = false; Actuators.Belt = false;
ActuatorsChanged = true;
} }
public void StartAuger1() { public void StartAuger1() {
Actuators.Auger1 = true; Actuators.Auger1 = true;
StartBelt();
ActuatorsChanged = true;
} }
public void StopAuger1() { public void StopAuger1() {
if (IsReblerActive) throw new NoClearanceException("Rebler aktiv");
Actuators.Auger1 = false; Actuators.Auger1 = false;
ActuatorsChanged = true;
} }
public void StartAuger2() { public void StartAuger2() {
Actuators.Auger2 = true; Actuators.Auger2 = true;
ActuatorsChanged = true;
} }
public void StopAuger2() { public void StopAuger2() {
if (IsBeltActive) throw new NoClearanceException("Förderband aktiv");
Actuators.Auger2 = false; Actuators.Auger2 = false;
} ActuatorsChanged = true;
public void StartWasteDisposal() {
StartBelt();
StartAuger1();
StartAuger2();
}
public void StopWasteDisposal() {
StopBelt();
StopAuger1();
StopAuger2();
} }
public void StartGradationPump() { public void StartGradationPump() {
Actuators.GradationPump = true; Actuators.GradationPump = true;
ActuatorsChanged = true;
} }
public void StopGradationPump() { public void StopGradationPump() {
Actuators.GradationPump = false; Actuators.GradationPump = false;
ActuatorsChanged = true;
} }
public void StartMP1Forward() { public void StartMP1Forward() {
if (!MP1HasClearance) throw new NoClearanceException("MP1 hat keine Freigabe");
Actuators.MP1 = MotorState.Forward; Actuators.MP1 = MotorState.Forward;
ActuatorsChanged = true;
} }
public void StartMP1Backward() { public void StartMP1Backward() {
if (!MP1HasClearance) throw new NoClearanceException("MP1 hat keine Freigabe");
Actuators.MP1 = MotorState.Backward; Actuators.MP1 = MotorState.Backward;
ActuatorsChanged = true;
} }
public void StopMP1() { public void StopMP1() {
Actuators.MP1 = MotorState.Halt; Actuators.MP1 = MotorState.Halt;
ActuatorsChanged = true;
} }
public void StartMP2Forward() { public void StartMP2Forward() {
if (!MP2HasClearance) throw new NoClearanceException("MP2 hat keine Freigabe");
Actuators.MP2 = MotorState.Forward; Actuators.MP2 = MotorState.Forward;
ActuatorsChanged = true;
} }
public void StartMP2Backward() { public void StartMP2Backward() {
if (!MP2HasClearance) throw new NoClearanceException("MP2 hat keine Freigabe");
Actuators.MP2 = MotorState.Backward; Actuators.MP2 = MotorState.Backward;
ActuatorsChanged = true;
} }
public void StopMP2() { public void StopMP2() {
Actuators.MP2 = MotorState.Halt; Actuators.MP2 = MotorState.Halt;
ActuatorsChanged = true;
} }
public void StartP12() { public void StartP12() {
if (!P12HasClearance) throw new NoClearanceException("P1/2 hat keine Freigabe");
Actuators.P12 = MotorState.Forward; Actuators.P12 = MotorState.Forward;
ActuatorsChanged = true;
} }
public void StopP12() { public void StopP12() {
Actuators.P12 = MotorState.Halt; Actuators.P12 = MotorState.Halt;
ActuatorsChanged = true;
} }
public void StartP3() { public void StartP3() {
if (!P3HasClearance) throw new NoClearanceException("P3 hat keine Freigabe");
Actuators.P3 = MotorState.Forward; Actuators.P3 = MotorState.Forward;
ActuatorsChanged = true;
} }
public void StopP3() { public void StopP3() {
Actuators.P3 = MotorState.Halt; Actuators.P3 = MotorState.Halt;
ActuatorsChanged = true;
} }
public void StartP4() { public void StartP4() {
if (!P4HasClearance) throw new NoClearanceException("P4 hat keine Freigabe");
Actuators.P4 = MotorState.Forward; Actuators.P4 = MotorState.Forward;
ActuatorsChanged = true;
} }
public void StopP4() { public void StopP4() {
Actuators.P4 = MotorState.Halt; Actuators.P4 = MotorState.Halt;
ActuatorsChanged = true;
} }
public void StartP5() { public void StartP5() {
if (!P5HasClearance) throw new NoClearanceException("P5 hat keine Freigabe");
Actuators.P5 = MotorState.Forward; Actuators.P5 = MotorState.Forward;
ActuatorsChanged = true;
} }
public void StopP5() { public void StopP5() {
Actuators.P5 = MotorState.Halt; Actuators.P5 = MotorState.Halt;
ActuatorsChanged = true;
} }
public void StartP6() { public void StartP6() {
if (!P6HasClearance) throw new NoClearanceException("P6 hat keine Freigabe");
Actuators.P6 = MotorState.Forward; Actuators.P6 = MotorState.Forward;
ActuatorsChanged = true;
} }
public void StopP6() { public void StopP6() {
Actuators.P6 = MotorState.Halt; Actuators.P6 = MotorState.Halt;
ActuatorsChanged = true;
} }
} }
} }
+7 -7
View File
@@ -5,7 +5,7 @@ namespace PamhagenSysCtrl.Helpers {
public record struct SensorStates( public record struct SensorStates(
long PressureSensors, long PressureSensors,
(FillState A1, FillState A2, FillState Press1, FillState Press2) FillingLevels, (FillState MW1, FillState MW2, FillState Press1, FillState Press2) FillingLevels,
int TroughSelector, int TroughSelector,
double GradationOe, double GradationOe,
FillState[] FillingLevelTanks, FillState[] FillingLevelTanks,
@@ -17,7 +17,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 StirrerA1, bool StirrerA2, bool Belt, bool Auger1, bool Auger2, bool GradationPump bool TroughAuger, bool Rebler, bool StirrerMW1, bool StirrerMW2, bool Belt, bool Auger1, bool Auger2, 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) {
@@ -107,8 +107,8 @@ namespace PamhagenSysCtrl.Helpers {
long pressure = (long)r1 | ((long)r2 << 16) | ((long)r3 << 32) | ((long)r4 << 48); long pressure = (long)r1 | ((long)r2 << 16) | ((long)r3 << 32) | ((long)r4 << 48);
pressure = (pressure & 0x01FFFFFFFFFFFFFF) | ((pressure & 0x0600000000000000) << 1); pressure = (pressure & 0x01FFFFFFFFFFFFFF) | ((pressure & 0x0600000000000000) << 1);
var a1 = Int3ToFillState((f1 ) & 0x7); // 111 011 001 000 var mw1 = Int3ToFillState((f1 ) & 0x7); // 111 011 001 000
var a2 = 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 = Int1ToFillState((f1 >> 6) & 0x1); // 1 0
var p2 = Int1ToFillState((f1 >> 7) & 0x1); // 1 0 var p2 = Int1ToFillState((f1 >> 7) & 0x1); // 1 0
var wp = ((f1 >> 8) & 0x3); // 10 01 var wp = ((f1 >> 8) & 0x3); // 10 01
@@ -137,7 +137,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, (a1, a2, 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() {
@@ -210,8 +210,8 @@ namespace PamhagenSysCtrl.Helpers {
(state.TroughAuger ? 0x01 : 0x00) | (state.TroughAuger ? 0x01 : 0x00) |
(state.Rebler ? 0x02 : 0x00) | (state.Rebler ? 0x02 : 0x00) |
(state.GetV(58) ? 0x04 : 0x00) | (state.GetV(58) ? 0x04 : 0x00) |
(state.StirrerA1 ? 0x08 : 0x00) | (state.StirrerMW1 ? 0x08 : 0x00) |
(state.StirrerA2 ? 0x10 : 0x00) | (state.StirrerMW2 ? 0x10 : 0x00) |
(state.Belt ? 0x20 : 0x00) | (state.Belt ? 0x20 : 0x00) |
(state.Auger1 ? 0x40 : 0x00) | (state.Auger1 ? 0x40 : 0x00) |
(state.Auger2 ? 0x80 : 0x00); (state.Auger2 ? 0x80 : 0x00);
+1 -1
View File
@@ -13,7 +13,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
public double Height { get; set; } public double Height { get; set; }
public double Angle { get; set; } public double Angle { get; set; }
public bool IsActive { public bool IsAnimationActive {
set { set {
if (value) { if (value) {
_storyboard?.Resume(); _storyboard?.Resume();
@@ -2,13 +2,12 @@
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Animation; using System.Windows.Media.Animation;
using System.Windows.Media.Media3D;
using System.Windows.Shapes; using System.Windows.Shapes;
namespace PamhagenSysCtrl.Helpers.Pipeline { namespace PamhagenSysCtrl.Helpers.Pipeline {
public class ConveyorBelt : INode { public class ConveyorBelt : INode {
public const double WIDTH = 35; public const double WIDTH = 30;
public const double HEIGHT = 120; public const double HEIGHT = 120;
public double CenterX { get; set; } public double CenterX { get; set; }
@@ -17,10 +16,11 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
public ISet<IEdge> Inputs { get; init; } public ISet<IEdge> Inputs { get; init; }
public ISet<IEdge> Outputs { get; init; } public ISet<IEdge> Outputs { get; init; }
public bool IsActive { public bool IsActive => CallbackActive();
get => _active; public bool IsAnimationActive {
get => _animationActive;
set { set {
_active = value; _animationActive = value;
if (value) { if (value) {
_storyboard?.Resume(); _storyboard?.Resume();
} else { } else {
@@ -29,15 +29,18 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
} }
} }
private bool _active; protected Func<bool> CallbackActive;
private bool _animationActive;
private Shape? _rect; private Shape? _rect;
private Polyline? _polyline; private Polyline? _polyline;
private Storyboard? _storyboard; private Storyboard? _storyboard;
public ConveyorBelt(string label, double x, double y) { public ConveyorBelt(string label, double x, double y, Func<bool> cbActive) {
CenterX = x; CenterX = x;
CenterY = y; CenterY = y;
Label = label; Label = label;
CallbackActive = cbActive;
Inputs = new HashSet<IEdge>(); Inputs = new HashSet<IEdge>();
Outputs = new HashSet<IEdge>(); Outputs = new HashSet<IEdge>();
} }
@@ -54,7 +57,7 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
_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);
var d = 20; var d = 15;
int n = (int)(HEIGHT / d) + 2; int n = (int)(HEIGHT / d) + 2;
_polyline = new Polyline() { _polyline = new Polyline() {
Points = [.. Enumerable.Range(0, n).SelectMany(i => new List<Point>() { Points = [.. Enumerable.Range(0, n).SelectMany(i => new List<Point>() {
@@ -77,12 +80,12 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
Storyboard.SetTarget(slideUp, _polyline); Storyboard.SetTarget(slideUp, _polyline);
Storyboard.SetTargetProperty(slideUp, new PropertyPath(Canvas.TopProperty)); Storyboard.SetTargetProperty(slideUp, new PropertyPath(Canvas.TopProperty));
var clipCanvas = new Canvas() { var clipCanvas = new Canvas() {
Width = WIDTH - 20, Width = WIDTH - 15,
Height = HEIGHT - 2, Height = HEIGHT - 8,
ClipToBounds = true, ClipToBounds = true,
}; };
clipCanvas.SetValue(Canvas.TopProperty, CenterY - HEIGHT / 2 + 2); clipCanvas.SetValue(Canvas.TopProperty, CenterY - HEIGHT / 2 + 4);
clipCanvas.SetValue(Canvas.LeftProperty, CenterX - WIDTH / 2 + 10); clipCanvas.SetValue(Canvas.LeftProperty, CenterX - WIDTH / 2 + 7.5);
clipCanvas.Children.Add(_polyline); clipCanvas.Children.Add(_polyline);
canvas.Children.Add(_rect); canvas.Children.Add(_rect);
@@ -97,12 +100,13 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
} }
public void Update(bool isHovering, bool isPressed) { public void Update(bool isHovering, bool isPressed) {
IsAnimationActive = IsActive;
_rect?.Fill = _rect?.Fill =
IsActive && isHovering && isPressed ? PamhagenBrushes.Red : IsAnimationActive && isHovering && isPressed ? PamhagenBrushes.Red :
IsActive && isHovering ? PamhagenBrushes.Red : IsAnimationActive && isHovering ? PamhagenBrushes.Red :
!IsActive && isHovering && isPressed ? PamhagenBrushes.Green : !IsAnimationActive && isHovering && isPressed ? PamhagenBrushes.Green :
!IsActive && isHovering ? PamhagenBrushes.Green : !IsAnimationActive && isHovering ? PamhagenBrushes.Green :
IsActive ? PamhagenBrushes.Green : IsAnimationActive ? PamhagenBrushes.Green :
Brushes.WhiteSmoke; Brushes.WhiteSmoke;
} }
} }
@@ -16,25 +16,31 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
public ISet<IEdge> Inputs { get; init; } public ISet<IEdge> Inputs { get; init; }
public ISet<IEdge> Outputs { get; init; } public ISet<IEdge> Outputs { get; init; }
public bool IsActive { public bool IsClickable { get; private set; }
get => _active; public bool IsActive => CallbackActive();
public bool IsAnimationActive {
get => _animationActive;
set { set {
_active = value; _animationActive = value;
_auger?.IsActive = value; _auger?.IsAnimationActive = value;
} }
} }
private bool _active; protected Func<bool> CallbackActive;
private bool _animationActive;
private Shape? _inner; private Shape? _inner;
private Shape? _outer; private Shape? _outer;
private Auger? _auger; private Auger? _auger;
private TextBlock? _text; private TextBlock? _text;
public EncasedAuger(string label, double x, double y, double angle = 0) { public EncasedAuger(string label, double x, double y, Func<bool> cbActive, double angle = 0, bool clickable = true) {
CenterX = x; CenterX = x;
CenterY = y; CenterY = y;
Label = label; Label = label;
Angle = angle; Angle = angle;
CallbackActive = cbActive;
IsClickable = clickable;
Inputs = new HashSet<IEdge>(); Inputs = new HashSet<IEdge>();
Outputs = new HashSet<IEdge>(); Outputs = new HashSet<IEdge>();
} }
@@ -80,12 +86,14 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
} }
public void Update(bool isHovering, bool isPressed) { public void Update(bool isHovering, bool isPressed) {
isHovering = isHovering && IsClickable;
IsAnimationActive = IsActive;
_inner?.Fill = _inner?.Fill =
IsActive && isHovering && isPressed ? PamhagenBrushes.Red : IsAnimationActive && isHovering && isPressed ? PamhagenBrushes.Red :
IsActive && isHovering ? PamhagenBrushes.Red : IsAnimationActive && isHovering ? PamhagenBrushes.Red :
!IsActive && isHovering && isPressed ? PamhagenBrushes.Green : !IsAnimationActive && isHovering && isPressed ? PamhagenBrushes.Green :
!IsActive && isHovering ? PamhagenBrushes.Green : !IsAnimationActive && isHovering ? PamhagenBrushes.Green :
IsActive ? PamhagenBrushes.Green : IsAnimationActive ? PamhagenBrushes.Green :
Brushes.WhiteSmoke; Brushes.WhiteSmoke;
} }
} }
+16 -11
View File
@@ -16,11 +16,12 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
public ISet<IEdge> Inputs { get; init; } public ISet<IEdge> Inputs { get; init; }
public ISet<IEdge> Outputs { get; init; } public ISet<IEdge> Outputs { get; init; }
public bool IsActive { public bool IsActive => CallbackActive();
get => _active; public bool IsAnimationActive {
get => _animationActive;
set { set {
_active = value; _animationActive = value;
_auger?.IsActive = value; _auger?.IsAnimationActive = value;
if (value) { if (value) {
_doorTransform?.BeginAnimation( _doorTransform?.BeginAnimation(
RotateTransform.AngleProperty, RotateTransform.AngleProperty,
@@ -41,7 +42,9 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
} }
} }
private bool _active; protected Func<bool> CallbackActive;
private bool _animationActive;
private Shape? _inner; private Shape? _inner;
private Shape? _outer; private Shape? _outer;
private TextBlock? _text; private TextBlock? _text;
@@ -49,10 +52,11 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
private Shape? _door; private Shape? _door;
private RotateTransform? _doorTransform; private RotateTransform? _doorTransform;
public EntryTrough(string label, double x, double y) { public EntryTrough(string label, double x, double y, Func<bool> cbActive) {
CenterX = x; CenterX = x;
CenterY = y; CenterY = y;
Label = label; Label = label;
CallbackActive = cbActive;
Inputs = new HashSet<IEdge>(); Inputs = new HashSet<IEdge>();
Outputs = new HashSet<IEdge>(); Outputs = new HashSet<IEdge>();
} }
@@ -99,12 +103,13 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
} }
public void Update(bool isHovering, bool isPressed) { public void Update(bool isHovering, bool isPressed) {
IsAnimationActive = IsActive;
_inner?.Fill = _inner?.Fill =
IsActive && isHovering && isPressed ? PamhagenBrushes.Red : IsAnimationActive && isHovering && isPressed ? PamhagenBrushes.Red :
IsActive && isHovering ? PamhagenBrushes.Red : IsAnimationActive && isHovering ? PamhagenBrushes.Red :
!IsActive && isHovering && isPressed ? PamhagenBrushes.Green : !IsAnimationActive && isHovering && isPressed ? PamhagenBrushes.Green :
!IsActive && isHovering ? PamhagenBrushes.Green : !IsAnimationActive && isHovering ? PamhagenBrushes.Green :
IsActive ? PamhagenBrushes.Green : IsAnimationActive ? PamhagenBrushes.Green :
Brushes.WhiteSmoke; Brushes.WhiteSmoke;
} }
} }
+6 -2
View File
@@ -55,15 +55,19 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
} }
public Path? GetPath(IEnumerable<INode> points, Func<INode, bool>? valid = null) { public Path? GetPath(IEnumerable<INode> points, Func<INode, bool>? valid = null) {
if (points.Count() < 2) throw new ArgumentException("Path is too short"); if (points.Count() < 2) return null;
var start = points.First(); var start = points.First();
var path = new Path(start, []); var path = new Path(start, []);
foreach (var end in points.Skip(1)) { foreach (var end in points.Skip(1)) {
if (GetPath(start, end, valid, visited: [..path.Hops.Select(h => h.Node)]) is not Path p) if (valid != null && !valid(start)) {
return null; return null;
} else if (GetPath(start, end, valid, visited: [.. path.Hops.Select(h => h.Node)]) is not Path p) {
return null;
} else {
path.Hops = [.. path.Hops, .. p.Hops]; path.Hops = [.. path.Hops, .. p.Hops];
start = end; start = end;
} }
}
return path; return path;
} }
+20 -2
View File
@@ -15,14 +15,26 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
public ISet<IEdge> Inputs { get; init; } public ISet<IEdge> Inputs { get; init; }
public ISet<IEdge> Outputs { get; init; } public ISet<IEdge> Outputs { get; init; }
public Brush? Highlight { get; set; }
public bool IsActive => CallbackActive();
public bool HasClearance => CallbackClearance();
public bool IsSelected => CallbackSelected(this);
protected Func<bool> CallbackActive;
protected Func<bool> CallbackClearance;
protected Func<Pump, bool> CallbackSelected;
private Shape? _circle; private Shape? _circle;
private Shape? _triangle; private Shape? _triangle;
private TextBlock? _text; private TextBlock? _text;
public Pump(string label, double x, double y) { public Pump(string label, double x, double y, Func<bool> cbActive, Func<bool> cbClearance, Func<Pump, bool> cbSelected) {
CenterX = x; CenterX = x;
CenterY = y; CenterY = y;
Label = label; Label = label;
CallbackActive = cbActive;
CallbackClearance = cbClearance;
CallbackSelected = cbSelected;
Inputs = new HashSet<IEdge>(); Inputs = new HashSet<IEdge>();
Outputs = new HashSet<IEdge>(); Outputs = new HashSet<IEdge>();
} }
@@ -63,7 +75,13 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
} }
public void Update(bool isHovering, bool isPresed) { public void Update(bool isHovering, bool isPresed) {
_circle?.Fill = isHovering ? Brushes.LightGray : Brushes.WhiteSmoke; _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);
} }
} }
} }
+5 -2
View File
@@ -15,7 +15,9 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
public ISet<IEdge> Inputs { get; init; } public ISet<IEdge> Inputs { get; init; }
public ISet<IEdge> Outputs { get; init; } public ISet<IEdge> Outputs { get; init; }
public bool IsActive { get; set; } public bool IsActive => CallbackActive();
protected Func<bool> CallbackActive;
private TextBlock? _text; private TextBlock? _text;
private Shape? _rect; private Shape? _rect;
@@ -24,10 +26,11 @@ namespace PamhagenSysCtrl.Helpers.Pipeline {
private Shape? _bottomInner; private Shape? _bottomInner;
private Shape? _bottomOuter; private Shape? _bottomOuter;
public Rebler(string label, double x, double y) { public Rebler(string label, double x, double y, Func<bool> cbActive) {
CenterX = x; CenterX = x;
CenterY = y; CenterY = y;
Label = label; Label = label;
CallbackActive = cbActive;
Inputs = new HashSet<IEdge>(); Inputs = new HashSet<IEdge>();
Outputs = new HashSet<IEdge>(); Outputs = new HashSet<IEdge>();
} }
@@ -0,0 +1,87 @@
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace PamhagenSysCtrl.Helpers.Pipeline {
public class Switcher : INode {
public const double WIDTH = 100;
public const double HEIGHT = 50;
public double CenterX { get; set; }
public double CenterY { get; set; }
public string Label { get; set; }
public ISet<IEdge> Inputs { get; init; }
public ISet<IEdge> Outputs { get; init; }
private Line? _main;
private Line? _shadow;
private RotateTransform? _mainTransform;
private RotateTransform? _shadowTransform;
protected Func<int> CallbackIsPosition;
protected Func<int> CallbackWantPosition;
public Switcher(string label, double x, double y, Func<int> cbIsPosition, Func<int> cbWantPosition) {
CenterX = x;
CenterY = y;
Label = label;
CallbackIsPosition = cbIsPosition;
CallbackWantPosition = cbWantPosition;
Inputs = new HashSet<IEdge>();
Outputs = new HashSet<IEdge>();
}
public void Draw(Canvas canvas) {
_mainTransform = new RotateTransform(0, CenterX, CenterY);
_shadowTransform = new RotateTransform(0, CenterX, CenterY);
_main = new Line() {
X1 = CenterX - WIDTH / 2, Y1 = CenterY,
X2 = CenterX + WIDTH / 2, Y2 = CenterY,
StrokeThickness = 5,
Stroke = Brushes.Black,
StrokeStartLineCap = PenLineCap.Square,
StrokeEndLineCap = PenLineCap.Square,
RenderTransform = _mainTransform,
};
_shadow = new Line() {
X1 = CenterX - WIDTH / 2, Y1 = CenterY,
X2 = CenterX + WIDTH / 2, Y2 = CenterY,
StrokeThickness = 5,
Stroke = Brushes.DarkGray,
StrokeStartLineCap = PenLineCap.Square,
StrokeEndLineCap = PenLineCap.Square,
RenderTransform = _shadowTransform,
};
canvas.Children.Add(_shadow);
canvas.Children.Add(_main);
}
public bool IsInside(double x, double y) {
return Math.Abs(x - CenterX) <= WIDTH / 2 && Math.Abs(y - CenterY) <= HEIGHT / 2;
}
public void Update(bool isHovering, bool isPressed) {
var isPos = CallbackIsPosition();
var wantPos = CallbackWantPosition();
if (isHovering && wantPos == isPos) wantPos = wantPos == 1 ? 2 : 1;
var target1 = isPos == 0 ? 0 : isPos == 1 ? 15 : -15;
var target2 = wantPos == 0 ? 0 : wantPos == 1 ? 15 : -15;
_mainTransform?.BeginAnimation(
RotateTransform.AngleProperty,
new DoubleAnimation {
From = _mainTransform.Angle,
To = target1,
Duration = TimeSpan.FromSeconds(Math.Abs(_mainTransform.Angle - target1) / 120.0)
});
_shadowTransform?.BeginAnimation(
RotateTransform.AngleProperty,
new DoubleAnimation {
From = _shadowTransform.Angle,
To = target2,
Duration = TimeSpan.FromSeconds(Math.Abs(_shadowTransform.Angle - target2) / 120.0)
});
}
}
}
+6 -1
View File
@@ -3,14 +3,19 @@
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net10.0-windows</TargetFramework> <TargetFramework>net10.0-windows</TargetFramework>
<Version>0.0.1</Version>
<Product>Anlagensteuerung Pamhagen</Product>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>App.manifest</ApplicationManifest>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.IO.Ports" Version="10.0.9" /> <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.10" />
<PackageReference Include="System.IO.Ports" Version="10.0.10" />
</ItemGroup> </ItemGroup>
</Project> </Project>
-278
View File
@@ -1,278 +0,0 @@
using PamhagenSysCtrl.Helpers;
using PamhagenSysCtrl.Windows;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace PamhagenSysCtrl {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
for (int i = 1; i <= 60; i++) {
var b = new Button() {
Content = $"V{i}",
Margin = new(10 + ((i - 1) / 10) * 50, 10 + ((i - 1) % 10) * 40, 10, 10),
BorderThickness = new(2),
FontWeight = FontWeights.Bold,
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
Height = 30,
Width = 40,
FontSize = 14,
};
var borderStyle = new Style(typeof(Border));
borderStyle.Setters.Add(new Setter(Border.CornerRadiusProperty, new CornerRadius(5)));
b.Resources.Add(typeof(Border), borderStyle);
b.Click += (sender, evt) => {
if (App.Plant == null || sender is not Button b) return;
var i = int.Parse($"{b.Content}"[1..]);
if (i > 57) {
return;
} else if (App.Plant.WantValveOpen(i)) {
App.Plant.CloseV(i);
} else {
App.Plant.OpenV(i);
}
App.Plant.CommitChanges();
};
ValvePanel.Children.Add(b);
}
var w = new PlantSchemeWindow();
w.Show();
}
public async void ConnectButton_Click(object? sender, RoutedEventArgs evt) {
try {
App.Plant = new(PortInput.Text);
App.Plant.Update += OnUpdate;
await App.Plant.StartThread();
} catch (Exception exc) {
App.Plant?.Dispose();
App.Plant = null;
var str = $"Beim Aufbauen einer Verbindung zur SPS ist ein Fehler aufgetreten:\n\n{exc.GetType().Name}: {exc.Message}";
if (exc.InnerException != null) str += $"\n\n{exc.InnerException.GetType().Name}: {exc.InnerException.Message}";
MessageBox.Show(str, "SPS-Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
}
public void OnUpdate(object? sender, PlantEventArgs evt) {
if (sender is not PamhagenPlant plant) return;
for (int i = 1; i <= 60; i++) {
var target = plant.WantValveClosed(i);
var actual = plant.IsValveClosed(i);
if (ValvePanel.Children[i - 1] is not Button b)
continue;
b.BorderBrush = target ? Brushes.DarkRed : Brushes.SlateGray;
b.Foreground = target ? Brushes.DarkRed : Brushes.Black;
b.Background = actual ? Brushes.MistyRose : Brushes.WhiteSmoke;
}
ReblerButton.BorderBrush = plant.IsReblerActive ? Brushes.DarkGreen : Brushes.DarkRed;
ReblerButton.Foreground = plant.IsReblerActive ? Brushes.DarkGreen : Brushes.DarkRed;
ReblerButton.Background = plant.IsReblerActive ? Brushes.MintCream: Brushes.MistyRose;
TroughAugerButton.BorderBrush = plant.IsTroughAugerActive ? Brushes.DarkGreen : Brushes.DarkRed;
TroughAugerButton.Foreground = plant.IsTroughAugerActive ? Brushes.DarkGreen : Brushes.DarkRed;
TroughAugerButton.Background = plant.IsTroughAugerActive ? Brushes.MintCream : Brushes.MistyRose;
StirrerA1Button.BorderBrush = plant.IsStirrerA1Active ? Brushes.DarkGreen : Brushes.DarkRed;
StirrerA1Button.Foreground = plant.IsStirrerA1Active ? Brushes.DarkGreen : Brushes.DarkRed;
StirrerA1Button.Background = plant.IsStirrerA1Active ? Brushes.MintCream : Brushes.MistyRose;
StirrerA2Button.BorderBrush = plant.IsStirrerA2Active ? Brushes.DarkGreen : Brushes.DarkRed;
StirrerA2Button.Foreground = plant.IsStirrerA2Active ? Brushes.DarkGreen : Brushes.DarkRed;
StirrerA2Button.Background = plant.IsStirrerA2Active ? 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.Foreground = plant.IsAuger1Active ? Brushes.DarkGreen : Brushes.DarkRed;
Auger1Button.Background = plant.IsAuger1Active ? Brushes.MintCream : Brushes.MistyRose;
Auger2Button.BorderBrush = plant.IsAuger2Active ? Brushes.DarkGreen : Brushes.DarkRed;
Auger2Button.Foreground = plant.IsAuger2Active ? Brushes.DarkGreen : Brushes.DarkRed;
Auger2Button.Background = plant.IsAuger2Active ? Brushes.MintCream : Brushes.MistyRose;
GradationPumpButton.BorderBrush = plant.IsGradationPumpActive ? Brushes.DarkGreen : Brushes.DarkRed;
GradationPumpButton.Foreground = plant.IsGradationPumpActive ? Brushes.DarkGreen : Brushes.DarkRed;
GradationPumpButton.Background = plant.IsGradationPumpActive ? Brushes.MintCream : Brushes.MistyRose;
MP1BackwardButton.BorderBrush = 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;
MP1HaltButton.BorderBrush = plant.MP1 == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray;
MP1HaltButton.Foreground = plant.MP1 == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray;
MP1HaltButton.Background = plant.MP1 == MotorState.Halt ? Brushes.MistyRose : Brushes.WhiteSmoke;
MP1ForwardButton.BorderBrush = plant.MP1 == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray;
MP1ForwardButton.Foreground = plant.MP1 == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray;
MP1ForwardButton.Background = plant.MP1 == MotorState.Forward ? Brushes.MintCream: Brushes.WhiteSmoke;
MP2BackwardButton.BorderBrush = plant.MP2 == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray;
MP2BackwardButton.Foreground = plant.MP2 == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray;
MP2BackwardButton.Background = plant.MP2 == MotorState.Backward ? Brushes.MintCream : Brushes.WhiteSmoke;
MP2HaltButton.BorderBrush = plant.MP2 == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray;
MP2HaltButton.Foreground = plant.MP2 == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray;
MP2HaltButton.Background = plant.MP2 == MotorState.Halt ? Brushes.MistyRose : Brushes.WhiteSmoke;
MP2ForwardButton.BorderBrush = plant.MP2 == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray;
MP2ForwardButton.Foreground = plant.MP2 == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray;
MP2ForwardButton.Background = plant.MP2 == MotorState.Forward ? Brushes.MintCream : Brushes.WhiteSmoke;
P12Button.BorderBrush = plant.P12 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed;
P12Button.Foreground = plant.P12 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed;
P12Button.Background = plant.P12 == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose;
Status.Text = $"""
Gradation °Oe: {plant.GradationOe:N2}
Presse Querschnecke: {plant.PresseQuerSchnecke}
Füllstände:
Wanne A1: {plant.FillLevelA1} (ausgewählt: {plant.WantA1Selected}, Status: {plant.IsA1Selected})
Wanne A2: {plant.FillLevelA2} (ausgewählt: {plant.WantA2Selected}, Status: {plant.IsA2Selected})
Presse 1: {plant.FillLevelPress1}
Presse 2: {plant.FillLevelPress2}
Tank 1: {plant.FillLevelT1}
Tank 2: {plant.FillLevelT2}
Tank 3: {plant.FillLevelT3}
Tank 4: {plant.FillLevelT4}
Tank 5: {plant.FillLevelT5}
Tank 6: {plant.FillLevelT6}
Tank 7: {plant.FillLevelT7}
Tank 8: {plant.FillLevelT8}
Tank 9: {plant.FillLevelT9}
""";
}
private void TroughAugerButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
if (App.Plant.IsTroughAugerActive) {
App.Plant.StopTroughAuger();
} else {
App.Plant.StartTroughAuger();
}
App.Plant.CommitChanges();
}
private void ReblerButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
if (App.Plant.IsReblerActive) {
App.Plant.StopRebler();
} else {
App.Plant.StartRebler();
}
App.Plant.CommitChanges();
}
private void StirrerA1Button_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
if (App.Plant.IsStirrerA1Active) {
App.Plant.StopStirrerA1();
} else {
App.Plant.StartStirrerA1();
}
App.Plant.CommitChanges();
}
private void StirrerA2Button_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
if (App.Plant.IsStirrerA2Active) {
App.Plant.StopStirrerA2();
} else {
App.Plant.StartStirrerA2();
}
App.Plant.CommitChanges();
}
private void BeltButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
if (App.Plant.IsBeltActive) {
App.Plant.StopBelt();
} else {
App.Plant.StartBelt();
}
App.Plant.CommitChanges();
}
private void Auger1Button_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
if (App.Plant.IsAuger1Active) {
App.Plant.StopAuger1();
} else {
App.Plant.StartAuger1();
}
App.Plant.CommitChanges();
}
private void Auger2Button_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
if (App.Plant.IsAuger2Active) {
App.Plant.StopAuger2();
} else {
App.Plant.StartAuger2();
}
App.Plant.CommitChanges();
}
private void GradationPumpButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
if (App.Plant.IsGradationPumpActive) {
App.Plant.StopGradationPump();
} else {
App.Plant.StartGradationPump();
}
App.Plant.CommitChanges();
}
private void MP1BackwardButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
App.Plant.StartMP1Backward();
App.Plant.CommitChanges();
}
private void MP1HaltButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
App.Plant.StopMP1();
App.Plant.CommitChanges();
}
private void MP1ForwardButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
App.Plant.StartMP1Forward();
App.Plant.CommitChanges();
}
private void MP2BackwardButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
App.Plant.StartMP2Backward();
App.Plant.CommitChanges();
}
private void MP2HaltButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
App.Plant.StopMP2();
App.Plant.CommitChanges();
}
private void MP2ForwardButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
App.Plant.StartMP2Forward();
App.Plant.CommitChanges();
}
private void P12Button_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
if (App.Plant.P12 == MotorState.Forward) {
App.Plant.StopP12();
} else {
App.Plant.StartP12();
}
App.Plant.CommitChanges();
}
}
}
@@ -1,19 +1,13 @@
<Window x:Class="PamhagenSysCtrl.MainWindow" <Window x:Class="PamhagenSysCtrl.ManualControlWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
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" xmlns:local="clr-namespace:PamhagenSysCtrl"
mc:Ignorable="d" mc:Ignorable="d"
Title="MainWindow" Height="550" Width="860"> Title="Manuelle Steuerung - Anlagensteuerung Pamhagen" Height="550" Width="860">
<Grid> <Grid>
<TextBox x:Name="PortInput" Text="COM1" <Grid x:Name="ValvePanel" Margin="5,5,5,5">
Margin="10,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Height="25" Width="80" FontSize="14" Padding="2,2,2,2"/>
<Button x:Name="ConnectButton" Content="Verbinden"
Margin="100,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Height="25" Width="80" FontSize="14"
Click="ConnectButton_Click"/>
<Grid x:Name="ValvePanel" Margin="5,50,5,5">
</Grid> </Grid>
<TextBlock x:Name="Status" FontSize="14" <TextBlock x:Name="Status" FontSize="14"
@@ -31,66 +25,75 @@
Margin="10,45,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,45,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="ReblerButton_Click"/> Click="ReblerButton_Click"/>
<Button x:Name="StirrerA1Button" Content="Rührer A1" Padding="2" Height="30" Width="120" <Button x:Name="StirrerMW1Button" Content="Rührer MW1" Padding="2" Height="30" Width="120"
FontSize="14" BorderThickness="2" FontWeight="Bold" FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="10,80,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,80,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="StirrerA1Button_Click"/> Click="StirrerMW1Button_Click"/>
<Button x:Name="StirrerA2Button" Content="Rührer A2" Padding="2" Height="30" Width="120" <Button x:Name="StirrerMW2Button" Content="Rührer MW2" Padding="2" Height="30" Width="120"
FontSize="14" BorderThickness="2" FontWeight="Bold" FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="10,115,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,115,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="StirrerA2Button_Click"/> Click="StirrerMW2Button_Click"/>
<Button x:Name="BeltButton" Content="Förderband" Padding="2" Height="30" Width="150"
FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="135,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="BeltButton_Click"/>
<Button x:Name="Auger1Button" Content="Förderschnecke 1" Padding="2" Height="30" Width="150" <Button x:Name="Auger1Button" Content="Förderschnecke 1" Padding="2" Height="30" Width="150"
FontSize="14" BorderThickness="2" FontWeight="Bold" FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="135,45,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,80,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="Auger2Button_Click"/> Click="Auger2Button_Click"/>
<Button x:Name="GradationPumpButton" Content="Gradationspumpe" 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"
Margin="135,115,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="135,115,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="GradationPumpButton_Click"/> Click="P6Button_Click"/>
<Button x:Name="MP1BackwardButton" Content="Zurück" Padding="2" Height="30" Width="60"
<Button x:Name="MP1ForwardButton" Content="Zurück" Padding="2" Height="30" Width="60"
FontSize="14" BorderThickness="2" FontWeight="Bold" FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="290,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="290,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="MP1ForwardButton_Click"/> Click="MP1BackwardButton_Click"/>
<Button x:Name="MP1HaltButton" Content="MP1" Padding="2" Height="30" Width="60" <Button x:Name="MP1HaltButton" Content="MP1" Padding="2" Height="30" Width="60"
FontSize="14" BorderThickness="2" FontWeight="Bold" FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="350,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="350,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="MP1HaltButton_Click"/> Click="MP1HaltButton_Click"/>
<Button x:Name="MP1BackwardButton" Content="Vor" Padding="2" Height="30" Width="60" <Button x:Name="MP1ForwardButton" Content="Vor" Padding="2" Height="30" Width="60"
FontSize="14" BorderThickness="2" FontWeight="Bold" FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="410,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="410,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="MP1BackwardButton_Click"/> Click="MP1ForwardButton_Click"/>
<Button x:Name="MP2ForwardButton" Content="Zurück" Padding="2" Height="30" Width="60" <Button x:Name="MP2BackwardButton" Content="Zurück" Padding="2" Height="30" Width="60"
FontSize="14" BorderThickness="2" FontWeight="Bold" FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="290,45,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="290,45,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="MP2ForwardButton_Click"/> Click="MP2BackwardButton_Click"/>
<Button x:Name="MP2HaltButton" Content="MP2" Padding="2" Height="30" Width="60" <Button x:Name="MP2HaltButton" Content="MP2" Padding="2" Height="30" Width="60"
FontSize="14" BorderThickness="2" FontWeight="Bold" FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="350,45,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="350,45,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="MP2HaltButton_Click"/> Click="MP2HaltButton_Click"/>
<Button x:Name="MP2BackwardButton" Content="Vor" Padding="2" Height="30" Width="60" <Button x:Name="MP2ForwardButton" Content="Vor" Padding="2" Height="30" Width="60"
FontSize="14" BorderThickness="2" FontWeight="Bold" FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="410,45,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="410,45,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="MP2BackwardButton_Click"/> Click="MP2ForwardButton_Click"/>
<Button x:Name="P12Button" Content="Pumpe 1/2" Padding="2" Height="30" Width="180" <Button x:Name="P12Button" Content="Pumpe 1/2" Padding="2" Height="30" Width="87.5"
FontSize="14" BorderThickness="2" FontWeight="Bold" FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="290,80,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="290,80,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="P12Button_Click"/> Click="P12Button_Click"/>
<Button x:Name="P3Button" Content="Pumpe 3" Padding="2" Height="30" Width="87.5"
FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="382.5,80,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="P3Button_Click"/>
<Button x:Name="P4Button" Content="Pumpe 4" Padding="2" Height="30" Width="87.5"
FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="290,115,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="P4Button_Click"/>
<Button x:Name="P5Button" Content="Pumpe 5" Padding="2" Height="30" Width="87.5"
FontSize="14" BorderThickness="2" FontWeight="Bold"
Margin="382.5,115,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Click="P5Button_Click"/>
</Grid> </Grid>
</Grid> </Grid>
</Window> </Window>
@@ -0,0 +1,362 @@
using PamhagenSysCtrl.Helpers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace PamhagenSysCtrl {
public partial class ManualControlWindow : Window {
public ManualControlWindow() {
InitializeComponent();
for (int i = 1; i <= 58; i++) {
var b = new Button() {
Content = $"V{i}",
Margin = new(10 + ((i - 1) / 10) * 50, 10 + ((i - 1) % 10) * 40, 10, 10),
BorderThickness = new(2),
FontWeight = FontWeights.Bold,
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
Height = 30,
Width = 40,
FontSize = 14,
};
var borderStyle = new Style(typeof(Border));
borderStyle.Setters.Add(new Setter(Border.CornerRadiusProperty, new CornerRadius(5)));
b.Resources.Add(typeof(Border), borderStyle);
b.Click += (sender, evt) => {
if (App.Plant == null || sender is not Button b) return;
var i = int.Parse($"{b.Content}"[1..]);
if (i == 58) {
if (App.Plant.WantMW1Selected) {
App.Plant.SelectMW2();
} else {
App.Plant.SelectMW1();
}
} else if (i > 57) {
return;
} else if (App.Plant.WantValveOpen(i)) {
App.Plant.CloseV(i);
} else {
App.Plant.OpenV(i);
}
};
ValvePanel.Children.Add(b);
}
App.Plant?.Update += OnUpdate;
}
public void OnUpdate(object? sender, PlantEventArgs evt) {
if (sender is not PamhagenPlant plant) return;
for (int i = 1; i <= 58; i++) {
var target = plant.WantValveClosed(i);
var actual = plant.IsValveClosed(i);
if (ValvePanel.Children[i - 1] is not Button b)
continue;
b.BorderBrush = target ? Brushes.DarkRed : Brushes.DarkGreen;
b.Foreground = target ? Brushes.DarkRed : Brushes.DarkGreen;
b.Background = actual ? Brushes.MistyRose : Brushes.MintCream;
}
ReblerButton.BorderBrush = plant.IsReblerActive ? Brushes.DarkGreen : Brushes.DarkRed;
ReblerButton.Foreground = plant.IsReblerActive ? Brushes.DarkGreen : Brushes.DarkRed;
ReblerButton.Background = plant.IsReblerActive ? Brushes.MintCream: Brushes.MistyRose;
TroughAugerButton.BorderBrush = plant.IsTroughAugerActive ? Brushes.DarkGreen : Brushes.DarkRed;
TroughAugerButton.Foreground = plant.IsTroughAugerActive ? Brushes.DarkGreen : Brushes.DarkRed;
TroughAugerButton.Background = plant.IsTroughAugerActive ? Brushes.MintCream : Brushes.MistyRose;
StirrerMW1Button.BorderBrush = plant.IsStirrerMW1Active ? Brushes.DarkGreen : Brushes.DarkRed;
StirrerMW1Button.Foreground = plant.IsStirrerMW1Active ? Brushes.DarkGreen : Brushes.DarkRed;
StirrerMW1Button.Background = plant.IsStirrerMW1Active ? Brushes.MintCream : Brushes.MistyRose;
StirrerMW2Button.BorderBrush = plant.IsStirrerMW2Active ? Brushes.DarkGreen : Brushes.DarkRed;
StirrerMW2Button.Foreground = plant.IsStirrerMW2Active ? Brushes.DarkGreen : Brushes.DarkRed;
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.Foreground = plant.IsAuger1Active ? Brushes.DarkGreen : Brushes.DarkRed;
Auger1Button.Background = plant.IsAuger1Active ? Brushes.MintCream : Brushes.MistyRose;
Auger2Button.BorderBrush = plant.IsAuger2Active ? Brushes.DarkGreen : Brushes.DarkRed;
Auger2Button.Foreground = plant.IsAuger2Active ? Brushes.DarkGreen : Brushes.DarkRed;
Auger2Button.Background = plant.IsAuger2Active ? Brushes.MintCream : Brushes.MistyRose;
MP1BackwardButton.BorderBrush = 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;
MP1HaltButton.BorderBrush = plant.MP1 == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray;
MP1HaltButton.Foreground = plant.MP1 == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray;
MP1HaltButton.Background = plant.MP1 == MotorState.Halt ? Brushes.MistyRose : Brushes.WhiteSmoke;
MP1ForwardButton.BorderBrush = plant.MP1 == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray;
MP1ForwardButton.Foreground = plant.MP1 == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray;
MP1ForwardButton.Background = plant.MP1 == MotorState.Forward ? Brushes.MintCream: Brushes.WhiteSmoke;
MP2BackwardButton.BorderBrush = plant.MP2 == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray;
MP2BackwardButton.Foreground = plant.MP2 == MotorState.Backward ? Brushes.DarkGreen : Brushes.SlateGray;
MP2BackwardButton.Background = plant.MP2 == MotorState.Backward ? Brushes.MintCream : Brushes.WhiteSmoke;
MP2HaltButton.BorderBrush = plant.MP2 == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray;
MP2HaltButton.Foreground = plant.MP2 == MotorState.Halt ? Brushes.DarkRed : Brushes.SlateGray;
MP2HaltButton.Background = plant.MP2 == MotorState.Halt ? Brushes.MistyRose : Brushes.WhiteSmoke;
MP2ForwardButton.BorderBrush = plant.MP2 == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray;
MP2ForwardButton.Foreground = plant.MP2 == MotorState.Forward ? Brushes.DarkGreen : Brushes.SlateGray;
MP2ForwardButton.Background = plant.MP2 == MotorState.Forward ? Brushes.MintCream : Brushes.WhiteSmoke;
P12Button.BorderBrush = plant.P12 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed;
P12Button.Foreground = plant.P12 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed;
P12Button.Background = plant.P12 == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose;
P3Button.BorderBrush = plant.P3 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed;
P3Button.Foreground = plant.P3 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed;
P3Button.Background = plant.P3 == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose;
P4Button.BorderBrush = plant.P4 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed;
P4Button.Foreground = plant.P4 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed;
P4Button.Background = plant.P4 == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose;
P5Button.BorderBrush = plant.P5 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed;
P5Button.Foreground = plant.P5 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed;
P5Button.Background = plant.P5 == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose;
P6Button.BorderBrush = plant.P6 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed;
P6Button.Foreground = plant.P6 == MotorState.Forward ? Brushes.DarkGreen : Brushes.DarkRed;
P6Button.Background = plant.P6 == MotorState.Forward ? Brushes.MintCream : Brushes.MistyRose;
Status.Text = $"""
Gradation °Oe: {plant.GradationOe:N2}
Presse Querschnecke: {plant.PresseQuerSchnecke}
Füllstände:
Maischewanne 1: {plant.FillLevelMW1} (ausgewählt: {plant.WantMW1Selected}, Status: {plant.IsMWSelected})
Maischewanne 2: {plant.FillLevelMW2} (ausgewählt: {plant.WantMW2Selected}, Status: {plant.IsMW2Selected})
Presse 1: {plant.FillLevelPress1}
Presse 2: {plant.FillLevelPress2}
Tank 1: {plant.FillLevelT1}
Tank 2: {plant.FillLevelT2}
Tank 3: {plant.FillLevelT3}
Tank 4: {plant.FillLevelT4}
Tank 5: {plant.FillLevelT5}
Tank 6: {plant.FillLevelT6}
Tank 7: {plant.FillLevelT7}
Tank 8: {plant.FillLevelT8}
Tank 9: {plant.FillLevelT9}
""";
}
private void TroughAugerButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
if (App.Plant.IsTroughAugerActive) {
App.Plant.StopTroughAuger();
} else {
App.Plant.StartTroughAuger();
}
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void ReblerButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
if (App.Plant.IsReblerActive) {
App.Plant.StopRebler();
} else {
App.Plant.StartRebler();
}
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void StirrerMW1Button_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
if (App.Plant.IsStirrerMW1Active) {
App.Plant.StopStirrerMW1();
} else {
App.Plant.StartStirrerMW1();
}
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void StirrerMW2Button_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
if (App.Plant.IsStirrerMW2Active) {
App.Plant.StopStirrerMW2();
} else {
App.Plant.StartStirrerMW2();
}
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
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) {
if (App.Plant == null) return;
try {
if (App.Plant.IsAuger1Active) {
App.Plant.StopAuger1();
} else {
App.Plant.StartAuger1();
}
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void Auger2Button_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
if (App.Plant.IsAuger2Active) {
App.Plant.StopAuger2();
} else {
App.Plant.StartAuger2();
}
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void MP1BackwardButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
App.Plant.StartMP1Backward();
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void MP1HaltButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
App.Plant.StopMP1();
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void MP1ForwardButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
App.Plant.StartMP1Forward();
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void MP2BackwardButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
App.Plant.StartMP2Backward();
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void MP2HaltButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
App.Plant.StopMP2();
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void MP2ForwardButton_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
App.Plant.StartMP2Forward();
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void P12Button_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
if (App.Plant.P12 == MotorState.Forward) {
App.Plant.StopP12();
} else {
App.Plant.StartP12();
}
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void P3Button_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
if (App.Plant.P3 == MotorState.Forward) {
App.Plant.StopP3();
} else {
App.Plant.StartP3();
}
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void P4Button_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
if (App.Plant.P4 == MotorState.Forward) {
App.Plant.StopP4();
} else {
App.Plant.StartP4();
}
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void P5Button_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
if (App.Plant.P5 == MotorState.Forward) {
App.Plant.StopP5();
} else {
App.Plant.StartP5();
}
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void P6Button_Click(object? sender, RoutedEventArgs? evt) {
if (App.Plant == null) return;
try {
if (App.Plant.P6 == MotorState.Forward) {
App.Plant.StopP6();
} else {
App.Plant.StartP6();
}
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
}
+43 -2
View File
@@ -4,10 +4,51 @@
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"
Title="Schema - Anlagensteuerung Pamhagen" Height="900" Width="1600" MinWidth="1500" MinHeight="900"> Title="Anlagensteuerung Pamhagen" Height="950" Width="1600" MinWidth="1500" MinHeight="950">
<Grid> <Grid>
<Grid.RowDefinitions>
<RowDefinition Height="19"/>
<RowDefinition Height="*"/>
<RowDefinition Height="24"/>
</Grid.RowDefinitions>
<Menu Grid.Row="0" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Einstellungen">
<MenuItem x:Name="Menu_Settings_ManualControl" Header="Manuelle Steuerung" Click="Menu_Settings_ManualControl_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xF271;"/>
</MenuItem.Icon>
</MenuItem>
<Separator/>
<MenuItem x:Name="Menu_Settings_Config" Header="Konfigurationsdatei öffnen..." Click="Menu_Settings_Config_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEC7A;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_Settings_Directory" Header="Konfigurationsspeicherort öffnen..." Click="Menu_Settings_Directory_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xED25;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="Hilfe">
<MenuItem Header="Über">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE946;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
<Grid Grid.Row="1" ClipToBounds="True">
<Canvas x:Name="SchemeCanvas" Width="1550" Height="900" VerticalAlignment="Center" HorizontalAlignment="Center" SnapsToDevicePixels="True" <Canvas x:Name="SchemeCanvas" Width="1550" Height="900" VerticalAlignment="Center" HorizontalAlignment="Center" SnapsToDevicePixels="True"
MouseMove="SchemeCanvas_MouseMove" MouseLeftButtonDown="SchemeCanvas_MouseLeftButtonDown" MouseLeftButtonUp="SchemeCanvas_MouseLeftButtonUp"> MouseMove="SchemeCanvas_MouseMove" MouseLeftButtonDown="SchemeCanvas_MouseLeftButtonDown" MouseLeftButtonUp="SchemeCanvas_MouseLeftButtonUp" Grid.ColumnSpan="2" Margin="25,0,0,0">
</Canvas> </Canvas>
</Grid> </Grid>
<StatusBar Grid.Row="2" BorderThickness="0,1,0,0" BorderBrush="Gray">
</StatusBar>
</Grid>
</Window> </Window>
+132 -15
View File
@@ -1,5 +1,6 @@
using PamhagenSysCtrl.Helpers; using PamhagenSysCtrl.Helpers;
using PamhagenSysCtrl.Helpers.Pipeline; using PamhagenSysCtrl.Helpers.Pipeline;
using System.Diagnostics;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
@@ -11,6 +12,7 @@ namespace PamhagenSysCtrl.Windows {
private ISource? _lastSource; private ISource? _lastSource;
private List<INode> _path = []; private List<INode> _path = [];
private ISet<INode>? _lastSelected; private ISet<INode>? _lastSelected;
private ISet<INode>? _hover;
public PlantSchemeWindow() { public PlantSchemeWindow() {
InitializeComponent(); InitializeComponent();
@@ -19,6 +21,28 @@ namespace PamhagenSysCtrl.Windows {
Graph.Draw(SchemeCanvas); Graph.Draw(SchemeCanvas);
} }
private void Menu_Settings_ManualControl_Click(object sender, RoutedEventArgs evt) {
var w = new ManualControlWindow();
w.Show();
}
private void Menu_Settings_Directory_Click(object sender, RoutedEventArgs evt) {
try {
Process.Start("explorer.exe", App.DataPath);
} catch { }
}
private void Menu_Settings_Config_Click(object sender, RoutedEventArgs evt) {
try {
Process.Start(new ProcessStartInfo {
FileName = "notepad.exe",
Arguments = App.ConfigPath,
Verb = "runas",
UseShellExecute = true,
});
} catch { }
}
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);
@@ -39,7 +63,9 @@ namespace PamhagenSysCtrl.Windows {
foreach (var c in clicked) { foreach (var c in clicked) {
if (_lastSource == null && Graph.SelectedPaths.Any(p => p.Start == c)) { if (_lastSource == null && Graph.SelectedPaths.Any(p => p.Start == c)) {
var path = Graph.SelectedPaths.First(p => p.Start == c); var path = Graph.SelectedPaths.First(p => p.Start == c);
if (!path.Hops.Any(h => (h.Node as Pump)?.IsActive ?? false)) {
Graph.RemovePath(path); Graph.RemovePath(path);
}
} else if (_lastSource != null && c is ISink sink) { } 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)); var res = Graph.GetPath([_lastSource, .._path, sink], (n) => n is not ISink && (n is not Valve v || !v.IsLocked));
if (res is Path path) { if (res is Path path) {
@@ -57,14 +83,92 @@ namespace PamhagenSysCtrl.Windows {
} else { } else {
_lastSource = null; _lastSource = null;
} }
try {
if (c is EntryTrough trough) { if (c is EntryTrough trough) {
trough.IsActive = !trough.IsActive; if (trough.IsActive) {
App.Plant?.StopTroughAuger();
} else {
App.Plant?.StartTroughAuger();
}
} else if (c is Rebler rebler) { } else if (c is Rebler rebler) {
rebler.IsActive = !rebler.IsActive; if (rebler.IsActive) {
App.Plant?.StopRebler();
} else {
App.Plant?.StartRebler();
}
} else if (c is Switcher switcher) {
if (App.Plant?.WantMW1Selected ?? false) {
App.Plant?.SelectMW2();
} else {
App.Plant?.SelectMW1();
}
} else if (c is EncasedAuger auger) { } else if (c is EncasedAuger auger) {
auger.IsActive = !auger.IsActive; if (auger == Graph.Auger1) {
if (auger.IsActive) {
App.Plant?.StopAuger1();
} else {
App.Plant?.StartAuger1();
}
} else if (auger == Graph.Auger2) {
if (auger.IsActive) {
App.Plant?.StopAuger2();
} else {
App.Plant?.StartAuger2();
}
}
} else if (c is ConveyorBelt belt) { } else if (c is ConveyorBelt belt) {
belt.IsActive = !belt.IsActive; if (belt.IsActive) {
App.Plant?.StopBelt();
} else {
App.Plant?.StartBelt();
}
} else if (c is Pump pump) {
if (pump == Graph.MP1) {
if (pump.IsActive) {
App.Plant?.StopMP1();
} else {
App.Plant?.StartMP1Forward();
}
} else if (pump == Graph.MP2) {
if (pump.IsActive) {
App.Plant?.StopMP2();
} else {
App.Plant?.StartMP2Forward();
}
} else if (pump == Graph.P12) {
if (pump.IsActive) {
App.Plant?.StopP12();
} else {
App.Plant?.StartP12();
}
} else if (pump == Graph.P3) {
if (pump.IsActive) {
App.Plant?.StopP3();
} else {
App.Plant?.StartP3();
}
} else if (pump == Graph.P4) {
if (pump.IsActive) {
App.Plant?.StopP4();
} else {
App.Plant?.StartP4();
}
} else if (pump == Graph.P5) {
if (pump.IsActive) {
App.Plant?.StopP5();
} else {
App.Plant?.StartP5();
}
} else if (pump == Graph.P6) {
if (pump.IsActive) {
App.Plant?.StopP6();
} else {
App.Plant?.StartP6();
}
}
}
} catch (NoClearanceException exc) {
MessageBox.Show($"{exc.Message}", "Keine Freigabe", MessageBoxButton.OK, MessageBoxImage.Error);
} }
} }
UpdateScheme(pos, evt.LeftButton == MouseButtonState.Pressed); UpdateScheme(pos, evt.LeftButton == MouseButtonState.Pressed);
@@ -75,14 +179,19 @@ namespace PamhagenSysCtrl.Windows {
e.Highlight = null; e.Highlight = null;
e.Flow = null; e.Flow = null;
} }
foreach (var p in Graph.SelectedPaths) { foreach (var n in Graph.Nodes) {
Graph.ColorPipesAdjacientToPath(p, PamhagenBrushes.Green, true, PamhagenBrushes.DimOrange); if (n is Pump p) {
p.Highlight = null;
} }
var hover = Graph.GetHover(pos.X, pos.Y); }
if (_lastSource != null && (hover.Count > 0 || _path.Count > 0)) { 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);
if (_lastSource != null && (_hover.Count > 0 || _path.Count > 0)) {
List<INode> points = [_lastSource, .. _path]; List<INode> points = [_lastSource, .. _path];
if (hover.Count > 0 && !points.Contains(hover.First())) { if (_hover.Count > 0 && !points.Contains(_hover.First())) {
points.Add(hover.First()); points.Add(_hover.First());
} }
if (points.Count >= 2) { if (points.Count >= 2) {
var res = Graph.GetPath(points, (n) => n is not ISink && (n is not Valve v || !v.IsLocked)); var res = Graph.GetPath(points, (n) => n is not ISink && (n is not Valve v || !v.IsLocked));
@@ -90,21 +199,29 @@ namespace PamhagenSysCtrl.Windows {
Graph.ColorPipesAdjacientToPath(path, PamhagenBrushes.Green, false, PamhagenBrushes.DimOrange); Graph.ColorPipesAdjacientToPath(path, PamhagenBrushes.Green, false, PamhagenBrushes.DimOrange);
} }
} }
} else if (_lastSource == null && hover.Count > 0 && Graph.SelectedPaths.Any(p => p.Start == hover.First())) { } 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 path = Graph.SelectedPaths.First(p => p.Start == _hover.First());
Graph.ColorPipesAdjacientToPath(path, PamhagenBrushes.Red, true, null); 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, false, null);
}
} }
if (_lastSource != null) { if (_lastSource != null) {
foreach (var edge in _lastSource.Outputs) { foreach (var edge in _lastSource.Outputs) {
edge.Highlight = PamhagenBrushes.Green; 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);
} }
} }
} }