From 7727f8d18e12cf92ac8e89f6d8773ee284af0957 Mon Sep 17 00:00:00 2001 From: Thomas Wiedebusch Date: Fri, 17 Nov 2023 14:03:54 +0100 Subject: [PATCH] CUST: - FwUpdateSw: - Retries for pulse mode deactivation, - dynamic retry delay common/.../GenesisFile/MeterLutFile: - dynamic file size based on LUT file header information --- .../Genesis/GenesisFile/MeterLutFile.cs | 29 +++++-- .../Genesis/GenesisFile/MeterLutUpdate.cs | 14 +-- .../FrmFwUpdateBuilder.Designer.cs | 2 - .../ServiceFwUpdateSw/FrmServiceFwUpdateSw.cs | 87 +++++++++++++------ 4 files changed, 91 insertions(+), 41 deletions(-) diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterLutFile.cs b/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterLutFile.cs index eeb640af..d65156fd 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterLutFile.cs +++ b/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterLutFile.cs @@ -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; + /// /// File name of the file on PC /// @@ -78,7 +79,16 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// Data length /// - public UInt32 DataLength; + private UInt32 _dataLength; + public UInt32 DataLength + { + get => _dataLength; + set + { + SizeLutFile = (Int32)value + LutFileHeaderLength; + _dataLength = value; + } + } /// /// Version of LUT file @@ -116,14 +126,21 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile public MeterSize MeterSize; /// + /// ATTENTION: The file size is dynamic! /// Verifies the LUT format based on its contents /// /// true if valid + /// + /// - Initial. + /// + /// + /// - Removed constant size, instead using header to calculate the size of the file. + /// 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(); payload.AddRange(entireFile.GetRange(MeterLutFile.IndexVersion, - MeterLutFile.LutFileDataLength + MeterLutFile.LengthVersion)); + (Int32)DataLength + MeterLutFile.LengthVersion)); CalculatedCrc = Crc16Ccitt.CalculateMsb1021(payload.ToArray()); if (CalculatedCrc != FileCrc) { diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterLutUpdate.cs b/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterLutUpdate.cs index 395c213d..fa444a38 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterLutUpdate.cs +++ b/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterLutUpdate.cs @@ -486,13 +486,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// 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; } diff --git a/ServiceFwUpdate/Ui/ServiceFwUpdateBuilder/FrmFwUpdateBuilder.Designer.cs b/ServiceFwUpdate/Ui/ServiceFwUpdateBuilder/FrmFwUpdateBuilder.Designer.cs index 7d4cc8b8..888c8b76 100644 --- a/ServiceFwUpdate/Ui/ServiceFwUpdateBuilder/FrmFwUpdateBuilder.Designer.cs +++ b/ServiceFwUpdate/Ui/ServiceFwUpdateBuilder/FrmFwUpdateBuilder.Designer.cs @@ -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 // diff --git a/ServiceFwUpdate/Ui/ServiceFwUpdateSw/FrmServiceFwUpdateSw.cs b/ServiceFwUpdate/Ui/ServiceFwUpdateSw/FrmServiceFwUpdateSw.cs index 0f816d6f..fee69f87 100644 --- a/ServiceFwUpdate/Ui/ServiceFwUpdateSw/FrmServiceFwUpdateSw.cs +++ b/ServiceFwUpdate/Ui/ServiceFwUpdateSw/FrmServiceFwUpdateSw.cs @@ -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 _readMeterFiles; @@ -223,7 +228,7 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw /// Timer for progress update /// 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( _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 /// /// - Enabled final connect with passwordLvl8 and skeletonKey to enable installation of password file. /// + /// + /// - LoginDelay dynamically based on trials, reset after successfully login. + /// 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 /// /// - Clean error collection messages on initial connect. /// + /// + /// - Retries on pulse mode switching off as this is very critical for a healthy communication and + /// may cause unpredictable behaviour if it fails. + /// 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 /// /// - Initial. /// + /// + /// - For FW version without LUT file needed return with success state, + /// - Reading initially the header to get the length of the LUT. + /// 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; }