ProductionUiCordonel: - re-organized to support multiple production processes part 3

This commit is contained in:
Thomas Wiedebusch
2025-04-14 15:39:22 +02:00
parent 83fba2f105
commit 8909602dac
18 changed files with 367 additions and 392 deletions
@@ -178,7 +178,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel
try
{
_processCtrl = new EolProcessController(Slot);
_processCtrl = new EolStateMachine(Slot);
}
catch (Exception ex)
{
@@ -178,7 +178,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel
try
{
_processCtrl = new EolProcessController(Slot);
_processCtrl = new EolStateMachine(Slot);
}
catch (Exception ex)
{
@@ -297,13 +297,200 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
/// </remarks>
private void TmrProcessCtrlCycle_Elapsed(Object state)
{
StateMachine();
GenericStateMachine();
}
#endregion --------------------------------------- Events -----------------------------------------------------
#region ------------------------------------------ State Machine ----------------------------------------------
public abstract void StateMachine();
/// <inheritdoc/>
public abstract void IndividualStateMachine(IProductionProcess process);
/// <summary>
/// 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 <see cref="ProcessStates.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="ProcessStates.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.
///
/// </summary>
/// <remarks date="2023-Jan-17" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void GenericStateMachine()
{// 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.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.RepeatFailedTests:
_stateMachineState = RepeatFailedTests();
break;
case ProcessState.PrintReturnReport:
_stateMachineState = ProcessState.Idle;
break;
case ProcessState.PrintSuccessReport:
_stateMachineState = ProcessState.Idle;
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(process, ProcessState.Idle, startAlways: true);
break;
case ProcessState.StateListDelimiter:
_stateMachineState = ProcessState.Idle;
break;
default:
IndividualStateMachine(process);
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 ------------------------------------------------------
@@ -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,17 +19,17 @@
/// </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,
TestFailed,
/// <summary>
/// User stopped process
/// </summary>
@@ -15,8 +15,8 @@ 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.ProductionProcesses.GenericProcesses;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
@@ -1,314 +0,0 @@
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
{
/// <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.
/// </summary>
public sealed class EolProcessController : BaseProcessController, IProcessController
{
#region ------------------------------------------ Variables --------------------------------------------------
//
#endregion --------------------------------------- Variables --------------------------------------------------
#region ------------------------------------------ Class ------------------------------------------------------
/// <summary>
/// Ctor
/// </summary>
/// <param name="slot">physical slot for this meter</param>
/// <remarks date="2025-Apr-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public EolProcessController(Int32 slot) : base(slot)
{
}
#endregion --------------------------------------- Class ------------------------------------------------------
#region ------------------------------------------ Events -----------------------------------------------------
//
#endregion --------------------------------------- Events -----------------------------------------------------
#region ------------------------------------------ State Machine ----------------------------------------------
/// <summary>
/// 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 <see cref="EolProcessStates.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="EolProcessStates.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.
///
/// </summary>
/// <remarks date="2023-Jan-17" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
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 ------------------------------------------------------
}
}
@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.CommonProcesses;
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
@@ -60,7 +60,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
new ProcessStateStruct(
null,
ProcessState.AbortFinalTest,
ProcessState.AbortTest,
Resources.StrStateSkipping,
false),
@@ -72,7 +72,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
new ProcessStateStruct(
null,
ProcessState.FinalTestFailed,
ProcessState.TestFailed,
Resources.StrStateFinalTestFailed,
false),
@@ -0,0 +1,148 @@
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
{
/// <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.
/// </summary>
public sealed class EolStateMachine : BaseProcessController, IProcessController
{
#region ------------------------------------------ Variables --------------------------------------------------
//
#endregion --------------------------------------- Variables --------------------------------------------------
#region ------------------------------------------ Class ------------------------------------------------------
/// <summary>
/// Ctor
/// </summary>
/// <param name="slot">physical slot for this meter</param>
/// <remarks date="2025-Apr-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public EolStateMachine(Int32 slot) : base(slot)
{
}
#endregion --------------------------------------- Class ------------------------------------------------------
#region ------------------------------------------ Events -----------------------------------------------------
//
#endregion --------------------------------------- Events -----------------------------------------------------
#region ------------------------------------------ State Machine ----------------------------------------------
/// <inheritdoc/>
/// <summary>
/// State machine of FinalProcessController which serves as preparation for SHIPPING named EOL (End-Of-Line).
/// </summary>
/// <remarks date="2023-Jan-17" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public override void IndividualStateMachine(IProductionProcess process)
{
switch (_stateMachineState)
{
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.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;
default:
_stateMachineState = ProcessState.Idle;
break;
}
}
#endregion --------------------------------------- State Machine ----------------------------------------------
#region ------------------------------------------ Tools ------------------------------------------------------
//
#endregion --------------------------------------- Tools ------------------------------------------------------
}
}
@@ -7,7 +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.CommonProcesses;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
@@ -4,7 +4,7 @@ using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.CommonProcesses
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses
{
public class CheckOrderNumber : BaseProductionProcess
{
@@ -4,7 +4,7 @@ using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.CommonProcesses
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.CommonProcesses
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.CommonProcesses
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses
{
/// <summary>
/// Production process detects the serial number of the Cordonel.
@@ -5,7 +5,7 @@ using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.CommonProcesses
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.CommonProcesses
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses
{
/// <summary>
/// Request the serial number and radio address from DB.
@@ -1,58 +1,12 @@
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
{
public interface IProcessController
{
/// <summary>
/// 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 <see cref="ProcessStates.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="ProcessStates.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.
///
/// Individual states to fill the state machine.
/// ATTENTION: All states have to be part of the <see cref="ProcessState"/>
/// </summary>
/// <remarks date="2023-Jan-17" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
void StateMachine();
void IndividualStateMachine(IProductionProcess process);
}
}
@@ -289,10 +289,10 @@
<Compile Include="Model\EOLHandler.cs" />
<Compile Include="ProductionProcesses\Enum\PickingProcessState.cs" />
<Compile Include="ProductionProcesses\EolProcesses\VisualInspection.cs" />
<Compile Include="ProductionProcesses\CommonProcesses\ErrorHandler.cs" />
<Compile Include="ProductionProcesses\GenericProcesses\ErrorHandler.cs" />
<Compile Include="ProductionProcesses\EolProcesses\ExecStoreConfiguration.cs" />
<Compile Include="ProductionProcesses\CommonProcesses\GetSerialNumberAndRadioAddress.cs" />
<Compile Include="ProductionProcesses\CommonProcesses\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" />
@@ -300,7 +300,7 @@
<Compile Include="ProductionProcesses\Enum\ProcessState.cs" />
<Compile Include="ProductionProcesses\EventArgs\SingleProcessStateArgs.cs" />
<Compile Include="ProductionProcesses\EolProcesses\EolProcessStates.cs" />
<Compile Include="ProductionProcesses\EolProcesses\EolProcessController.cs" />
<Compile Include="ProductionProcesses\EolProcesses\EolStateMachine.cs" />
<Compile Include="ProductionProcesses\IProcessController.cs" />
<Compile Include="ProductionProcesses\ProcessStateStruct.cs" />
<Compile Include="Data\PickingResultData.cs" />
@@ -319,15 +319,15 @@
<DependentUpon>MultiModeChild.xaml</DependentUpon>
</Compile>
<Compile Include="Model\IMultiModeChildViewModel.cs" />
<Compile Include="ProductionProcesses\CommonProcesses\CheckSerialNumber.cs" />
<Compile Include="ProductionProcesses\CommonProcesses\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\CommonProcesses\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" />