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.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Const;
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
private String _actualOperation;
private FwUpdateState _fwUpdateState;
private FwUpdateState _fwBackupUpdateState;
// Process counter for caller to monitor actual progress
private Int32 _processedBytesCtr;
@@ -713,6 +715,9 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
/// <remarks date="2019-Jun-29" author="Thomas Wiedebusch">
/// - Exit preparation added
/// </remarks>
/// <remarks date="2022-Mar-04" author="Thomas Wiedebusch">
/// - Introduced lock for repeated execution of state machine
/// </remarks>
public Boolean UpdateMeterFw()
{
if (!UpdatePreparation())
@@ -721,6 +726,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
}
_fwUpdateState = FwUpdateState.StartInitial;
_fwBackupUpdateState = FwUpdateState.Idle;
while (_fwUpdateState != FwUpdateState.Idle && !StopUpdateProcess)
{
FwUpdateStateMachine();
@@ -886,6 +892,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
//set state machine for automatic FW update
_fwUpdateState = FwUpdateState.Idle;
_fwBackupUpdateState = FwUpdateState.Idle;
_actualOperation = "";
//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
/// file system is slowing down for write accesses.
/// </remarks>
/// <remarks date="2022-Mar-04" author="Thomas Wiedebusch">
/// - Introduced lock for repeated execution of state machine
/// </remarks>
private void FwUpdateStateMachine()
{
switch (_fwUpdateState)
if (_fwBackupUpdateState == _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;
Thread.Sleep(1);
}
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
@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using Logic.ProductionToProductMapper.Cordonel;
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.Registers;
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
private String _actualOperation;
private LutUpdateState _lutUpdateState;
private LutUpdateState _lutBackupUpdateState;
// Look up table file
public MeterLutFile LutFile;
@@ -100,6 +104,17 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
// error message for display of error reason
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
#region Events
@@ -154,6 +169,17 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
{
if (genesisMeter == null) return;
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>
@@ -276,6 +302,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
_actualOperation = "";
ErrorMessage = "";
_lutUpdateState = LutUpdateState.StartInitial;
_lutBackupUpdateState = LutUpdateState.Idle;
while (_lutUpdateState != LutUpdateState.Idle && !StopUpdateProcess)
{
LutUpdateStateMachine();
@@ -317,6 +344,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
//set state machine for automatic FW update
_lutUpdateState = LutUpdateState.Idle;
_lutBackupUpdateState = LutUpdateState.Idle;
_actualOperation = "";
_operationCtr = 0;
@@ -349,6 +377,15 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
//set default timeout for file write operation
_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
StopUpdateProcess = false;
return true;
@@ -365,6 +402,12 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
if (_meterFile != null) _meterFile.OnProcessUpdate -= ProcessUpdate_Event;
_meterFile = null;
ReestablishMeterFileSystem();
foreach (var item in _registersAfterUpdate)
{
_genesisMeter.WriteRegister(item.Key, item.Value);
}
_genesisMeter?.Logout();
if (!StopUpdateProcess)
{
@@ -385,64 +428,78 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
/// <remarks date="2022-Feb-16" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2022-Mar-04" author="Thomas Wiedebusch">
/// - Introduced lock for repeated execution of state machine
/// </remarks>
private void LutUpdateStateMachine()
{
switch (_lutUpdateState)
if (_lutBackupUpdateState == _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;
Thread.Sleep(1);
}
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
@@ -752,9 +809,10 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
LutFileReadBack = new MeterLutFile(fileToUpload);
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;
_operationCtr--;