diff --git a/Common/.shared/SharedAssemblyInfo.cs b/Common/.shared/SharedAssemblyInfo.cs
index bcca328f..6c4ebd47 100644
--- a/Common/.shared/SharedAssemblyInfo.cs
+++ b/Common/.shared/SharedAssemblyInfo.cs
@@ -14,4 +14,4 @@ using System.Reflection;
//[assembly: AssemblyVersion("1.2.*.0")]
-[assembly: AssemblyVersion("2.6.16.*")]
+[assembly: AssemblyVersion("2.7.0.*")]
diff --git a/Common/Production/ProductionUiCordonel/FinalTest.xaml.cs b/Common/Production/ProductionUiCordonel/FinalTest.xaml.cs
index 96393683..4e1fae51 100644
--- a/Common/Production/ProductionUiCordonel/FinalTest.xaml.cs
+++ b/Common/Production/ProductionUiCordonel/FinalTest.xaml.cs
@@ -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;
@@ -53,7 +54,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel
// maximum processing steps for calculation of progress bar
private Int32 _maxOverallProcessSteps;
private Int32 _maxSingleProcessSteps;
- private FinalProcessController _processCtrl;
+ private BaseProcessController _processCtrl;
private DateTimeOffset _startTime;
private DispatcherTimer _timer;
private String _lastLoggingTextToAvoidRepetition;
@@ -177,7 +178,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel
try
{
- _processCtrl = new FinalProcessController(Slot);
+ _processCtrl = new EolProcessController(Slot);
}
catch (Exception ex)
{
diff --git a/Common/Production/ProductionUiCordonel/Picking.xaml.cs b/Common/Production/ProductionUiCordonel/Picking.xaml.cs
index 5aee7568..72e42923 100644
--- a/Common/Production/ProductionUiCordonel/Picking.xaml.cs
+++ b/Common/Production/ProductionUiCordonel/Picking.xaml.cs
@@ -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;
@@ -53,7 +54,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel
// maximum processing steps for calculation of progress bar
private Int32 _maxOverallProcessSteps;
private Int32 _maxSingleProcessSteps;
- private FinalProcessController _processCtrl;
+ private BaseProcessController _processCtrl;
private DateTimeOffset _startTime;
private DispatcherTimer _timer;
private String _lastLoggingTextToAvoidRepetition;
@@ -177,7 +178,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel
try
{
- _processCtrl = new FinalProcessController(Slot);
+ _processCtrl = new EolProcessController(Slot);
}
catch (Exception ex)
{
diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/FinalProcessController.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/BaseProcessController.cs
similarity index 62%
rename from Common/Production/ProductionUiCordonel/ProductionProcesses/FinalProcessController.cs
rename to Common/Production/ProductionUiCordonel/ProductionProcesses/BaseProcessController.cs
index 408c53a3..8ddac59b 100644
--- a/Common/Production/ProductionUiCordonel/ProductionProcesses/FinalProcessController.cs
+++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/BaseProcessController.cs
@@ -8,27 +8,20 @@ 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.EolProcesses;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EventArgs;
using Timer = System.Threading.Timer;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
{
///
- /// 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
///
- public sealed class FinalProcessController
+ public abstract class BaseProcessController : IProcessController
{
#region ------------------------------------------ Variables --------------------------------------------------
// actual genesis meter
- private IGenesisMeter _genesisMeter;
+ protected IGenesisMeter _genesisMeter;
// Cordonel requirements for production
private CordonelRequirements _cordonelRequirements;
// EOL progress states for production
@@ -38,19 +31,20 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
// batch just used for setup, as only one meter will be assigned
private MeterBatch _meterBatch;
// state of the state machine
- private ProcessState _stateMachineState;
+ protected ProcessState _stateMachineState;
// locker to avoid repeated state execution
- private ProcessState _lockedProcessState;
+ protected ProcessState _lockedProcessState;
// sender of state change event to evaluate in e.g. error process
- private IProductionProcess _stateChangeRequestProcess;
+ protected IProductionProcess _stateChangeRequestProcess;
// List of production processes
public List Processes
{
- get; private set;
+ get;
+ private set;
}
// Container for production process states
- private ProcessStateDef _processStateDef;
+ protected EolProcessStates _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,24 +63,36 @@ 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
+ ///
+ /// Change the overview table of the GUI to the state of the individual process
+ ///
public event EventHandler OnSingleProcessStateChanged;
+
+ ///
+ /// 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.
+ ///
public event EventHandler OnOverallProcessStateChanged;
+
+ ///
+ /// Request for logging to GUI output and log-file
+ ///
public event EventHandler OnProcessLogRequest;
- // progress bar and screen info of ongoing processes
+
+ ///
+ /// Keep the progress bars of the GUI up to date
+ ///
public event EventHandler OnProcessProgressChanged;
// cancellation token
- private CancellationTokenSource _cancellationTokenSource;
- private CancellationToken _cancellationToken;
+ protected CancellationTokenSource _cancellationTokenSource;
+ protected CancellationToken _cancellationToken;
// cyclic timer for state machine call
private Timer _processCtrlTimer;
- private const Int32 ProcessTimerCycleMs = 250;
+ protected const Int32 ProcessTimerCycleMs = 250;
// defines for state machine
///
@@ -111,7 +117,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
/// - Always RegisterWatchService on,
/// - Always MinMaxCHeck on.
///
- public FinalProcessController(Int32 slot)
+ protected BaseProcessController(Int32 slot)
{
_processCtrlTimer = new Timer(TmrProcessCtrlCycle_Elapsed, null, ProcessTimerCycleMs, ProcessTimerCycleMs);
_slot = slot;
@@ -248,6 +254,20 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
OnProcessLogRequest?.Invoke(sender, e);
}
+ ///
+ /// Wrapper to fire this event from a derived class
+ ///
+ ///
+ ///
+ ///
+ /// - Initial
+ ///
+ protected void OverallProcessStateChanged(Object sender, ProcessStateArgs e)
+ {
+ // signal state change to logger
+ OnOverallProcessStateChanged?.Invoke(sender, e);
+ }
+
///
/// Handler to set a new state for the state machine regarding the entire final-process
///
@@ -282,272 +302,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
#endregion --------------------------------------- Events -----------------------------------------------------
#region ------------------------------------------ State Machine ----------------------------------------------
- ///
- /// State machine of FinalProcessController which serves as preparation for SHIPPING named EOL (End-Of-Line).
- ///
- ///
- /// INSTRUCTION FOR NORMAL USAGE:
- /// -----------------------------
- /// Call the SetupProcess() with the
- /// - new process automatically collected from 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 with new
- /// _actualProcessState as input,
- /// - successExitState being set to (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.
- ///
- ///
- ///
- /// - Initial
- ///
- 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 actual required process from the state definition list
- var process = _processStateDef?.GetProcessOfState(_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(1000);
- // 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(process, ProcessState.CheckNewMeter);
- 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.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:
- 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(process, ProcessState.Idle, startAlways: true);
- break;
-
- case ProcessState.StateListDelimiter:
- default:
- _stateMachineState = ProcessState.Idle;
- 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
+ public abstract void StateMachine();
#endregion --------------------------------------- State Machine ----------------------------------------------
#region ------------------------------------------ Tools ------------------------------------------------------
@@ -588,7 +344,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
///
/// - Handle genesis meter is null as new meter.
///
- private Boolean CheckForNewMeter()
+ protected Boolean CheckForNewMeter()
{
if (_genesisMeter == null ||
(!string.IsNullOrEmpty(_lastPcbId) &&
@@ -626,7 +382,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
///
/// - Leave skipped processes as is.
///
- private ProcessState RepeatFailedTests()
+ protected ProcessState RepeatFailedTests()
{
var state = ProcessState.Idle;
@@ -668,7 +424,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
///
/// - Kick off exec parametrization if check after reboot failed.
///
- private void ProcessErrorHandler(IProductionProcess process)
+ protected void ProcessErrorHandler(IProductionProcess process)
{
if (process == null)
return;
@@ -693,7 +449,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
///
/// Calculate the maximum of all processes expected to execute and signal to GUI.
///
- private void CalcAndPushMaxOverAllProcessSteps()
+ protected void CalcAndPushMaxOverAllProcessSteps()
{
if (_processStateDef == null)
return;
@@ -707,7 +463,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
///
/// Calculate all processes which are completed or skipped and signal to GUI.
///
- private void CalcAndPushOverAllProgress()
+ protected void CalcAndPushOverAllProgress()
{
if (Processes == null)
return;
@@ -736,7 +492,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
if (Processes == null)
Processes = new List();
if (_processStateDef == null)
- _processStateDef = new ProcessStateDef();
+ _processStateDef = new EolProcessStates();
// get all processes relevant for EOL from table which has to be sorted
var processes = _processStateDef.GetAllEolRelevantProcesses();
@@ -791,7 +547,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
///
/// - Changed setup process to start process being able to create a process list in advance.
///
- private void StartProcess(IProductionProcess process, ProcessState? successExitState,
+ protected void StartProcess(IProductionProcess process, ProcessState? successExitState,
ProcessState errorExitState = ProcessState.Error, ProcessState breakExitState = ProcessState.Stop,
IProductionProcess waitForSingleProcess = DoNotWaitForSingleProcessCompletion,
Boolean waitForAllProcesses = false, Boolean startAlways = false)
@@ -956,7 +712,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
///
/// - Cancellation token source.
///
- private void KillProcesses()
+ protected void KillProcesses()
{
_cancellationTokenSource?.Cancel();
Thread.Sleep(1000);
@@ -978,7 +734,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
///
/// - Initial
///
- private void AbortProcesses()
+ protected void AbortProcesses()
{
if (Processes == null || Processes.Count == 0)
return;
diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ConnectCordonel.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/CommonProcesses/ConnectCordonel.cs
similarity index 94%
rename from Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ConnectCordonel.cs
rename to Common/Production/ProductionUiCordonel/ProductionProcesses/CommonProcesses/ConnectCordonel.cs
index aa619be1..a0fac18a 100644
--- a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ConnectCordonel.cs
+++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/CommonProcesses/ConnectCordonel.cs
@@ -1,10 +1,9 @@
using System;
using Xylem.Common.CommonCore.Consts;
-using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.CommonProcesses;
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.CommonProcesses
{
public class ConnectCordonel : BaseProductionProcess
{
diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/GetSerialNumberAndRadioAddress.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/CommonProcesses/GetSerialNumberAndRadioAddress.cs
similarity index 99%
rename from Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/GetSerialNumberAndRadioAddress.cs
rename to Common/Production/ProductionUiCordonel/ProductionProcesses/CommonProcesses/GetSerialNumberAndRadioAddress.cs
index efe7c97d..562d993e 100644
--- a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/GetSerialNumberAndRadioAddress.cs
+++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/CommonProcesses/GetSerialNumberAndRadioAddress.cs
@@ -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.CommonProcesses
{
///
/// Request the serial number and radio address from DB.
diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckProductionState.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckProductionState.cs
index a5607356..72993b35 100644
--- a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckProductionState.cs
+++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckProductionState.cs
@@ -15,6 +15,7 @@ using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Logic.ProductionOrderCore.OrderData;
using Xylem.Common.Logic.ProductionOrderCore.TestResults;
using Xylem.Common.Production.ProductionUiCordonel.Model;
+using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.CommonProcesses;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/EolProcessController.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/EolProcessController.cs
new file mode 100644
index 00000000..58041c78
--- /dev/null
+++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/EolProcessController.cs
@@ -0,0 +1,314 @@
+using System;
+using System.Threading;
+using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
+using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EventArgs;
+
+namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
+{
+ ///
+ /// 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.
+ ///
+ public sealed class EolProcessController : BaseProcessController, IProcessController
+ {
+ #region ------------------------------------------ Variables --------------------------------------------------
+ //
+ #endregion --------------------------------------- Variables --------------------------------------------------
+ #region ------------------------------------------ Class ------------------------------------------------------
+
+ ///
+ /// Ctor
+ ///
+ /// physical slot for this meter
+ ///
+ /// - Initial
+ ///
+ public EolProcessController(Int32 slot) : base(slot)
+ {
+ }
+
+ #endregion --------------------------------------- Class ------------------------------------------------------
+ #region ------------------------------------------ Events -----------------------------------------------------
+ //
+ #endregion --------------------------------------- Events -----------------------------------------------------
+ #region ------------------------------------------ State Machine ----------------------------------------------
+ ///
+ /// State machine of FinalProcessController which serves as preparation for SHIPPING named EOL (End-Of-Line).
+ ///
+ ///
+ /// INSTRUCTION FOR NORMAL USAGE:
+ /// -----------------------------
+ /// Call the SetupProcess() with the
+ /// - new process automatically collected from 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 with new
+ /// _actualProcessState as input,
+ /// - successExitState being set to (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.
+ ///
+ ///
+ ///
+ /// - Initial
+ ///
+ public override 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
+ OverallProcessStateChanged(this, new ProcessStateArgs(_stateMachineState));
+
+ // get the actual required process from the state definition list
+ var process = _processStateDef?.GetProcessOfState(_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(1000);
+ // 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(process, ProcessState.CheckNewMeter);
+ 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.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:
+ 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(process, ProcessState.Idle, startAlways: true);
+ break;
+
+ case ProcessState.StateListDelimiter:
+ default:
+ _stateMachineState = ProcessState.Idle;
+ 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 ------------------------------------------------------
+ //
+ #endregion --------------------------------------- Tools ------------------------------------------------------
+ }
+}
diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/ProcessStateDef.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/EolProcessStates.cs
similarity index 98%
rename from Common/Production/ProductionUiCordonel/ProductionProcesses/ProcessStateDef.cs
rename to Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/EolProcessStates.cs
index 5cfecbdc..b7c8b4c9 100644
--- a/Common/Production/ProductionUiCordonel/ProductionProcesses/ProcessStateDef.cs
+++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/EolProcessStates.cs
@@ -2,11 +2,10 @@
using System.Collections.Generic;
using System.Linq;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.CommonProcesses;
-using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
-using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses;
+using Xylem.Common.Production.ProductionUiCordonel.Properties;
-namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
+namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
{
///
/// Concatenation for all production process states:
@@ -17,7 +16,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
///
/// - Initial
///
- public class ProcessStateDef
+ public class EolProcessStates
{
///
/// Collection of state information
@@ -31,7 +30,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
/// - Sort the active steps from first til last step as this is the order as those will be sorted in the
/// process list UI.
///
- public ProcessStateDef()
+ public EolProcessStates()
{
_stateContainer = new List
{
diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecPasswordFile.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecPasswordFile.cs
index 3d7f3bb4..11d6bcf6 100644
--- a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecPasswordFile.cs
+++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecPasswordFile.cs
@@ -66,7 +66,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
///
/// - Speed up to quick return if passwords are already set.
///
- /// ATTENTION: This requires to remove those on new PcbId
+ /// ATTENTION: This requires to remove those on new PcbId
/// for CheckMeterChange!
///
///
diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/GetFinalParametrization.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/GetFinalParametrization.cs
index 9f72441f..2a322f9a 100644
--- a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/GetFinalParametrization.cs
+++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/GetFinalParametrization.cs
@@ -39,7 +39,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
///
/// - Speed up to quick return if programming parameters are already set.
///
- /// ATTENTION: This requires to remove those on new PcbId
+ /// ATTENTION: This requires to remove those on new PcbId
/// for CheckMeterChange!
///
///
diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/IProcessController.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/IProcessController.cs
new file mode 100644
index 00000000..26615686
--- /dev/null
+++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/IProcessController.cs
@@ -0,0 +1,58 @@
+namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
+{
+ public interface IProcessController
+ {
+ ///
+ /// State machine of ProcessController which serves as template for all production processes.
+ ///
+ ///
+ /// INSTRUCTION FOR NORMAL USAGE:
+ /// -----------------------------
+ /// Call the SetupProcess() with the
+ /// - new process automatically collected from 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 with new
+ /// _actualProcessState as input,
+ /// - successExitState being set to (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.
+ ///
+ ///
+ ///
+ /// - Initial
+ ///
+ void StateMachine();
+ }
+}
diff --git a/Common/Production/ProductionUiCordonel/ProductionUiCordonel.csproj b/Common/Production/ProductionUiCordonel/ProductionUiCordonel.csproj
index 6f4ea12c..767a4554 100644
--- a/Common/Production/ProductionUiCordonel/ProductionUiCordonel.csproj
+++ b/Common/Production/ProductionUiCordonel/ProductionUiCordonel.csproj
@@ -291,7 +291,7 @@
-
+
@@ -299,7 +299,9 @@
-
+
+
+
@@ -325,12 +327,12 @@
-
+
-
+