Merge branch 'Dev_Picking'

# Conflicts:
#	Common/.shared/SharedAssemblyInfo.cs
#	Common/Production/ProductionUiCordonel/App.config
#	Common/Production/ProductionUiCordonel/App.xaml.cs
#	Common/Production/ProductionUiCordonel/ProductionUiCordonel.csproj
#	Common/Production/ProductionUiCordonel/ProductionWindow.xaml
#	Common/Production/ProductionUiCordonel/ProductionWindow.xaml.cs
This commit is contained in:
Thomas Wiedebusch
2025-05-27 10:46:18 +02:00
81 changed files with 4168 additions and 10011 deletions
@@ -34,11 +34,11 @@
<appSettings>
<add key="Mode" value="1" /> <!--1 = EOL ; 2 = Picking-->
<add key="SirtComport433" value="14" /> <!--Individual for Thomas-->
<add key="SirtComport868" value="15" /> <!--Individual for Thomas-->
<add key="SirtBoxNr" value="2" /> <!--Roland and Thomas, box for development, box at shipping station is 1-->
<add key="StationID" value="2" /> <!--Roland and Thomas, shipping station is 1-->
<add key="Mode" value="2" /> <!--1 = EOL ; 2 = Picking-->
<add key="SirtComport433" value="14" /> <!--Individual for Thomas-->
<add key="SirtComport868" value="15" /> <!--Individual for Thomas-->
<add key="SirtBoxNr" value="2" /> <!--Roland and Thomas, box for development, box at shipping station is 1-->
<add key="StationID" value="1" /> <!--Roland and Thomas, shipping station is 1-->
</appSettings>
@@ -1,6 +1,8 @@
using System;
using System.Configuration;
using System.Windows;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.PickingProcesses;
namespace Xylem.Common.Production.ProductionUiCordonel
{
@@ -11,24 +13,29 @@ namespace Xylem.Common.Production.ProductionUiCordonel
{
private void Application_Startup(Object sender, StartupEventArgs e)
{
switch (ConfigurationManager.AppSettings[$"Mode"])
switch (ConfigurationManager.AppSettings["Mode"])
{
case "1":
var mainWindow = new FinalTest();
mainWindow.Show();
var ProductionWindow = new ProductionWindow(
ProductionUiCordonel.Properties.Resources.StrEolSoftwareNameVersion,
"ProductionUiCordonel", new EolProcessStateDef());
ProductionWindow.Show();
break;
//case "2":
// var Picking = new Picking();
// Picking.Show();
// break;
case "2":
ProductionWindow = new ProductionWindow(
ProductionUiCordonel.Properties.Resources.StrPickingSoftwareNameVersion,
"PickingUiCordonel", new PickingProcessStateDef());
ProductionWindow.Show();
break;
default:
var mainWindowDef = new FinalTest();
mainWindowDef.Show();
ProductionWindow = new ProductionWindow(
ProductionUiCordonel.Properties.Resources.StrEolSoftwareNameVersion,
"ProductionUiCordonel", new EolProcessStateDef());
ProductionWindow.Show();
break;
}
}
}
}
@@ -1,78 +0,0 @@
<Window x:Class="Xylem.Common.Production.ProductionUiCordonel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:productionUiCordonel="clr-namespace:Xylem.Common.Production.ProductionUiCordonel"
mc:Ignorable="d"
Title="Cordonel Production UI" MinHeight="500" MinWidth="400" Height="600" Width="644" Initialized="Window_Initialized" Closing="Window_Closing">
<DockPanel HorizontalAlignment="Right" Width="634" Height="569" VerticalAlignment="Top" Margin="0,0,2,0">
<ToolBarTray DockPanel.Dock="Top">
<ToolBar>
<Label>Slot:</Label>
<ComboBox Name="cbxSlotBox" Margin="0,5,0,0" VerticalAlignment="Top" SelectionChanged="CbxSlotBox_SelectionChanged" />
<Button Name="BtnConntect" VerticalAlignment="Top" Click="BtnConnect_Click" HorizontalAlignment="Right" ToolTip="Force connect to meter">
<StackPanel Orientation="Horizontal">
<Image Source="/Resources/Connect_16x.png" />
<TextBlock Margin="3,0,0,0">Connect</TextBlock>
</StackPanel>
</Button>
</ToolBar>
<ToolBar Name="tbarOrder">
<Label>Order:</Label>
<TextBox Name="txtOrderNr" HorizontalAlignment="Left" Height="26" IsEnabled="False" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="81" />
</ToolBar>
<ToolBar>
<Label>Autodetect:</Label>
<Button Name="BtnSwitchAutoDetect" VerticalAlignment="Top" Click="BtnSwitchAutoDetect_Click" HorizontalAlignment="Right" >
<StackPanel Orientation="Horizontal">
<Image Source="/Resources/Refresh_16x.png" />
<TextBlock Name="tblkAutodetectState" Text="Off" Margin="3,0,0,0"/>
</StackPanel>
</Button>
</ToolBar>
</ToolBarTray>
<StatusBar DockPanel.Dock="Bottom">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem Grid.Column="0">
<Label Content="PcbID:"/>
</StatusBarItem>
<StatusBarItem Grid.Column="1">
<Label Name="lblPcbID" Content="-"/>
</StatusBarItem>
<Separator Grid.Column="2" />
<StatusBarItem Grid.Column="3">
<TextBlock Name="lblMessage" Text="Init" />
</StatusBarItem>
<Separator Grid.Column="4" />
<StatusBarItem Grid.Column="5">
<Label x:Name="lblState" Content="" HorizontalAlignment="Left" Height="Auto" VerticalAlignment="Top" />
</StatusBarItem>
</StatusBar>
<StackPanel Background="#FFE5E5E5" SizeChanged="StackPanel_SizeChanged" Height="506" VerticalAlignment="Top" HorizontalAlignment="Stretch" Width="625" >
<productionUiCordonel:FinalTest x:Name="ucFinalRadioTest" Loaded="UcFinalRadioTest_Loaded" Width="Auto" VerticalAlignment="Top" HorizontalAlignment="Stretch" />
</StackPanel>
</DockPanel>
</Window>
@@ -1,451 +0,0 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using Newtonsoft.Json;
using Xylem.Common.CommonCore.Configuration;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Logic.SoftwareAccessHelper;
using Xylem.Common.Production.ProductionUiCordonel.Model;
namespace Xylem.Common.Production.ProductionUiCordonel
{
//public class QuantityToBackgroundConverter : IValueConverter
//{
// public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
// {
// var dc = DisplayCodes.None;
// if (DisplayCodes.TryParse(value.ToString(), out dc))
// {
// string hex = "";
// int intValue;
// if (int.TryParse(value.ToString(), out intValue))
// {
// hex = intValue.ToString("X");
// }
// return $"{dc} ({hex})";
// }
// else
// {
// return "?";
// }
// }
// public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
// {
// throw new NotImplementedException();
// }
//}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow
{
private Timer tmrAutoDetect;
public EventHandler OnPcbIdDetect;
private Int32 intervalWithoutDetect = 1000;
private Int32 intervalWithDetect = 10000;
private String _currentPcbID;
public PickingViewModel PickingData = new PickingViewModel();
public String CurrentPcbID
{
set
{
if (_currentPcbID != value && !(string.IsNullOrEmpty(_currentPcbID) && value == null))
{
if (_currentPcbID != value)
{
if (!string.IsNullOrEmpty(_currentPcbID) && string.IsNullOrEmpty(value))
{
_currentPcbID = value;
updateUi(lblPcbID, _currentPcbID);
}
else
{
_currentPcbID = value;
updateUi(lblPcbID, _currentPcbID);
if (!string.IsNullOrEmpty(_currentPcbID))
{
GetPcbMapping(_currentPcbID);
}
}
}
OnPcbIdDetect?.Invoke(CurrentPcbID, EventArgs.Empty);
}
}
get => _currentPcbID;
}
public enum uiState
{
not_connected,
connected
}
private uiState _currentState;
public uiState CurrentState
{
set
{
_currentState = value;
updateUi(lblState, _currentState.ToString());
}
get => _currentState;
}
private String _lastMsg;
public String LastMsg
{
set
{
_lastMsg = value;
updateUi(lblMessage, _lastMsg);
}
get => _lastMsg;
}
public MainWindow()
{
try
{
if (true)
{
}
InitializeComponent();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace);
}
}
public ApplicationSettings Settings { get; set; }
private void Window_Initialized(Object sender, EventArgs e)
{
var configfile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
nameof(Hardware.WaterMeter.Genesis), ProgramConfig.SerialConfigFileName);
if (!File.Exists(configfile))
{
throw new ApplicationException($"Configuration file {configfile} not found ");
}
var tr = new StreamReader(configfile);
var meterConfigList = JsonConvert.DeserializeObject<SlotConfig[]>(tr.ReadToEnd());
cbxSlotBox.Items.Clear();
foreach (var item in meterConfigList.OrderBy(s => s.Slot))
{
cbxSlotBox.Items.Add(item.Slot);
}
cbxSlotBox.SelectedIndex = 0;
tmrAutoDetect = new Timer(intervalWithoutDetect);
tmrAutoDetect.Elapsed += TmrAutoDetect_Elapsed;
tmrAutoDetect.Enabled = false;
Settings = new ApplicationSettings("ProductionUiConfig.json");
useSetting(true);
}
private void TmrAutoDetect_Elapsed(Object sender, ElapsedEventArgs e)
{
tmrAutoDetect.Enabled = false;
var hasDetect = Detect(true, 1);
if (hasDetect)
{
tmrAutoDetect.Interval = intervalWithDetect;
}
else
{
tmrAutoDetect.Interval = intervalWithoutDetect;
}
tmrAutoDetect.Enabled = Settings.AutoDetect;
}
private void BtnConnect_Click(Object sender, RoutedEventArgs e)
{
var hasDetect = Detect(false, (Int32)cbxSlotBox.SelectedValue);
Settings.Slot = (Int32)cbxSlotBox.SelectedValue;
storeSetting();
}
private void updateUi(ContentControl c, String content)
{
c.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() => { c.Content = content; }));
}
private void updateUi(TextBox c, String content)
{
c.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() => { c.Text = content; }));
}
private void updateUi(TextBlock c, String content)
{
c.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() => { c.Text = content; }));
}
private MeterBatch CurrentBatch;
private IGenesisMeter _currentMeter;
private Boolean detectIsLocked;
private IGenesisMeter CurrentMeter
{
get => _currentMeter;
set
{
_currentMeter = value;
if (ucFinalRadioTest != null)
{
if (_currentMeter?.PcbId != null)
{
// ucFinalRadioTest.GenesisMeter = value;
}
else
{
// ucFinalRadioTest.GenesisMeter = null;
}
}
}
}
private Boolean Detect(Boolean ignoreErrors, Int32 slot)
{
if (!detectIsLocked)
{
LastMsg = "Start Detect";
Boolean hasDetect = false;
if (string.IsNullOrEmpty(CurrentPcbID) || CurrentMeter == null)
{
var mb = new MeterBatch();
var tryMeter = new GenesisMeter();
try
{
tryMeter.SetupFromConfigFile(slot);
mb.AddMeter(tryMeter);
tryMeter.Logout();
LastMsg = "Try to get PcbID";
CurrentPcbID = tryMeter.GetPcbId();
hasDetect = !string.IsNullOrEmpty(CurrentPcbID);
CurrentBatch = mb;
CurrentMeter = tryMeter;
if (hasDetect)
{
LastMsg = $"New PcbID detected {CurrentPcbID}";
CurrentMeter.Login();
}
else
{
LastMsg = "No Meter found";
DisposeAllConnections();
}
}
catch (Exception e)
{
if (!ignoreErrors)
{
MessageBox.Show($"Error occur.{ Environment.NewLine} {e.Message}");
}
}
}
else
{
LastMsg = "Check if Meter is still connected";
var ret = CurrentMeter.GetPcbId();
if (string.IsNullOrEmpty(ret) || ret != CurrentPcbID)
{
LastMsg = "Meter connection lost";
hasDetect = false;
DisposeAllConnections();
CurrentPcbID = "";
}
else
{
LastMsg = "Meter is still connected";
hasDetect = true;
}
}
return hasDetect;
}
LastMsg = "Meter is still connected (locked)";
return true;
}
private void Window_Closing(Object sender, CancelEventArgs e)
{
DisposeAllConnections();
}
private void DisposeAllConnections()
{
if (CurrentBatch != null)
{
CurrentBatch.RemoveAllMeters();
if (CurrentMeter != null)
{
CurrentMeter.Dispose();
}
CurrentBatch.Dispose();
}
PickingData.Empty();
}
private void GetPcbMapping(String pcbID)
{
if (string.IsNullOrEmpty(pcbID))
{
return;
}
var url = $"{ServiceUrls.MarriageServiceUrl()}GetSerialNumberAndOrderNumber?PcbId={pcbID}&withRadioAdress=true";
Task.Run(() =>
{
try
{
var responseJson = LocalWebRequest.GetRequest(url);
var a = JsonConvert.DeserializeObject<String[]>(responseJson);
if (ucFinalRadioTest != null)
{
var tmpInt = 0;
int.TryParse(a[1], out tmpInt);
// ucFinalRadioTest.OrderNumber = tmpInt;
tmpInt = 0;
if (a.Length > 2)
{
int.TryParse(a[2], out tmpInt);
}
//ucFinalRadioTest.RadioAddress = tmpInt;
// ucFinalRadioTest.SerialNumber = a[0] + "/" + a[3];
}
}
catch (Exception ex)
{
if (ex is WebException)
{
MessageBox.Show($"Error on Store: {Environment.NewLine} { ((WebException)ex).Message}");
}
MessageBox.Show(ex.Message);
}
});
}
private void Button_Click(Object sender, RoutedEventArgs e)
{
}
private void UcFinalRadioTest_OnLockMeter(Object sender, EventArgs e)
{
detectIsLocked = true;
}
private void UcFinalRadioTestOnOnReleaseMeter(Object sender, EventArgs e)
{
detectIsLocked = false;
//ucFinalRadioTest.SetUpBlank(false);
}
private void BtnSwitchAutoDetect_Click(Object sender, RoutedEventArgs e)
{
Settings.AutoDetect = !Settings.AutoDetect;
storeSetting();
useSetting();
}
private void CbxSlotBox_SelectionChanged(Object sender, SelectionChangedEventArgs e)
{
}
private void storeSetting()
{
Settings.Update("ProductionUiConfig.json");
}
private void useSetting(Boolean setSlot = false)
{
tmrAutoDetect.Enabled = Settings.AutoDetect;
tblkAutodetectState.Text = "Off";
if (Settings.AutoDetect)
{
tblkAutodetectState.Text = "On";
}
if (setSlot)
{
cbxSlotBox.SelectedValue = Settings.Slot;
}
}
private void StackPanel_SizeChanged(Object sender, SizeChangedEventArgs e)
{
ucFinalRadioTest.Height = e.NewSize.Height;
}
}
}
@@ -1,152 +0,0 @@
<Window x:Class="Xylem.Common.Production.ProductionUiCordonel.MultiMode"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MultiMode" Height="322.026" Width="660.69" Loaded="Window_Loaded" Closed="Window_Closed">
<Grid Margin="0,0,28,0">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="238" MinHeight="150"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition MinHeight="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="125"/>
<ColumnDefinition Width="125"/>
<ColumnDefinition Width="125"/>
<ColumnDefinition Width="125"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Background="{Binding PickingResultColor}" Content="Kommissionierung" HorizontalAlignment="Left" Margin="10,1,0,0" VerticalAlignment="Top" Grid.Row="0" Grid.Column="0" Height="26" Width="115" />
<Label Background="{Binding PickingResultColor}" Content="Scanner:" HorizontalAlignment="Left" Margin="10,1,0,0" VerticalAlignment="Top" Grid.Row="1" Grid.Column="0" Height="26" Width="115" />
<Label Background="{Binding PickingResultColor}" Content="{Binding PickingScanner, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="10,1,0,0" VerticalAlignment="Top" Grid.Row="1" Grid.Column="1" Height="26" Width="115" />
<Label Background="{Binding PickingResultColor}" Content="Ep:" HorizontalAlignment="Left" Margin="10,1,0,0" VerticalAlignment="Top" Grid.Row="1" Grid.Column="2" Height="26" Width="115" />
<Label Background="{Binding PickingResultColor}" Content="{Binding PickingSlot, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="10,1,0,0" VerticalAlignment="Top" Grid.Row="1" Grid.Column="3" Height="26" Width="115" />
<Label Background="{Binding PickingResultColor}" Content="Medlung:" HorizontalAlignment="Left" Margin="10,1,0,0" VerticalAlignment="Top" Grid.Row="2" Grid.Column="0" Height="26" Width="115" />
<Label Background="{Binding PickingResultColor}" Content="{Binding PickingState, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="36,1,0,0" VerticalAlignment="Top" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" Height="26" Width="332" />
<Label Background="{Binding PickingResultColor}" Content="Ergebnis:" HorizontalAlignment="Left" Margin="10,1,0,0" VerticalAlignment="Top" Grid.Row="3" Grid.Column="0" Height="26" Width="115" />
<Label Background="{Binding PickingResultColor}" Content="{Binding PickingResult, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="10,1,0,0" VerticalAlignment="Top" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="3" Height="26" Width="359" />
<!--<Label Content="Dichtigkeit" HorizontalAlignment="Left" Margin="10,1,0,0" VerticalAlignment="Top" Grid.Row="1" Grid.Column="0" Height="26" Width="105" />
<Label Content="{Binding PressureHe.Sate}" HorizontalAlignment="Left" Margin="10,1,0,0" VerticalAlignment="Top" Grid.Row="1" Grid.Column="1" Height="26" Width="105" />
<Label Content="Druck" HorizontalAlignment="Left" Margin="10,1,0,0" VerticalAlignment="Top" Grid.Row="2" Grid.Column="0" Height="26" Width="105" />
<Label Content="{Binding PressureSate}" HorizontalAlignment="Left" Margin="10,1,0,0" VerticalAlignment="Top" Grid.Row="2" Grid.Column="1" Height="26" Width="105" />
<Label Content="Verknüpfung" HorizontalAlignment="Left" Margin="10,1,0,0" VerticalAlignment="Top" Grid.Row="3" Grid.Column="0" Height="26" Width="105" />
<Label Content="{Binding MarrySate}" HorizontalAlignment="Left" Margin="10,1,0,0" VerticalAlignment="Top" Grid.Row="3" Grid.Column="1" Height="26" Width="105" />-->
<!--<Grid Grid.Row="1" Grid.Column="0" Grid.RowSpan="4">
<Grid.RowDefinitions>
<RowDefinition Height="12*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="100*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="20*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="180" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<Label x:Name="lblHeadline" Content="HE-Schnüffeln" Grid.Column="0" Grid.Row="0" FontWeight="Bold" FontSize="24" Height="47" VerticalAlignment="Top" Margin="0,0,113,0" Grid.RowSpan="2" Grid.ColumnSpan="2" />
<Label x:Name="lblLeakrate" Content="Grenz-Leckrate >" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Grid.Row="0" Grid.Column="2" Height="26" Width="123" />
<TextBox x:Name="txtLeakrate" Height="26" IsEnabled="True" TextWrapping="Wrap" Text="{Binding Result.LeakRate, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Grid.Row="0" Grid.Column="3" HorizontalAlignment="Left" Width="171" TextChanged="TextBox_TextChanged"/>
<TextBox x:Name="TxtPresurePcbId" Height="26" IsEnabled="False" TextWrapping="Wrap" Text="{Binding CurrentPcbID, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" Width="150" TextChanged="TextBox_TextChanged"/>
<Label Content="PcbId" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Grid.Row="1" Grid.Column="0" Height="26" Width="40"/>
<StackPanel Orientation="Horizontal" Grid.Row="1" Grid.Column="2">
<TextBlock Text="Rev: "/>
<Label Content="{Binding Result.Rev, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" VerticalAlignment="Top" Height="26" Width="40"/>
</StackPanel>
<Label Content="Prüfdruck in bar" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Grid.Row="2" Grid.Column="0" Height="26" Width="113" Grid.ColumnSpan="2" />
<TextBox x:Name="txtPressure" Height="26" IsEnabled="True" TextWrapping="Wrap" Text="{Binding Result.TestPressure, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Left" Width="150" TextChanged="TextBox_TextChanged"/>
<TextBox x:Name="txtTester" Height="26" IsEnabled="True" TextWrapping="Wrap" Text="{Binding Result.TesterName, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Left" Width="150" TextChanged="TextBox_TextChanged" />
<Label Content="Prüfer" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Grid.Row="3" Grid.Column="0" Height="26" Width="85" />
<ListView x:Name="listViewTestPotins" ItemsSource="{Binding Result.Testpoints, UpdateSourceTrigger=PropertyChanged}" Grid.Row="2" Grid.Column="2" Grid.RowSpan="4" HorizontalAlignment="left" Width="251" Margin="10,10,0,0" Grid.ColumnSpan="2" >
<ListView.View>
<GridView >
<GridViewColumn Header="Nr" >
<GridViewColumn.CellTemplate>
<DataTemplate >
<Label Content="{Binding Ident}" Height="22" FontWeight="Bold" FontSize="10" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Wert" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Input}" Height="22" Width="100" FontWeight="Bold" FontSize="13" TextChanged="TextBox_TextChanged" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="Für alle übernehmen" Height="22" FontWeight="Bold" FontSize="13" Click="Button_Click" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<Label Content="Bemerkung" HorizontalAlignment="Left" Margin="0,10,0,0" VerticalAlignment="Top" Height="26" Width="113" Grid.Column="0" Grid.Row="4" Grid.ColumnSpan="2" />
<TextBox Text="{Binding Result.Remark, UpdateSourceTrigger=PropertyChanged}" Margin="10,10,10,7" TextWrapping="Wrap" AcceptsReturn="True" AcceptsTab="True" SpellCheck.IsEnabled="True" SpellCheck.SpellingReform="PreAndPostreform" Grid.Row="4" Grid.Column="1" TextChanged="TextBox_TextChanged"/>
<Button Name="btnPresueOk" Background="LightGreen" Grid.ColumnSpan="2" Content="Bestanden" HorizontalAlignment="Left" Height="35" Margin="10,5,0,0" Grid.Row="6" VerticalAlignment="Top" Width="280" Click="BtnPresueOk_Click" Grid.Column="2"/>
<Button Name="btnPresueFailed" Background="LightPink" Grid.ColumnSpan="2" Content="Nicht Bestanden" HorizontalAlignment="Left" Height="35" Margin="9,5,0,0" Grid.Row="6" VerticalAlignment="Top" Width="281" Click="BtnPresueFailed_Click" Grid.Column="0" />
</Grid>-->
</Grid>
</Window>
@@ -1,206 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Xylem.Common.Production.ProductionUiCordonel.Model;
namespace Xylem.Common.Production.ProductionUiCordonel
{
/// <summary>
/// Interaktionslogik für MultiMode.xaml
/// </summary>
public partial class MultiMode : Window
{
//#region Raw Input API
//[DllImport("User32.dll")]
//extern static uint GetRawInputDeviceList(IntPtr pRawInputDeviceList, ref uint uiNumDevices, uint cbSize);
//[DllImport("User32.dll")]
//extern static uint GetRawInputDeviceInfo(IntPtr hDevice, uint uiCommand, IntPtr pData, ref uint pcbSize);
//[DllImport("User32.dll")]
//extern static bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevice, uint uiNumDevices, uint cbSize);
//[DllImport("User32.dll")]
//extern static uint GetRawInputData(IntPtr hRawInput, uint uiCommand, IntPtr pData, ref uint pcbSize, uint cbSizeHeader);
//#endregion
MultiModeMasterViewModel multiModeMasterViewModel = new MultiModeMasterViewModel();
private List<MultiModeChild> Childs = new List<MultiModeChild>();
public ApplicationSettings AppSettings { get; private set; }
//class RawInputDevice : InputDevice
//{
// public override IInputElement Target => throw new NotImplementedException();
// public override PresentationSource ActiveSource => throw new NotImplementedException();
//}
public MultiMode()
{
DataContext = multiModeMasterViewModel;
InitializeComponent();
Topmost = false;
}
private void keyUp(Object sender, KeyEventArgs e)
{
}
public void SetBaseFocus()
{
//foreach (var item in Childs)
//{
// item.Key.Activate();
//}
//this.Activate();
//txtBarcodeInput.Focus();
}
private MultiModeChild addChild(IMultiModeChildViewModel model)
{
var a = new MultiModeChild(model);
a.Show();
return a;
}
CancellationTokenSource cts;
private void Window_Loaded(Object sender, RoutedEventArgs e)
{
cts = new CancellationTokenSource();
AppSettings = new ApplicationSettings("MultiProductionUiConfig.json");
multiModeMasterViewModel.StartPicking(cts);
//var trigger = new List<string>();
//if (AppSettings.ChildForms == null || !AppSettings.ChildForms.Any())
//{
// AppSettings.ChildForms = new List<ApplicationSettings.MultiFormChilds>();
// AppSettings.ChildForms.Add(new ApplicationSettings.MultiFormChilds() { Name = "Picking", Slot = 1, ViewModelType = typeof(PickingMultiModeViewModel), StartPos = null, Visisble = true, TextPort = "COM11" });
// AppSettings.ChildForms.Add(new ApplicationSettings.MultiFormChilds() { Name = "PressureHE", Slot = 3, ViewModelType = typeof(PressureMultiModeViewModel), StartPos = null, Visisble = true, TextPort = null });
// AppSettings.ChildForms.Add(new ApplicationSettings.MultiFormChilds() { Name = "PressureHY", Slot = 1, ViewModelType = typeof(PressureMultiModeViewModel), StartPos = null, Visisble = true, TextPort = null });
// AppSettings.ChildForms.Add(new ApplicationSettings.MultiFormChilds() { Name = "Marriage", Slot = 4, ViewModelType = typeof(MarriageMultiModeViewModel), StartPos = null, Visisble = true, TextPort = "COM1" });
// AppSettings.Update("MultiProductionUiConfig.json");
//}
////AppSettings.ChildForms.ForEach(c => trigger.Add(c.TextPort.ToString()));
//cts = new CancellationTokenSource();
//foreach (var item in AppSettings.ChildForms)
//{
// if (item.Visisble)
// {
// IMultiModeChildViewModel mdl = null;
// if (item.ViewModelType == typeof(PickingMultiModeViewModel))
// {
// mdl = new PickingMultiModeViewModel(item.Name, item.Slot, trigger, AppSettings.StationId);
// }
// else if (item.ViewModelType == typeof(PressureMultiModeViewModel))
// {
// mdl = new PressureMultiModeViewModel(item.Name, item.Slot, trigger, AppSettings.StationId);
// }
// else if (item.ViewModelType == typeof(MarriageMultiModeViewModel))
// {
// mdl = new MarriageMultiModeViewModel(item.Name, item.Slot, trigger, AppSettings.StationId);
// }
// if (mdl == null)
// {
// throw new ApplicationException("Bad configuration or old software");
// }
// var child = addChild(mdl);
// child.StartTextInputListener(item.TextPort, cts.Token);
// if (item.StartPos != null)
// {
// child.WindowStartupLocation = WindowStartupLocation.Manual;
// child.Top = item.StartPos.Top;
// child.Left = item.StartPos.Left;
// child.Height = item.StartPos.Height;
// child.Width = item.StartPos.Width;
// child.UseDefaultPos = false;
// }
// child.PosChanged += delegate (object val, EventArgs a)
// {
// if (val is MultiModeChild mmc)
// {
// if (mmc.ViewModel is BaseMultiModeChildViewModel baseChild)
// {
// var aasd = AppSettings.ChildForms.FirstOrDefault(c => c.Name == baseChild.ChildName);
// aasd.StartPos = new ApplicationSettings.Pos() { Height = mmc.Height, Left = mmc.Left, Top = mmc.Top, Width = mmc.Width };
// AppSettings.Update("MultiProductionUiConfig.json");
// }
// }
// };
// child.Show();
// Childs.Add(child);
// }
//}
//SetBaseFocus();
}
private void delegeChild_PosChange(Object sender, EventArgs e)
{
throw new NotImplementedException();
}
private void TxtBarcodeInput_TextChanged(Object sender, TextChangedEventArgs e)
{
//if (sender is TextBox text)
//{
// if (text.Text.ToLower().Contains("clear"))
// {
// text.Text = string.Empty;
// }
// else if (text.Text.Contains(System.Environment.NewLine))
// {
// foreach (var dispatchChild in Childs.Where(x => x.Value == text.Text[0]))
// {
// dispatchChild.Key.InputText(text.Text.Substring(1, text.Text.Length - 1).Trim());
// }
// text.Text = "";
// SetBaseFocus();
// }
//}
}
private void Window_Closed(Object sender, EventArgs e)
{
cts.Cancel();
foreach (var item in Childs)
{
item.Dispose();
item.Close();
}
}
}
}
@@ -1,84 +0,0 @@
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ctls="clr-namespace:Xylem.Common.Production.ProductionUiCordonel.ctls"
x:Class="Xylem.Common.Production.ProductionUiCordonel.MultiModeChild"
mc:Ignorable="d"
Title="MultiModeChild" Height="802.824" Width="704.975" WindowStartupLocation="Manual">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition Height="60"/>
<RowDefinition Height="25"/>
<RowDefinition Height="60"/>
<RowDefinition Height="25"/>
<RowDefinition Height="60"/>
<RowDefinition Height="25"/>
<RowDefinition Height="60"/>
<RowDefinition Height="*"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Label Content="Name"
HorizontalAlignment="Left"
Margin="0,0,0,0"
VerticalAlignment="Top"
Grid.Row="0"/>
<Label Background="LightGray" FontSize="17pt"
HorizontalContentAlignment="Right" Margin="10,5,10,0"
VerticalAlignment="Top" Height="40"
Content="{Binding ChildName}"
Grid.Row="1" />
<Label Content="Order"
HorizontalAlignment="Left"
Margin="0,0,0,0"
VerticalAlignment="Top"
Grid.Row="2"/>
<Label Background="LightGray" FontSize="17pt"
HorizontalContentAlignment="Right" Margin="10,10,10,0"
VerticalAlignment="Top" Height="40"
Content="{Binding Order, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="3" />
<Label Content="SlotInfo"
HorizontalAlignment="Left"
Margin="0,0,0,0"
VerticalAlignment="Top"
Grid.Row="4"/>
<Label Background="LightGray" FontSize="17pt"
HorizontalContentAlignment="Right" Margin="10,10,10,0"
VerticalAlignment="Top" Height="40"
Content="{Binding SlotInfo, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="5"/>
<Label Content="State"
HorizontalAlignment="Left"
Margin="0,0,0,0"
VerticalAlignment="Top"
Grid.Row="6"/>
<Label Background="LightGray" FontSize="17pt"
HorizontalContentAlignment="Right" Margin="10,10,10,0"
VerticalAlignment="Top" Height="40"
Content="{Binding CurrentSlotStateText, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="7"/>
<ctls:ctlMultiModePressure x:Name="PressureControl" Visibility="{Binding VisibilityPressure}" Grid.Row="8" Margin="0,0,0,0" OnBarcodeInput="PressureControl_OnBarcodeInput" />
<ctls:ctlMultiModePicking x:Name="PickingControl" Visibility="{Binding VisibilityPicking}" Grid.Row="8" Margin="0,0,0,0" />
<ctls:ctlMultiModeMarriage x:Name="MarriageControl" Visibility="{Binding VisibilityMarriage}" Grid.Row="8" Margin="0,0,0,0"/>
<Button Content="POS" Grid.Row="9" Click="Button_Click" />
<Button Content="Store" Grid.Row="10" Click="Button_Click_1" />
</Grid>
</Window>
@@ -1,135 +0,0 @@
using System;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using Xylem.Common.Production.ProductionUiCordonel.Event;
using Xylem.Common.Production.ProductionUiCordonel.Model;
namespace Xylem.Common.Production.ProductionUiCordonel
{
/// <summary>
/// Interaktionslogik für MultiModeChild.xaml
/// </summary>
public partial class MultiModeChild : Window, IDisposable
{
public IMultiModeChildViewModel ViewModel;
public Boolean UseDefaultPos { get; internal set; } = true;
public event EventHandler PosChanged;
public MultiModeChild(IMultiModeChildViewModel viewModel)
{
InitializeComponent();
ViewModel = viewModel;
DataContext = ViewModel;
if (ViewModel is PressureMultiModeViewModel pressureModel)
{
PressureControl.setViewModel(pressureModel);
Left = 0;
Top = 250;
Height = SystemParameters.PrimaryScreenHeight - 300;
Width = SystemParameters.PrimaryScreenWidth / 2;
}
if (ViewModel is PickingMultiModeViewModel pickingModel)
{
PickingControl.setViewModel(pickingModel);
Left = SystemParameters.VirtualScreenLeft;
Top = SystemParameters.VirtualScreenTop;
Height = SystemParameters.VirtualScreenHeight;
Width = SystemParameters.VirtualScreenWidth - SystemParameters.PrimaryScreenWidth;
}
if (ViewModel is MarriageMultiModeViewModel marriageModel)
{
MarriageControl.setViewModel(marriageModel);
Left = SystemParameters.PrimaryScreenWidth / 2;
Top = 250;
Height = SystemParameters.PrimaryScreenHeight - 300;
Width = SystemParameters.PrimaryScreenWidth / 2;
}
}
internal void StartTextInputListener(String port, CancellationToken cancellation)
{
if (port == null)
{
return;
}
var task = new Task(() =>
{
var a = new SerialPort(port);
a.Open();
a.NewLine = "\r";
while (true)
{
if (cancellation.IsCancellationRequested)
{
break;
}
var resText = a.ReadLine();
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() =>
{
ViewModel.InputText(resText);
}
));
}
}, cancellation);
task.Start();
}
internal void InputText(String text)
{
ViewModel.InputText(text);
}
private void BtnPresueFailed_Click(Object sender, RoutedEventArgs e)
{
}
private void TxtPresurePcbId_TextChanged(Object sender, TextChangedEventArgs e)
{
}
private void PressureControl_OnBarcodeInput(Object sender, StringArgs e)
{
InputText(e.Content);
}
public void Dispose()
{
ViewModel.Dispose();
}
private void Button_Click(Object sender, RoutedEventArgs e)
{
if (sender is Button btn)
{
btn.Content = $"POS L{Left},T{Top },H{Height},W{Width}";
}
}
private void Button_Click_1(Object sender, RoutedEventArgs e)
{
PosChanged?.Invoke(this, EventArgs.Empty);
}
}
}
@@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
{
/// <summary>
/// Concatenation for all production process states:
/// </summary>
/// <remarks date="2025-Apr-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public class BaseProcessStatesDef : IProcessStateDef
{
/// <summary>
/// Collection of state information
/// </summary>
public List<ProcessStateStruct> StateContainer { get; }
/// <summary>
/// Ctor:
/// Initialize all hidden standard generic states
/// </summary>
protected BaseProcessStatesDef()
{
StateContainer = new List<ProcessStateStruct>
{
new ProcessStateStruct(
productionProcess: null,
processState: ProcessState.Idle,
processInfo: Resources.StrStateIdle,
productionProcessNo: null,
nextStateOnSuccess: ProcessState.Idle),
new ProcessStateStruct(
productionProcess: new ErrorHandler(null),
processState: ProcessState.Error,
processInfo: Resources.StrProcessStateError,
productionProcessNo: null,
nextStateOnSuccess: ProcessState.Idle,
startAlways: true),
new ProcessStateStruct(
productionProcess: null,
processState: ProcessState.RepeatFailedTests,
processInfo: Resources.StrStateRepeatFinalTest,
productionProcessNo: null,
nextStateOnSuccess: BaseProductionProcess.DoNotChangeStateAfterCompletion),
new ProcessStateStruct(
productionProcess: null,
processState:ProcessState.AbortTest,
processInfo:Resources.StrStateSkipping,
productionProcessNo:null,
nextStateOnSuccess:ProcessState.Idle),
new ProcessStateStruct(
productionProcess: null,
processState:ProcessState.Stop,
processInfo:Resources.StrStateUserStop,
productionProcessNo:null,
nextStateOnSuccess:ProcessState.Idle),
new ProcessStateStruct(
productionProcess: null,
processState:ProcessState.PrintSuccessReport,
processInfo:Resources.StrStatePrintSuccessReport,
productionProcessNo:null,
nextStateOnSuccess:ProcessState.Idle),
new ProcessStateStruct(
productionProcess: null,
processState:ProcessState.PrintReturnReport,
processInfo:Resources.StrStatePrintReturnReport,
productionProcessNo:null,
nextStateOnSuccess:ProcessState.Idle)
};
}
///<inheritdoc/>
public ProcessStateStruct GetProcessStateStructOfState(ProcessState stateMachineState)
{
return (from stateContainer in StateContainer
where stateContainer.ProcessState == stateMachineState
select stateContainer).FirstOrDefault();
}
///<inheritdoc/>
public IProductionProcess GetProcessOfState(ProcessState stateMachineState)
{
return (from stateContainer in StateContainer
where stateContainer.ProcessState == stateMachineState
select stateContainer.ProductionProcess).FirstOrDefault();
}
///<inheritdoc/>
public String GetStateInfoOfState(ProcessState? stateMachineState)
{
if (stateMachineState == null)
stateMachineState = ProcessState.Error;
return (from stateContainer in StateContainer
where stateContainer.ProcessState == stateMachineState
select stateContainer.ProcessInfo).FirstOrDefault();
}
///<inheritdoc/>
public String GetStateInfoOfProcess(IProductionProcess process)
{
return (from stateContainer in StateContainer
where stateContainer.ProductionProcess == process
select stateContainer.ProcessInfo).FirstOrDefault();
}
///<inheritdoc/>
public ProcessState GetStateOfInfoText(String text)
{
return (from stateContainer in StateContainer
where stateContainer.ProcessInfo == text
select stateContainer.ProcessState).FirstOrDefault();
}
///<inheritdoc/>
public ProcessState GetStateOfProcess(IProductionProcess process)
{
return (from stateContainer in StateContainer
where stateContainer.ProductionProcess == process
select stateContainer.ProcessState).FirstOrDefault();
}
///<inheritdoc/>
public Int32 GetNumberOfInitializedProcesses()
{
return StateContainer.Count(stateContainer => stateContainer.ProductionProcessNo != null);
}
///<inheritdoc/>
public List<IProductionProcess> GetAllEolRelevantProcesses()
{
var sortedList = StateContainer
.Where(y => y.ProductionProcessNo != null)
.OrderBy(x => x.ProductionProcessNo);
return (from state in sortedList select state.ProductionProcess).ToList();
}
}
}
@@ -16,7 +16,7 @@ using Xylem.Common.Logic.ProductionOrderCore;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EventArgs;
using Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
using Xylem.Common.Utils.ProcessExec;
using Xylem.Common.Utils.ProcessExec.EventArguments;
@@ -292,7 +292,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
/// <inheritdoc cref="IProductionProcess"/>
public void StartProcess(ProcessState? successExitState, ProcessState errorExitState = ProcessState.Error,
ProcessState breakExitState = ProcessState.AbortFinalTest)
ProcessState breakExitState = ProcessState.AbortTest)
{
if (Meter == null)
{
@@ -19,18 +19,14 @@
/// </summary>
Error,
/// <summary>
/// Abort final test execution
/// Abort test execution
/// </summary>
AbortFinalTest,
AbortTest,
/// <summary>
/// Repeat the entire final test
/// Repeat the entire test
/// </summary>
RepeatFailedTests,
/// <summary>
/// Final test failed
/// </summary>
FinalTestFailed,
/// <summary>
/// User stopped process
/// </summary>
Stop,
@@ -9,7 +9,7 @@ using Xylem.Common.Logic.ProductionOrderCore.Vako;
using Xylem.Common.Logic.SoftwareAccessHelper;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
{
@@ -16,6 +16,7 @@ using Xylem.Common.Logic.ProductionOrderCore.OrderData;
using Xylem.Common.Logic.ProductionOrderCore.TestResults;
using Xylem.Common.Production.ProductionUiCordonel.Model;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
@@ -589,7 +590,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
}
// Set the information of this software
EolProgress.EOLSoftwareNameAndVersion = $"{Resources.StrSoftwareNameVersion} " +
EolProgress.EOLSoftwareNameAndVersion = $"{Resources.StrEolSoftwareNameVersion} " +
$"{Assembly.GetExecutingAssembly().GetName().Version}";
return EolProgress.EOLSoftwareNameAndVersion;
@@ -726,7 +727,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
{
if (EolProgress != null)
EolProgress.RequirementPcbIdChecked = EOLStatus.FAIL;
feedbackMsg = Resources.StrSuccessMsgOrderBasedRequirements;
feedbackMsg = Resources.StrErrorMsgRequirementPcbId;
return EOLStatus.FAIL;
}
@@ -1033,14 +1034,14 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
// Create an output message addon for alternative power consumption calculation
var strAlternativeCalc = ProductionStatus.AlternativeRequiredBatteryLoadPercent == null
? ""
: $" - {Resources.StrMsgRequirementAbsolutLimitDrainedBatteryAtEol}: " +
: $" - {Resources.StrMsgRequirementAbsolutLimitDrainedBattery}: " +
$"{ProductionRequirements.MaxDrainedBatteryLoadPercentShipping:F2} %" +
$" ({Resources.StrMsgAlternativeDrainedBatteryCalculation})";
var batteryLoad = $"{Resources.StrMsgDrainedBatteryAtEol}: " +
var batteryLoad = $"{Resources.StrMsgDrainedBattery}: " +
// Actual calculated battery load based on readings from meter
$"{ProductionStatus.DrainedBatteryLoadPercent:F2} %" +
$" - {Resources.StrMsgRequirementLimitDrainedBatteryAtEol}: " +
$" - {Resources.StrMsgRequirementLimitDrainedBattery}: " +
// Required battery load from standard or special requirement
$"{threshold:F2} % {strAlternativeCalc}";
@@ -1108,11 +1109,11 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
}
// Build message for required versus calculated lifetime
var lifeTime = $"{Resources.StrMsgRemainingLifeTimeAtEol}: " +
var lifeTime = $"{Resources.StrMsgRemainingLifeTime}: " +
// Actual calculated lifetime based on readings from meter
$"{ProductionStatus.RemainingLifeTimeYears:F2} " +
$"{Resources.StrMsgYears}" +
$" - {Resources.StrMsgRequiredLifeTimeAtEol}: " +
$" - {Resources.StrMsgRequiredLifeTime}: " +
// Required lifetime from special or standard requirement
$"{ProductionRequirements.RequiredLifeTimeYearsShipping:F2} " +
$"{Resources.StrMsgYears}";
@@ -14,7 +14,7 @@ using Xylem.Common.Logic.ProductionOrderCore;
using Xylem.Common.Logic.SoftwareAccessHelper;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
using Button = System.Windows.Controls.Button;
// ReSharper disable UnusedAutoPropertyAccessor.Local
@@ -0,0 +1,302 @@
using System.Collections.Generic;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
{
/// <summary>
/// Concatenation for all production process states:
/// - Production process class (representing a production process step), null if hidden process,
/// - attached state,
/// - state information to display a text message,
/// - production process number to define where to display in the overview,
/// - next state which should be executed after this succeeded,
/// [optional]:
/// - own error state,
/// - own break state,
/// - kick off state for parallel execution after starting this process in a separate task,
/// - wait for single process to finish successfully,
/// - wait for all processes to finish successfully,
/// - start always independent of any result, e.g. the error state needs to be started if called!
/// </summary>
/// <remarks date="2025-Apr-15" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public class EolProcessStateDef : BaseProcessStatesDef
{
/// <summary>
/// State-machine definitions of production-processes which feeds the state-machine.
///
///
/// INSTRUCTION FOR PREPARATION:
/// -----------------------------
/// STEP 1: Define some states for your processes <see cref="ProcessState"/>
/// STEP 2: Implement your processes like <see cref="DetectCordonel"/>
/// STEP 3: Register and chain your states here
///
///
/// INSTRUCTION FOR NORMAL USAGE:
/// -----------------------------
/// Means one state automatically switched on successfully execution to the next required state.
///
/// Define the (e.g.):
/// productionProcess: new DetectCordonel(Resources.StrStateDetectCordonel),
/// processState: ProcessState.DetectCordonel,
/// processInfo: Resources.StrStateDetectCordonel,
/// productionProcessNo: 0,
/// nextStateOnSuccess: ProcessState.CheckNewMeter),
///
/// If a special state is needed on errorExitState or breakExitState these can be assigned. Otherwise, the
/// "errorExitState" is linked to the generic-error-state, the "breakExitState" to the abort-test-state.
///
///
/// INSTRUCTION FOR NON-BLOCKING MULTIPLE PROCESSES SIMULTANEOUSLY ACTIVITIES:
/// --------------------------------------------------------------------------
/// Sometimes production processes may be able to be executed simultaneously as they are independent of each
/// other. One example is a database access for data collection from different tables.
///
/// This is needed for non-blocking immediately new state change (not waiting for the successful execution of
/// the actual new assigned process).
///
/// Define the (e.g.):
///
/// productionProcess: new ConnectCordonel(Resources.StrStateConnectCordonel),
/// processState: ProcessState.ConnectCordonel,
/// processInfo: Resources.StrStateConnectCordonel,
/// productionProcessNo: 1,
/// // Avoid automatic change after completion as parallel process is active
/// nextStateOnSuccess: BaseProductionProcess.DoNotChangeStateAfterCompletion,
/// // Kick off parallel process execution after starting the "ConnectCordonel() task"
/// kickOffParallelState: ProcessState.GetSerialNumberAndRadioAddress),
///
///
/// INSTRUCTION TO DELAY THE START OF A PROCESS AND WAIT ON RESULTS OF OTHER PROCESS(ES):
/// -------------------------------------------------------------------------------------
/// Each new process execution can be delayed as it may need the result of another process which is active.
///
/// Wait for a single process to complete:
/// Set the "waitForSingleProcess" to the state of the process needed to wait for (e.g.):
///
/// productionProcess: new CheckProductionState(Resources.StrStateCheckProductionState),
/// processState: ProcessState.CheckProductionState,
/// processInfo: Resources.StrStateCheckProductionState,
/// productionProcessNo: 4,
/// // Avoid automatic change after completion as parallel process is active
/// nextStateOnSuccess: BaseProductionProcess.DoNotChangeStateAfterCompletion,
/// // Kick off parallel process execution after starting
/// kickOffParallelState: ProcessState.ProcessPasswordFile,
/// // Wait until this process has been successfully completed
/// waitForSingleProcess: ProcessState.ConnectCordonel),
///
/// Wait for all preceding processes to complete to be sure to get all data and all processed are succeeded:
/// Set the "waitForAllProcesses" to true (e.g.):
/// productionProcess: new ExecPasswordFile(Resources.StrStatePasswordFile),
/// processState: ProcessState.ProcessPasswordFile,
/// processInfo: Resources.StrStatePasswordFile,
/// productionProcessNo: 5,
/// nextStateOnSuccess: ProcessState.ExecFinalParametrization,
/// waitForAllProcesses: true),
///
/// </summary>
public EolProcessStateDef()
{
var stateContainer = new List<ProcessStateStruct>
{
#region ----------------------------------- GENERIC-STATES---------------------------------------------
// Detection of Cordonel has to be completed before assigning next state, so leave to automatic
// to change the state after successfully execution. The detection is the base for all others.
new ProcessStateStruct(
productionProcess: new DetectCordonel(Resources.StrStateDetectCordonel),
processState: ProcessState.DetectCordonel,
processInfo: Resources.StrStateDetectCordonel,
productionProcessNo: 0,
nextStateOnSuccess: ProcessState.CheckNewMeter),
// Detection of Cordonel has to be completed before assigning next state. It has to be checked if
// the meter changed to clear all collected contents of previous run. To UPDATE ALL INFORMATION after
// new meter assignment the "DetectCordonel" has to be repeated to return here "Old Meter" and then
// enter the "ConnectCordonel"!
new ProcessStateStruct(
productionProcess: null,
processState: ProcessState.CheckNewMeter,
processInfo: Resources.StrProcessStateCheckNewMeter,
productionProcessNo: null,
nextStateOnSuccess: ProcessState.DetectCordonel,
errorExitState: ProcessState.ConnectCordonel),
// Precondition: - The detection process has to be completed as the PcbId is needed.
// AVOID to change the process state on completion as parallel process is active!
new ProcessStateStruct(
productionProcess: new ConnectCordonel(Resources.StrStateConnectCordonel),
processState: ProcessState.ConnectCordonel,
processInfo: Resources.StrStateConnectCordonel,
productionProcessNo: 1,
// Avoid automatic change after completion as parallel process is active
nextStateOnSuccess: BaseProductionProcess.DoNotChangeStateAfterCompletion,
// Kick off parallel process execution
kickOffParallelState: ProcessState.GetSerialNumberAndRadioAddress),
// Precondition: - The detection process has to be completed as the PcbId is needed.
// AVOID to change the process state on completion as parallel process is active!
new ProcessStateStruct(
productionProcess: new GetSerialNumberAndRadioAddress(Resources.StrStateGetSerialAndRadioAddress),
processState:ProcessState.GetSerialNumberAndRadioAddress,
processInfo:Resources.StrStateGetSerialAndRadioAddress,
productionProcessNo: 2,
// Avoid automatic change after completion as parallel process is active
nextStateOnSuccess: BaseProductionProcess.DoNotChangeStateAfterCompletion,
// Kick off parallel process execution
kickOffParallelState:ProcessState.GetProgrammingParameters),
// This scans the order number, it has to be executed exclusively with automatic
new ProcessStateStruct(
productionProcess: new CheckOrderNumber(Resources.StrStateCheckOrderNumber),
processState: ProcessState.CheckOrderNumber,
processInfo: Resources.StrStateCheckOrderNumber,
productionProcessNo: 11,
nextStateOnSuccess: ProcessState.CheckSerialNumber),
// This scans the serial number, it has to be executed exclusively with automatic
new ProcessStateStruct(
productionProcess: new CheckSerialNumber( Resources.StrStateCheckSerialNumber),
processState: ProcessState.CheckSerialNumber,
processInfo: Resources.StrStateCheckSerialNumber,
productionProcessNo: 12,
nextStateOnSuccess: ProcessState.CheckAttachments),
#endregion -------------------------------- GENERIC-STATES --------------------------------------------
#region ----------------------------------- EOL-STATES ------------------------------------------------
// Precondition: - The connection process has to be completed as a readout of some register
// values from the Cordonel are needed to check the update capability:
// - Wait on completed connection process before starting this process.
// AVOID to change the process state on completion as parallel process is active!
new ProcessStateStruct(
productionProcess: new GetFinalParametrization(Resources.StrStateGetProgrammingParameters),
processState: ProcessState.GetProgrammingParameters,
processInfo: Resources.StrStateGetProgrammingParameters,
productionProcessNo: 3,
// Avoid automatic change after completion as parallel process is active
nextStateOnSuccess: BaseProductionProcess.DoNotChangeStateAfterCompletion,
// Kick off parallel process execution
kickOffParallelState: ProcessState.CheckProductionState,
// Wait until this process has been successfully completed
waitForSingleProcess: ProcessState.ConnectCordonel),
// Precondition: - The connection process has to be completed as a readout of some register
// values from the Cordonel are needed to check the update capability:
// - Wait on completed connection process before starting this process.
// AVOID to change the process state on completion as parallel process is active!
new ProcessStateStruct(
productionProcess: new CheckProductionState(Resources.StrStateCheckProductionState),
processState: ProcessState.CheckProductionState,
processInfo: Resources.StrStateCheckProductionState,
productionProcessNo: 4,
// Avoid automatic change after completion as parallel process is active
nextStateOnSuccess: BaseProductionProcess.DoNotChangeStateAfterCompletion,
// Kick off parallel process execution
kickOffParallelState: ProcessState.ProcessPasswordFile,
// Wait until this process has been successfully completed
waitForSingleProcess: ProcessState.ConnectCordonel),
// Precondition: - The connection process has to be completed as a password change in the DB
// will take place: Skeleton to Password Level 8. Not waiting for completion may cause side
// effects on login. Wait for all processes to complete.
// Leave state switch to automatic to avoid side effects on incomplete updated DB for next
// steps.
new ProcessStateStruct(
productionProcess: new ExecPasswordFile(Resources.StrStatePasswordFile),
processState: ProcessState.ProcessPasswordFile,
processInfo: Resources.StrStatePasswordFile,
productionProcessNo: 5,
nextStateOnSuccess: ProcessState.ExecFinalParametrization,
waitForAllProcesses: true),
// This writes all setups to the device, it has to be executed exclusively with automatic
new ProcessStateStruct(
productionProcess: new ExecFinalParametrization(Resources.StrStateFinalParametrization),
processState: ProcessState.ExecFinalParametrization,
processInfo: Resources.StrStateFinalParametrization,
productionProcessNo: 6,
nextStateOnSuccess: ProcessState.ExecStoreConfiguration),
// Precondition: - The final parametrization has to be finished as this shall be stored
// to non-volatile memory and checked if remaining valid after the reboot.
// Store configuration has to be executed exclusively with automatic
new ProcessStateStruct(
productionProcess: new ExecStoreConfiguration(Resources.StrStateStoreConfiguration),
processState: ProcessState.ExecStoreConfiguration,
processInfo: Resources.StrStateStoreConfiguration,
productionProcessNo: 7,
nextStateOnSuccess: ProcessState.RebootCordonel),
// Precondition: - The store configuration has to be executed to safe all settings
// to non-volatile memory and checked if remaining valid after the reboot.
// Reboot has to be executed exclusively with automatic
new ProcessStateStruct(
productionProcess: new ExecRebootCordonel(Resources.StrStateRebootCordonel),
processState: ProcessState.RebootCordonel,
processInfo: Resources.StrStateRebootCordonel,
productionProcessNo: 8,
nextStateOnSuccess: ProcessState.CheckFinalParametrization),
// Precondition: - The reboot process including the store all configurations has to be
// completed.
// Check final configuration has to be executed exclusively with automatic
new ProcessStateStruct(
productionProcess: new CheckFinalParametrization(Resources.StrStateCheckParametrization),
processState: ProcessState.CheckFinalParametrization,
processInfo: Resources.StrStateCheckParametrization,
productionProcessNo:9,
nextStateOnSuccess: ProcessState.CheckRadio),
// Precondition: - The final parametrization, reboot process including the store all
// configurations and the check of all setups survived the reboot have to be completed.
// Radio signal level check, skipped on NA region or if skipping is required by special
// approval
new ProcessStateStruct(
productionProcess: new CheckRadio(Resources.StrStateRadioCheck),
processState: ProcessState.CheckRadio,
processInfo: Resources.StrStateRadioCheck,
productionProcessNo:10,
nextStateOnSuccess: ProcessState.CheckOrderNumber),
// Precondition: - All checks have to be executed with positive result
new ProcessStateStruct(
productionProcess: new CheckAttachments(Resources.StrStateCheckAttachments),
processState: ProcessState.CheckAttachments,
processInfo: Resources.StrStateCheckAttachments,
productionProcessNo:13,
nextStateOnSuccess: ProcessState.ExecVisualInspection),
// Precondition: - All checks have to be executed with positive result
new ProcessStateStruct(
productionProcess: new VisualInspection(Resources.StrStateVisualInspection),
processState: ProcessState.ExecVisualInspection,
processInfo: Resources.StrStateVisualInspection,
productionProcessNo:14,
nextStateOnSuccess: ProcessState.PrepareShippingMode),
// Precondition: - All checks have to be executed with positive result
new ProcessStateStruct(
productionProcess: new PrepareShipping(Resources.StrStatePrepareShipping),
processState: ProcessState.PrepareShippingMode,
processInfo: Resources.StrStatePrepareShipping,
productionProcessNo:15,
nextStateOnSuccess: ProcessState.ReadyForShipping),
new ProcessStateStruct(
productionProcess: null,
processState:ProcessState.ReadyForShipping,
processInfo:Resources.StrStateReadyForShipping,
productionProcessNo:null,
nextStateOnSuccess:ProcessState.Idle),
#endregion -------------------------------- EOL-STATES ------------------------------------------------
};
StateContainer.AddRange(stateContainer);
}
}
}
@@ -10,7 +10,7 @@ using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Logic.ProductionOrderCore;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
//using Xylem.Common.Ui.ProductionUiCordonel.UserControls.FinalTest;
@@ -66,7 +66,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2023-Jul-11" author="Thomas Wiedebusch">
/// - Speed up to quick return if passwords are already set.
///
/// ATTENTION: This requires to remove those on new PcbId <see cref="FinalProcessController"/>
/// ATTENTION: This requires to remove those on new PcbId <see cref="ProcessController"/>
/// for CheckMeterChange!
///
/// </remarks>
@@ -7,6 +7,7 @@ using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Logic.SoftwareAccessHelper;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
@@ -38,7 +39,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2023-Jul-11" author="Thomas Wiedebusch">
/// - Speed up to quick return if programming parameters are already set.
///
/// ATTENTION: This requires to remove those on new PcbId <see cref="FinalProcessController"/>
/// ATTENTION: This requires to remove those on new PcbId <see cref="ProcessController"/>
/// for CheckMeterChange!
/// </remarks>
/// <remarks date="2023-Jul-19" author="Thomas Wiedebusch">
@@ -6,7 +6,7 @@ using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Production.ProductionUiCordonel.Model;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
{
@@ -1,192 +0,0 @@
using Newtonsoft.Json;
using ProductionUiCordonel.ProductionProcesses.Enum;
using ProductionUiCordonel.UserControls.FinalTest;
using System;
using System.Windows.Controls;
using System.Windows.Threading;
using Xylem.Common.CommonCore.Configuration;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisStatus;
using Xylem.Common.Logic.SoftwareAccessHelper;
namespace ProductionUiCordonel.ProductionProcesses.Actions
{
public class ProductionCheckShippingState : BaseProductionProcess
{
private UcPlain UserControl;
public ProductionCheckShippingState()
{
name = "Prüfe versandbereitschaft des Zählers";
currentProcessState = ProductionProcessState.Waiting;
}
public override void InitUserControl()
{
UserControl = new UcPlain();
UserControl.lblHl.Content = name;
UserControl.lblText.Content = "Hydraulisch geprüft? Batterie i.O?";
}
public override UserControl GetControl()
{
if (UserControl == null)
{
throw new Exception($"UserControl {GetType()} not Ready");
}
return UserControl;
}
public override void ProcessDone()
{
//clear
}
private void doStartProcessLoop()
{
var retriesRemaining = 1;
while (retriesRemaining >= 0)
{
try
{
Progress = 0;
//var url = $"http://localhost:56011/api/FinalCheck/GetProgrammingParameters?PcbID={Meter.PcbId}";
var url = $"{ServiceUrls.MeterInfoForTestBench()}{Meter.PcbId}";
var csdRet = LocalWebRequest.GetRequest(url);
var res = JsonConvert.DeserializeObject<String[]>(csdRet);
var CalibrationRun = res[4];
var LastTestBechState = 0;
Boolean? HyState;
Boolean? preState = null;
if (!string.IsNullOrEmpty(res[5]))
{
int.TryParse(res[5], out var tmp);
LastTestBechState = tmp;
}
if (LastTestBechState <= 20)
{
HyState = null;
}
else if (LastTestBechState <= 30)
{
HyState = true;
}
else
{
HyState = false;
}
if (!string.IsNullOrEmpty(CalibrationRun))
{
preState = true;
}
if (!HyState.HasValue || !HyState.Value)
{
UserControl.lblText.Dispatcher.Invoke(DispatcherPriority.Send,
new Action(() =>
{
UserControl.lblText.Content = "Zähler nicht erfolgreich geprüft";
}
));
currentProcessState = ProductionProcessState.Error;
}
if (!preState.HasValue || !preState.Value)
{
UserControl.lblText.Dispatcher.Invoke(DispatcherPriority.Send,
new Action(() =>
{
UserControl.lblText.Content = "Zähler nicht erfolgreich vorbehandelt";
}
));
currentProcessState = ProductionProcessState.Error;
}
Progress = 50;
var genesisStatus = new GenesisStatus();
// this calculates all life time values
if (!GenesisStatusHandler.BuildLifeTimeInformation(Meter, genesisStatus))
{
UserControl.lblText.Dispatcher.Invoke(DispatcherPriority.Send,
new Action(() =>
{
UserControl.lblText.Content = "Batterie nicht lesbar";
}
));
Meter.WriteLog("Batterie nicht lesbar");
break;
}
//lastFrame
Meter.WriteLog($"totalUsed at EOL {genesisStatus.DrainedBatteryLoadPercent:F2}");
if (res[1] == "3212095")
{
currentProcessState = ProductionProcessState.Done;
return;
}
if (genesisStatus.RemainingLifeTimeYears < 20)
{
if (genesisStatus.DrainedBatteryLoadPercent > 8)
{
currentProcessState = ProductionProcessState.Error;
UserControl.lblText.Content = "Batterie über 10% entladen";
Meter.WriteLog("Batterie über 10% entladen");
}
}
try
{
}
catch (Exception)
{
throw;
}
//;
if (Meter.LutCrc != $@"0x{int.Parse(res[8]):X4}")
{
currentProcessState = ProductionProcessState.Error;
UserControl.lblText.Content = "Lut not correct";
}
if (currentProcessState != ProductionProcessState.Error)
{
currentProcessState = ProductionProcessState.Done;
return;
}
retriesRemaining -= 1;
currentProcessState = ProductionProcessState.Error;
}
catch (Exception ex)
{
Meter.WriteLog($"{ex}");
currentProcessState = ProductionProcessState.Error;
retriesRemaining -= 1;
}
}
currentProcessState = ProductionProcessState.Error;
}
public override void StartProcess()
{
doStartProcessLoop();
}
}
}
@@ -1,143 +0,0 @@
using System;
using System.Collections.Generic;
using System.Windows.Controls;
using ProductionUiCordonel.ProductionProcesses.Enum;
using ProductionUiCordonel.UserControls.FinalTest;
namespace ProductionUiCordonel.ProductionProcesses.Actions
{
public class ProductionProcessDone : BaseProductionProcess
{
public event EventHandler<Tuple<Boolean,Boolean, Boolean>> isDone;
private UcDialog UserControl;
public DialogResult Result = DialogResult.None;
public enum DialogResult
{
None,
Retry,
Print,
Abort
}
public Boolean DialogIsDone { get; }
private Boolean Error;
public ProductionProcessDone(Boolean error = false)
{
Error = error;
DialogIsDone = false;
name = "Fertig";
currentProcessState = ProductionProcessState.Waiting;
}
public override void InitUserControl()
{
UserControl = new UcDialog();
}
public override UserControl GetControl()
{
if (UserControl == null)
{
throw new Exception($"UserControl {GetType()} not Ready");
}
return UserControl;
}
public override void ProcessDone()
{
//clear
}
private void StepDone()
{
currentProcessState = ProductionProcessState.Done;
var retry = false;
var abort = false;
var print = false;
if (Error)
{
switch (Result)
{
case DialogResult.Retry:
retry = true;
break;
case DialogResult.Print:
abort = true;
print = true;
break;
case DialogResult.Abort:
abort = true;
break;
}
}
isDone?.Invoke(null, new Tuple<Boolean, Boolean,Boolean>(retry, abort, print));
}
private void StepError(String ErrorText = "Ein Fehler ist aufgetreten")
{
UserControl.SetDesc(ErrorText);
var errorOptionList = new List<UcDialog.DialogOption>();
var retry = new UcDialog.DialogOption("Wiederholen", "Alle Schritte werden wiederholt", () =>
{
Result = DialogResult.Retry;
StepDone();
});
var Abort = new UcDialog.DialogOption("Abbruch", "Diesen Zähler NICHT verpacken", () =>
{
Result = DialogResult.Abort;
StepDone();
});
var print = new UcDialog.DialogOption("Rückläuferschein drucken", "Rückläuferschein beim Zähler lassen und auf Klärung durch QS warten", () =>
{
Result = DialogResult.Print;
StepDone();
});
errorOptionList.Add(retry);
errorOptionList.Add(Abort);
errorOptionList.Add(print);
UserControl.SetDialogOptions(errorOptionList);
}
private void StepSuccsess()
{
UserControl.SetDesc("Zähler verpacken");
var OptionList = new List<UcDialog.DialogOption>();
var retry = new UcDialog.DialogOption("Ok", "", () =>
{
Result = DialogResult.None;
StepDone();
});
OptionList.Add(retry);
UserControl.SetDialogOptions(OptionList);
}
public override void StartProcess()
{
if (Error)
{
StepError();
}
else
{
StepSuccsess();
}
}
}
}
@@ -1,364 +0,0 @@
using Newtonsoft.Json;
using ProductionUiCordonel.ProductionProcesses.Enum;
using ProductionUiCordonel.UserControls.FinalTest;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Controls;
using Xylem.Common.CommonCore.Configuration;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
namespace ProductionUiCordonel.ProductionProcesses.Actions
{
public class ProductionProcessFinalParameterization : BaseProductionProcess
{
private UcPlain UserControl;
public ProductionProcessFinalParameterization()
{
name = "Final Parameterization";
currentProcessState = ProductionProcessState.Waiting;
}
public override void InitUserControl()
{
UserControl = new UcPlain();
}
public override UserControl GetControl()
{
if (UserControl == null)
{
throw new Exception($"UserControl {this.GetType()} not Ready");
}
return UserControl;
}
public override void ProcessDone()
{
//clear
}
public Task<Tuple<String, Boolean>> _currentGenesis_WriteRegister<T>(String reg, T value, Boolean waitForResult = true,
Boolean checkRegister = false)
{
return new Task<Tuple<String, Boolean>>(() =>
{
var ret = Meter.WriteRegister(reg, value, waitForResult, checkRegister);
if (!ret)
{
ret = Meter.WriteRegister(reg, value, waitForResult, checkRegister);
}
return new Tuple<String, Boolean>(reg, ret);
});
}
public Task<Tuple<String, Boolean>> wirteRegisterInBetweenCheck(String reg, Int32 value, Boolean waitForResult = true,
Boolean checkRegister = false, int addToCheck = 10, int addFromCheck = 0)
{
return new Task<Tuple<String, Boolean>>(() =>
{
Meter.WriteRegister(reg, value, waitForResult);
var accucalValue = RegisterConverter.ConvertTo<Int32>(Meter.ReadRegister(reg));
if (accucalValue >= (value + addFromCheck) && value <= accucalValue + (addToCheck))
{
return new Tuple<String, Boolean>(reg, true);
}
return new Tuple<String, Boolean>(reg, false);
});
}
public override void StartProcess()
{
base.Progress = 0;
var useRadio = true;
var radioParams = new RadioConfigurationParams();
if (useRadio)
{
var url = ServiceUrls.GenesisFinalCheckServiceUrl();
url = url + $"GetRadioConfiguration?PcbId={Meter.PcbId}";
var http = (HttpWebRequest)WebRequest.Create(url);
http.Method = "GET";
http.Timeout = 2000;
var response = (HttpWebResponse)http.GetResponse();
var responseStream = response.GetResponseStream();
if (responseStream == null)
{
throw new InvalidOperationException();
}
using (var sr = new StreamReader(responseStream))
{
var responseJson = sr.ReadToEnd();
radioParams = JsonConvert.DeserializeObject<RadioConfigurationParams>(responseJson);
}
}
base.Progress = 20;
var overAllResult = new List<Task<Tuple<String, Boolean>>>();
//611.219.280
//
var ts = DateTimeOffset.UtcNow - new DateTimeOffset(2000, 1, 1, 0, 0, 0, new TimeSpan(0));
//Alarms
////_currentGenesis.WriteRegister("SENSUSRADIO_MainAlarmMask?
////_currentGenesis.WriteRegister("SENSUSRADIO_ExtendedAlarmMask ?
//CUSTOMER_AlarmBroadcastMask?
//CUSTOMER_AlarmEnableMask ?
//CUSTOMER_AlarmVisualAutoClearMask?
//CUSTOMER_AlarmVisualMask ?
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_SealDisplay",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
//Meter.SetLcdText(true, null);
//overAllResult.Add(_currentGenesis_WriteRegister(Register.Customer.TriggerAlarmCancel,
// (new Byte[] { 0xFF, 0xFF, 0xFF, 0xFF }).Reverse().ToArray(), true, false));
overAllResult.Add(wirteRegisterInBetweenCheck("CUSTOMER_LeakFlowThreshold", 375, true, true, 1, -1));
overAllResult.Add(wirteRegisterInBetweenCheck("CUSTOMER_ExcessFlowVolumeThreshold", 25000, true, true, 1, -1));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_ExcessFlowTimeThreshold",
(new Byte[] { 0x00, 0x00, 0x00, 0x05 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_LeakTimeThreshold",
(new Byte[] { 0x00, 0x00, 0x4E, 0xC0 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_Locale",
(new Byte[] { 0x00, 0x00, 0x00, 0xD0 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_PressureHighDelay",
(new Byte[] { 0x00, 0x00, 0x02, 0x58 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_PressureHighThreshold",
(new Byte[] { 0x00, 0x0B, 0x71, 0xB0 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_PressureLowDelay",
(new Byte[] { 0x00, 0x00, 0x02, 0x58 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_PressureLowThreshold",
(new Byte[] { 0x00, 0x00, 0x75, 0x30 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_ReverseFlowTimeThreshold",
(new Byte[] { 0x00, 0x00, 0x00, 0x78 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_TemperatureHighDelay",
(new Byte[] { 0x00, 0x00, 0x01, 0x2C }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_TemperatureHighThreshold",
(new Byte[] { 0x00, 0x00, 0x01, 0xF4 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_TemperatureLowDelay",
(new Byte[] { 0x00, 0x00, 0x01, 0x2C }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_TemperatureLowThreshold",
(new Byte[] { 0x00, 0x00, 0x00, 0x14 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_ArrowThreshold",
(new Byte[] { 0x00, 0x1E, 0x84, 0x80 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_DisplayPow10",
(new Byte[] { 0x00, 0x00, 0x00, 0xFD }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_DisplayUnits",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_ForwardArrow",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_HardErrorLimit",
(new Byte[] { 0x00, 0x00, 0x00, 0x04 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_LedMode",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
//overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_LowFlowMaxPeriod", (new Byte[] { 0x00, 0x00, 0x00, 0x3C }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_LowFlowMaxPeriod", 60 << 16, true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_LowFlowThreshold",
(new Byte[] { 0x00, 0x00, 0x00, 0xC8 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_MeterSize",
(new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_PipeFillingDelay",
(new Byte[] { 0x00, 0x00, 0x00, 0x1E }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_ResetAccumulators",
(new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray(), true, false));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_SampleRate",
(new Byte[] { 0x00, 0x00, 0x00, 0x02 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_UpdateThreshold",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("IRDA_AdapterPresenceLimit",
(new Byte[] { 0x00, 0x00, 0x00, 0x60 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("IRDA_PulseReportRate",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_FlowPoint",
(new Byte[] { 0x00, 0x00, 0x00, 0x02 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_FlowUnits",
(new Byte[] { 0x00, 0x00, 0x00, 0x04 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_PressureOffset",
(new Byte[] { 0x00, 0x00, 0x03, 0xF5 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_PressurePresent",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_PressureRate",
(new Byte[] { 0x00, 0x0D, 0xBB, 0xA0 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_PressureUnits",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_PulseLength",
(new Byte[] { 0x00, 0x00, 0x00, 0x07 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_PulseMode",
(new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_PulseWeight",
(new Byte[] { 0x00, 0x00, 0x27, 0x10 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_TemperatureUnits",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("PERIODICLOG_AverageFlowPeriod",
(new Byte[] { 0x00, 0x00, 0x00, 0x05 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("PERIODICLOG_DataLogContents",
(new Byte[] { 0x0C, 0x00, 0x24, 0x03 }).Reverse().ToArray(), true, false));
overAllResult.Add(_currentGenesis_WriteRegister("PERIODICLOG_DataLogPeriod",
(new Byte[] { 0x00, 0x00, 0x00, 0x3C }).Reverse().ToArray(), true, false));
overAllResult.Add(_currentGenesis_WriteRegister("PERIODICLOG_FixedDateDayOfMonth",
(new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray(), true, false));
overAllResult.Add(_currentGenesis_WriteRegister("PERIODICLOG_FixedDateReadingContents",
(new Byte[] { 0x0C, 0x00, 0x24, 0x03 }).Reverse().ToArray(), true, false));
overAllResult.Add(_currentGenesis_WriteRegister("PERIODICLOG_PeriodicLogLifeTimeCounter",
(new Byte[] { 0x00, 0x00, 0x00, 0x0 }).Reverse().ToArray(), true, false));
overAllResult.Add(_currentGenesis_WriteRegister("PERIODICLOG_ResetCounter",
(new Byte[] { 0x00, 0x00, 0x00, 0x0 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("POWERMON_BatterySelection",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, false));
overAllResult.Add(_currentGenesis_WriteRegister("POWERMON_WarnFromClamp",
(new Byte[] { 0x25, 0x89, 0x06, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_WakeupInterval",
(new Byte[] { 0x00, 0x00, 0x00, 0x03 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_MbusState",
(new Byte[] { 0x00, 0x00, 0x00, 0x07 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_UtcTimeOffset",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
//// just read later //overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_FrequencyIndicator}).Reverse(), true, true);
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_HistoricalAlarmsDays",
(new Byte[] { 0x00, 0x00, 0x00, 0x1D }).Reverse().ToArray(), true, true));
//Original
//0x11, 0x12, 0x13, 0x14, 0x21, 0x22, 0x23, 0x24, 0x31, 0x32, 0x33, 0x34, 0x41, 0x42, 0x43, 0x44
//Write to register
//0x14, 0x13, 0x12, 0x11, 0x24, 0x23, 0x22, 0x21, 0x34, 0x33, 0x32, 0x31, 0x44, 0x43, 0x42, 0x41
//good one
//E6-C8-88-00-DE-B8-68-C0-D6-A8-48-80-CE-98-28-40
//0E-05-04-04-00-14-30-68-D0-14-0E-80-40-63-93-73-22
//&bad one
//0E-05-04-05-00-00-88-C8-E6-C0-68-B8-DE-80-48-A8-D6-40-28-98-CE
//0E-05-04-04-00-22-73-93-63-40-80-0E-14-D0-68-30-14
if (useRadio)
{
//var funkadresse = new List<byte>();
//funkadresse.AddRange((new Byte[] { 0xE6, 0xC8, 0x88, 0x00 }).ToArray());
//funkadresse.AddRange((new Byte[] { 0xDE, 0xB8, 0x68, 0xC0 }).ToArray());
//funkadresse.AddRange((new Byte[] { 0xD6, 0xA8, 0x48, 0x80 }).ToArray());
//funkadresse.AddRange((new Byte[] { 0xCE, 0x98, 0x28, 0x40 }).ToArray());
//overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_EncryptionKey", funkadresse.ToArray(),
// true, true));
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_EncryptionKey",
(new Byte[]
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}).ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_RadioAddress", radioParams.RadioAddress, true, true));
if (radioParams.PowerLevel > 0 && radioParams.PowerLevelOption > 0 && radioParams.ImpedanceCodeNew > 0 && radioParams.ImpedanceCodeOption > 0)
{
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_PowerLevel", radioParams.PowerLevel, true, true));
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_PowerLevelOption", radioParams.PowerLevelOption, true, true));
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_ImpedanceCodeNew", radioParams.ImpedanceCodeNew, true, true));
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_ImpedanceCodeOption", radioParams.ImpedanceCodeOption, true, true));
}
else
{
base.currentProcessState = ProductionProcessState.Error;
return;
}
//_currentGenesis_WriteRegister("SENSUSRADIO_CustomerText",
// (new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, false);
}
overAllResult.Add(wirteRegisterInBetweenCheck("SYSTEM_CalendarSeconds", Convert.ToInt32(ts.TotalSeconds)));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_SealDisplay",
(new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray(), true, true));
////radio end state!
//overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_SystemState",
// (new Byte[] { 0x00, 0x00, 0x00, 0xFF }).Reverse().ToArray(), true, false));
//overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_SystemState",
// (new Byte[] { 0x00, 0x00, 0x00, 0x02 }).Reverse().ToArray(), true, true));
var ret = true;
int done = 0;
foreach (var cTask in overAllResult)
{
cTask.RunSynchronously();
if (cTask.Result.Item2)
{
base.NewStatus($"set Parameter {cTask.Result.Item1} succeed");
}
else
{
base.NewStatus($"set Parameter {cTask.Result.Item1} failed");
ret = false;
break;
}
base.Progress = 20 + ((Int32)Math.Round(((Double)done / (Double)overAllResult.Count) * 80, 0));
done = done + 1;
}
if (!ret)
{
base.currentProcessState = ProductionProcessState.Error;
}
else
{
currentProcessState = ProductionProcessState.Done;
}
}
}
}
@@ -1,355 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Controls;
using Newtonsoft.Json;
using ProductionUiCordonel.ProductionProcesses.Enum;
using ProductionUiCordonel.UserControls.FinalTest;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
namespace ProductionUiCordonel.ProductionProcesses.Actions
{
public class ProductionProcessFinalParametrizationFix : BaseProductionProcess
{
private UcPlain UserControl;
public ProductionProcessFinalParametrizationFix()
{
name = "Final Parametrization";
currentProcessState = ProductionProcessState.Waiting;
}
public override void InitUserControl()
{
UserControl = new UcPlain();
}
public override UserControl GetControl()
{
if (UserControl == null)
{
throw new Exception($"UserControl {GetType()} not Ready");
}
return UserControl;
}
public override void ProcessDone()
{
//clear
}
public Task<Tuple<String, Boolean>> _currentGenesis_WriteRegister<T>(String reg, T value, Boolean waitForResult = true,
Boolean checkRegister = false)
{
return new Task<Tuple<String, Boolean>>(() =>
{
var ret = Meter.WriteRegister(reg, value, waitForResult, checkRegister);
if (!ret)
{
ret = Meter.WriteRegister(reg, value, waitForResult, checkRegister);
}
return new Tuple<String, Boolean>(reg, ret);
});
}
public Task<Tuple<String, Boolean>> wirteRegisterInBetweenCheck(String reg, Int32 value, Boolean waitForResult = true,
Boolean checkRegister = false, Int32 addToCheck = 10, Int32 addFromCheck = 0)
{
return new Task<Tuple<String, Boolean>>(() =>
{
Meter.WriteRegister(reg, value, waitForResult);
var accucalValue = RegisterConverter.ConvertTo<Int32>(Meter.ReadRegister(reg));
if (accucalValue >= (value + addFromCheck) && value <= accucalValue + (addToCheck))
{
return new Tuple<String, Boolean>(reg, true);
}
return new Tuple<String, Boolean>(reg, false);
});
}
public override void StartProcess()
{
Progress = 0;
RadioConfigurationParams radioParams;
{
var url = "http://localhost:56011/api/FinalCheck/";
url += $"GetRadioConfiguration?PcbId={Meter.PcbId}&withPressure=false";
var http = (HttpWebRequest)WebRequest.Create(url);
http.Method = "GET";
http.Timeout = 20000;
var response = (HttpWebResponse)http.GetResponse();
var responseStream = response.GetResponseStream();
if (responseStream == null)
{
throw new InvalidOperationException();
}
using (var sr = new StreamReader(responseStream))
{
var responseJson = sr.ReadToEnd();
radioParams = JsonConvert.DeserializeObject<RadioConfigurationParams>(responseJson);
}
}
Progress = 20;
var overAllResult = new List<Task<Tuple<String, Boolean>>>();
//611.219.280
//
//Alarms
////_currentGenesis.WriteRegister("SENSUSRADIO_MainAlarmMask?
////_currentGenesis.WriteRegister("SENSUSRADIO_ExtendedAlarmMask ?
//CUSTOMER_AlarmBroadcastMask?
//CUSTOMER_AlarmEnableMask ?
//CUSTOMER_AlarmVisualAutoClearMask?
//CUSTOMER_AlarmVisualMask ?
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_SealDisplay",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
//Meter.SetLcdText(true, null);
//overAllResult.Add(_currentGenesis_WriteRegister(Register.Customer.TriggerAlarmCancel,
// (new Byte[] { 0xFF, 0xFF, 0xFF, 0xFF }).Reverse().ToArray(), true, false));
overAllResult.Add(wirteRegisterInBetweenCheck("CUSTOMER_LeakFlowThreshold", 375, true, true, 1, -1));
overAllResult.Add(wirteRegisterInBetweenCheck("CUSTOMER_ExcessFlowVolumeThreshold", 25000, true, true, 1, -1));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_ExcessFlowTimeThreshold",
(new Byte[] { 0x00, 0x00, 0x00, 0x05 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_LeakTimeThreshold",
(new Byte[] { 0x00, 0x00, 0x4E, 0xC0 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_Locale",
(new Byte[] { 0x00, 0x00, 0x00, 0xD0 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_PressureHighDelay",
(new Byte[] { 0x00, 0x00, 0x02, 0x58 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_PressureHighThreshold",
(new Byte[] { 0x00, 0x0B, 0x71, 0xB0 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_PressureLowDelay",
(new Byte[] { 0x00, 0x00, 0x02, 0x58 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_PressureLowThreshold",
(new Byte[] { 0x00, 0x00, 0x75, 0x30 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_ReverseFlowTimeThreshold",
(new Byte[] { 0x00, 0x00, 0x00, 0x78 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_TemperatureHighDelay",
(new Byte[] { 0x00, 0x00, 0x01, 0x2C }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_TemperatureHighThreshold",
(new Byte[] { 0x00, 0x00, 0x01, 0xF4 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_TemperatureLowDelay",
(new Byte[] { 0x00, 0x00, 0x01, 0x2C }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_TemperatureLowThreshold",
(new Byte[] { 0x00, 0x00, 0x00, 0x14 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_ArrowThreshold",
(new Byte[] { 0x00, 0x1E, 0x84, 0x80 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_DisplayPow10",
(new Byte[] { 0x00, 0x00, 0x00, 0xFD }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_DisplayUnits",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_ForwardArrow",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_HardErrorLimit",
(new Byte[] { 0x00, 0x00, 0x00, 0x04 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_LedMode",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
//overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_LowFlowMaxPeriod", (new Byte[] { 0x00, 0x00, 0x00, 0x3C }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_LowFlowMaxPeriod", 60 << 16, true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_LowFlowThreshold",
(new Byte[] { 0x00, 0x00, 0x00, 0xC8 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_MeterSize",
(new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_PipeFillingDelay",
(new Byte[] { 0x00, 0x00, 0x00, 0x1E }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_ResetAccumulators",
(new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray()));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_SampleRate",
(new Byte[] { 0x00, 0x00, 0x00, 0x02 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_MinValidAmplitude",
(new Byte[] { 0x25, 0x80, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_MaxValidDeltaToF",
(new Byte[] { 0x00, 0x03, 0xC6, 0x5E }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_UpdateThreshold",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("IRDA_AdapterPresenceLimit",
(new Byte[] { 0x00, 0x00, 0x00, 0x60 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("IRDA_PulseReportRate",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_FlowPoint",
(new Byte[] { 0x00, 0x00, 0x00, 0x02 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_FlowUnits",
(new Byte[] { 0x00, 0x00, 0x00, 0x04 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_PressureOffset",
(new Byte[] { 0x00, 0x00, 0x03, 0xF5 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_PressurePresent",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_PressureRate",
(new Byte[] { 0x00, 0x0D, 0xBB, 0xA0 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_PressureUnits",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_PulseLength",
(new Byte[] { 0x00, 0x00, 0x00, 0x07 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_PulseMode",
(new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_PulseWeight",
(new Byte[] { 0x00, 0x00, 0x27, 0x10 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("METROLOGYASST_TemperatureUnits",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("POWERMON_BatterySelection",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray()));
overAllResult.Add(_currentGenesis_WriteRegister("POWERMON_WarnFromClamp",
(new Byte[] { 0x25, 0x89, 0x06, 0x00 }).Reverse().ToArray(), true, true));
//// just read later //overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_FrequencyIndicator}).Reverse(), true, true);
//Original
//0x11, 0x12, 0x13, 0x14, 0x21, 0x22, 0x23, 0x24, 0x31, 0x32, 0x33, 0x34, 0x41, 0x42, 0x43, 0x44
//Write to register
//0x14, 0x13, 0x12, 0x11, 0x24, 0x23, 0x22, 0x21, 0x34, 0x33, 0x32, 0x31, 0x44, 0x43, 0x42, 0x41
//good one
//E6-C8-88-00-DE-B8-68-C0-D6-A8-48-80-CE-98-28-40
//0E-05-04-04-00-14-30-68-D0-14-0E-80-40-63-93-73-22
//&bad one
//0E-05-04-05-00-00-88-C8-E6-C0-68-B8-DE-80-48-A8-D6-40-28-98-CE
//0E-05-04-04-00-22-73-93-63-40-80-0E-14-D0-68-30-14
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_WakeupInterval",
(new Byte[] { 0x00, 0x00, 0x00, 0x03 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_MbusState",
(new Byte[] { 0x00, 0x00, 0x00, 0x07 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_UtcTimeOffset",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_HistoricalAlarmsDays",
(new Byte[] { 0x00, 0x00, 0x00, 0x1D }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("PERIODICLOG_AverageFlowPeriod",
(new Byte[] { 0x00, 0x00, 0x00, 0x05 }).Reverse().ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("PERIODICLOG_DataLogContents",
(new Byte[] { 0x0C, 0x00, 0x24, 0x03 }).Reverse().ToArray()));
overAllResult.Add(_currentGenesis_WriteRegister("PERIODICLOG_DataLogPeriod",
(new Byte[] { 0x00, 0x00, 0x00, 0x3C }).Reverse().ToArray()));
overAllResult.Add(_currentGenesis_WriteRegister("PERIODICLOG_FixedDateDayOfMonth",
(new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray()));
overAllResult.Add(_currentGenesis_WriteRegister("PERIODICLOG_FixedDateReadingContents",
(new Byte[] { 0x0C, 0x00, 0x24, 0x03 }).Reverse().ToArray()));
overAllResult.Add(_currentGenesis_WriteRegister("PERIODICLOG_PeriodicLogLifeTimeCounter",
(new Byte[] { 0x00, 0x00, 0x00, 0x0 }).Reverse().ToArray()));
overAllResult.Add(_currentGenesis_WriteRegister("PERIODICLOG_ResetCounter",
(new Byte[] { 0x00, 0x00, 0x00, 0x0 }).Reverse().ToArray(), true, true));
//var funkadresse = new List<byte>();
//funkadresse.AddRange((new Byte[] { 0xE6, 0xC8, 0x88, 0x00 }).ToArray());
//funkadresse.AddRange((new Byte[] { 0xDE, 0xB8, 0x68, 0xC0 }).ToArray());
//funkadresse.AddRange((new Byte[] { 0xD6, 0xA8, 0x48, 0x80 }).ToArray());
//funkadresse.AddRange((new Byte[] { 0xCE, 0x98, 0x28, 0x40 }).ToArray());
//overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_EncryptionKey", funkadresse.ToArray(),
// true, true));
//overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_EncryptionKey",
// (new Byte[]
// {
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
// }).ToArray(), true, true));
var funkadresse = new List<Byte>();
funkadresse.AddRange((new Byte[] { 0x0E, 0x9F, 0xAC, 0xC6 }).ToArray());
funkadresse.AddRange((new Byte[] { 0xA0, 0xE1, 0xEF, 0x1C }).ToArray());
funkadresse.AddRange((new Byte[] { 0x3B, 0x27, 0xA4, 0x9B }).ToArray());
funkadresse.AddRange((new Byte[] { 0xAC, 0x21, 0x65, 0x35 }).ToArray());
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_EncryptionKey", funkadresse.ToArray(), true, true));
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_RadioAddress", radioParams.RadioAddress, true, true));
if (radioParams.PowerLevel > 0 && radioParams.PowerLevelOption > 0 && radioParams.ImpedanceCodeNew > 0 && radioParams.ImpedanceCodeOption > 0)
{
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_PowerLevel", radioParams.PowerLevel, true, true));
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_PowerLevelOption", radioParams.PowerLevelOption, true, true));
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_ImpedanceCodeNew", radioParams.ImpedanceCodeNew, true, true));
overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_ImpedanceCodeOption", radioParams.ImpedanceCodeOption, true, true));
}
else
{
currentProcessState = ProductionProcessState.Error;
return;
}
//_currentGenesis_WriteRegister("SENSUSRADIO_CustomerText",
// (new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, false);
var ts = DateTimeOffset.UtcNow - new DateTimeOffset(2000, 1, 1, 0, 0, 0, new TimeSpan(0));
overAllResult.Add(wirteRegisterInBetweenCheck("SYSTEM_CalendarSeconds", Convert.ToInt32(ts.TotalSeconds)));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_SealDisplay",
(new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray(), true, true));
////radio end state!
//overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_SystemState",
// (new Byte[] { 0x00, 0x00, 0x00, 0xFF }).Reverse().ToArray(), true, false));
//overAllResult.Add(_currentGenesis_WriteRegister("SENSUSRADIO_SystemState",
// (new Byte[] { 0x00, 0x00, 0x00, 0x02 }).Reverse().ToArray(), true, true));
var ret = true;
Int32 done = 0;
foreach (var cTask in overAllResult)
{
cTask.RunSynchronously();
if (cTask.Result.Item2)
{
NewStatus($"set Parameter {cTask.Result.Item1} succeed");
}
else
{
NewStatus($"set Parameter {cTask.Result.Item1} failed");
ret = false;
break;
}
Progress = 20 + ((Int32)Math.Round((done / (Double)overAllResult.Count) * 80, 0));
done += 1;
}
if (!ret)
{
currentProcessState = ProductionProcessState.Error;
}
else
{
currentProcessState = ProductionProcessState.Done;
}
}
}
}
@@ -1,273 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;
using ProductionUiCordonel.ProductionProcesses.Enum;
using ProductionUiCordonel.UserControls.FinalTest;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Logic.ProductionOrderCore;
namespace ProductionUiCordonel.ProductionProcesses.Actions
{
public class ProductionProcessFinalParametrizationVakoCsd : BaseProductionProcess
{
private UcPlain UserControl;
private ProductionProcessConnect connectProcess;
private Boolean hasRadio;
public ProductionProcessFinalParametrizationVakoCsd(ProductionProcessConnect connectProcess, Boolean registerHasRadio = true)
{
this.connectProcess = connectProcess;
name = "Kunden Parametrierung";
currentProcessState = ProductionProcessState.Waiting;
hasRadio = registerHasRadio;
}
public override void InitUserControl()
{
UserControl = new UcPlain
{
lblHl =
{
Content = name
},
lblText =
{
Content = "Dieser Vorgang kann bis zu 1 Minute dauern!"
}
};
}
public override UserControl GetControl()
{
if (UserControl == null)
{
throw new Exception($"UserControl {GetType()} not Ready");
}
return UserControl;
}
public override void ProcessDone()
{
//clear
}
public Task<Tuple<String, Boolean>> _currentGenesis_WriteRegister<T>(String reg, T value, Boolean waitForResult = true,
Boolean checkRegister = false)
{
return new Task<Tuple<String, Boolean>>(() =>
{
Task.Delay(10).Wait();
var ret = Meter.WriteRegister(reg, value, waitForResult, checkRegister);
if (!ret)
{
ret = Meter.WriteRegister(reg, value, waitForResult, checkRegister);
}
return new Tuple<String, Boolean>(reg, ret);
});
}
public Task<Tuple<String, Boolean>> wirteRegisterInBetweenCheck(String reg, Int32 value, Boolean waitForResult = true,
Int32 addToCheck = 10, Int32 addFromCheck = 0)
{
return new Task<Tuple<String, Boolean>>(() =>
{
Meter.WriteRegister(reg, value, waitForResult);
var actualValue = RegisterConverter.ConvertTo<Int32>(Meter.ReadRegister(reg));
if (actualValue >= (value + addFromCheck) && value <= actualValue + (addToCheck))
{
return new Tuple<String, Boolean>(reg, true);
}
return new Tuple<String, Boolean>(reg, false);
});
}
private void doStartProcessLoop()
{
var remainingRetries = 4;
while (remainingRetries >= 0)
{
try
{
while (connectProcess.asyncProgrammingParametersTask.Status == TaskStatus.Running)
{
Thread.Sleep(500);
}
if (connectProcess.asyncProgrammingParametersTask.Status == TaskStatus.RanToCompletion
&& connectProcess.asyncProgrammingParametersTask.Result != null
&& connectProcess.asyncProgrammingParametersTask.Result.Any())
{
var myJsonResponse = connectProcess.asyncProgrammingParametersTask.Result;
Progress = 15;
//fix
// CUSTOMER_LeakFlowThreshold
//CUSTOMER_ExcessFlowVolumeThreshold
var overAllResult = new List<Task<Tuple<String, Boolean>>>();
//611.219.280
//
var ts = DateTimeOffset.UtcNow - new DateTimeOffset(2000, 1, 1, 0, 0, 0, new TimeSpan(0));
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_SealDisplay",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true));
// keien frq aber radio parameter
if (!Meter.RadioFrequencyMhz.HasValue && myJsonResponse.Any(a => a.RegisterName.Contains("RADIO")))
{
throw new ApplicationException("Radio inconsistent between meter and programmingparameters");
}
// eine frequen aber keine radio parameter
if (Meter.RadioFrequencyMhz.HasValue && !myJsonResponse.Any(a => a.RegisterName.Contains("RADIO")))
{
throw new ApplicationException("Radio inconsistent between meter and programmingparameters");
}
foreach (var item in myJsonResponse.Where(m => m.Source == ProgrammingSource.Vako))
{
if (item.RegisterName == "CUSTOMER_LeakFlowThreshold")
{
}
//todo: find a better sln for SENSUSRADIO_EncryptionKey
//SENSUSRADIO_EncryptionKey dont reserve SENSUSRADIO_EncryptionKey!
if (item.RegisterName.ToLower().Contains("displaypow".ToLower()) || item.RegisterName.ToLower().Contains("METROLOGYASST_FlowPoint".ToLower()))
{
overAllResult.Add(_currentGenesis_WriteRegister(item.RegisterName, new[] { item.RegisterValue.ToArray()[0] }, true, true));
}
else if (item.RegisterName.ToLower().Contains("SENSUSRADIO_EncryptionKey".ToLower()))
{
overAllResult.Add(_currentGenesis_WriteRegister(item.RegisterName, item.RegisterValue.ToArray(), true, true));
}
else
{
overAllResult.Add(_currentGenesis_WriteRegister(item.RegisterName, item.RegisterValue.Reverse().ToArray(), true, true));
}
//else if (!item.RegisterName.ToLower().Contains("apparrangement"))
//{
// overAllResult.Add(_currentGenesis_WriteRegister(item.RegisterName, item.RegisterValue.Reverse().ToArray(), true, true));
//}
}
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_AlarmEnableMask", new Byte[] { 0x00, 0x00, 0x80, 0x53 }.Reverse().ToArray()));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_AlarmBroadcastMask", new Byte[] { 0x00, 0x00, 0x1f, 0xdc }.Reverse().ToArray()));
overAllResult.Add(_currentGenesis_WriteRegister("CUSTOMER_AlarmVisualMask", new Byte[] { 0x00, 0x00, 0x1f, 0xdc }.Reverse().ToArray()));
foreach (var item in myJsonResponse.Where(m => m.Source == ProgrammingSource.Csd))
{
//todo: find a better sln for SENSUSRADIO_EncryptionKey
//SENSUSRADIO_EncryptionKey dont reserve SENSUSRADIO_EncryptionKey!
if (item.RegisterName.ToLower().Contains("SENSUSRADIO_EncryptionKey".ToLower()))
{
overAllResult.Add(_currentGenesis_WriteRegister(item.RegisterName, item.RegisterValue.ToArray(), true, true));
}
else if (!item.RegisterName.ToLower().Contains("apparrangement"))
{
overAllResult.Add(_currentGenesis_WriteRegister(item.RegisterName, item.RegisterValue.Reverse().ToArray(), true, true));
}
}
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_ResetAccumulators",
(new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray()));
overAllResult.Add(wirteRegisterInBetweenCheck("SYSTEM_CalendarSeconds", Convert.ToInt32(ts.TotalSeconds)));
overAllResult.Add(wirteRegisterInBetweenCheck("IRDA_MfgDate", Convert.ToInt32(ts.TotalSeconds)));
overAllResult.Add(_currentGenesis_WriteRegister("NFC_EraseRMA",
(new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray()));
if (Meter.RadioFrequencyMhz.HasValue)
{
overAllResult.Add(_currentGenesis_WriteRegister("LOGGER_TriggerLogFlush",
(new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray()));
overAllResult.Add(_currentGenesis_WriteRegister("PERIODICLOG_ResetCounter",
(new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray()));
}
overAllResult.Add(_currentGenesis_WriteRegister("GENESISFLOW_SealDisplay",
(new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray(), true, true));
var ret = true;
Int32 done = 0;
foreach (var cTask in overAllResult)
{
try
{
Thread.Sleep(50);
cTask.RunSynchronously();
if (cTask.Result.Item2)
{
NewStatus($"Schreiben des Parameter {cTask.Result.Item1} war erfolgreich");
}
else
{
if (cTask.Result.Item1.ToLower().Contains("Metersize".ToLower()) || cTask.Result.Item1.ToLower().Contains("NFC_EraseRMA".ToLower()) || cTask.Result.Item1.ToLower().Contains("CUSTOMER_ExcessFlowVolumeThreshold".ToLower()) || cTask.Result.Item1.ToLower().Contains("CUSTOMER_LeakFlowThreshold".ToLower()) || cTask.Result.Item1.ToLower() == "SENSUSRADIO_MetroRadioLifeTimeCounter".ToLower())
{
NewStatus($"Warnung! Fehler beim Schreiben des Parameters {cTask.Result.Item1}");
}
else
{
NewStatus($"Warnung! Fehler beim Schreiben des Parameters {cTask.Result.Item1}");
ret = false;
break;
}
}
}
catch (Exception)
{
if (cTask.Result.Item1 != "GENESISFLOW_MaxDeltaToFRange")
{
throw;
}
}
Progress = 15 + ((Int32)Math.Round((done / (Double)overAllResult.Count) * 85, 0));
done += 1;
}
if (!ret)
{
}
else
{
currentProcessState = ProductionProcessState.Done;
return;
}
remainingRetries -= 1;
}
else
{
currentProcessState = ProductionProcessState.Error;
remainingRetries = -1;
NewStatus("Warnung! Fehler beim Laden des Parametersatzes");
}
}
catch (Exception ex)
{
currentProcessState = ProductionProcessState.Error;
NewStatus($"Warnung! Fehler beim Behandeln von {ex.Message}");
remainingRetries = remainingRetries - 1;
}
}
}
public override void StartProcess()
{
doStartProcessLoop();
}
}
}
@@ -1,107 +0,0 @@
using System;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Threading;
using ProductionUiCordonel.ProductionProcesses.Enum;
using ProductionUiCordonel.UserControls.FinalTest;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
namespace ProductionUiCordonel.ProductionProcesses.Actions
{
public class ProductionProcessFullReadOut : BaseProductionProcess
{
private UcFullReadOut UserControl;
public ProductionProcessFullReadOut()
{
name = "Sicherung des Zählerzustandes";
currentProcessState = ProductionProcessState.Waiting;
}
public override void InitUserControl()
{
UserControl = new UcFullReadOut();
}
public override UserControl GetControl()
{
if (UserControl == null)
{
throw new Exception($"UserControl {GetType()} not Ready");
}
return UserControl;
}
public override void ProcessDone()
{
//clear
}
public override void StartProcess()
{
try
{
UserControl.lblHl.Dispatcher.Invoke(DispatcherPriority.Send,
new Action(() => { UserControl.lblHl.Content = "Read out Registers"; }
));
var oldConfigEntry = Meter.Configuration.UseRegisterWatchService;
if (oldConfigEntry)
{
Meter.Configuration.UseRegisterWatchService = false;
}
var total = Meter.GetRegistersDic().Keys.ToList();
var done = 0;
foreach (var dic in total)
{
try
{
var done1 = done;
UserControl.lblText.Dispatcher.Invoke(DispatcherPriority.Send,
new Action(() =>
{
UserControl.lblText.Content = $"Read {dic.GetIdent()}({done1}/{total.Count})";
}
));
if (dic.RegisterName.ToLower() != "password" && dic.RegisterDetail.Privilege.Lvl8 != Access.WO)
{
switch (dic.GetIdent().ToLower())
{
case "sensusradio_encryptionkey":
Meter.ReadRegister(dic.GetIdent(), 32);
break;
default:
if (dic.DataType == typeof(UInt64) || dic.DataType == typeof(Int64))
{
Meter.ReadRegister(dic.GetIdent(), 32);
}
else
{
Meter.ReadRegister(dic.GetIdent());
}
break;
}
}
Progress = (Int32)Math.Round(((Double)done / total.Count) * 100, 0);
}
catch (Exception )
{
//todo: error logger
}
done += 1;
}
Meter.StoreCurrentRegisterDic();
Meter.Configuration.UseRegisterWatchService = oldConfigEntry;
currentProcessState = ProductionProcessState.Done;
}
catch (Exception )
{
currentProcessState = ProductionProcessState.Error;
}
}
}
}
@@ -1,86 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using Newtonsoft.Json;
using ProductionUiCordonel.ProductionProcesses.Enum;
using ProductionUiCordonel.UserControls.FinalTest;
using Xylem.Common.CommonCore.Configuration;
using Xylem.Common.Logic.ProductionOrderCore;
using Xylem.Common.Logic.SoftwareAccessHelper;
namespace ProductionUiCordonel.ProductionProcesses.Actions
{
public class ProductionProcessGetFinalParametrizationVakoCsd : BaseProductionProcess
{
public IList<ProgrammingParameters> Result = new List<ProgrammingParameters>();
private UcPlain UserControl;
public ProductionProcessGetFinalParametrizationVakoCsd()
{
name = "Hole Kunden Parametrierung";
currentProcessState = ProductionProcessState.Waiting;
}
public override void InitUserControl()
{
UserControl = new UcPlain();
UserControl.lblHl.Content = name;
UserControl.lblText.Content = "Kann bis zu 1 Minute dauern!";
}
public override UserControl GetControl()
{
if (UserControl == null)
{
throw new Exception($"UserControl {GetType()} not Ready");
}
return UserControl;
}
public override void ProcessDone()
{
//clear
}
private void doStartProcessLoop()
{
var retriesRemaining = 2;
while (retriesRemaining >= 0)
{
try
{
Progress = 0;
//var url = $"http://localhost:56011/api/FinalCheck/GetProgrammingParameters?PcbID={Meter.PcbId}";
var url = $"{ServiceUrls.GenesisFinalCheckServiceUrl()}GetProgrammingParameters?PcbID={Meter.PcbId}";
var csdRet = LocalWebRequest.GetRequest(url, 60000);
Result = JsonConvert.DeserializeObject<IList<ProgrammingParameters>>(csdRet);
Progress = 100;
if (!Result.Any())
{
currentProcessState = ProductionProcessState.Error;
}
else
{
currentProcessState = ProductionProcessState.Done;
return;
}
retriesRemaining -= 1;
}
catch (Exception )
{
currentProcessState = ProductionProcessState.Error;
retriesRemaining -= 1;
}
}
}
public override void StartProcess()
{
doStartProcessLoop();
}
}
}
@@ -1,155 +0,0 @@
using ProductionUiCordonel.ProductionProcesses.Enum;
using ProductionUiCordonel.UserControls.FinalTest;
using System;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd;
namespace ProductionUiCordonel.ProductionProcesses.Actions
{
public class ProductionProcessPasswordFile : BaseProductionProcess
{
private UcPasswordFile _userControl;
private MeterPwdDb _pwdContainer = new MeterPwdDb();
private MeterPwdFile _meterPwdFile;
public ProductionProcessPasswordFile()
{
name = "PasswordFile";
currentProcessState = ProductionProcessState.Waiting;
}
public override void InitUserControl()
{
_userControl = new UcPasswordFile();
}
public override UserControl GetControl()
{
if (_userControl == null)
{
throw new Exception($"UserControl {GetType()} not Ready");
}
return _userControl;
}
private void updateImage(Image ctlImage, Boolean? success = null)
{
if (success.HasValue)
{
if (success.Value)
{
}
}
}
public override void StartProcess()
{
var ret = MeterPwdHandlerDb.RequestPwdFileFromDb(Meter.PcbId, out _pwdContainer);
updateImage(_userControl.RequestPassword, ret);
Progress += 16;
Meter.Logout();
ret = Meter.Login();
//If the password file is written, the "Password" from _pwdContainer is overwritten from Skeleton-key
//to the "level 8 password" form the LuDB server. The DB password column is the current login password
//for production. Initially the "Password" is the Skeleton-key which can not be part of the list of
//Passwords. The level 8 password is the last in the _pwdContainer list of passwords.
if (Encoding.UTF8.GetString(_pwdContainer.ListOfPasswords.Last()) == _pwdContainer.Password)
{
NewStatus("Passwörter schon geschrieben. Prozess wird übersprungen");
currentProcessState = ProductionProcessState.Done;
return;
}
//Get the passwords from LuDB, insert the skeleton-kay and generate the password file
if (ret)
{
ret = CreatePasswordFile();
}
updateImage(_userControl.CreateFile, ret);
Progress += 16;
//Write the password file to the Cordonel, read it back and make a binary verification
if (ret)
{
ret = WriteAndVerifyPasswordFileToMeter();
}
updateImage(_userControl.WriteToMeter, ret);
Progress += 16;
//Check if new password is in the meter ... even if the file write wasn't successful
if (ret)
{
Meter.Logout();
ret = Meter.Login(Encoding.UTF8.GetString(_pwdContainer.ListOfPasswords.Last()));
}
//Password in Production DB can be updated (overwrite Skeleton-key in Password column with
//password level 8 from LuDB) because the login with installed password file succeeded,
//this is the indicator, that the password file is valid.
if (ret)
{
ret = MeterPwdHandlerDb.SetPwdFromSkeletonToLvl8inDd(Meter.PcbId, _pwdContainer);
}
updateImage(_userControl.UpdatePassword, ret);
Progress += 16;
if (ret)
{
ret = Meter.Logout();
}
updateImage(_userControl.Logout, ret);
Progress += 16;
//test the production database password
if (ret)
{
Meter.ClearPassword();
ret = Meter.Login();
}
updateImage(_userControl.CheckNewPassword, ret);
Progress = 100;
if (!ret)
{
currentProcessState = ProductionProcessState.Error;
}
else
{
currentProcessState = ProductionProcessState.Done;
}
}
public override void ProcessDone()
{
//clear
}
#region realWork
public Boolean CreatePasswordFile()
{
_meterPwdFile = new MeterPwdFile(Meter);
return _meterPwdFile.BuildPwdFile(_pwdContainer.ListOfPasswords, 1, _pwdContainer.Skeleton);
}
public Boolean WriteAndVerifyPasswordFileToMeter()
{
if (_meterPwdFile.UnlockEraseWriteMeterPwdFile())
{
if (_meterPwdFile.WriteMeterPwdFile())
{
return _meterPwdFile.VerifyMeterPwdFile();
}
}
return false;
}
#endregion
}
}
@@ -1,473 +0,0 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using ProductionUiCordonel.ProductionProcesses.Enum;
using ProductionUiCordonel.UserControls.FinalTest;
using SIRTCOM;
using System;
using System.Configuration;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Threading;
using Xylem.Common.CommonCore.Configuration;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Logic.SoftwareAccessHelper;
using Button = System.Windows.Controls.Button;
using UserControl = System.Windows.Controls.UserControl;
namespace ProductionUiCordonel.ProductionProcesses.Actions
{
public class ProductionProcessRadioCheck : BaseProductionProcess
{
private UcRadioCheck UserControl;
public ProductionProcessRadioCheck()
{
name = "Radio Check";
currentProcessState = ProductionProcessState.Waiting;
}
public override void InitUserControl()
{
UserControl = new UcRadioCheck();
UserControl.OnRetryRequested += delegate { GetRssi(); };
UserControl.OnResetRequested += delegate { Reset(); };
UserControl.OnForceAPam += delegate { forcePush(); };
UserControl.OnDoneRequested += delegate
{
currentProcessState = ProductionProcessState.Done;
};
}
public override UserControl GetControl()
{
if (UserControl == null)
{
throw new Exception($"UserControl {GetType()} not Ready");
}
return UserControl;
}
public override void ProcessDone()
{
if (SENSUSRADIO_WakeupInterval != null)
{
var ret = Meter.WriteRegister("SENSUSRADIO_WakeupInterval", SENSUSRADIO_WakeupInterval, true, true);
if (!ret)
{
currentProcessState = ProductionProcessState.Error;
}
}
//clear
}
private Byte[] SENSUSRADIO_WakeupInterval;
public override void StartProcess()
{
//readout adress, freq,
if (Meter.RadioFrequencyMhz.HasValue)
{
var freq = RegisterConverter.ConvertTo<UInt32>(Meter.ReadRegister("SENSUSRADIO_FrequencyIndicator"));
RadioAdress = RegisterConverter.ConvertTo<UInt32>(Meter.ReadRegister("SENSUSRADIO_RadioAddress"));
Comport = Convert.ToByte(ConfigurationManager.AppSettings[$"SirtComport{freq}"]);
var enc = Meter.ReadRegister("SENSUSRADIO_EncryptionKey", 16);
EncyptionKey = LocalWebRequest.GetRequest($"{ServiceUrls.GenesisFinalCheckServiceUrl()}GetEncryptionKey?PcbID={Meter.PcbId}", 80000).ToUpper().Trim('\"');
if (!EncyptionKey.Equals(BitConverter.ToString(enc.ToArray()).Replace("-", "")))
{
MessageBox.Show("Funkschlüssel überprüfen!");
}
EncyptionKey = BitConverter.ToString(enc.ToArray()).Replace("-", "");
//if (freq == 868)
//{
// Comport = 8;
//}
//else
//{
// Comport = 7;
//}
BoxNr = Convert.ToByte(ConfigurationManager.AppSettings["SirtBoxNr"]);
DebugMessage($"Start RadioCheck Comport:{Comport} ,BoxNr:{BoxNr}, freq: {freq}, RadioAddress:{RadioAdress}s\r\n");
Meter.WriteRegister("SENSUSRADIO_IrDAModulePresent", 1, true, true);
SENSUSRADIO_WakeupInterval = Meter.ReadRegister("SENSUSRADIO_WakeupInterval");
Meter.WriteRegister("SENSUSRADIO_WakeupInterval", 0);
GetRssi();
}
else
{
currentProcessState = ProductionProcessState.Done;
}
}
#region FormerForm
private SaveData saveData;
private Statemashine stm;
private static HttpClient httpclient;
private Byte? RSSI_PAM_Min;
private Byte? RSSI_PAM_Max;
private Byte? RSSI_PAM;
private Byte? RSSI_SEMI_Min;
private Byte? RSSI_SEMI_Max;
private Byte? RSSI_SEMI;
public Byte BoxNr;
public Int32 Ordernumber;
public UInt32 RadioAdress;
public Byte Comport;
public String EncyptionKey = "";
private void enableBtn(Button btn, Boolean enable)
{
btn.Dispatcher.Invoke(DispatcherPriority.Send,
new Action(() =>
{
btn.IsEnabled = enable;
}
));
}
private void SetUi()
{
UserControl.SetUi(new UcRadioCheck.UIContainer(RSSI_PAM_Min, RSSI_SEMI_Min, RSSI_PAM, RSSI_SEMI, RSSI_PAM_Max, RSSI_SEMI_Max));
}
private async void GetRssi()
{
//StartProcess();
enableBtn(UserControl.btnGetRssi, false);
DebugMessage("");
saveData = new SaveData();
SetUi();
//Meter.WriteRegister("SENSUSRADIO_PowerLevel", 67, true, true);
//Meter.WriteRegister("SENSUSRADIO_PowerLevelOption", 59, true, true);
//Meter.WriteRegister("SENSUSRADIO_ImpedanceCodeNew", 2992, true, true);
//Meter.WriteRegister("SENSUSRADIO_ImpedanceCodeOption", 2936, true, true);
try
{
await GetRssiMinMaxAsync(BoxNr, Ordernumber);
SetUi();
GetRSSI((Int32)RadioAdress, Comport, EncyptionKey);
SetUi();
}
catch (Exception ex)
{
DebugMessage($"Fehler in buttonSende_Click():\r\n{ex.Message}\r\n{ex.InnerException}", true);
}
enableBtn(UserControl.btnGetRssi, true);
}
private void clearRssi()
{
RSSI_PAM_Min = null;
RSSI_PAM_Max = null;
RSSI_PAM = null;
RSSI_SEMI_Min = null;
RSSI_SEMI_Max = null;
RSSI_SEMI = null;
}
private void Reset()
{
if (!(stm == null))
{
stm.ClearPam();
stm.Stop();
}
DebugMessage("Reset\r\n");
clearRssi();
SetUi();
StartProcess();
}
private class SaveData
{
public Int32 RadioAdress { get; set; }
public Byte RSSI_PAM { get; set; }
public Byte RSSI_SEMI { get; set; }
public Int32 LimitId { get; set; }
public Byte Result { get; set; }
}
/// <summary>
/// Speichert das gefüllte saveData Objekt via SaveRSSI Webservice
/// </summary>
private async void SaveDataToDB()
{
DebugMessage("Calling SaveRSSI Webservice...", true);
try
{
saveData.RadioAdress = (Int32)RadioAdress; // 0-4294967295
String url = $"{ServiceUrls.GenesisRadioCheckServiceUrl()}SaveRSSI";
String body = JsonConvert.SerializeObject(saveData);
await PostDataAsync(url, body);
DebugMessage(" Ready\r\n", true);
}
catch (Exception ex)
{
DebugMessage($"Fehler in SaveDataToDB():\r\n{ex.Message}\r\n{ex.InnerException}", true);
}
}
private async Task GetRssiMinMaxAsync(Byte BoxNr, Int32 FaNr)
{
//DebugMessage("Calling GetLimits Webservice...", true);
try
{
// TODO anpassen
String url = $"{ServiceUrls.GenesisRadioCheckServiceUrl()}GetLimits/{BoxNr}/{FaNr}";
String body = await GetResponseAsync(url);
JObject o = JObject.Parse(body);
saveData.LimitId = Convert.ToInt32(o.SelectToken("LimitId").ToString());
RSSI_PAM_Min = Convert.ToByte(o.SelectToken("RSSI_PAM_Min").ToString());
RSSI_PAM_Max = Convert.ToByte(o.SelectToken("RSSI_PAM_Max").ToString());
RSSI_SEMI_Min = Convert.ToByte(o.SelectToken("RSSI_SEMI_Min").ToString());
RSSI_SEMI_Max = Convert.ToByte(o.SelectToken("RSSI_SEMI_Max").ToString());
DebugMessage(" Ready\r\n", true);
}
catch (Exception ex)
{
saveData.LimitId = 0;
RSSI_PAM_Min = 100;
RSSI_PAM_Max = 200;
RSSI_SEMI_Min = 100;
RSSI_SEMI_Max = 200;
// RSSI_PAM_Min RSSI_PAM_Max RSSI_SEMI_Min RSSI_SEMI_Max
//145 164 182
// throw new Exception("Fehler in GetRssiMinMax()", ex);
}
}
private void GetRSSI(Int32 RadioAdress, Int32 ByteCOMPort, String strKey)
{
RSSI_PAM = null;
RSSI_SEMI = null;
stm = new Statemashine();
Byte P81_1 = 0;
saveData.RadioAdress = RadioAdress;
stm.initialise((Byte)ByteCOMPort);
Wrapper wrp = new Wrapper();
stm.StartLoggin("C:\\GenesisLog\\log.txt");
// ist wohl nicht nötig da der SIRT richtig konfiguroert ist:
// wrp.ActivateSirt(byte modul1, byte modul2, byte actTimeout, out byte outP12_01, out byte outP12_02);
DebugMessage($"Sending PAM to {RadioAdress.ToString()} ...", true);
stm.ClearPam();
stm.Timeout_s = 60;
if (strKey == "")
{
// Dummy PAM 00 OHNE Funkschlüssel
stm.SendPAM(RadioAdress.ToString(), "00", "", P81_1, 1, 0, "", "0", false);
}
else
{
// Dummy PAM 00 mit Funkschlüssel
stm.SendPAM(RadioAdress.ToString(), "00", "", P81_1, 1, 0, strKey, "0", true);
}
stm.OnStateChanged += Stm_OnStateChanged;
stm.Start(); // Start polling
DebugMessage(" OK\r\n", true);
Meter.ResetAlarm(GenesisMeter.Alarm.EMPTY_PIPE);
Thread.Sleep(5000);
//Meter.WriteRegister("SENSUSRADIO_SYSTEMSTATE", 0x01, true, false);
forcePush();
if (false)
{
GotRSSICallback(1, 1);
}
}
private void forcePush()
{
Meter.WriteRegister("SENSUSRADIO_SYSTEMSTATE", 0x55);
Meter.WriteRegister("SENSUSRADIO_SYSTEMSTATE", 0x02);
}
public void DebugMessage(String value, Boolean Append = false)
{
NewStatus(value);
}
private void GotRSSICallback(Byte Rssi_Semi_Sirt, Byte Rssi_Pam_Cord)
{
// RSSI beim Empfangen der PAM gemessen vom Cordonel (Sendestärke des SIRT verpackt in SEMI, byte 20)
DebugMessage($"RSSI Cord of PAM (Sirt => Cord):{Rssi_Pam_Cord}\r\n", true);
RSSI_PAM = Rssi_Pam_Cord;
RSSI_SEMI = Rssi_Semi_Sirt;
// RSSI beim Empfangen der SEMI gemessen vom SIRT (Sendestärke des Cordonel in byte 4)
DebugMessage($"RSSI Sirt of SEMI (Cord => Sirt):{Rssi_Semi_Sirt}\r\n", true);
saveData.RSSI_PAM = Rssi_Pam_Cord;
saveData.RSSI_SEMI = Rssi_Semi_Sirt;
saveData.Result = 1;
if (RSSI_PAM < RSSI_PAM_Min)
{
saveData.Result = 0;
}
if (RSSI_SEMI < RSSI_SEMI_Min)
{
saveData.Result = 0;
}
if (RSSI_PAM > RSSI_PAM_Max)
{
saveData.Result = 0;
}
if (RSSI_SEMI > RSSI_SEMI_Max)
{
saveData.Result = 0;
}
SaveDataToDB();
SetUi();
if (saveData.Result == 1)
{
UserControl.ViewResult();
}
}
private void Stm_OnStateChanged(Object sender, MyEventArgs e)
{
// our Pam
PAM Pam = stm.GetPAM(1);
if (Pam.State == PAM.PAMStates.Timeout)
{
DebugMessage($"Timeout nach {Pam.GetTime()} s\r\n");
}
if (Pam.CountSemi > 0)
{
// we recieved the SEMI
//btnGetRssi.IsEnabled = true;
stm.Stop();
stm.ClearPam();
String HexbytesSEMI = Pam.Semi.HexData.Replace(" ", "");
Byte[] SEMI_bytes = new Byte[HexbytesSEMI.Length / 2];
for (Int32 i = 0; i < HexbytesSEMI.Length; i += 2)
{
SEMI_bytes[i / 2] = Convert.ToByte(HexbytesSEMI.Substring(i, 2), 16);
}
// SEMI Status
if (SEMI_bytes[19] == 0)
{
// RSSI beim Empfangen der SEMI gemessen vom Sirt
Byte Semi_Rssi_Sirt = SEMI_bytes[2];
// RSSI beim Empfangen der PAM gemessen vom Cordonel
Byte Pam_Rssi_Cord = SEMI_bytes[20];
GotRSSICallback(Semi_Rssi_Sirt, Pam_Rssi_Cord);
}
else
{
// SEMI Status ist nicht 0.
DebugMessage(Pam.Semi.GetPamErrorMessage(), true);
}
stm.ClearPam();
}
else
{
DebugMessage($"BUPS:{Pam.CountBup}\r\n", true);
}
}
public static async Task<String> PostDataAsync(String url, String data)
{
httpclient = new HttpClient();
// 20 Sekunden Timeout
httpclient.Timeout = new TimeSpan(0, 0, 20);
StringContent queryString = new StringContent(data, Encoding.UTF8, "application/json");
HttpResponseMessage response = await httpclient.PostAsync(url, queryString);
response.EnsureSuccessStatusCode();
String content = await response.Content.ReadAsStringAsync();
return content;
}
private static async Task<String> GetResponseAsync(String url)
{
httpclient = new HttpClient();
// 20 Sekunden Timeout
httpclient.Timeout = new TimeSpan(0, 0, 20);
HttpResponseMessage response = httpclient.GetAsync(url).Result;
response.EnsureSuccessStatusCode();
String content = await response.Content.ReadAsStringAsync();
return content;
}
#endregion
}
}
@@ -1,72 +0,0 @@
using ProductionUiCordonel.ProductionProcesses.Enum;
using ProductionUiCordonel.UserControls.FinalTest;
using System;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Threading;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
namespace ProductionUiCordonel.ProductionProcesses.Actions
{
public class ProductionProcessRegisterWatch : BaseProductionProcess
{
private UcFullReadOut UserControl;
public ProductionProcessRegisterWatch()
{
name = "Sicherung des Zählerzustandes";
currentProcessState = ProductionProcessState.Waiting;
}
public override void InitUserControl()
{
UserControl = new UcFullReadOut();
}
public override UserControl GetControl()
{
if (UserControl == null)
{
throw new Exception($"UserControl {GetType()} not Ready");
}
return UserControl;
}
public override void ProcessDone()
{
//clear
}
public override void StartProcess()
{
UserControl.lblHl.Dispatcher.Invoke(DispatcherPriority.Send,
new Action(() => { UserControl.lblHl.Content = "Read out Registers"; }
));
var total = Meter.GetRegistersDic().Keys.Where(n => n.RegisterName == "TemperatureMeasurePeriod" || n.RegisterName == "PressureMeasurePeriod" || n.RegisterName == "METROLOGYASST_PressureRate").ToList();
var done = 0;
foreach (var dic in total)
{
var ret = Meter.ReadRegister(dic.GetIdent());
if (dic.AppName == "SENSUSRADIO")
{
Meter.WriteLog($"Register Watch: {dic.RegisterName} = { RegisterConverter.ConvertTo<UInt16>(ret)}");
}
else
{
Meter.WriteLog($"Register Watch: {dic.RegisterName} = { RegisterConverter.ConvertTo<UInt32>(ret)}");
}
}
done += 1;
//Meter.StoreCurrentRegisterDic();
//Meter.Configuration.UseRegisterWatchService = oldConfigEntry;
currentProcessState = ProductionProcessState.Done;
}
}
}
@@ -1,258 +0,0 @@
using ProductionUiCordonel.ProductionProcesses.Enum;
using ProductionUiCordonel.UserControls.FinalTest;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows.Controls;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile;
namespace ProductionUiCordonel.ProductionProcesses.Actions
{
public class ProductionProcessShippingMode : BaseProductionProcess
{
private UcPlain UserControl;
private Boolean doRestart;
private Boolean hasRadio;
public ProductionProcessShippingMode(Boolean restart = false, Boolean registerHasRadio = true)
{
name = "Versandbereitschaft herstellen";
currentProcessState = ProductionProcessState.Waiting;
doRestart = restart;
hasRadio = registerHasRadio;
}
public override void InitUserControl()
{
UserControl = new UcPlain();
UserControl.lblHl.Content = name;
UserControl.lblText.Content = "Display und Alarme für den Kunden einstellen.";
}
public override UserControl GetControl()
{
if (UserControl == null)
{
throw new Exception($"UserControl {GetType()} not Ready");
}
return UserControl;
}
public override void ProcessDone()
{
//clear
}
public override void StartProcess()
{
hasRadio = Meter.RadioFrequencyMhz.HasValue;
if (hasRadio)
{
if (Meter.StoreAllConfigurations())
{
if (doRestart)
{
var CheckValuesAfterBoot = new List<Tuple<String, Byte[]>>
{
new Tuple<String, Byte[]>("METROLOGYASST_PulseLength",
Meter.ReadRegister("METROLOGYASST_PulseLength")),
new Tuple<String, Byte[]>("POWERMON_BatteryQuantity",
Meter.ReadRegister("POWERMON_BatteryQuantity")),
new Tuple<String, Byte[]>("GENESISFLOW_MeterSize", Meter.ReadRegister("GENESISFLOW_MeterSize")),
new Tuple<String, Byte[]>("GENESISFLOW_CalFactor1", Meter.ReadRegister("GENESISFLOW_CalFactor1")),
new Tuple<String, Byte[]>("SENSUSRADIO_RadioAddress", Meter.ReadRegister("SENSUSRADIO_RadioAddress"))
};
var _meterResetPsu = new MeterResetPsu(Meter)
{
StoreConfigEnable = true
};
var rebootSucceeded = _meterResetPsu.MeterResetPsuExecute();
Thread.Sleep(10000);
if (!Meter.Login())
{
Meter.WriteLog("relogin first attempt Failed");
Thread.Sleep(5000);
if (!Meter.Login())
{
Meter.WriteLog("relogins Failed");
currentProcessState = ProductionProcessState.Error;
}
}
Boolean? hasDiffrences = null;
foreach (var item in CheckValuesAfterBoot)
{
if (!item.Item2.SequenceEqual(Meter.ReadRegister(item.Item1)))
{
hasDiffrences = true;
}
else
{
if (!hasDiffrences.HasValue)
{
hasDiffrences = false;
}
}
}
if (rebootSucceeded && hasDiffrences.HasValue && !hasDiffrences.Value)
{
Meter.WriteRegister("SENSUSRADIO_SystemState", 0xFF);
Meter.Logout();
Thread.Sleep(5000);
if (!Meter.Login())
{
Meter.WriteLog("relogin first attempt Failed");
Thread.Sleep(5000);
if (!Meter.Login())
{
Meter.WriteLog("relogin Failed");
currentProcessState = ProductionProcessState.Error;
}
}
var SENSUSRADIO_MainAlarmMask = Meter.ReadRegister("SENSUSRADIO_MainAlarmMask");
var SENSUSRADIO_ExtendedAlarmMask = Meter.ReadRegister("SENSUSRADIO_ExtendedAlarmMask");
var SENSUSRADIO_SystemState = Meter.ReadRegister("SENSUSRADIO_SystemState");
if (SENSUSRADIO_SystemState[0] == 0x02 && SENSUSRADIO_MainAlarmMask[0] != 0)
{
currentProcessState = ProductionProcessState.Done;
}
else
{
Meter.WriteLog("resetRadio Failed");
currentProcessState = ProductionProcessState.Error;
}
}
else
{
Meter.WriteLog("reboot compare failed");
currentProcessState = ProductionProcessState.Error;
}
}
else
{
Meter.ResetAlarm(GenesisMeter.Alarm.ALL);//(true, new byte[] { 0x00, 0x00, 0x00, 0x01 });
Meter.SetLcdText(true, new Byte[] { 0x00, 0x00, 0x00, 0x01 });
Meter.ResetAlarm(GenesisMeter.Alarm.EMPTY_PIPE);//(true, new byte[] { 0x00, 0x00, 0x00, 0x01 });
Meter.WriteRegister("SENSUSRADIO_SystemState", 0xFF);
Meter.Logout();
Thread.Sleep(5000);
if (!Meter.Login())
{
Thread.Sleep(5000);
if (!Meter.Login())
{
currentProcessState = ProductionProcessState.Error;
return;
}
}
var SENSUSRADIO_MainAlarmMask = Meter.ReadRegister("SENSUSRADIO_MainAlarmMask");
var SENSUSRADIO_ExtendedAlarmMask = Meter.ReadRegister("SENSUSRADIO_ExtendedAlarmMask");
var SENSUSRADIO_SystemState = Meter.ReadRegister("SENSUSRADIO_SystemState");
if (SENSUSRADIO_SystemState[0] == 0x02 && SENSUSRADIO_MainAlarmMask[0] != 0)
{
currentProcessState = ProductionProcessState.Done;
}
else
{
Meter.WriteLog("resetRadio Failed");
currentProcessState = ProductionProcessState.Error;
}
}
}
else
{
currentProcessState = ProductionProcessState.Error;
}
return;
}
else
{
if (Meter.StoreAllConfigurations())
{
if (doRestart)
{
var CheckValuesAufterBoot = new List<Tuple<String, Byte[]>>();
CheckValuesAufterBoot.Add(new Tuple<String, Byte[]>("METROLOGYASST_PulseLength", Meter.ReadRegister("METROLOGYASST_PulseLength")));
CheckValuesAufterBoot.Add(new Tuple<String, Byte[]>("POWERMON_BatteryQuantity", Meter.ReadRegister("POWERMON_BatteryQuantity")));
CheckValuesAufterBoot.Add(new Tuple<String, Byte[]>("GENESISFLOW_MeterSize", Meter.ReadRegister("GENESISFLOW_MeterSize")));
CheckValuesAufterBoot.Add(new Tuple<String, Byte[]>("GENESISFLOW_CalFactor1", Meter.ReadRegister("GENESISFLOW_CalFactor1")));
var _meterResetPsu = new MeterResetPsu(Meter)
{
StoreConfigEnable = true
};
var rebootSucceeded = _meterResetPsu.MeterResetPsuExecute();
Thread.Sleep(10000);
Meter.Login();
Boolean? hasDiffrences = null;
foreach (var item in CheckValuesAufterBoot)
{
if (!item.Item2.SequenceEqual(Meter.ReadRegister(item.Item1)))
{
hasDiffrences = true;
}
else
{
if (!hasDiffrences.HasValue)
{
hasDiffrences = false;
}
}
}
Meter.ResetAlarm(GenesisMeter.Alarm.ALL);//(true, new byte[] { 0x00, 0x00, 0x00, 0x01 });
Meter.SetLcdText(true, new Byte[] { 0x00, 0x00, 0x00, 0x01 });
Meter.ResetAlarm(GenesisMeter.Alarm.EMPTY_PIPE);//(true, new byte[] { 0x00, 0x00, 0x00, 0x01 });
if (rebootSucceeded && hasDiffrences.HasValue && !hasDiffrences.Value)
{
currentProcessState = ProductionProcessState.Done;
}
else
{
Meter.WriteLog("reboot compare failed");
currentProcessState = ProductionProcessState.Error;
}
}
else
{
currentProcessState = ProductionProcessState.Done;
}
return;
}
}
currentProcessState = ProductionProcessState.Error;
}
}
}
@@ -1,162 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using ProductionUiCordonel.ProductionProcesses.Enum;
using ProductionUiCordonel.UserControls.FinalTest;
namespace ProductionUiCordonel.ProductionProcesses.Actions
{
public class ProductionProcessSpecialConfig : BaseProductionProcess
{
private UcDialog UserControl;
public Boolean DialogIsDone { get; }
public ProductionProcessSpecialConfig()
{
DialogIsDone = false;
name = "Customer Parameterization";
currentProcessState = ProductionProcessState.Waiting;
}
public override void InitUserControl()
{
UserControl = new UcDialog();
}
public override UserControl GetControl()
{
if (UserControl == null)
{
throw new Exception($"UserControl {GetType()} not Ready");
}
return UserControl;
}
public override void ProcessDone()
{
//clear
}
private void StepDone()
{
currentProcessState = ProductionProcessState.Done;
}
private void StepRadio()
{
UserControl.SetDesc("Set EncryptionKey");
var radioOptionList = new List<UcDialog.DialogOption>();
var radioDefault = new UcDialog.DialogOption("old default", "same as iperl", () =>
{
var funkadresse = new List<Byte>();
funkadresse.AddRange((new Byte[] { 0xE6, 0xC8, 0x88, 0x00 }).ToArray());
funkadresse.AddRange((new Byte[] { 0xDE, 0xB8, 0x68, 0xC0 }).ToArray());
funkadresse.AddRange((new Byte[] { 0xD6, 0xA8, 0x48, 0x80 }).ToArray());
funkadresse.AddRange((new Byte[] { 0xCE, 0x98, 0x28, 0x40 }).ToArray());
Meter.WriteRegister("SENSUSRADIO_EncryptionKey", funkadresse.ToArray(), true, true);
StepDone();
});
var upRadioOnly = new UcDialog.DialogOption("alpha", "000...", () =>
{
Meter.WriteRegister("SENSUSRADIO_EncryptionKey", (new Byte[]
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}).ToArray(), true, true);
StepDone();
});
var optOneLiter = new UcDialog.DialogOption("new default", "new default key", () =>
{
var funkadresse = new List<Byte>();
funkadresse.AddRange((new Byte[] { 0x0E, 0x9F, 0xAC, 0xC6 }).ToArray());
funkadresse.AddRange((new Byte[] { 0xA0, 0xE1, 0xEF, 0x1C }).ToArray());
funkadresse.AddRange((new Byte[] { 0x3B, 0x27, 0xA4, 0x9B }).ToArray());
funkadresse.AddRange((new Byte[] { 0xAC, 0x21, 0x65, 0x35 }).ToArray());
Meter.WriteRegister("SENSUSRADIO_EncryptionKey", funkadresse.ToArray(), true, true);
StepDone();
});
radioOptionList.Add(radioDefault);
radioOptionList.Add(upRadioOnly);
radioOptionList.Add(optOneLiter);
UserControl.SetDialogOptions(radioOptionList);
}
private void StepWakeUp()
{
UserControl.SetDesc("Set Radio WakeUp");
var wakeupOptionList = new List<UcDialog.DialogOption>();
var wakeUpDefault = new UcDialog.DialogOption("wakeup Water and Radio", "Default for nearly all", () =>
{
Meter.WriteRegister("SENSUSRADIO_WakeupInterval", (new Byte[] { 0x00, 0x00, 0x00, 0x03 }).Reverse().ToArray(), true, true);
StepRadio();
});
var wakeUpRadioOnly = new UcDialog.DialogOption("wakeup only Radio", "WakeupInterval to 6 (only radio)", () =>
{
Meter.WriteRegister("SENSUSRADIO_WakeupInterval", (new Byte[] { 0x00, 0x00, 0x00, 0x06 }).Reverse().ToArray(), true, true);
StepRadio();
});
var pulseOptOneLiter = new UcDialog.DialogOption("wakeup off", "WakeupInterval to 0, no Radio active (e.g. USA))", () =>
{
Meter.WriteRegister("SENSUSRADIO_WakeupInterval", (new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true);
StepRadio();
});
wakeupOptionList.Add(wakeUpDefault);
wakeupOptionList.Add(wakeUpRadioOnly);
wakeupOptionList.Add(pulseOptOneLiter);
UserControl.SetDialogOptions(wakeupOptionList);
}
private void StepPulse()
{
UserControl.SetDesc("Set Pulse Configuration");
var pulseOptionList = new List<UcDialog.DialogOption>();
var pulseOptOff = new UcDialog.DialogOption("Pulse off", "set pulsemode to off", () =>
{
Meter.WriteRegister("METROLOGYASST_PulseLength", (new Byte[] { 0x00, 0x00, 0x00, 0x07 }).Reverse().ToArray(), true, true);
Meter.WriteRegister("METROLOGYASST_PulseMode", (new Byte[] { 0x00, 0x00, 0x00, 0x00 }).Reverse().ToArray(), true, true);
Meter.WriteRegister("METROLOGYASST_PulseWeight", (new Byte[] { 0x00, 0x00, 0x27, 0x10 }).Reverse().ToArray(), true, true);
StepWakeUp();
});
var pulseOptDefault = new UcDialog.DialogOption("Pulse Default", "1 pulse per 10 l, pulse mode a4 and 200ms pulse length", () =>
{
Meter.WriteRegister("METROLOGYASST_PulseLength", (new Byte[] { 0x00, 0x00, 0x00, 0x07 }).Reverse().ToArray(), true, true);
Meter.WriteRegister("METROLOGYASST_PulseMode", (new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray(), true, true);
Meter.WriteRegister("METROLOGYASST_PulseWeight", (new Byte[] { 0x00, 0x00, 0x27, 0x10 }).Reverse().ToArray(), true, true);
StepWakeUp();
});
var pulseOptOneLiter = new UcDialog.DialogOption("One Liter Pulse", "1 pulse per 1l, pulse mode a4 and 20ms pulse length", () =>
{
Meter.WriteRegister("METROLOGYASST_PulseLength", (new Byte[] { 0x00, 0x00, 0x00, 0x04 }).Reverse().ToArray(), true, true);
Meter.WriteRegister("METROLOGYASST_PulseMode", (new Byte[] { 0x00, 0x00, 0x00, 0x01 }).Reverse().ToArray(), true, true);
Meter.WriteRegister("METROLOGYASST_PulseWeight", (new Byte[] { 0x00, 0x00, 0x03, 0xE8 }).Reverse().ToArray(), true, true);
StepWakeUp();
});
pulseOptionList.Add(pulseOptOff);
pulseOptionList.Add(pulseOptDefault);
pulseOptionList.Add(pulseOptOneLiter);
UserControl.SetDialogOptions(pulseOptionList);
}
public override void StartProcess()
{
StepPulse();
}
}
}
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
{
@@ -2,9 +2,9 @@
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses
{
public class CheckOrderNumber : BaseProductionProcess
{
@@ -2,9 +2,9 @@
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses
{
public class CheckSerialNumber : BaseProductionProcess
{
@@ -3,7 +3,7 @@ using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses
{
public class ConnectCordonel : BaseProductionProcess
{
@@ -3,7 +3,7 @@ using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses
{
/// <summary>
/// Production process detects the serial number of the Cordonel.
@@ -3,9 +3,9 @@ using System.Collections.Generic;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses
{
public class ErrorHandler : BaseProductionProcess
{
@@ -8,7 +8,7 @@ using Xylem.Common.Logic.SoftwareAccessHelper;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses
{
/// <summary>
/// Request the serial number and radio address from DB.
@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
{
public interface IProcessStateDef
{
/// <summary>
/// Collection of state information
/// </summary>
List<ProcessStateStruct> StateContainer { get; }
/// <summary>
/// Get the process start struct linked to state of state machine
/// </summary>
/// <param name="stateMachineState">state to search for information text</param>
/// <returns>process state struct of the attached state</returns>
/// <remarks date="2025-Apr-15" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
ProcessStateStruct GetProcessStateStructOfState(ProcessState stateMachineState);
/// <summary>
/// Get the process linked to state of state machine
/// </summary>
/// <param name="stateMachineState">state to search for information text</param>
/// <returns>process attached to the state</returns>
/// <remarks date="2023-Feb-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
IProductionProcess GetProcessOfState(ProcessState stateMachineState);
/// <summary>
/// Get the current information of the state machine state
/// </summary>
/// <param name="stateMachineState">state to search for information text</param>
/// <returns>information attached to state depending on region</returns>
/// <remarks date="2023-Feb-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2024-Mar-18" author="Thomas Wiedebusch">
/// - If state == null return error message.
/// </remarks>
String GetStateInfoOfState(ProcessState? stateMachineState);
/// <summary>
/// Get the info text from production process
/// </summary>
/// <param name="process">process to look for state info text</param>
/// <returns>information attached to process depending on region</returns>
/// <remarks date="2023-Mar-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
String GetStateInfoOfProcess(IProductionProcess process);
/// <summary>
/// Get the state machine state of the text
/// </summary>
/// <param name="text">information text to search if state is assigned</param>
/// <returns>state attached to information text</returns>
/// <remarks date="2023-Feb-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
ProcessState GetStateOfInfoText(String text);
/// <summary>
/// Get the state machine state from for the process
/// </summary>
/// <param name="process">process to look for state info text</param>
/// <returns>state attached to process</returns>
/// <remarks date="2023-Mar-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
ProcessState GetStateOfProcess(IProductionProcess process);
/// <summary>
/// Get number of EOL relevant processes for statistic e.g. progress bar
/// </summary>
/// <returns>number of initialized processes</returns>
/// <remarks date="2023-Mar-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2024-Sep-01" author="Thomas Wiedebusch">
/// - Used marker for production relevant processes
/// </remarks>
Int32 GetNumberOfInitializedProcesses();
/// <summary>
/// Get a sorted list of all production processes in order of the execution sequence
/// </summary>
/// <returns>list of initialized processes</returns>
/// <remarks date="2024-Jul-15" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2024-Sep-01" author="Thomas Wiedebusch">
/// - Used marker for production relevant processes
/// </remarks>
/// <remarks date="2025-Apr-14" author="Thomas Wiedebusch">
/// - Changed to sorted list by production process number
/// </remarks>
List<IProductionProcess> GetAllEolRelevantProcesses();
}
}
@@ -0,0 +1,840 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Logic.ProductionToProductMapper.Files.Fw;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Const;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisStatus;
using Xylem.Common.Logic.ProductionOrderCore.OrderData;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.PickingProcesses
{
/// <summary>
/// Check all parameters needed as preparation for assembly.
/// </summary>
public class CheckProductionState : BaseProductionProcess
{
/// <summary>
/// Maximum of expected processing steps for this process to feed the GUI progress bar.
/// </summary>
private const Int32 MaxActualProgressSteps = 13;
/// <summary>
/// FW update package containing the ABC and the ADF.
/// </summary>
public List<FilePart> FwUpdatePackage
{
get; set;
}
/// <summary>
/// Ctor
/// </summary>
public CheckProductionState(String name) : base(name)
{
}
/// <summary>
/// Start database calls and meter readouts here for better testability and ability to fill the
/// objects manually by the test routine.
/// </summary>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public override StatusReturn PreExecuteProcess()
{
if (Meter == null || string.IsNullOrEmpty(Meter.PcbId) || Meter.PcbId.Length < GenesisMeter.MinPcbIdLength)
{
Meter?.WriteLog("PcbId requirements do not match");
return StatusReturn.Failed;
}
// Some statistics for GUI and internal processing
SignalRunningState(MaxActualProgressSteps);
// Get the requirement (Standard or special)
if (!GenesisStatusHandler.GetCordonelRequirementInfoFromDb(Meter.PcbId, ProductionRequirements,
out var error) || ProductionRequirements == null)
{
Meter.WriteLog(error);
ErrorMsgDispatcher(Resources.StrErrorMsgProductionRequirements);
return StatusReturn.Failed;
}
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
ActualProcessProgress++;
// Get clustered FileId of firmware package for production or on special requirement release candidates
var isSpecialActive = ProductionRequirements.IsSpecialActive != null &&
(Boolean)ProductionRequirements.IsSpecialActive &&
ProductionRequirements.IsSpecialApproved != null &&
(Boolean)ProductionRequirements.IsSpecialApproved;
if (string.IsNullOrEmpty(ProductionRequirements.FwVersion) ||
!MeterFwUpdate.SearchProdFwInDb(ProductionRequirements.FwVersion, out var parentFileId, out error,
!isSpecialActive) || parentFileId == null)
{
Meter.WriteLog(error);
ErrorMsgDispatcher(Resources.StrErrorMsgLoadingFwFromDb);
return StatusReturn.Failed;
}
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
ActualProcessProgress++;
// Get the real content of the FW package including all binaries of each app and the application description file
FwUpdatePackage = new List<FilePart>();
if (!MeterFwUpdate.GetFwPackageFromDb((Int32)parentFileId, FwUpdatePackage, out error) ||
FwUpdatePackage == null)
{
Meter.WriteLog(error);
ErrorMsgDispatcher(Resources.StrErrorMsgLoadingFwFromDb);
return StatusReturn.Failed;
}
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
ActualProcessProgress++;
// Read out the Cordonel and calculate remaining lifetime and fill the ProductionStatus
if (!GenesisStatusHandler.BuildLifeTimeInformation(Meter, ProductionStatus, out error))
{
Meter.WriteLog(error);
ErrorMsgDispatcher(Resources.StrErrorMsgLifeTimeCalculation);
return StatusReturn.Failed;
}
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
ActualProcessProgress++;
return StatusReturn.Okay;
}
/// <summary>
/// Check all parameters needed as preparation for completion to validate the status and the
/// remaining operational time. This includes special permissions for individual parameters.
/// The picking requirements including the special permissions are taken from the DB and can be
/// assigned to an order number (meaning for all devices produced under this order) or an individual
/// pcbId. The setup and calculation of the exceeded time and drained will be taken from the Cordonel
/// (read from registers in POWERMON).
/// Precondition: - The <see cref="GetSerialNumberAndRadioAddress"/> process has to
/// be completed as the PcbId and the order number is needed.
/// Checks:
/// - Requirements are collected,
/// - PcbId,
/// - Region,
/// - FW,
/// - LUT CRC,
/// - Meter size
/// - Drained battery load versus required remaining battery load in percent,
/// - Remaining run time in years,
/// </summary>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Roland Drahbesh">
/// - Initial.
/// </remarks>
public override StatusReturn ExecuteProcess()
{
#region ----------------------------------- Check Requirements --------------------------------------------
var statusReturn = CheckRequirements(out var feedbackMsg);
if (StatusReturn.Failed == statusReturn)
{
ErrorMsgDispatcher(feedbackMsg);
return StatusReturn.Failed;
}
SuccessMsgDispatcher(feedbackMsg);
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check Production Order ----------------------------------------
// Production order
if (StatusReturn.Failed == CheckProductionOrderNumber(out feedbackMsg))
{
ErrorMsgDispatcher(feedbackMsg);
return StatusReturn.Failed;
}
SuccessMsgDispatcher(feedbackMsg);
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check PCBID ---------------------------------------------------
// PCBID read from meter compared with requirement PcbIds
statusReturn = CheckPcbId(out feedbackMsg);
// Exit here with error
if (StatusReturn.Failed == statusReturn)
{
ErrorMsgDispatcher(feedbackMsg);
return StatusReturn.Failed;
}
SuccessMsgDispatcher(feedbackMsg);
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check Region --------------------------------------------------
if (StatusReturn.Failed == CheckRegion(out feedbackMsg))
{
ErrorMsgDispatcher(feedbackMsg);
return StatusReturn.Failed;
}
SuccessMsgDispatcher(feedbackMsg);
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check LUT CRC -------------------------------------------------
if (string.IsNullOrEmpty(CheckLutCrc(out feedbackMsg)))
{
ErrorMsgDispatcher(feedbackMsg);
return StatusReturn.Failed;
}
SuccessMsgDispatcher(feedbackMsg);
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check Meter Size ----------------------------------------------
if (string.IsNullOrEmpty(CheckMeterSize(out feedbackMsg)))
{
ErrorMsgDispatcher(feedbackMsg);
return StatusReturn.Failed;
}
SuccessMsgDispatcher(feedbackMsg);
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check Firmware and Apps ---------------------------------------
if (string.IsNullOrEmpty(CheckFwAndApps(out feedbackMsg)))
{
ErrorMsgDispatcher(feedbackMsg);
return StatusReturn.Failed;
}
SuccessMsgDispatcher(feedbackMsg);
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check Remaining Battery Load ----------------------------------
var erroneousSequenceStep = false;
if (StatusReturn.Failed == CheckRemainingBatteryLoad(out feedbackMsg))
{
ErrorMsgDispatcher(feedbackMsg);
//return StatusReturn.Failed;
// Do not return immediately to avoid multiple special-requirements on multiple errors
erroneousSequenceStep = true;
}
SuccessMsgDispatcher(feedbackMsg);
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check Remaining Life Time -------------------------------------
if (StatusReturn.Failed == CheckRemainingLifetime(out feedbackMsg))
{
ErrorMsgDispatcher(feedbackMsg);
//return StatusReturn.Failed;
// Do not return immediately to avoid multiple special-requirements on multiple errors
erroneousSequenceStep = true;
}
SuccessMsgDispatcher(feedbackMsg);
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check Storage Months ------------------------------------------
if (StatusReturn.Failed == CheckStorageMonths(out feedbackMsg))
{
ErrorMsgDispatcher(feedbackMsg);
//return StatusReturn.Failed;
// Do not return immediately to avoid multiple special-requirements on multiple errors
erroneousSequenceStep = true;
}
SuccessMsgDispatcher(feedbackMsg);
ActualProcessProgress++;
#endregion
#region ----------------------------------- Summarize Check Results ---------------------------------------
// One or more checks failed
if (erroneousSequenceStep)
return StatusReturn.Failed;
// Here everything went fine
SuccessMsgDispatcher(feedbackMsg);
ActualProcessProgress++;
// All test successfully passed reaching this line
return StatusReturn.Okay;
#endregion -------------------------------- Summarize Check Results ---------------------------------------
}
/// <summary>
/// Common routine to exit with error and user window with error message
/// </summary>
/// <param name="errorMsg"></param>
/// <remarks date="2024-Nov-06" author="Thomas Wiedebusch">
/// - Extracted from <see cref="ExecuteProcess"/> for better testability.
/// </remarks>
private StatusReturn ExitWithErrorAndUserWindow(String errorMsg)
{
ErrorMsgDispatcher(errorMsg);
MessageBox.Show(errorMsg, Resources.StrProcessStateError, MessageBoxButton.OK, MessageBoxImage.Error);
return StatusReturn.Failed;
}
/// <summary>
/// Check for requirement permission special or standard or none:
/// - If the special requirement is active all fields a filled with the according values from this "special"
/// requirement,
/// - Else those fields contain the "standard" requirements!
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_001]_Check_Requirements
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private StatusReturn CheckRequirements(out String feedbackMsg)
{
feedbackMsg = "";
if (ProductionRequirements == null)
{
feedbackMsg = Resources.StrErrorMsgProductionRequirements;
return StatusReturn.Failed;
}
// Check if requirement is active (free for production) and approved by a second person. Priority special
// requirement.
var isSpecialActive = false;
var isStandardActive = false;
if (ProductionRequirements.IsSpecialActive != null && (Boolean)ProductionRequirements.IsSpecialActive &&
ProductionRequirements.IsSpecialApproved != null && (Boolean)ProductionRequirements.IsSpecialApproved)
isSpecialActive = true;
if (ProductionRequirements.IsStandardActive != null && (Boolean)ProductionRequirements.IsStandardActive &&
ProductionRequirements.IsStandardApproved != null && (Boolean)ProductionRequirements.IsStandardApproved)
isStandardActive = true;
// At least one requirement needs to be active and a requirement number and version has to be set
if ((!isStandardActive && !isSpecialActive) ||
// The standard Id and version have always to be set even if the special is active
ProductionRequirements.StandardId == null || ProductionRequirements.StandardVersion == null ||
// If the special is active the version and Id have to be defined
(isSpecialActive && (ProductionRequirements.SpecialId == null ||
ProductionRequirements.SpecialVersion == null)))
{
feedbackMsg = Resources.StrErrorMsgRequirementInactive;
return StatusReturn.Failed;
}
feedbackMsg = isSpecialActive &&
ProductionRequirements.SpecialId != null &&
ProductionRequirements.SpecialVersion != null ?
Resources.StrWarningMsgSpecialRequirementActive +
$" Id.Ver: {ProductionRequirements.SpecialId}.{ProductionRequirements.SpecialVersion}" :
Resources.StrSuccessMsgRequirementActive +
$" Id.Ver {ProductionRequirements.StandardId}.{ProductionRequirements.StandardVersion}";
return StatusReturn.Okay;
}
/// <summary>
/// Check production order number between meter and requirements.
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_002]_Check_ProductionOrderNo
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private StatusReturn CheckProductionOrderNumber(out String feedbackMsg)
{
feedbackMsg = "";
if (Meter == null ||
ProductionRequirements == null ||
ProductionRequirements.ProductionOrderNr != Meter.OrderNumber)
{
feedbackMsg = Resources.StrErrorMsgRequirementProductionOrder;
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementProductionOrder} - " +
$"{Resources.StrLblOrderNumberTxt}: {Meter.OrderNumber}";
return StatusReturn.Okay;
}
/// <summary>
/// Check PcbId of meter and requirements. Mark PcbId as skipped if
/// production requirements PcbId is null as this is the indication that all Cordonels
/// assigned to this production order are produced according to this requirement.
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_003]_Check_PcbId
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private StatusReturn CheckPcbId(out String feedbackMsg)
{
feedbackMsg = "";
if (Meter == null ||
ProductionRequirements == null ||
Meter.PcbId == null ||
Meter.PcbId.Length < GenesisMeter.MinPcbIdLength)
{
feedbackMsg = Resources.StrErrorMsgRequirementPcbId;
return StatusReturn.Failed;
}
if (ProductionRequirements.PcbId == null)
{
feedbackMsg = Resources.StrSuccessMsgOrderBasedRequirements;
return StatusReturn.Okay;
}
if (Meter.PcbId.Equals(ProductionRequirements.PcbId.ToString()))
{
feedbackMsg = $"{Resources.StrSuccessMsgRequirementPcbId} - PcbId: {Meter.PcbId}";
return StatusReturn.Okay;
}
feedbackMsg = Resources.StrErrorMsgRequirementPcbId;
return StatusReturn.Failed;
}
/// <summary>
/// Check region between meter and requirements
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_004]_Check_Region
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private StatusReturn CheckRegion(out String feedbackMsg)
{
feedbackMsg = "";
if (ProductionRequirements == null ||
string.IsNullOrEmpty(ProductionRequirements.Region) ||
string.IsNullOrEmpty(Meter.Region) ||
!Meter.Region.Equals(ProductionRequirements.Region))
{
feedbackMsg = Resources.StrErrorMsgRequirementRegion;
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementRegion} - Region: {Meter.Region}";
return StatusReturn.Okay;
}
/// <summary>
/// Check Look-Up-Table (LUT) for metrology calibration values for the checksum (CRC) of this table
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_005]_Check_LUT_CRC
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private String CheckLutCrc(out String feedbackMsg)
{
feedbackMsg = "";
if (Meter == null ||
ProductionRequirements == null ||
string.IsNullOrEmpty(ProductionRequirements.LutCrc) ||
string.IsNullOrEmpty(Meter.LutCrc) ||
!Meter.LutCrc.Equals(ProductionRequirements.LutCrc))
{
feedbackMsg = Resources.StrErrorMsgRequirementLut;
return "";
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementLut} - LUT CRC: {Meter.LutCrc}";
return Meter.LutCrc;
}
/// <summary>
/// Check meter size
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_006]_Check_Meter_Size
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private String CheckMeterSize(out String feedbackMsg)
{
feedbackMsg = "";
if (Meter == null ||
ProductionRequirements == null || string.IsNullOrEmpty(ProductionRequirements.MeterSize) ||
string.IsNullOrEmpty(Meter.MeterSize) ||
MeterSizeConverter.ConvertMeterSizeNameToEnum(Meter.MeterSize) !=
MeterSizeConverter.ConvertMeterSizeNameToEnum(ProductionRequirements.MeterSize))
{
feedbackMsg = Resources.StrErrorMsgRequirementMeterSize;
return "";
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementMeterSize} - " +
$"{Resources.StrLblMeterSizeTxt}: {Meter.MeterSize}";
return ProductionRequirements.MeterSize;
}
/// <summary>
/// Check the installed firmware (FW) and all individual applications (Apps) installed to the meter:
/// - Take the FLEXNETVERSION as base information which FW is installed in the Cordonel and compare this with
/// the production-requirement,
/// - The downloaded FW package containing all Apps as application-binary-container (ABC) will be compared with
/// the reported Apps by the Cordonel for their version and CRC using the "<see cref="MeterFwUpdate"/> class,
/// - Crosscheck if the application-description-file (ADF) states all Apps which are installed in the Cordonel,
/// - Check the Core-Revision of the Cordonel against the ADF to guarantee that this FW is supported by the
/// Cordonel operational system (Breeze).
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_007]_Check_FW_and_Apps
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private String CheckFwAndApps(out String feedbackMsg)
{
feedbackMsg = "";
// Check installed FW version with FLEXNET information
var fwUpdate = new MeterFwUpdate(Meter);
if (Meter == null ||
ProductionRequirements == null ||
string.IsNullOrEmpty(ProductionRequirements.FwVersion) ||
string.IsNullOrEmpty(Meter.FwVersion) ||
!Meter.FwVersion.Equals(ProductionRequirements.FwVersion) ||
// Check if FW package has been loaded and contains at least the minimal required files
FwUpdatePackage == null ||
FwUpdatePackage.Count < MeterFwUpdate.MinAppsRequiredForOperation)
{
feedbackMsg += $"{Resources.StrErrorMsgRequirementFw}\n";
return "";
}
var fileApps = new List<FileApplications>();
if (FwUpdatePackage != null)
{
foreach (var file in FwUpdatePackage)
{
// load the ABC (application binary container)
if (file.FileName.Contains("binfile") &&
file.FileName.EndsWith(".bin") &&
// Sometimes the * complete *.bin is part of the binaries containing all app in one file.
// As this is not part of the ADF it has to be skipped from loading!!!!
!file.FileName.Contains("complete"))
{
var fileApplication = new FileApplications(file.FileName)
{
BinData = new List<Byte>(file.FileContent.ToList())
};
fileApps.Add(fileApplication);
}
// Load the ADF (application description file, containing the information
// of each application)
else if (file.FileName.Contains("product") && file.FileName.EndsWith(".txt"))
{
var enc = new System.Text.ASCIIEncoding();
var adf = enc.GetString(file.FileContent.ToArray());
fwUpdate.LoadPackageFileFromText(adf);
}
}
fwUpdate.LoadFileApps(fileApps);
}
var checkSucceeded = true;
// Compare every installed application for version and CRC with the required to be defined by the downloaded
// ABC (application binary container)
fwUpdate.CompareAllMeterAndFileApps();
if (// Validate package description file with required Apps (fileApps) and build the CORE REVISIONS MIN MAX
!fwUpdate.ValidateFileAppsWithPackageFile() ||
// Check if any meter Apps is outdated or does contain an un-matching CRC
Meter.MeterAppListVersion.Any(app => app.Status != MeterAppState.MeterAppUpToDate &&
app.Status != MeterAppState.MeterAppNotRequired))
{
feedbackMsg += $"{Resources.StrErrorMsgAppVersionOrCrc}\n";
checkSucceeded = false;
// Do not immediately return to summarize all errors following up
}
// The package description file has to be parsed in advance using the fwUpdate.ValidateFileAppsWithPackageFile()!!
if (!fwUpdate.CheckCoreRevision(Meter.CoreRevision))
{
feedbackMsg += $"{Resources.StrErrorMsgCoreRevision}\n";
// Do not immediately return to summarize all errors following up
checkSucceeded = false;
}
if (!checkSucceeded)
{
feedbackMsg += $"{Resources.StrErrorMsgRequirementFw}\n";
return "";
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementFw} - Firmware: {Meter.FwVersion}";
return Meter.FwVersion;
}
/// <summary>
/// Check the remaining battery load based on the thresholds which can be either:
/// - The AlternativeRequiredBatteryLoadPercent if the QuiescentCurrent_uA is set to a value greater than 50 uA
/// and the EstimatedProductionBatteryLoadPercent is greater than 0.0 %
/// and
/// - the MaxDrainedBatteryLoadPercentAssembly limits the threshold,
/// or only
/// - the MaxDrainedBatteryLoadPercentAssembly limits the threshold as absolut limit.
///
/// ATTENTION: The MaxDrainedBatteryLoadPercentAssembly forces the absolut limit even if the alternative
/// calculation is being used.
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_008]_Check_Remaining_Battery_Load
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private StatusReturn CheckRemainingBatteryLoad(out String feedbackMsg)
{
if (Meter == null ||
ProductionRequirements?.MaxDrainedBatteryLoadPercentAssembly == null ||
ProductionStatus == null)
{
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
return StatusReturn.Failed;
}
// If the values for calculation of drained battery load and exceeded lifetime are not accessible
if (ProductionStatus.DrainedBatteryLoad_uAs == 0 || ProductionStatus.ExceededLifeTime_s == 0)
{
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
return StatusReturn.Failed;
}
// Set up the alternative calculation values needed for given current and supplement for production.
// These values will be set by the requirement and have a higher priority than the standard calculation
// based on the drained battery load and the estimated prediction based on this and the time of operation.
if (ProductionRequirements.QuiescentCurrent_uA > GenesisMeter.QuiescentCurrent_uA &&
ProductionRequirements.EstimatedProductionBatteryLoadPercent > 0)
{
ProductionStatus.QuiescentCurrent_uA = ProductionRequirements.QuiescentCurrent_uA;
ProductionStatus.EstimatedProductionBatteryLoadPercent =
ProductionRequirements.EstimatedProductionBatteryLoadPercent;
}
// Calculate the power consumption
if (!GenesisStatusHandler.CalculateLifeTime(ProductionStatus))
{
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
return StatusReturn.Failed;
}
// Check battery load in percent and compare with requirements for limits for drained battery load
// For the alternative calculation based quiescent current and production supplement this will be used.
// Build message for required versus calculated lifetime
var threshold = ProductionStatus.AlternativeRequiredBatteryLoadPercent ??
ProductionRequirements.MaxDrainedBatteryLoadPercentAssembly;
// Create an output message addon for alternative power consumption calculation
var strAlternativeCalc = ProductionStatus.AlternativeRequiredBatteryLoadPercent == null
? ""
: $" - {Resources.StrMsgRequirementAbsolutLimitDrainedBattery}: " +
$"{ProductionRequirements.MaxDrainedBatteryLoadPercentAssembly:F2} %" +
$" ({Resources.StrMsgAlternativeDrainedBatteryCalculation})";
var batteryLoad = $"{Resources.StrMsgDrainedBattery}: " +
// Actual calculated battery load based on readings from meter
$"{ProductionStatus.DrainedBatteryLoadPercent:F2} %" +
$" - {Resources.StrMsgRequirementLimitDrainedBattery}: " +
// Required battery load from standard or special requirement
$"{threshold:F2} % {strAlternativeCalc}";
// This is the absolut limit even if the "AlternativeRequiredBatteryLoadPercent" will be used
if (ProductionStatus.DrainedBatteryLoadPercent >
ProductionRequirements.MaxDrainedBatteryLoadPercentAssembly)
{
feedbackMsg = $"{Resources.StrErrorMsg}: {batteryLoad}";
return StatusReturn.Failed;
}
// This is the limit given by the "AlternativeRequiredBatteryLoadPercent"
if (ProductionStatus.AlternativeRequiredBatteryLoadPercent != null &&
ProductionStatus.DrainedBatteryLoadPercent > ProductionStatus.AlternativeRequiredBatteryLoadPercent)
{
feedbackMsg = $"{Resources.StrErrorMsg}: {batteryLoad}";
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrSuccessMsg}: {batteryLoad}";
return StatusReturn.Okay;
}
/// <summary>
/// Check the remaining lifetime
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_009]_Check_Remaining_Lifetime
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private StatusReturn CheckRemainingLifetime(out String feedbackMsg)
{
if (Meter == null ||
ProductionRequirements?.RequiredLifeTimeYearsAssembly == null ||
ProductionRequirements.RequiredLifeTimeYearsAssembly <= 0 ||
ProductionStatus == null)
{
feedbackMsg = Resources.StrErrorMsgLifeTimeCalculation;
return StatusReturn.Failed;
}
// Calculate the power consumption
if (!GenesisStatusHandler.CalculateLifeTime(ProductionStatus))
{
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
return StatusReturn.Failed;
}
// Build message for required versus calculated lifetime
var lifeTime = $"{Resources.StrMsgRemainingLifeTime}: " +
// Actual calculated lifetime based on readings from meter
$"{ProductionStatus.RemainingLifeTimeYears:F2} " +
$"{Resources.StrMsgYears}" +
$" - {Resources.StrMsgRequiredLifeTime}: " +
// Required lifetime from special or standard requirement
$"{ProductionRequirements.RequiredLifeTimeYearsAssembly:F2} " +
$"{Resources.StrMsgYears}";
// Check required years for assembly
if (ProductionRequirements.RequiredLifeTimeYearsAssembly > ProductionStatus.RemainingLifeTimeYears)
{
feedbackMsg = $"{Resources.StrErrorMsg}: {lifeTime}";
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrSuccessMsg}: {lifeTime}";
return StatusReturn.Okay;
}
/// <summary>
/// Check the months between first battery activation and now
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_010]_Check_Storage_Months
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private StatusReturn CheckStorageMonths(out String feedbackMsg)
{
// Based on first attached battery and the POWEMON_TotalUsedSeconds (Kitron production date) and actual date
if (Meter == null ||
ProductionRequirements?.MaxStorageMonths == null ||
ProductionRequirements.MaxStorageMonths < 0.0 ||
ProductionStatus == null ||
ProductionStatus.DrainedBatteryLoad_uAs == 0 ||
ProductionStatus.ExceededLifeTime_s == 0)
{
// Storage months requirement or Kitron production date unknown
feedbackMsg = Resources.StrErrorMsgRequirementMaximumStorageMonthUnknown;
return StatusReturn.Failed;
}
// Calculate the power consumption
if (!GenesisStatusHandler.CalculateLifeTime(ProductionStatus) || ProductionStatus.BatteryAssemblyDateTimeUtc == null)
{
feedbackMsg = Resources.StrErrorMsgRequirementMaximumStorageMonthUnknown;
return StatusReturn.Failed;
}
var actualDate = DateTime.UtcNow.Date;
var batteryAssemblyDate = ProductionStatus.BatteryAssemblyDateTimeUtc.Value.Date;
// Give one day more as rounding issue
var expirationDate = batteryAssemblyDate.AddDays((Double)ProductionRequirements.MaxStorageMonths * GenesisStatusHandler.DAYS_PER_MONTH + 1.0);
var strStorageProduction = $"{Resources.StrLblKitronProductionDate}: " +
$"{ProductionStatus.BatteryAssemblyDateTimeUtc.Value:dd-MMM-yyyy} - " +
$"{Resources.StrLblExpirationDate}: {expirationDate:dd-MMM-yyyy} - " +
$"{Resources.StrLblMaxStorageMonths}: " +
$"{(Int32)ProductionRequirements.MaxStorageMonths} {Resources.StrLblMonths}";
if (actualDate >= expirationDate)
{
// Storage months exceeded
feedbackMsg = $"{Resources.StrErrorMsgRequirementMaximumStorageMonth} - " + strStorageProduction;
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementMaximumStorageMonth} - " + strStorageProduction;
return StatusReturn.Okay;
}
}
}
@@ -0,0 +1,178 @@
using System.Collections.Generic;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.PickingProcesses
{
/// <summary>
/// Concatenation for all production process states to feed the state machine:
/// - Production process class (representing a production process step), null if hidden process,
/// - attached state,
/// - state information to display a text message,
/// - production process number to define where to display in the overview,
/// - next state which should be executed after this succeeded,
/// [optional]:
/// - own error state,
/// - own break state,
/// - kick off state for parallel execution after starting this process in a separate task,
/// - wait for single process to finish successfully,
/// - wait for all processes to finish successfully,
/// - start always independent of any result, e.g. the error state needs to be started if called!
/// </summary>
/// <remarks date="2025-Apr-15" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public class PickingProcessStateDef : BaseProcessStatesDef
{
/// <summary>
/// State-machine definitions of production-processes which feeds the state-machine.
///
///
/// INSTRUCTION FOR PREPARATION:
/// -----------------------------
/// STEP 1: Define some states for your processes <see cref="ProcessState"/>
/// STEP 2: Implement your processes like <see cref="DetectCordonel"/>
/// STEP 3: Register and chain your states here
///
///
/// INSTRUCTION FOR NORMAL USAGE:
/// -----------------------------
/// Means one state automatically switched on successfully execution to the next required state.
///
/// Define the (e.g.):
/// productionProcess: new DetectCordonel(Resources.StrStateDetectCordonel),
/// processState: ProcessState.DetectCordonel,
/// processInfo: Resources.StrStateDetectCordonel,
/// productionProcessNo: 0,
/// nextStateOnSuccess: ProcessState.CheckNewMeter),
///
/// If a special state is needed on errorExitState or breakExitState these can be assigned. Otherwise, the
/// "errorExitState" is linked to the generic-error-state, the "breakExitState" to the abort-test-state.
///
///
/// INSTRUCTION FOR NON-BLOCKING MULTIPLE PROCESSES SIMULTANEOUSLY ACTIVITIES:
/// --------------------------------------------------------------------------
/// Sometimes production processes may be able to be executed simultaneously as they are independent of each
/// other. One example is a database access for data collection from different tables.
///
/// This is needed for non-blocking immediately new state change (not waiting for the successful execution of
/// the actual new assigned process).
///
/// Define the (e.g.):
///
/// productionProcess: new ConnectCordonel(Resources.StrStateConnectCordonel),
/// processState: ProcessState.ConnectCordonel,
/// processInfo: Resources.StrStateConnectCordonel,
/// productionProcessNo: 1,
/// // Avoid automatic change after completion as parallel process is active
/// nextStateOnSuccess: BaseProductionProcess.DoNotChangeStateAfterCompletion,
/// // Kick off parallel process execution after starting the "ConnectCordonel() task"
/// kickOffParallelState: ProcessState.GetSerialNumberAndRadioAddress),
///
///
/// INSTRUCTION TO DELAY THE START OF A PROCESS AND WAIT ON RESULTS OF OTHER PROCESS(ES):
/// -------------------------------------------------------------------------------------
/// Each new process execution can be delayed as it may need the result of another process which is active.
///
/// Wait for a single process to complete:
/// Set the "waitForSingleProcess" to the state of the process needed to wait for (e.g.):
///
/// productionProcess: new CheckProductionState(Resources.StrStateCheckProductionState),
/// processState: ProcessState.CheckProductionState,
/// processInfo: Resources.StrStateCheckProductionState,
/// productionProcessNo: 4,
/// // Avoid automatic change after completion as parallel process is active
/// nextStateOnSuccess: BaseProductionProcess.DoNotChangeStateAfterCompletion,
/// // Kick off parallel process execution after starting
/// kickOffParallelState: ProcessState.ProcessPasswordFile,
/// // Wait until this process has been successfully completed
/// waitForSingleProcess: ProcessState.ConnectCordonel),
///
/// Wait for all preceding processes to complete to be sure to get all data and all processed are succeeded:
/// Set the "waitForAllProcesses" to true (e.g.):
/// productionProcess: new ExecPasswordFile(Resources.StrStatePasswordFile),
/// processState: ProcessState.ProcessPasswordFile,
/// processInfo: Resources.StrStatePasswordFile,
/// productionProcessNo: 5,
/// nextStateOnSuccess: ProcessState.ExecFinalParametrization,
/// waitForAllProcesses: true),
///
/// </summary>
public PickingProcessStateDef()
{
var stateContainer = new List<ProcessStateStruct>
{
#region ----------------------------------- GENERIC-STATES---------------------------------------------
// Detection of Cordonel has to be completed before assigning next state, so leave to automatic
// to change the state after successfully execution. The detection is the base for all others.
new ProcessStateStruct(
productionProcess: new DetectCordonel(Resources.StrStateDetectCordonel),
processState: ProcessState.DetectCordonel,
processInfo: Resources.StrStateDetectCordonel,
productionProcessNo: 0,
nextStateOnSuccess: ProcessState.CheckNewMeter),
// Detection of Cordonel has to be completed before assigning next state. It has to be checked if
// the meter changed to clear all collected contents of previous run. To UPDATE ALL INFORMATION after
// new meter assignment the "DetectCordonel" has to be repeated to return here "Old Meter" and then
// enter the "ConnectCordonel"!
new ProcessStateStruct(
productionProcess: null,
processState: ProcessState.CheckNewMeter,
processInfo: Resources.StrProcessStateCheckNewMeter,
productionProcessNo: null,
nextStateOnSuccess: ProcessState.DetectCordonel,
errorExitState: ProcessState.ConnectCordonel),
// Precondition: - The detection process has to be completed as the PcbId is needed.
// AVOID to change the process state on completion as parallel process is active!
new ProcessStateStruct(
productionProcess: new ConnectCordonel(Resources.StrStateConnectCordonel),
processState: ProcessState.ConnectCordonel,
processInfo: Resources.StrStateConnectCordonel,
productionProcessNo: 1,
// Avoid automatic change after completion as parallel process is active
nextStateOnSuccess: BaseProductionProcess.DoNotChangeStateAfterCompletion,
// Kick off parallel process execution
kickOffParallelState: ProcessState.GetSerialNumberAndRadioAddress),
// Precondition: - The detection process has to be completed as the PcbId is needed.
// AVOID to change the process state on completion as parallel process is active!
new ProcessStateStruct(
productionProcess: new GetSerialNumberAndRadioAddress(Resources.StrStateGetSerialAndRadioAddress),
processState: ProcessState.GetSerialNumberAndRadioAddress,
processInfo: Resources.StrStateGetSerialAndRadioAddress,
productionProcessNo: 2,
nextStateOnSuccess: ProcessState.CheckOrderNumber,
waitForAllProcesses: true),
// This scans the order number, it has to be executed exclusively with automatic
new ProcessStateStruct(
productionProcess: new CheckOrderNumber(Resources.StrStateCheckOrderNumber),
processState: ProcessState.CheckOrderNumber,
processInfo: Resources.StrStateCheckOrderNumber,
productionProcessNo:3,
nextStateOnSuccess: ProcessState.CheckProductionState),
#endregion -------------------------------- GENERIC-STATES --------------------------------------------
#region ----------------------------------- PICKING-STATES --------------------------------------------
// Precondition: - The connection process has to be completed as a readout of some register
// values from the Cordonel are needed to check the update capability:
// - Wait on completed connection process before starting this process.
new ProcessStateStruct(
productionProcess: new CheckProductionState(Resources.StrStateCheckProductionState),
processState: ProcessState.CheckProductionState,
processInfo: Resources.StrStateCheckProductionState,
productionProcessNo: 4,
nextStateOnSuccess: ProcessState.Idle),
//TODO Roland: implement your states here
#endregion -------------------------------- PICKING-STATES --------------------------------------------
};
StateContainer.AddRange(stateContainer);
}
}
}
@@ -9,22 +9,15 @@ using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisStatus;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EventArgs;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses;
using Timer = System.Threading.Timer;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
{
/// <summary>
/// Preparing the Cordonel for shipping:
/// - Setup device with standard parameters covered by the VAKO or customer specific CSD,
/// - If radio is installed: radio test,
/// - Requiring radio key from LuDB,
/// - Requiring 7 passwords for the Cordonel access levels,
/// - Building the password file (Level 3 is the modified skeleton key),
/// - Store all configurations,
/// - Reboot the Cordonel to load all settings from FLASH to RAM,
/// - Configuration check of all parameters defined by VAKO and CSD.
/// Implementation for all methods needed to drive the state machine of the process controller
/// </summary>
public sealed class FinalProcessController
public class ProcessController
{
#region ------------------------------------------ Variables --------------------------------------------------
// actual genesis meter
@@ -46,11 +39,12 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
// List of production processes
public List<IProductionProcess> Processes
{
get; private set;
get;
private set;
}
// Container for production process states
private ProcessStateDef _processStateDef;
private IProcessStateDef _processStateDef;
// Container for production status information, a list is used to keep the content over all classes of production
// processes as a reference seems not to return their value (always reset to the initial value)
@@ -69,15 +63,27 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
// reminder for slot
private readonly Int32 _slot;
// processing retries
private const Int32 MaxProcessRetries = 2;
//private static Int32 _processRetryCtr;
//event for process list change or state of single process step change
/// <summary>
/// Change the overview table of the GUI to the state of the individual process
/// </summary>
public event EventHandler<SingleProcessStateArgs> OnSingleProcessStateChanged;
/// <summary>
/// The overall process chain changed needed to signal to the GUI
/// - Can be forced by the state machine to invoke,
/// - Will be used via the OverallProcessStateChanged_Handler by the individual processes to return the next
/// required sate after break, error or completion to change the state of the state machine.
/// </summary>
public event EventHandler<ProcessStateArgs> OnOverallProcessStateChanged;
/// <summary>
/// Request for logging to GUI output and log-file
/// </summary>
public event EventHandler<ProcessLogArgs> OnProcessLogRequest;
// progress bar and screen info of ongoing processes
/// <summary>
/// Keep the progress bars of the GUI up to date
/// </summary>
public event EventHandler<ProcessProgressArgs> OnProcessProgressChanged;
// cancellation token
@@ -88,11 +94,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
private Timer _processCtrlTimer;
private const Int32 ProcessTimerCycleMs = 250;
// defines for state machine
/// <summary>
/// Avoid waiting for a single process completion.
/// </summary>
private const IProductionProcess DoNotWaitForSingleProcessCompletion = null;
public const IProductionProcess DoNotWaitForSingleProcessCompletion = null;
/// <summary>
/// Kick off a parallel execution of the state machine.
/// </summary>
public const Boolean ExecuteInParallel = true;
#endregion --------------------------------------- Variables --------------------------------------------------
#region ------------------------------------------ Class ------------------------------------------------------
@@ -103,6 +113,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
/// - Create and setup Cordonel.
/// </summary>
/// <param name="slot">physical slot for this meter</param>
/// <param name="processSateDef"></param>
/// <remarks date="2023-Feb-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
@@ -111,12 +122,13 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
/// - Always RegisterWatchService on,
/// - Always MinMaxCHeck on.
/// </remarks>
public FinalProcessController(Int32 slot)
public ProcessController(Int32 slot, IProcessStateDef processSateDef)
{
_processCtrlTimer = new Timer(TmrProcessCtrlCycle_Elapsed, null, ProcessTimerCycleMs, ProcessTimerCycleMs);
_slot = slot;
_cancellationTokenSource = new CancellationTokenSource();
_cancellationToken = _cancellationTokenSource.Token;
_processStateDef = processSateDef;
InitNewMeter();
InitProcesses();
// program start with idle process
@@ -247,7 +259,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
{
OnProcessLogRequest?.Invoke(sender, e);
}
/// <summary>
/// Handler to set a new state for the state machine regarding the entire final-process
/// </summary>
@@ -282,60 +293,22 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
#endregion --------------------------------------- Events -----------------------------------------------------
#region ------------------------------------------ State Machine ----------------------------------------------
/// <summary>
/// State machine of FinalProcessController which serves as preparation for SHIPPING named EOL (End-Of-Line).
/// State-machine of ProcessController which serves all production processes.
/// This-state machine executes all states defined in the "GenericStateMachineDef" and the individual
/// production-process specific "XxxStateMachineDef".
///
///
/// INSTRUCTION FOR NORMAL USAGE:
/// -----------------------------
/// Call the SetupProcess() with the
/// - new process automatically collected from <see cref="ProcessStateDef.GetProcessOfState"/> with new
/// _actualProcessState as input,
/// - successExitState set to the next required state after successfully execution.
/// That's it! The normal state change has always only one production process active and waits for completion
/// before changing to a new state and therefore a new production process.
///
/// If a special state is needed on errorExitState or breakExitState these can be assigned. Otherwise, the
/// errorExitState is linked to the error state, the breakExitState to the stop state.
///
///
/// INSTRUCTION FOR NON-BLOCKING MULTIPLE PROCESSES SIMULTANEOUSLY ACTIVITIES:
/// --------------------------------------------------------------------------
/// Sometimes production processes may be able to be executed simultaneously as they are independent of each
/// other. One example is a database access for data collection from different tables.
///
/// Call the SetupProcess with the
/// - new process automatically collected from <see cref="ProcessStateDef.GetProcessOfState"/> with new
/// _actualProcessState as input,
/// - successExitState being set to <see cref="BaseProductionProcess.DoNotChangeStateAfterCompletion"/> (null).
/// This will lock the sate change after completion as multiple processes may be active and a state change after
/// completion may cause unpredictable operation, the state is already where it should be (manually changed),
/// - change the state after the SetupProcess() call manually.
///
/// This is needed for non-blocking immediately new state change (not waiting for the successful execution of
/// the actual new assigned process):
/// E.g. all DB accesses need a certain time to get data, kicking off (fire and forget) the process without
/// waiting for the result, the exitSuccessState has to be set to null to avoid a state change on successfully
/// execution of the process. Instead, change the state here to go ahead with the next state. This forces a wait
/// on the state who needs the data from the "fire and forget" states, SEE next instruction how to proceed!
///
///
/// INSTRUCTION TO DELAY THE START OF A PROCESS:
/// --------------------------------------------
/// Each new process execution can be delayed as it may need the result of another process which is active.
///
/// Wait for a single process to complete:
/// - set the "waitForSingleProcess" to the process to wait for.
/// Wait for all preceding processes to complete:
/// - set the "waitForAllProcesses" to true.
///
/// A few states with special functions will be caught in advance in this StateMachine
/// </summary>
/// <remarks date="2023-Jan-17" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2025-Apr-15" author="Thomas Wiedebusch">
/// - Generic implementation
/// </remarks>
private void StateMachine()
{
// avoid repeated execution of the state machine on unchanged state
{// avoid repeated execution of the state machine on unchanged state
if (_stateMachineState == _lockedProcessState)
{
Thread.Sleep(ProcessTimerCycleMs >> 1);
@@ -353,9 +326,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
// signal state change to logger
OnOverallProcessStateChanged?.Invoke(this, new ProcessStateArgs(_stateMachineState));
// get the actual required process from the state definition list
var process = _processStateDef?.GetProcessOfState(_stateMachineState);
// get the processStateStruct of the actual set stateMachineSTate
var processStateStruct = _processStateDef.GetProcessStateStructOfState(_stateMachineState);
switch (_stateMachineState)
{
case ProcessState.Idle:
@@ -366,7 +339,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
// Wait for cancellation
if (_cancellationToken.IsCancellationRequested)
{
Thread.Sleep(1000);
Thread.Sleep(500);
// Reset the cancellation request
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = new CancellationTokenSource();
@@ -378,7 +351,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
// Detection of Cordonel has to be completed before assigning next state, so leave to automatic
// to change the state after successfully execution. The detection is the base for all others.
StartProcess(process, ProcessState.CheckNewMeter);
StartProcess(processStateStruct);
break;
case ProcessState.CheckNewMeter:
@@ -389,129 +362,12 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
_stateMachineState = CheckForNewMeter() ? ProcessState.DetectCordonel : ProcessState.ConnectCordonel;
break;
case ProcessState.ConnectCordonel:
// Precondition: - The detection process has to be completed as the PcbId is needed.
// AVOID to change the process state on completion as parallel process is active!
StartProcess(process, BaseProductionProcess.DoNotChangeStateAfterCompletion);
// Do not wait on completion as new parallel process can be started by manually state change.
_stateMachineState = ProcessState.GetSerialNumberAndRadioAddress;
break;
case ProcessState.GetSerialNumberAndRadioAddress:
// Precondition: - The detection process has to be completed as the PcbId is needed.
// AVOID to change the process state on completion as parallel process is active!
StartProcess(process, BaseProductionProcess.DoNotChangeStateAfterCompletion);
// Do not wait on completion as new parallel process can be started by manually state change.
_stateMachineState = ProcessState.GetProgrammingParameters;
break;
case ProcessState.GetProgrammingParameters:
// Precondition: - The connection process has to be completed as a readout of some register
// values from the Cordonel are needed to check the update capability:
// - Wait on completed connection process before starting this process.
// AVOID to change the process state on completion as parallel process is active!
StartProcess(process, BaseProductionProcess.DoNotChangeStateAfterCompletion,
waitForSingleProcess: _processStateDef?.GetProcessOfState(ProcessState.ConnectCordonel));
// Do not wait on completion as new parallel process can be started by manually state change.
_stateMachineState = ProcessState.CheckProductionState;
break;
case ProcessState.CheckProductionState:
// Precondition: - The connection process has to be completed as a readout of some register
// values from the Cordonel are needed to check the update capability:
// - Wait on completed connection process before starting this process.
// AVOID to change the process state on completion as parallel process is active!
StartProcess(process, BaseProductionProcess.DoNotChangeStateAfterCompletion,
waitForSingleProcess: _processStateDef?.GetProcessOfState(ProcessState.ConnectCordonel));
// Do not wait on completion as new parallel process can be started by manually state change.
_stateMachineState = ProcessState.ProcessPasswordFile;
break;
case ProcessState.ProcessPasswordFile:
// Precondition: - The connection process has to be completed as a password change in the DB
// will take place: Skeleton to Password Level 8. Not waiting for completion may cause side
// effects on login. Wait for all processes to complete.
// Leave state switch to automatic to avoid side effects on incomplete updated DB for next
// steps.
StartProcess(process, ProcessState.ExecFinalParametrization, waitForAllProcesses: true);
break;
case ProcessState.ExecFinalParametrization:
// This writes all setups to the device, it has to be executed exclusively with automatic
StartProcess(process, ProcessState.ExecStoreConfiguration);
break;
case ProcessState.ExecStoreConfiguration:
// Precondition: - The final parametrization has to be finished as this shall be stored
// to non-volatile memory and checked if remaining valid after the reboot.
// Store configuration has to be executed exclusively with automatic
StartProcess(process, ProcessState.RebootCordonel);
break;
case ProcessState.RebootCordonel:
// Precondition: - The store configuration has to be executed to safe all settings
// to non-volatile memory and checked if remaining valid after the reboot.
// Reboot has to be executed exclusively with automatic
StartProcess(process, ProcessState.CheckFinalParametrization);
break;
case ProcessState.CheckFinalParametrization:
// Precondition: - The reboot process including the store all configurations has to be
// completed.
// Check final configuration has to be executed exclusively with automatic
StartProcess(process, ProcessState.CheckRadio);
break;
case ProcessState.CheckRadio:
// Precondition: - The final parametrization, reboot process including the store all
// configurations and the check of all setups survived the reboot have to be completed.
// Radio signal level check, skipped on NA region or if skipping is required by special
// approval
StartProcess(process, ProcessState.CheckOrderNumber);
break;
case ProcessState.CheckOrderNumber:
// This scans the order number, it has to be executed exclusively with automatic
StartProcess(process, ProcessState.CheckSerialNumber, waitForAllProcesses: true);
break;
case ProcessState.CheckSerialNumber:
// This scans the serial number, it has to be executed exclusively with automatic
StartProcess(process, ProcessState.CheckAttachments);
break;
case ProcessState.CheckAttachments:
// Precondition: - All checks have to be executed with positive result
StartProcess(process, ProcessState.ExecVisualInspection);
break;
case ProcessState.ExecVisualInspection:
// Precondition: - All checks have to be executed with positive result
StartProcess(process, ProcessState.PrepareShippingMode);
break;
case ProcessState.PrepareShippingMode:
// Precondition: - All checks have to be executed with positive result
StartProcess(process, ProcessState.ReadyForShipping);
break;
case ProcessState.ReadyForShipping:
_stateMachineState = ProcessState.Idle;
break;
case ProcessState.RepeatFailedTests:
_stateMachineState = RepeatFailedTests();
break;
case ProcessState.PrintReturnReport:
_stateMachineState = ProcessState.Idle;
break;
case ProcessState.PrintSuccessReport:
_stateMachineState = ProcessState.Idle;
break;
case ProcessState.AbortFinalTest:
case ProcessState.AbortTest:
AbortProcesses();
_stateMachineState = ProcessState.Idle;
break;
@@ -527,14 +383,22 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
// Start the error handler with its user feedback request. It has to be started
// always even if the abortion of all processes is required! The error handler
// will change the exit state based on the user input.
StartProcess(process, ProcessState.Idle, startAlways: true);
StartProcess(processStateStruct);
break;
case ProcessState.StateListDelimiter:
default:
_stateMachineState = ProcessState.Idle;
break;
}//
default:
// This is the call to the processes for a specific test
StartProcess(processStateStruct);
if (processStateStruct.KickOffParallelState != null)
{
_stateMachineState = (ProcessState)processStateStruct.KickOffParallelState;
}
break;
}
}
catch (ThreadAbortException ex)
{
@@ -549,6 +413,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
}
}// state locked against repeated execution
#endregion --------------------------------------- State Machine ----------------------------------------------
#region ------------------------------------------ Tools ------------------------------------------------------
/// <summary>
@@ -738,8 +603,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
{
if (Processes == null)
Processes = new List<IProductionProcess>();
if (_processStateDef == null)
_processStateDef = new ProcessStateDef();
// get all processes relevant for EOL from table which has to be sorted
var processes = _processStateDef.GetAllEolRelevantProcesses();
@@ -768,14 +631,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
/// - Adds event handlers for process information,
/// - Starts process.
/// </summary>
/// <param name="process"></param>
/// <param name="successExitState"></param>
/// <param name="errorExitState"></param>
/// <param name="breakExitState"></param>
/// <param name="waitForSingleProcess">wait for single processes to complete before execution</param>
/// <param name="waitForAllProcesses">wait for all processes to complete before execution</param>
/// <param name="startAlways">start even if abort is required</param>
/// <returns>actual state</returns>
/// <param name="processStateStruct"></param>
/// <returns>actual state</returns>
/// <remarks date="2023-Jan-31" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
@@ -794,41 +651,42 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
/// <remarks date="2024-Sep-01" author="Thomas Wiedebusch">
/// - Changed setup process to start process being able to create a process list in advance.
/// </remarks>
private void StartProcess(IProductionProcess process, ProcessState? successExitState,
ProcessState errorExitState = ProcessState.Error, ProcessState breakExitState = ProcessState.Stop,
IProductionProcess waitForSingleProcess = DoNotWaitForSingleProcessCompletion,
Boolean waitForAllProcesses = false, Boolean startAlways = false)
/// <remarks date="2025-Apr-15" author="Thomas Wiedebusch">
/// - Feed with processStateStruct.
/// </remarks>
protected void StartProcess(ProcessStateStruct processStateStruct)
{
// deny access if uninitialized
if (Processes == null || process == null)
if (Processes == null || processStateStruct.ProductionProcess == null)
return;
// do not start a new process if any error or abort request is signaled
if (!startAlways && AnyProcessRequiresAbortion())
if (AnyProcessRequiresAbortion() && !processStateStruct.StartAlways )
{
return;
}
// process is waiting for execution after init, this overrides the eventually abort state
process.InitProcess();
processStateStruct.ProductionProcess.InitProcess();
// wait for a single process to complete before start of new depending on process
if (waitForSingleProcess != DoNotWaitForSingleProcessCompletion)
if (processStateStruct.WaitForSingleProcess != null)
{
// each process will have its own timer until a timeout will change the state
while (Processes.Any(pp => pp.ProcessName == waitForSingleProcess.ProcessName
&& WaitForProcess(pp)
&& !AnyProcessRequiresAbortion()))
while (Processes.Any(pp => !AnyProcessRequiresAbortion()
&& _processStateDef.GetStateOfProcess(pp) == processStateStruct.WaitForSingleProcess
&& WaitForProcess(pp)))
{
Thread.Sleep(50);
}
}
// wait for all initialized processes to complete before start the new process
if (waitForAllProcesses)
if (processStateStruct.WaitForAllProcesses)
{
// each process will have its own timer until a timeout will change the state,
// avoid checking the new process, which is not started due to this waiting loop
while (Processes.Any(pp => pp.ProcessName != process.ProcessName
while (Processes.Any(pp => pp.ProcessName != processStateStruct.ProductionProcess.ProcessName
&& WaitForProcess(pp)
&& !AnyProcessRequiresAbortion()))
{
@@ -837,28 +695,29 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
}
// if any process exits meanwhile with an error or abort request avoid a start of this process
if ((!startAlways && AnyProcessRequiresAbortion())
|| process.SingleProcessState == SingleProcessState.Idle
|| process.SingleProcessState == SingleProcessState.Abort)
if (( !processStateStruct.StartAlways && AnyProcessRequiresAbortion())
|| processStateStruct.ProductionProcess.SingleProcessState == SingleProcessState.Idle
|| processStateStruct.ProductionProcess.SingleProcessState == SingleProcessState.Abort)
{
process.AbortProcess();
processStateStruct.ProductionProcess.AbortProcess();
return;
}
// References to commonly used production process objects
process.Meter = _genesisMeter;
process.RegisterRestorer = _registerRestorer;
process.RadioEncryptionKey = _radioEncryptionKey;
process.PwdContainer = _pwdContainer;
process.ProductionStatus = _productionStatus;
process.ProductionRequirements = _cordonelRequirements;
process.EolProgress = _cordonelEolProgress;
process.CancellationToken = _cancellationToken;
processStateStruct.ProductionProcess.Meter = _genesisMeter;
processStateStruct.ProductionProcess.RegisterRestorer = _registerRestorer;
processStateStruct.ProductionProcess.RadioEncryptionKey = _radioEncryptionKey;
processStateStruct.ProductionProcess.PwdContainer = _pwdContainer;
processStateStruct.ProductionProcess.ProductionStatus = _productionStatus;
processStateStruct.ProductionProcess.ProductionRequirements = _cordonelRequirements;
processStateStruct.ProductionProcess.EolProgress = _cordonelEolProgress;
processStateStruct.ProductionProcess.CancellationToken = _cancellationToken;
// kick off process execution, the breakExitState will not be used
process.StartProcess(successExitState, errorExitState, breakExitState);
processStateStruct.ProductionProcess.StartProcess(processStateStruct.NextStateOnSuccess,
processStateStruct.ErrorExitState, processStateStruct.BreakExitState);
// connect process events to actual process
AddProcessEvents(process);
AddProcessEvents(processStateStruct.ProductionProcess);
}
/// <summary>
@@ -0,0 +1,860 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using LaaPackages.Features.Cordonel.Models;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisStatus;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EventArgs;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses;
using Timer = System.Threading.Timer;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
{
/// <summary>
/// Implementation for all methods needed to drive the state machine of the process controller
/// </summary>
public class ProcessController
{
#region ------------------------------------------ Variables --------------------------------------------------
// actual genesis meter
private IGenesisMeter _genesisMeter;
// Cordonel requirements for production
private CordonelRequirements _cordonelRequirements;
// EOL progress states for production
private EOLProgressModel _cordonelEolProgress;
// last pcbId for check of changed genesisMeter
private String _lastPcbId;
// batch just used for setup, as only one meter will be assigned
private MeterBatch _meterBatch;
// state of the state machine
private ProcessState _stateMachineState;
// locker to avoid repeated state execution
private ProcessState _lockedProcessState;
// sender of state change event to evaluate in e.g. error process
private IProductionProcess _stateChangeRequestProcess;
// List of production processes
public List<IProductionProcess> Processes
{
get;
private set;
}
// Container for production process states
private IProcessStateDef _processStateDef;
// Container for production status information, a list is used to keep the content over all classes of production
// processes as a reference seems not to return their value (always reset to the initial value)
private GenesisStatus _productionStatus;
// register restorer
private RegisterRestorer _registerRestorer;
// sensus radio encryption key
private List<String> _radioEncryptionKey;
// Password container with all information, a list is used to keep the content over all classes of production
// processes as a reference seems not to return their value (always reset to the initial value)
private MeterPwdDb _pwdContainer;
// reminder for slot
private readonly Int32 _slot;
/// <summary>
/// Change the overview table of the GUI to the state of the individual process
/// </summary>
public event EventHandler<SingleProcessStateArgs> OnSingleProcessStateChanged;
/// <summary>
/// The overall process chain changed needed to signal to the GUI
/// - Can be forced by the state machine to invoke,
/// - Will be used via the OverallProcessStateChanged_Handler by the individual processes to return the next
/// required sate after break, error or completion to change the state of the state machine.
/// </summary>
public event EventHandler<ProcessStateArgs> OnOverallProcessStateChanged;
/// <summary>
/// Request for logging to GUI output and log-file
/// </summary>
public event EventHandler<ProcessLogArgs> OnProcessLogRequest;
/// <summary>
/// Keep the progress bars of the GUI up to date
/// </summary>
public event EventHandler<ProcessProgressArgs> OnProcessProgressChanged;
// cancellation token
private CancellationTokenSource _cancellationTokenSource;
private CancellationToken _cancellationToken;
// cyclic timer for state machine call
private Timer _processCtrlTimer;
private const Int32 ProcessTimerCycleMs = 250;
/// <summary>
/// Avoid waiting for a single process completion.
/// </summary>
public const IProductionProcess DoNotWaitForSingleProcessCompletion = null;
/// <summary>
/// Kick off a parallel execution of the state machine.
/// </summary>
public const Boolean ExecuteInParallel = true;
#endregion --------------------------------------- Variables --------------------------------------------------
#region ------------------------------------------ Class ------------------------------------------------------
/// <summary>
/// Ctor:
/// - Setup process timer for cyclic call of state machine,
/// - Create and setup Cordonel.
/// </summary>
/// <param name="slot">physical slot for this meter</param>
/// <param name="processSateDef"></param>
/// <remarks date="2023-Feb-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2023-Jul-04" author="Thomas Wiedebusch">
/// - Always set to production mode to load the registers for this FW and not the latest,
/// - Always RegisterWatchService on,
/// - Always MinMaxCHeck on.
/// </remarks>
public ProcessController(Int32 slot, IProcessStateDef processSateDef)
{
_processCtrlTimer = new Timer(TmrProcessCtrlCycle_Elapsed, null, ProcessTimerCycleMs, ProcessTimerCycleMs);
_slot = slot;
_cancellationTokenSource = new CancellationTokenSource();
_cancellationToken = _cancellationTokenSource.Token;
_processStateDef = processSateDef;
InitNewMeter();
InitProcesses();
// program start with idle process
_stateMachineState = ProcessState.Idle;
}
/// <summary>
/// Initialization of new meter
/// </summary>
/// <remarks date="2023-Feb-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2024-Jul-15" author="Thomas Wiedebusch">
/// - Initialize processes to build a list of all production processes.
/// </remarks>
/// <remarks date="2024-Sep-09" author="Thomas Wiedebusch">
/// - Removed list of failed production processes
/// </remarks>
private void InitNewMeter()
{
_genesisMeter = new GenesisMeter();
_genesisMeter.SetupFromConfigFile(_slot);
// Important setup to overwrite setting from "Setup" in GTB
_genesisMeter.Configuration.ProductionMode = true;
_genesisMeter.Configuration.UseRegisterWatchService = true;
_genesisMeter.Configuration.UseMinMaxCheck = true;
// Meter batch even if only one meter in use as the meter batch routines can be used
_meterBatch = new MeterBatch();
_meterBatch.AddMeter(_genesisMeter);
// Set lock process to unreachable state delimiter to force initial entry of state machine
_lockedProcessState = ProcessState.StateListDelimiter;
// Create a register restorer
_registerRestorer = new RegisterRestorer(_genesisMeter);
_radioEncryptionKey = new List<String>();
// Containers for production status and standard or special requirement for production
_productionStatus = new GenesisStatus();
_cordonelRequirements = new CordonelRequirements();
// Password container as reminder for repeated test on identical device
_pwdContainer = new MeterPwdDb();
// EOL progress to feed the sentinel
_cordonelEolProgress = new EOLProgressModel();
}
/// <summary>
/// Removal of old meter
/// </summary>
private void RemoveMeter()
{
_genesisMeter?.ClearPassword();
// meter batch dispose disposes all IMeters
_meterBatch?.Dispose();
_meterBatch = null;
_genesisMeter = null;
_registerRestorer = null;
_radioEncryptionKey = null;
_productionStatus = null;
_cordonelRequirements = null;
_pwdContainer = null;
_cordonelEolProgress = null;
}
/// <summary>
/// Dispose.
/// </summary>
/// <remarks date="2023-Feb-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public void Dispose()
{
_processCtrlTimer?.Dispose();
_processCtrlTimer = null;
_processStateDef = null;
KillProcesses();
RemoveMeter();
}
#endregion --------------------------------------- Class ------------------------------------------------------
#region ------------------------------------------ Events -----------------------------------------------------
/// <summary>
/// Single process state or process list changed event,
/// Use event to force special procedure like uninstalling events
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks date="2023-Feb-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2023-Jun-29" author="Thomas Wiedebusch">
/// - Remove process events if process is finished.
/// </remarks>
/// <remarks date="2023-Jul-17" author="Thomas Wiedebusch">
/// - Remove process events if process is finished or skipped.
/// </remarks>
private void SingleProcessStateChanged_Handler(Object sender, SingleProcessStateArgs e)
{
IProductionProcess pp = null;
if (sender is IProductionProcess process)
{
pp = process;
}
// uninstall events
switch (e.SingleProcessState)
{
// after execution independent of the result, the events coming from the process
// are needed to be removed
case SingleProcessState.Succeeded:
case SingleProcessState.Skipped:
case SingleProcessState.Abort:
case SingleProcessState.Error:
RemoveProcessEvents(pp);
break;
case SingleProcessState.Retry:
case SingleProcessState.Waiting:
case SingleProcessState.Running:
break;
}
// signal state change to GUI
OnSingleProcessStateChanged?.Invoke(sender, e);
}
private void ProcessProgress_Handler(Object sender, ProcessProgressArgs e)
{
OnProcessProgressChanged?.Invoke(sender, e);
}
private void ProcessLog_Handler(Object sender, ProcessLogArgs e)
{
OnProcessLogRequest?.Invoke(sender, e);
}
/// <summary>
/// Handler to set a new state for the state machine regarding the entire final-process
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks date="2023-Mar-03" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void OverallProcessStateChanged_Handler(Object sender, ProcessStateArgs e)
{
// remind caller of the state change request to act on e.g. error processing
_stateChangeRequestProcess = (IProductionProcess)sender;
// received new status from external
if (e.State != null)
{
_stateMachineState = (ProcessState)e.State;
}
}
/// <summary>
/// Process timer
/// </summary>
/// <param name="state"></param>
/// <remarks date="2023-Jan-31" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void TmrProcessCtrlCycle_Elapsed(Object state)
{
StateMachine();
}
#endregion --------------------------------------- Events -----------------------------------------------------
#region ------------------------------------------ State Machine ----------------------------------------------
/// <summary>
/// State-machine of ProcessController which serves all production processes.
/// This-state machine executes all states defined in the "GenericStateMachineDef" and the individual
/// production-process specific "XxxStateMachineDef".
///
/// A few states with special functions will be caught in advance in this StateMachine
/// </summary>
/// <remarks date="2023-Jan-17" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2025-Apr-15" author="Thomas Wiedebusch">
/// - Generic implementation
/// </remarks>
private void StateMachine()
{// avoid repeated execution of the state machine on unchanged state
if (_stateMachineState == _lockedProcessState)
{
Thread.Sleep(ProcessTimerCycleMs >> 1);
}
else
{
try
{
// remind backup state to avoid repeated execution and side effects
_lockedProcessState = _stateMachineState;
// signal overall process progress to GUI based on ready processes
CalcAndPushOverAllProgress();
// signal state change to logger
OnOverallProcessStateChanged?.Invoke(this, new ProcessStateArgs(_stateMachineState));
// get the processStateStruct of the actual set stateMachineSTate
var processStateStruct = _processStateDef.GetProcessStateStructOfState(_stateMachineState);
switch (_stateMachineState)
{
case ProcessState.Idle:
// This state needs an external state change like user input to go ahead
break;
case ProcessState.DetectCordonel:
// Wait for cancellation
if (_cancellationToken.IsCancellationRequested)
{
Thread.Sleep(500);
// Reset the cancellation request
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = new CancellationTokenSource();
_cancellationToken = _cancellationTokenSource.Token;
}
// Set up the counter for maximum overall process progress bar in GUI
CalcAndPushMaxOverAllProcessSteps();
// Detection of Cordonel has to be completed before assigning next state, so leave to automatic
// to change the state after successfully execution. The detection is the base for all others.
StartProcess(processStateStruct);
break;
case ProcessState.CheckNewMeter:
// Detection of Cordonel has to be completed before assigning next state. It has to be checked if
// the meter changed to clear all collected contents of previous run. To UPDATE ALL INFORMATION after
// new meter assignment the "DetectCordonel" has to be repeated to return here "Old Meter" and then
// enter the "ConnectCordonel"!
_stateMachineState = CheckForNewMeter() ? ProcessState.DetectCordonel : ProcessState.ConnectCordonel;
break;
case ProcessState.RepeatFailedTests:
_stateMachineState = RepeatFailedTests();
break;
case ProcessState.AbortTest:
AbortProcesses();
_stateMachineState = ProcessState.Idle;
break;
case ProcessState.Stop:
KillProcesses();
_stateMachineState = ProcessState.Idle;
break;
case ProcessState.Error:
// Common error handling routine
ProcessErrorHandler(_stateChangeRequestProcess);
// Start the error handler with its user feedback request. It has to be started
// always even if the abortion of all processes is required! The error handler
// will change the exit state based on the user input.
StartProcess(processStateStruct);
break;
case ProcessState.StateListDelimiter:
_stateMachineState = ProcessState.Idle;
break;
default:
// This is the call to the processes for a specific test
StartProcess(processStateStruct);
if (processStateStruct.KickOffParallelState != null)
{
_stateMachineState = (ProcessState)processStateStruct.KickOffParallelState;
}
break;
}
}
catch (ThreadAbortException ex)
{
_genesisMeter?.WriteLog(ex.Message);
_stateMachineState = ProcessState.Idle;
}
catch (Exception ex)
{
_genesisMeter?.WriteLog(ex.Message);
_stateMachineState = ProcessState.Error;
}
}
}// state locked against repeated execution
#endregion --------------------------------------- State Machine ----------------------------------------------
#region ------------------------------------------ Tools ------------------------------------------------------
/// <summary>
/// Returns the currentGenesis information for display
/// </summary>
/// <returns></returns>
/// <remarks date="2023-Feb-27" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public CordonelRequirements GetProductionRequirements()
{
return _cordonelRequirements;
}
/// <summary>
/// Returns the currentGenesis information for display
/// </summary>
/// <returns></returns>
/// <remarks date="2023-Feb-27" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public IGenesisMeter GetGenesisMeter()
{
return _genesisMeter;
}
/// <summary>
/// Clears all collected data on new genesisMeter and reminds the detected for next
/// detection and run of this comparison.
/// </summary>
/// <returns>true for new meter</returns>
/// <remarks date="2023-Jun-28" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2023-Jul-17" author="Thomas Wiedebusch">
/// - The StartProcessSequence has already removed all processes from list to have a clean start,
/// avoid it here again, because the detect is now in the list!
/// </remarks>
/// <remarks date="2024-Jul-15" author="Thomas Wiedebusch">
/// - Handle genesis meter is null as new meter.
/// </remarks>
/// <remarks date="2025-Mai-12" author="Thomas Wiedebusch">
/// - Check _genesisMeter not null before equality with old pcbid.
/// </remarks>
private Boolean CheckForNewMeter()
{
if (_genesisMeter == null ||
(!string.IsNullOrEmpty(_lastPcbId) &&
_genesisMeter?.PcbId != null && !_genesisMeter.PcbId.Equals(_lastPcbId)))
{
// This loop will be entered after the meter has changed, but not on the initial meter
// after program start.
KillProcesses();
RemoveMeter();
InitNewMeter();
InitProcesses();
// Remind actual genesis
_lastPcbId = _genesisMeter?.PcbId;
// New meter detected
return true;
}
// Remind actual genesis
_lastPcbId = _genesisMeter?.PcbId;
// Old meter
return false;
}
/// <summary>
/// Restart final test on first failed or aborted test.
/// </summary>
/// <remarks date="2023-Jul-18" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2024-Sep-09" author="Thomas Wiedebusch">
/// - Removed list of failed production processes and search in Processes
/// </remarks>
/// <remarks date="2024-Oct-21" author="Thomas Wiedebusch">
/// - Leave skipped processes as is.
/// </remarks>
private ProcessState RepeatFailedTests()
{
var state = ProcessState.Idle;
// Search for first test in processes list which failed or aborted due to final test in
// parallel processing forced abort of active processes
foreach (var p in Processes)
{
if (p.SingleProcessState == SingleProcessState.Error ||
p.SingleProcessState == SingleProcessState.Abort ||
p.SingleProcessState == SingleProcessState.Retry)
{
// set the state to the first entry to kick off the state machine here
state = _processStateDef.GetStateOfProcess(p);
break;
}
}
// Put all processes which are not successfully completed to init (Waiting for execution)
foreach (var pp in Processes.Where(pp => pp.SingleProcessState != SingleProcessState.Succeeded &&
pp.SingleProcessState != SingleProcessState.Skipped))
{
pp.IdleProcess();
}
return state;
}
/// <summary>
/// Error handler
/// </summary>
/// <remarks date="2023-Feb-15" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2024-Feb-07" author="Thomas Wiedebusch">
/// - Activated check for sequences in production process for ProductionProcessCheckFinalParametrization
/// to re-start with ProductionProcessExecFinalParametrization.
/// </remarks>
/// <remarks date="2024-Sep-09" author="Thomas Wiedebusch">
/// - Removed list of failed production processes
/// </remarks>
/// <remarks date="2025-Jan-28" author="Thomas Wiedebusch">
/// - Kick off exec parametrization if check after reboot failed.
/// </remarks>
private void ProcessErrorHandler(IProductionProcess process)
{
if (process == null)
return;
// Special action if parametrization check failed to repeat it
if (ProcessState.CheckFinalParametrization == _processStateDef.GetStateOfProcess(process))
{
// Signal state change to retry sequence exec config, store config, reboot, check config
var pp = _processStateDef.GetProcessOfState(ProcessState.ExecFinalParametrization);
pp.RetryProcess();
pp = _processStateDef.GetProcessOfState(ProcessState.ExecStoreConfiguration);
pp.RetryProcess();
pp = _processStateDef.GetProcessOfState(ProcessState.RebootCordonel);
pp.RetryProcess();
pp = _processStateDef.GetProcessOfState(ProcessState.CheckFinalParametrization);
pp.RetryProcess();
}
// Error signal needed to abort to all processes
AbortProcesses();
}
/// <summary>
/// Calculate the maximum of all processes expected to execute and signal to GUI.
/// </summary>
private void CalcAndPushMaxOverAllProcessSteps()
{
if (_processStateDef == null)
return;
var _maxOverallProcessesSteps = _processStateDef.GetNumberOfInitializedProcesses();
OnProcessProgressChanged?.Invoke(this, new ProcessProgressArgs(_maxOverallProcessesSteps,
ProcessProgressArgs.ProcessProgressType.MaxOverallProcessSteps));
}
/// <summary>
/// Calculate all processes which are completed or skipped and signal to GUI.
/// </summary>
private void CalcAndPushOverAllProgress()
{
if (Processes == null)
return;
// check the readiness of all processes in the loop
var _overallReadyProcesses = 0;
foreach (var p in Processes)
{
if (p.SingleProcessState == SingleProcessState.Succeeded ||
p.SingleProcessState == SingleProcessState.Skipped)
_overallReadyProcesses++;
}
OnProcessProgressChanged?.Invoke(this, new ProcessProgressArgs(_overallReadyProcesses,
ProcessProgressArgs.ProcessProgressType.OverallProcessesSteps));
}
/// <summary>
/// Initialize all processes which are connected to a production process.
/// </summary>
/// <remarks date="2024-Jul-15" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void InitProcesses()
{
if (Processes == null)
Processes = new List<IProductionProcess>();
// get all processes relevant for EOL from table which has to be sorted
var processes = _processStateDef.GetAllEolRelevantProcesses();
foreach (var process in processes)
{
// process is standby for execution after idle, this overrides the eventually abort state
process.IdleProcess();
// add process to list to feed the process window in main screen
if (Processes.All(pp => pp.ProcessName != process.ProcessName))
{
Processes.Add(process);
}
}
}
/// <summary>
/// Production process kick off:
/// - Sets single process state to waiting within the process init,
/// - Adds new process to process list,
/// - Optional waits for single process to complete before process is going to be started,
/// - Optional waits for all previously assigned and started processes to complete before process is going to
/// be started,
/// - Assigns slot, productionStatus, programmingParameters and genesisMeter to process,
/// - Adds event handlers for process information,
/// - Starts process.
/// </summary>
/// <param name="processStateStruct"></param>
/// <returns>actual state</returns>
/// <remarks date="2023-Jan-31" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2023-Jun-21" author="Thomas Wiedebusch">
/// - Skipped process due to one of the previous processes failed
/// </remarks>
/// <remarks date="2023-Jul-17" author="Thomas Wiedebusch">
/// - Restructured to avoid process start if processes to wait for are not completed
/// </remarks>
/// <remarks date="2023-Dec-15" author="Thomas Wiedebusch">
/// - Changed from programming parameters to register restorer
/// </remarks>
/// <remarks date="2024-Jul-15" author="Thomas Wiedebusch">
/// - Changed setup process to start process being able to create a process list in advance.
/// </remarks>
/// <remarks date="2024-Sep-01" author="Thomas Wiedebusch">
/// - Changed setup process to start process being able to create a process list in advance.
/// </remarks>
/// <remarks date="2025-Apr-15" author="Thomas Wiedebusch">
/// - Feed with processStateStruct.
/// </remarks>
protected void StartProcess(ProcessStateStruct processStateStruct)
{
// deny access if uninitialized
if (Processes == null || processStateStruct.ProductionProcess == null)
return;
// do not start a new process if any error or abort request is signaled
if (AnyProcessRequiresAbortion() && !processStateStruct.StartAlways )
{
return;
}
// process is waiting for execution after init, this overrides the eventually abort state
processStateStruct.ProductionProcess.InitProcess();
// wait for a single process to complete before start of new depending on process
if (processStateStruct.WaitForSingleProcess != null)
{
// each process will have its own timer until a timeout will change the state
while (Processes.Any(pp => !AnyProcessRequiresAbortion()
&& _processStateDef.GetStateOfProcess(pp) == processStateStruct.WaitForSingleProcess
&& WaitForProcess(pp)))
{
Thread.Sleep(50);
}
}
// wait for all initialized processes to complete before start the new process
if (processStateStruct.WaitForAllProcesses)
{
// each process will have its own timer until a timeout will change the state,
// avoid checking the new process, which is not started due to this waiting loop
while (Processes.Any(pp => pp.ProcessName != processStateStruct.ProductionProcess.ProcessName
&& WaitForProcess(pp)
&& !AnyProcessRequiresAbortion()))
{
Thread.Sleep(50);
}
}
// if any process exits meanwhile with an error or abort request avoid a start of this process
if (( !processStateStruct.StartAlways && AnyProcessRequiresAbortion())
|| processStateStruct.ProductionProcess.SingleProcessState == SingleProcessState.Idle
|| processStateStruct.ProductionProcess.SingleProcessState == SingleProcessState.Abort)
{
processStateStruct.ProductionProcess.AbortProcess();
return;
}
// References to commonly used production process objects
processStateStruct.ProductionProcess.Meter = _genesisMeter;
processStateStruct.ProductionProcess.RegisterRestorer = _registerRestorer;
processStateStruct.ProductionProcess.RadioEncryptionKey = _radioEncryptionKey;
processStateStruct.ProductionProcess.PwdContainer = _pwdContainer;
processStateStruct.ProductionProcess.ProductionStatus = _productionStatus;
processStateStruct.ProductionProcess.ProductionRequirements = _cordonelRequirements;
processStateStruct.ProductionProcess.EolProgress = _cordonelEolProgress;
processStateStruct.ProductionProcess.CancellationToken = _cancellationToken;
// kick off process execution, the breakExitState will not be used
processStateStruct.ProductionProcess.StartProcess(processStateStruct.NextStateOnSuccess,
processStateStruct.ErrorExitState, processStateStruct.BreakExitState);
// connect process events to actual process
AddProcessEvents(processStateStruct.ProductionProcess);
}
/// <summary>
/// Check all processes for error or abort request
/// </summary>
/// <returns>true if abort required</returns>
/// <remarks date="2025-Apr-10" author="Thomas Wiedebusch">
/// - Cancellation token.
/// </remarks>
private Boolean AnyProcessRequiresAbortion()
{
return Processes.Any(pp => pp.SingleProcessState == SingleProcessState.Error ||
pp.SingleProcessState == SingleProcessState.Abort ||
_cancellationToken.IsCancellationRequested);
}
/// <summary>
/// Check process if in status waiting or running (incomplete)
/// </summary>
/// <param name="pp">production process</param>
/// <returns>true if process to wait for execution or exit running state</returns>
private Boolean WaitForProcess(IProductionProcess pp)
{
return pp.SingleProcessState == SingleProcessState.Waiting ||
pp.SingleProcessState == SingleProcessState.Running;
}
/// <summary>
/// Install process events
/// </summary>
/// <param name="process"></param>
private void AddProcessEvents(IProductionProcess process)
{
if (process == null)
return;
process.OnProcessProgressChanged += ProcessProgress_Handler;
process.OnProcessLogRequest += ProcessLog_Handler;
process.OnSingleProcessStateChanged += SingleProcessStateChanged_Handler;
process.OnOverallProcessStateChanged += OverallProcessStateChanged_Handler;
}
/// <summary>
/// Remove process events
/// </summary>
/// <param name="process"></param>
private void RemoveProcessEvents(IProductionProcess process)
{
if (process == null)
return;
process.OnProcessProgressChanged -= ProcessProgress_Handler;
process.OnProcessLogRequest -= ProcessLog_Handler;
process.OnSingleProcessStateChanged -= SingleProcessStateChanged_Handler;
process.OnOverallProcessStateChanged -= OverallProcessStateChanged_Handler;
}
/// <summary>
/// Get the actual process state of the state machine
/// </summary>
/// <returns>actual process state</returns>
/// <remarks date="2023-Jan-17" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private ProcessState GetProcessState()
{
return _stateMachineState;
}
/// <summary>
/// Set the new process state of the state machine
/// </summary>
/// <returns>true, if process state is set</returns>
/// <remarks date="2023-Jan-17" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public Boolean SetProcessState(ProcessState newProcessState)
{
if (newProcessState != _stateMachineState)
{
_stateMachineState = newProcessState;
return true;
}
return false;
}
/// <summary>
/// Remove all processes from the <see cref="Processes"/> list:
/// - Signal change to GUI,
/// - Remove process events all,
/// - Set state to abort.
/// </summary>
/// <remarks date="2023-Jul-11" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2025-Feb-13" author="Thomas Wiedebusch">
/// - Abort register access.
/// </remarks>
/// <remarks date="2025-Apr-07" author="Thomas Wiedebusch">
/// - Cancellation token source.
/// </remarks>
private void KillProcesses()
{
_cancellationTokenSource?.Cancel();
Thread.Sleep(1000);
if (Processes == null || Processes.Count == 0)
return;
// put all processes to idle and remove events of these processes
foreach (var pp in Processes)
{
pp.IdleProcess();
}
}
/// <summary>
/// Abort all processes if one previous process failed, but leave the <see cref="Processes"/> list intact.
/// Leave all events for those which are already running intact to force a clean stop.
/// </summary>
/// <remarks date="2023-Jun-21" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void AbortProcesses()
{
if (Processes == null || Processes.Count == 0)
return;
// Skip all processes which are not succeeded, erroneous or skipped to leave the status intact
// and request abort for all others
foreach (var pp in Processes.Where(pp => pp.SingleProcessState != SingleProcessState.Succeeded &&
pp.SingleProcessState != SingleProcessState.Error &&
pp.SingleProcessState != SingleProcessState.Skipped &&
pp.SingleProcessState != SingleProcessState.Retry))
{
pp.AbortProcess();
}
}
#endregion --------------------------------------- Tools ------------------------------------------------------
}
}
@@ -1,306 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
{
/// <summary>
/// Concatenation for all production process states:
/// - Production process class (representing a production process step),
/// - attached state,
/// - state information to display a text message.
/// </summary>
/// <remarks date="2023-Feb-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public class ProcessStateDef
{
/// <summary>
/// Collection of state information
/// </summary>
private readonly List<ProcessStateStruct> _stateContainer;
/// <summary>
/// Ctor
/// - Setup state and state informal text,
/// - Setup ProductionProcessFunctions, if these are null, it is just a state without any predefined function
/// - Sort the active steps from first til last step as this is the order as those will be sorted in the
/// process list UI.
/// </summary>
public ProcessStateDef()
{
_stateContainer = new List<ProcessStateStruct>
{
new ProcessStateStruct(
null,
ProcessState.Idle,
Resources.StrStateIdle,
false),
new ProcessStateStruct(
null,
ProcessState.CheckNewMeter,
Resources.StrProcessStateCheckNewMeter,
false),
new ProcessStateStruct(
new ErrorHandler(null),
ProcessState.Error,
Resources.StrProcessStateError,
false),
new ProcessStateStruct(
null,
ProcessState.RepeatFailedTests,
Resources.StrStateRepeatFinalTest,
false),
new ProcessStateStruct(
null,
ProcessState.AbortFinalTest,
Resources.StrStateSkipping,
false),
new ProcessStateStruct(
null,
ProcessState.Stop,
Resources.StrStateUserStop,
false),
new ProcessStateStruct(
null,
ProcessState.FinalTestFailed,
Resources.StrStateFinalTestFailed,
false),
new ProcessStateStruct(
null,
ProcessState.ReadyForShipping,
Resources.StrStateReadyForShipping,
false),
new ProcessStateStruct(
null,
ProcessState.PrintSuccessReport,
Resources.StrStatePrintSuccessReport,
false),
new ProcessStateStruct(
null,
ProcessState.PrintReturnReport,
Resources.StrStatePrintReturnReport,
false),
new ProcessStateStruct(
new DetectCordonel(Resources.StrStateDetectCordonel),
ProcessState.DetectCordonel,
Resources.StrStateDetectCordonel,
true),
new ProcessStateStruct(
new ConnectCordonel(Resources.StrStateConnectCordonel),
ProcessState.ConnectCordonel,
Resources.StrStateConnectCordonel,
true),
new ProcessStateStruct(
new GetSerialNumberAndRadioAddress(Resources.StrStateGetSerialAndRadioAddress),
ProcessState.GetSerialNumberAndRadioAddress,
Resources.StrStateGetSerialAndRadioAddress,
true),
new ProcessStateStruct(
new GetFinalParametrization(Resources.StrStateGetProgrammingParameters),
ProcessState.GetProgrammingParameters,
Resources.StrStateGetProgrammingParameters,
true),
new ProcessStateStruct(
new CheckProductionState(Resources.StrStateCheckProductionState),
ProcessState.CheckProductionState,
Resources.StrStateCheckProductionState,
true),
new ProcessStateStruct(
new ExecPasswordFile(Resources.StrStatePasswordFile),
ProcessState.ProcessPasswordFile,
Resources.StrStatePasswordFile,
true),
new ProcessStateStruct(
new ExecFinalParametrization(Resources.StrStateFinalParametrization),
ProcessState.ExecFinalParametrization,
Resources.StrStateFinalParametrization,
true),
new ProcessStateStruct(
new ExecStoreConfiguration(Resources.StrStateStoreConfiguration),
ProcessState.ExecStoreConfiguration,
Resources.StrStateStoreConfiguration,
true),
new ProcessStateStruct(
new ExecRebootCordonel(Resources.StrStateRebootCordonel),
ProcessState.RebootCordonel,
Resources.StrStateRebootCordonel,
true),
new ProcessStateStruct(
new CheckFinalParametrization(Resources.StrStateCheckParametrization),
ProcessState.CheckFinalParametrization,
Resources.StrStateCheckParametrization,
true),
new ProcessStateStruct(
new CheckRadio(Resources.StrStateRadioCheck),
ProcessState.CheckRadio, Resources.StrStateRadioCheck,
true),
new ProcessStateStruct(
new CheckOrderNumber(Resources.StrStateCheckOrderNumber),
ProcessState.CheckOrderNumber,
Resources.StrStateCheckOrderNumber,
true),
new ProcessStateStruct(
new CheckSerialNumber( Resources.StrStateCheckSerialNumber),
ProcessState.CheckSerialNumber,
Resources.StrStateCheckSerialNumber,
true),
new ProcessStateStruct(
new CheckAttachments(Resources.StrStateCheckAttachments),
ProcessState.CheckAttachments,
Resources.StrStateCheckAttachments,
true),
new ProcessStateStruct(
new VisualInspection(Resources.StrStateVisualInspection),
ProcessState.ExecVisualInspection,
Resources.StrStateVisualInspection,
true),
new ProcessStateStruct(
new PrepareShipping(Resources.StrStatePrepareShipping),
ProcessState.PrepareShippingMode,
Resources.StrStatePrepareShipping,
true)
};
}
/// <summary>
/// Get the process linked to state of state machine
/// </summary>
/// <param name="stateMachineState">state to search for information text</param>
/// <returns>process attached to state</returns>
/// <remarks date="2023-Feb-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public IProductionProcess GetProcessOfState(ProcessState stateMachineState)
{
return (from stateContainer in _stateContainer
where stateContainer.State == stateMachineState
select stateContainer.Process).FirstOrDefault();
}
/// <summary>
/// Get the current information of the state machine state
/// </summary>
/// <param name="stateMachineState">state to search for information text</param>
/// <returns>information attached to state depending on region</returns>
/// <remarks date="2023-Feb-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2024-Mar-18" author="Thomas Wiedebusch">
/// - If state == null return error message.
/// </remarks>
public String GetStateInfoOfState(ProcessState? stateMachineState)
{
if (stateMachineState == null)
stateMachineState = ProcessState.Error;
return (from stateContainer in _stateContainer
where stateContainer.State == stateMachineState
select stateContainer.Info).FirstOrDefault();
}
/// <summary>
/// Get the info text from production process
/// </summary>
/// <param name="process">process to look for state info text</param>
/// <returns>information attached to process depending on region</returns>
/// <remarks date="2023-Mar-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public String GetStateInfoOfProcess(IProductionProcess process)
{
return (from stateContainer in _stateContainer
where stateContainer.Process == process
select stateContainer.Info).FirstOrDefault();
}
/// <summary>
/// Get the state machine state of the text
/// </summary>
/// <param name="text">information text to search if state is assigned</param>
/// <returns>state attached to information text</returns>
/// <remarks date="2023-Feb-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public ProcessState GetStateOfInfoText(String text)
{
return (from stateContainer in _stateContainer
where stateContainer.Info == text
select stateContainer.State).FirstOrDefault();
}
/// <summary>
/// Get the state machine state from for the process
/// </summary>
/// <param name="process">process to look for state info text</param>
/// <returns>state attached to process</returns>
/// <remarks date="2023-Mar-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public ProcessState GetStateOfProcess(IProductionProcess process)
{
return (from stateContainer in _stateContainer
where stateContainer.Process == process
select stateContainer.State).FirstOrDefault();
}
/// <summary>
/// Get number of EOL relevant processes for statistic e.g. progress bar
/// </summary>
/// <returns>number of initialized processes</returns>
/// <remarks date="2023-Mar-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2024-Sep-01" author="Thomas Wiedebusch">
/// - Used marker for production relevant processes
/// </remarks>
public Int32 GetNumberOfInitializedProcesses()
{
return _stateContainer.Count(stateContainer => stateContainer.ProductionRelevant );
}
/// <summary>
/// Get all EOL relevant states
/// </summary>
/// <returns>list of initialized processes</returns>
/// <remarks date="2024-Jul-15" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2024-Sep-01" author="Thomas Wiedebusch">
/// - Used marker for production relevant processes
/// </remarks>
public List<IProductionProcess> GetAllEolRelevantProcesses()
{
return (from state in _stateContainer where state.ProductionRelevant
select state.Process).ToList();
}
}
}
@@ -4,42 +4,105 @@ using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
{
/// <summary>
/// Concatenation between final test state and text
/// Concatenation between test state and text feeding the state machine with next state.
/// </summary>
public struct ProcessStateStruct
{
/// <summary>
/// Assign process state information
/// </summary>
/// <param name="process">production process</param>
/// <param name="state">assigned state for call of this process</param>
/// <param name="info">name for process to display a message</param>
/// <param name="productionRelevant">all processes which are relevant for the EOL production</param>
/// <param name="productionProcess">production process, set to null for "invisible" states</param>
/// <param name="processState">assigned state for call of this process used by the state machine</param>
/// <param name="processInfo">name for process to display a message in the GUI</param>
/// <param name="productionProcessNo">set a unique number to sort the table of production processes,
/// set to null for all hidden processes which are not a production process</param>
/// <param name="nextStateOnSuccess">BaseProductionProcess.DoNotChangeStateAfterCompletion will not
/// change the state automatically</param>
/// <param name="errorExitState"></param>
/// <param name="breakExitState"></param>
/// <param name="kickOffParallelState">start a parallel process</param>
/// <param name="waitForSingleProcess">wait for single processes to complete before execution</param>
/// <param name="waitForAllProcesses">wait for all processes to complete before execution</param>
/// <param name="startAlways">start even if abort is required</param>
/// <remarks date="2023-Jan-17" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public ProcessStateStruct(IProductionProcess process, ProcessState state, String info, Boolean productionRelevant)
/// <remarks date="2025-Apr-14" author="Thomas Wiedebusch">
/// - Extended to steer state machine with this struct.
/// </remarks>
public ProcessStateStruct(IProductionProcess productionProcess, ProcessState processState, String processInfo,
UInt32? productionProcessNo, ProcessState? nextStateOnSuccess, ProcessState errorExitState = ProcessState.Error,
ProcessState breakExitState = ProcessState.Stop,
ProcessState? kickOffParallelState = null,
ProcessState? waitForSingleProcess = null,
Boolean waitForAllProcesses = false, Boolean startAlways = false)
{
Process = process;
State = state;
Info = info;
ProductionRelevant = productionRelevant;
ProductionProcess = productionProcess;
ProcessState = processState;
ProcessInfo = processInfo;
ProductionProcessNo = productionProcessNo;
NextStateOnSuccess = nextStateOnSuccess;
ErrorExitState = errorExitState;
BreakExitState = breakExitState;
KickOffParallelState = kickOffParallelState;
WaitForSingleProcess = waitForSingleProcess;
WaitForAllProcesses = waitForAllProcesses;
StartAlways = startAlways;
}
/// <summary>
/// Start a parallel process
/// </summary>
public ProcessState? KickOffParallelState { get; }
/// <summary>
/// The state of the final test
/// </summary>
public IProductionProcess Process { get; }
public IProductionProcess ProductionProcess { get; }
/// <summary>
/// The state of the final test
/// </summary>
public ProcessState State { get; }
public ProcessState ProcessState { get; }
/// <summary>
/// The info message of the state
/// </summary>
public String Info { get; }
public String ProcessInfo { get; }
/// <summary>
/// Processes which are relevant for the EOL production
/// Processes which are relevant for the production have a number for the sequence they should be displayed
/// in the GUI, all hidden (non-)production processes are set to null
/// </summary>
public Boolean ProductionRelevant { get; }
public UInt32? ProductionProcessNo { get; }
/// <summary>
/// Next state required after successfully execution
/// </summary>
public ProcessState? NextStateOnSuccess { get; }
/// <summary>
/// Special state used for error or for the second output of the process
/// </summary>
public ProcessState ErrorExitState { get; }
/// <summary>
/// Special state required on user break
/// </summary>
public ProcessState BreakExitState { get; }
/// <summary>
/// Wait for a single process named here to take the result into account
/// </summary>
public ProcessState? WaitForSingleProcess { get; }
/// <summary>
/// Wait until all preceding processes are finished with success
/// </summary>
public Boolean WaitForAllProcesses { get; }
/// <summary>
/// Sate which has to be active always even if a predecessor failed (e.g. error state)
/// </summary>
public Boolean StartAlways { get; }
}
}
@@ -286,17 +286,21 @@
<Compile Include="Model\EOLHandler.cs" />
<Compile Include="ProductionProcesses\Enum\PickingProcessState.cs" />
<Compile Include="ProductionProcesses\EolProcesses\VisualInspection.cs" />
<Compile Include="ProductionProcesses\EolProcesses\ErrorHandler.cs" />
<Compile Include="ProductionProcesses\IProcessStateDef.cs" />
<Compile Include="ProductionProcesses\BaseProcessStatesDef.cs" />
<Compile Include="ProductionProcesses\GenericProcesses\ErrorHandler.cs" />
<Compile Include="ProductionProcesses\EolProcesses\ExecStoreConfiguration.cs" />
<Compile Include="ProductionProcesses\EolProcesses\GetSerialNumberAndRadioAddress.cs" />
<Compile Include="ProductionProcesses\EolProcesses\DetectCordonel.cs" />
<Compile Include="ProductionProcesses\GenericProcesses\GetSerialNumberAndRadioAddress.cs" />
<Compile Include="ProductionProcesses\GenericProcesses\DetectCordonel.cs" />
<Compile Include="ProductionProcesses\EolProcesses\ExecRebootCordonel.cs" />
<Compile Include="ProductionProcesses\EolProcesses\PrepareShipping.cs" />
<Compile Include="ProductionProcesses\Enum\DialogResult.cs" />
<Compile Include="ProductionProcesses\Enum\ImgState.cs" />
<Compile Include="ProductionProcesses\Enum\ProcessState.cs" />
<Compile Include="ProductionProcesses\EventArgs\SingleProcessStateArgs.cs" />
<Compile Include="ProductionProcesses\ProcessStateDef.cs" />
<Compile Include="ProductionProcesses\EolProcesses\EolProcessStatesDef.cs" />
<Compile Include="ProductionProcesses\PickingProcesses\CheckProductionState.cs" />
<Compile Include="ProductionProcesses\PickingProcesses\PickingProcessStatesDef.cs" />
<Compile Include="ProductionProcesses\ProcessStateStruct.cs" />
<Compile Include="Data\PickingResultData.cs" />
<Compile Include="Data\PressureResultDataNotifyable.cs" />
@@ -307,27 +311,21 @@
<Compile Include="Model\MarriageMultiModeViewModel.cs" />
<Compile Include="Model\PickingMultiModeViewModel.cs" />
<Compile Include="Model\PickingViewModel.cs" />
<Compile Include="MultiMode.xaml.cs">
<DependentUpon>MultiMode.xaml</DependentUpon>
</Compile>
<Compile Include="MultiModeChild.xaml.cs">
<DependentUpon>MultiModeChild.xaml</DependentUpon>
</Compile>
<Compile Include="Model\IMultiModeChildViewModel.cs" />
<Compile Include="ProductionProcesses\EolProcesses\CheckSerialNumber.cs" />
<Compile Include="ProductionProcesses\EolProcesses\CheckOrderNumber.cs" />
<Compile Include="ProductionProcesses\GenericProcesses\CheckSerialNumber.cs" />
<Compile Include="ProductionProcesses\GenericProcesses\CheckOrderNumber.cs" />
<Compile Include="ProductionProcesses\EolProcesses\CheckAttachments.cs" />
<Compile Include="ProductionProcesses\EolProcesses\CheckProductionState.cs" />
<Compile Include="ProductionProcesses\EolProcesses\CheckFinalParametrization.cs" />
<Compile Include="ProductionProcesses\EolProcesses\GetFinalParametrization.cs" />
<Compile Include="ProductionProcesses\EolProcesses\ExecFinalParametrization.cs" />
<Compile Include="ProductionProcesses\EolProcesses\CheckRadio.cs" />
<Compile Include="ProductionProcesses\EolProcesses\ConnectCordonel.cs" />
<Compile Include="ProductionProcesses\GenericProcesses\ConnectCordonel.cs" />
<Compile Include="ProductionProcesses\EolProcesses\ExecPasswordFile.cs" />
<Compile Include="ProductionProcesses\BaseProductionProcess.cs" />
<Compile Include="ProductionProcesses\Enum\SingleProcessState.cs" />
<Compile Include="ProductionProcesses\IProductionProcess.cs" />
<Compile Include="ProductionProcesses\FinalProcessController.cs" />
<Compile Include="ProductionProcesses\ProcessController.cs" />
<Compile Include="ProductionProcesses\EventArgs\ProcessStateArgs.cs" />
<Compile Include="ProductionProcesses\EventArgs\ProcessLogArgs.cs" />
<Compile Include="ProductionProcesses\EventArgs\ProcessProgressArgs.cs" />
@@ -344,20 +342,20 @@
<Compile Include="UserAccessWindow.xaml.cs">
<DependentUpon>UserAccessWindow.xaml</DependentUpon>
</Compile>
<Compile Include="UserControls\FinalTest\IUserControl.cs" />
<Compile Include="UserControls\FinalTest\UcBarcodeInput.xaml.cs">
<Compile Include="UserControls\IUserControl.cs" />
<Compile Include="UserControls\UcBarcodeInput.xaml.cs">
<DependentUpon>UcBarcodeInput.xaml</DependentUpon>
</Compile>
<Compile Include="UserControls\FinalTest\UcDialog.xaml.cs">
<Compile Include="UserControls\UcDialog.xaml.cs">
<DependentUpon>UcDialog.xaml</DependentUpon>
</Compile>
<Compile Include="UserControls\FinalTest\UcRadioCheck.xaml.cs">
<Compile Include="UserControls\UcRadioCheck.xaml.cs">
<DependentUpon>UcRadioCheck.xaml</DependentUpon>
</Compile>
<Compile Include="UserControls\FinalTest\UcPlain.xaml.cs">
<Compile Include="UserControls\UcPlain.xaml.cs">
<DependentUpon>UcPlain.xaml</DependentUpon>
</Compile>
<Compile Include="UserControls\FinalTest\UcPasswordFile.xaml.cs">
<Compile Include="UserControls\UcPasswordFile.xaml.cs">
<DependentUpon>UcPasswordFile.xaml</DependentUpon>
</Compile>
<Page Include="ctls\ctlMultiModeMarriage.xaml">
@@ -372,54 +370,42 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<<<<<<< HEAD
<Page Include="FinalTest.xaml">
=======
<Page Include="ProductionWindow.xaml">
>>>>>>> Dev_Picking
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="FinalTest.xaml.cs">
<DependentUpon>FinalTest.xaml</DependentUpon>
<Compile Include="ProductionWindow.xaml.cs">
<DependentUpon>ProductionWindow.xaml</DependentUpon>
</Compile>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Page Include="MultiMode.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="MultiModeChild.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="UserAccessWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="UserControls\FinalTest\UcBarcodeInput.xaml">
<Page Include="UserControls\UcBarcodeInput.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="UserControls\FinalTest\UcDialog.xaml">
<Page Include="UserControls\UcDialog.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="UserControls\FinalTest\UcRadioCheck.xaml">
<Page Include="UserControls\UcRadioCheck.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="UserControls\FinalTest\UcPlain.xaml">
<Page Include="UserControls\UcPlain.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="UserControls\FinalTest\UcPasswordFile.xaml">
<Page Include="UserControls\UcPasswordFile.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
@@ -612,7 +598,6 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Enum\" />
<Folder Include="ProductionProcesses\PickingProcesses\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
@@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="Xylem.Common.Production.ProductionUiCordonel.FinalTest"
x:Class="Xylem.Common.Production.ProductionUiCordonel.ProductionWindow"
mc:Ignorable="d"
Title="FinalTest" MinHeight="730" MinWidth="820" Height="730" Width="820" Initialized="Window_Initialized" Closing="Window_Closing" ResizeMode="CanMinimize" HorizontalAlignment="Right" WindowStartupLocation="CenterScreen" Loaded="Window_Loaded">
<DockPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="2,2,2,2">
@@ -21,6 +21,7 @@ using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EventArgs;
using Xylem.Common.Utils.Logging;
using Xylem.Common.Utils.UserAccessCtrl;
@@ -33,7 +34,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel
/// <summary>
/// Interaction logic for FinalTest.xaml
/// </summary>
public sealed partial class FinalTest
public sealed partial class ProductionWindow
{
#region variables and properties
private readonly ILogger _logger;
@@ -53,19 +54,20 @@ namespace Xylem.Common.Production.ProductionUiCordonel
// maximum processing steps for calculation of progress bar
private Int32 _maxOverallProcessSteps;
private Int32 _maxSingleProcessSteps;
private FinalProcessController _processCtrl;
private ProcessController _processCtrl;
private DateTimeOffset _startTime;
private DispatcherTimer _timer;
private String _lastLoggingTextToAvoidRepetition;
private CordonelRequirements _productionRequirements;
private UserAccessWindow _userAccessWindow;
private IProcessStateDef _processStateDef;
#endregion
public FinalTest()
public ProductionWindow(String softwareName, String projectName, IProcessStateDef processStateDef)
{
_logger = NLogHelper.CreateOrGetLogger("ProductionUiCordonel");
_logger = NLogHelper.CreateOrGetLogger(projectName);
_processStateDef = processStateDef;
String msg;
try
{
@@ -93,7 +95,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel
InitializeComponent();
// Log version to files for debug purposes
_strSoftwareNameVersion = $"{Properties.Resources.StrSoftwareNameVersion} {_version}";
_strSoftwareNameVersion = $"{softwareName} {_version}";
_logger.Info(_strSoftwareNameVersion);
Title = _strSoftwareNameVersion;
@@ -177,7 +179,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel
try
{
_processCtrl = new FinalProcessController(Slot);
_processCtrl = new ProcessController(Slot, _processStateDef);
}
catch (Exception ex)
{
@@ -0,0 +1,977 @@
using System;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Threading;
using LaaPackages.Features.Cordonel.Models;
using Newtonsoft.Json;
using NLog;
using Xylem.Common.CommonCore.Configuration;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EventArgs;
using Xylem.Common.Utils.Logging;
using Xylem.Common.Utils.UserAccessCtrl;
using Control = System.Windows.Forms.Control;
using TextBox = System.Windows.Controls.TextBox;
using UserControl = System.Windows.Controls.UserControl;
namespace Xylem.Common.Production.ProductionUiCordonel
{
/// <summary>
/// Interaction logic for FinalTest.xaml
/// </summary>
public sealed partial class ProductionWindow
{
#region variables and properties
private readonly ILogger _logger;
// remind manually changed culture setting
private static readonly CultureInfo _cultureInfo = Thread.CurrentThread.CurrentCulture;
private static readonly AssemblyName _assemblyName = Assembly.GetExecutingAssembly().GetName();
private static readonly String _assemblyExecRootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
private static readonly Version _version = _assemblyName.Version;
private readonly String _strSoftwareNameVersion;
private RegisterLDAPUser _registerForm;
private Int32 Slot
{
get; set;
}
// maximum processing steps for calculation of progress bar
private Int32 _maxOverallProcessSteps;
private Int32 _maxSingleProcessSteps;
private ProcessController _processCtrl;
private DateTimeOffset _startTime;
private DispatcherTimer _timer;
private String _lastLoggingTextToAvoidRepetition;
private CordonelRequirements _productionRequirements;
private UserAccessWindow _userAccessWindow;
private IProcessStateDef _processStateDef;
#endregion
public ProductionWindow(String softwareName, String projectName, IProcessStateDef processStateDef)
{
_logger = NLogHelper.CreateOrGetLogger(projectName);
_processStateDef = processStateDef;
String msg;
try
{
// returns true in DEBUG configuration
if (!Logic.SoftwareAccessHelper.Access.HasAccess(_assemblyName))
{
msg = $"{Properties.Resources.StrStartMessageSwLicenseExpired} {_version}";
_logger.Error(msg);
MessageBox.Show(msg, Properties.Resources.StrProcessStateError,
MessageBoxButton.OK, MessageBoxImage.Error);
Close();
return;
}
}
catch (Exception e)
{
msg = Properties.Resources.StrErrorMsgMissingNetworkConnetction + " " + e.Message;
_logger.Error(msg);
MessageBox.Show(msg,
Properties.Resources.StrProcessStateError,
MessageBoxButton.OK, MessageBoxImage.Error);
Close();
return;
}
InitializeComponent();
// Log version to files for debug purposes
_strSoftwareNameVersion = $"{softwareName} {_version}";
_logger.Info(_strSoftwareNameVersion);
Title = _strSoftwareNameVersion;
//static labels
UpdateContentControl(lblSerialNumberTxt, Properties.Resources.StrLblSerialNumberTxt + ":");
UpdateContentControl(lblRadioAddressText, Properties.Resources.StrLblRadioAddressTxt + ":");
UpdateContentControl(lblOrderNumberText, Properties.Resources.StrLblOrderNumberTxt + ":");
UpdateContentControl(lblMeterSizeText, Properties.Resources.StrLblMeterSizeTxt + ":");
UpdateContentControl(lblSingleProcessText, Properties.Resources.StrLblSingleProcessText + ":");
UpdateContentControl(lblTotalProcessText, Properties.Resources.StrLblTotalProcessText + ":");
optionsMenu.Header = Properties.Resources.StrMenuOptionsText;
optionsMenuChangePassword.Header = Properties.Resources.StrMenuChangePasswordText;
hlpMenu.Header = Properties.Resources.StrMenuHelpText;
UpdateUi(lblApprovalText, "");
UpdateUi(lblTimeText, Properties.Resources.StrLblTimeText + ":");
// buttons
UpdateContentControl(btnStart, Properties.Resources.StrBtnStart);
UpdateContentControl(btnStop, Properties.Resources.StrBtnStop);
InitializeFormState();
}
private ApplicationSettings Settings
{
get; set;
}
/// <summary>
/// Initialize the FinalTest GUI:
/// - Read the configuration file for slot and port assignments and settings,
/// - Dispatch slots to slot combobox,
/// - Read setting from "ProductionUiConfig.json" file for GUI and slot setup,
/// - Create final process controller and install events,
/// - Create a dispatch timer to display the elapsed time of the process.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Window_Initialized(Object sender, EventArgs e)
{
var configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
nameof(Hardware.WaterMeter.Genesis), ProgramConfig.SerialConfigFileName);
if (!File.Exists(configFile))
{
var msg = Properties.Resources.StrErrorMsgMissingConfigurationFile;
MessageBox.Show(msg,
Properties.Resources.StrProcessStateError,
MessageBoxButton.OK, MessageBoxImage.Error);
_logger.Error(msg);
throw new ApplicationException(msg);
}
var tr = new StreamReader(configFile);
var meterConfigList = JsonConvert.DeserializeObject<SlotConfig[]>(tr.ReadToEnd());
cbxSlotBox.Items.Clear();
if (meterConfigList != null)
{
foreach (var item in meterConfigList.OrderBy(s => s.Slot))
{
cbxSlotBox.Items.Add(item.Slot);
}
}
cbxSlotBox.SelectedIndex = 0;
Settings = new ApplicationSettings("ProductionUiConfig.json");
UseSetting(true);
if (cbxSlotBox.SelectedItem == null || string.IsNullOrEmpty(cbxSlotBox.SelectedItem.ToString()) ||
!int.TryParse(cbxSlotBox.SelectedItem.ToString(), out var slot))
{
Slot = 1;
}
else
{
Slot = slot;
}
try
{
_processCtrl = new ProcessController(Slot, _processStateDef);
}
catch (Exception ex)
{
_logger.Error(ex.Message);
MessageBox.Show(ex.Message,
Properties.Resources.StrProcessStateError,
MessageBoxButton.OK, MessageBoxImage.Error);
throw new ApplicationException(ex.Message);
}
//install the process changed event handler for single processes
_processCtrl.OnSingleProcessStateChanged += SingleProcessStateChanged_Handler;
_processCtrl.OnProcessLogRequest += ProcessLog_Handler;
_processCtrl.OnProcessProgressChanged += ProcessProgress_Handler;
_processCtrl.OnOverallProcessStateChanged += ProcessStateChange_Handler;
_timer = new DispatcherTimer();
_timer.Tick += TmrProgressUpdate_Tick;
_timer.Interval = TimeSpan.FromMilliseconds(500);
UpdateStatusGrid();
}
private void Window_Closing(Object sender, CancelEventArgs e)
{
_userAccessWindow?.Close();
_processCtrl.OnSingleProcessStateChanged -= SingleProcessStateChanged_Handler;
_processCtrl.OnProcessLogRequest -= ProcessLog_Handler;
_processCtrl.OnProcessProgressChanged -= ProcessProgress_Handler;
_processCtrl.OnOverallProcessStateChanged -= ProcessStateChange_Handler;
_userAccessWindow = null;
_processCtrl.Dispose();
_processCtrl = null;
_timer.Stop();
_timer.Tick -= TmrProgressUpdate_Tick;
_timer = null;
_registerForm = null;
}
private void UseSetting(Boolean setSlot = false)
{
//tblkAutodetectState.Text = "Off";
//if (Settings.AutoDetect)
//{
// tblkAutodetectState.Text = "On";
//}
if (setSlot)
{
cbxSlotBox.SelectedValue = Settings.Slot;
}
}
private void InitializeFormState()
{
if (Software.IsRegistrationPending)
{
_registerForm = new RegisterLDAPUser();
_registerForm.OnCancellation += OnUserAccessControlCancellation;
UiElmEnable(optionsMenu, false, false);
}
else
{
UiElmEnable(optionsMenu, true);
UiElmEnable(optionsMenuChangePassword, Software.IsAutenticated);
UiElmEnable(optionsMenuLogout, Software.IsAutenticated);
UiElmEnable(optionsMenuLogin, !Software.IsAutenticated);
UiElmEnable(btnStart, Software.IsAutenticated);
UiElmEnable(btnStop, false);
UpdateContentControl(lblOperator,
Software.IsAutenticated
? $"{Properties.Resources.StrLblOperatorNameText}: {Software.UserName}"
: $"{Properties.Resources.StrLblOperatorNameText}: ?");
}
}
/// <summary>
/// Shows the user access window with login form or password change form.
/// </summary>
/// <param name="visible"></param>
/// <param name="control">Windows forms control</param>
/// <param name="headerTxt"></param>
/// <remarks date="2025-Mar-25" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void ShowUserAccessWindow(Boolean visible = true, Control control = null, String headerTxt = "")
{
if (visible && control != null)
{
UiElmEnable(optionsMenu, false);
if (_userAccessWindow == null)
_userAccessWindow = new UserAccessWindow(control, headerTxt);
// View the user access window as container for the windows forms password change and login
_userAccessWindow.OnCloseWindow += OnUserAccessWindowClosed_Handler;
_userAccessWindow.ShowDialog();
}
else
{
if (_userAccessWindow != null)
{
_userAccessWindow.OnCloseWindow -= OnUserAccessWindowClosed_Handler;
try
{
_userAccessWindow.Close();
}
catch (Exception)
{
//ignore
}
_userAccessWindow = null;
}
if (_registerForm != null)
_registerForm.OnCancellation -= OnUserAccessControlCancellation;
UiElmEnable(optionsMenu, true);
}
}
/// <summary>
/// Close event handler of the user access window with login form or password change form.
/// </summary>
/// <param name="sender"></param>
/// <param name="eventArgs"></param>
/// <remarks date="2025-Mar-25" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void OnUserAccessWindowClosed_Handler(Object sender, EventArgs eventArgs)
{
ShowUserAccessWindow(false);
InitializeFormState();
}
private void UpdateUi(ContentControl c, String content, Brush color = null)
{
if (color == null)
color = Brushes.Black;
c.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() =>
{
c.Content = content;
c.Foreground = color;
}));
}
private void UpdateUi(TextBox c, String content, Brush color = null)
{
if (color == null)
color = Brushes.Black;
c.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() =>
{
c.Text = content;
c.Foreground = color;
}));
}
private void UpdateUi(TextBlock c, String content, Brush color = null)
{
if (color == null)
color = Brushes.Black;
c.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() =>
{
c.Text = content;
c.Foreground = color;
}));
}
/// <summary>
/// Update the elapsed time as information
/// </summary>
/// <returns>information attached to process depending on region</returns>
/// <remarks date="2024-Jul-11" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void SetTimeDisplay()
{
var time = DateTimeOffset.UtcNow;
var timeSpan = time - _startTime;
UpdateContentControl(lblTimeValue, $@"{(UInt32)timeSpan.TotalMinutes}:{timeSpan.Seconds:00}");
}
/// <summary>
/// Common routine for state change:
/// - Log state change,
/// - Execute stop procedure on error.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks date="2020-Dec-12" author="Roland Drabesch">
/// - Initial
/// </remarks>
/// <remarks date="2024-Aug-30" author="Thomas Wiedebusch">
/// - Extended.
/// </remarks>
/// <remarks date="2025-Apr-10" author="Thomas Wiedebusch">
/// - Extended.
/// </remarks>
private void ProcessStateChange_Handler(Object sender, ProcessStateArgs e)
{
_logger.Debug($"Slot:{Slot} - Final test state: {e.State}");
// Nothing to display
if (e.State == ProcessState.Idle)
{
// Update progress bars
ProcessProgress_Handler(this, new ProcessProgressArgs(0,
ProcessProgressArgs.ProcessProgressType.OverallProcessesSteps));
ProcessProgress_Handler(this, new ProcessProgressArgs(0,
ProcessProgressArgs.ProcessProgressType.ActualProcessSteps));
}
// Stop all procedures
if (e.State == ProcessState.Stop)
{
_timer?.Stop();
UiElmEnable(btnStart, true);
UiElmEnable(btnStop, false);
// update progress bars
ProcessProgress_Handler(this, new ProcessProgressArgs(0,
ProcessProgressArgs.ProcessProgressType.OverallProcessesSteps));
ProcessProgress_Handler(this, new ProcessProgressArgs(0,
ProcessProgressArgs.ProcessProgressType.ActualProcessSteps));
// remove user control
SetNewUserControl(null);
}
// Repeat failed tests
if (e.State == ProcessState.RepeatFailedTests)
{
UiElmEnable(btnStart, false);
UiElmEnable(btnStop, true);
_timer?.Start();
}
// Stop time for manual operation and visual inspection as this is not part of the configuration
if (e.State == ProcessState.CheckOrderNumber)
{
_timer?.Stop();
}
// Prepare shipping
if (e.State == ProcessState.PrepareShippingMode)
{
_timer?.Stop();
UiElmEnable(btnStart, false);
UiElmEnable(btnStop, false);
}
// Ready for shipping
if (e.State == ProcessState.ReadyForShipping)
{
// Create extended report including all steps and each register write, read and comparison
CreateFile(Properties.Resources.StrInfoSuccessPapers);
BtnStop_Click(this, null);
}
// Print return report
if (e.State == ProcessState.PrintReturnReport)
{
PrintErrorNote();
BtnStop_Click(this, null);
}
// Print success report
if (e.State == ProcessState.PrintSuccessReport)
{
PrintSuccessNote();
BtnStop_Click(this, null);
}
}
/// <summary>
/// Enable UI elements
/// </summary>
/// <param name="elm"></param>
/// <param name="isEnabled"></param>
/// <param name="isVisible"></param>
private void UiElmEnable(UIElement elm, Boolean isEnabled, Boolean isVisible = true)
{
elm.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() =>
{
elm.Visibility = isVisible ? Visibility.Visible : Visibility.Hidden;
elm.IsEnabled = isEnabled;
}));
}
/// <summary>
/// Fill logging window with information
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks date="????" author="Roland Drabesch">
/// - Initial
/// </remarks>
/// <remarks date="2023-07-06" author="Thomas Wiedebusch">
/// - Changed to rich text edit with colored output.
/// </remarks>
/// <remarks date="2024-08-28" author="Thomas Wiedebusch">
/// - Avoid output of repeated text or useless empty text.
/// </remarks>
private void ProcessLog_Handler(Object sender, ProcessLogArgs e)
{
rtbLog.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() =>
{
if (e != null)
{
try
{
// remove unacceptable spacing between the lines
rtbLog.Document.LineHeight = 1;
var color = e.Color ?? Brushes.Black;
var text = e.Data;
if (!string.IsNullOrEmpty(text) && !text.Equals(_lastLoggingTextToAvoidRepetition))
{
var tr = new TextRange(rtbLog.Document.ContentEnd, rtbLog.Document.ContentEnd)
{
Text = text
};
tr.ApplyPropertyValue(TextElement.ForegroundProperty, color);
rtbLog.ScrollToEnd();
rtbLog.AppendText(Environment.NewLine);
}
_lastLoggingTextToAvoidRepetition = text;
}
catch (FormatException)
{
}
}
}
));
}
/// <summary>
/// Set values for progress bar, maximum and actual for single process and overall process.
/// - Limit values to 0.0..100.0.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks date="????" author="Roland Drabesch">
/// - Initial
/// </remarks>
/// <remarks date="2024-07-18" author="Thomas Wiedebusch">
/// - Introduced multi line text with center alignment.
/// </remarks>
private void ProcessProgress_Handler(Object sender, ProcessProgressArgs e)
{
if (e.ProgressType == ProcessProgressArgs.ProcessProgressType.MaxOverallProcessSteps)
{
_maxOverallProcessSteps = e.Progress;
}
else if (e.ProgressType == ProcessProgressArgs.ProcessProgressType.MaxActualProcessSteps)
{
_maxSingleProcessSteps = e.Progress;
}
if (e.ProgressType == ProcessProgressArgs.ProcessProgressType.OverallProcessesSteps)
{
pbTotalProgress.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() =>
{
// limit output to progress bar to 0..100 as integer for process bar
var progress = _maxOverallProcessSteps <= 0 ? 0.0 : e.Progress * 100.0 / _maxOverallProcessSteps;
progress = progress > 100.0 ? 100.0 : progress < 0.0 ? 0.0 : progress;
pbTotalProgress.Value = (Int32)progress;
UpdateContentControl(lblTotalProgressValue, $@"{progress:##0.0} %");
}
));
}
else if (e.ProgressType == ProcessProgressArgs.ProcessProgressType.ActualProcessSteps)
{
pbSubProgress.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() =>
{
// limit output to progress bar to 0..100 as integer for process bar
var progress = _maxSingleProcessSteps <= 0 ? 0.0 : e.Progress * 100.0 / _maxSingleProcessSteps;
progress = progress > 100.0 ? 100.0 : progress < 0.0 ? 0.0 : progress;
pbSubProgress.Value = (Int32)progress;
UpdateContentControl(lblSubProgressValue, $@"{progress:##0.0} %");
}
));
}
}
/// <summary>
/// Output message to control without access violation.
/// </summary>
/// <returns></returns>
/// <remarks date="????" author="Roland Drabesch">
/// - Initial
/// </remarks>
/// <remarks date="2023-07-11" author="Thomas Wiedebusch">
/// - Introduced multi line text with center alignment.
/// </remarks>
private static void UpdateContentControl(ContentControl ctl, String text)
{
ctl.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() =>
{
ctl.Content = new TextBlock()
{
Text = text,
TextWrapping = TextWrapping.Wrap,
TextAlignment = TextAlignment.Center
};
}
));
}
/// <summary>
/// Status grid update for all single processes
/// </summary>
private void UpdateStatusGrid()
{
var dt = new DataTable();
dt.Columns.Add(Properties.Resources.StrDataTableColumnProcess);
dt.Columns.Add(Properties.Resources.StrDataTableColumnState);
var i = 0;
if (_processCtrl?.Processes == null)
return;
foreach (var item in _processCtrl.Processes)
{
dt.Rows.Add();
dt.Rows[i][0] = item.ProcessName;
dt.Rows[i][1] = ProductionProcessStateHelper.GetTextFromProductionProcessState(item.SingleProcessState);
i++;
}
dgTotalProcess.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() =>
{
dgTotalProcess.DataContext = dt;
}
));
}
/// <summary>
/// Dispatch new user control to main screen
/// </summary>
/// <param name="uc"></param>
private void SetNewUserControl(UserControl uc)
{
dgTotalProcess.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() =>
{
// remove last userControl
dpSubProcess.Children.Clear();
if (uc != null)
{
dpSubProcess.Children.Add(uc);
DockPanel.SetDock(uc, Dock.Top); // & Dock.Left);
}
}
));
}
#region TimerControls
/// <summary>
/// Handle timing depending updates.
/// </summary>
/// <returns></returns>
/// <remarks date="2024-07-11" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void TmrProgressUpdate_Tick(Object sender, EventArgs e)
{
SetTimeDisplay();
}
#endregion
/// <summary>
/// Update of status grid with information returned by user control.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks date="????" author="Roland Drabesch">
/// - Initial
/// </remarks>
/// <remarks date="2024-08-28" author="Thomas Wiedebusch">
/// - Activate new user control on start of process.
/// </remarks>
private void SingleProcessStateChanged_Handler(Object sender, SingleProcessStateArgs e)
{
if (sender is IProductionProcess productionProcess)
{
if (e.SingleProcessState == SingleProcessState.Running)
{
// Set new user control
SetNewUserControl(productionProcess.GetUserControl());
}
else
{
// remove user control on idle, skipped, abort or error
SetNewUserControl(null);
}
}
UpdateStatusGrid();
UpdateGuiWithProductInfos();
}
private void OnUserAccessControlCancellation(Control control)
{
OnUserAccessWindowClosed_Handler(this, null);
}
/// <summary>
/// Set labels of device information and order number
/// </summary>
/// <remarks date="????" author="Roland Drabesch">
/// - Initial
/// </remarks>
/// <remarks date="2024-07-15" author="Thomas Wiedebusch">
/// - Additional info of Cordonel.
/// </remarks>
private void UpdateGuiWithProductInfos()
{
var genesisMeter = _processCtrl?.GetGenesisMeter();
_productionRequirements = _processCtrl?.GetProductionRequirements();
if (genesisMeter == null)
return;
UpdateContentControl(lblOrderNumberValue,
genesisMeter.OrderNumber != 0 ? genesisMeter.OrderNumber.ToString() : "?");
UpdateContentControl(lblRadioAddressValue,
genesisMeter.RadioAddress != 0 ? genesisMeter.RadioAddress.ToString() : "");
UpdateContentControl(lblRadioAddressText,
genesisMeter.RadioAddress != 0 ? Properties.Resources.StrLblRadioAddressTxt + ":" : "");
UpdateContentControl(lblFwValue,
!string.IsNullOrEmpty(genesisMeter.FwVersion) ? genesisMeter.FwVersion : "?");
UpdateContentControl(lblMeterSizeValue,
!string.IsNullOrEmpty(genesisMeter.MeterSize) ? genesisMeter.MeterSize : "?");
UpdateContentControl(lblLutValue,
!string.IsNullOrEmpty(genesisMeter.LutCrc) ? genesisMeter.LutCrc : "?");
UpdateContentControl(lblSerialNumberValue,
!string.IsNullOrEmpty(genesisMeter.CustomerSerialNumber) ? genesisMeter.CustomerSerialNumber : "?");
UpdateContentControl(lblRegionValue,
!string.IsNullOrEmpty(genesisMeter.Region) ? genesisMeter.Region : "?");
UpdateUi(lblPcbIdValue, !string.IsNullOrEmpty(genesisMeter.PcbId) ? genesisMeter.PcbId : "?");
var msg = "";
var color = Brushes.Black;
if (_productionRequirements?.IsSpecialActive != null && (Boolean)_productionRequirements.IsSpecialActive &&
_productionRequirements?.IsSpecialApproved != null && (Boolean)_productionRequirements.IsSpecialApproved)
{
msg = $"{Properties.Resources.StrLblSpecialRequirement} - Id.Ver: {_productionRequirements.SpecialId}." +
$"{_productionRequirements.SpecialVersion}";
color = Brushes.Red;
}
else if (_productionRequirements?.IsStandardActive != null && (Boolean)_productionRequirements.IsStandardActive &&
_productionRequirements?.IsStandardApproved != null && (Boolean)_productionRequirements.IsStandardApproved)
{
msg = $"{Properties.Resources.StrLblStandardRequirement} - Id.Ver: {_productionRequirements.StandardId}." +
$"{_productionRequirements.StandardVersion}";
}
UpdateUi(lblApprovalText, msg, color);
}
//private void BtnSwitchAutoDetect_Click(Object sender, RoutedEventArgs e)
//{
// //Settings.AutoDetect = !Settings.AutoDetect;
// //StoreSetting();
// //UseSetting();
//}
private void OnLogout_Click(Object sender, EventArgs e)
{
Software.Logout();
InitializeFormState();
}
private void OnLogin_Click(Object sender, EventArgs args)
{
ShowUserAccessWindow(true, new LoginForm(OnUserAccessControlCancellation),
Properties.Resources.StrLoginMsg);
}
private void OnChangePassword_Click(Object _, EventArgs _1)
{
ShowUserAccessWindow(true, new ChangePassword(OnUserAccessControlCancellation),
Properties.Resources.StrChangePasswordMsg);
}
private void CbxSlotBox_SelectionChanged(Object sender, SelectionChangedEventArgs e)
{
}
private void StoreSetting()
{
Settings.Update("ProductionUiConfig.json");
}
//private void BtnConnect_Click(Object sender, RoutedEventArgs e)
//{
// Settings.Slot = (Int32)cbxSlotBox.SelectedValue;
// StoreSetting();
//}
private void BtnStart_Click(Object sender, RoutedEventArgs e)
{
UiElmEnable(btnStart, false);
UiElmEnable(btnStop, true);
_startTime = DateTimeOffset.UtcNow;
_timer?.Start();
// remove last userControl
dpSubProcess.Children.Clear();
rtbLog.Document.Blocks.Clear();
// clear all visual fields as it may be a new pcbId
UpdateContentControl(lblOrderNumberValue, "?");
UpdateContentControl(lblRadioAddressValue, "");
UpdateContentControl(lblRadioAddressText, "");
UpdateContentControl(lblFwValue, "?");
UpdateContentControl(lblMeterSizeValue, "?");
UpdateContentControl(lblLutValue, "?");
UpdateContentControl(lblSerialNumberValue, "?");
UpdateContentControl(lblRegionValue, "?");
UpdateUi(lblPcbIdValue, "?");
_processCtrl?.SetProcessState(ProcessState.DetectCordonel);
}
private void PrintErrorNote()
{
// Safe report to file
CreateFile(Properties.Resources.StrInstructionReturnPapers, true, true);
}
private void PrintSuccessNote()
{
// Safe report to file
CreateFile(Properties.Resources.StrInfoSuccessPapers, true, true);
}
/// <summary>
/// Logging a file with all process information, building a compressed report for printout.
/// </summary>
/// <remarks date="2025-Apr-10" author="Thomas Wiedebusch">
/// - Reduced printout.
/// </remarks>
private void CreateFile(String headerInfo, Boolean shortReport = false, Boolean printReport = false)
{
var sb = new StringBuilder();
var genesisMeter = _processCtrl.GetGenesisMeter();
sb.AppendLine("========================================================================");
sb.AppendLine(headerInfo);
sb.AppendLine("========================================================================");
sb.AppendLine(_strSoftwareNameVersion);
sb.AppendLine("------------------------------------------------------------------------");
sb.AppendLine($"{Properties.Resources.StrDateTime}: {DateTime.Now}");
sb.AppendLine("------------------------------------------------------------------------");
sb.AppendLine($"PcbId: {genesisMeter.PcbId}");
sb.AppendLine($"{Properties.Resources.StrPrintOrderNumberTxt}: {genesisMeter.OrderNumber}");
sb.AppendLine($"{Properties.Resources.StrLblSerialNumberTxt}: {genesisMeter.CustomerSerialNumber}");
sb.AppendLine($"{Properties.Resources.StrLblOperatorNameText}: {Software.UserName}");
sb.AppendLine("------------------------------------------------------------------------");
//TODO THW take the log path from setup (done with GTB)
var loggingPath = $"C:\\GenesisLog\\{DateTime.Now:yyyy-MM-dd}\\ProductionUiCordonel\\";
var fileName = $"{loggingPath}{DateTime.Now.ToString("yyyyMMddHHmmss")}_ExtendedReport_{genesisMeter.PcbId}.txt";
// Copy the header to the compressed string builder
var compressedSb = new StringBuilder();
if (shortReport)
{
compressedSb.Append(sb);
}
var rtbText = new TextRange(
rtbLog.Document.ContentStart,
rtbLog.Document.ContentEnd
);
sb.Append($"{rtbText.Text}");
// Write the complete report to the file
File.WriteAllText(fileName, sb.ToString());
// Reduce sb content to compress it for printout
if (shortReport)
{
fileName = $"{loggingPath}{DateTime.Now.ToString("yyyyMMddHHmmss")}" +
$"_ShortReport_{genesisMeter.PcbId}.txt";
var sr = new StringReader(rtbText.Text);
var srLine = sr.ReadLine();
while (srLine != null)
{
if (srLine.Contains(Properties.Resources.StrProcessStateError) ||
srLine.Contains(Properties.Resources.StrProcessStateDone) ||
srLine.Contains(Properties.Resources.StrProcessStateWarning) ||
srLine.Contains(Properties.Resources.StrProcessStateSkipped) ||
srLine.Contains(Properties.Resources.StrWarningMsgSpecialRequirementActive))
compressedSb.AppendLine(srLine);
srLine = sr.ReadLine();
}
// Returns the compressed file for error report printout
File.WriteAllText(fileName, compressedSb.ToString());
if (printReport)
{
Process.Start(fileName);
}
}
}
/// <summary>
/// Stop button as user interaction to kill the finalization process
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnStop_Click(Object sender, RoutedEventArgs e)
{
//set the process state to the state machine
_processCtrl?.SetProcessState(ProcessState.Stop);
}
/// <summary>
/// Open requirement in Web-Browser.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void LblApprovalText_Click(Object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var requirementId = 0;
var requirementWebPageLink = ServiceUrls.InspectCordonelRequirementInfoServiceUrl();
if (_productionRequirements?.SpecialId != null &&
_productionRequirements?.IsSpecialApproved != null &&
_productionRequirements.SpecialId != 0)
{
requirementId = (Int32)_productionRequirements.SpecialId;
requirementWebPageLink += $"SpecialRequirements#{requirementId}";
}
else if (_productionRequirements?.StandardId != null &&
_productionRequirements?.IsStandardApproved != null &&
_productionRequirements.StandardId != 0)
{
requirementId = (Int32)_productionRequirements.StandardId;
requirementWebPageLink += $"StandardRequirements#{requirementId}";
}
if (requirementId != 0)
{
Process.Start(requirementWebPageLink);
}
}
private void HlpMenu_Click(Object sender, RoutedEventArgs e)
{
try
{
var filename = "";
if (Thread.CurrentThread.CurrentCulture.Name.Contains("de"))
{
filename = Path.Combine(_assemblyExecRootPath, "Docu", @"Handbuch Cordonel Versandfertigmachen (VFM).pdf");
}
else if (Thread.CurrentThread.CurrentCulture.Name.Contains("en"))
{
filename = Path.Combine(_assemblyExecRootPath, "Docu", @"Manual Cordonel End of Line Test (EOL).pdf");
}
if (!string.IsNullOrEmpty(filename))
{
Process.Start(filename);
}
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
}
private void Window_Loaded(Object sender, RoutedEventArgs e)
{
if (!Software.IsAutenticated)
OnLogin_Click(this, null);
}
}
}
@@ -285,6 +285,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Cordonel - Shipping Preparation - Version.
/// </summary>
internal static string StrEolSoftwareNameVersion {
get {
return ResourceManager.GetString("StrEolSoftwareNameVersion", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ERROR.
/// </summary>
@@ -855,18 +864,18 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
/// <summary>
/// Looks up a localized string similar to ERROR: Maximum drained battery load overflow.
/// </summary>
internal static string StrErrorMsgThresholdDrainedBatteryAtEol {
internal static string StrErrorMsgThresholdDrainedBattery {
get {
return ResourceManager.GetString("StrErrorMsgThresholdDrainedBatteryAtEol", resourceCulture);
return ResourceManager.GetString("StrErrorMsgThresholdDrainedBattery", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Minimum required lifetime underflow.
/// </summary>
internal static string StrErrorMsgThresholdRemainingLifeTimeAtEol {
internal static string StrErrorMsgThresholdRemainingLifeTime {
get {
return ResourceManager.GetString("StrErrorMsgThresholdRemainingLifeTimeAtEol", resourceCulture);
return ResourceManager.GetString("StrErrorMsgThresholdRemainingLifeTime", resourceCulture);
}
}
@@ -1260,9 +1269,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
/// <summary>
/// Looks up a localized string similar to Drained battery load.
/// </summary>
internal static string StrMsgDrainedBatteryAtEol {
internal static string StrMsgDrainedBattery {
get {
return ResourceManager.GetString("StrMsgDrainedBatteryAtEol", resourceCulture);
return ResourceManager.GetString("StrMsgDrainedBattery", resourceCulture);
}
}
@@ -1332,36 +1341,36 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
/// <summary>
/// Looks up a localized string similar to Estimated remaining lifetime.
/// </summary>
internal static string StrMsgRemainingLifeTimeAtEol {
internal static string StrMsgRemainingLifeTime {
get {
return ResourceManager.GetString("StrMsgRemainingLifeTimeAtEol", resourceCulture);
return ResourceManager.GetString("StrMsgRemainingLifeTime", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Minimum required lifetime.
/// </summary>
internal static string StrMsgRequiredLifeTimeAtEol {
internal static string StrMsgRequiredLifeTime {
get {
return ResourceManager.GetString("StrMsgRequiredLifeTimeAtEol", resourceCulture);
return ResourceManager.GetString("StrMsgRequiredLifeTime", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Absolut restriction.
/// </summary>
internal static string StrMsgRequirementAbsolutLimitDrainedBatteryAtEol {
internal static string StrMsgRequirementAbsolutLimitDrainedBattery {
get {
return ResourceManager.GetString("StrMsgRequirementAbsolutLimitDrainedBatteryAtEol", resourceCulture);
return ResourceManager.GetString("StrMsgRequirementAbsolutLimitDrainedBattery", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Maximum allowed.
/// </summary>
internal static string StrMsgRequirementLimitDrainedBatteryAtEol {
internal static string StrMsgRequirementLimitDrainedBattery {
get {
return ResourceManager.GetString("StrMsgRequirementLimitDrainedBatteryAtEol", resourceCulture);
return ResourceManager.GetString("StrMsgRequirementLimitDrainedBattery", resourceCulture);
}
}
@@ -1392,6 +1401,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Cordonel - Picking - Version.
/// </summary>
internal static string StrPickingSoftwareNameVersion {
get {
return ResourceManager.GetString("StrPickingSoftwareNameVersion", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Production Order Number .
/// </summary>
@@ -1518,15 +1536,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Cordonel - Shipping Preparation - Version.
/// </summary>
internal static string StrSoftwareNameVersion {
get {
return ResourceManager.GetString("StrSoftwareNameVersion", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New Cordonel EOL Software available! The software is going to exit! This version:.
/// </summary>
@@ -2052,9 +2061,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
/// <summary>
/// Looks up a localized string similar to Success: Minimum required lifetime is in range.
/// </summary>
internal static string StrSuccessMsgThresholdRemainingLifeTimeAtEol {
internal static string StrSuccessMsgThresholdRemainingLifeTime {
get {
return ResourceManager.GetString("StrSuccessMsgThresholdRemainingLifeTimeAtEol", resourceCulture);
return ResourceManager.GetString("StrSuccessMsgThresholdRemainingLifeTime", resourceCulture);
}
}
@@ -700,42 +700,45 @@
<data name="StrSuccessMsgDbToMeterEncryptionKey" xml:space="preserve">
<value>Erfolg: Datenbank und Cordonel Funkschlüssel erfolgreich abgeglichen</value>
</data>
<data name="StrMsgDrainedBatteryAtEol" xml:space="preserve">
<data name="StrMsgDrainedBattery" xml:space="preserve">
<value>Verbrauchte Batterieladung</value>
</data>
<data name="StrMsgAlternativeDrainedBatteryCalculation" xml:space="preserve">
<value>Berechnung basierend auf Ruhestrom und Produktionszuschlag</value>
</data>
<data name="StrErrorMsgThresholdDrainedBatteryAtEol" xml:space="preserve">
<data name="StrErrorMsgThresholdDrainedBattery" xml:space="preserve">
<value>FEHLER: Maximale erlaubte Batterieentladung überschritten</value>
</data>
<data name="StrMsgRequirementAbsolutLimitDrainedBatteryAtEol" xml:space="preserve">
<data name="StrMsgRequirementAbsolutLimitDrainedBattery" xml:space="preserve">
<value>Absoluter Grenzwert</value>
</data>
<data name="StrMsgRequirementLimitDrainedBatteryAtEol" xml:space="preserve">
<data name="StrMsgRequirementLimitDrainedBattery" xml:space="preserve">
<value>Maximal erlaubt</value>
</data>
<data name="StrMsgRemainingLifeTimeAtEol" xml:space="preserve">
<data name="StrMsgRemainingLifeTime" xml:space="preserve">
<value>Abgeschätzte Restlaufzeit</value>
</data>
<data name="StrErrorMsgThresholdRemainingLifeTimeAtEol" xml:space="preserve">
<data name="StrErrorMsgThresholdRemainingLifeTime" xml:space="preserve">
<value>FEHLER: Minimale geforderte Restlaufzeit unterschritten</value>
</data>
<data name="StrErrorMsgBatteryLoadCalculation" xml:space="preserve">
<value>FEHLER: Batterieentladung konnte nicht berechnet werden</value>
</data>
<data name="StrSuccessMsgThresholdRemainingLifeTimeAtEol" xml:space="preserve">
<data name="StrSuccessMsgThresholdRemainingLifeTime" xml:space="preserve">
<value>Erfolg: Minimale Restlaufzeit eingehalten</value>
</data>
<data name="StrMsgRequiredLifeTimeAtEol" xml:space="preserve">
<data name="StrMsgRequiredLifeTime" xml:space="preserve">
<value>Minimal geforderte Restlaufzeit</value>
</data>
<data name="StrMsgYears" xml:space="preserve">
<value>Jahre</value>
</data>
<data name="StrSoftwareNameVersion" xml:space="preserve">
<data name="StrEolSoftwareNameVersion" xml:space="preserve">
<value>Cordonel - Versandfertigmachen - Version</value>
</data>
<data name="StrPickingSoftwareNameVersion" xml:space="preserve">
<value>Cordonel - Montage - Version</value>
</data>
<data name="StrInstructionScanHousing" xml:space="preserve">
<value>Deckel scannen</value>
</data>
@@ -700,42 +700,45 @@
<data name="StrSuccessMsgDbToMeterEncryptionKey" xml:space="preserve">
<value>Success: Database with Cordonel radio encryption key successfully crosschecked</value>
</data>
<data name="StrMsgDrainedBatteryAtEol" xml:space="preserve">
<data name="StrMsgDrainedBattery" xml:space="preserve">
<value>Drained battery load</value>
</data>
<data name="StrMsgAlternativeDrainedBatteryCalculation" xml:space="preserve">
<value>Calculation based on quiescent current and production supplement</value>
</data>
<data name="StrErrorMsgThresholdDrainedBatteryAtEol" xml:space="preserve">
<data name="StrErrorMsgThresholdDrainedBattery" xml:space="preserve">
<value>ERROR: Maximum drained battery load overflow</value>
</data>
<data name="StrMsgRequirementAbsolutLimitDrainedBatteryAtEol" xml:space="preserve">
<data name="StrMsgRequirementAbsolutLimitDrainedBattery" xml:space="preserve">
<value>Absolut restriction</value>
</data>
<data name="StrMsgRequirementLimitDrainedBatteryAtEol" xml:space="preserve">
<data name="StrMsgRequirementLimitDrainedBattery" xml:space="preserve">
<value>Maximum allowed</value>
</data>
<data name="StrMsgRemainingLifeTimeAtEol" xml:space="preserve">
<data name="StrMsgRemainingLifeTime" xml:space="preserve">
<value>Estimated remaining lifetime</value>
</data>
<data name="StrErrorMsgThresholdRemainingLifeTimeAtEol" xml:space="preserve">
<data name="StrErrorMsgThresholdRemainingLifeTime" xml:space="preserve">
<value>ERROR: Minimum required lifetime underflow</value>
</data>
<data name="StrErrorMsgBatteryLoadCalculation" xml:space="preserve">
<value>ERROR: Failed to calculate battery load</value>
</data>
<data name="StrSuccessMsgThresholdRemainingLifeTimeAtEol" xml:space="preserve">
<data name="StrSuccessMsgThresholdRemainingLifeTime" xml:space="preserve">
<value>Success: Minimum required lifetime is in range</value>
</data>
<data name="StrMsgRequiredLifeTimeAtEol" xml:space="preserve">
<data name="StrMsgRequiredLifeTime" xml:space="preserve">
<value>Minimum required lifetime</value>
</data>
<data name="StrMsgYears" xml:space="preserve">
<value>years</value>
</data>
<data name="StrSoftwareNameVersion" xml:space="preserve">
<data name="StrEolSoftwareNameVersion" xml:space="preserve">
<value>Cordonel - Shipping Preparation - Version</value>
</data>
<data name="StrPickingSoftwareNameVersion" xml:space="preserve">
<value>Cordonel - Picking - Version</value>
</data>
<data name="StrInstructionScanHousing" xml:space="preserve">
<value>Scan housing</value>
</data>
@@ -1,4 +1,4 @@
namespace Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest
namespace Xylem.Common.Production.ProductionUiCordonel.UserControls
{
public interface IUserControl
{
@@ -1,4 +1,4 @@
<UserControl x:Class="Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest.UcBarcodeInput"
<UserControl x:Class="Xylem.Common.Production.ProductionUiCordonel.UserControls.UcBarcodeInput"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@@ -2,7 +2,7 @@
using System.Windows.Input;
using System.Windows.Threading;
namespace Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest
namespace Xylem.Common.Production.ProductionUiCordonel.UserControls
{
/// <summary>
/// Interaction logic for UcDialog.xaml
@@ -1,4 +1,4 @@
<UserControl x:Class="Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest.UcDialog"
<UserControl x:Class="Xylem.Common.Production.ProductionUiCordonel.UserControls.UcDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@@ -5,7 +5,7 @@ using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
namespace Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest
namespace Xylem.Common.Production.ProductionUiCordonel.UserControls
{
/// <summary>
/// Interaction logic for UcDialog.xaml
@@ -1,4 +1,4 @@
<UserControl x:Class="Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest.UcPasswordFile"
<UserControl x:Class="Xylem.Common.Production.ProductionUiCordonel.UserControls.UcPasswordFile"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@@ -4,7 +4,7 @@ using System.Windows.Controls;
using System.Windows.Threading;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
namespace Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest
namespace Xylem.Common.Production.ProductionUiCordonel.UserControls
{
/// <summary>
/// Interaction logic for UcPasswordFile.xaml
@@ -1,4 +1,4 @@
<UserControl x:Class="Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest.UcPlain"
<UserControl x:Class="Xylem.Common.Production.ProductionUiCordonel.UserControls.UcPlain"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@@ -1,7 +1,7 @@
using System;
using System.Windows.Threading;
namespace Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest
namespace Xylem.Common.Production.ProductionUiCordonel.UserControls
{
/// <summary>
/// Interaction logic for UcPlain.xaml
@@ -1,4 +1,4 @@
<UserControl x:Class="Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest.UcRadioCheck"
<UserControl x:Class="Xylem.Common.Production.ProductionUiCordonel.UserControls.UcRadioCheck"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@@ -6,7 +6,7 @@ using System.Windows.Media;
using System.Windows.Threading;
using Color = System.Drawing.Color;
namespace Xylem.Common.Production.ProductionUiCordonel.UserControls.FinalTest
namespace Xylem.Common.Production.ProductionUiCordonel.UserControls
{
/// <summary>
/// Interaction logic for UcRadioCheck.xaml