CUST: - FwUpdateSw: - Retries for pulse mode deactivation, - dynamic retry delay

common/.../GenesisFile/MeterLutFile: - dynamic file size based on LUT file header information
This commit is contained in:
Thomas Wiedebusch 2023-11-17 14:03:54 +01:00
parent d62b55790c
commit 7727f8d18e
4 changed files with 91 additions and 41 deletions

View File

@ -44,11 +44,12 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
// header size of file
public const Int32 LutFileHeaderLength = LengthLutFileId + LengthDataLength + LengthCrc + LengthVersion;
// data size of file
public const Int32 LutFileDataLength = 730;
// data size of file is variable found 730, 5840, 13140, so it has to be read using the header
// public const Int32 LutFileDataLength = 730;
// LUT file entire file size
public const Int32 SizeLutFile = LutFileHeaderLength + LutFileDataLength;
public Int32 SizeLutFile;
/// <summary>
/// File name of the file on PC
/// </summary>
@ -78,7 +79,16 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
/// <summary>
/// Data length
/// </summary>
public UInt32 DataLength;
private UInt32 _dataLength;
public UInt32 DataLength
{
get => _dataLength;
set
{
SizeLutFile = (Int32)value + LutFileHeaderLength;
_dataLength = value;
}
}
/// <summary>
/// Version of LUT file
@ -116,14 +126,21 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
public MeterSize MeterSize;
/// <summary>
/// ATTENTION: The file size is dynamic!
/// Verifies the LUT format based on its contents
/// </summary>
/// <returns>true if valid</returns>
/// <remarks date="2023-Nov-13" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2023-Nov-17" author="Thomas Wiedebusch">
/// - Removed constant size, instead using header to calculate the size of the file.
/// </remarks>
public Boolean ValidateLutFormat()
{
IsValid = false;
if (BinData == null || BinData.Count < SizeLutFile)
if (BinData == null || BinData.Count < LutFileHeaderLength)
return false;
// extract the identifier and check, if this is a LUT file
StrLutFileId = Encoding.UTF8.GetString(BinData.ToArray(), MeterLutFile.IndexLutFileId,
@ -145,7 +162,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
entireFile.AddRange(BinData);
var payload = new List<Byte>();
payload.AddRange(entireFile.GetRange(MeterLutFile.IndexVersion,
MeterLutFile.LutFileDataLength + MeterLutFile.LengthVersion));
(Int32)DataLength + MeterLutFile.LengthVersion));
CalculatedCrc = Crc16Ccitt.CalculateMsb1021(payload.ToArray());
if (CalculatedCrc != FileCrc)
{

View File

@ -486,13 +486,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
/// </remarks>
private Boolean ValidateLutFile()
{
// check if LUT file contains data
if (LutFile?.BinData == null || LutFile.BinData.Count < MeterLutFile.SizeLutFile)
{
ErrorMessage = "File size mismatch!";
return false;
}
// Take the file content to validate the format based
var lutFileFormatValid = LutFile.ValidateLutFormat();
if (LutFile.CrcError)
@ -501,6 +494,13 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
return false;
}
// check if LUT file contains data
if (LutFile?.BinData == null || LutFile.BinData.Count != LutFile.SizeLutFile)
{
ErrorMessage = "File size mismatch!";
return false;
}
return lutFileFormatValid;
}

View File

@ -433,7 +433,6 @@
this.tbxCordonelProductionOrdersSearch.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.tbxCordonelProductionOrdersSearch.Name = "tbxCordonelProductionOrdersSearch";
this.tbxCordonelProductionOrdersSearch.MouseClick += new System.Windows.Forms.MouseEventHandler(this.tbxCordonelProductionOrdersSearch_MouseClick);
this.tbxCordonelProductionOrdersSearch.Enter += new System.EventHandler(this.tbxCordonelProductionOrdersSearch_Enter);
this.tbxCordonelProductionOrdersSearch.KeyDown += new System.Windows.Forms.KeyEventHandler(this.tbxCordonelProductionOrdersSearch_KeyDown);
//
// cbxCordonelProductionOrdersSearch
@ -441,7 +440,6 @@
resources.ApplyResources(this.cbxCordonelProductionOrdersSearch, "cbxCordonelProductionOrdersSearch");
this.cbxCordonelProductionOrdersSearch.FormattingEnabled = true;
this.cbxCordonelProductionOrdersSearch.Name = "cbxCordonelProductionOrdersSearch";
this.cbxCordonelProductionOrdersSearch.DropDownClosed += new System.EventHandler(this.btnCordonelSearch_Click);
//
// lblInfoSelectCustomer
//

View File

@ -88,8 +88,8 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw
private const Int32 MaxPartialFileDataSize = 2 * 1024;
// boot variables, reboot timeout before stopping PCB ID readout trials
private const Int32 RebootTimeoutMs = 120000;
private Int32 _bootDelayCtrMs;
private const Int32 RebootTimeout_ms = 120000;
private Int32 _bootDelayCtr_ms;
private Boolean _timerIntervalExpired;
private Boolean _timerDisplayOn;
private Int32 _progressBarValueCounter;
@ -133,7 +133,7 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw
// license setup with start up check delay to end program if license is unknown
private SoftwareLicense _softwareLicense;
private Int32 _licenseCheckTimerMs = 2000;
private Int32 _licenseCheckTimer_ms = 2000;
// extended information of safe for report
private FwUpdateSafeInfo _fwUpdateSafeInfo;
@ -171,6 +171,11 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw
//// directory information
//private String _currentDirectory;
// Login retry parameters to delay recurrent login trial after unsuccessfully trial to avoid lock
// of authentication by the meter
private Int32 _loginDelay_ms;
private const Int32 DefaultLoginDelay_ms = 1000;
// directory information
private List<String> _readMeterFiles;
@ -223,7 +228,7 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw
/// Timer for progress update
/// </summary>
private readonly System.Threading.Timer _tmrProgressUpdate;
private const Int32 TimerIntervalMs = 200;
private const Int32 TimerInterval_ms = 200;
#endregion --------------------------------------- Variables --------------------------------------------------
@ -545,7 +550,7 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw
switch (_processState)
{
case ProcessState.Idle:
if (_softwareLicense == null && (_licenseCheckTimerMs -= TimerIntervalMs) < 0)
if (_softwareLicense == null && (_licenseCheckTimer_ms -= TimerInterval_ms) < 0)
_processState = ProcessState.ValidateSoftware;
break;
case ProcessState.EraseMeterFiles:
@ -1794,14 +1799,14 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw
lutLineList.Add(lutLine);
if (lutFileFormatValid)
{
if (_fwUpdateSafeLutFile == null || _fwUpdateSafeLutFile.BinData.Count != MeterLutFile.SizeLutFile)
if (_fwUpdateSafeLutFile == null || _fwUpdateSafeLutFile.BinData.Count != meterLutFile.SizeLutFile)
{
LogStringList(Resources.StrMessageLutFile, lutLineList,
Resources.StrLutFileFromMeterValid);
}
else
{
for (var c = 0; c < MeterLutFile.SizeLutFile; c++)
for (var c = 0; c < meterLutFile.DataLength; c++)
{
// If one byte is different the comparison failed
if (_fwUpdateSafeLutFile.BinData[c] != meterLutFile.BinData[c])
@ -3008,25 +3013,25 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw
{
String text = null;
// var timerInterval = _tmrProgressUpdate.Interval;
var timerInterval = TimerIntervalMs;
_bootDelayCtrMs = 0;
var internalCommunicationDelayMs = _bootDelayCtrMs + 10 * timerInterval;
var timerInterval = TimerInterval_ms;
_bootDelayCtr_ms = 0;
var internalCommunicationDelayMs = _bootDelayCtr_ms + 10 * timerInterval;
while (_bootDelayCtrMs < RebootTimeoutMs && string.IsNullOrEmpty(text))
while (_bootDelayCtr_ms < RebootTimeout_ms && string.IsNullOrEmpty(text))
{
if (!_timerIntervalExpired)
continue;
_timerIntervalExpired = false;
// internal communication delay to avoid overload of Cordonel
if (_bootDelayCtrMs % internalCommunicationDelayMs == 0)
if (_bootDelayCtr_ms % internalCommunicationDelayMs == 0)
{
text = RegisterConverter.ConvertTo<String>(
_currentGenesis.ReadRegister(Register.Configexchange.PcbSerialNumber, 12));
}
ProcessUpdate_Event(this, new ProcessExecEventArgs(Resources.StrUpdateAppAndRestartMeter,
_bootDelayCtrMs * 100.0 / RebootTimeoutMs));
_bootDelayCtrMs += timerInterval;
_bootDelayCtr_ms * 100.0 / RebootTimeout_ms));
_bootDelayCtr_ms += timerInterval;
}
return !string.IsNullOrEmpty(text);
@ -3358,6 +3363,9 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw
/// <remarks date="2023-Nov-09" author="Thomas Wiedebusch">
/// - Enabled final connect with passwordLvl8 and skeletonKey to enable installation of password file.
/// </remarks>
/// <remarks date="2023-Nov-17" author="Thomas Wiedebusch">
/// - LoginDelay dynamically based on trials, reset after successfully login.
/// </remarks>
private Boolean ExecConnect()
{
// If the PCB ID is not set (read from meter) this is the first initial login.
@ -3402,14 +3410,12 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw
return false;
}
// Delay needed if repeated login trial given by Cordonel, the next communication has to
// wait that long, otherwise the Cordonel locks for 2s, 4s, 8s, 16s and so on on every retry!
Thread.Sleep(3000);
// This login may be time consuming on readout of application information
// first try to login with password Level 8, this is needed if the password file is installed
if (LoginAndSpecialSetupProcedure(_passwordLvl8))
{
// Reset login delay as on successfully login the meter resets the retry-lock-in-delay
_loginDelay_ms = DefaultLoginDelay_ms;
LogText(Resources.StrLoginPwdLevel8);
_passwordFileIsCorrupted = false;
return true;
@ -3417,11 +3423,14 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw
// Delay needed if repeated login trial given by Cordonel, the next communication has to
// wait that long, otherwise the Cordonel locks for 2s, 4s, 8s, 16s and so on on every retry!
Thread.Sleep(5000);
_loginDelay_ms *= 2;
Thread.Sleep(_loginDelay_ms);
// Use the skeleton key for login as the password file may not be installed or invalid
if (LoginAndSpecialSetupProcedure(_skeletonKey))
{
// Reset login delay as on successfully login the meter resets the retry-lock-in-delay
_loginDelay_ms = DefaultLoginDelay_ms;
LogText(Resources.StrLoginSkeletonKey);
return true;
}
@ -3453,26 +3462,47 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw
/// <remarks date="2023-Nov-15" author="Thomas Wiedebusch">
/// - Clean error collection messages on initial connect.
/// </remarks>
/// <remarks date="2023-Nov-17" author="Thomas Wiedebusch">
/// - Retries on pulse mode switching off as this is very critical for a healthy communication and
/// may cause unpredictable behaviour if it fails.
/// </remarks>
private Boolean LoginAndSpecialSetupProcedure(String password)
{
Boolean retVal;
var pulseModeOffRetryCtr = 2;
if (_initialConnect)
{
// Start with a fresh list of errors to decide after the entire procedure how to proceed
_errorCollectionMessages?.Clear();
// Login without reading all registers and FW keeping the communication to a minimum to switch
// the pulse mode OFF before reading all installed apps, which will be unpredictable on activated
// pulse module.
retVal = _currentGenesis.Login(password, skipReadMeterFwAndAssignRegisters: true);
do
{
// Login without reading all registers and FW keeping the communication to a minimum to switch
// the pulse mode OFF before reading all installed apps, which will be unpredictable on activated
// pulse module.
retVal = _currentGenesis.Login(password, skipReadMeterFwAndAssignRegisters: true);
if (retVal)
{
// Switch the pulse module inactive on initial login at first connect and log out
DeactivatePulseMode();
}
else
{
// Delay needed if repeated login trial given by Cordonel, the next communication has to
// wait that long, otherwise the Cordonel locks for 2s, 4s, 8s, 16s and so on on every retry!
_loginDelay_ms *= 2;
Thread.Sleep(_loginDelay_ms);
}
// Increase th login delay to avoid
} while (!retVal && pulseModeOffRetryCtr-- > 0);
// If the login couldn't be performed exit with error
if (!retVal)
{
return false;
}
// Switch the pulse module inactive on initial login at first connect and log out
DeactivatePulseMode();
}
// Second login if initial was executed including reading out all installed applications
@ -4157,6 +4187,10 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw
/// <remarks date="2023-Nov-13" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2023-Nov-17" author="Thomas Wiedebusch">
/// - For FW version without LUT file needed return with success state,
/// - Reading initially the header to get the length of the LUT.
/// </remarks>
private void RestoreLutFile(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error)
{
// Remind the invoker being able to generate error messages based on the last state
@ -4183,6 +4217,7 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw
// 1. b) LUT file not installed and not delivered by the FW update safe
// This FW version does not require a LUT file
InfoProcessSuccess(lblLutFileCheck, Resources.StrLutFileNotNeeded);
_processState = successExitState;
LogText(Resources.StrLutFileNotNeeded);
return;
}