ProductionUiCordonel: - cancellation token

This commit is contained in:
Thomas Wiedebusch
2025-04-07 15:52:52 +02:00
parent 4296920966
commit c3d6f3c18d
20 changed files with 233 additions and 97 deletions
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using FluentAssertions;
using LaaPackages.Features.Cordonel.Models;
using Logic.ProductionToProductMapper.Files.Fw;
@@ -524,7 +525,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
var meterSizeForParams = "?";
var unmodifiedParamsUsableAsReferenceForNextTest = false;
var intermediateRecoveryRegisters = new List<RecoveryRegisterItem>();
foreach (var testCase in _testCases)
{
// Prepare one test result for an entire sequence to test
@@ -622,8 +623,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
testPp = new ExecFinalParametrization("UnitTestExecFinalParametrization");
testPp.RegisterRestorer = testCase.RegisterRestorer;
CommonCallClassCheck<ExecFinalParametrization>(testPp, testCase,
ref testResult);
CommonCallClassCheck<ExecFinalParametrization>(testPp, testCase, ref testResult);
break;
case "ExecStoreConfiguration":
@@ -632,8 +632,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
{
testCase.RequiredEOLProgress.StoreConfigurationDoneChecked = EOLStatus.OK;
testPp = new ExecStoreConfiguration("UnitTestExecStoreConfiguration");
CommonCallClassCheck<ExecStoreConfiguration>(testPp, testCase,
ref testResult);
CommonCallClassCheck<ExecStoreConfiguration>(testPp, testCase, ref testResult);
}
else
{
@@ -679,10 +678,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
testCase.RequiredEOLProgress.ParametrizationCompareChecked =
paramsCheckWillSucceed ? EOLStatus.OK : EOLStatus.FAIL;
testPp = new CheckFinalParametrization(
"UnitTestCheckFinalParametrization");
CommonCallClassCheck<CheckFinalParametrization>(testPp, testCase,
ref testResult);
testPp = new CheckFinalParametrization("UnitTestCheckFinalParametrization");
CommonCallClassCheck<CheckFinalParametrization>(testPp, testCase, ref testResult);
break;
}
}
@@ -393,7 +393,16 @@ namespace Xylem.Common.Production.ProductionUiCordonel
// Stop all procedures
if (e.State == ProcessState.Stop)
{
BtnStop_Click(this, null);
_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)
@@ -849,18 +858,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel
/// <param name="e"></param>
private void BtnStop_Click(Object sender, RoutedEventArgs e)
{
_timer?.Stop();
UiElmEnable(btnStart, true);
UiElmEnable(btnStop, false);
//set the process state to the state machine
_processCtrl?.SetProcessState(ProcessState.Stop);
// 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);
}
/// <summary>
@@ -154,6 +154,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
// assembly version information
private static readonly Version _version = Assembly.GetExecutingAssembly().GetName().Version;
/// <inheritdoc/>
public CancellationToken CancellationToken { set; get; }
/// <inheritdoc/>
public event EventHandler<SingleProcessStateArgs> OnSingleProcessStateChanged;
@@ -248,6 +251,18 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
return retVal;
}
/// <summary>
/// Action for cancellation token to abort process
/// </summary>
/// <remarks date="2025-Apr-07" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
protected StatusReturn CancellationProcedure()
{
AbortProcess();
return StatusReturn.Failed;
}
/// <summary>
/// Set new single process state and fire event from base class:
/// - First set new SingleProcessState,
@@ -298,7 +313,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
var processExec = new ProcessExec();
processExec.NewProcess(InitProcess, PreExecuteProcess, ExecuteProcess, FinalizeProcess,
_cultureInfo, exitProcessStateObjects);
_cultureInfo, exitProcessStateObjects, CancellationToken);
}
/// <inheritdoc cref="IProductionProcess"/>
@@ -46,6 +46,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2025-Apr-03" author="Thomas Wiedebusch">
/// - Moved setting of time and reset counters from here to <see cref="ExecRebootCordonel"/>.
/// </remarks>
/// <remarks date="2025-Apr-07" author="Thomas Wiedebusch">
/// - CancellationToken.
/// </remarks>
public override StatusReturn ExecuteProcess()
{
// Uncheck executed process to leave the result open needed for retries
@@ -76,38 +79,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
}
}
//// Get the time in UTC to program the time for the Cordonel in seconds since 01. Jan 2000
//// to set the new UTC after reboot
//var teaTime = new TimeT();
//// Build byte array
//var rawSeconds = RegisterConverter.ValueToByteArray(teaTime.UtcNowToSecondsSince2000);
//var timerSetup = new ProgrammingParameters("SYSTEM_CalendarSeconds", rawSeconds,
// ProgrammingSource.ProgramInternal);
//// This is the manufacturing date of the Cordonel
//var irdaMfgDate = new ProgrammingParameters("IRDA_MfgDate", rawSeconds,
// ProgrammingSource.ProgramInternal);
//// Reset reboot counter
//var nullRebootCtr = new ProgrammingParameters("CUSTOMER_RebootCount", new Byte[] { 0x00 },
// ProgrammingSource.ProgramInternal);
//var retValBol = WriteRegisterLogAndProcessCtr(timerSetup);
//retValBol &= WriteRegisterLogAndProcessCtr(irdaMfgDate);
//retValBol &= WriteRegisterLogAndProcessCtr(nullRebootCtr);
//if (Meter.RadioFrequencyMhz != null)
//{
// // Reset periodic log reset counter
// var periodicLogResetCtr = new ProgrammingParameters("PERIODICLOG_ResetCounter", new Byte[] { 0x00 },
// ProgrammingSource.ProgramInternal);
// retValBol &= WriteRegisterLogAndProcessCtr(periodicLogResetCtr);
// // Reset sensus radio reset counter
// var sensusRadioResetCtr = new ProgrammingParameters("SENSUSRADIO_ResetCounter", new Byte[] { 0x00 },
// ProgrammingSource.ProgramInternal);
// retValBol &= WriteRegisterLogAndProcessCtr(sensusRadioResetCtr);
//}
// Execute final read after reboot
RegisterRestorer.OnProcessUpdate += ProcessUpdate_Handler;
var retValBol = RegisterRestorer.FinalReadRegisters();
retValBol &= RegisterRestorer.CompareRegisters();
var retValBol = RegisterRestorer.FinalReadRegisters(CancellationToken);
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
retValBol &= RegisterRestorer.CompareRegisters(CancellationToken);
RegisterRestorer.OnProcessUpdate -= ProcessUpdate_Handler;
if (retValBol)
@@ -76,6 +76,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2024-Nov-08" author="Thomas Wiedebusch">
/// - Extracted from <see cref="ExecuteProcess"/> for better testability.
/// </remarks>
/// <remarks date="2025-Apr-06" author="Thomas Wiedebusch">
/// - CancellationToken.
/// </remarks>
public override StatusReturn PreExecuteProcess()
{
if (Meter == null || string.IsNullOrEmpty(Meter.PcbId) || Meter.PcbId.Length < GenesisMeter.MinPcbIdLength)
@@ -95,6 +98,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
return StatusReturn.Failed;
}
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
ActualProcessProgress++;
// Get the requirement (Standard or special)
@@ -106,6 +113,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
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
@@ -123,6 +134,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
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
@@ -135,6 +150,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
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
@@ -145,6 +164,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
return StatusReturn.Failed;
}
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
ActualProcessProgress++;
// Check if requirement will force skipping of calibration
@@ -159,6 +182,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
return StatusReturn.Failed;
}
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
ActualProcessProgress++;
// Get the pre- and final flow test state.
@@ -171,6 +198,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
return StatusReturn.Failed;
}
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
ActualProcessProgress++;
// Fill the results to calibration check register restorer which will be used to read the actual
@@ -200,10 +231,14 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
return StatusReturn.Failed;
}
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
// Activate logging to output
CalibCheckRegisterRestorer.OnProcessUpdate += ProcessUpdate_Handler;
// Read out all parameters from meter and compare it
if (!CalibCheckRegisterRestorer.ReadPresetRegisters())
if (!CalibCheckRegisterRestorer.ReadPresetRegisters(CancellationToken))
{
// Stop logging
CalibCheckRegisterRestorer.OnProcessUpdate -= ProcessUpdate_Handler;
@@ -1220,7 +1255,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
CalibCheckRegisterRestorer.OnProcessUpdate += ProcessUpdate_Handler;
// Compare the database value and the meter value (read in advance in the PreExecuteProcess)
if (!CalibCheckRegisterRestorer.CompareRegisters())
if (!CalibCheckRegisterRestorer.CompareRegisters(CancellationToken))
{
// Stop logging
CalibCheckRegisterRestorer.OnProcessUpdate -= ProcessUpdate_Handler;
@@ -173,6 +173,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2025-Feb-20" author="Thomas Wiedebusch">
/// - Separate TestInit and SirtInit (only if test shouldn't be skipped).
/// </remarks>
/// <remarks date="2025-Apr-07" author="Thomas Wiedebusch">
/// - CancellationToken.
/// </remarks>
public override StatusReturn ExecuteProcess()
{
// This will force to reset all values, Sentinel and Prepare shipping will fail based on EOL Progress
@@ -278,12 +281,20 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
ClearRssiMeasurement();
// kick off RSSI test
StartRssiMeasurement();
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
CancellationProcedure();
var exitTime = DateTime.Now + TimeSpan.FromSeconds(Timeout_s);
var actualTime = DateTime.Now;
// wait on result or timeout or stop request from main window
while (!_testFinished && exitTime > actualTime && SingleProcessState != SingleProcessState.Idle)
{
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
CancellationProcedure();
Thread.Sleep(TimeoutLoop_ms);
actualTime = DateTime.Now;
ActualProcessProgress++;
@@ -97,7 +97,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
}
RegisterRestorer.OnProcessUpdate += ProcessUpdate_Handler;
var retVal = RegisterRestorer.EolProgrammingRegisters(RegisterRestorer.RecoveryRegisters);
var retVal = RegisterRestorer.EolProgrammingRegisters(RegisterRestorer.RecoveryRegisters,
CancellationToken);
RegisterRestorer.OnProcessUpdate -= ProcessUpdate_Handler;
EolProgress.ParametrizationDoneChecked = retVal == StatusReturn.Okay ? EOLStatus.OK : EOLStatus.FAIL;
@@ -83,6 +83,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2024-Sep-05" author="Thomas Wiedebusch">
/// - EOL progress signaled.
/// </remarks>
/// <remarks date="2025-Apr-07" author="Thomas Wiedebusch">
/// - CancellationToken.
/// </remarks>
public override StatusReturn ExecuteProcess()
{
// Uncheck executed process to leave the result open needed for retries
@@ -131,6 +134,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
retValBol = MeterPwdHandlerDb.RequestPwdFileFromDb(Meter.PcbId, out var pwdContainer);
ActualProcessProgress++;
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
if (!retValBol)
{
ucPwd.UpdateImage(ucPwd.imgRequestPassword, ImgState.Error);
@@ -76,7 +76,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
};
_meterResetPsu.OnProcessUpdate += ProcessUpdate_Handler;
var rebootSucceeded = _meterResetPsu.MeterResetPsuExecute();
var rebootSucceeded = _meterResetPsu.MeterResetPsuExecute(CancellationToken);
_meterResetPsu.OnProcessUpdate -= ProcessUpdate_Handler;
if (!rebootSucceeded)
@@ -57,6 +57,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2025-Jan-21" author="Thomas Wiedebusch">
/// - Output error message from register restorer..
/// </remarks>
/// <remarks date="2025-Apr-07" author="Thomas Wiedebusch">
/// - CancellationToken.
/// </remarks>
public override StatusReturn ExecuteProcess()
{
SignalRunningState();
@@ -99,6 +102,11 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
if (!retVal)
return StatusReturn.Failed;
}
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
RegisterRestorer.RecoveryRegisters = new List<RecoveryRegisterItem>();
@@ -38,6 +38,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2024-Mar-13" author="Thomas Wiedebusch">
/// - Get customer serial number for display.
/// </remarks>
/// <remarks date="2025-Apr-07" author="Thomas Wiedebusch">
/// - CancellationToken.
/// </remarks>
public override StatusReturn ExecuteProcess()
{
if (Meter == null)
@@ -82,6 +85,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
if (!retVal) return StatusReturn.Failed;
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
retVal &= OrderDetailsDb.GetCordonelOrderDetails(Meter.OrderNumber, out var orderDetails);
if (!retVal)
@@ -53,6 +53,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2024-Aug-30" author="Thomas Wiedebusch">
/// - Redesigned.
/// </remarks>
/// <remarks date="2025-Apr-07" author="Thomas Wiedebusch">
/// - CancellationToken.
/// </remarks>
public override StatusReturn ExecuteProcess()
{
try
@@ -98,12 +101,17 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
{
eolTriggerReturn = EOLHandler.TriggerSentinel(Meter.PcbId, out feedback, out error);
dataBaseCallIsFinished = true;
});
}, CancellationToken);
// Keep progress bar alive
while (!dataBaseCallIsFinished)
{
Thread.Sleep(TimeoutLoop_ms);
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
ActualProcessProgress++;
}
@@ -80,7 +80,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
// progress bar and screen info of ongoing processes
public event EventHandler<ProcessProgressArgs> OnProcessProgressChanged;
private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource();
// cancellation token
private CancellationTokenSource _cancellationTokenSource;
private CancellationToken _cancellationToken;
// cyclic timer for state machine call
private Timer _processCtrlTimer;
@@ -113,6 +115,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
{
_processCtrlTimer = new Timer(TmrProcessCtrlCycle_Elapsed, null, ProcessTimerCycleMs, ProcessTimerCycleMs);
_slot = slot;
_cancellationTokenSource = new CancellationTokenSource();
_cancellationToken = _cancellationTokenSource.Token;
InitNewMeter();
InitProcesses();
// program start with idle process
@@ -355,10 +359,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
switch (_stateMachineState)
{
case ProcessState.Idle:
// This state need an external state change like user input
// This state needs an external state change like user input to go ahead
break;
case ProcessState.DetectCordonel:
// 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();
@@ -522,9 +531,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
break;
}//
}
catch (ThreadAbortException)
catch (ThreadAbortException ex)
{
throw;
_genesisMeter?.WriteLog(ex.Message);
_stateMachineState = ProcessState.Idle;
}
catch (Exception ex)
{
@@ -835,6 +845,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
process.ProductionStatus = _productionStatus;
process.ProductionRequirements = _cordonelRequirements;
process.EolProgress = _cordonelEolProgress;
process.CancellationToken = _cancellationToken;
// kick off process execution, the breakExitState will not be used
process.StartProcess(successExitState, errorExitState, breakExitState);
@@ -935,6 +946,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
/// </remarks>
private void KillProcesses()
{
_cancellationTokenSource?.Cancel();
Thread.Sleep(1000);
if (Processes == null || Processes.Count == 0)
return;
@@ -942,8 +956,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
foreach (var pp in Processes)
{
pp.IdleProcess();
if (_registerRestorer != null)
_registerRestorer.StopRegisterAccess = true;
}
}
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Controls;
using LaaPackages.Features.Cordonel.Models;
using Xylem.Common.CommonCore.Consts;
@@ -13,6 +14,11 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
{
public interface IProductionProcess
{
/// <summary>
/// Cancellation token to stop operation
/// </summary>
CancellationToken CancellationToken { set; get; }
/// <summary>
/// Event for process list change or state of single process step change from wait to execute to done or error
/// </summary>