MeterFwUpdate and MeterLutUpdate:

- Introduced lock for repeated execution of state machine
This commit is contained in:
Thomas Wiedebusch
2022-03-04 09:10:08 +01:00
parent 63121d7c41
commit 1ae56612ff
2 changed files with 221 additions and 143 deletions
@@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications; using Xylem.Common.Hardware.WaterMeter.Genesis.Applications;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Const; using Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Const;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
@@ -139,6 +140,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
// Text for caller to inform about actual process being carried out // Text for caller to inform about actual process being carried out
private String _actualOperation; private String _actualOperation;
private FwUpdateState _fwUpdateState; private FwUpdateState _fwUpdateState;
private FwUpdateState _fwBackupUpdateState;
// Process counter for caller to monitor actual progress // Process counter for caller to monitor actual progress
private Int32 _processedBytesCtr; private Int32 _processedBytesCtr;
@@ -713,6 +715,9 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
/// <remarks date="2019-Jun-29" author="Thomas Wiedebusch"> /// <remarks date="2019-Jun-29" author="Thomas Wiedebusch">
/// - Exit preparation added /// - Exit preparation added
/// </remarks> /// </remarks>
/// <remarks date="2022-Mar-04" author="Thomas Wiedebusch">
/// - Introduced lock for repeated execution of state machine
/// </remarks>
public Boolean UpdateMeterFw() public Boolean UpdateMeterFw()
{ {
if (!UpdatePreparation()) if (!UpdatePreparation())
@@ -721,6 +726,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
} }
_fwUpdateState = FwUpdateState.StartInitial; _fwUpdateState = FwUpdateState.StartInitial;
_fwBackupUpdateState = FwUpdateState.Idle;
while (_fwUpdateState != FwUpdateState.Idle && !StopUpdateProcess) while (_fwUpdateState != FwUpdateState.Idle && !StopUpdateProcess)
{ {
FwUpdateStateMachine(); FwUpdateStateMachine();
@@ -886,6 +892,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
//set state machine for automatic FW update //set state machine for automatic FW update
_fwUpdateState = FwUpdateState.Idle; _fwUpdateState = FwUpdateState.Idle;
_fwBackupUpdateState = FwUpdateState.Idle;
_actualOperation = ""; _actualOperation = "";
//update file information //update file information
@@ -1040,97 +1047,110 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
/// useful as on increased size and number of applications needed to install in one run, the /// useful as on increased size and number of applications needed to install in one run, the
/// file system is slowing down for write accesses. /// file system is slowing down for write accesses.
/// </remarks> /// </remarks>
/// <remarks date="2022-Mar-04" author="Thomas Wiedebusch">
/// - Introduced lock for repeated execution of state machine
/// </remarks>
private void FwUpdateStateMachine() private void FwUpdateStateMachine()
{ {
switch (_fwUpdateState) if (_fwBackupUpdateState == _fwUpdateState)
{ {
case FwUpdateState.Idle: Thread.Sleep(1);
break;
case FwUpdateState.StartInitial:
_updateProcedureRetryCtr = 0;
_fwUpdateState = FwUpdateState.BuildFileApps;
break;
case FwUpdateState.BuildFileApps:
if (!SingleFilePartsRetryEnable && !ConsecutiveFilePartsRetryEnable)
{
_remainingFileAppsParts.Clear();
}
BuildFileApps();
_fwUpdateState = FwUpdateState.DownloadFileApps;
break;
case FwUpdateState.DownloadFileApps:
_fwUpdateState = DownloadFileApps()
? FwUpdateState.BuildUpgradeControlFile
: FwUpdateState.StepFailed;
break;
case FwUpdateState.BuildUpgradeControlFile:
//prepare the trigger to select all successfully downloaded applications
PrepareTrigger();
_fwUpdateState = BuildUpgradeCtrlFile()
? FwUpdateState.DownloadUpgradeControlFile
: FwUpdateState.StepFailed;
break;
case FwUpdateState.DownloadUpgradeControlFile:
_fwUpdateState = DownloadUpgradeCtrlFile()
? FwUpdateState.TriggerUpgrade
: FwUpdateState.StepFailed;
break;
case FwUpdateState.TriggerUpgrade:
_fwUpdateState = TriggerUpgrade() ? FwUpdateState.Idle : FwUpdateState.StepFailed;
break;
case FwUpdateState.StepFailed:
_processedBytesCtr = 0;
_overallBytesCtr = 0;
//retry entire file if some partial file writes failed
if (RetryEntireFileAfterFailedPartialRetries == _updateProcedureRetryCtr)
{
_remainingFileAppsParts.Clear();
}
_fwUpdateState = _updateProcedureRetryCtr++ < UpdateProcedureRetries
? FwUpdateState.RepeatFwUpdate
: FwUpdateState.UpdateFailed;
break;
case FwUpdateState.RepeatFwUpdate:
_processedBytesCtr = 0;
_overallBytesCtr = 0;
_fwUpdateState = FwUpdateState.BuildFileApps;
if (_updateProcedureRetryCtr >= IncreaseTimeoutUpdateProcedureRetries)
{
_meterFile?.SetExtremeFileWriteTimeout();
}
break;
case FwUpdateState.UpdateFailed:
_processedBytesCtr = 0;
_overallBytesCtr = 0;
StopUpdateProcess = true;
_fwUpdateState = FwUpdateState.Idle;
break;
case FwUpdateState.UploadFileApps:
break;
case FwUpdateState.CompareFileApps:
break;
case FwUpdateState.EraseUpdateFile:
break;
default:
_fwUpdateState = FwUpdateState.Idle;
break;
} }
else
{
// remind backup state to avoid repeated execution and side effects
_fwBackupUpdateState = _fwUpdateState;
switch (_fwUpdateState)
{
case FwUpdateState.Idle:
break;
case FwUpdateState.StartInitial:
_updateProcedureRetryCtr = 0;
_fwUpdateState = FwUpdateState.BuildFileApps;
break;
case FwUpdateState.BuildFileApps:
if (!SingleFilePartsRetryEnable && !ConsecutiveFilePartsRetryEnable)
{
_remainingFileAppsParts.Clear();
}
BuildFileApps();
_fwUpdateState = FwUpdateState.DownloadFileApps;
break;
case FwUpdateState.DownloadFileApps:
_fwUpdateState = DownloadFileApps()
? FwUpdateState.BuildUpgradeControlFile
: FwUpdateState.StepFailed;
break;
case FwUpdateState.BuildUpgradeControlFile:
//prepare the trigger to select all successfully downloaded applications
PrepareTrigger();
_fwUpdateState = BuildUpgradeCtrlFile()
? FwUpdateState.DownloadUpgradeControlFile
: FwUpdateState.StepFailed;
break;
case FwUpdateState.DownloadUpgradeControlFile:
_fwUpdateState = DownloadUpgradeCtrlFile()
? FwUpdateState.TriggerUpgrade
: FwUpdateState.StepFailed;
break;
case FwUpdateState.TriggerUpgrade:
_fwUpdateState = TriggerUpgrade() ? FwUpdateState.Idle : FwUpdateState.StepFailed;
break;
case FwUpdateState.StepFailed:
_processedBytesCtr = 0;
_overallBytesCtr = 0;
//retry entire file if some partial file writes failed
if (RetryEntireFileAfterFailedPartialRetries == _updateProcedureRetryCtr)
{
_remainingFileAppsParts.Clear();
}
_fwUpdateState = _updateProcedureRetryCtr++ < UpdateProcedureRetries
? FwUpdateState.RepeatFwUpdate
: FwUpdateState.UpdateFailed;
break;
case FwUpdateState.RepeatFwUpdate:
_processedBytesCtr = 0;
_overallBytesCtr = 0;
_fwUpdateState = FwUpdateState.BuildFileApps;
if (_updateProcedureRetryCtr >= IncreaseTimeoutUpdateProcedureRetries)
{
_meterFile?.SetExtremeFileWriteTimeout();
}
break;
case FwUpdateState.UpdateFailed:
_processedBytesCtr = 0;
_overallBytesCtr = 0;
StopUpdateProcess = true;
_fwUpdateState = FwUpdateState.Idle;
break;
case FwUpdateState.UploadFileApps:
break;
case FwUpdateState.CompareFileApps:
break;
case FwUpdateState.EraseUpdateFile:
break;
default:
_fwUpdateState = FwUpdateState.Idle;
break;
}
}// lock repeated execution of identical state
} }
#endregion #endregion
@@ -1,9 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using Logic.ProductionToProductMapper.Cordonel; using Logic.ProductionToProductMapper.Cordonel;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.Consts; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers; using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore.Consts; using Xylem.Common.Hardware.WaterMeter.WaterMeterCore.Consts;
@@ -85,6 +88,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
// Text for caller to inform about actual process being carried out // Text for caller to inform about actual process being carried out
private String _actualOperation; private String _actualOperation;
private LutUpdateState _lutUpdateState; private LutUpdateState _lutUpdateState;
private LutUpdateState _lutBackupUpdateState;
// Look up table file // Look up table file
public MeterLutFile LutFile; public MeterLutFile LutFile;
@@ -100,6 +104,17 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
// error message for display of error reason // error message for display of error reason
public String ErrorMessage; public String ErrorMessage;
/// <summary>
/// Register subset needed to adjust for update performance content before update
/// </summary>
private readonly Dictionary<String, Byte[]> _registersBeforeUpdate = new Dictionary<String, Byte[]>();
/// <summary>
/// Register to restore after update
/// </summary>
private Dictionary<String, Byte[]> _registersAfterUpdate = new Dictionary<String, Byte[]>();
#endregion #endregion
#region Events #region Events
@@ -154,6 +169,17 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
{ {
if (genesisMeter == null) return; if (genesisMeter == null) return;
AssignGenesis(genesisMeter); AssignGenesis(genesisMeter);
if (genesisMeter.MeterAppListVersion.Any(f => f.AppName == "GENESISFLOW" && f.IsInstalled))
{
_registersBeforeUpdate.Add(Register.Genesisflow.LedMode, new[] {(Byte) LedMode.Off});
_registersBeforeUpdate.Add(Register.Genesisflow.SampleRate, new Byte[] {1});
}
if (genesisMeter.MeterAppListVersion.Any(f => f.AppName == "METROLOGYASST" && f.IsInstalled))
_registersBeforeUpdate.Add(Register.Mertrologyasst.PulseMode, new Byte[] { 0 });
//deny access if NA product, because this does not have the radio app installed
if (genesisMeter.MeterAppListVersion.Any(f => f.AppName == "SENSUSRADIO" && f.IsInstalled))
_registersBeforeUpdate.Add(Register.Sensusradio.WakeupInterval, new Byte[] { 6 });
} }
/// <summary> /// <summary>
@@ -276,6 +302,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
_actualOperation = ""; _actualOperation = "";
ErrorMessage = ""; ErrorMessage = "";
_lutUpdateState = LutUpdateState.StartInitial; _lutUpdateState = LutUpdateState.StartInitial;
_lutBackupUpdateState = LutUpdateState.Idle;
while (_lutUpdateState != LutUpdateState.Idle && !StopUpdateProcess) while (_lutUpdateState != LutUpdateState.Idle && !StopUpdateProcess)
{ {
LutUpdateStateMachine(); LutUpdateStateMachine();
@@ -317,6 +344,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
//set state machine for automatic FW update //set state machine for automatic FW update
_lutUpdateState = LutUpdateState.Idle; _lutUpdateState = LutUpdateState.Idle;
_lutBackupUpdateState = LutUpdateState.Idle;
_actualOperation = ""; _actualOperation = "";
_operationCtr = 0; _operationCtr = 0;
@@ -349,6 +377,15 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
//set default timeout for file write operation //set default timeout for file write operation
_meterFile.SetDefaultFileWriteTimeout(); _meterFile.SetDefaultFileWriteTimeout();
//switch LED off set sample rate to 1 -> 1Hz to slow down CPU load
_registersAfterUpdate = new Dictionary<String, Byte[]>();
foreach (var item in _registersBeforeUpdate)
{
var writeBackValue = _genesisMeter.ReadRegister(item.Key);
_registersAfterUpdate.Add(item.Key, writeBackValue);
_genesisMeter.WriteRegister(item.Key, item.Value);
}
//remove the update stop action //remove the update stop action
StopUpdateProcess = false; StopUpdateProcess = false;
return true; return true;
@@ -365,6 +402,12 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
if (_meterFile != null) _meterFile.OnProcessUpdate -= ProcessUpdate_Event; if (_meterFile != null) _meterFile.OnProcessUpdate -= ProcessUpdate_Event;
_meterFile = null; _meterFile = null;
ReestablishMeterFileSystem(); ReestablishMeterFileSystem();
foreach (var item in _registersAfterUpdate)
{
_genesisMeter.WriteRegister(item.Key, item.Value);
}
_genesisMeter?.Logout(); _genesisMeter?.Logout();
if (!StopUpdateProcess) if (!StopUpdateProcess)
{ {
@@ -385,64 +428,78 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
/// <remarks date="2022-Feb-16" author="Thomas Wiedebusch"> /// <remarks date="2022-Feb-16" author="Thomas Wiedebusch">
/// - Initial /// - Initial
/// </remarks> /// </remarks>
/// <remarks date="2022-Mar-04" author="Thomas Wiedebusch">
/// - Introduced lock for repeated execution of state machine
/// </remarks>
private void LutUpdateStateMachine() private void LutUpdateStateMachine()
{ {
switch (_lutUpdateState) if (_lutBackupUpdateState == _lutUpdateState)
{ {
case LutUpdateState.Idle: Thread.Sleep(1);
break;
case LutUpdateState.StartInitial:
_updateProcedureRetryCtr = 0;
_lutUpdateState = LutUpdateState.PrepareLutDownload;
break;
case LutUpdateState.PrepareLutDownload:
_lutUpdateState = PrepareLutDownload() ? LutUpdateState.EraseLutFile : LutUpdateState.StepFailed;
break;
case LutUpdateState.EraseLutFile:
_lutUpdateState = EraseLutFiles() ? LutUpdateState.DownloadLutFile : LutUpdateState.StepFailed;
break;
case LutUpdateState.DownloadLutFile:
_lutUpdateState = DownloadLutFiles() ? LutUpdateState.UploadLutFile : LutUpdateState.StepFailed;
break;
case LutUpdateState.UploadLutFile:
_lutUpdateState = UploadAndCompareLutFiles() ? LutUpdateState.Idle : LutUpdateState.StepFailed;
break;
case LutUpdateState.CompareLutFile:
break;
case LutUpdateState.StepFailed:
_lutUpdateState = _updateProcedureRetryCtr++ < UpdateProcedureRetries
? LutUpdateState.RepeatLutUpdate : LutUpdateState.UpdateFailed;
break;
case LutUpdateState.RepeatLutUpdate:
_lutUpdateState = LutUpdateState.DownloadLutFile;
if (_updateProcedureRetryCtr >= IncreaseTimeoutUpdateProcedureRetries)
{
_meterFile?.SetExtremeFileWriteTimeout();
}
break;
case LutUpdateState.UpdateFailed:
StopUpdateProcess = true;
_lutUpdateState = LutUpdateState.Idle;
break;
case LutUpdateState.LutUpdateStateListDelimiter:
_lutUpdateState = LutUpdateState.Idle;
break;
default:
_lutUpdateState = LutUpdateState.Idle;
break;
} }
else
{
// remind backup state to avoid repeated execution and side effects
_lutBackupUpdateState = _lutUpdateState;
switch (_lutUpdateState)
{
case LutUpdateState.Idle:
break;
case LutUpdateState.StartInitial:
_updateProcedureRetryCtr = 0;
_lutUpdateState = LutUpdateState.PrepareLutDownload;
break;
case LutUpdateState.PrepareLutDownload:
_lutUpdateState =
PrepareLutDownload() ? LutUpdateState.EraseLutFile : LutUpdateState.StepFailed;
break;
case LutUpdateState.EraseLutFile:
_lutUpdateState = EraseLutFiles() ? LutUpdateState.DownloadLutFile : LutUpdateState.StepFailed;
break;
case LutUpdateState.DownloadLutFile:
_lutUpdateState = DownloadLutFiles() ? LutUpdateState.UploadLutFile : LutUpdateState.StepFailed;
break;
case LutUpdateState.UploadLutFile:
_lutUpdateState = UploadAndCompareLutFiles() ? LutUpdateState.Idle : LutUpdateState.StepFailed;
break;
case LutUpdateState.CompareLutFile:
break;
case LutUpdateState.StepFailed:
_lutUpdateState = _updateProcedureRetryCtr++ < UpdateProcedureRetries
? LutUpdateState.RepeatLutUpdate
: LutUpdateState.UpdateFailed;
break;
case LutUpdateState.RepeatLutUpdate:
_lutUpdateState = LutUpdateState.DownloadLutFile;
if (_updateProcedureRetryCtr >= IncreaseTimeoutUpdateProcedureRetries)
{
_meterFile?.SetExtremeFileWriteTimeout();
}
break;
case LutUpdateState.UpdateFailed:
StopUpdateProcess = true;
_lutUpdateState = LutUpdateState.Idle;
break;
case LutUpdateState.LutUpdateStateListDelimiter:
_lutUpdateState = LutUpdateState.Idle;
break;
default:
_lutUpdateState = LutUpdateState.Idle;
break;
}
}// lock for identical state
} }
#endregion #endregion
@@ -752,9 +809,10 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
LutFileReadBack = new MeterLutFile(fileToUpload); LutFileReadBack = new MeterLutFile(fileToUpload);
if (_meterFile.ReadMeterFile(fileToUpload, out LutFileReadBack.BinData, LutFile.BinData.Count)) if (_meterFile.ReadMeterFile(fileToUpload, out LutFileReadBack.BinData, LutFile.BinData.Count))
{ {
if (LutFileReadBack.BinData != LutFile.BinData) // compare the entire buffer which also includes the header of the file
if (!LutFileReadBack.BinData.SequenceEqual(LutFile.BinData))
{ {
ErrorMessage += $"Verification of {fileToUpload} failed!\n"; ErrorMessage += $"Meter LUT file is unequal to downloaded {fileToUpload}!\n";
returnValue = false; returnValue = false;
_operationCtr--; _operationCtr--;