5149 lines
234 KiB
C#
5149 lines
234 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Drawing;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Windows.Forms;
|
|
using Logic.ProductionToProductMapper.Cordonel;
|
|
using Newtonsoft.Json;
|
|
using NLog;
|
|
using Xylem.Common.CommonCore.Consts;
|
|
using Xylem.Common.CommonCore.ThreadWatcher;
|
|
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications;
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Const;
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Consts;
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile;
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd;
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisStatus;
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes;
|
|
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
|
|
using Xylem.Common.Logic.ProductionOrderCore.OrderData;
|
|
using Xylem.Common.Utils.Logging;
|
|
using Xylem.Common.Utils.ProcessExec;
|
|
using Xylem.Common.Utils.ProcessExec.EventArguments;
|
|
using Xylem.Common.Utils.UiInvoker;
|
|
using Xylem.Common.Utils.UiLanguageControl;
|
|
using Xylem.ServiceFwUpdate.Common.FwUpdateConfig.Consts;
|
|
using Xylem.ServiceFwUpdate.Common.FwUpdateSafe;
|
|
using Xylem.ServiceFwUpdate.Common.FwUpdateSafe.Consts;
|
|
using Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw.Const;
|
|
using Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw.Properties;
|
|
|
|
namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw
|
|
{
|
|
/// <summary>
|
|
/// FW update form
|
|
/// </summary>
|
|
/// <remarks date="2020-Nov-30" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
[Serializable]
|
|
public partial class FrmServiceFwUpdateSw : Form
|
|
{
|
|
#region ------------------------------------------ Variables --------------------------------------------------
|
|
|
|
// Genesis (legacy and program internal name for Cordonel)
|
|
private GenesisMeter _currentGenesis;
|
|
private readonly MeterBatch _meterBatch;
|
|
private Byte[] _fwUpdateSafePwdFile;
|
|
private MeterLutFile _fwUpdateSafeLutFile;
|
|
private PowerCorrection _fwUpdatePowerCorrection;
|
|
private MeterFile _meterFile;
|
|
private String _passwordLvl8;
|
|
private String _skeletonKey;
|
|
|
|
// port settings
|
|
private MeterPortScanner _serialPortScanner;
|
|
private PortConfig _portConfig;
|
|
private String _portType;
|
|
|
|
private readonly DataTable _dataTable = new DataTable();
|
|
private readonly Thread _fwUpdateSwThread;
|
|
private const Int32 Slot = 0;
|
|
|
|
private DateTimeOffset _startTime;
|
|
private Boolean _resetTimeMeasurement;
|
|
private Boolean _stopRequest;
|
|
|
|
private readonly ILogger _logger;
|
|
|
|
private readonly CancellationTokenSource _processToken = new CancellationTokenSource();
|
|
|
|
// actual process state
|
|
private ProcessState _processState;
|
|
// locker to avoid repeated state execution
|
|
private ProcessState _lastProcessState;
|
|
// reminder for state change to execute e.g. error messaging
|
|
private ProcessState _invokerProcessState;
|
|
|
|
// used for display of waiting for meter if the state does not change
|
|
private String _lastFwUpdateState;
|
|
private Int32 _processProgressObserver;
|
|
private MeterFwUpdate _meterFwUpdate;
|
|
// file block part size in which the applications will be split for the FW-Update download
|
|
private const Int32 MaxPartialFileDataSize = 2 * 1024;
|
|
|
|
// boot variables, reboot timeout before stopping PCB ID readout trials
|
|
private const Int32 RebootTimeout_ms = 180000;
|
|
private Int32 _bootDelayCtr_ms;
|
|
private Boolean _timerIntervalExpired;
|
|
private Boolean _timerDisplayOn;
|
|
private Int32 _progressBarValueCounter;
|
|
|
|
// reminder for corrupted password file
|
|
private Boolean _passwordFileIsCorrupted;
|
|
// initial connect to force a quick connect being able switch the pulse mode off
|
|
private Boolean _initialConnect;
|
|
// remind final login, as this allows EXPLICIT login with password level 8 to validate password file
|
|
private Boolean _afterUpdateConnect;
|
|
// remind recovery request to check finally all register settings
|
|
private Boolean _recoveryRegistersRequired;
|
|
// reminder for Genesis found in update list (_cordonelDeviceInfos)
|
|
private Boolean _genesisInUpdateList;
|
|
|
|
// status information
|
|
private static readonly Color ColorDefault = Color.Black;
|
|
private static readonly Color ColorWarning = Color.DarkOrange;
|
|
private static readonly Color ColorSuccess = Color.Green;
|
|
private static readonly Color ColorProcessFailed = Color.Red;
|
|
private static readonly Color ColorOngoingProcess = Color.Blue;
|
|
private static readonly Color ColorUnknownStatus = Color.Gray;
|
|
private const String SuccessSign = @"✔";
|
|
private const String FailedSign = @"✘";
|
|
private const String WarningSign = @"!";
|
|
|
|
private const String StrSeparator = "-----------------------------------------------------" +
|
|
"-----------------------------------------------------" +
|
|
"-----------------------------";
|
|
|
|
// data grid styles
|
|
private readonly DataGridViewCellStyle _styleInstalled = new DataGridViewCellStyle();
|
|
private readonly DataGridViewCellStyle _styleNotInstalled = new DataGridViewCellStyle();
|
|
private readonly DataGridViewCellStyle _styleInvalidMeterCrc = new DataGridViewCellStyle();
|
|
private readonly DataGridViewCellStyle _styleUpdateRequired = new DataGridViewCellStyle();
|
|
private readonly DataGridViewCellStyle _styleDownloadSucceeded = new DataGridViewCellStyle();
|
|
private readonly DataGridViewCellStyle _styleDownloadFailed = new DataGridViewCellStyle();
|
|
private readonly DataGridViewCellStyle _styleDownloadOngoing = new DataGridViewCellStyle();
|
|
private readonly DataGridViewCellStyle _styleValidated = new DataGridViewCellStyle();
|
|
private readonly DataGridViewCellStyle _styleVerificationRequired = new DataGridViewCellStyle();
|
|
private readonly DataGridViewCellStyle _styleEraseRequired = new DataGridViewCellStyle();
|
|
private readonly DataGridViewCellStyle _styleCommunicationError = new DataGridViewCellStyle();
|
|
|
|
// update information
|
|
private Boolean _binaryPackageLoaded;
|
|
|
|
// license setup with start up check delay to end program if license is unknown
|
|
private SoftwareLicense _softwareLicense;
|
|
private Int32 _licenseCheckTimer_ms = 2000;
|
|
|
|
// extended information of safe for report
|
|
private FwUpdateSafeInfo _fwUpdateSafeInfo;
|
|
|
|
// Cordonel device information containing everything needed to update a specific Cordonel
|
|
private readonly List<CordonelDeviceInfo> _cordonelDeviceInfos = new List<CordonelDeviceInfo>();
|
|
private CordonelFirmware _cordonelFirmwarePackage = new CordonelFirmware();
|
|
|
|
// external update remarks form
|
|
private FrmHistory _frmHistory = new FrmHistory();
|
|
|
|
// remind manually changed culture setting
|
|
private CultureInfo _cultureInfo;
|
|
private readonly Version _version;
|
|
|
|
// base path to update packages received from FW-Update Loader
|
|
private FwUpdatePaths _basePath;
|
|
|
|
// path and name of report files
|
|
private String _pathNameReportFile;
|
|
// path and name of the installed applications
|
|
private String _pathNameCordonelAppVersionFile;
|
|
|
|
// path and name of the restore settings file on crashed application or interrupted update
|
|
private String _pathNameCordonelRegisterRestoreSettingsFile;
|
|
// Register subset needed to adjust for update performance content before update
|
|
private Dictionary<String, Byte[]> _pulseModeRegistersRestore = new Dictionary<String, Byte[]>();
|
|
|
|
// loading the lists of meter files which shall be erased before the FW-Update and restored after.
|
|
private MeterFilesEraseRestore _meterFilesEraseRestore;
|
|
|
|
// register restore
|
|
private RegisterRestorer _registerRestorer;
|
|
|
|
//// 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 const Int32 DefaultLoginDelay_ms = 2000;
|
|
private Int32 _loginDelay_ms = DefaultLoginDelay_ms;
|
|
|
|
// directory information
|
|
private List<String> _readMeterFiles;
|
|
|
|
// remind customer serial number for logging
|
|
private String _customerSerialNumber = "?";
|
|
|
|
// error message collection to decide for field operator action after one run
|
|
private List<String> _errorCollectionMessages = new List<String>();
|
|
|
|
// State machine sequence for preparation of meter release. This will be used by the error process as well
|
|
// as by the state machine to have a common ruler for this sorted sequence!
|
|
private List<ProcessState> _stateSequencePrepareMeterRelease = new List<ProcessState>
|
|
{
|
|
ProcessState.RestoreMeterFiles,
|
|
ProcessState.RestoreLutFile,
|
|
ProcessState.RecoverRegisters,
|
|
ProcessState.FinalReadRegisters,
|
|
ProcessState.CompareRegisters,
|
|
ProcessState.RestorePasswordFile,
|
|
ProcessState.FinalConnect,
|
|
ProcessState.ReadEngineeringLogs,
|
|
ProcessState.CorrectPowerOverestimation,
|
|
ProcessState.CalculateLifeTime,
|
|
ProcessState.VerifyMeterFiles,
|
|
ProcessState.CheckFwUpdateSuccess
|
|
};
|
|
|
|
// State machine sequence for start of the connection and check for meter. This will be used by the error
|
|
// process as well as by the state machine to have a common ruler for this sorted sequence!
|
|
private List<ProcessState> _stateSequencePrepareAndCheckMeter = new List<ProcessState>
|
|
{
|
|
ProcessState.PortScan,
|
|
ProcessState.InitialConnect,
|
|
ProcessState.InitialReadRegisters,
|
|
ProcessState.InitialReadMeterFiles,
|
|
ProcessState.ReadInfoEraseRestoreFiles,
|
|
ProcessState.LoadUpdateFiles,
|
|
ProcessState.CheckUpdateRequest
|
|
};
|
|
|
|
// State machine sequence for start of the connection and check for meter. This will be used by the error
|
|
// process as well as by the state machine to have a common ruler for this sorted sequence!
|
|
private List<ProcessState> _stateSequenceFwUpdate = new List<ProcessState>
|
|
{
|
|
ProcessState.CheckUpdateCapability,
|
|
ProcessState.EraseMeterFiles,
|
|
ProcessState.FirmwareUpdate,
|
|
ProcessState.Reboot,
|
|
ProcessState.RebootConnect,
|
|
ProcessState.RestoreMeterFiles
|
|
};
|
|
|
|
/// <summary>
|
|
/// Timer for progress update
|
|
/// </summary>
|
|
private readonly System.Threading.Timer _tmrProgressUpdate;
|
|
private const Int32 TimerInterval_ms = 200;
|
|
|
|
#endregion --------------------------------------- Variables --------------------------------------------------
|
|
|
|
#region ------------------------------------------ State Machine ----------------------------------------------
|
|
/// <summary>
|
|
/// State machine for FW update process:
|
|
/// Will be executed in an endless loop from the "Service FW-Update Thread" until exit. State will be executed
|
|
/// once and then locked for repeated execution. A state change is the reason for reentry.
|
|
/// NOTE:
|
|
/// Change the state immediately before calling any routine to avoid repeated execution of routine!
|
|
/// </summary>
|
|
/// <remarks date="2020-Oct-21" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-12" author="Thomas Wiedebusch">
|
|
/// - State execution locked if not changed!
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-16" author="Thomas Wiedebusch">
|
|
/// - Reboot logic implemented.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-21" author="Thomas Wiedebusch">
|
|
/// - States reworked.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-22" author="Thomas Wiedebusch">
|
|
/// - Validate software state added.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-03" author="Thomas Wiedebusch">
|
|
/// - Password file installation forces re-login trial.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-17" author="Thomas Wiedebusch">
|
|
/// - Added meter file readout, erasure, restore and verification.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-18" author="Thomas Wiedebusch">
|
|
/// - RestAllLabels changed,
|
|
/// - After reinstalling the password file go here to idle in FwUpdateSuccess, in the password file restore
|
|
/// routine a login will be forced if the password has been installed again.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-02" author="Thomas Wiedebusch">
|
|
/// - Disable all buttons until license validated.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-09" author="Thomas Wiedebusch">
|
|
/// - Report file generation.
|
|
/// - Call of LogInstalledFw
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-10" author="Thomas Wiedebusch">
|
|
/// - Report file generation removed,
|
|
/// - Meter files pre update read does not stop process if it fails,
|
|
/// - Log installed Meter FW removed.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-17" author="Thomas Wiedebusch">
|
|
/// - On Init set buttons to download disable but connect enable.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-15" author="Thomas Wiedebusch">
|
|
/// - On final connection after update and password restore, the connect will be exit with
|
|
/// check update request to make the final check.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Jun-08" author="Roland Drabesch">
|
|
/// - Sleep on equal process state to force suspend of actual thread.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Jun-30" author="Thomas Wiedebusch">
|
|
/// - If the register have been backup on a subsequent FW-Update trial, the read, erase of meter files will be
|
|
/// skipped to avoid erasure of the upgXX - Files which are successfully downloaded. So only the remaining
|
|
/// upgXX - Files will be downloaded as the MeterFwUpdate object reminds all successfully downloaded Apps.
|
|
/// </remarks>
|
|
/// <remarks date="2022-Mai-10" author="Thomas Wiedebusch">
|
|
/// - Release display on idle entry.
|
|
/// </remarks>
|
|
/// <remarks date="2022-Jul-22" author="Thomas Wiedebusch">
|
|
/// - Release display on success entry and on error state.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-18..25" author="Thomas Wiedebusch">
|
|
/// - Reorganized to support maintenance before the FW update capability check.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-27" author="Thomas Wiedebusch">
|
|
/// - Release meter to normal operation after error state.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-09" author="Thomas Wiedebusch">
|
|
/// - Major rework of sequence.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-14" author="Thomas Wiedebusch">
|
|
/// - Prepared for engineering logs.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-15..21" author="Thomas Wiedebusch">
|
|
/// - State machine sequences based on ProcessState tables forcing the sequencing.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-22" author="Thomas Wiedebusch">
|
|
/// - Restore meter files and compare registers removed for this release.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-27" author="Thomas Wiedebusch">
|
|
/// - Compare registers reactivated based on new interface (configuration.json) with new
|
|
/// "StaticType": “approximate”.
|
|
/// </remarks>
|
|
/// <remarks date="2024-May-07" author="Thomas Wiedebusch">
|
|
/// - Power correction for FW update to new version starting with R1.3.0 for EMEA and
|
|
/// R2.0.06 for NA applied.
|
|
/// </remarks>
|
|
/// <remarks date="2024-May-16" author="Thomas Wiedebusch">
|
|
/// - Force stop on stop request.
|
|
/// </remarks>
|
|
private void FwUpdateSwStateMachine()
|
|
{
|
|
while (!_processToken.IsCancellationRequested)
|
|
{
|
|
if (_lastProcessState == _processState)
|
|
{
|
|
Thread.Sleep(1);
|
|
// do nothing until state changed
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
_lastProcessState = _processState;
|
|
if (_stopRequest)
|
|
_processState = ProcessState.Stop;
|
|
switch (_processState)
|
|
{
|
|
case ProcessState.Init:
|
|
ResetAllStatusLabels();
|
|
SetDisableDownloadAndEnableConnect();
|
|
_processState = ProcessState.Idle;
|
|
break;
|
|
|
|
case ProcessState.Idle:
|
|
break;
|
|
|
|
case ProcessState.ValidateSoftware:
|
|
ValidateSoftware(ProcessState.Idle);
|
|
break;
|
|
|
|
//[Stop] initiated from user
|
|
case ProcessState.Stop:
|
|
// stop clears all status and ongoing processes
|
|
StopProcesses();
|
|
// do not change status leave process state stop on stop
|
|
break;
|
|
|
|
case ProcessState.EndOfList:
|
|
// The end of a process sequence state list shouldn't be reached, handle it as error
|
|
case ProcessState.Error:
|
|
// Error process has to take care of the next state
|
|
ErrorProcesses();
|
|
break;
|
|
|
|
// [Connect] initiates port scan if NOT already done once
|
|
case ProcessState.PortScan:
|
|
ResetAllStatusLabels();
|
|
PortScanner(GetNextProcessState(_processState, _stateSequencePrepareAndCheckMeter));
|
|
break;
|
|
|
|
// [Connect] initiates initial connect, switches pulse mode off
|
|
case ProcessState.InitialConnect:
|
|
DisposeGenesis();
|
|
_initialConnect = true;
|
|
_afterUpdateConnect = false;
|
|
_recoveryRegistersRequired = false;
|
|
Connect(GetNextProcessState(_processState, _stateSequencePrepareAndCheckMeter));
|
|
break;
|
|
|
|
case ProcessState.InitialReadRegisters:
|
|
ReadRegisters(GetNextProcessState(_processState, _stateSequencePrepareAndCheckMeter));
|
|
break;
|
|
|
|
case ProcessState.InitialReadMeterFiles:
|
|
ReadMeterFiles(GetNextProcessState(_processState, _stateSequencePrepareAndCheckMeter));
|
|
break;
|
|
|
|
case ProcessState.ReadInfoEraseRestoreFiles:
|
|
PrepareInfoMeterFilesEraseRestore(GetNextProcessState(_processState,
|
|
_stateSequencePrepareAndCheckMeter));
|
|
break;
|
|
|
|
case ProcessState.LoadUpdateFiles:
|
|
LoadUpdateFiles(GetNextProcessState(_processState, _stateSequencePrepareAndCheckMeter));
|
|
break;
|
|
|
|
case ProcessState.CheckUpdateRequest:
|
|
// If the FW is up to date (return second state) the meter release can be initiated
|
|
CheckUpdateRequest(ProcessState.CheckUpdateCapability, ProcessState.PrepareMeterRelease);
|
|
break;
|
|
|
|
case ProcessState.CheckUpdateCapability:
|
|
CheckUpdateCapability(GetNextProcessState(_processState, _stateSequenceFwUpdate));
|
|
break;
|
|
|
|
case ProcessState.EraseMeterFiles:
|
|
// even if the erasure failed it should be continued
|
|
MeterFilesErase(GetNextProcessState(_processState, _stateSequenceFwUpdate));
|
|
break;
|
|
|
|
case ProcessState.FirmwareUpdate:
|
|
FwUpdate(GetNextProcessState(_processState, _stateSequenceFwUpdate));
|
|
break;
|
|
|
|
case ProcessState.Reboot:
|
|
_afterUpdateConnect = true;
|
|
Reboot(GetNextProcessState(_processState, _stateSequenceFwUpdate));
|
|
break;
|
|
|
|
case ProcessState.RebootConnect:
|
|
DisposeGenesis();
|
|
Connect(GetNextProcessState(_processState, _stateSequenceFwUpdate));
|
|
break;
|
|
|
|
case ProcessState.RestoreMeterFiles:
|
|
// Restore meter files is necessary after FW-Update procedure as this has erased
|
|
// those files before the update.
|
|
//TODO THW MeterFilesRestore(GetNextProcessState(_processState, _stateSequencePrepareMeterRelease));
|
|
_processState = GetNextProcessState(_processState, _stateSequencePrepareMeterRelease);
|
|
break;
|
|
|
|
case ProcessState.PrepareMeterRelease:
|
|
// The prepare meter release is the entry for the finalizing of maintenance sequence
|
|
// after an error which makes it necessary to release the meter but restore passwords
|
|
// and log all internal meter information for analysis.
|
|
// ATTENTION: Needed to reassign to "ProcessState.RestoreLutFile" to set a valid state
|
|
// of the _stateSequencePrepareMeterRelease beyond the FW-Update entry. This is needed
|
|
// to start the sequence.
|
|
_processState = ProcessState.RestoreLutFile;
|
|
break;
|
|
case ProcessState.RestoreLutFile:
|
|
RestoreLutFile(GetNextProcessState(_processState, _stateSequencePrepareMeterRelease));
|
|
break;
|
|
|
|
case ProcessState.RecoverRegisters:
|
|
RegisterRecovery(GetNextProcessState(_processState, _stateSequencePrepareMeterRelease));
|
|
break;
|
|
|
|
case ProcessState.FinalReadRegisters:
|
|
// Read the registers after update as the definitions may have changed or after recovery
|
|
// has been executed
|
|
ReadRegisters(GetNextProcessState(_processState, _stateSequencePrepareMeterRelease));
|
|
break;
|
|
|
|
case ProcessState.CompareRegisters:
|
|
ReadRegisters(GetNextProcessState(_processState, _stateSequencePrepareMeterRelease));
|
|
break;
|
|
|
|
case ProcessState.RestorePasswordFile:
|
|
RestorePasswordFile(GetNextProcessState(_processState, _stateSequencePrepareMeterRelease));
|
|
break;
|
|
|
|
case ProcessState.FinalConnect:
|
|
_afterUpdateConnect = false;
|
|
Connect(GetNextProcessState(_processState, _stateSequencePrepareMeterRelease));
|
|
break;
|
|
|
|
case ProcessState.VerifyMeterFiles:
|
|
ReadMeterFiles(GetNextProcessState(_processState, _stateSequencePrepareMeterRelease));
|
|
break;
|
|
|
|
case ProcessState.ReadEngineeringLogs:
|
|
//TODO THW ReadEngineeringLogs(ProcessState.CalculateLifeTime, ProcessState.CalculateLifeTime);
|
|
_processState = GetNextProcessState(_processState, _stateSequencePrepareMeterRelease);
|
|
break;
|
|
|
|
case ProcessState.CorrectPowerOverestimation:
|
|
CorrectPowerOverestimation(GetNextProcessState(_processState, _stateSequencePrepareMeterRelease));
|
|
break;
|
|
|
|
case ProcessState.CalculateLifeTime:
|
|
CalculateLifeTime(GetNextProcessState(_processState, _stateSequencePrepareMeterRelease));
|
|
break;
|
|
|
|
case ProcessState.CheckFwUpdateSuccess:
|
|
// If the update request returns false, the FW is up to date,
|
|
// if true it needs an update (update did not succeed) release meter to normal operation to
|
|
// leave this to the user
|
|
CheckUpdateRequest(ProcessState.ReleaseMeterToNormalOperation, ProcessState.FwUpdateSuccess);
|
|
break;
|
|
|
|
case ProcessState.FwUpdateSuccess:
|
|
ReleaseMeterToNormalOperation();
|
|
FwUpdateSuccessMessage();
|
|
_processState = ProcessState.Idle;
|
|
break;
|
|
|
|
case ProcessState.ReleaseMeterToNormalOperation:
|
|
ReleaseMeterToNormalOperation();
|
|
_processState = ProcessState.Idle;
|
|
break;
|
|
|
|
default:
|
|
_processState = ProcessState.Idle;
|
|
break;
|
|
}
|
|
|
|
}
|
|
catch (ThreadAbortException)
|
|
{
|
|
throw;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (_fwUpdateSwThread.ThreadState == ThreadState.Aborted
|
|
|| _fwUpdateSwThread.ThreadState == ThreadState.AbortRequested)
|
|
{
|
|
MessageBoxShow(e.ToString(), Resources.StrMessageWindowFailed, MessageBoxButtons.OK,
|
|
MessageBoxIcon.Error);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
//finally
|
|
//{
|
|
//}
|
|
}
|
|
}// state locked against repeated execution
|
|
}
|
|
#endregion --------------------------------------- State Machine ----------------------------------------------
|
|
|
|
#region ------------------------------------------ Timer Controls ---------------------------------------------
|
|
|
|
/// <summary>
|
|
/// The timer is used to check to update a progress bar for routines not having an event callback handler and
|
|
/// to monitor for hanging communication. If the communication gets stuck this means, that the execution
|
|
/// routine will not fire any event, finally the timer as to interact.
|
|
/// This routine will be executed in the "Form Service FW-Update thread".
|
|
/// </summary>
|
|
/// <remarks date="2020-Nov-30" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
|
|
/// - Timer interval expired introduced.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-02" author="Thomas Wiedebusch">
|
|
/// - Added label waiting for meter response at reboot.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-17" author="Thomas Wiedebusch">
|
|
/// - Added meter file access states.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-02" author="Thomas Wiedebusch">
|
|
/// - Timeout for license information.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-29" author="Thomas Wiedebusch">
|
|
/// - Update to all cyclic progress bar changes.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
|
|
/// - Changed timer to System.Threading.Timer as System.Windows.Forms.Timer does not operate if called as DLL
|
|
/// from the FW-Update Loader and the FW-Update Loader uses System.Windows.Forms.Timer.
|
|
/// - UiInvoker needed as timer now being called from separate thread.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Nov-16" author="Thomas Wiedebusch">
|
|
/// - Added new states.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-16..22" author="Thomas Wiedebusch">
|
|
/// - Added new states.
|
|
/// </remarks>
|
|
private void TmrProgressUpdate_Tick(Object state)
|
|
{
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
|
|
switch (_processState)
|
|
{
|
|
case ProcessState.Idle:
|
|
if (_softwareLicense == null && (_licenseCheckTimer_ms -= TimerInterval_ms) < 0)
|
|
_processState = ProcessState.ValidateSoftware;
|
|
break;
|
|
case ProcessState.EraseMeterFiles:
|
|
case ProcessState.RestoreMeterFiles:
|
|
case ProcessState.InitialReadMeterFiles:
|
|
case ProcessState.VerifyMeterFiles:
|
|
case ProcessState.PreUpdateReadMeterFiles:
|
|
case ProcessState.RestorePasswordFile:
|
|
case ProcessState.RestoreLutFile:
|
|
case ProcessState.ReadEngineeringLogs:
|
|
case ProcessState.InitialConnect:
|
|
case ProcessState.RebootConnect:
|
|
case ProcessState.FinalConnect:
|
|
// These routines do not have an event callback handler
|
|
if (_progressBarValueCounter++ > 100)
|
|
_progressBarValueCounter = 0;
|
|
UiInvoker.ProgressBarInvoker(barOverallProgressUpdate, _progressBarValueCounter);
|
|
break;
|
|
|
|
case ProcessState.FirmwareUpdate:
|
|
var singleProgress = (Int32)(_meterFwUpdate?.SingleFileProcessCtrPercent ?? 0);
|
|
if (_meterFwUpdate?.GetFwUpdateStateOperation() != _lastFwUpdateState)
|
|
{
|
|
_lastFwUpdateState = _meterFwUpdate?.GetFwUpdateStateOperation();
|
|
FillDataGridWithAllInfos();
|
|
UiInvoker.ControlInvoker(lblWaitingForMeterResponse, ColorProcessFailed, visible: false);
|
|
}
|
|
else
|
|
{
|
|
// If the communication gets frozen
|
|
UiInvoker.ControlInvoker(lblWaitingForMeterResponse, ColorProcessFailed,
|
|
Resources.StrWaitForMeterResponse, _processProgressObserver == singleProgress);
|
|
}
|
|
_processProgressObserver = singleProgress;
|
|
break;
|
|
|
|
case ProcessState.Reboot:
|
|
UiInvoker.ControlInvoker(lblWaitingForMeterResponse, ColorProcessFailed,
|
|
Resources.StrWaitForMeterResponse);
|
|
_timerIntervalExpired = true;
|
|
break;
|
|
}
|
|
|
|
if (_timerDisplayOn)
|
|
UpdateTimeDisplay();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Timing measurement to keep user informed about expired time.
|
|
/// </summary>
|
|
/// <remarks date="2020-Nov-30" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
|
|
/// - UiInvoker as timer is now in separate task calling this routine.
|
|
/// </remarks>
|
|
private void UpdateTimeDisplay()
|
|
{
|
|
var time = DateTimeOffset.UtcNow;
|
|
var timeSpan = time - _startTime;
|
|
UiInvoker.ControlInvoker(lblUpdateTime, ColorDefault, $@"{(UInt32)timeSpan.TotalMinutes}:{timeSpan.Seconds:00}");
|
|
}
|
|
#endregion --------------------------------------- Timer Controls ---------------------------------------------
|
|
|
|
#region ------------------------------------------ Interfaces -------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Interface function to receive serial byte stream assigned to a special container.
|
|
/// As a complex field cannot be passed between a dynamic loaded assembly due to
|
|
/// the unknown signature or assembly version, a simply data type will be passed and
|
|
/// then converted to the complex type.
|
|
/// </summary>
|
|
/// <param name="dataContainerId"></param>
|
|
/// <param name="serialJsonStream"></param>
|
|
/// <returns>true if succeeded</returns>
|
|
/// <remarks date="2020-Dec-14" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-22" author="Thomas Wiedebusch">
|
|
/// - Set process state to validate the software if this message comes in.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Jan-14" author="Thomas Wiedebusch">
|
|
/// - Set path to FW-Update SW directory to take all DLLs from there.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-02" author="Thomas Wiedebusch">
|
|
/// - Software license object at incoming license information.
|
|
/// </remarks>
|
|
/// <remarks date="20212-Nov-29" author="Thomas Wiedebusch">
|
|
/// - Single FW-package.
|
|
/// </remarks>
|
|
/// <remarks date="20212-Nov-29" author="Thomas Wiedebusch">
|
|
/// - Single FW-package.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-23" author="Thomas Wiedebusch">
|
|
/// - Added FW update safe information for report
|
|
/// </remarks>
|
|
// ReSharper disable once UnusedMember.Global this cannot be seen here because its
|
|
// called from externally
|
|
public Boolean SetDataContainerJson(Int32 dataContainerId, String serialJsonStream)
|
|
{
|
|
var dataContainerName = (DataContainerName)dataContainerId;
|
|
switch (dataContainerName)
|
|
{
|
|
case DataContainerName.FwUpdateSafeInfo:
|
|
_fwUpdateSafeInfo = new FwUpdateSafeInfo();
|
|
_fwUpdateSafeInfo = JsonConvert.DeserializeObject<FwUpdateSafeInfo>(serialJsonStream);
|
|
return true;
|
|
|
|
case DataContainerName.CordonelDeviceInfo:
|
|
_cordonelDeviceInfos.Add(JsonConvert.DeserializeObject<CordonelDeviceInfo>(serialJsonStream));
|
|
return true;
|
|
|
|
case DataContainerName.SoftwareLicense:
|
|
_softwareLicense = new SoftwareLicense();
|
|
_softwareLicense = JsonConvert.DeserializeObject<SoftwareLicense>(serialJsonStream);
|
|
_processState = ProcessState.ValidateSoftware;
|
|
return true;
|
|
|
|
case DataContainerName.UpdatePackageBasePath:
|
|
_basePath = JsonConvert.DeserializeObject<FwUpdatePaths>(serialJsonStream);
|
|
return true;
|
|
|
|
case DataContainerName.UpdatePackage:
|
|
_cordonelFirmwarePackage = JsonConvert.DeserializeObject<CordonelFirmware>(serialJsonStream);
|
|
return true;
|
|
|
|
default:
|
|
InfoProcessFailed(null, Resources.StrDataContainerUnknown);
|
|
break;
|
|
|
|
}
|
|
return false;
|
|
}
|
|
#endregion --------------------------------------- Interfaces -------------------------------------------------
|
|
|
|
#region ------------------------------------------ Form Load Unload -------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Ctor FW update
|
|
/// </summary>
|
|
/// <remarks date="2021-Mar-17" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
|
|
/// - Changed timer to System.Threading.Timer as System.Windows.Forms.Timer does not operate if called as DLL
|
|
/// from the FW-Update Loader and the FW-Update Loader uses System.Windows.Forms.Timer.
|
|
/// </remarks>
|
|
/// <remarks date="2024-May-13" author="Thomas Wiedebusch">
|
|
/// - Culture info fixed to English.
|
|
/// </remarks>
|
|
public FrmServiceFwUpdateSw()
|
|
{
|
|
_logger = NLogHelper.CreateOrGetLogger("ServiceFwUpdateSw");
|
|
_version = Assembly.GetExecutingAssembly().GetName().Version;
|
|
|
|
// Log version to files for debug purposes
|
|
_logger.Info(StrSeparator);
|
|
_logger.Info($"Service FwUpdateSw Version: {_version.Major}.{_version.Minor}.{_version.Build}");
|
|
_logger.Info(StrSeparator);
|
|
|
|
_meterBatch = new MeterBatch();
|
|
|
|
InitializeComponent();
|
|
|
|
_cultureInfo = Thread.CurrentThread.CurrentCulture;
|
|
radioBtnEnglishLanguage.Checked = true;
|
|
if (_cultureInfo.IetfLanguageTag == "de-DE")
|
|
{
|
|
radioBtnGermanLanguage.Checked = true;
|
|
}
|
|
|
|
// switch always to English
|
|
RadioBtnEnglishLanguage_Click(this, null);
|
|
|
|
SetDesktopLocation(0, 0);
|
|
|
|
lblFwUpdateInfo.Text = $@"Version: {_version.Major}.{_version.Minor}.{_version.Build}";
|
|
|
|
// set locked repeat initially different from process state to unlock first entry to state machine
|
|
_lastProcessState = ProcessState.Unspecified;
|
|
_invokerProcessState = ProcessState.Unspecified;
|
|
_processState = ProcessState.Init;
|
|
|
|
//assign thread to loop and start thread
|
|
_fwUpdateSwThread = new Thread(FwUpdateSwStateMachine);
|
|
|
|
ThreadWatcher.Instance.Start(_fwUpdateSwThread);
|
|
|
|
_timerDisplayOn = false;
|
|
|
|
_tmrProgressUpdate = new System.Threading.Timer(TmrProgressUpdate_Tick, null, 200, 200);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Logout from Genesis and dispose it to force a new read of registers.
|
|
/// </summary>
|
|
/// <remarks date="2020-Nov-30" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2022-Apr-28" author="Thomas Wiedebusch">
|
|
/// - Release display to normal operation,
|
|
/// - switch measurement LED off.
|
|
/// </remarks>
|
|
/// <remarks date="2022-Mai-10" author="Thomas Wiedebusch">
|
|
/// - Release display removed.
|
|
/// </remarks>
|
|
private void DisposeGenesis()
|
|
{
|
|
_currentGenesis?.Logout();
|
|
// Batch removal disposes the meter!
|
|
_meterBatch?.RemoveAllMeters();
|
|
_currentGenesis = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset all status labels, clear data table and set genesis to not connected.
|
|
/// </summary>
|
|
/// <remarks date="2020-Dec-08" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-23" author="Thomas Wiedebusch">
|
|
/// - Cleaned for thread safe access to labels and controls.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Jan-05" author="Thomas Wiedebusch">
|
|
/// - Removed clear history.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-18" author="Thomas Wiedebusch">
|
|
/// - Split label reset to pre-update and post-update to keep the pre-update information.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-02" author="Thomas Wiedebusch">
|
|
/// - Removed button settings.
|
|
/// </remarks>
|
|
/// <remarks date="2024-May-13" author="Thomas Wiedebusch">
|
|
/// - Disabled language selection, culture info fixed to english.
|
|
/// </remarks>
|
|
private void ResetAllStatusLabels()
|
|
{
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
|
|
InfoStatusUnknown(lblCordonelDetection, Resources.StrCordonelDetectUnspecified);
|
|
InfoStatusUnknown(lblCordonelAuthentication, Resources.StrCordonelAuthenticationUnspecified);
|
|
UiInvoker.ControlInvoker(lblConnectPcb, ColorProcessFailed, Resources.StrNotConnected);
|
|
UiInvoker.ControlViewInvoker(lblProductToInstall, false);
|
|
|
|
UiInvoker.ControlEnableInvoker(grpLanguageSelection, false);
|
|
|
|
InfoStatusUnknown(lblUpdateInformationStatus, Resources.StrUpdateInformationStatusUnspecified);
|
|
InfoStatusUnknown(lblCordonelUpdateCapability, Resources.StrCordonelUpdateCapabilityUnspecified);
|
|
InfoStatusUnknown(lblRegisterReadout, Resources.StrRegisterReadoutUnspecified);
|
|
InfoStatusUnknown(lblFileErase, Resources.StrFileReadoutUnspecified);
|
|
InfoStatusUnknown(lblFirmwareUpdate, Resources.StrFirmwareUpdateUnspecified);
|
|
InfoStatusUnknown(lblRegisterRestore, Resources.StrRegisterRestoreUnspecified);
|
|
InfoStatusUnknown(lblFileRestore, Resources.StrFileRestoreUnspecified);
|
|
InfoStatusUnknown(lblPasswordFileCheck, Resources.StrPasswordFileRestoreUnspecified);
|
|
InfoStatusUnknown(lblLutFileCheck, Resources.StrLutFileRestoreUnspecified);
|
|
|
|
SetOverallProgressDisplayOff();
|
|
SetActualProgressDisplayOff();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clear history window.
|
|
/// </summary>
|
|
/// <remarks date="2021-Jan-05" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void ClearHistoryWindow()
|
|
{
|
|
if (_frmHistory != null && _frmHistory.rtbHistory.InvokeRequired)
|
|
{
|
|
_frmHistory.rtbHistory.Invoke(new Action(() => { _frmHistory.rtbHistory.Clear(); }));
|
|
}
|
|
else
|
|
_frmHistory?.rtbHistory.Clear();
|
|
|
|
}
|
|
|
|
private void FrmFwUpdate_Load(Object sender, EventArgs e)
|
|
{
|
|
_styleInstalled.BackColor = Color.White;
|
|
_styleInstalled.ForeColor = Color.Black;
|
|
|
|
_styleNotInstalled.BackColor = Color.LightGray;
|
|
_styleNotInstalled.ForeColor = Color.Black;
|
|
|
|
_styleInvalidMeterCrc.BackColor = Color.White;
|
|
_styleInvalidMeterCrc.ForeColor = Color.Purple;
|
|
|
|
_styleUpdateRequired.BackColor = Color.White;
|
|
_styleUpdateRequired.ForeColor = Color.Red;
|
|
|
|
_styleEraseRequired.BackColor = Color.LightGray;
|
|
_styleEraseRequired.ForeColor = Color.Black;
|
|
|
|
_styleDownloadOngoing.BackColor = Color.GreenYellow;
|
|
_styleDownloadOngoing.ForeColor = Color.Black;
|
|
|
|
_styleValidated.BackColor = Color.White;
|
|
_styleValidated.ForeColor = Color.Green;
|
|
|
|
_styleDownloadSucceeded.BackColor = Color.LightGreen;
|
|
_styleDownloadSucceeded.ForeColor = Color.Green;
|
|
|
|
_styleDownloadFailed.BackColor = Color.LightPink;
|
|
_styleDownloadFailed.ForeColor = Color.Red;
|
|
|
|
_styleCommunicationError.BackColor = Color.LightPink;
|
|
_styleCommunicationError.ForeColor = Color.Red;
|
|
|
|
_styleVerificationRequired.BackColor = Color.LightGoldenrodYellow;
|
|
_styleVerificationRequired.ForeColor = Color.Green;
|
|
|
|
_resetTimeMeasurement = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finalize update and clean up. Bring the display back to normal operation and switch the LED off.
|
|
/// </summary>
|
|
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
|
|
/// - Added LED off.
|
|
/// </remarks>
|
|
/// <remarks date="2022-Apr-28" author="Thomas Wiedebusch">
|
|
/// - Login before display back to normal operation.
|
|
/// </remarks>
|
|
/// <remarks date="2022-Mai-10" author="Thomas Wiedebusch">
|
|
/// - Release display removed.
|
|
/// </remarks>
|
|
private void FrmFwUpdate_FormClosing(Object sender, FormClosingEventArgs e)
|
|
{
|
|
|
|
_frmHistory?.Close();
|
|
_processToken?.Cancel();
|
|
// this additionally disposes all meters
|
|
_meterBatch?.RemoveAllMeters();
|
|
_meterBatch?.Dispose();
|
|
_meterFwUpdate?.Dispose();
|
|
_meterFwUpdate = null;
|
|
_registerRestorer = null;
|
|
_tmrProgressUpdate?.Dispose();
|
|
|
|
}
|
|
#endregion --------------------------------------- Form Load Unload -------------------------------------------
|
|
|
|
#region ------------------------------------------ Data Grid Controls -----------------------------------------
|
|
/// <summary>
|
|
/// Build data grid and fill it with meter and file information,
|
|
/// compare version and CRC of meter and file
|
|
/// </summary>
|
|
private void FillDataGridWithAllInfos()
|
|
{
|
|
if (_currentGenesis == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Invoke(new Action(() =>
|
|
{
|
|
_dataTable.Rows.Clear();
|
|
_dataTable.Columns.Clear();
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
|
|
_dataTable.Columns.Add(Resources.StrTableAppName, typeof(String));
|
|
_dataTable.Columns.Add(Resources.StrTableAppId, typeof(String));
|
|
_dataTable.Columns.Add(Resources.StrTableMeterVersion, typeof(String));
|
|
_dataTable.Columns.Add(Resources.StrTableMeterCrc, typeof(String));
|
|
_dataTable.Columns.Add(Resources.StrTableFileVersion, typeof(String));
|
|
_dataTable.Columns.Add(Resources.StrTableFileCrc, typeof(String));
|
|
_dataTable.Columns.Add(Resources.StrTableFileSize, typeof(String));
|
|
_dataTable.Columns.Add(Resources.StrTableStatus, typeof(String));
|
|
|
|
foreach (var meterApp in _currentGenesis.MeterAppListVersion)
|
|
{
|
|
var row = _dataTable.NewRow();
|
|
//application information read from configuration
|
|
row[Resources.StrTableAppName] = meterApp.AppName;
|
|
row[Resources.StrTableAppId] = MeterFwUpdate.ConvertAppIdToString(meterApp.AppId);
|
|
|
|
_dataTable.Rows.Add(row);
|
|
}
|
|
|
|
//output data to data grid view
|
|
gridViewApplic.DataSource = _dataTable.DefaultView;
|
|
|
|
foreach (DataRow dataRow in _dataTable.Rows)
|
|
{
|
|
DisplayFileAppInfo(dataRow);
|
|
DisplayMeterAppInfo(dataRow);
|
|
}
|
|
|
|
foreach (DataGridViewColumn column in gridViewApplic.Columns)
|
|
{
|
|
column.SortMode = DataGridViewColumnSortMode.NotSortable;
|
|
}
|
|
|
|
//color the results of the compare
|
|
UpdateInformationStyleSet();
|
|
}));
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set meter application information to data grid view
|
|
/// </summary>
|
|
/// <param name="dataRow"></param>
|
|
private void DisplayMeterAppInfo(DataRow dataRow)
|
|
{
|
|
if (_currentGenesis == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var meterStateAppInfo = new MeterAppStateInfo();
|
|
|
|
foreach (var meterApp in _currentGenesis.MeterAppListVersion)
|
|
{
|
|
var appId = dataRow[Resources.StrTableAppId].ToString();
|
|
if (appId != MeterFwUpdate.ConvertAppIdToString(meterApp.AppId))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
dataRow[Resources.StrTableStatus] = meterStateAppInfo.GetTextFromState(meterApp.Status);
|
|
dataRow[Resources.StrTableMeterVersion] = meterApp.StrVersion;
|
|
dataRow[Resources.StrTableMeterCrc] = meterApp.IsInstalled ?
|
|
MeterFwUpdate.ConvertCrcToString(meterApp.Crc) : "";
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set file application information to data grid view.
|
|
/// </summary>
|
|
/// <param name="dataRow"></param>
|
|
private void DisplayFileAppInfo(DataRow dataRow)
|
|
{
|
|
if (_currentGenesis == null || _meterFwUpdate?.FileApps == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (var meterApp in _currentGenesis.MeterAppListVersion)
|
|
{
|
|
var appId = dataRow[Resources.StrTableAppId].ToString();
|
|
if (appId != MeterFwUpdate.ConvertAppIdToString(meterApp.AppId))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach (var fileApp in _meterFwUpdate.FileApps.Where(fileApp => meterApp.AppId == fileApp.AppId))
|
|
{
|
|
dataRow[Resources.StrTableFileVersion] = fileApp.StrVersion;
|
|
dataRow[Resources.StrTableFileCrc] = MeterFwUpdate.ConvertCrcToString(fileApp.Crc);
|
|
dataRow[Resources.StrTableFileSize] = $"{(Double)fileApp.BinData.Count / 1024:0.000}";
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Display the update information
|
|
/// </summary>
|
|
/// <remarks date="2021-Jun-30" author="Thomas Wiedebusch">
|
|
/// - MeterAppState.FileAppInvalid changed from _styleDownloadOngoing to _styleNotInstalled
|
|
/// </remarks>
|
|
/// <remarks date="2024-Apr-23" author="Thomas Wiedebusch">
|
|
/// - New style for communication error
|
|
/// </remarks>
|
|
private void UpdateInformationStyleSet()
|
|
{
|
|
foreach (DataGridViewRow dataGridRow in gridViewApplic.Rows)
|
|
{
|
|
var testString = dataGridRow.Cells[Resources.StrTableStatus].Value.ToString();
|
|
var meterAppStateInfo = new MeterAppStateInfo();
|
|
var meterAppState = meterAppStateInfo.GetStateFromText(testString);
|
|
|
|
switch (meterAppState)
|
|
{
|
|
case MeterAppState.MeterAppInstalledUnchecked:
|
|
dataGridRow.DefaultCellStyle = _styleInstalled;
|
|
break;
|
|
case MeterAppState.MeterAppUpToDate:
|
|
dataGridRow.DefaultCellStyle = _styleValidated;
|
|
break;
|
|
case MeterAppState.MeterAppInstallationRequired:
|
|
dataGridRow.DefaultCellStyle = _styleUpdateRequired;
|
|
break;
|
|
case MeterAppState.FileAppInvalid:
|
|
dataGridRow.DefaultCellStyle = _styleNotInstalled;
|
|
break;
|
|
case MeterAppState.MeterAppVersionOutdated:
|
|
dataGridRow.DefaultCellStyle = _styleUpdateRequired;
|
|
break;
|
|
case MeterAppState.Unknown:
|
|
dataGridRow.DefaultCellStyle = _styleCommunicationError;
|
|
break;
|
|
case MeterAppState.MeterAppNotInstalled:
|
|
dataGridRow.DefaultCellStyle = _styleNotInstalled;
|
|
break;
|
|
case MeterAppState.MeterAppNotRequired:
|
|
dataGridRow.DefaultCellStyle = _styleNotInstalled;
|
|
break;
|
|
case MeterAppState.InvalidCrc:
|
|
dataGridRow.DefaultCellStyle = _styleInvalidMeterCrc;
|
|
break;
|
|
case MeterAppState.MeterAppErasureRequired:
|
|
dataGridRow.DefaultCellStyle = _styleEraseRequired;
|
|
break;
|
|
case MeterAppState.MeterAppDownloadSucceeded:
|
|
dataGridRow.DefaultCellStyle = _styleDownloadSucceeded;
|
|
break;
|
|
case MeterAppState.MeterAppDownloadSuspicious:
|
|
dataGridRow.DefaultCellStyle = _styleVerificationRequired;
|
|
break;
|
|
case MeterAppState.MeterAppSuccessfulErased:
|
|
dataGridRow.DefaultCellStyle = _styleDownloadSucceeded;
|
|
break;
|
|
case MeterAppState.MeterAppSuccessfulUpdated:
|
|
dataGridRow.DefaultCellStyle = _styleDownloadSucceeded;
|
|
break;
|
|
case MeterAppState.MeterAppDownloadFailed:
|
|
dataGridRow.DefaultCellStyle = _styleDownloadFailed;
|
|
break;
|
|
case MeterAppState.MeterAppDownloadActive:
|
|
dataGridRow.DefaultCellStyle = _styleDownloadOngoing;
|
|
break;
|
|
default:
|
|
dataGridRow.DefaultCellStyle = _styleNotInstalled;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endregion --------------------------------------- Data Grid Controls -----------------------------------------
|
|
|
|
#region ------------------------------------------ Checks -----------------------------------------------------
|
|
/// <summary>
|
|
/// Loading update package ABC and description file ADF.
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <remarks date="2020-Dec-14" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-21" author="Thomas Wiedebusch">
|
|
/// - Error messages exported.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Feb-18" author="Thomas Wiedebusch">
|
|
/// - Removed information like region and size of fw update package from name of package,
|
|
/// - Added check for null of FwPackage info, binary content and description file.
|
|
/// </remarks>
|
|
/// <remarks date="2024-Apr-24" author="Thomas Wiedebusch">
|
|
/// - Update app installation status.
|
|
/// </remarks>
|
|
private void LoadUpdateFiles(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error)
|
|
{
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
if (_meterFwUpdate == null)
|
|
_meterFwUpdate = new MeterFwUpdate(_currentGenesis);
|
|
_binaryPackageLoaded = false;
|
|
|
|
try
|
|
{
|
|
if (_cordonelDeviceInfos == null
|
|
|| _cordonelFirmwarePackage?.FwPackageInfo == null
|
|
|| _cordonelFirmwarePackage.BinaryApplicationFiles == null
|
|
|| _cordonelFirmwarePackage.BinaryApplicationFiles.Count == 0
|
|
|| _cordonelFirmwarePackage.PackageDescriptionFile == null)
|
|
{
|
|
InfoProcessFailed(lblUpdateInformationStatus, Resources.StrPackageFileInvalid);
|
|
_processState = errorExitState;
|
|
return;
|
|
}
|
|
foreach (var device in _cordonelDeviceInfos.Where(device =>
|
|
_currentGenesis.PcbId == device.PcbId))
|
|
{
|
|
|
|
if (_cordonelFirmwarePackage.FwPackageInfo.Name == device.RequiredFwRelease)
|
|
{
|
|
// here the firmware package is valid, encode content
|
|
var encoding = new ASCIIEncoding();
|
|
var packageFile =
|
|
encoding.GetString(_cordonelFirmwarePackage.PackageDescriptionFile.FileContent.ToArray());
|
|
|
|
// setup package description file in FwUpdate class
|
|
if (!_meterFwUpdate.LoadPackageFileFromText(packageFile))
|
|
{
|
|
InfoProcessFailed(lblUpdateInformationStatus, Resources.StrPackageFileInvalid);
|
|
_processState = errorExitState;
|
|
return;
|
|
}
|
|
|
|
var fileApps = new List<FileApplications>();
|
|
|
|
foreach (var app in _cordonelFirmwarePackage.BinaryApplicationFiles.Where(f => f.FileName.EndsWith(".bin")))
|
|
{
|
|
var fileApplication = new FileApplications(app.FileName)
|
|
{
|
|
BinData = new List<Byte>(app.FileContent.ToList())
|
|
};
|
|
|
|
fileApps.Add(fileApplication);
|
|
}
|
|
|
|
if (_meterFwUpdate.LoadFileApps(fileApps))
|
|
{
|
|
// Check status of installed applications
|
|
_meterFwUpdate.AssignGenesis(_currentGenesis);
|
|
_meterFwUpdate.CompareAllMeterAndFileApps();
|
|
FillDataGridWithAllInfos();
|
|
|
|
_binaryPackageLoaded = true;
|
|
_processState = successExitState;
|
|
break;
|
|
}
|
|
InfoProcessFailed(lblUpdateInformationStatus, Resources.StrUpdateInformationStatusFailed);
|
|
_processState = errorExitState;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
InfoProcessFailed(lblUpdateInformationStatus, Resources.StrUpdateInformationStatusFailed);
|
|
_processState = errorExitState;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate the version and the expiration date of the software.
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <remarks date="2020-Dec-22" author="Thomas Wiedebusch">
|
|
/// - Initial based on previous implementation of CheckUpdateEnabled().
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-09" author="Thomas Wiedebusch">
|
|
/// - Duty Date implemented.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-02" author="Thomas Wiedebusch">
|
|
/// - Enable [Connect] and [Port Scan] if license is validated.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-22" author="Thomas Wiedebusch">
|
|
/// - Software license element naming adapted to DB content.
|
|
/// </remarks>
|
|
private void ValidateSoftware(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error)
|
|
{
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
if (_version == null || _softwareLicense?.Program == null)
|
|
{
|
|
_processState = errorExitState;
|
|
return;
|
|
}
|
|
|
|
var programName = Assembly.GetExecutingAssembly().GetName().Name;
|
|
if (_version.Major == _softwareLicense.Major &&
|
|
_version.Minor == _softwareLicense.Minor &&
|
|
_version.Build == _softwareLicense.Build &&
|
|
_softwareLicense.Program == programName &&
|
|
DateTime.Compare(_softwareLicense.ValidTo, DateTime.Now) >= 1)
|
|
{
|
|
_processState = successExitState;
|
|
SetDisableDownloadAndEnableConnect();
|
|
return;
|
|
}
|
|
|
|
_processState = errorExitState;
|
|
}
|
|
|
|
/// <summary>
|
|
/// State call to check the update capability.
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <remarks date="2020-Dec-21" author="Thomas Wiedebusch">
|
|
/// - Initial based on previous implementation of CheckUpdateEnabled().
|
|
/// </remarks>
|
|
/// <remarks date="2021-Jan-14" author="Thomas Wiedebusch">
|
|
/// - Check metrology update capability.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-26" author="Thomas Wiedebusch">
|
|
/// - Check region radio.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-16" author="Thomas Wiedebusch">
|
|
/// - Exported CheckPackageFileAndFileApps to CheckUpdateRequest to separate capability check from
|
|
/// applications are up-to-date.
|
|
/// </remarks>
|
|
private void CheckUpdateCapability(ProcessState successExitState,
|
|
ProcessState errorExitState = ProcessState.Error)
|
|
{
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
Invoke(new Action(() =>
|
|
{
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
if (!CoreRevisionValid())
|
|
{
|
|
_processState = errorExitState;
|
|
// overwrite the invoker state to create a message
|
|
_invokerProcessState = ProcessState.CheckCoreRevision;
|
|
return;
|
|
}
|
|
if (!MetrologyUpdateCapability())
|
|
{
|
|
_processState = errorExitState;
|
|
// overwrite the invoker state to create a message
|
|
_invokerProcessState = ProcessState.CheckMetrology;
|
|
return;
|
|
}
|
|
if (!RegionRadioUpdateCapability())
|
|
{
|
|
_processState = errorExitState;
|
|
// overwrite the invoker state to create a message
|
|
_invokerProcessState = ProcessState.CheckRegionRadio;
|
|
return;
|
|
}
|
|
|
|
if (!MeterSizeUpdateCapability())
|
|
{
|
|
_processState = errorExitState;
|
|
// overwrite the invoker state to create a message
|
|
_invokerProcessState = ProcessState.CheckMeterSize;
|
|
return;
|
|
}
|
|
|
|
// update capability is verified as good!
|
|
InfoProcessSuccess(lblCordonelUpdateCapability, Resources.StrCordonelUpdateCapabilitySucceeded);
|
|
_processState = successExitState;
|
|
}));
|
|
|
|
SetControlDownloadEnable();
|
|
}
|
|
|
|
/// <summary>
|
|
/// State call to check for FW-Update request.
|
|
/// </summary>
|
|
/// <param name="successExitState">update is needed</param>
|
|
/// <param name="breakExitState">skip update</param>
|
|
/// <param name="errorExitState">incompatible update in safe</param>
|
|
/// <remarks date="2020-Dec-21" author="Thomas Wiedebusch">
|
|
/// - Initial based on previous implementation of CheckUpdateEnabled().
|
|
/// </remarks>
|
|
/// <remarks date="2024-Apr-23..24" author="Thomas Wiedebusch">
|
|
/// - Check for communication issues to avoid wrong information on installed apps.
|
|
/// </remarks>
|
|
private void CheckUpdateRequest(ProcessState successExitState, ProcessState breakExitState,
|
|
ProcessState errorExitState = ProcessState.Error)
|
|
{
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
Invoke(new Action(() =>
|
|
{
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
|
|
// check if apps are marked as status unknown due to communication error
|
|
var allAppsDetected = AllAppsDetected();
|
|
|
|
if (_invokerProcessState == ProcessState.CheckUpdateRequest)
|
|
{
|
|
if (!CheckPackageFileAndFileApps())
|
|
{
|
|
_processState = errorExitState;
|
|
// overwrite the invoker state to create a message
|
|
_invokerProcessState = ProcessState.CheckUpdateFiles;
|
|
return;
|
|
}
|
|
|
|
if (!allAppsDetected)
|
|
{
|
|
_processState = errorExitState;
|
|
// overwrite the invoker state to create a message
|
|
_invokerProcessState = ProcessState.CheckCommunication;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (allAppsDetected)
|
|
{
|
|
if (!_currentGenesis.MeterAppListVersion.Any(f => f.Update) &&
|
|
!_currentGenesis.MeterAppListVersion.Any(f => f.Erase))
|
|
{
|
|
// Nothing has to be updated, the latest FW is installed from program handler
|
|
// it stops operation with break for FW-Update
|
|
_processState = breakExitState;
|
|
}
|
|
else
|
|
{
|
|
// an update needs to be executed
|
|
_processState = successExitState;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Communication error and therefore unknown installation state
|
|
_processState = errorExitState;
|
|
}
|
|
}));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Correct power overestimation at FW change from
|
|
/// <see cref="PowCorrConst.EmeaFwThresholdForPowCorr"/>,
|
|
/// <see cref="PowCorrConst.NaFNaFwThresholdForPowCorr"/>.
|
|
/// Precondition:
|
|
/// Meter registers have to be read in advance,
|
|
/// Meter files have to be read in advance.
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <remarks date="2024-May-13..17" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void CorrectPowerOverestimation(ProcessState successExitState,
|
|
ProcessState errorExitState = ProcessState.Error)
|
|
{
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
if (_currentGenesis?.InstalledFwVersion == null ||
|
|
_cordonelFirmwarePackage?.FwPackageInfo?.Version == null)
|
|
{
|
|
InfoProcessFailed(null, Resources.StrPowerCorrectionFailed);
|
|
_processState = errorExitState;
|
|
return;
|
|
}
|
|
|
|
var powCorrFile = new MeterPowerCorrectionFile(_currentGenesis);
|
|
// CASE 1: Power correction has been executed in previous run:
|
|
// Check if file "1\\powcorr" is in meter
|
|
if (_readMeterFiles.Any(f => f.Contains(MeterPowerCorrectionFile.StrMeterPowCorrFileName)))
|
|
{
|
|
// read the powCorrFile and build a string list of the file content
|
|
powCorrFile.ReadPowCorrMeterFile();
|
|
LogStringList(Resources.StrPowerCorrection, powCorrFile.FileContent,
|
|
Resources.StrPowerCorrectionNotNeeded);
|
|
_processState = successExitState;
|
|
return;
|
|
}
|
|
|
|
// CASE 2: Power correction needed due to update of FW to corrected version regarding the
|
|
// power calculation:
|
|
// Check required versus installed FW versions and threshold version for power correction
|
|
// using the preset _fwUpdatePowerCorrection.ActualFwVersion on initial connect as after
|
|
// the FW update this is lost in _currentGenesis.InstalledFwVersion and overwritten by the
|
|
// new version after the FW update!
|
|
// REMIND: Update only allowed if the actual FW version is higher or equal to threshold (Update succeeded)
|
|
if ((_currentGenesis.Region == "EMEA" &&
|
|
_cordonelFirmwarePackage.FwPackageInfo.Version >= PowCorrConst.EmeaFwThresholdForPowCorr &&
|
|
_fwUpdatePowerCorrection.ActualFwVersion < PowCorrConst.EmeaFwThresholdForPowCorr &&
|
|
_currentGenesis.InstalledFwVersion >= PowCorrConst.EmeaFwThresholdForPowCorr) ||
|
|
(_currentGenesis.Region == "NA" &&
|
|
_cordonelFirmwarePackage.FwPackageInfo.Version >= PowCorrConst.NaFNaFwThresholdForPowCorr &&
|
|
_fwUpdatePowerCorrection.ActualFwVersion < PowCorrConst.NaFNaFwThresholdForPowCorr &&
|
|
_currentGenesis.InstalledFwVersion >= PowCorrConst.NaFNaFwThresholdForPowCorr))
|
|
{
|
|
// CASE 2.1: Missing power correction settings from FW update package
|
|
// preset meter power correction file
|
|
_fwUpdatePowerCorrection.RequiredFwVersion = (UInt32?)_cordonelFirmwarePackage.FwPackageInfo.Version;
|
|
|
|
// estimate the power correction values which includes the pulse adapter installation marker and
|
|
// the readout of Cordonel registers to get the actual status
|
|
var powCorrCalc = new PowerCorrectionCalc(_currentGenesis, _fwUpdatePowerCorrection);
|
|
var retVal = powCorrCalc.PowerCorrEstimation();
|
|
if (retVal)
|
|
{
|
|
// pulse mode was switched off, skip power correction
|
|
if (!_fwUpdatePowerCorrection.PulseAdapterMarkedAsInstalled )
|
|
{
|
|
InfoProcessSuccess(null, Resources.StrPowerCorrectionNotNeeded);
|
|
_processState = successExitState;
|
|
return;
|
|
}
|
|
|
|
// correct the total used charge in meter including store configuration
|
|
retVal = powCorrCalc.CorrectPowerConsumption();
|
|
|
|
if (retVal)
|
|
{
|
|
// store results to "1\\powcorr" in the meter
|
|
powCorrFile.WritePowCorrMeterFile(_fwUpdatePowerCorrection);
|
|
}
|
|
}
|
|
|
|
// one step of the power correction execution failed
|
|
if (!retVal)
|
|
{
|
|
InfoProcessFailed(null, Resources.StrPowerCorrectionFailed);
|
|
_processState = errorExitState;
|
|
return;
|
|
}
|
|
}// check for region and required FW version threshold
|
|
else // power correction mustn't be performed as the jump in version is not given
|
|
{
|
|
InfoProcessSuccess(null, Resources.StrPowerCorrectionNotNeeded);
|
|
_processState = successExitState;
|
|
return;
|
|
}
|
|
|
|
// log result of successfully executed power correction
|
|
LogStringList(Resources.StrPowerCorrection, powCorrFile.FileContent,
|
|
Resources.StrPowerCorrectionSucceeded);
|
|
_processState = successExitState;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculate the consumed power and remaining life time.
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <remarks date="2023-Nov-21" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void CalculateLifeTime(ProcessState successExitState,
|
|
ProcessState errorExitState = ProcessState.Error)
|
|
{
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
var status = new GenesisStatus();
|
|
|
|
if (GenesisStatusHandler.BuildLifeTimeInformation(_currentGenesis, status))
|
|
{
|
|
var lifeTimeInfo = new List<String>
|
|
{
|
|
$"{Resources.StrLifeTimeDrainedBattery} {status.DrainedBatteryLoadPercent:F2} %",
|
|
$"{Resources.StrLifeTimeRemainingYears} {status.RemainingLifeTimeYears:F2} " +
|
|
$"{Resources.StrLifeTimeYears}"
|
|
};
|
|
|
|
LogStringList(Resources.StrLifeTimeCalculation, lifeTimeInfo, Resources.StrLifeTimeCalculationSucceeded);
|
|
_processState = successExitState;
|
|
}
|
|
else
|
|
{
|
|
InfoProcessFailed(null, Resources.StrLifeTimeCalculationFailed);
|
|
_processState = errorExitState;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Core revision check
|
|
/// </summary>
|
|
/// <returns>true if core revision is valid</returns>
|
|
/// <remarks date="2020-Nov-30" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Jan-14" author="Thomas Wiedebusch">
|
|
/// - Moved success message to caller.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-15" author="Thomas Wiedebusch">
|
|
/// - Success message implemented.
|
|
/// </remarks>
|
|
/// <remarks date="2022-Dec-05" author="Thomas Wiedebusch">
|
|
/// - Core revision data type changed from string ti int32?.
|
|
/// </remarks>
|
|
private Boolean CoreRevisionValid()
|
|
{
|
|
// check the update capability
|
|
if (MeterFwUpdateCapability.CheckCoreRevision(_meterFwUpdate, _currentGenesis.CoreRevision))
|
|
{
|
|
InfoProcessSuccess(null, Resources.StrCordonelUpdateCapabilityCoreSucceeded);
|
|
return true;
|
|
}
|
|
|
|
// set information of required and detected versions, check failed!
|
|
var coreLineList = new List<String>
|
|
{
|
|
@"Minimum: " + _meterFwUpdate.CoreRevisionMinimum / 100 + "." +
|
|
_meterFwUpdate.CoreRevisionMinimum % 100,
|
|
@"Maximum: " + _meterFwUpdate.CoreRevisionMaximum / 100 + "." + _meterFwUpdate.CoreRevisionMaximum % 100,
|
|
@"Cordonel: " + _currentGenesis.StrCoreRevision
|
|
};
|
|
|
|
LogStringList(Resources.StrCoreRevision, coreLineList,
|
|
errorMessage: Resources.StrCordonelUpdateCapabilityCoreFailed);
|
|
|
|
InfoProcessFailed(lblCordonelUpdateCapability, Resources.StrCordonelUpdateCapabilityCoreFailed, false);
|
|
_processState = ProcessState.Error;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Metrology update capability check
|
|
/// </summary>
|
|
/// <returns>true if core revision is valid</returns>
|
|
/// <remarks date="2021-Jan-14" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-15" author="Thomas Wiedebusch">
|
|
/// - Success message implemented.
|
|
/// </remarks>
|
|
private Boolean MetrologyUpdateCapability()
|
|
{
|
|
// check the update capability
|
|
if (MeterFwUpdateCapability.CheckMetrology(_meterFwUpdate, _currentGenesis))
|
|
{
|
|
InfoProcessSuccess(null, Resources.StrCordonelUpdateCapabilityMetrologySucceeded);
|
|
return true;
|
|
}
|
|
InfoProcessFailed(lblCordonelUpdateCapability, Resources.StrCordonelUpdateCapabilityMetrologyFailed);
|
|
_processState = ProcessState.Error;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check that during detection every request returns a valid communication to avoid unpredictable information
|
|
/// of installed applications, on missing communication the installation status is undefined!
|
|
/// </summary>
|
|
/// <returns>true if during app detection every message returns a response</returns>
|
|
/// <remarks date="2024-Apr-23" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private Boolean AllAppsDetected()
|
|
{
|
|
// check the update capability
|
|
if (_currentGenesis.MeterAppListVersion.All(f => f.Status != MeterAppState.Unknown))
|
|
return true;
|
|
|
|
InfoProcessFailed(lblCordonelUpdateCapability, Resources.StrCordonelCommunicationFailed);
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Region and radio update capability check:
|
|
/// - Region (NA, EMEA, China),
|
|
/// - RadioFrequencyMhz (null, 433 MHz, 868 MHz),
|
|
/// If any information cannot be read from the meter, it will be assumed, that the FwUpdateBuilder operator
|
|
/// assembled a correct FwUpdateSafe. Otherwise old devices may be unable to update.
|
|
/// The FwPackage has to be checked in advance before calling this routine, as all these information is needed
|
|
/// for this update capability check.
|
|
/// </summary>
|
|
/// <returns>true all checks are approved and valid</returns>
|
|
/// <remarks date="2021-Apr-26" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2022-Apr-27" author="Thomas Wiedebusch">
|
|
/// - Starting with FW 1.2.* all frequencies will be supported, removed frequency check,
|
|
/// - Supported China as new region.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Feb-18" author="Thomas Wiedebusch">
|
|
/// - Region, RadioFrequencyMhz, MeterSize taken from fw package info instead of package name,
|
|
/// - Meter sizes list taken into account.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Feb-18" author="Thomas Wiedebusch">
|
|
/// - Meter sizes check exported to separate routine.
|
|
/// </remarks>
|
|
private Boolean RegionRadioUpdateCapability()
|
|
{
|
|
var retVal = true;
|
|
|
|
// extract the package information
|
|
var fwPackRegion = _cordonelFirmwarePackage.FwPackageInfo.Region;
|
|
var fwPackRadioFrequencyMhz = _cordonelFirmwarePackage.FwPackageInfo.RadioFrequencyMhz;
|
|
|
|
// region
|
|
if (!string.IsNullOrEmpty(fwPackRegion))
|
|
{
|
|
// accept region if identical, on undefined installed region or support for all regions
|
|
if (_currentGenesis.Region == fwPackRegion
|
|
|| Constants.StrWildcard == fwPackRegion
|
|
|| Constants.StrUnknown == _currentGenesis.Region)
|
|
{
|
|
InfoProcessSuccess(null, Resources.StrCordonelUpdateCapabilityRegionSucceeded);
|
|
}
|
|
else
|
|
{
|
|
retVal = false;
|
|
// set information of required and detected regions radio, check failed!
|
|
var lineList = new List<String>
|
|
{
|
|
$"{Resources.StrFwPackageInfo} {fwPackRegion}",
|
|
$@"Cordonel: {_currentGenesis.Region}"
|
|
};
|
|
LogStringList(Resources.StrMessageSystemRegion, lineList,
|
|
errorMessage: Resources.StrCordonelUpdateCapabilityRegionFailed);
|
|
InfoProcessFailed(lblCordonelUpdateCapability, Resources.StrCordonelUpdateCapabilityRegionFailed, false);
|
|
}
|
|
}
|
|
|
|
// if radio frequency is null the region is "NA" or the fw package serves all frequencies,
|
|
// if the radio frequency is explicit set, it has to be verified against the preset value in the Cordonel
|
|
if (fwPackRadioFrequencyMhz == null || fwPackRadioFrequencyMhz == _currentGenesis.RadioFrequencyMhz
|
|
|| _currentGenesis.RadioFrequencyMhz == null)
|
|
{
|
|
InfoProcessSuccess(null, Resources.StrCordonelUpdateCapabilityRadioSucceeded);
|
|
}
|
|
else
|
|
{
|
|
retVal = false;
|
|
// set information of required and detected radio frequency check failed!
|
|
var lineList = new List<String>
|
|
{
|
|
Resources.StrFwPackageInfo + " " + fwPackRadioFrequencyMhz + " MHz",
|
|
@"Cordonel: " + _currentGenesis.RadioFrequencyMhz + " MHz"
|
|
};
|
|
LogStringList(Resources.StrMessageSystemRadio, lineList,
|
|
errorMessage: Resources.StrCordonelUpdateCapabilityRadioFailed);
|
|
|
|
InfoProcessFailed(lblCordonelUpdateCapability, Resources.StrCordonelUpdateCapabilityRadioFailed, false);
|
|
}
|
|
|
|
if (!retVal)
|
|
_processState = ProcessState.Error;
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Meter size update capability check:
|
|
/// - MeterSize (DN40, DN50,..., DN300, US1_5, US2, US3,..., US12.
|
|
/// If any information cannot be read from the meter, it will be assumed, that the FwUpdateBuilder operator
|
|
/// assembled a correct FwUpdateSafe. Otherwise old devices may be unable to update.
|
|
/// The FwPackage has to be checked in advance before calling this routine, as all these information is needed
|
|
/// for this update capability check.
|
|
/// </summary>
|
|
/// <returns>true if meter size is approved</returns>
|
|
/// <remarks date="2023-Feb-23" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private Boolean MeterSizeUpdateCapability()
|
|
{
|
|
var retVal = true;
|
|
|
|
// extract the package information
|
|
var fwPackMeterSizes = _cordonelFirmwarePackage.FwPackageInfo.MeterSizes;
|
|
|
|
// meter size
|
|
if (fwPackMeterSizes != null && fwPackMeterSizes.Count > 0)
|
|
{
|
|
// check the update capability for meter size,
|
|
// if fw package supports all meter sizes or the meter size is unknown
|
|
if (fwPackMeterSizes.Any(size => size == _currentGenesis.MeterSize)
|
|
|| fwPackMeterSizes.Any(size => size == Constants.StrWildcard)
|
|
|| Constants.StrUnknown == _currentGenesis.MeterSize)
|
|
{
|
|
InfoProcessSuccess(null, Resources.StrCordonelUpdateCapabilityMeterSizeSucceeded);
|
|
}
|
|
else
|
|
{
|
|
retVal = false;
|
|
// set information of required and detected regions check failed!
|
|
var msg = " ";
|
|
var cnt = fwPackMeterSizes.Count;
|
|
foreach (var size in fwPackMeterSizes)
|
|
{
|
|
msg += size;
|
|
if (--cnt > 0)
|
|
msg += ", ";
|
|
}
|
|
var lineList = new List<String>
|
|
{
|
|
Resources.StrFwPackageInfo + msg,
|
|
@"Cordonel: " + _currentGenesis.MeterSize
|
|
};
|
|
LogStringList(Resources.StrMessageMeterSize, lineList,
|
|
errorMessage: Resources.StrCordonelUpdateCapabilityRegionFailed);
|
|
|
|
InfoProcessFailed(lblCordonelUpdateCapability, Resources.StrCordonelUpdateCapabilityMeterSizeFailed,
|
|
false);
|
|
}
|
|
}
|
|
|
|
if (!retVal)
|
|
_processState = ProcessState.Error;
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compare the package information file and package content with installed applications
|
|
/// </summary>
|
|
/// <remarks date="2020-Nov-30" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-23" author="Thomas Wiedebusch">
|
|
/// - Control view invoker added.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
/// <remarks date="2023-Feb-17" author="Thomas Wiedebusch">
|
|
/// - Name of fw package taken from fw package info.
|
|
/// </remarks>
|
|
private Boolean CheckPackageFileAndFileApps()
|
|
{
|
|
var fwPackageName = _cordonelFirmwarePackage.FwPackageInfo.Name;
|
|
if (string.IsNullOrEmpty(fwPackageName) || !_binaryPackageLoaded)
|
|
{
|
|
InfoProcessFailed(lblUpdateInformationStatus, Resources.StrUpdatePackageInvalid);
|
|
return false;
|
|
}
|
|
|
|
InfoProcessSuccess(lblProductToInstall, Resources.StrProductToInstall + @" " + fwPackageName);
|
|
UiInvoker.ControlViewInvoker(lblProductToInstall, true);
|
|
|
|
if (_meterFwUpdate.ValidateFileAppsWithPackageFile())
|
|
{
|
|
InfoProcessSuccess(lblUpdateInformationStatus, Resources.StrUpdateInformationStatusSucceeded);
|
|
return true;
|
|
}
|
|
InfoProcessFailed(lblUpdateInformationStatus, Resources.StrUpdateInformationStatusFailed);
|
|
return false;
|
|
}
|
|
#endregion --------------------------------------- Checks -----------------------------------------------------
|
|
|
|
#region ------------------------------------------ User Interaction -------------------------------------------
|
|
/// <summary>
|
|
/// Establish connection to water-meter
|
|
/// Avoid port scan if meter has been detected.
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2020-Nov-27" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2023-Aug-22" author="Thomas Wiedebusch">
|
|
/// - _finalConnectAfterUpdate set to false as user forces a new connection to create a new report and switch
|
|
/// eventually to new device without exit the program.
|
|
/// </remarks>
|
|
private void BtnConnect_Click(Object sender, EventArgs e)
|
|
{
|
|
// restart for new device
|
|
_afterUpdateConnect = false;
|
|
_stopRequest = false;
|
|
if (_resetTimeMeasurement)
|
|
{
|
|
_startTime = DateTimeOffset.UtcNow;
|
|
_resetTimeMeasurement = false;
|
|
}
|
|
|
|
// if the Genesis/Cordonel has been already detected, the port scan can be bypassed
|
|
if (_currentGenesis != null && !string.IsNullOrEmpty(_currentGenesis.PcbId))
|
|
_processState = ProcessState.InitialConnect;
|
|
else
|
|
_processState = ProcessState.PortScan;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Repeat port scan on changed interface.
|
|
/// This removed the current meter from lists
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2020-Nov-27" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void btnPortScan_Click(Object sender, EventArgs e)
|
|
{
|
|
// IMPORTANT: DisposeGenesis() is needed here even if it will be executed again in Connect():
|
|
// The init disposes the Genesis. If a new port is selected by removal and reinsert of the IrDA
|
|
// readout head, the port may have changed. This forces a new port scan as the Genesis is null.
|
|
DisposeGenesis();
|
|
BtnConnect_Click(this, null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// User request to stop all operations
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2020-Nov-27" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void BtnStop_Click(Object sender, EventArgs e)
|
|
{
|
|
_processState = ProcessState.Stop;
|
|
_stopRequest = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Automatic update of entire FW
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2020-Nov-27" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-24" author="Thomas Wiedebusch">
|
|
/// - Start of update procedure changed to read files to erase and restore.
|
|
/// </remarks>
|
|
private void BtnUpdateFw_Click(Object sender, EventArgs e)
|
|
{
|
|
if (_currentGenesis == null || _meterFwUpdate?.FileApps == null ||
|
|
_registerRestorer == null)
|
|
return;
|
|
|
|
_stopRequest = false;
|
|
_startTime = DateTimeOffset.UtcNow;
|
|
_resetTimeMeasurement = false;
|
|
_processState = ProcessState.ReadInfoEraseRestoreFiles;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Open/Toggle history window
|
|
/// </summary>
|
|
/// <remarks date="2020-Nov-27" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void BtnHistory_Click(Object sender, EventArgs e)
|
|
{
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
|
|
var xPosition = Location.X + Size.Width;
|
|
var yPosition = Location.Y;
|
|
|
|
if (_frmHistory == null)
|
|
return;
|
|
if (_frmHistory.Visible)
|
|
{
|
|
_frmHistory.Hide();
|
|
btnHistory.Text = Resources.StrHistoryWindowShow;
|
|
}
|
|
else
|
|
{
|
|
_frmHistory.SetDesktopLocation(xPosition, yPosition);
|
|
_frmHistory.Show();
|
|
btnHistory.Text = Resources.StrHistoryWindowToggle;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Release Cordonel display from"Idle" to normal operation
|
|
/// </summary>
|
|
/// <remarks date="2022-Nov-25" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void BtnReleaseDisplay_Click(Object sender, EventArgs e)
|
|
{
|
|
ReleaseDisplayAndSwitchLedOff();
|
|
}
|
|
|
|
#endregion --------------------------------------- User Interaction -------------------------------------------
|
|
|
|
#region ------------------------------------------ Tools ------------------------------------------------------
|
|
/// <summary>
|
|
/// Logging of password file read from meter.
|
|
/// </summary>
|
|
/// <remarks date="2023-Oct-11" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-19" author="Thomas Wiedebusch">
|
|
/// - Output message changed to get colored error or success messages.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-27" author="Thomas Wiedebusch">
|
|
/// - Message for uninstalled password file moved from <see cref="ExecConnect"/>ExecConnect to here.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-09" author="Thomas Wiedebusch">
|
|
/// - Avoid comparison if password file is not in FW-Update safe.
|
|
/// </remarks>
|
|
private Boolean ReadLogAndCompareMeterPwdFile()
|
|
{
|
|
if (_currentGenesis == null)
|
|
return false;
|
|
_currentGenesis.ReLogin();
|
|
Boolean retVal;
|
|
try
|
|
{
|
|
// Read the meter password file
|
|
var meterFile = new MeterFile(_currentGenesis);
|
|
retVal = meterFile.ReadMeterFile(MeterPwdFile.StrPasswordFileName, out var hashedMeterPwdFile);
|
|
|
|
// Separate all hashed passwords for logging each in an individual line
|
|
var hashedPwdList = new List<String>();
|
|
if (retVal)
|
|
{
|
|
_passwordFileIsCorrupted = false;
|
|
for (var idx = 0; idx < hashedMeterPwdFile.Count; idx += MeterPwdDb.HashedPwdLength)
|
|
{
|
|
var hashedPwd = BitConverter.ToString(hashedMeterPwdFile.GetRange(idx,
|
|
MeterPwdDb.HashedPwdLength).ToArray());
|
|
hashedPwdList.Add(hashedPwd);
|
|
}
|
|
}
|
|
|
|
// Analyze the read out of meter password file
|
|
if (hashedMeterPwdFile == null || hashedMeterPwdFile.Count == 0)
|
|
{
|
|
// Log information about password file is not installed
|
|
InfoProcessFailed(null, Resources.StrPasswordFileNotInstalled);
|
|
_passwordFileIsCorrupted = true;
|
|
retVal = false;
|
|
}
|
|
|
|
// If the FW-Update safe does not contain a password file this cannot be compared
|
|
if (_fwUpdateSafePwdFile == null)
|
|
{
|
|
// If the FW-Update safe does not contain a password file just log the read out
|
|
if (!_passwordFileIsCorrupted)
|
|
{
|
|
LogStringList(Resources.StrMessageHashedPwdFile, hashedPwdList,
|
|
errorMessage: Resources.StrPasswordFileNotDelivered);
|
|
}
|
|
else
|
|
{
|
|
LogErrorText(Resources.StrPasswordFileNotDelivered);
|
|
}
|
|
}
|
|
else if (!_passwordFileIsCorrupted)
|
|
{
|
|
// Compare the installed password file with the password file from FW update safe
|
|
var pwdFile = new MeterPwdFile(_currentGenesis);
|
|
_passwordFileIsCorrupted = !pwdFile.VerifyMeterPwdFile(_fwUpdateSafePwdFile);
|
|
|
|
if (!_passwordFileIsCorrupted)
|
|
{
|
|
LogStringList(Resources.StrMessageHashedPwdFile, hashedPwdList,
|
|
Resources.StrPasswordFileCompareSucceeded);
|
|
}
|
|
else
|
|
{
|
|
// password file is suspicious
|
|
LogStringList(Resources.StrMessageHashedPwdFile, hashedPwdList,
|
|
errorMessage: Resources.StrPasswordFileCompareFailed);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
LogErrorText(Resources.StrPasswordFileReadoutFailed);
|
|
retVal = false;
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Logging of LUT file read from meter and validate the CRC generated from raw data read from meter
|
|
/// and compared with meter lookup CRC set in meter.
|
|
/// </summary>
|
|
/// <remarks date="2023-Nov-13" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-22" author="Thomas Wiedebusch">
|
|
/// - LUT file invalid as it cannot be read.
|
|
/// </remarks>
|
|
private Boolean ReadLogAndCompareMeterLutFile()
|
|
{
|
|
if (_currentGenesis == null)
|
|
return false;
|
|
_currentGenesis.ReLogin();
|
|
Boolean retVal;
|
|
try
|
|
{
|
|
// Read the meter LUT file
|
|
var meterFile = new MeterFile(_currentGenesis);
|
|
var meterLutFile = new MeterLutFile();
|
|
retVal = meterFile.ReadMeterFile(MeterLutFile.StrLutMeterFileName, out meterLutFile.BinData);
|
|
|
|
// Extract information from read meter LUT file
|
|
var lutLineList = new List<String>();
|
|
if (retVal)
|
|
{
|
|
// Validate the meter LUT file format
|
|
var lutFileFormatValid = meterLutFile.ValidateLutFormat();
|
|
var lutLine = $@"LUT Version: {meterLutFile.Version}";
|
|
lutLineList.Add(lutLine);
|
|
lutLine = $@"LUT {Resources.StrMessageMeterSize} " +
|
|
$@"{MeterSizeConverter.ConvertMeterSizeEnumToSizeName(meterLutFile.MeterSize)}";
|
|
lutLineList.Add(lutLine);
|
|
lutLine = $@"{Resources.StrMessageLutCrc} 0x{meterLutFile.FileCrc:X4}";
|
|
lutLineList.Add(lutLine);
|
|
if (lutFileFormatValid)
|
|
{
|
|
if (_fwUpdateSafeLutFile == null || _fwUpdateSafeLutFile.BinData.Count != meterLutFile.SizeLutFile)
|
|
{
|
|
LogStringList(Resources.StrMessageLutFile, lutLineList,
|
|
Resources.StrLutFileFromMeterValid);
|
|
}
|
|
else
|
|
{
|
|
for (var c = 0; c < meterLutFile.DataLength; c++)
|
|
{
|
|
// If one byte is different the comparison failed
|
|
if (_fwUpdateSafeLutFile.BinData[c] != meterLutFile.BinData[c])
|
|
{
|
|
LogStringList(Resources.StrMessageLutFile, lutLineList,
|
|
errorMessage: Resources.StrLutFileCompareFailed);
|
|
retVal = false;
|
|
}
|
|
}
|
|
|
|
// Here all bytes are identical
|
|
if (!retVal)
|
|
{
|
|
LogStringList(Resources.StrMessageLutFile, lutLineList,
|
|
Resources.StrLutFileCompareSucceeded);
|
|
}
|
|
}
|
|
}
|
|
// Here the LUT file format is invalid
|
|
else
|
|
{
|
|
LogStringList(Resources.StrMessageLutFile, lutLineList,
|
|
errorMessage: Resources.StrLutFileFromMeterInvalid);
|
|
retVal = false;
|
|
}
|
|
}
|
|
// Here the LUT file format is invalid
|
|
else
|
|
{
|
|
LogStringList(Resources.StrMessageLutFile, lutLineList,
|
|
errorMessage: Resources.StrLutFileFromMeterInvalid);
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
LogErrorText(Resources.StrLutFileReadoutFailed);
|
|
retVal = false;
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set data and time.
|
|
/// </summary>
|
|
/// <remarks date="2023-Oct-09" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2024-Feb-08" author="Thomas Wiedebusch">
|
|
/// - Avoid to setup IRDA_MfgDate as this is the manufacturing date and not the FW-Update date!
|
|
/// </remarks>
|
|
private Boolean SetDateTime()
|
|
{
|
|
if (_currentGenesis == null)
|
|
return false;
|
|
_currentGenesis.ReLogin();
|
|
Boolean retVal;
|
|
try
|
|
{
|
|
// get the time in UTC to program the time for the Cordonel in seconds since 01. Jan 2000 00:00:00 UTC
|
|
var teaTime = new TimeT();
|
|
// build byte array
|
|
var rawSeconds = RegisterConverter.ValueToByteArray(teaTime.UtcNowToSecondsSince2000);
|
|
retVal = _currentGenesis.WriteRegister("SYSTEM_CalendarSeconds", rawSeconds);
|
|
LogSuccessText(Resources.StrDateTimeSetupSucceeded + " " + teaTime);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
LogErrorText(Resources.StrDateTimeSetupFailed);
|
|
retVal = false;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Activate the radio in customer mode with logging of success or failed.
|
|
/// </summary>
|
|
/// <remarks date="2023-Feb-18" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Feb-20" author="Thomas Wiedebusch">
|
|
/// - Reworked with new information from Joerg Lachenmayer:
|
|
/// 1. Write 0xff,
|
|
/// 2. logout,
|
|
/// 3. Wait 1000 ms,
|
|
/// 4. login
|
|
/// 5. read if not 0x01.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Mar-07" author="Thomas Wiedebusch">
|
|
/// - Handle SystemState 0 = OFF
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-16" author="Thomas Wiedebusch">
|
|
/// - Store configuration of radio settings
|
|
/// </remarks>
|
|
private Boolean SetRadioToCustomerMode()
|
|
{
|
|
if (_currentGenesis == null)
|
|
return false;
|
|
_currentGenesis.ReLogin();
|
|
Boolean retVal;
|
|
try
|
|
{
|
|
// read initially if radio is NOT in production mode (0x01), where it sends unencrypted radio telegrams
|
|
var radioSystemState =
|
|
RegisterConverter.ByteArrayToValue<Int32>(_currentGenesis.ReadRegister(Register.Sensusradio.SystemState));
|
|
if (radioSystemState != 0x01 && radioSystemState != 0)
|
|
{
|
|
LogSuccessText(Resources.StrRadioAlreadyActive);
|
|
retVal = true;
|
|
}
|
|
else // switch radio active to customer mode with encrypted communication
|
|
{
|
|
// write register to 0xFF means radio activation with programmed radio key to customer mode,
|
|
// SENSUSRADIO needs to store all information to CONFIGEXCHANGE, therefore a logout has to be forced
|
|
_currentGenesis.WriteRegister(Register.Sensusradio.SystemState, 0xFF);
|
|
_currentGenesis.Logout();
|
|
|
|
// give the radio app the time to store and restart in customer mode
|
|
Thread.Sleep(1000);
|
|
|
|
// login for read back of new mode which shouldn't be 0x01
|
|
_currentGenesis.ReLogin();
|
|
// read back if radio is no longer in production mode
|
|
if (0x01 != RegisterConverter.ByteArrayToValue<Int32>(_currentGenesis.ReadRegister(Register.Sensusradio.SystemState)))
|
|
{
|
|
_currentGenesis.WriteRegister(Register.Sensusradio.StoreConfiguration, 1);
|
|
LogSuccessText(Resources.StrRadioActivationSuccess);
|
|
retVal = true;
|
|
}
|
|
else
|
|
{
|
|
LogErrorText(Resources.StrRadioActivationFailed);
|
|
retVal = false;
|
|
}
|
|
} // initially radio active check
|
|
}
|
|
catch (Exception)
|
|
{
|
|
LogErrorText(Resources.StrRadioActivationFailed);
|
|
retVal = false;
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The pulse module interrupts and disturbs the IrDA communication. As quick fix the pulse module can be switched off,
|
|
/// as soon as a communication could be established. As the pulse output will only be switched on again, if a pulse
|
|
/// module is attached to the device, this pulse mode can be switched active instantaneously after deactivation.
|
|
/// </summary>
|
|
/// <remarks date="2023-Mar-07" author="Thomas Wiedebusch">
|
|
/// - Initial, sequence of deactivation based on code from Roland Drabesch.
|
|
/// </remarks>
|
|
private Boolean DeactivatePulseMode()
|
|
{
|
|
var retVal = false;
|
|
try
|
|
{
|
|
if (!_currentGenesis.IsLoggedOn)
|
|
_currentGenesis.ReLogin();
|
|
|
|
// create path and name for register setup reminder for this PcbId
|
|
_pathNameCordonelRegisterRestoreSettingsFile = Path.Combine(FwUpdateConfig.ReportedPath,
|
|
$"{_currentGenesis.PcbId}_" + FwUpdateConfig.RestoreSettingsFileName);
|
|
|
|
var pulseMode =
|
|
RegisterConverter.ByteArrayToValue<Byte>(_currentGenesis.ReadRegister(Register.Metrologyasst.PulseMode));
|
|
// if pulse mode is already set to 0 (OFF) this could be initial setup or a indication of a crashed update
|
|
if (pulseMode == 0)
|
|
{
|
|
LogText(Resources.StrPulseModeInactive);
|
|
// check if a file exists with settings from a previous update
|
|
if (File.Exists(_pathNameCordonelRegisterRestoreSettingsFile))
|
|
{
|
|
var fileData = File.ReadAllText(_pathNameCordonelRegisterRestoreSettingsFile);
|
|
_pulseModeRegistersRestore = JsonConvert.DeserializeObject<Dictionary<String, Byte[]>>(fileData);
|
|
}
|
|
retVal = true;
|
|
}
|
|
|
|
//if retVal is true, the pulse mode is switched OFF, no further action is required
|
|
if (!retVal)
|
|
{
|
|
// safe the pulse setting
|
|
_pulseModeRegistersRestore.Add(Register.Metrologyasst.PulseMode, new[] { pulseMode });
|
|
// Create new file as for report only one version is allowed
|
|
var serializedData = JsonConvert.SerializeObject(_pulseModeRegistersRestore);
|
|
var asciiStream = Encoding.UTF8.GetBytes(serializedData);
|
|
File.WriteAllBytes(_pathNameCordonelRegisterRestoreSettingsFile, asciiStream);
|
|
|
|
// write pulse mode temporary to 0 (OFF) and wait for result
|
|
retVal = _currentGenesis.WriteRegister(Register.Metrologyasst.PulseMode, 0);
|
|
LogText(Resources.StrPulseModeTemporaryDeactivated);
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
retVal = false;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
/// <summary>
|
|
/// Restore all listed pulse mode registers.
|
|
/// </summary>
|
|
/// <remarks date="2023-Mar-07" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private Boolean RestorePulseMode()
|
|
{
|
|
var retVal = true;
|
|
try
|
|
{
|
|
_currentGenesis.ReLogin();
|
|
|
|
foreach (var item in _pulseModeRegistersRestore)
|
|
{
|
|
retVal &= _currentGenesis.WriteRegister(item.Key, item.Value);
|
|
}
|
|
|
|
retVal &= _currentGenesis.WriteRegister(Register.Metrologyasst.StoreConfiguration, 1);
|
|
if (retVal && _pulseModeRegistersRestore.Any(x => x.Key == Register.Metrologyasst.PulseMode))
|
|
LogText(Resources.StrPulseModeRestored);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
retVal = false;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Store configurations of all applications with logging of success or failed.
|
|
/// </summary>
|
|
/// <remarks date="2023-Feb-17" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private Boolean StoreConfigurations()
|
|
{
|
|
if (_currentGenesis == null)
|
|
return false;
|
|
_currentGenesis.ReLogin();
|
|
Boolean retVal;
|
|
Thread.Sleep(1000);
|
|
if (_currentGenesis.StoreAllConfigurations())
|
|
{
|
|
LogSuccessText(Resources.StrStoreConfigSuccess);
|
|
retVal = true;
|
|
}
|
|
else
|
|
{
|
|
LogErrorText(Resources.StrStoreConfigFailed);
|
|
retVal = false;
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Release display and switch measurement LED off.
|
|
/// </summary>
|
|
/// <remarks date="2022-Apr-27" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2022-Jul-22" author="Thomas Wiedebusch">
|
|
/// - Store configuration genesisflow after display release. This is essential if a reboot will occur
|
|
/// to have the correct display of the accumulated volume and flow rate!
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-15" author="Thomas Wiedebusch">
|
|
/// - Additional message of LED off
|
|
/// </remarks>
|
|
private Boolean ReleaseDisplayAndSwitchLedOff()
|
|
{
|
|
if (_currentGenesis == null)
|
|
return false;
|
|
var retVal = _currentGenesis.ReLogin();
|
|
// Set LCD DISPLAY from IdLE and 9999 back to operation, this may be necessary if the update has been
|
|
// interrupted by a program stop due to a power loss or any unpredictable event.
|
|
LogText(_currentGenesis.WriteRegister(Register.Genesisflow.TriggerIdle, 0)
|
|
? Resources.StrLedOffSucceeded
|
|
: Resources.StrLedOffFailed);
|
|
// deactivate LED, not needed to get measurements during the update or on repeated start after system
|
|
// failure. This overwrites the settings in _meterFwUpdate.
|
|
LogText(_currentGenesis.WriteRegister(Register.Genesisflow.LedMode, 0)
|
|
? Resources.StrReleaseDisplaySucceeded
|
|
: Resources.StrReleaseDisplayFailed);
|
|
|
|
// safe all settings for GENESISFLOW application
|
|
retVal &= _currentGenesis.WriteRegister(Register.Genesisflow.StoreConfiguration, 1);
|
|
Thread.Sleep(2000);
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Store all made settings and release meter to normal operation.
|
|
/// </summary>
|
|
/// <remarks date="2023-Oct-7" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private Boolean ReleaseMeterToNormalOperation()
|
|
{
|
|
var retVal = ReleaseDisplayAndSwitchLedOff();
|
|
retVal &= RestorePulseMode();
|
|
SetOverallProgressDisplayOff();
|
|
SetDisableDownloadAndEnableConnect();
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Logging of string lists.
|
|
/// </summary>
|
|
/// <param name="strHeader">Head line for comment</param>
|
|
/// <param name="strings">string list</param>
|
|
/// <param name="successMessage">optional message if process succeeded</param>
|
|
/// <param name="errorMessage">optional message if process failed</param>
|
|
/// <remarks date="2023-Oct-19" author="Thomas Wiedebusch">
|
|
/// - Output message changed to get colored error or success messages.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-24" author="Thomas Wiedebusch">
|
|
/// - LogErrorText.
|
|
/// </remarks>
|
|
private void LogStringList(String strHeader, IEnumerable<String> strings, String successMessage = null,
|
|
String errorMessage = null)
|
|
{
|
|
LogText(strHeader);
|
|
foreach (var text in strings)
|
|
{
|
|
LogText(text);
|
|
}
|
|
if (!string.IsNullOrEmpty(successMessage))
|
|
LogSuccessText(successMessage);
|
|
if (!string.IsNullOrEmpty(errorMessage))
|
|
LogErrorText(errorMessage);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Logging of installed meter firmware and meter information.
|
|
/// </summary>
|
|
/// <remarks date="2021-Mar-09" author="Thomas Wiedebusch">
|
|
/// - Added application list file handling as json file: One file with all new installed versions.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-26" author="Thomas Wiedebusch">
|
|
/// - Added region and radio.
|
|
/// </remarks>
|
|
/// <remarks date="2022-Dec-05" author="Thomas Wiedebusch">
|
|
/// - Core revision data type changed from string ti int32?.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Aug-15" author="Thomas Wiedebusch">
|
|
/// - LUT CRC and MeterSize added.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Aug-31" author="Thomas Wiedebusch">
|
|
/// - FW version added.
|
|
/// </remarks>
|
|
private void LogInstalledMeterFw()
|
|
{
|
|
if (_currentGenesis == null)
|
|
return;
|
|
LogText($"PCB ID: {_currentGenesis.PcbId}");
|
|
LogText($"{Resources.StrMessageCustomerSerialNumber} {_customerSerialNumber}");
|
|
LogText($"FW Version: {_currentGenesis.FwVersion}");
|
|
LogText($"{Resources.StrMessageSystemCoreRevision} {_currentGenesis.StrCoreRevision}");
|
|
LogText($"{Resources.StrMessageSystemRegion} {_currentGenesis.Region}");
|
|
LogText($"{Resources.StrMessageSystemRadio} {_currentGenesis.RadioFrequencyMhz}");
|
|
LogText($"{Resources.StrMessageMeterSize} {_currentGenesis.MeterSize}");
|
|
if (!string.IsNullOrEmpty(_currentGenesis.LutCrc))
|
|
LogText($"{Resources.StrMessageLutCrc} {_currentGenesis.LutCrc}");
|
|
LogText(Resources.StrMessageInstalledApp);
|
|
var appList = new List<CordonelAppVersion>();
|
|
|
|
// the core has the -1 as identifier and is not updateable
|
|
var coreVersion = new CordonelAppVersion(-1, _currentGenesis.StrCoreRevision, false);
|
|
appList.Add(coreVersion);
|
|
|
|
foreach (var app in _currentGenesis.MeterAppListVersion)
|
|
{
|
|
var versionString = app.IsInstalled ? $"V: {app.StrVersion} - CRC: 0x{app.Crc:X4}" :
|
|
Resources.StrMessageAppNotInstalled;
|
|
|
|
if (app.Status == MeterAppState.Unknown)
|
|
versionString = Resources.StrCordonelCommunicationFailed;
|
|
|
|
LogText($"{Resources.StrTableAppId}: 0x{app.AppId:X2} - {versionString} - " +
|
|
$"{Resources.StrTableAppName}: {app.AppName}");
|
|
|
|
if (app.IsInstalled)
|
|
{
|
|
var appVersion = new CordonelAppVersion(app.AppId, app.StrVersion);
|
|
|
|
// Copy metrology update permission
|
|
if (app.AppName == MeterFwUpdateCapability.MetrologyName &&
|
|
_currentGenesis.MetrologyUpgradePermission != MeterFwUpdateCapability.MetrologyUpgradePermitted)
|
|
{
|
|
appVersion.IsUpdateable = false;
|
|
}
|
|
appList.Add(appVersion);
|
|
}
|
|
}
|
|
LogText(StrSeparator);
|
|
|
|
if (string.IsNullOrEmpty(_pathNameCordonelAppVersionFile))
|
|
return;
|
|
try
|
|
{
|
|
// Create new file as for report only one version is allowed
|
|
var serializedData = JsonConvert.SerializeObject(appList);
|
|
var asciiStream = Encoding.UTF8.GetBytes(serializedData);
|
|
File.WriteAllBytes(_pathNameCordonelAppVersionFile, asciiStream);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
MessageBoxShow(e.Message, Resources.StrTestReportFailed, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Take the process state from a sorted list of sequenced processes.
|
|
/// </summary>
|
|
/// <remarks date="2023-Nov-15" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private ProcessState GetNextProcessState(ProcessState invokerProcessState,
|
|
IReadOnlyList<ProcessState> processSateSequence)
|
|
{
|
|
if (_stopRequest)
|
|
return ProcessState.Stop;
|
|
// The last entry is an end of list marker
|
|
for (var c = 0; c < processSateSequence.Count - 1; c++)
|
|
{
|
|
if (invokerProcessState == processSateSequence[c])
|
|
return processSateSequence[c + 1];
|
|
}
|
|
// If the process state is not in the list
|
|
_invokerProcessState = ProcessState.EndOfList;
|
|
return ProcessState.Error;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common message window.
|
|
/// </summary>
|
|
/// <remarks date="2020-Dec-23" author="Thomas Wiedebusch">
|
|
/// - Control view invoker added.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Jan-29" author="Thomas Wiedebusch">
|
|
/// - Force main form being TopMost to see the message box on top.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-29" author="Thomas Wiedebusch">
|
|
/// - Forcing message box being modal and on top.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-14" author="Thomas Wiedebusch">
|
|
/// - Return user selection.
|
|
/// </remarks>
|
|
private DialogResult MessageBoxShow(String text, String caption, MessageBoxButtons buttons,
|
|
MessageBoxIcon icon)
|
|
{
|
|
SetOverallProgressDisplayOff();
|
|
SetActualProgressDisplayOff();
|
|
UiInvoker.ControlInvoker(lblWaitingForMeterResponse, ColorProcessFailed, visible: false);
|
|
return MessageBox.Show(text, caption, buttons, icon, MessageBoxDefaultButton.Button1,
|
|
MessageBoxOptions.ServiceNotification);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Output exclusively to user update remarks text window.
|
|
/// </summary>
|
|
/// <remarks date="2021-Apr-26" author="Thomas Wiedebusch">
|
|
/// - Color added.
|
|
/// </remarks>
|
|
private void LogText(String txtHistory)
|
|
{
|
|
InfoWindowColoredText(txtHistory, ColorDefault);
|
|
_logger.Info(txtHistory);
|
|
ReportText(txtHistory);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Output exclusively to user update remarks text window.
|
|
/// </summary>
|
|
private void LogErrorText(String txtHistory)
|
|
{
|
|
InfoWindowColoredText(txtHistory, ColorProcessFailed);
|
|
_logger.Info(txtHistory);
|
|
ReportText(txtHistory);
|
|
LogText(StrSeparator);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Output exclusively to user update remarks text window.
|
|
/// </summary>
|
|
private void LogWarningText(String txtHistory)
|
|
{
|
|
InfoWindowColoredText(txtHistory, ColorWarning);
|
|
_logger.Info(txtHistory);
|
|
ReportText(txtHistory);
|
|
LogText(StrSeparator);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Output exclusively to user update remarks text window.
|
|
/// </summary>
|
|
private void LogSuccessText(String txtHistory)
|
|
{
|
|
InfoWindowColoredText(txtHistory, ColorSuccess);
|
|
_logger.Info(txtHistory);
|
|
ReportText(txtHistory);
|
|
LogText(StrSeparator);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Output exclusively to user update remarks text window.
|
|
/// </summary>
|
|
private void InfoWindowColoredText(String txtHistory, Color color)
|
|
{
|
|
if (_frmHistory?.rtbHistory == null)
|
|
return;
|
|
Invoke(new Action(() =>
|
|
{
|
|
_frmHistory.rtbHistory.SuspendLayout();
|
|
_frmHistory.rtbHistory.SelectionStart = _frmHistory.rtbHistory.Text.Length;
|
|
_frmHistory.rtbHistory.SelectionLength = 0;
|
|
_frmHistory.rtbHistory.SelectionColor = color;
|
|
_frmHistory.rtbHistory.AppendText($"{txtHistory}{Environment.NewLine}");
|
|
_frmHistory.rtbHistory.SelectionColor = _frmHistory.rtbHistory.ForeColor;
|
|
_frmHistory.rtbHistory.ScrollToCaret();
|
|
_frmHistory.rtbHistory.ResumeLayout();
|
|
}));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Output to user update remarks text window and report file.
|
|
/// </summary>
|
|
/// <remarks date="2021-Mar-09" author="Thomas Wiedebusch">
|
|
/// - Additional use for report file.
|
|
/// </remarks>
|
|
private void ReportText(String txtHistory)
|
|
{
|
|
if (string.IsNullOrEmpty(_pathNameReportFile))
|
|
return;
|
|
try
|
|
{
|
|
// Create new file or add to existing file
|
|
if (File.Exists(_pathNameReportFile))
|
|
{
|
|
File.AppendAllText(_pathNameReportFile, txtHistory + Environment.NewLine);
|
|
}
|
|
else
|
|
{
|
|
File.WriteAllText(_pathNameReportFile, txtHistory + Environment.NewLine);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
MessageBoxShow(e.Message, Resources.StrTestReportFailed, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Neutral information to label, user text box and logger.
|
|
/// </summary>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
private void InfoStatusUnknown(Control label, String msg, Boolean userTextOutput = false)
|
|
{
|
|
if (userTextOutput)
|
|
LogText(msg);
|
|
UiInvoker.ControlInvoker(label, ColorUnknownStatus, msg);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Success information to label, user text box and logger.
|
|
/// </summary>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
private void InfoProcessActive(Control label, String msg, Boolean log = true)
|
|
{
|
|
if (log)
|
|
{
|
|
LogText(msg);
|
|
}
|
|
|
|
UiInvoker.ControlInvoker(label, ColorOngoingProcess, msg);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Success information to label, user text box and logger.
|
|
/// </summary>
|
|
private void InfoProcessSuccess(Control label, String msg, Boolean log = true)
|
|
{
|
|
if (log)
|
|
{
|
|
LogSuccessText(msg);
|
|
}
|
|
|
|
UiInvoker.ControlInvoker(label, ColorSuccess, $@"{SuccessSign} {msg}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Success information to label, user text box and logger.
|
|
/// </summary>
|
|
private void InfoProcessWarning(Control label, String msg, Boolean log = true)
|
|
{
|
|
if (log)
|
|
{
|
|
LogWarningText(msg);
|
|
}
|
|
|
|
UiInvoker.ControlInvoker(label, ColorWarning, $@"{WarningSign} {msg}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Failed information to label, user text box and logger.
|
|
/// </summary>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
private void InfoProcessFailed(Control label, String msg, Boolean log = true)
|
|
{
|
|
if (log)
|
|
{
|
|
LogErrorText(msg);
|
|
}
|
|
|
|
UiInvoker.ControlInvoker(label, ColorProcessFailed, $@"{FailedSign} {msg}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Action on FW-Update success
|
|
/// </summary>
|
|
/// <remarks date="2020-Dec-18" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-22" author="Thomas Wiedebusch">
|
|
/// - Control invoker implemented.
|
|
/// </remarks>
|
|
private void FwUpdateSuccessMessage()
|
|
{
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
|
|
InfoProcessSuccess(lblFirmwareUpdate, Resources.StrFirmwareUpdateSucceeded);
|
|
SetDisableDownloadAndEnableConnect();
|
|
InfoProcessSuccess(lblCordonelUpdateCapability, Resources.StrFwUpToDateMessage);
|
|
MessageBoxShow(Resources.StrFwUpToDateMessage, "Info", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common routine to enable download last control gets focus
|
|
/// </summary>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
/// <remarks date="2024-May-13" author="Thomas Wiedebusch">
|
|
/// - Disabled language selection, culture info fixed to english.
|
|
/// </remarks>
|
|
private void SetControlDownloadEnable()
|
|
{
|
|
UiInvoker.ControlEnableInvoker(grpLanguageSelection, false);
|
|
UiInvoker.ControlEnableInvoker(btnStop, false);
|
|
UiInvoker.ControlEnableInvoker(btnReleaseDisplay, true);
|
|
UiInvoker.ControlEnableInvoker(btnPortScan, true);
|
|
UiInvoker.ControlEnableInvoker(btnConnect, true);
|
|
// set focus
|
|
UiInvoker.ControlEnableInvoker(btnUpdateFw, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common routine to lock download last control gets focus
|
|
/// </summary>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
/// <remarks date="2023-Feb-22" author="Thomas Wiedebusch">
|
|
/// - Enable "release Display" if Cordonel is connected
|
|
/// </remarks>
|
|
/// <remarks date="2024-May-13" author="Thomas Wiedebusch">
|
|
/// - Disabled language selection, culture info fixed to english.
|
|
/// </remarks>
|
|
private void SetDisableDownloadAndEnableConnect()
|
|
{
|
|
UiInvoker.ControlEnableInvoker(grpLanguageSelection, false);
|
|
UiInvoker.ControlEnableInvoker(btnStop, false);
|
|
// if the FwVersion is set, the password is valid and a login was performed in advance to read it
|
|
if (_currentGenesis != null
|
|
&& !string.IsNullOrEmpty(_currentGenesis.PcbId)
|
|
&& !string.IsNullOrEmpty(_currentGenesis.FwVersion))
|
|
{
|
|
UiInvoker.ControlEnableInvoker(btnReleaseDisplay, true);
|
|
}
|
|
else
|
|
{
|
|
UiInvoker.ControlEnableInvoker(btnReleaseDisplay, false);
|
|
}
|
|
UiInvoker.ControlEnableInvoker(btnPortScan, true);
|
|
UiInvoker.ControlEnableInvoker(btnUpdateFw, false);
|
|
// set focus
|
|
UiInvoker.ControlEnableInvoker(btnConnect, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common routine to set controls during register access with water meter
|
|
/// </summary>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
private void SetControlsAllButtonsDisabled()
|
|
{
|
|
UiInvoker.ControlEnableInvoker(grpLanguageSelection, false);
|
|
UiInvoker.ControlEnableInvoker(btnConnect, false);
|
|
UiInvoker.ControlEnableInvoker(btnUpdateFw, false);
|
|
UiInvoker.ControlEnableInvoker(btnPortScan, false);
|
|
UiInvoker.ControlEnableInvoker(btnStop, false);
|
|
UiInvoker.ControlEnableInvoker(btnReleaseDisplay, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common routine to set controls during communication with water meter
|
|
/// last control gets focus
|
|
/// </summary>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
private void SetControlsCommunicationActive()
|
|
{
|
|
UiInvoker.ControlEnableInvoker(grpLanguageSelection, false);
|
|
UiInvoker.ControlEnableInvoker(btnConnect, false);
|
|
UiInvoker.ControlEnableInvoker(btnUpdateFw, false);
|
|
UiInvoker.ControlEnableInvoker(btnPortScan, false);
|
|
UiInvoker.ControlEnableInvoker(btnReleaseDisplay, false);
|
|
// set focus
|
|
UiInvoker.ControlEnableInvoker(btnStop, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common routine to activate overall process bar and text
|
|
/// </summary>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
private void SetOverallProgressDisplayOn(String startText = "")
|
|
{
|
|
_timerDisplayOn = true;
|
|
_progressBarValueCounter = 0;
|
|
UiInvoker.ProgressBarInvoker(barOverallProgressUpdate);
|
|
UiInvoker.ControlInvoker(lblOverallProcess, ColorDefault, startText);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common routine to hide overall process bar and text.
|
|
/// </summary>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
private void SetOverallProgressDisplayOff()
|
|
{
|
|
_timerDisplayOn = false;
|
|
_progressBarValueCounter = 0;
|
|
UiInvoker.ControlViewInvoker(barOverallProgressUpdate, false);
|
|
UiInvoker.ControlInvoker(lblWaitingForMeterResponse, ColorProcessFailed, visible: false);
|
|
UiInvoker.ControlViewInvoker(lblOverallProcess, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common routine to activate actual process bar and text
|
|
/// </summary>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
private void SetActualProgressDisplayOn(String startText = "")
|
|
{
|
|
UiInvoker.ProgressBarInvoker(barActualProgressUpdate);
|
|
UiInvoker.ControlInvoker(lblActualProcess, ColorDefault, startText);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common routine to hide actual process bar and text
|
|
/// </summary>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
private void SetActualProgressDisplayOff()
|
|
{
|
|
UiInvoker.ControlViewInvoker(barActualProgressUpdate, false);
|
|
UiInvoker.ControlViewInvoker(lblActualProcess, false);
|
|
}
|
|
#endregion --------------------------------------- Tools ------------------------------------------------------
|
|
|
|
#region ------------------------------------------ Common Process Routines ------------------------------------
|
|
/// <summary>
|
|
/// Common routine to convert objects to process states.
|
|
/// Objects are sorted:
|
|
/// 1. successState
|
|
/// 2. errorState
|
|
/// 3. breakState
|
|
/// </summary>
|
|
/// <param name="objects">input of object array</param>
|
|
/// <param name="successState">success state, default set to idle</param>
|
|
/// <param name="errorState">error state, default set to error</param>
|
|
/// <param name="breakState">break state, default set to stop</param>
|
|
/// <remarks date="2020-Dec-12" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private static void ObjectsToProcessStates(IReadOnlyList<Object> objects, out ProcessState successState,
|
|
out ProcessState errorState, out ProcessState breakState)
|
|
{
|
|
// cast objects to process states sorted as success, error, break
|
|
successState = ProcessState.Idle;
|
|
errorState = ProcessState.Error;
|
|
breakState = ProcessState.Stop;
|
|
|
|
if (objects == null)
|
|
return;
|
|
|
|
if (objects.Count > 0 && objects[0] != null)
|
|
successState = (ProcessState)objects[0];
|
|
if (objects.Count > 1 && objects[1] != null)
|
|
errorState = (ProcessState)objects[1];
|
|
if (objects.Count > 2 && objects[2] != null)
|
|
breakState = (ProcessState)objects[2];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Error processes.
|
|
/// </summary>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-21" author="Thomas Wiedebusch">
|
|
/// - Error message dispatcher.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-22" author="Thomas Wiedebusch">
|
|
/// - Additional messages.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Jan-14" author="Thomas Wiedebusch">
|
|
/// - Additional messages.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Jan-29" author="Thomas Wiedebusch">
|
|
/// - Password file restore added.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-03" author="Thomas Wiedebusch">
|
|
/// - Dispose meter at stop during port scan to avoid lock of port.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-03" author="Thomas Wiedebusch">
|
|
/// - Disable download on selected errors instead of all.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-20" author="Thomas Wiedebusch">
|
|
/// - Enable download on previously failed update procedure as this has been check in advance.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-26" author="Thomas Wiedebusch">
|
|
/// - Region radio message.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Feb-22" author="Thomas Wiedebusch">
|
|
/// - Meter size message.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-16" author="Thomas Wiedebusch">
|
|
/// - Meter size message.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-10" author="Thomas Wiedebusch">
|
|
/// - Meter LUT message.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-15..16" author="Thomas Wiedebusch">
|
|
/// - Error state decides what to do next.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-29" author="Thomas Wiedebusch">
|
|
/// - If LUT restore generates an error all processes have to be stopped as the meter is NOT functional.
|
|
/// </remarks>
|
|
/// <remarks date="2024-Apr-24" author="Thomas Wiedebusch">
|
|
/// - COmmunication error on app detection.
|
|
/// </remarks>
|
|
private void ErrorProcesses()
|
|
{
|
|
SetOverallProgressDisplayOff();
|
|
SetActualProgressDisplayOff();
|
|
_resetTimeMeasurement = true;
|
|
// take the invoker of the error state to generate the message and / or popup window
|
|
switch (_invokerProcessState)
|
|
{
|
|
// Port scan and Cordonel detection, this is the start of the connection and check sequence
|
|
case ProcessState.PortScan:
|
|
var msg = Resources.StrRequestPortFailed;
|
|
ErrorProcessCommon(msg);
|
|
// If the port scan failed there is nothing to do than manually pressing connect again
|
|
_processState = ProcessState.Idle;
|
|
break;
|
|
case ProcessState.InitialConnect:
|
|
msg = Resources.StrCordonelAuthenticationFailed;
|
|
ErrorProcessCommon(msg);
|
|
// If initial connect failed it makes no sense to go ahead
|
|
_processState = ProcessState.Idle;
|
|
break;
|
|
case ProcessState.InitialReadRegisters:
|
|
msg = Resources.StrRegisterReadoutFailed;
|
|
ErrorProcessCommon(msg);
|
|
// Even if this state failed try to maintain the next steps of the sequence
|
|
_processState = GetNextProcessState(_invokerProcessState, _stateSequencePrepareAndCheckMeter);
|
|
break;
|
|
case ProcessState.InitialReadMeterFiles:
|
|
msg = Resources.StrFileReadoutFailed;
|
|
ErrorProcessCommon(msg);
|
|
// Even if this state failed try to maintain the next steps of the sequence
|
|
_processState = GetNextProcessState(_invokerProcessState, _stateSequencePrepareAndCheckMeter);
|
|
break;
|
|
case ProcessState.ReadInfoEraseRestoreFiles:
|
|
msg = Resources.StrFileEraseNotDefined;
|
|
ErrorProcessCommon(msg);
|
|
// Even if this state failed try to maintain the next steps of the sequence
|
|
_processState = GetNextProcessState(_invokerProcessState, _stateSequencePrepareAndCheckMeter);
|
|
break;
|
|
case ProcessState.LoadUpdateFiles:
|
|
msg = Resources.StrUpdateInformationStatusFailed;
|
|
ErrorProcessCommon(msg);
|
|
// Even if this state failed try to maintain the next steps of the sequence
|
|
_processState = GetNextProcessState(_invokerProcessState, _stateSequencePrepareAndCheckMeter);
|
|
break;
|
|
|
|
// This is the prepare meter release sequence which should be continued even if one step failed
|
|
case ProcessState.RestoreMeterFiles:
|
|
msg = Resources.StrFileRestoreFailed;
|
|
ErrorProcessCommon(msg);
|
|
_processState = GetNextProcessState(_invokerProcessState, _stateSequencePrepareMeterRelease);
|
|
break;
|
|
case ProcessState.RestoreLutFile:
|
|
//msg = Resources.StrLutFileRestoreFailed;
|
|
msg = Resources.StrLutFileCompareFailed;
|
|
ErrorProcessCommon(msg);
|
|
// If LUT file is invalid all processes have to be stopped as the meter is NOT functional.
|
|
_processState = ProcessState.Idle;
|
|
break;
|
|
case ProcessState.RecoverRegisters:
|
|
msg = Resources.StrRegisterRecoveryFailed;
|
|
ErrorProcessCommon(msg);
|
|
// Even if this state failed try to maintain the next steps of the sequence
|
|
_processState = GetNextProcessState(_invokerProcessState, _stateSequencePrepareMeterRelease);
|
|
break;
|
|
case ProcessState.FinalReadRegisters:
|
|
msg = Resources.StrRegisterReadoutFailed;
|
|
ErrorProcessCommon(msg);
|
|
_processState = GetNextProcessState(_invokerProcessState, _stateSequencePrepareMeterRelease);
|
|
break;
|
|
case ProcessState.CompareRegisters:
|
|
msg = Resources.StrRegisterCompareFailed;
|
|
ErrorProcessCommon(msg);
|
|
_processState = GetNextProcessState(_invokerProcessState, _stateSequencePrepareMeterRelease);
|
|
break;
|
|
case ProcessState.RestorePasswordFile:
|
|
msg = Resources.StrPasswordFileRestoreFailed;
|
|
ErrorProcessCommon(msg);
|
|
_processState = GetNextProcessState(_invokerProcessState, _stateSequencePrepareMeterRelease);
|
|
break;
|
|
case ProcessState.VerifyMeterFiles:
|
|
msg = Resources.StrFileRestoreFailed;
|
|
ErrorProcessCommon(msg);
|
|
_processState = GetNextProcessState(_invokerProcessState, _stateSequencePrepareMeterRelease);
|
|
break;
|
|
case ProcessState.ReadEngineeringLogs:
|
|
msg = Resources.StrEngineeringLogsReadFailed;
|
|
ErrorProcessCommon(msg);
|
|
_processState = GetNextProcessState(_invokerProcessState, _stateSequencePrepareMeterRelease);
|
|
break;
|
|
case ProcessState.CalculateLifeTime:
|
|
msg = Resources.StrLifeTimeCalculationFailed;
|
|
ErrorProcessCommon(msg);
|
|
_processState = GetNextProcessState(_invokerProcessState, _stateSequencePrepareMeterRelease);
|
|
break;
|
|
|
|
// This is the check sequence for update capability
|
|
case ProcessState.CheckFwUpdateSuccess:
|
|
msg = Resources.StrFirmwareUpdateFailed;
|
|
ErrorProcessCommon(msg);
|
|
InfoProcessFailed(lblCordonelUpdateCapability, Resources.StrFwUpdateFailed);
|
|
_processState = ProcessState.ReleaseMeterToNormalOperation;
|
|
break;
|
|
case ProcessState.CheckCoreRevision:
|
|
msg = Resources.StrCordonelUpdateCapabilityCoreFailed;
|
|
ErrorProcessCommon(msg);
|
|
_processState = ProcessState.PrepareMeterRelease;
|
|
break;
|
|
case ProcessState.CheckMetrology:
|
|
msg = Resources.StrCordonelUpdateCapabilityMetrologyFailed;
|
|
ErrorProcessCommon(msg);
|
|
_processState = ProcessState.PrepareMeterRelease;
|
|
break;
|
|
case ProcessState.CheckRegionRadio:
|
|
msg = Resources.StrCordonelUpdateCapabilityRegionFailed;
|
|
ErrorProcessCommon(msg);
|
|
_processState = ProcessState.PrepareMeterRelease;
|
|
break;
|
|
case ProcessState.CheckMeterSize:
|
|
msg = Resources.StrCordonelUpdateCapabilityMeterSizeFailed;
|
|
ErrorProcessCommon(msg);
|
|
_processState = ProcessState.PrepareMeterRelease;
|
|
break;
|
|
case ProcessState.CheckUpdateFiles:
|
|
msg = Resources.StrUpdateInformationStatusFailed;
|
|
ErrorProcessCommon(msg);
|
|
_processState = ProcessState.PrepareMeterRelease;
|
|
break;
|
|
case ProcessState.CheckCommunication:
|
|
msg = Resources.StrCordonelCommunicationFailed;
|
|
ErrorProcessCommon(msg);
|
|
_processState = ProcessState.PrepareMeterRelease;
|
|
break;
|
|
|
|
// This is the FW-Update process
|
|
case ProcessState.FirmwareUpdate:
|
|
var requiredUpdates = _meterFwUpdate?.GetFailedFileApps();
|
|
msg = Resources.StrFirmwareUpdateFailed;
|
|
if (requiredUpdates != null && requiredUpdates.Count > 0)
|
|
{
|
|
msg += $"\n{Resources.StrFailedToUpdateApp}\n";
|
|
FileApplications lastFile = null;
|
|
foreach (var file in requiredUpdates)
|
|
{
|
|
if (lastFile == null || lastFile.AppId != file.AppId)
|
|
{
|
|
msg += $"{file.AppName}\n";
|
|
}
|
|
|
|
lastFile = file;
|
|
}
|
|
}
|
|
//TODO THW initialize window with retry button or cancel, on cancel prepare meter release
|
|
ErrorProcessCommon(msg);
|
|
// A retry of the download can be done because all checks passed in advance!
|
|
SetControlDownloadEnable();
|
|
_processState = ProcessState.Idle;
|
|
break;
|
|
|
|
// Reboot to enable new FW automatically executed by the FW-Update process on trigger upgrade command
|
|
case ProcessState.Reboot:
|
|
msg = Resources.StrRebootFailed;
|
|
//TODO THW initialize window with retry button or cancel, on cancel prepare meter release
|
|
ErrorProcessCommon(msg);
|
|
_processState = ProcessState.Idle;
|
|
break;
|
|
|
|
// Validation of this SW version and due date
|
|
case ProcessState.ValidateSoftware:
|
|
MessageBoxShow(Resources.StrSoftwareVersionExpired, Resources.StrMessageWindowFailed,
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
// Close the form
|
|
Invoke(new Action(Close));
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common routine for error process with:
|
|
/// - Disable download control and enable connect,
|
|
/// - Logging to log-file,
|
|
/// - Add error message to error collection list,
|
|
/// - Message output window with user selection feedback.
|
|
/// </summary>
|
|
/// <param name="msg">message to display and log</param>
|
|
/// <param name="buttons"></param>
|
|
/// <param name="icon"></param>
|
|
/// <returns>user selection of message window</returns>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private DialogResult ErrorProcessCommon(String msg, MessageBoxButtons buttons = MessageBoxButtons.OK,
|
|
MessageBoxIcon icon = MessageBoxIcon.Asterisk)
|
|
{
|
|
SetDisableDownloadAndEnableConnect();
|
|
_logger.Error($"Slot:{Slot} - {msg}");
|
|
_errorCollectionMessages?.Add(msg);
|
|
return MessageBoxShow(msg, Resources.StrMessageWindowFailed, buttons, icon);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop all ongoing processes.
|
|
/// </summary>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-21" author="Thomas Wiedebusch">
|
|
/// - Break message dispatcher.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-22" author="Thomas Wiedebusch">
|
|
/// - Check update capability state required on exit.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Jan-05" author="Thomas Wiedebusch">
|
|
/// - Switch depending on process to create correct message.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-03" author="Thomas Wiedebusch">
|
|
/// - Dispose meter at stop during port scan to avoid lock of port.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-18" author="Thomas Wiedebusch">
|
|
/// - Enable [Connect] and [Port Scan],
|
|
/// - _processState to idle.
|
|
/// </remarks>
|
|
private void StopProcesses()
|
|
{
|
|
InfoProcessFailed(null, Resources.StrUserStop);
|
|
SetOverallProgressDisplayOff();
|
|
SetActualProgressDisplayOff();
|
|
SetDisableDownloadAndEnableConnect();
|
|
_resetTimeMeasurement = true;
|
|
if (_serialPortScanner != null)
|
|
_serialPortScanner.StopPortScan = true;
|
|
if (_meterFwUpdate != null)
|
|
_meterFwUpdate.StopUpdateProcess = true;
|
|
if (_registerRestorer != null)
|
|
_registerRestorer.StopRegisterAccess = true;
|
|
if (_meterFile != null)
|
|
_meterFile.StopProcess = true;
|
|
MessageBoxShow(Resources.StrUserStop, "Info", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
|
|
|
|
// take the invoker of the error state to generate the message and / or popup window
|
|
switch (_invokerProcessState)
|
|
{
|
|
case ProcessState.PortScan:
|
|
_meterBatch?.RemoveAllMeters();
|
|
_processState = ProcessState.Idle;
|
|
break;
|
|
default:
|
|
_processState = ProcessState.Idle;
|
|
break;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Common routine to process update event to display and log results
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2020-Dec-10" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
|
|
/// - Take all senders for processing bar and text output.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-09" author="Thomas Wiedebusch">
|
|
/// - Change to log text for RegisterRestorer and FW-Update to fill report file.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-10" author="Thomas Wiedebusch">
|
|
/// - Removed log for FW-Update process as this gives no additional useful information.
|
|
/// </remarks>
|
|
public virtual void ProcessUpdate_Event(Object sender, ProcessExecEventArgs e)
|
|
{
|
|
switch (sender)
|
|
{
|
|
case RegisterRestorer _:
|
|
if (!string.IsNullOrEmpty(e.ActualProcessMessage))
|
|
{
|
|
LogText(e.ActualProcessMessage);
|
|
}
|
|
break;
|
|
|
|
case MeterPortScanner _:
|
|
if (!string.IsNullOrEmpty(e.ActualProcessMessage))
|
|
{
|
|
LogText(e.ActualProcessMessage);
|
|
}
|
|
break;
|
|
|
|
case MeterFwUpdate _:
|
|
// Nothing to do here
|
|
break;
|
|
}
|
|
|
|
OverallProcessUpdate(e);
|
|
if (e.ActualProcessPercent != null)
|
|
ActualProcessUpdate(e);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update of overall process bar and text output
|
|
/// </summary>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2020-Dec-10" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-23" author="Thomas Wiedebusch">
|
|
/// - Invoker implemented.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
private void OverallProcessUpdate(ProcessExecEventArgs e)
|
|
{
|
|
UiInvoker.ControlInvoker(lblOverallProcess, ColorDefault, e.OverallProcessMessage);
|
|
if (e.OverallProcessPercent == null)
|
|
return;
|
|
var progress = (Int32)e.OverallProcessPercent;
|
|
UiInvoker.ProgressBarInvoker(barOverallProgressUpdate, progress > 100 ? 100 : progress);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update of single process bar and text output
|
|
/// </summary>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2020-Dec-10" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-23" author="Thomas Wiedebusch">
|
|
/// - Invoker implemented.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
private void ActualProcessUpdate(ProcessExecEventArgs e)
|
|
{
|
|
UiInvoker.ControlInvoker(lblActualProcess, ColorDefault, e.ActualProcessMessage);
|
|
if (e.ActualProcessPercent == null)
|
|
return;
|
|
var progress = (Int32)e.ActualProcessPercent;
|
|
UiInvoker.ProgressBarInvoker(barActualProgressUpdate, progress > 100 ? 100 : progress);
|
|
}
|
|
#endregion --------------------------------------- Common Process Routines ------------------------------------
|
|
|
|
#region ------------------------------------------ Reboot -----------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Update the firmware and restart the water-meter.
|
|
/// INFO: Takes a lot of time until the water-meter answers again.
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <param name="breakExitState"></param>
|
|
/// <remarks date="2020-Dec-12" author="Thomas Wiedebusch">
|
|
/// - Changed port scanner call using state responses after execution.
|
|
/// </remarks>
|
|
private void Reboot(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error,
|
|
ProcessState breakExitState = ProcessState.Stop)
|
|
{
|
|
if (_currentGenesis == null)
|
|
{
|
|
_processState = errorExitState;
|
|
return;
|
|
}
|
|
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
var exitProcessStateObjects = new Object[3];
|
|
exitProcessStateObjects[0] = successExitState;
|
|
exitProcessStateObjects[1] = errorExitState;
|
|
exitProcessStateObjects[2] = breakExitState;
|
|
|
|
var processExec = new ProcessExec();
|
|
processExec.NewProcess(InitReboot, ExecReboot, FinalizeReboot, _cultureInfo,
|
|
exitProcessStateObjects);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialization of reboot procedure.
|
|
/// </summary>
|
|
/// <remarks date="2020-Dec-12" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void InitReboot()
|
|
{
|
|
SetOverallProgressDisplayOn(Resources.StrUpdateAppAndRestartMeter);
|
|
_timerIntervalExpired = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execution of reboot.
|
|
/// </summary>
|
|
/// <returns>true if successfully executed</returns>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
|
|
/// - Timer interval expired introduced.
|
|
/// </remarks>
|
|
private StatusReturn ExecReboot()
|
|
{
|
|
String text = null;
|
|
// var timerInterval = _tmrProgressUpdate.Interval;
|
|
var timerInterval = TimerInterval_ms;
|
|
_bootDelayCtr_ms = 0;
|
|
var internalCommunicationDelayMs = _bootDelayCtr_ms + 10 * timerInterval;
|
|
|
|
while (_bootDelayCtr_ms < RebootTimeout_ms && string.IsNullOrEmpty(text))
|
|
{
|
|
if (!_timerIntervalExpired)
|
|
continue;
|
|
_timerIntervalExpired = false;
|
|
// internal communication delay to avoid overload of Cordonel
|
|
if (_bootDelayCtr_ms % internalCommunicationDelayMs == 0)
|
|
{
|
|
text = RegisterConverter.ByteArrayToValue<String>(
|
|
_currentGenesis.ReadRegister(Register.Configexchange.PcbSerialNumber, 12));
|
|
}
|
|
|
|
ProcessUpdate_Event(this, new ProcessExecEventArgs(Resources.StrUpdateAppAndRestartMeter,
|
|
_bootDelayCtr_ms * 100.0 / RebootTimeout_ms));
|
|
_bootDelayCtr_ms += timerInterval;
|
|
}
|
|
|
|
return string.IsNullOrEmpty(text) ? StatusReturn.Failed : StatusReturn.Okay;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finalization of reboot.
|
|
/// </summary>
|
|
/// <param name="success">successful execution</param>
|
|
/// <param name="exitProcessStateObjects">objects containing process states sorted success-, error-,
|
|
/// break exit state</param>
|
|
/// <remarks date="2020-Dec-12" author="Thomas Wiedebusch">
|
|
/// - Taking objects as representative for finalizing process states.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
|
|
/// - Using success from execute routine
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
|
|
/// - Change the process state before setting the SetOverallProgressDisplayOff as timer may reactivate it.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-16" author="Thomas Wiedebusch">
|
|
/// - Set date and time.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-23" author="Thomas Wiedebusch">
|
|
/// - Check all installed meter apps.
|
|
/// </remarks>
|
|
/// <remarks date="2024-Feb-12" author="Thomas Wiedebusch">
|
|
/// - Request new pcbId as the auto-detected may be corrupt.
|
|
/// </remarks>
|
|
private void FinalizeReboot(StatusReturn success, IReadOnlyList<Object> exitProcessStateObjects)
|
|
{
|
|
ObjectsToProcessStates(exitProcessStateObjects, out var successExitState, out var errorExitState,
|
|
out _);
|
|
|
|
// try to login to check if meter is ready after reboot procedure
|
|
_currentGenesis.GetPcbId();
|
|
_currentGenesis.ReLogin();
|
|
|
|
// Check status of installed applications
|
|
_meterFwUpdate?.CompareAllMeterAndFileApps();
|
|
|
|
_processState = _currentGenesis.IsLoggedOn ? successExitState : errorExitState;
|
|
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
|
|
// set the time
|
|
SetDateTime();
|
|
|
|
}
|
|
#endregion --------------------------------------- Reboot -----------------------------------------------------
|
|
|
|
#region ------------------------------------------ Port Scan --------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Execution of port scan.
|
|
/// INFO: Very time consuming because of trial to open the port listed in Win-Device-Manager,
|
|
/// the more ports listed, the more time will be needed. This may last a few minutes!
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <param name="breakExitState"></param>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-12" author="Thomas Wiedebusch">
|
|
/// - Changed port scanner call using state responses after execution.
|
|
/// </remarks>
|
|
private void PortScanner(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error,
|
|
ProcessState breakExitState = ProcessState.Stop)
|
|
{
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
var exitProcessStateObjects = new Object[3];
|
|
exitProcessStateObjects[0] = successExitState;
|
|
exitProcessStateObjects[1] = errorExitState;
|
|
exitProcessStateObjects[2] = breakExitState;
|
|
|
|
var processExec = new ProcessExec();
|
|
processExec.NewProcess(InitPortScanner, ExecPortScanner, FinalizePortScanner, _cultureInfo,
|
|
exitProcessStateObjects);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialization of port scanner.
|
|
/// </summary>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void InitPortScanner()
|
|
{
|
|
//initialize the port type if not read from file
|
|
_portType = "Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.IrdaSerialPort";
|
|
if (_serialPortScanner == null)
|
|
_serialPortScanner = new MeterPortScanner(_portType);
|
|
//set the process bars
|
|
SetOverallProgressDisplayOn();
|
|
SetControlsCommunicationActive();
|
|
|
|
_serialPortScanner.OnProcessUpdate += ProcessUpdate_Event;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execution of port scanner.
|
|
/// </summary>
|
|
/// <returns>true if successfully executed</returns>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private StatusReturn ExecPortScanner()
|
|
{
|
|
_serialPortScanner.ScanAllSerialPorts(Slot, _basePath?.PortConfigFilePathName);
|
|
return _serialPortScanner.AutoDetectedPortName == null ? StatusReturn.Failed : StatusReturn.Okay;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finalization of port scanner.
|
|
/// </summary>
|
|
/// <param name="success">successful execution</param>
|
|
/// <param name="exitProcessStateObjects">objects containing process states sorted success-, error-,
|
|
/// break exit state</param>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-12" author="Thomas Wiedebusch">
|
|
/// - Taking objects as representative for finalizing process states.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
|
|
/// - Using success from execute routine
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-19" author="Thomas Wiedebusch">
|
|
/// - Reworked exit state.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-22" author="Thomas Wiedebusch">
|
|
/// - Result display moved to here.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Jan-05" author="Thomas Wiedebusch">
|
|
/// - Removed additional stop user stop message,
|
|
/// - Dispose port scanner.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
|
|
/// - Change the process state before setting the SetOverallProgressDisplayOff as timer may reactivate it.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mai-11" author="Thomas Wiedebusch">
|
|
/// - Delay after port scan to free serial port for new assignment.
|
|
/// </remarks>
|
|
private void FinalizePortScanner(StatusReturn success, IReadOnlyList<Object> exitProcessStateObjects)
|
|
{
|
|
if (_serialPortScanner != null)
|
|
_serialPortScanner.OnProcessUpdate -= ProcessUpdate_Event;
|
|
|
|
ObjectsToProcessStates(exitProcessStateObjects, out var successExitState, out var errorExitState,
|
|
out var breakExitState);
|
|
|
|
if (_processState == breakExitState)
|
|
{
|
|
_serialPortScanner?.Dispose();
|
|
SetOverallProgressDisplayOff();
|
|
return;
|
|
}
|
|
|
|
if (success != StatusReturn.Okay)
|
|
{
|
|
// If the IrDA port scan failed, it makes no sense to signal Cordonel Detection failed!
|
|
InfoProcessFailed(null, Resources.StrRequestPortFailed);
|
|
_processState = errorExitState;
|
|
_serialPortScanner?.Dispose();
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
return;
|
|
}
|
|
|
|
_portConfig = new PortConfig
|
|
{
|
|
PortName = _serialPortScanner?.AutoDetectedPortName,
|
|
Type = _portType
|
|
};
|
|
|
|
_serialPortScanner?.Dispose();
|
|
InfoProcessSuccess(lblCordonelDetection, Resources.StrCordonelDetectSucceeded);
|
|
|
|
_processState = successExitState;
|
|
// wait to free the serial port
|
|
GC.Collect();
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
}
|
|
#endregion --------------------------------------- Port Scan --------------------------------------------------
|
|
|
|
#region ------------------------------------------ Connect ----------------------------------------------------
|
|
/// <summary>
|
|
/// Establish connection to genesis water meter
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <param name="breakExitState"></param>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-12" author="Thomas Wiedebusch">
|
|
/// - Changed port scanner call using state responses after execution.
|
|
/// </remarks>
|
|
private void Connect(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error,
|
|
ProcessState breakExitState = ProcessState.Stop)
|
|
{
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
var exitProcessStateObjects = new Object[3];
|
|
exitProcessStateObjects[0] = successExitState;
|
|
exitProcessStateObjects[1] = errorExitState;
|
|
exitProcessStateObjects[2] = breakExitState;
|
|
|
|
var processExec = new ProcessExec();
|
|
processExec.NewProcess(InitConnect, ExecConnect, FinalizeConnect, _cultureInfo, exitProcessStateObjects);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialization of connect to water-meter.
|
|
/// </summary>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mai-11" author="Thomas Wiedebusch">
|
|
/// - Delay at start up to allow dispose for eventually assigned port from scanner.
|
|
/// </remarks>
|
|
private void InitConnect()
|
|
{
|
|
SetControlsCommunicationActive();
|
|
Thread.Sleep(1000);
|
|
if (_currentGenesis == null)
|
|
{
|
|
//assign new meter and assign meter to FW update file if this exists
|
|
_currentGenesis = new GenesisMeter(Slot, _portConfig, null)
|
|
{
|
|
Configuration =
|
|
{
|
|
//configuration has to be set BEFORE adding meter to batch to avoid e.g. auto update files
|
|
//from network and therefore have a long network request timeout before the task starts
|
|
UseRegisterWatchService = false,
|
|
UseMinMaxCheck = false,
|
|
AutoUpdateFiles = false
|
|
}
|
|
};
|
|
_meterBatch?.AddMeter(_currentGenesis);
|
|
}
|
|
|
|
if (_currentGenesis == null)
|
|
{
|
|
_processState = ProcessState.Error;
|
|
return;
|
|
}
|
|
|
|
//assign register restorer or set new created Genesis to access the post update register restorer
|
|
if (_registerRestorer == null)
|
|
_registerRestorer = new RegisterRestorer(_currentGenesis);
|
|
else
|
|
_registerRestorer.AssignGenesis(_currentGenesis);
|
|
|
|
SetOverallProgressDisplayOn(Resources.StrConnecting);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execution of connect to water-meter.
|
|
/// Sequence:
|
|
/// - Check if pcbId of meter is set, if not this is an initial login. On initial login it has to require
|
|
/// the pcbId (read it from the meter) and mark this initial access as this will execute some special
|
|
/// steps <see cref="LoginAndSpecialSetupProcedure"/>,
|
|
/// - Create the report files for the detected pcbId,
|
|
/// - Searches for update information extracted from the FW update safe and extract password level 8 and
|
|
/// the skeleton key,
|
|
/// - Delays 3s as a previous login may have failed to avoid blocking of login procedure,
|
|
/// - Tries to login with password ( this is the level 8 pwd), exit here if successfully logged in,
|
|
///
|
|
/// - If login with password level 8 failed:
|
|
/// - The password file may be corrupted or not installed,
|
|
/// - Delays 5s,
|
|
/// - Try to login with skeletonKey and exit if successfully logged in, else mark procedure as failed.
|
|
/// </summary>
|
|
/// <returns>true if successfully executed</returns>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-18" author="Thomas Wiedebusch">
|
|
/// - Returns true if _currentGenesis is logged on.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Jan-29" author="Thomas Wiedebusch">
|
|
/// - Check if password file is installed by logging in with password Level 8, if this is not possible
|
|
/// the password file may be corrupted or not installed, then try the skeletonKey and remind the
|
|
/// password file error being able to restore it.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-02" author="Thomas Wiedebusch">
|
|
/// - If login with password Level 8 failed, keep delay and try login with skeletonKey.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-03" author="Thomas Wiedebusch">
|
|
/// - Reset marker for password corruption.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-15" author="Thomas Wiedebusch">
|
|
/// - Display information if Genesis is not in update list (password = null and skeletonKey = null).
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-12" author="Thomas Wiedebusch">
|
|
/// - Additional delay before first login to avoid lock if login has been tried just before,
|
|
/// - Check for final login as this allows EXPLICIT login with password level 8 to validate password file.
|
|
/// </remarks>
|
|
/// <remarks date="2022-Mai-10" author="Thomas Wiedebusch">
|
|
/// - Execute even if still logged on to meter,
|
|
/// - Store all configurations .
|
|
/// </remarks>
|
|
/// <remarks date="2023-Feb-17" author="Thomas Wiedebusch">
|
|
/// - Switch radio on if not NA version.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Mar-01" author="Thomas Wiedebusch">
|
|
/// - "Release Display" before "Store Configuration".
|
|
/// </remarks>
|
|
/// <remarks date="2023-Mar-07" author="Thomas Wiedebusch">
|
|
/// - "Deactivate Pulse Mode" at initial login to avoid interference of pulses to IrDA communication.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Aug-22" author="Thomas Wiedebusch">
|
|
/// - Create report file early to catch radio and pulse mode activity.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-11" author="Thomas Wiedebusch">
|
|
/// - AVOID marking of _passwordFile is corrupted on initial login as the pulse output may be active
|
|
/// and needs to be switched OFF first in the <see cref="LoginAndSpecialSetupProcedure"/>,
|
|
/// - Verifies password file of meter by reading it out and comparing it with the password file
|
|
/// from the FW update safe, remind the hashedPwdFile being able to restore it if not installed,
|
|
/// - Additional logging of password login or skeleton login,
|
|
/// - Report customer serial number.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-18" author="Thomas Wiedebusch">
|
|
/// - Restructured.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-18" author="Thomas Wiedebusch">
|
|
/// - Avoid report file generation on finalLoginAfterUpdate.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-27" author="Thomas Wiedebusch">
|
|
/// - Message moved to <see cref="ReadLogAndCompareMeterPwdFile"/>.
|
|
/// </remarks>
|
|
/// <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>
|
|
/// <remarks date="2023-Nov-22" author="Thomas Wiedebusch">
|
|
/// - Added password valid information main screen and log file.
|
|
/// </remarks>
|
|
/// <remarks date="2024-May-07" author="Thomas Wiedebusch">
|
|
/// - Added power correction on FW update to specific version.
|
|
/// </remarks>
|
|
private StatusReturn ExecConnect()
|
|
{
|
|
// If the PCB ID is not set (read from meter) this is the first initial login.
|
|
if (string.IsNullOrEmpty(_currentGenesis.PcbId))
|
|
{
|
|
// Read out the PCB ID from water-meter needed to open the password container of this device
|
|
_currentGenesis.GetPcbId();
|
|
// Create report file
|
|
if (_initialConnect)
|
|
{
|
|
BuildReportFiles();
|
|
}
|
|
}
|
|
|
|
// Search the PCB ID and extract password for login
|
|
_skeletonKey = null;
|
|
_passwordLvl8 = null;
|
|
_genesisInUpdateList = false;
|
|
if (_cordonelDeviceInfos != null)
|
|
{
|
|
foreach (var device in _cordonelDeviceInfos.Where(device => _currentGenesis.PcbId == device.PcbId))
|
|
{
|
|
_skeletonKey = device.PwdContainer.SkeletonKey;
|
|
_passwordLvl8 = device.PwdContainer.PasswordLvl8;
|
|
_customerSerialNumber = device.CustomerSerialNumber;
|
|
|
|
// extract the hashed safe password file for validation
|
|
_fwUpdateSafePwdFile = new Byte[MeterPwdDb.PwdFileLength];
|
|
_fwUpdateSafePwdFile = device.PwdContainer.EncryptedPasswordFile;
|
|
|
|
// get LUT file content from FW update safe
|
|
_fwUpdateSafeLutFile = device.LutFile;
|
|
// extract the power corrections and required FW update version
|
|
_fwUpdatePowerCorrection = device.PowerCorrectionValues;
|
|
|
|
// mark that meter is in update list
|
|
_genesisInUpdateList = true;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (_passwordLvl8 == null || _skeletonKey == null)
|
|
{
|
|
_processState = ProcessState.Error;
|
|
return StatusReturn.Failed;
|
|
}
|
|
|
|
// 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);
|
|
InfoProcessSuccess(lblPasswordFileCheck, Resources.StrPasswordFileValid);
|
|
_passwordFileIsCorrupted = false;
|
|
return StatusReturn.Okay;
|
|
}
|
|
|
|
// 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);
|
|
|
|
// 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 StatusReturn.Okay;
|
|
}
|
|
|
|
_processState = ProcessState.Error;
|
|
return StatusReturn.Failed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common login and special setup procedure, as this is needed for login with password level 8
|
|
/// and for login with skeleton key:
|
|
/// - Initial login without register readout being able to quick switch off the pulse mode,
|
|
/// - Switch off the pulse mode on initial connection,
|
|
/// - Activate the radio in customer mode,
|
|
/// - Release the display to normal operation,
|
|
/// - Setup date and time,
|
|
/// - Store all configurations on initial connection,
|
|
/// - Logs the password file content if not initial connected.
|
|
/// </summary>
|
|
/// <param name="password">lvl8 or skeleton</param>
|
|
/// <returns>true if successfully executed</returns>
|
|
/// <remarks date="2023-Mar-07" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-18" author="Thomas Wiedebusch">
|
|
/// - Date time setup,
|
|
/// - Read and log meter password file.
|
|
/// </remarks>
|
|
/// <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>
|
|
/// <remarks date="2023-Nov-24" author="Thomas Wiedebusch">
|
|
/// - Fill data grid with all FW infos.
|
|
/// </remarks>
|
|
/// <remarks date="2024-Feb-20" author="Thomas Wiedebusch">
|
|
/// - Changed program flow.
|
|
/// </remarks>
|
|
/// <remarks date="2024-Apr-24" author="Thomas Wiedebusch">
|
|
/// - Update app information on new connect.
|
|
/// </remarks>
|
|
/// <remarks date="2024-May-15" author="Thomas Wiedebusch">
|
|
/// - Remind installed FW version on initial connect to _fwUpdatePowerCorrection as this will be lost
|
|
/// after FW update in _currentGenesis.InstalledFwVersion and set to the version after the update.
|
|
/// </remarks>
|
|
private Boolean LoginAndSpecialSetupProcedure(String password)
|
|
{
|
|
Boolean retVal;
|
|
var pulseModeOffRetryCtr = 1;
|
|
if (_initialConnect)
|
|
{
|
|
// Start with a fresh list of errors to decide after the entire procedure how to proceed
|
|
_errorCollectionMessages?.Clear();
|
|
|
|
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();
|
|
|
|
// Reset login delay as on successfully login the meter resets the retry lock time
|
|
_loginDelay_ms = DefaultLoginDelay_ms;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
// Second login if initial was executed including reading out all installed applications
|
|
retVal = _currentGenesis.Login(password);
|
|
|
|
// Set radio to customer mode (encryption active)
|
|
if (_initialConnect && retVal)
|
|
{
|
|
if (_fwUpdatePowerCorrection == null)
|
|
{
|
|
//do something
|
|
_fwUpdatePowerCorrection = new PowerCorrection();
|
|
}
|
|
|
|
// fill all currently known infos to power correction
|
|
_fwUpdatePowerCorrection.ActualPulseAdapterIsInstalled = _currentGenesis.PulseAdapterAutoDetected;
|
|
_fwUpdatePowerCorrection.ActualFwVersion = _currentGenesis.InstalledFwVersion;
|
|
|
|
if (_currentGenesis.RadioFrequencyMhz != null)
|
|
{
|
|
SetRadioToCustomerMode();
|
|
}
|
|
|
|
// safe all settings from RAM to FLASH to keep those for reboot
|
|
StoreConfigurations();
|
|
|
|
// Read out the installed password file for logging and compare it with the required one
|
|
ReadLogAndCompareMeterPwdFile();
|
|
|
|
// remind initial connect has been executed once
|
|
_initialConnect = false;
|
|
}
|
|
|
|
// display to normal operation and stores the setup permanently
|
|
retVal = ReleaseDisplayAndSwitchLedOff();
|
|
|
|
// Check status of installed applications
|
|
_meterFwUpdate?.AssignGenesis(_currentGenesis);
|
|
_meterFwUpdate?.CompareAllMeterAndFileApps();
|
|
FillDataGridWithAllInfos();
|
|
|
|
return retVal;
|
|
}
|
|
/// <summary>
|
|
/// Finalize of connection process to water-meter.
|
|
/// </summary>
|
|
/// <param name="success">successful execution</param>
|
|
/// <param name="exitProcessStateObjects">objects containing process states sorted success-, error-,
|
|
/// break exit state</param>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-12" author="Thomas Wiedebusch">
|
|
/// - Taking objects as representative for finalizing process states.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
|
|
/// - Using success from execute routine
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-19" author="Thomas Wiedebusch">
|
|
/// - Reworked exit state.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-22" author="Thomas Wiedebusch">
|
|
/// - Label corrected for user stop.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-23" author="Thomas Wiedebusch">
|
|
/// - Added Cordonel detected.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Jan-05" author="Thomas Wiedebusch">
|
|
/// - Removed additional stop user stop message.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-03" author="Thomas Wiedebusch">
|
|
/// - Log information if password file is not installed.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-15" author="Thomas Wiedebusch">
|
|
/// - Display information if Genesis is not in update list.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-25" author="Thomas Wiedebusch">
|
|
/// - Used UiInvoker
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-10" author="Thomas Wiedebusch">
|
|
/// - Build report files added.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
|
|
/// - Change the process state before setting the SetOverallProgressDisplayOff as timer may reactivate it.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-15" author="Thomas Wiedebusch">
|
|
/// - Avoid report of versions on final login after update, this is already reported.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-20" author="Thomas Wiedebusch">
|
|
/// - Final login message produced to log password file validation.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Mar-07" author="Thomas Wiedebusch">
|
|
/// - Enable connect if error.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Aug-22" author="Thomas Wiedebusch">
|
|
/// - Report file generation moved to <see cref="ExecConnect"/>.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-15" author="Thomas Wiedebusch">
|
|
/// - Log always installed meter FW.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-22" author="Thomas Wiedebusch">
|
|
/// - Final login is not a grant for valid password file anymore.
|
|
/// </remarks>
|
|
private void FinalizeConnect(StatusReturn success, IReadOnlyList<Object> exitProcessStateObjects)
|
|
{
|
|
ObjectsToProcessStates(exitProcessStateObjects, out var successExitState, out var errorExitState,
|
|
out var breakExitState);
|
|
|
|
if (_processState == breakExitState)
|
|
{
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
return;
|
|
}
|
|
|
|
if (success != StatusReturn.Okay)
|
|
{
|
|
InfoProcessFailed(lblCordonelAuthentication, !_genesisInUpdateList
|
|
? $"{_currentGenesis.PcbId} {Resources.StrCordonelNotInUpdateList}"
|
|
: Resources.StrCordonelAuthenticationFailed);
|
|
|
|
_processState = errorExitState;
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
SetDisableDownloadAndEnableConnect();
|
|
return;
|
|
}
|
|
|
|
_meterFwUpdate?.AssignGenesis(_currentGenesis);
|
|
|
|
// if authentication succeeded, the cordonel port detection must have succeeded in advance,
|
|
// this information might get lost on Connect() without previous port scan.
|
|
InfoProcessSuccess(lblCordonelDetection, Resources.StrCordonelDetectSucceeded);
|
|
InfoProcessSuccess(lblCordonelAuthentication, Resources.StrCordonelAuthenticationSucceeded);
|
|
|
|
// use here direct text assignment to avoid x or v before label
|
|
UiInvoker.ControlInvoker(lblConnectPcb, ColorSuccess,
|
|
Resources.StrPartPcbConnected + @" " + _currentGenesis.PcbId);
|
|
UiInvoker.ControlInvoker(lblCoreRevision, ColorDefault,
|
|
Resources.StrCoreRevision + @" " + $"{_currentGenesis.StrCoreRevision}");
|
|
|
|
LogInstalledMeterFw();
|
|
_processState = successExitState;
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
}
|
|
#endregion --------------------------------------- Connect ----------------------------------------------------
|
|
|
|
#region ------------------------------------------ Register Read ----------------------------------------------
|
|
/// <summary>
|
|
/// Execution of register read.
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <param name="breakExitState"></param>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-12" author="Thomas Wiedebusch">
|
|
/// - Changed port scanner call using state responses after execution.
|
|
/// </remarks>
|
|
private void ReadRegisters(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error,
|
|
ProcessState breakExitState = ProcessState.Stop)
|
|
{
|
|
if (_currentGenesis == null || _registerRestorer == null)
|
|
{
|
|
_processState = errorExitState;
|
|
return;
|
|
}
|
|
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
var exitProcessStateObjects = new Object[3];
|
|
exitProcessStateObjects[0] = successExitState;
|
|
exitProcessStateObjects[1] = errorExitState;
|
|
exitProcessStateObjects[2] = breakExitState;
|
|
|
|
var processExec = new ProcessExec();
|
|
processExec.NewProcess(InitReadRegister, ExecReadRegister, FinalReadRegisters,
|
|
_cultureInfo, exitProcessStateObjects);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialization of register backup.
|
|
/// </summary>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void InitReadRegister()
|
|
{
|
|
SetControlsAllButtonsDisabled();
|
|
if (_invokerProcessState == ProcessState.CompareRegisters)
|
|
{
|
|
InfoProcessActive(lblRegisterReadout, Resources.StrRegisterCompareActive);
|
|
}
|
|
else
|
|
{
|
|
InfoProcessActive(lblRegisterReadout, Resources.StrRegisterReadoutActive);
|
|
}
|
|
//set the process bars
|
|
SetOverallProgressDisplayOn(Resources.StrRegisterReadoutActive);
|
|
_registerRestorer.OnProcessUpdate += ProcessUpdate_Event;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execution of register read.
|
|
/// </summary>
|
|
/// <returns>true if successfully executed</returns>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-23" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-21" author="Thomas Wiedebusch">
|
|
/// - Skip final read and compare if not updated or no recovery request.
|
|
/// </remarks>
|
|
private StatusReturn ExecReadRegister()
|
|
{
|
|
Boolean retVal;
|
|
switch (_invokerProcessState)
|
|
{
|
|
case ProcessState.InitialReadRegisters:
|
|
retVal = _registerRestorer.InitialReadRegisters();
|
|
break;
|
|
|
|
case ProcessState.FinalReadRegisters:
|
|
if (_afterUpdateConnect || _recoveryRegistersRequired)
|
|
{
|
|
retVal = _registerRestorer.FinalReadRegisters();
|
|
}
|
|
else
|
|
{
|
|
retVal = true;
|
|
}
|
|
break;
|
|
|
|
case ProcessState.CompareRegisters:
|
|
if (_afterUpdateConnect || _recoveryRegistersRequired)
|
|
{
|
|
retVal = _registerRestorer.CompareRegisters();
|
|
}
|
|
else
|
|
{
|
|
retVal = true;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
retVal = _registerRestorer.ReadRegisters();
|
|
break;
|
|
}
|
|
return retVal ? StatusReturn.Okay : StatusReturn.Failed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finalization of register reading.
|
|
/// </summary>
|
|
/// <param name="success">successful execution</param>
|
|
/// <param name="exitProcessStateObjects">objects containing process states sorted success-, error-,
|
|
/// break exit state</param>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-12" author="Thomas Wiedebusch">
|
|
/// - Taking objects as representative for finalizing process states.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
|
|
/// - Using success from execute routine
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-19" author="Thomas Wiedebusch">
|
|
/// - Reworked exit state.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
|
|
/// - Change the process state before setting the SetOverallProgressDisplayOff as timer may reactivate it.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-21" author="Thomas Wiedebusch">
|
|
/// - Context depending message.
|
|
/// </remarks>
|
|
private void FinalReadRegisters(StatusReturn success, IReadOnlyList<Object> exitProcessStateObjects)
|
|
{
|
|
if (_registerRestorer != null)
|
|
_registerRestorer.OnProcessUpdate -= ProcessUpdate_Event;
|
|
|
|
ObjectsToProcessStates(exitProcessStateObjects, out var successExitState, out var errorExitState,
|
|
out var breakExitState);
|
|
|
|
if (_processState == breakExitState)
|
|
{
|
|
InfoProcessFailed(lblRegisterReadout, Resources.StrRegisterUserBreak);
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
return;
|
|
}
|
|
|
|
String msg;
|
|
if (success == StatusReturn.Okay)
|
|
{
|
|
switch (_invokerProcessState)
|
|
{
|
|
case ProcessState.InitialReadRegisters:
|
|
msg = Resources.StrRegisterReadoutSucceeded;
|
|
break;
|
|
case ProcessState.FinalReadRegisters:
|
|
msg = Resources.StrRegisterReadoutSucceeded;
|
|
break;
|
|
case ProcessState.CompareRegisters:
|
|
msg = Resources.StrRegisterCompareSucceeded;
|
|
break;
|
|
default:
|
|
msg = Resources.StrRegisterReadoutSucceeded;
|
|
break;
|
|
}
|
|
InfoProcessSuccess(lblRegisterReadout, msg);
|
|
_processState = successExitState;
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
return;
|
|
}
|
|
|
|
switch (_invokerProcessState)
|
|
{
|
|
case ProcessState.InitialReadRegisters:
|
|
msg = Resources.StrRegisterReadoutFailed;
|
|
break;
|
|
case ProcessState.FinalReadRegisters:
|
|
msg = Resources.StrRegisterReadoutFailed;
|
|
break;
|
|
case ProcessState.CompareRegisters:
|
|
msg = Resources.StrRegisterCompareFailed;
|
|
break;
|
|
default:
|
|
msg = Resources.StrRegisterReadoutFailed;
|
|
break;
|
|
}
|
|
InfoProcessFailed(lblRegisterReadout, msg);
|
|
_processState = errorExitState;
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
}
|
|
#endregion --------------------------------------- Register Read ----------------------------------------------
|
|
|
|
#region ------------------------------------------ File Access ------------------------------------------------
|
|
/// <summary>
|
|
/// Read all meter files for erase and restore from file defined in FW-Update safe.
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <remarks date="2021-Feb-16" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-12" author="Thomas Wiedebusch">
|
|
/// - Added password file to erase if password file is marked as corrupted. It has to be replaced!!!!
|
|
/// </remarks>
|
|
private void PrepareInfoMeterFilesEraseRestore(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error)
|
|
{
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
try
|
|
{
|
|
// loading the lists of meter files which shall be erased before the FW-Update and restored after.
|
|
_meterFilesEraseRestore = new MeterFilesEraseRestore();
|
|
|
|
if (_basePath.MeterFilesConfigFilePathName != null &&
|
|
File.Exists(_basePath.MeterFilesConfigFilePathName))
|
|
{
|
|
using (var tr = new StreamReader(_basePath.MeterFilesConfigFilePathName))
|
|
{
|
|
var fileStream = tr.ReadToEnd();
|
|
_meterFilesEraseRestore = JsonConvert.DeserializeObject<MeterFilesEraseRestore>(fileStream);
|
|
}
|
|
|
|
if (_meterFilesEraseRestore != null)
|
|
{
|
|
// If the password file is corrupted, it has to be erased. The restore loop will re-install a valid
|
|
// password-file
|
|
if (_passwordFileIsCorrupted)
|
|
_meterFilesEraseRestore.Erase.Add(MeterPwdFile.StrPasswordFileName);
|
|
|
|
_processState = successExitState;
|
|
return;
|
|
}
|
|
}
|
|
|
|
InfoProcessFailed(lblFileErase, Resources.StrFileEraseNotDefined);
|
|
InfoProcessFailed(lblFileRestore, Resources.StrFileRestoreNotDefined);
|
|
_processState = errorExitState;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show(ex.Message);
|
|
InfoProcessFailed(lblFileErase, Resources.StrFileEraseNotDefined);
|
|
InfoProcessFailed(lblFileRestore, Resources.StrFileRestoreNotDefined);
|
|
_processState = errorExitState;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reading all meter files and log the content of it.
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <remarks date="2021-Feb-15" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
|
|
/// - Change the process state before setting the SetOverallProgressDisplayOff as timer may reactivate it.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-19" author="Thomas Wiedebusch">
|
|
/// - Output message changed.
|
|
/// </remarks>
|
|
/// <remarks date="2024-Mar-12" author="Thomas Wiedebusch">
|
|
/// - If verify meter files succeeded the lblFileErase can be set to success as every file could be read.
|
|
/// </remarks>
|
|
/// <remarks date="2024-Apr-09" author="Thomas Wiedebusch">
|
|
/// - ReLogin replaced with Logout, Delay 250 ms and Login.
|
|
/// </remarks>
|
|
private void ReadMeterFiles(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error)
|
|
{
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
// return on unassigned meter
|
|
if (_currentGenesis != null)
|
|
{
|
|
SetControlsCommunicationActive();
|
|
SetOverallProgressDisplayOn(Resources.StrFileAccessActive);
|
|
|
|
switch (_invokerProcessState)
|
|
{
|
|
case ProcessState.InitialReadMeterFiles:
|
|
case ProcessState.PreUpdateReadMeterFiles:
|
|
InfoProcessActive(lblFileErase, Resources.StrFileAccessActive, false);
|
|
break;
|
|
case ProcessState.VerifyMeterFiles:
|
|
InfoProcessActive(lblFileErase, Resources.StrFileAccessActive, false);
|
|
InfoProcessActive(lblFileRestore, Resources.StrFileAccessActive, false);
|
|
break;
|
|
}
|
|
|
|
_currentGenesis.Logout();
|
|
Thread.Sleep(250);
|
|
_currentGenesis.Login();
|
|
// read all meter files
|
|
_meterFile = new MeterFile(_currentGenesis);
|
|
|
|
if (_readMeterFiles == null)
|
|
_readMeterFiles = new List<String>();
|
|
_readMeterFiles.Clear();
|
|
|
|
if (_meterFile.ReadMeterFileCatalog(out var meterFilesDrive0, MeterFile.StrMeterDrive0))
|
|
{
|
|
_readMeterFiles.AddRange(meterFilesDrive0);
|
|
}
|
|
if (_meterFile.ReadMeterFileCatalog(out var meterFilesDrive1, MeterFile.StrMeterDrive1))
|
|
{
|
|
_readMeterFiles.AddRange(meterFilesDrive1);
|
|
}
|
|
if (_readMeterFiles.Count > 0)
|
|
{
|
|
_processState = successExitState;
|
|
SetOverallProgressDisplayOff();
|
|
switch (_invokerProcessState)
|
|
{
|
|
case ProcessState.InitialReadMeterFiles:
|
|
case ProcessState.PreUpdateReadMeterFiles:
|
|
InfoProcessSuccess(lblFileErase, Resources.StrFileReadoutSucceeded, false);
|
|
break;
|
|
case ProcessState.VerifyMeterFiles:
|
|
InfoProcessSuccess(lblFileErase, Resources.StrFileReadoutSucceeded, false);
|
|
InfoProcessSuccess(lblFileRestore, Resources.StrFileReadoutSucceeded, false);
|
|
break;
|
|
}
|
|
//log files
|
|
LogStringList(Resources.StrFileRead, _readMeterFiles, Resources.StrFileReadoutSucceeded);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (_invokerProcessState)
|
|
{
|
|
case ProcessState.InitialReadMeterFiles:
|
|
case ProcessState.PreUpdateReadMeterFiles:
|
|
InfoProcessFailed(lblFileErase, Resources.StrFileReadoutFailed, false);
|
|
break;
|
|
case ProcessState.VerifyMeterFiles:
|
|
InfoProcessFailed(lblFileRestore, Resources.StrFileReadoutFailed, false);
|
|
break;
|
|
}
|
|
//log files
|
|
LogStringList(Resources.StrFileRead, _readMeterFiles, errorMessage: Resources.StrFileReadoutFailed);
|
|
|
|
_processState = errorExitState;
|
|
SetOverallProgressDisplayOff();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to erase unused files to free space for the FW-Update files.
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <remarks date="2021-Feb-16" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
|
|
/// - Change the process state before setting the SetOverallProgressDisplayOff as timer may reactivate it.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-19" author="Thomas Wiedebusch">
|
|
/// - Output message changed to get colored error or success messages.
|
|
/// </remarks>
|
|
private void MeterFilesErase(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error)
|
|
{
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
// return on unassigned meter
|
|
if (_currentGenesis != null)
|
|
{
|
|
SetControlsCommunicationActive();
|
|
InfoProcessActive(lblFileErase, Resources.StrFileAccessActive);
|
|
_currentGenesis.ReLogin();
|
|
// read all meter files
|
|
var meterFile = new MeterFile(_currentGenesis);
|
|
var erasedMeterFiles = new List<String>();
|
|
var filesToErase = new List<String>();
|
|
var wildcardList = new List<String>();
|
|
|
|
// check if any file has to be erased
|
|
if (_meterFilesEraseRestore?.Erase != null && _meterFilesEraseRestore.Erase.Count > 0 &&
|
|
_readMeterFiles != null && _readMeterFiles.Count > 0)
|
|
{
|
|
// build a wildcard list and remove wildcard '*'
|
|
foreach (var meterFileToEraseMask in _meterFilesEraseRestore.Erase.Where(
|
|
x => x.Contains(Constants.StrWildcard)))
|
|
{
|
|
var wildcardMask = meterFileToEraseMask.Split('*');
|
|
wildcardList.Add(wildcardMask[0]);
|
|
}
|
|
|
|
// build a new list of all files referenced by wildcard search
|
|
filesToErase.AddRange(_readMeterFiles.Where(readMeterFile =>
|
|
wildcardList.Any(readMeterFile.Contains)));
|
|
// add all exactly named files
|
|
filesToErase.AddRange(_readMeterFiles.Where(readMeterFile =>
|
|
_meterFilesEraseRestore.Erase.Any(readMeterFile.Contains)));
|
|
|
|
// erase all files from list
|
|
foreach (var eraseFile in filesToErase)
|
|
{
|
|
SetOverallProgressDisplayOn(Resources.StrFileErasing + eraseFile);
|
|
|
|
// here the real erase will be executed
|
|
if (!meterFile.UnlockEraseWriteMeterFile(eraseFile))
|
|
continue;
|
|
if (meterFile.EraseMeterFile(eraseFile))
|
|
{
|
|
erasedMeterFiles.Add(eraseFile);
|
|
}
|
|
}
|
|
|
|
if (erasedMeterFiles.Count == filesToErase.Count)
|
|
{
|
|
_processState = successExitState;
|
|
InfoProcessSuccess(lblFileErase, Resources.StrFileEraseSucceeded, false);
|
|
//log files
|
|
LogStringList(Resources.StrFileErased, erasedMeterFiles, Resources.StrFileEraseSucceeded);
|
|
SetOverallProgressDisplayOff();
|
|
return;
|
|
}
|
|
}
|
|
LogStringList(Resources.StrFileErased, erasedMeterFiles, errorMessage: Resources.StrFileReadoutFailed);
|
|
|
|
}
|
|
InfoProcessFailed(lblFileErase, Resources.StrFileReadoutFailed, false);
|
|
_processState = errorExitState;
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to install the erased files.
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <remarks date="2021-Feb-16" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private void MeterFilesRestore(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error)
|
|
{
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
// return on unassigned meter
|
|
if (_currentGenesis != null)
|
|
{
|
|
SetControlsCommunicationActive();
|
|
InfoProcessActive(lblFileRestore, Resources.StrFileAccessActive);
|
|
|
|
_currentGenesis.ReLogin();
|
|
var meterFile = new MeterFile(_currentGenesis);
|
|
var restoredMeterFiles = new List<String>();
|
|
|
|
// check if any file has to be restored
|
|
if (_meterFilesEraseRestore?.Restore != null)
|
|
{
|
|
// check if file exists which should be restored
|
|
foreach (var restoreFile in _meterFilesEraseRestore.Restore)
|
|
{
|
|
if (_readMeterFiles != null)
|
|
{
|
|
// skip file restore if this file has already been created
|
|
// e.g. an application may create a file on re-boot
|
|
if (_readMeterFiles.Contains(restoreFile.Name))
|
|
{
|
|
// this file will be put to list even if the application has created it by itself
|
|
restoredMeterFiles.Add(restoreFile.Name);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
SetOverallProgressDisplayOn(Resources.StrFileRestoring + restoreFile.Name);
|
|
// this is the restore execution
|
|
if (!meterFile.UnlockEraseWriteMeterFile(restoreFile.Name))
|
|
continue;
|
|
if (meterFile.CreateEmptyMeterFile(restoreFile.Name, restoreFile.ByteSize))
|
|
{
|
|
restoredMeterFiles.Add(restoreFile.Name);
|
|
}
|
|
}
|
|
|
|
if (restoredMeterFiles.Count > 0)
|
|
{
|
|
//log files
|
|
LogStringList(Resources.StrFileRestored, restoredMeterFiles);
|
|
}
|
|
|
|
if (restoredMeterFiles.Count == _meterFilesEraseRestore.Restore.Count)
|
|
{
|
|
_processState = successExitState;
|
|
InfoProcessSuccess(lblFileRestore, Resources.StrFileRestoreSucceeded);
|
|
SetOverallProgressDisplayOff();
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
InfoProcessFailed(lblFileRestore, Resources.StrFileRestoreFailed);
|
|
_processState = errorExitState;
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to install the password file if this is corrupted, Genesis has to be logged in.
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <remarks date="2021-Jan-29" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-02" author="Thomas Wiedebusch">
|
|
/// - Message if succeeded.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-03" author="Thomas Wiedebusch">
|
|
/// - Reset marker for password corruption.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-18" author="Thomas Wiedebusch">
|
|
/// - Password restored message included if password file was present at all times.
|
|
/// - After reinstalling the password file a login will be forced with the connect status request
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
|
|
/// - Change the process state before setting the SetOverallProgressDisplayOff as timer may reactivate it.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-12" author="Thomas Wiedebusch">
|
|
/// - Mark for final login as this allows EXPLICIT login with password level 8 to validate password file.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-20" author="Thomas Wiedebusch">
|
|
/// - Changed password file valid message.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-26" author="Thomas Wiedebusch">
|
|
/// - Checked for password file not in update container.
|
|
/// </remarks>
|
|
/// <remarks date="2022-Apr-28" author="Thomas Wiedebusch">
|
|
/// - Release display to normal operation,
|
|
/// - switch measurement LED off.
|
|
/// </remarks>
|
|
/// <remarks date="2022-Mai-10" author="Thomas Wiedebusch">
|
|
/// - Release display removed.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-11" author="Thomas Wiedebusch">
|
|
/// - Hashed password file read on connect from FW update safe.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-24" author="Thomas Wiedebusch">
|
|
/// - Read and log password file after restoring.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-27" author="Thomas Wiedebusch">
|
|
/// - Removed check for password file length, leave it to the <see cref="MeterPwdFile.CheckHashedPwdFile"/>
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-21" author="Thomas Wiedebusch">
|
|
/// - Removed state change to final connect.
|
|
/// </remarks>
|
|
private void RestorePasswordFile(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error)
|
|
{
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
// if the password file is valid exit
|
|
if (!_passwordFileIsCorrupted)
|
|
{
|
|
InfoProcessSuccess(lblPasswordFileCheck, Resources.StrPasswordFileValid);
|
|
_processState = successExitState;
|
|
return;
|
|
}
|
|
|
|
InfoProcessActive(lblPasswordFileCheck, Resources.StrPasswordFileRestoreActive);
|
|
// try to install password, first get the password file from device info
|
|
var pwdFileObject = new MeterPwdFile(_currentGenesis);
|
|
if (_fwUpdateSafePwdFile != null &&
|
|
pwdFileObject.CheckHashedPwdFile(_fwUpdateSafePwdFile, _skeletonKey, _passwordLvl8))
|
|
{
|
|
SetControlsCommunicationActive();
|
|
_currentGenesis?.ReLogin();
|
|
if (pwdFileObject.UnlockEraseWriteMeterPwdFile())
|
|
{
|
|
if (pwdFileObject.WriteAndVerifyMeterPwdFile(_fwUpdateSafePwdFile) &&
|
|
ReadLogAndCompareMeterPwdFile())
|
|
{
|
|
_passwordFileIsCorrupted = false;
|
|
_afterUpdateConnect = false;
|
|
InfoProcessSuccess(lblPasswordFileCheck, Resources.StrPasswordFileRestoreSucceeded);
|
|
_processState = successExitState;
|
|
}
|
|
else
|
|
{
|
|
InfoProcessFailed(lblPasswordFileCheck, Resources.StrPasswordFileRestoreFailed);
|
|
_processState = errorExitState;
|
|
}
|
|
}
|
|
|
|
}
|
|
// the password file is NOT in the password container
|
|
else
|
|
{
|
|
InfoProcessFailed(lblPasswordFileCheck, Resources.StrPasswordFileFromSafeInvalid);
|
|
_processState = errorExitState;
|
|
}
|
|
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads the LUT and restores it if different from given LUT in FwUpdateSafe.
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <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>
|
|
/// <remarks date="2023-Nov-22" author="Thomas Wiedebusch">
|
|
/// - Taking meter response of invalid LUT file indicated by 0x8000xxxx.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-27" author="Thomas Wiedebusch">
|
|
/// - Added check for string LutCrc is not null or empty.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Nov-28" author="Thomas Wiedebusch">
|
|
/// - Use installed FW version to decide if LUT is required.
|
|
/// </remarks>
|
|
/// <remarks date="2024-May-16" author="Thomas Wiedebusch">
|
|
/// - Uses LUT CRC and file list for installation requirement.
|
|
/// </remarks>
|
|
private void RestoreLutFile(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error)
|
|
{
|
|
// Remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
InfoProcessActive(lblLutFileCheck, Resources.StrLutFileRestoreActive, false);
|
|
|
|
// 1. LUT file is needed but invalid
|
|
if (!string.IsNullOrEmpty(_currentGenesis.LutCrc) &&
|
|
_currentGenesis.LutCrc.Contains(MeterLutFile.StrLutFileInvalidCrc))
|
|
{
|
|
InfoProcessFailed(lblLutFileCheck, Resources.StrLutFileNotInstalled);
|
|
_processState = errorExitState;
|
|
return;
|
|
}
|
|
|
|
// 2. LUT file NOT installed:
|
|
// If meter LUT CRC is not set and the meter files do not report the LUT, the meter does not have an
|
|
// installed LUT
|
|
if ((string.IsNullOrEmpty(_currentGenesis.LutCrc) ||
|
|
_currentGenesis.LutCrc.Equals(Constants.StrUnknown))
|
|
&& _readMeterFiles != null
|
|
&& !_readMeterFiles.Any(f => f.Contains(MeterLutFile.StrLutMeterFileName))
|
|
&& !_readMeterFiles.Any(f => f.Contains(MeterLutFile.StrLutBackupMeterFileName)))
|
|
{
|
|
// 2. a) LUT file not installed but required by the FW update safe:
|
|
// If the safe contains a LUT file, the installation of it has failed or it got lost during
|
|
// update
|
|
if (_fwUpdateSafeLutFile != null)
|
|
{
|
|
InfoProcessFailed(lblLutFileCheck, Resources.StrLutFileNotInstalled);
|
|
_processState = errorExitState;
|
|
return;
|
|
}
|
|
|
|
// 2. 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;
|
|
}
|
|
|
|
// 3. LUT file is installed:
|
|
// 3.a) Lut file is installed but NOT in the safe:
|
|
// If the LUT is not in the safe it cannot be restored or compared to the required content but can
|
|
// be analyzed and logged
|
|
// 3.b) LUT file is installed and delivered by the FW update safe:
|
|
// On validated installed LUT file in meter this can be compared with the required LUT file
|
|
if (!ReadLogAndCompareMeterLutFile())
|
|
{
|
|
// Set label but do not log as this will be done by ReadLogAndCompareMeterLutFile
|
|
InfoProcessFailed(lblLutFileCheck, Resources.StrLutFileCompareFailed, false);
|
|
_processState = errorExitState;
|
|
}
|
|
else
|
|
{
|
|
// Set label but do not log as this will be done by ReadLogAndCompareMeterLutFile
|
|
InfoProcessSuccess(lblLutFileCheck, Resources.StrLutFileCompareSucceeded, false);
|
|
_processState = successExitState;
|
|
}
|
|
|
|
// Change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
}
|
|
|
|
#endregion --------------------------------------- File Access ------------------------------------------------
|
|
|
|
#region ------------------------------------------ Register Recovery -----------------------------------------
|
|
/// <summary>
|
|
/// Execution of register recovery.
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <param name="breakExitState"></param>
|
|
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void RegisterRecovery(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error,
|
|
ProcessState breakExitState = ProcessState.Stop)
|
|
{
|
|
if (_currentGenesis == null || _registerRestorer == null)
|
|
{
|
|
_processState = errorExitState;
|
|
return;
|
|
}
|
|
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
var exitProcessStateObjects = new Object[3];
|
|
exitProcessStateObjects[0] = successExitState;
|
|
exitProcessStateObjects[1] = errorExitState;
|
|
exitProcessStateObjects[2] = breakExitState;
|
|
|
|
var processExec = new ProcessExec();
|
|
processExec.NewProcess(InitRegisterRecovery, ExecRegisterRecovery, FinalizeRegisterRecovery,
|
|
_cultureInfo, exitProcessStateObjects);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialization of register backup.
|
|
/// </summary>
|
|
/// <remarks date="2020-Dec-18" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void InitRegisterRecovery()
|
|
{
|
|
SetControlsAllButtonsDisabled();
|
|
InfoProcessActive(lblRegisterRestore, Resources.StrRegisterAccessActive);
|
|
//set the process bars
|
|
SetOverallProgressDisplayOn(Resources.StrRegisterAccessActive);
|
|
_registerRestorer.OnProcessUpdate += ProcessUpdate_Event;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execution of register recovery.
|
|
///
|
|
/// ATTENTION:
|
|
/// Do NOT sort the recovery registers as those needed to be executed in the correct sequence, as those
|
|
/// are preparing the meter to a special mode to take the values and finalize the settings.
|
|
///
|
|
/// </summary>
|
|
/// <returns>true if successfully executed</returns>
|
|
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-09" author="Thomas Wiedebusch">
|
|
/// - Recovery registers at initial login.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Oct-26" author="Thomas Wiedebusch">
|
|
/// - Removed redundant marker for recovery required.
|
|
/// </remarks>
|
|
private StatusReturn ExecRegisterRecovery()
|
|
{
|
|
var retVal = StatusReturn.Okay;
|
|
_recoveryRegistersRequired = false;
|
|
if (_cordonelDeviceInfos != null)
|
|
{
|
|
foreach (var device in _cordonelDeviceInfos.Where(device => _currentGenesis.PcbId == device.PcbId))
|
|
{
|
|
// If recovery registers not defined, skip this step with success
|
|
if (device.RecoveryRegisters == null || device.RecoveryRegisters.Count == 0)
|
|
return StatusReturn.Okay;
|
|
|
|
retVal = _registerRestorer.WriteRegisters(device.RecoveryRegisters);
|
|
break;
|
|
}
|
|
|
|
_recoveryRegistersRequired = true;
|
|
Thread.Sleep(1000);
|
|
if (!StoreConfigurations())
|
|
retVal = StatusReturn.Failed;
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finalization of register restore.
|
|
/// </summary>
|
|
/// <param name="success">successful execution</param>
|
|
/// <param name="exitProcessStateObjects">objects containing process states sorted success-, error-,
|
|
/// break exit state</param>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-12" author="Thomas Wiedebusch">
|
|
/// - Taking objects as representative for finalizing process states.es
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-18" author="Thomas Wiedebusch">
|
|
/// - Using success from execute routine
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-19" author="Thomas Wiedebusch">
|
|
/// - Reworked exit state.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-07" author="Thomas Wiedebusch">
|
|
/// - Change the process state before setting the SetOverallProgressDisplayOff as timer may reactivate it.
|
|
/// </remarks>
|
|
private void FinalizeRegisterRecovery(StatusReturn success, IReadOnlyList<Object> exitProcessStateObjects)
|
|
{
|
|
if (_registerRestorer != null)
|
|
_registerRestorer.OnProcessUpdate -= ProcessUpdate_Event;
|
|
|
|
ObjectsToProcessStates(exitProcessStateObjects, out var successExitState, out var errorExitState,
|
|
out var breakExitState);
|
|
|
|
|
|
if (_processState == breakExitState)
|
|
{
|
|
InfoProcessFailed(lblRegisterRestore, Resources.StrRegisterUserBreak);
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
return;
|
|
}
|
|
|
|
if (success == StatusReturn.Okay)
|
|
{
|
|
InfoProcessSuccess(lblRegisterRestore, Resources.StrRegisterRecoverySucceeded, false);
|
|
_processState = successExitState;
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
return;
|
|
}
|
|
|
|
if (success == StatusReturn.Warning)
|
|
{
|
|
InfoProcessWarning(lblRegisterRestore, Resources.StrRegisterRecoveryWarning, false);
|
|
// but go ahead to continue writing of registers
|
|
_processState = successExitState;
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
return;
|
|
}
|
|
|
|
InfoProcessFailed(lblRegisterRestore, Resources.StrRegisterRecoveryFailed, false);
|
|
_processState = errorExitState;
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
}
|
|
#endregion --------------------------------------- Register Recovery -----------------------------------------
|
|
|
|
#region ------------------------------------------ FW-Update --------------------------------------------------
|
|
/// <summary>
|
|
/// Execution of FW-Update.
|
|
/// </summary>
|
|
/// <param name="successExitState"></param>
|
|
/// <param name="errorExitState"></param>
|
|
/// <param name="breakExitState"></param>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-12" author="Thomas Wiedebusch">
|
|
/// - Changed port scanner call using state responses after execution.
|
|
/// </remarks>
|
|
private void FwUpdate(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error,
|
|
ProcessState breakExitState = ProcessState.Stop)
|
|
{
|
|
if (_currentGenesis == null || _meterFwUpdate?.FileApps == null ||
|
|
_registerRestorer == null)
|
|
{
|
|
_processState = errorExitState;
|
|
return;
|
|
}
|
|
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
var exitProcessStateObjects = new Object[3];
|
|
exitProcessStateObjects[0] = successExitState;
|
|
exitProcessStateObjects[1] = errorExitState;
|
|
exitProcessStateObjects[2] = breakExitState;
|
|
|
|
var processExec = new ProcessExec();
|
|
processExec.NewProcess(InitFwUpdate, ExecFwUpdate, FinalizeFwUpdate, _cultureInfo,
|
|
exitProcessStateObjects);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialization of FW-Update.
|
|
/// </summary>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void InitFwUpdate()
|
|
{
|
|
_meterFwUpdate.MaxPartialFileDataSize = MaxPartialFileDataSize;
|
|
_meterFwUpdate.OnProcessUpdate += ProcessUpdate_Event;
|
|
|
|
SetControlsCommunicationActive();
|
|
SetActualProgressDisplayOn();
|
|
SetOverallProgressDisplayOn();
|
|
InfoProcessActive(lblFirmwareUpdate, Resources.StrFirmwareUpdateOngoing);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execution of FW-Update.
|
|
/// </summary>
|
|
/// <returns>true if successfully executed</returns>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private StatusReturn ExecFwUpdate()
|
|
{
|
|
return _meterFwUpdate.UpdateMeterFw() ? StatusReturn.Okay : StatusReturn.Failed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finalization of FW-Update.
|
|
/// </summary>
|
|
/// <param name="success">successful execution</param>
|
|
/// <param name="exitProcessStateObjects">objects containing process states sorted success-, error-,
|
|
/// break exit state</param>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-12" author="Thomas Wiedebusch">
|
|
/// - Taking objects as representative for finalizing process states.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-18" author="Thomas Wiedebusch">
|
|
/// - Using success from execute routine.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-19" author="Thomas Wiedebusch">
|
|
/// - Reworked exit state.
|
|
/// </remarks>
|
|
private void FinalizeFwUpdate(StatusReturn success, IReadOnlyList<Object> exitProcessStateObjects)
|
|
{
|
|
if (_meterFwUpdate != null)
|
|
_meterFwUpdate.OnProcessUpdate -= ProcessUpdate_Event;
|
|
|
|
ObjectsToProcessStates(exitProcessStateObjects, out var successExitState, out var errorExitState,
|
|
out var breakExitState);
|
|
|
|
SetActualProgressDisplayOff();
|
|
SetOverallProgressDisplayOff();
|
|
|
|
if (_processState == breakExitState)
|
|
{
|
|
InfoProcessFailed(lblFirmwareUpdate, Resources.StrFirmwareUpdateBreak);
|
|
return;
|
|
}
|
|
|
|
if (success == StatusReturn.Okay)
|
|
{
|
|
InfoProcessActive(lblFirmwareUpdate, Resources.StrFirmwareUpdateCheck);
|
|
_processState = successExitState;
|
|
return;
|
|
}
|
|
|
|
InfoProcessFailed(lblFirmwareUpdate, Resources.StrFirmwareUpdateFailed);
|
|
_processState = errorExitState;
|
|
}
|
|
#endregion --------------------------------------- FW-Update --------------------------------------------------
|
|
|
|
#region ------------------------------------------ Reports ----------------------------------------------------
|
|
/// <summary>
|
|
/// Build the report files for application list being installed and report log.
|
|
/// </summary>
|
|
/// <remarks date="2021-Mar-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-10" author="Thomas Wiedebusch">
|
|
/// - Changed function type as this is not time consuming.
|
|
/// - Implemented directory creation if it does not exists.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-29" author="Thomas Wiedebusch">
|
|
/// - Common definition for report- and app version extensions.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Apr-12" author="Thomas Wiedebusch">
|
|
/// - Logging additional information: Order number, Customer, FwUpdateSw version, Date.
|
|
/// </remarks>
|
|
private Boolean BuildReportFiles()
|
|
{
|
|
try
|
|
{
|
|
if (_basePath == null || string.IsNullOrEmpty(_basePath.CustomerAndOrderNumber) ||
|
|
_currentGenesis == null || string.IsNullOrEmpty(_currentGenesis.PcbId))
|
|
{
|
|
InfoProcessFailed(null, Resources.StrTestReportUnspecified);
|
|
return false;
|
|
}
|
|
|
|
var reportNameBase = _currentGenesis.PcbId + FwUpdateConfig.FwUpdateFileFieldSeparator +
|
|
_basePath.CustomerAndOrderNumber;
|
|
var nameReportFile = reportNameBase + FwUpdateConfig.ReportFileExtension;
|
|
var nameCordonelAppVersionFile = reportNameBase + FwUpdateConfig.AppVersionFileExtension;
|
|
|
|
if (!Directory.Exists(FwUpdateConfig.UnreportedPath))
|
|
{
|
|
Directory.CreateDirectory(FwUpdateConfig.UnreportedPath);
|
|
}
|
|
|
|
_pathNameReportFile = Path.Combine(FwUpdateConfig.UnreportedPath, nameReportFile);
|
|
_pathNameCordonelAppVersionFile = Path.Combine(FwUpdateConfig.UnreportedPath,
|
|
nameCordonelAppVersionFile);
|
|
|
|
if (!string.IsNullOrEmpty(_pathNameReportFile) &&
|
|
!string.IsNullOrEmpty(_pathNameCordonelAppVersionFile))
|
|
{
|
|
LogText($"{Resources.StrTestReportDate} {DateTime.UtcNow:dddd, dd-MMM-yyyy HH:mm:ss} UTC");
|
|
LogText($"{Resources.StrTestReportSwNameVersion} " +
|
|
$"{_version.Major}.{_version.Minor}.{_version.Build}");
|
|
LogText(StrSeparator);
|
|
|
|
if (_fwUpdateSafeInfo != null)
|
|
{
|
|
LogText($"{Resources.StrTestReporSafeName} " + _fwUpdateSafeInfo.FwUpdateSafeName);
|
|
LogText($"{Resources.StrTestReportFwUpdateValidDate} " +
|
|
$"{_fwUpdateSafeInfo.FwUpdateValidDate:dddd, dd-MMM-yyyy HH:mm:ss} UTC");
|
|
LogText($"{Resources.StrTestReportFwUpdateFieldOperator} " + _fwUpdateSafeInfo.FwUpdateFieldOperatorId);
|
|
LogText($"{Resources.StrTestReportFwUpdateBuilderInfo} " + _fwUpdateSafeInfo.FwUpdateBuilderInfo);
|
|
LogText($"{Resources.StrTestReportSafeBuiltDate} " +
|
|
$"{_fwUpdateSafeInfo.SafeBuiltDateTime:dddd, dd-MMM-yyyy HH:mm:ss} UTC");
|
|
LogText($"{Resources.StrTestReportSafeBuiltOperator} " + _fwUpdateSafeInfo.FwUpdateBuilderOperatorId);
|
|
|
|
LogText(StrSeparator);
|
|
LogText($"{Resources.StrTestReportCustomerName} " + _fwUpdateSafeInfo.CustomerName);
|
|
LogText($"{Resources.StrTestReportCustomerNumber} " + _fwUpdateSafeInfo.CustomerNumber);
|
|
LogText($"{Resources.StrTestReportOrderNumber} " + _fwUpdateSafeInfo.FwUpdateOrderPosition);
|
|
}
|
|
else
|
|
{
|
|
// CustomerName_OrderNumber
|
|
var customerOrderSplit =
|
|
_basePath.CustomerAndOrderNumber.Split(FwUpdateConfig.FwUpdateFileFieldSeparator);
|
|
LogText($"{Resources.StrTestReportCustomerName} {customerOrderSplit[0]}");
|
|
LogText($"{Resources.StrTestReportOrderNumber} {customerOrderSplit[1]}");
|
|
}
|
|
LogText(StrSeparator);
|
|
return true;
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
InfoProcessFailed(null, Resources.StrTestReportFailed);
|
|
return false;
|
|
}
|
|
InfoProcessFailed(null, Resources.StrTestReportFailed);
|
|
return false;
|
|
}
|
|
#endregion --------------------------------------- Report -----------------------------------------------------
|
|
|
|
#region ------------------------------------------ Language ---------------------------------------------------
|
|
/// <summary>
|
|
/// Select language at runtime: English
|
|
/// </summary>
|
|
/// <remarks date="2020-Nov-27" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-23" author="Thomas Wiedebusch">
|
|
/// - Change menu.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-26" author="Thomas Wiedebusch">
|
|
/// - Avoid repetition if current culture is equal to required.
|
|
/// </remarks>
|
|
private void RadioBtnEnglishLanguage_Click(Object sender, EventArgs e)
|
|
{
|
|
if (Thread.CurrentThread.CurrentCulture.Name == "en-GB")
|
|
return;
|
|
_cultureInfo = new CultureInfo("en-GB");
|
|
ChangeLanguageControls();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Select language at runtime: German
|
|
/// </summary>
|
|
/// <remarks date="2020-Nov-27" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-23" author="Thomas Wiedebusch">
|
|
/// - Change menu.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-26" author="Thomas Wiedebusch">
|
|
/// - Avoid repetition if current culture is equal to required.
|
|
/// </remarks>
|
|
private void RadioBtnGermanLanguage_Click(Object sender, EventArgs e)
|
|
{
|
|
if (Thread.CurrentThread.CurrentCulture.Name == "de-DE")
|
|
return;
|
|
_cultureInfo = new CultureInfo("de-DE");
|
|
ChangeLanguageControls();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Change language at runtime
|
|
/// </summary>
|
|
/// <remarks date="2021-Feb-23" author="Thomas Wiedebusch">
|
|
/// - Initial based on code example
|
|
/// https://stackoverflow.com/questions/52178064/winforms-localization-how-to-change-the-language-of-a-menu.
|
|
/// </remarks>
|
|
private void ChangeLanguageControls()
|
|
{
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
var resources = new ComponentResourceManager(typeof(FrmServiceFwUpdateSw));
|
|
resources.ApplyResources(this, "$this");
|
|
ControlExtensions.ChangeControlText(resources, Controls);
|
|
// connect again if already connected to update all information
|
|
if (_currentGenesis != null && !string.IsNullOrEmpty(_currentGenesis.PcbId))
|
|
_processState = ProcessState.InitialConnect;
|
|
ResetAllStatusLabels();
|
|
lblFwUpdateInfo.Text = $@"Version: {_version.Major}.{_version.Minor}.{_version.Build}";
|
|
// Display new history
|
|
ClearHistoryWindow();
|
|
}
|
|
|
|
#endregion --------------------------------------- Language ---------------------------------------------------
|
|
|
|
}
|
|
}
|
|
|