diff --git a/Common/Common.sln.DotSettings b/Common/Common.sln.DotSettings index 25cd6bf6..bf277268 100644 --- a/Common/Common.sln.DotSettings +++ b/Common/Common.sln.DotSettings @@ -52,6 +52,7 @@ True True True + True True True True @@ -62,6 +63,7 @@ True True True + True True True True diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisCore/RegisterRestorer.cs b/Common/Hardware/WaterMeter/Genesis/GenesisCore/RegisterRestorer.cs index 6f40126d..f7137e89 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisCore/RegisterRestorer.cs +++ b/Common/Hardware/WaterMeter/Genesis/GenesisCore/RegisterRestorer.cs @@ -184,11 +184,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore /// private Int32 _actualRegisterCtr; - /// - /// Stop register access - /// - public Boolean StopRegisterAccess; - /// /// Genesis meter object /// @@ -575,6 +570,8 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore List recoveryRegistersDb, out String errorMsg) { errorMsg = ""; + if (string.IsNullOrEmpty(pcbId)) + return StatusReturn.Failed; try { var url = $"{ServiceUrls.GenesisFinalCheckServiceUrl()}GetProgrammingParameters?PcbID={pcbId}"; @@ -622,14 +619,18 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore /// /// list of registers to update from database without special sequence /// + /// /// true if setup succeeded /// /// - Initial. /// - public StatusReturn EolProgrammingRegisters(List recoveryRegistersDb) + /// + /// - CancellationToken. + /// + public StatusReturn EolProgrammingRegisters(List recoveryRegistersDb, CancellationToken cancellationToken) { // mark EOL as true to force accu reset - return WriteRegisters(recoveryRegistersDb, true); + return WriteRegisters(recoveryRegistersDb, cancellationToken, true); } /// @@ -670,6 +671,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore /// /// list of registers to update from database without special sequence /// + /// /// EOL (end of line) programming with accu reset /// true if restoring succeeded /// @@ -724,7 +726,11 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore /// /// - Exported write routines to . /// - public StatusReturn WriteRegisters(List recoveryRegistersDb, Boolean eolProgramming = false) + /// + /// - CancellationToken. + /// + public StatusReturn WriteRegisters(List recoveryRegistersDb, CancellationToken cancellationToken, + Boolean eolProgramming = false) { if (_currentGenesis == null || string.IsNullOrEmpty(_currentGenesis.PcbId) || @@ -742,7 +748,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore RecoveryRegisters = recoveryRegistersDb; } - StopRegisterAccess = false; _actualRegisterCtr = 0; // Build string list for debug @@ -789,7 +794,8 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(Resources.StrRegisterRecoveryPreparation, actualProcessMessage: Resources.StrRegisterRecoveryPreparation)); - if (StatusReturn.Failed == WriteRegisterParameterSet(_prepareProgramming, recRegistersCount, meterRegisters)) + if (StatusReturn.Failed == WriteRegisterParameterSet(_prepareProgramming, recRegistersCount, meterRegisters, + cancellationToken)) { // the logout will force the radio app to activate the settings _currentGenesis?.Logout(); @@ -800,7 +806,8 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(Resources.StrRegisterRecoveryExec, actualProcessMessage: Resources.StrRegisterRecoveryExec)); - if (StatusReturn.Failed == WriteRegisterParameterSet(recoveryRegistersDb, recRegistersCount, meterRegisters)) + if (StatusReturn.Failed == WriteRegisterParameterSet(recoveryRegistersDb, recRegistersCount, meterRegisters, + cancellationToken)) { // the logout will force the radio app to activate the settings _currentGenesis?.Logout(); @@ -811,7 +818,8 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(Resources.StrRegisterRecoveryFinalization, actualProcessMessage: Resources.StrRegisterRecoveryFinalization)); - if (StatusReturn.Failed == WriteRegisterParameterSet(finalizeProgrammingAll, recRegistersCount, meterRegisters)) + if (StatusReturn.Failed == WriteRegisterParameterSet(finalizeProgrammingAll, recRegistersCount, meterRegisters, + cancellationToken)) { // the logout will force the radio app to activate the settings _currentGenesis?.Logout(); @@ -837,12 +845,16 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore /// /// /// + /// /// /// /// - Initial imported from . /// + /// + /// - CancellationToken. + /// private StatusReturn WriteRegisterParameterSet(List recoveryRegistersDb, Int32 recRegistersCount, - MeterRegisters meterRegisters) + MeterRegisters meterRegisters, CancellationToken cancellationToken) { foreach (var recReg in recoveryRegistersDb) { @@ -915,7 +927,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore overallProcessCtrPercent, actualProcessMsg)); } - if (StopRegisterAccess) + if (cancellationToken.IsCancellationRequested) break; } @@ -928,6 +940,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore /// - The _restoreRegisters has to be filled with all registers being able to restore (RW), /// - The _finalReadRegisters has to be filled. /// + /// /// true if equal /// /// - Initial. @@ -965,13 +978,15 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore /// /// - Hash password and encryption key with SHA256. /// - public Boolean CompareRegisters() + /// + /// - CancellationToken. + /// + public Boolean CompareRegisters(CancellationToken cancellationToken) { if (_pcbIdFinalRegisterRead != _pcbIdWriteRegisters || RecoveryRegisters == null) return false; - StopRegisterAccess = false; _actualRegisterCtr = 0; var retVal = true; @@ -1132,7 +1147,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore } } - if (StopRegisterAccess) + if (cancellationToken.IsCancellationRequested) break; } catch (Exception ex) @@ -1147,6 +1162,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore /// Read the registers after the update for comparision. Build the post update registers here, /// because an update may have changed the registers (new or removed registers). /// + /// /// true if registers could be read /// /// - Initial. @@ -1176,7 +1192,10 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore /// /// - Check readBack value for null. /// - public Boolean FinalReadRegisters() + /// + /// - CancellationToken. + /// + public Boolean FinalReadRegisters(CancellationToken cancellationToken) { if (_currentGenesis == null) return false; @@ -1199,7 +1218,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore _finalRegisterList.Sort(); // executes login read logout - retVal = ReadRegistersSet(ref _finalRegisters); + retVal = ReadRegistersSet(ref _finalRegisters, cancellationToken); // backup read data to RecoveryRegisters if (RecoveryRegisters != null && _finalRegisters != null) @@ -1241,6 +1260,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore /// The defined registers are based on the "configuration.json" and the installed application with /// a specific version. /// + /// /// true if registers could be read /// /// - Initial. @@ -1265,7 +1285,10 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore /// /// - Register list changed from RegisterDefinition to RecoveryRegisterItem list. /// - public Boolean InitialReadRegisters() + /// + /// - CancellationToken. + /// + public Boolean InitialReadRegisters(CancellationToken cancellationToken) { if (_currentGenesis == null) return false; @@ -1291,7 +1314,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore _initialRegisterList = _initialRegisters.Select(register => register.RegisterIdent).ToList(); _initialRegisterList.Sort(); - return ReadRegistersSet(ref _initialRegisters); + return ReadRegistersSet(ref _initialRegisters, cancellationToken); } /// @@ -1346,6 +1369,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore /// The defined registers are based on the "configuration.json" and the installed application with a specific /// version. /// + /// /// true if registers could be read /// /// - Initial. @@ -1353,7 +1377,10 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore /// /// - Register list changed from RegisterDefinition to RecoveryRegisterItem list. /// - public Boolean ReadRegisters() + /// + /// - CancellationToken. + /// + public Boolean ReadRegisters(CancellationToken cancellationToken) { if (_currentGenesis == null) return false; @@ -1368,17 +1395,21 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs($@"{Resources.StrReadRegister}", 0, Resources.StrReadRegistersBeforeUpdate)); - return ReadRegistersSet(ref registers); + return ReadRegistersSet(ref registers, cancellationToken); } /// /// Read predefined registers from RecoveryRegisters. /// + /// /// true if registers could be read /// /// - Initial. /// - public Boolean ReadPresetRegisters() + /// + /// - CancellationToken. + /// + public Boolean ReadPresetRegisters(CancellationToken cancellationToken) { if (_currentGenesis == null || RecoveryRegisters == null || RecoveryRegisters.Count == 0) return false; @@ -1389,7 +1420,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore _pcbIdWriteRegisters = _currentGenesis.PcbId; _pcbIdFinalRegisterRead = _currentGenesis.PcbId; - return ReadRegistersSet(ref RecoveryRegisters); + return ReadRegistersSet(ref RecoveryRegisters, cancellationToken); } /// @@ -1401,6 +1432,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore /// Executes login, read cycle and logout. /// /// registers to read + /// /// true if registers could be read /// /// - Initial. @@ -1430,11 +1462,13 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore /// /// - Register list changed from RegisterDefinition to RecoveryRegisterItem list. /// - private Boolean ReadRegistersSet(ref List registers) + /// + /// - CancellationToken. + /// + private Boolean ReadRegistersSet(ref List registers, CancellationToken cancellationToken) { if (_currentGenesis == null) return false; - StopRegisterAccess = false; _actualRegisterCtr = 0; // read values from Genesis @@ -1503,7 +1537,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore overallProcessCtrPercent, actualProcessMsg)); } - if (StopRegisterAccess) + if (cancellationToken.IsCancellationRequested) break; } catch (Exception ex) diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisFile/GenesisFile.csproj b/Common/Hardware/WaterMeter/Genesis/GenesisFile/GenesisFile.csproj index aefdb732..4502e737 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisFile/GenesisFile.csproj +++ b/Common/Hardware/WaterMeter/Genesis/GenesisFile/GenesisFile.csproj @@ -211,6 +211,7 @@ PublicResXFileCodeGenerator Resources.de.Designer.cs + Designer PublicResXFileCodeGenerator diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterResetPsu.cs b/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterResetPsu.cs index 3597e876..2a29ebca 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterResetPsu.cs +++ b/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterResetPsu.cs @@ -259,6 +259,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// Update meter lookup table /// + /// /// /// /// - Initial @@ -269,7 +270,10 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// - Check reboot counter to verify reboot being done. /// - public Boolean MeterResetPsuExecute() + /// + /// - CancellationToken. + /// + public Boolean MeterResetPsuExecute(CancellationToken cancellationToken = default(CancellationToken)) { var statusReturn = _genesisMeter.RebootGenesis(); // Status return "Unknown" is from the real Genesis @@ -291,6 +295,8 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile _genesisMeter.ReadRegister(registerName)); while (_meterResetPsuState != MeterResetPsuState.Idle && !StopUpdateProcess) { + if (cancellationToken.IsCancellationRequested) + StopUpdateProcess = true; MeterResetPsuStateMachine(); } var rebootCountAfterReboot = RegisterConverter.ByteArrayToValue( diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.de.resx b/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.de.resx index bea1a4f8..fbdf1424 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.de.resx +++ b/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.de.resx @@ -130,7 +130,7 @@ Sende Aktualisierungsdateien - Sende Initialisierungsdatei für Aktualisierung + Sende Neustartkonfigurationsdatei Lösche Aktualisierungsdatei diff --git a/Common/Production/ProductionUiCordonel.UnitTests/Steps/CommonProductionProcessSteps.cs b/Common/Production/ProductionUiCordonel.UnitTests/Steps/CommonProductionProcessSteps.cs index d896dffe..6edb24f2 100644 --- a/Common/Production/ProductionUiCordonel.UnitTests/Steps/CommonProductionProcessSteps.cs +++ b/Common/Production/ProductionUiCordonel.UnitTests/Steps/CommonProductionProcessSteps.cs @@ -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(); - + 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(testPp, testCase, - ref testResult); + CommonCallClassCheck(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(testPp, testCase, - ref testResult); + CommonCallClassCheck(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(testPp, testCase, - ref testResult); + testPp = new CheckFinalParametrization("UnitTestCheckFinalParametrization"); + CommonCallClassCheck(testPp, testCase, ref testResult); break; } } diff --git a/Common/Production/ProductionUiCordonel/FinalTest.xaml.cs b/Common/Production/ProductionUiCordonel/FinalTest.xaml.cs index 1eae9842..ed4519f2 100644 --- a/Common/Production/ProductionUiCordonel/FinalTest.xaml.cs +++ b/Common/Production/ProductionUiCordonel/FinalTest.xaml.cs @@ -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 /// 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); } /// diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/BaseProductionProcess.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/BaseProductionProcess.cs index b8b74ff5..09391fba 100644 --- a/Common/Production/ProductionUiCordonel/ProductionProcesses/BaseProductionProcess.cs +++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/BaseProductionProcess.cs @@ -154,6 +154,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses // assembly version information private static readonly Version _version = Assembly.GetExecutingAssembly().GetName().Version; + /// + public CancellationToken CancellationToken { set; get; } + /// public event EventHandler OnSingleProcessStateChanged; @@ -248,6 +251,18 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses return retVal; } + /// + /// Action for cancellation token to abort process + /// + /// + /// - Initial + /// + protected StatusReturn CancellationProcedure() + { + AbortProcess(); + return StatusReturn.Failed; + } + /// /// 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); } /// diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckFinalParametrization.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckFinalParametrization.cs index 0b4a9e2d..e3a08504 100644 --- a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckFinalParametrization.cs +++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckFinalParametrization.cs @@ -46,6 +46,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr /// /// - Moved setting of time and reset counters from here to . /// + /// + /// - CancellationToken. + /// 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) diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckProductionState.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckProductionState.cs index a526bfaa..d7979e17 100644 --- a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckProductionState.cs +++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckProductionState.cs @@ -76,6 +76,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr /// /// - Extracted from for better testability. /// + /// + /// - CancellationToken. + /// 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; diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckRadio.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckRadio.cs index 3d9f68fe..09ebe34b 100644 --- a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckRadio.cs +++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/CheckRadio.cs @@ -173,6 +173,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr /// /// - Separate TestInit and SirtInit (only if test shouldn't be skipped). /// + /// + /// - CancellationToken. + /// 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++; diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecFinalParametrization.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecFinalParametrization.cs index f5ee63d0..b17d558d 100644 --- a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecFinalParametrization.cs +++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecFinalParametrization.cs @@ -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; diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecPasswordFile.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecPasswordFile.cs index e46b0fa8..e91e74fb 100644 --- a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecPasswordFile.cs +++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecPasswordFile.cs @@ -83,6 +83,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr /// /// - EOL progress signaled. /// + /// + /// - CancellationToken. + /// 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); diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecRebootCordonel.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecRebootCordonel.cs index 538c5d2f..2b071f93 100644 --- a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecRebootCordonel.cs +++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/ExecRebootCordonel.cs @@ -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) diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/GetFinalParametrization.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/GetFinalParametrization.cs index 59d8bd64..51f82cd0 100644 --- a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/GetFinalParametrization.cs +++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/GetFinalParametrization.cs @@ -57,6 +57,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr /// /// - Output error message from register restorer.. /// + /// + /// - CancellationToken. + /// 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(); diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/GetSerialNumberAndRadioAddress.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/GetSerialNumberAndRadioAddress.cs index 5c6b9768..efe7c97d 100644 --- a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/GetSerialNumberAndRadioAddress.cs +++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/GetSerialNumberAndRadioAddress.cs @@ -38,6 +38,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr /// /// - Get customer serial number for display. /// + /// + /// - CancellationToken. + /// 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) diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/PrepareShipping.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/PrepareShipping.cs index 92364db8..cac3474e 100644 --- a/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/PrepareShipping.cs +++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/EolProcesses/PrepareShipping.cs @@ -53,6 +53,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr /// /// - Redesigned. /// + /// + /// - CancellationToken. + /// 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++; } diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/FinalProcessController.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/FinalProcessController.cs index 4e9203b9..11d6a882 100644 --- a/Common/Production/ProductionUiCordonel/ProductionProcesses/FinalProcessController.cs +++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/FinalProcessController.cs @@ -80,7 +80,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses // progress bar and screen info of ongoing processes public event EventHandler 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 /// 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; } } diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/IProductionProcess.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/IProductionProcess.cs index 08fd6e4c..cf8c9e31 100644 --- a/Common/Production/ProductionUiCordonel/ProductionProcesses/IProductionProcess.cs +++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/IProductionProcess.cs @@ -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 { + /// + /// Cancellation token to stop operation + /// + CancellationToken CancellationToken { set; get; } + /// /// Event for process list change or state of single process step change from wait to execute to done or error /// diff --git a/Common/Utils/ProcessExec/ProcessExec.cs b/Common/Utils/ProcessExec/ProcessExec.cs index 82e67300..70bff0ea 100644 --- a/Common/Utils/ProcessExec/ProcessExec.cs +++ b/Common/Utils/ProcessExec/ProcessExec.cs @@ -48,16 +48,20 @@ namespace Xylem.Common.Utils.ProcessExec /// finalizing function /// language selection passing /// exit objects + /// /// /// - Initial. /// - /// + /// /// - Function for pre-execution before the main execution starts being executed in the new /// thread. /// + /// + /// - CancellationToken. + /// public void NewProcess(VoidCallbackFunction initFn, StatusReturnCallbackFunction preExecFn, StatusReturnCallbackFunction execFn, ExitStateCallbackFunction finalFn, - CultureInfo cultureInfo, Object[] exitObjects) + CultureInfo cultureInfo, Object[] exitObjects, CancellationToken cancellationToken) { _exitObjects = new Object[exitObjects.Length]; _exitObjects = exitObjects; @@ -72,15 +76,17 @@ namespace Xylem.Common.Utils.ProcessExec Thread.CurrentThread.CurrentUICulture = cultureInfo; Thread.CurrentThread.CurrentCulture = cultureInfo; - if (preExecFn != null) + if (preExecFn != null && !cancellationToken.IsCancellationRequested) success = (StatusReturn)preExecFn.DynamicInvoke(); - if (execFn == null || success != StatusReturn.Okay) + if (execFn == null || success != StatusReturn.Okay || cancellationToken.IsCancellationRequested) return; success = (StatusReturn)execFn.DynamicInvoke(); - }).ContinueWith(delegate { finalFn?.Invoke(success, _exitObjects); }); + }, cancellationToken) + .ContinueWith(delegate { finalFn?.Invoke(success, _exitObjects); }, + TaskContinuationOptions.NotOnCanceled); } } }