3932 lines
175 KiB
C#
3932 lines
175 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.Registers;
|
|
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
|
|
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;
|
|
|
|
// 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 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 RebootTimeoutMs = 120000;
|
|
private Int32 _bootDelayCtrMs;
|
|
private Boolean _timerIntervalExpired;
|
|
private Boolean _timerDisplayOn;
|
|
private Int32 _progressBarValueCounter;
|
|
|
|
// reminder for corrupted password file
|
|
private Boolean _passwordFileIsCorrupted;
|
|
// remind final login, as this allows EXPLICIT login with password level 8 to validate password file
|
|
private Boolean _finalLoginAfterUpdate;
|
|
// reminder for Genesis found in update list (_cordonelDeviceInfos)
|
|
private Boolean _genesisInUpdateList;
|
|
|
|
// status information
|
|
private static readonly Color ColorDefault = Color.Black;
|
|
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 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();
|
|
|
|
// update information
|
|
private Boolean _binaryPackageLoaded;
|
|
|
|
// license setup with start up check delay to end program if license is unknown
|
|
private SoftwareLicense _softwareLicense;
|
|
private Int32 _licenseCheckTimerMs = 2000;
|
|
|
|
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;
|
|
|
|
// directory information
|
|
private List<String> _readMeterFiles;
|
|
|
|
/// <summary>
|
|
/// Timer for progress update
|
|
/// </summary>
|
|
private readonly System.Threading.Timer _tmrProgressUpdate;
|
|
private const Int32 TimerIntervalMs = 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>
|
|
private void FwUpdateSwStateMachine()
|
|
{
|
|
while (!_processToken.IsCancellationRequested)
|
|
{
|
|
if (_lastProcessState == _processState)
|
|
{
|
|
Thread.Sleep(1);
|
|
// do nothing until state changed
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
_lastProcessState = _processState;
|
|
switch (_processState)
|
|
{
|
|
case ProcessState.ValidateSoftware:
|
|
ValidateSoftware(ProcessState.Idle);
|
|
break;
|
|
|
|
case ProcessState.Idle:
|
|
break;
|
|
|
|
case ProcessState.Stop:
|
|
// stop clears all status's and ongoing processes
|
|
StopProcesses();
|
|
// leave process state stop on stop
|
|
break;
|
|
|
|
case ProcessState.Error:
|
|
// stop clears all status's and ongoing processes
|
|
ErrorProcesses();
|
|
_processState = ProcessState.Idle;
|
|
break;
|
|
|
|
case ProcessState.Init:
|
|
DisposeGenesis();
|
|
ResetAllStatusLabels();
|
|
SetDisableDownloadAndEnableConnect();
|
|
_processState = ProcessState.Idle;
|
|
break;
|
|
|
|
case ProcessState.Reboot:
|
|
Reboot(ProcessState.Connect);
|
|
break;
|
|
|
|
case ProcessState.Connect:
|
|
// Dispose Genesis to force new register read!
|
|
DisposeGenesis();
|
|
Connect(_finalLoginAfterUpdate
|
|
? ProcessState.CheckUpdateRequest
|
|
: ProcessState.ReadMeterFilesConfig);
|
|
break;
|
|
|
|
case ProcessState.QuickConnect:
|
|
Connect(ProcessState.LoadUpdateFiles);
|
|
break;
|
|
|
|
case ProcessState.LoadUpdateFiles:
|
|
LoadUpdateFiles(ProcessState.CheckUpdateCapability);
|
|
break;
|
|
|
|
case ProcessState.CheckUpdateCapability:
|
|
CheckUpdateCapability(ProcessState.CheckUpdateRequest);
|
|
break;
|
|
|
|
case ProcessState.CheckUpdateRequest:
|
|
CheckUpdateRequest(ProcessState.Idle, ProcessState.RegisterRestore);
|
|
break;
|
|
|
|
case ProcessState.RegisterRestore:
|
|
// if registers are already restored on subsequent FW-Update trials
|
|
// this step can be skipped.
|
|
if (_registerRestorer != null && _registerRestorer.RegisterRestoreExecuted())
|
|
_processState = ProcessState.ReadPostUpdateMeterFiles;
|
|
else
|
|
RegisterRestore(ProcessState.ReadPostUpdateMeterFiles);
|
|
break;
|
|
|
|
case ProcessState.PortScan:
|
|
ResetAllStatusLabels();
|
|
PortScanner(ProcessState.Connect);
|
|
break;
|
|
|
|
case ProcessState.RegisterBackup:
|
|
// if registers are already backed up on subsequent FW-Update trials
|
|
// this step can be skipped. Additional the meter files read and erase must be
|
|
// skipped to avoid erasure of successfully downloaded upgXX - Files!
|
|
if (_registerRestorer != null && _registerRestorer.RegisterBackupExecuted())
|
|
_processState = ProcessState.ReadPreUpdateMeterFiles;
|
|
else
|
|
RegisterBackup(ProcessState.ReadInitialMeterFiles);
|
|
break;
|
|
|
|
case ProcessState.ReadMeterFilesConfig:
|
|
// even if the read of config file failed it should be continued,
|
|
// but erasure doesn't make any sense, so this has to be skipped by directly calling
|
|
// the ReadPreUpdateMeterFiles instead of ReadInitialMeterFiles
|
|
MeterFilesConfigRead(ProcessState.LoadUpdateFiles, ProcessState.LoadUpdateFiles);
|
|
break;
|
|
|
|
case ProcessState.ReadInitialMeterFiles:
|
|
// even if the read failed it should be continued with the FW-Update
|
|
MeterFilesRead(ProcessState.EraseMeterFiles, ProcessState.FirmwareUpdate);
|
|
break;
|
|
|
|
case ProcessState.EraseMeterFiles:
|
|
// even if the erasure failed it should be continued
|
|
MeterFilesErase(ProcessState.ReadPreUpdateMeterFiles,
|
|
ProcessState.ReadPreUpdateMeterFiles);
|
|
break;
|
|
|
|
case ProcessState.ReadPreUpdateMeterFiles:
|
|
// even if the pre update read failed it should be continued
|
|
MeterFilesRead(ProcessState.FirmwareUpdate, ProcessState.FirmwareUpdate);
|
|
break;
|
|
|
|
case ProcessState.ReadPostUpdateMeterFiles:
|
|
// even if the read of meter files failed it should be continued
|
|
MeterFilesRead(ProcessState.RestoreMeterFiles, ProcessState.RestoreMeterFiles);
|
|
break;
|
|
|
|
case ProcessState.RestoreMeterFiles:
|
|
// even if the restore of meter files failed it should be continued
|
|
MeterFilesRestore(ProcessState.VerifyMeterFiles, ProcessState.VerifyMeterFiles);
|
|
break;
|
|
|
|
case ProcessState.VerifyMeterFiles:
|
|
// even if the verification of meter files failed it should be continued
|
|
MeterFilesRead(ProcessState.PwdFileRestore, ProcessState.PwdFileRestore);
|
|
break;
|
|
|
|
case ProcessState.FirmwareUpdate:
|
|
FwUpdate(ProcessState.Reboot);
|
|
break;
|
|
|
|
case ProcessState.FirmwareUpdateSuccess:
|
|
ReleaseDisplay();
|
|
RestoreRegisters();
|
|
FwUpdateSuccessMessage();
|
|
_processState = ProcessState.Idle;
|
|
break;
|
|
|
|
case ProcessState.PwdFileRestore:
|
|
PwdFileRestore(ProcessState.FirmwareUpdateSuccess);
|
|
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>
|
|
//private void TmrProgressUpdate_Tick(Object sender, EventArgs e)
|
|
private void TmrProgressUpdate_Tick(Object state)
|
|
{
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
|
|
switch (_processState)
|
|
{
|
|
case ProcessState.Idle:
|
|
if (_softwareLicense == null && (_licenseCheckTimerMs -= TimerIntervalMs) < 0)
|
|
_processState = ProcessState.ValidateSoftware;
|
|
break;
|
|
case ProcessState.EraseMeterFiles:
|
|
case ProcessState.RestoreMeterFiles:
|
|
case ProcessState.ReadInitialMeterFiles:
|
|
case ProcessState.ReadPostUpdateMeterFiles:
|
|
case ProcessState.VerifyMeterFiles:
|
|
case ProcessState.ReadPreUpdateMeterFiles:
|
|
case ProcessState.PwdFileRestore:
|
|
case ProcessState.Connect:
|
|
// 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>
|
|
// 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.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>
|
|
public FrmServiceFwUpdateSw()
|
|
{
|
|
_logger = NLogHelper.CreateOrGetLogger("ServiceFwUpdateSw");
|
|
_version = Assembly.GetExecutingAssembly().GetName().Version;
|
|
|
|
// Log version to files for debug purposes
|
|
_logger.Info(StrSeparator);
|
|
_logger.Info($"Service FW-Update SW 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;
|
|
}
|
|
|
|
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()
|
|
{
|
|
// 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>
|
|
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);
|
|
|
|
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);
|
|
|
|
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;
|
|
|
|
_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;
|
|
}
|
|
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
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>
|
|
private void UpdateInformationStyleSet()
|
|
{
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
|
|
if (_meterFwUpdate == null)
|
|
return;
|
|
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.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.MeterAppNotInstalled:
|
|
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>
|
|
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;
|
|
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
|
|
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.RequiredRelease)
|
|
{
|
|
// 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))
|
|
{
|
|
_binaryPackageLoaded = true;
|
|
_processState = successExitState;
|
|
break;
|
|
}
|
|
InfoProcessFailed(lblUpdateInformationStatus, Resources.StrUpdateInformationStatusFailed);
|
|
_processState = errorExitState;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
InfoProcessFailed(lblUpdateInformationStatus, Resources.StrUpdateInformationStatusFailed);
|
|
_processState = errorExitState;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate 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>
|
|
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;
|
|
|
|
_meterFwUpdate?.AssignGenesis(_currentGenesis);
|
|
_meterFwUpdate?.CompareAllMeterAndFileApps();
|
|
Invoke(new Action(() =>
|
|
{
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
if (!CheckPackageFileAndFileApps())
|
|
{
|
|
_processState = errorExitState;
|
|
// overwrite the invoker state to create a message
|
|
_invokerProcessState = ProcessState.CheckUpdateFiles;
|
|
return;
|
|
}
|
|
FillDataGridWithAllInfos();
|
|
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"></param>
|
|
/// <param name="breakExitState"></param>
|
|
/// <remarks date="2020-Dec-21" author="Thomas Wiedebusch">
|
|
/// - Initial based on previous implementation of CheckUpdateEnabled().
|
|
/// </remarks>
|
|
private void CheckUpdateRequest(ProcessState successExitState, ProcessState breakExitState)
|
|
{
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
// this check has been placed here to force display update
|
|
if (_currentGenesis.MeterAppListVersion.Any(f => f.Update) ||
|
|
_currentGenesis.MeterAppListVersion.Any(f => f.Erase))
|
|
{
|
|
// an update needs to be executed
|
|
_processState = successExitState;
|
|
}
|
|
else
|
|
{
|
|
// Nothing has to be updated, the latest FW is installed from program handler
|
|
// it stops operation with break for FW-Update
|
|
_processState = breakExitState;
|
|
}
|
|
}
|
|
|
|
/// <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()
|
|
{
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
|
|
// 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!
|
|
LogText(StrSeparator);
|
|
LogErrorText(Resources.StrCoreRevision);
|
|
LogErrorText(@"Minimum :" + _meterFwUpdate.CoreRevisionMinimum / 100 + ":" + _meterFwUpdate.CoreRevisionMinimum % 100);
|
|
LogErrorText(@"Maximum :" + _meterFwUpdate.CoreRevisionMaximum / 100 + "." + _meterFwUpdate.CoreRevisionMaximum % 100);
|
|
LogErrorText(@"Cordonel :" + GenesisMeter.BuildFwVersionString(_currentGenesis.CoreRevision));
|
|
InfoProcessFailed(lblCordonelUpdateCapability, Resources.StrCordonelUpdateCapabilityCoreFailed);
|
|
_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()
|
|
{
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
|
|
// 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>
|
|
/// 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()
|
|
{
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
|
|
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!
|
|
LogText(StrSeparator);
|
|
LogErrorText(Resources.StrFwPackageInfo + " " + fwPackRegion);
|
|
LogErrorText(@"Cordonel: " + _currentGenesis.Region);
|
|
InfoProcessFailed(lblCordonelUpdateCapability, Resources.StrCordonelUpdateCapabilityRegionFailed);
|
|
}
|
|
}
|
|
|
|
// 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!
|
|
LogText(StrSeparator);
|
|
LogErrorText(Resources.StrFwPackageInfo + " " + fwPackRadioFrequencyMhz + " MHz");
|
|
LogErrorText(@"Cordonel: " + _currentGenesis.RadioFrequencyMhz + " MHz");
|
|
InfoProcessFailed(lblCordonelUpdateCapability, Resources.StrCordonelUpdateCapabilityRadioFailed);
|
|
}
|
|
|
|
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()
|
|
{
|
|
Thread.CurrentThread.CurrentUICulture = _cultureInfo;
|
|
Thread.CurrentThread.CurrentCulture = _cultureInfo;
|
|
|
|
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!
|
|
LogText(StrSeparator);
|
|
var msg = " ";
|
|
var cnt = fwPackMeterSizes.Count;
|
|
foreach (var size in fwPackMeterSizes)
|
|
{
|
|
msg += size;
|
|
if (--cnt > 0)
|
|
msg += ", ";
|
|
}
|
|
LogErrorText(Resources.StrFwPackageInfo + msg);
|
|
LogErrorText(@"Cordonel: " + _currentGenesis.MeterSize);
|
|
InfoProcessFailed(lblCordonelUpdateCapability, Resources.StrCordonelUpdateCapabilityMeterSizeFailed);
|
|
}
|
|
}
|
|
|
|
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
|
|
_finalLoginAfterUpdate = 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.Connect;
|
|
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;
|
|
}
|
|
|
|
/// <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>
|
|
private void BtnUpdateFw_Click(Object sender, EventArgs e)
|
|
{
|
|
if (_currentGenesis == null || _meterFwUpdate?.FileApps == null ||
|
|
_registerRestorer == null)
|
|
return;
|
|
|
|
_startTime = DateTimeOffset.UtcNow;
|
|
_resetTimeMeasurement = false;
|
|
_processState = ProcessState.RegisterBackup;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Open/Toggle history window
|
|
/// </summary>
|
|
/// <remarks date="2020-Nov-27" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void BtnHistory_Click(Object sender, EventArgs e)
|
|
{
|
|
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)
|
|
{
|
|
ReleaseDisplay();
|
|
}
|
|
|
|
#endregion --------------------------------------- User Interaction -------------------------------------------
|
|
|
|
#region ------------------------------------------ Tools ------------------------------------------------------
|
|
/// <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. login
|
|
/// 4. read if not 0x01.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Mar-07" author="Thomas Wiedebusch">
|
|
/// - Handle SystemState 0 = OFF
|
|
/// </remarks>
|
|
private Boolean ActivateRadio()
|
|
{
|
|
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.ConvertTo<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(500);
|
|
|
|
// 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.ConvertTo<Int32>(_currentGenesis.ReadRegister(Register.Sensusradio.SystemState)))
|
|
{
|
|
LogSuccessText(Resources.StrRadioActivationSuccess);
|
|
retVal = true;
|
|
}
|
|
else
|
|
{
|
|
LogErrorText(Resources.StrRadioActivationFailed);
|
|
retVal = false;
|
|
}
|
|
} // initially radio active check
|
|
}
|
|
catch (Exception)
|
|
{
|
|
LogErrorText(Resources.StrRadioActivationFailed);
|
|
retVal = false;
|
|
}
|
|
_currentGenesis.Logout();
|
|
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="Roland Drabesch/Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private Boolean DeactivatePulseModule()
|
|
{
|
|
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.ConvertTo<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;
|
|
}
|
|
|
|
_currentGenesis.Logout();
|
|
return retVal;
|
|
}
|
|
/// <summary>
|
|
/// Restore all listed registers.
|
|
/// </summary>
|
|
/// <remarks date="2023-Mar-07" author="Roland Drabesch/Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
private Boolean RestoreRegisters()
|
|
{
|
|
var retVal = true;
|
|
try
|
|
{
|
|
if (!_currentGenesis.IsLoggedOn)
|
|
_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;
|
|
}
|
|
|
|
_currentGenesis.Logout();
|
|
return retVal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Store configurations 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;
|
|
if (_currentGenesis.StoreAllConfigurations())
|
|
{
|
|
LogSuccessText(Resources.StrStoreConfigSuccess);
|
|
retVal = true;
|
|
}
|
|
else
|
|
{
|
|
LogErrorText(Resources.StrStoreConfigFailed);
|
|
retVal = false;
|
|
}
|
|
_currentGenesis.Logout();
|
|
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.
|
|
/// </remarks>
|
|
private void ReleaseDisplay()
|
|
{
|
|
if (_currentGenesis == null)
|
|
return;
|
|
_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.
|
|
_currentGenesis.SetLcdText(true, new Byte[] { 0, 0, 0, 0 });
|
|
// deactivate LED, not needed to get measurements during the update or on repeated start after system
|
|
// failure. This overwrites the settings in _meterFwUpdate.
|
|
_currentGenesis.WriteRegister(Register.Genesisflow.LedMode, new[] { (Byte)LedMode.Off });
|
|
_currentGenesis.WriteRegister(Register.Genesisflow.StoreConfiguration, 1);
|
|
LogText(Resources.StrDisplayReleased);
|
|
_currentGenesis.Logout();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Logging of string lists.
|
|
/// </summary>
|
|
private void LogStringList(String strHeader, IEnumerable<String> strings)
|
|
{
|
|
LogText(StrSeparator);
|
|
LogText(strHeader);
|
|
foreach (var text in strings)
|
|
{
|
|
LogText(text);
|
|
}
|
|
LogText(StrSeparator);
|
|
}
|
|
|
|
/// <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(StrSeparator);
|
|
LogText($"PCB ID: {_currentGenesis.PcbId}");
|
|
LogText($"FW Version: {_currentGenesis.FwVersion}");
|
|
LogText($"{Resources.StrMessageSystemCoreRevision} {GenesisMeter.BuildFwVersionString(_currentGenesis.CoreRevision)}");
|
|
LogText($"{Resources.StrMessageSystemRegion} {_currentGenesis.Region}");
|
|
LogText($"{Resources.StrMessageSystemRadio} {_currentGenesis.RadioFrequencyMhz}");
|
|
LogText($"{Resources.StrMessageMeterSize} {_currentGenesis.MeterSize}");
|
|
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, GenesisMeter.BuildFwVersionString(_currentGenesis.CoreRevision), false);
|
|
appList.Add(coreVersion);
|
|
|
|
foreach (var app in _currentGenesis.MeterAppListVersion)
|
|
{
|
|
var versionString = app.IsInstalled ? $"V: {app.StrVersion} - CRC: 0x{app.Crc:X4}" :
|
|
Resources.StrMessageAppNotInstalled;
|
|
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>
|
|
/// 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>
|
|
private void MessageBoxShow(String text, String caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
|
{
|
|
SetOverallProgressDisplayOff();
|
|
SetActualProgressDisplayOff();
|
|
UiInvoker.ControlInvoker(lblWaitingForMeterResponse, ColorProcessFailed, visible: false);
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Output exclusively to user update remarks text window.
|
|
/// </summary>
|
|
private void LogSuccessText(String txtHistory)
|
|
{
|
|
InfoWindowColoredText(txtHistory, ColorSuccess);
|
|
_logger.Info(txtHistory);
|
|
ReportText(txtHistory);
|
|
}
|
|
|
|
/// <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>
|
|
/// 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)
|
|
{
|
|
LogText(StrSeparator);
|
|
LogErrorText(msg);
|
|
LogText(StrSeparator);
|
|
}
|
|
|
|
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();
|
|
LogText(StrSeparator);
|
|
InfoProcessSuccess(lblCordonelUpdateCapability, Resources.StrFwUpToDateMessage);
|
|
LogText(StrSeparator);
|
|
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>
|
|
private void SetControlDownloadEnable()
|
|
{
|
|
UiInvoker.ControlEnableInvoker(grpLanguageSelection, true);
|
|
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>
|
|
private void SetDisableDownloadAndEnableConnect()
|
|
{
|
|
UiInvoker.ControlEnableInvoker(grpLanguageSelection, true);
|
|
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>
|
|
private void ErrorProcesses()
|
|
{
|
|
SetOverallProgressDisplayOff();
|
|
SetActualProgressDisplayOff();
|
|
_resetTimeMeasurement = true;
|
|
// take the invoker of the error state to generate the message and / or popup window
|
|
switch (_invokerProcessState)
|
|
{
|
|
case ProcessState.PwdFileRestore:
|
|
var msg = $"{Resources.StrPasswordFileRestoreFailed}";
|
|
_logger.Error($"Slot:{Slot} - {msg}");
|
|
MessageBoxShow(msg, Resources.StrMessageWindowFailed, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
break;
|
|
case ProcessState.CheckCoreRevision:
|
|
SetDisableDownloadAndEnableConnect();
|
|
MessageBoxShow(Resources.StrCordonelUpdateCapabilityCoreFailed, Resources.StrMessageWindowFailed,
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
break;
|
|
case ProcessState.CheckMetrology:
|
|
SetDisableDownloadAndEnableConnect();
|
|
MessageBoxShow(Resources.StrCordonelUpdateCapabilityMetrologyFailed,
|
|
Resources.StrMessageWindowFailed, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
break;
|
|
case ProcessState.CheckRegionRadio:
|
|
SetDisableDownloadAndEnableConnect();
|
|
MessageBoxShow(Resources.StrCordonelUpdateCapabilityRegionFailed,
|
|
Resources.StrMessageWindowFailed, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
break;
|
|
case ProcessState.CheckMeterSize:
|
|
SetDisableDownloadAndEnableConnect();
|
|
MessageBoxShow(Resources.StrCordonelUpdateCapabilityMeterSizeFailed,
|
|
Resources.StrMessageWindowFailed, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
break;
|
|
case ProcessState.CheckUpdateFiles:
|
|
case ProcessState.LoadUpdateFiles:
|
|
SetDisableDownloadAndEnableConnect();
|
|
msg = $"{Resources.StrUpdateInformationStatusFailed}";
|
|
_logger.Error($"Slot:{Slot} - {msg}");
|
|
MessageBoxShow(msg, Resources.StrMessageWindowFailed, MessageBoxButtons.OK,
|
|
MessageBoxIcon.Error);
|
|
break;
|
|
case ProcessState.FirmwareUpdate:
|
|
var requiredUpdates = _meterFwUpdate?.GetFailedFileApps();
|
|
var text = Resources.StrFirmwareUpdateFailed;
|
|
if (requiredUpdates != null && requiredUpdates.Count > 0)
|
|
{
|
|
text += $"\n{Resources.StrFailedToUpdateApp}\n";
|
|
FileApplications lastFile = null;
|
|
foreach (var file in requiredUpdates)
|
|
{
|
|
if (lastFile == null || lastFile.AppId != file.AppId)
|
|
{
|
|
text += $"{file.AppName}\n";
|
|
}
|
|
|
|
lastFile = file;
|
|
}
|
|
}
|
|
MessageBoxShow(text, Resources.StrMessageWindowFailed, MessageBoxButtons.OK,
|
|
MessageBoxIcon.Error);
|
|
// a retry of the download can be done because all checks passed in advance!
|
|
SetControlDownloadEnable();
|
|
break;
|
|
case ProcessState.PortScan:
|
|
SetDisableDownloadAndEnableConnect();
|
|
_meterBatch?.RemoveAllMeters();
|
|
MessageBoxShow(Resources.StrRequestPortFailed, Resources.StrMessageWindowFailed,
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
break;
|
|
case ProcessState.Reboot:
|
|
SetDisableDownloadAndEnableConnect();
|
|
MessageBoxShow(Resources.StrRebootFailed, Resources.StrMessageWindowFailed,
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
break;
|
|
case ProcessState.ValidateSoftware:
|
|
MessageBoxShow(Resources.StrSoftwareVersionExpired, Resources.StrMessageWindowFailed,
|
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
// close the form
|
|
Invoke(new Action(Close));
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <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;
|
|
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;
|
|
case ProcessState.PwdFileRestore:
|
|
_processState = ProcessState.Idle;
|
|
break;
|
|
default:
|
|
// to enable the "FW-Update" button again.
|
|
_processState = ProcessState.CheckUpdateCapability;
|
|
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 Boolean ExecReboot()
|
|
{
|
|
String text = null;
|
|
// var timerInterval = _tmrProgressUpdate.Interval;
|
|
var timerInterval = TimerIntervalMs;
|
|
_bootDelayCtrMs = 0;
|
|
var internalCommunicationDelayMs = _bootDelayCtrMs + 10 * timerInterval;
|
|
|
|
while (_bootDelayCtrMs < RebootTimeoutMs && string.IsNullOrEmpty(text))
|
|
{
|
|
if (!_timerIntervalExpired)
|
|
continue;
|
|
_timerIntervalExpired = false;
|
|
// internal communication delay to avoid overload of Cordonel
|
|
if (_bootDelayCtrMs % internalCommunicationDelayMs == 0)
|
|
{
|
|
text = RegisterConverter.ConvertTo<String>(
|
|
_currentGenesis.ReadRegister(Register.Configexchange.PcbSerialNumber, 12));
|
|
}
|
|
|
|
ProcessUpdate_Event(this, new ProcessExecEventArgs(Resources.StrUpdateAppAndRestartMeter,
|
|
_bootDelayCtrMs * 100.0 / RebootTimeoutMs));
|
|
_bootDelayCtrMs += timerInterval;
|
|
}
|
|
|
|
return !string.IsNullOrEmpty(text);
|
|
}
|
|
|
|
/// <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>
|
|
private void FinalizeReboot(Boolean 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.ReLogin();
|
|
|
|
_processState = _currentGenesis.IsLoggedOn ? successExitState : errorExitState;
|
|
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
}
|
|
#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 Boolean ExecPortScanner()
|
|
{
|
|
_serialPortScanner.ScanAllSerialPorts(Slot, _basePath?.PortConfigFilePathName);
|
|
return _serialPortScanner.AutoDetectedPortName != null;
|
|
}
|
|
|
|
/// <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(Boolean 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)
|
|
{
|
|
// 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, FinalConnect, _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>
|
|
/// 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,
|
|
/// - Release the display to normal operation,
|
|
/// - Store all configurations on initial connection.
|
|
/// </summary>
|
|
/// <param name="password">lvl8 or skeleton</param>
|
|
/// <param name="initialLogin">initial connection</param>
|
|
/// <returns>true if successfully executed</returns>
|
|
/// <remarks date="2023-Mar-07" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private Boolean LoginAndSpecialSetupProcedure(String password, Boolean initialLogin)
|
|
{
|
|
// 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
|
|
var retVal = _currentGenesis.Login(password, skipReadMeterFwAndAssignRegisters: initialLogin);
|
|
if (!retVal)
|
|
return false;
|
|
|
|
// switch the pulse module inactive
|
|
if (initialLogin)
|
|
DeactivatePulseModule();
|
|
|
|
// login with reading out all installed applications
|
|
retVal = _currentGenesis.Login(password);
|
|
|
|
if (_currentGenesis.RadioFrequencyMhz != null)
|
|
ActivateRadio();
|
|
|
|
// display to normal operation
|
|
ReleaseDisplay();
|
|
|
|
// safe all settings from RAM to FLASH to keep those for reboot
|
|
if (initialLogin)
|
|
StoreConfigurations();
|
|
|
|
return retVal;
|
|
}
|
|
/// <summary>
|
|
/// Execution of connect to water-meter.
|
|
/// </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>
|
|
private Boolean ExecConnect()
|
|
{
|
|
var initialLogin = false;
|
|
// read out the PCB ID from water-meter to open the password container of this device
|
|
if (string.IsNullOrEmpty(_currentGenesis.PcbId))
|
|
{
|
|
// if the PcbId is unknown, it is an initial login
|
|
initialLogin = true;
|
|
_currentGenesis.GetPcbId();
|
|
}
|
|
|
|
// create report file
|
|
BuildReportFiles();
|
|
|
|
// search the PCB ID and extract password for login
|
|
String password = null;
|
|
String skeletonKey = null;
|
|
_genesisInUpdateList = false;
|
|
if (_cordonelDeviceInfos != null)
|
|
{
|
|
foreach (var device in _cordonelDeviceInfos.Where(device => _currentGenesis.PcbId == device.PcbId))
|
|
{
|
|
skeletonKey = device.PwdContainer.SkeletonKey;
|
|
password = device.PwdContainer.PasswordLvl8;
|
|
_genesisInUpdateList = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (password == null || skeletonKey == null)
|
|
{
|
|
_processState = ProcessState.Error;
|
|
return false;
|
|
}
|
|
|
|
// delay needed if repeated login trial given by Cordonel, the next communication has to
|
|
// wait that long, otherwise the Cordonel locks for 2s, 4s, 8s, 16s and so on on every retry!
|
|
Thread.Sleep(3000);
|
|
|
|
// this login may be time consuming on readout of application information
|
|
// first try to login with password Level 8, this is needed if the password file is installed
|
|
if (LoginAndSpecialSetupProcedure(password, initialLogin))
|
|
{
|
|
_passwordFileIsCorrupted = false;
|
|
return true;
|
|
}
|
|
|
|
// here the password file is corrupted or not installed, try to login with skeletonKey
|
|
_passwordFileIsCorrupted = true;
|
|
|
|
// On final login EXPLICIT login with password level 8 to validate password file
|
|
if (_finalLoginAfterUpdate)
|
|
{
|
|
_processState = ProcessState.Error;
|
|
return false;
|
|
}
|
|
|
|
// delay needed if repeated login trial given by Cordonel, the next communication has to
|
|
// wait that long, otherwise the Cordonel locks for 2s, 4s, 8s, 16s and so on on every retry!
|
|
Thread.Sleep(5000);
|
|
|
|
// use the skeleton key for login as the password file may not be installed or invalid
|
|
if (LoginAndSpecialSetupProcedure(skeletonKey, initialLogin))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
_processState = ProcessState.Error;
|
|
return false;
|
|
}
|
|
|
|
/// <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>
|
|
private void FinalConnect(Boolean 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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
// log information about password file is not installed, but go ahead as usual
|
|
if (_passwordFileIsCorrupted)
|
|
{
|
|
InfoProcessFailed(null, Resources.StrPasswordFileNotInstalled);
|
|
}
|
|
|
|
_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, _finalLoginAfterUpdate
|
|
? Resources.StrPasswordFileValid
|
|
: 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 + @" " +
|
|
$"{GenesisMeter.BuildFwVersionString(_currentGenesis.CoreRevision)}");
|
|
// avoid report again on final login after update
|
|
if (!_finalLoginAfterUpdate)
|
|
LogInstalledMeterFw();
|
|
_processState = successExitState;
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
}
|
|
#endregion --------------------------------------- Connect ----------------------------------------------------
|
|
|
|
#region ------------------------------------------ Register Backup --------------------------------------------
|
|
/// <summary>
|
|
/// Execution of register backup, Genesis has to be logged in.
|
|
/// </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 RegisterBackup(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(InitRegisterBackup, ExecRegisterBackup, FinalizeRegisterBackup,
|
|
_cultureInfo, exitProcessStateObjects);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialization of register backup.
|
|
/// </summary>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void InitRegisterBackup()
|
|
{
|
|
SetControlsAllButtonsDisabled();
|
|
InfoProcessActive(lblRegisterReadout, Resources.StrRegisterReadoutActive);
|
|
//set the process bars
|
|
SetOverallProgressDisplayOn(Resources.StrRegisterReadoutActive);
|
|
_registerRestorer.OnProcessUpdate += ProcessUpdate_Event;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execution of register before update read.
|
|
/// </summary>
|
|
/// <returns>true if successfully executed</returns>
|
|
/// <remarks date="2020-Dec-09" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private Boolean ExecRegisterBackup()
|
|
{
|
|
return _registerRestorer.PreUpdateReadRegisters();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finalization of register backup.
|
|
/// </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>
|
|
private void FinalizeRegisterBackup(Boolean 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;
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
InfoProcessSuccess(lblRegisterReadout, Resources.StrRegisterReadoutSucceeded);
|
|
_processState = successExitState;
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
return;
|
|
}
|
|
|
|
InfoProcessFailed(lblRegisterReadout, Resources.StrRegisterReadoutFailed);
|
|
_processState = errorExitState;
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
}
|
|
#endregion --------------------------------------- Register Backup --------------------------------------------
|
|
|
|
#region ------------------------------------------ File Access ------------------------------------------------
|
|
/// <summary>
|
|
/// Reading the meter file configurations.
|
|
/// </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 MeterFilesConfigRead(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. This routine can be used pre- and post erase, update and restore.
|
|
/// </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>
|
|
private void MeterFilesRead(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.ReadInitialMeterFiles:
|
|
case ProcessState.ReadPreUpdateMeterFiles:
|
|
InfoProcessActive(lblFileErase, Resources.StrFileAccessActive);
|
|
break;
|
|
case ProcessState.ReadPostUpdateMeterFiles:
|
|
case ProcessState.VerifyMeterFiles:
|
|
InfoProcessActive(lblFileRestore, Resources.StrFileAccessActive);
|
|
break;
|
|
}
|
|
_currentGenesis.ReLogin();
|
|
// read all meter files
|
|
var 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)
|
|
{
|
|
//log files
|
|
LogStringList(Resources.StrFileRead, _readMeterFiles);
|
|
|
|
_processState = successExitState;
|
|
_currentGenesis.Logout();
|
|
SetOverallProgressDisplayOff();
|
|
switch (_invokerProcessState)
|
|
{
|
|
case ProcessState.ReadInitialMeterFiles:
|
|
case ProcessState.ReadPreUpdateMeterFiles:
|
|
InfoProcessSuccess(lblFileErase, Resources.StrFileReadoutSucceeded);
|
|
break;
|
|
case ProcessState.ReadPostUpdateMeterFiles:
|
|
case ProcessState.VerifyMeterFiles:
|
|
InfoProcessSuccess(lblFileRestore, Resources.StrFileReadoutSucceeded);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
_currentGenesis.Logout();
|
|
}
|
|
|
|
switch (_invokerProcessState)
|
|
{
|
|
case ProcessState.ReadInitialMeterFiles:
|
|
case ProcessState.ReadPreUpdateMeterFiles:
|
|
InfoProcessFailed(lblFileErase, Resources.StrFileReadoutFailed);
|
|
break;
|
|
case ProcessState.ReadPostUpdateMeterFiles:
|
|
case ProcessState.VerifyMeterFiles:
|
|
InfoProcessFailed(lblFileRestore, Resources.StrFileReadoutFailed);
|
|
break;
|
|
}
|
|
_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>
|
|
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 > 0)
|
|
{
|
|
//log files
|
|
LogStringList(Resources.StrFileErased, erasedMeterFiles);
|
|
}
|
|
|
|
if (erasedMeterFiles.Count == filesToErase.Count)
|
|
{
|
|
_processState = successExitState;
|
|
InfoProcessSuccess(lblFileErase, Resources.StrFileEraseSucceeded);
|
|
_currentGenesis.Logout();
|
|
SetOverallProgressDisplayOff();
|
|
return;
|
|
}
|
|
}
|
|
|
|
_currentGenesis.Logout();
|
|
}
|
|
|
|
InfoProcessFailed(lblFileErase, Resources.StrFileReadoutFailed);
|
|
_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);
|
|
_currentGenesis.Logout();
|
|
SetOverallProgressDisplayOff();
|
|
return;
|
|
}
|
|
}
|
|
|
|
_currentGenesis.Logout();
|
|
}
|
|
|
|
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>
|
|
private void PwdFileRestore(ProcessState successExitState, ProcessState errorExitState = ProcessState.Error)
|
|
{
|
|
// remind the invoker being able to generate error messages based on the last state
|
|
_invokerProcessState = _processState;
|
|
|
|
// remind, that the final login with the Level 8 password has to be forced to validate the password file
|
|
_finalLoginAfterUpdate = true;
|
|
|
|
// if the password file is valid
|
|
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);
|
|
var hashedPwdFile = new Byte[MeterPwdDb.PwdFileLength];
|
|
if (_cordonelDeviceInfos != null)
|
|
{
|
|
foreach (var device in _cordonelDeviceInfos.Where(device => _currentGenesis.PcbId == device.PcbId))
|
|
{
|
|
hashedPwdFile = device.PwdContainer.EncryptedPasswordFile;
|
|
break;
|
|
}
|
|
|
|
if (hashedPwdFile != null && hashedPwdFile.Length == MeterPwdDb.PwdFileLength)
|
|
{
|
|
SetControlsCommunicationActive();
|
|
_currentGenesis?.ReLogin();
|
|
if (pwdFileObject.UnlockEraseWriteMeterPwdFile())
|
|
{
|
|
if (pwdFileObject.WriteMeterPwdFile(hashedPwdFile))
|
|
{
|
|
// a reconnect will check for proper installed password file and check the Level 8 pwd.
|
|
_processState = ProcessState.Connect;
|
|
_passwordFileIsCorrupted = false;
|
|
InfoProcessSuccess(lblPasswordFileCheck, Resources.StrPasswordFileRestoreSucceeded);
|
|
}
|
|
}
|
|
|
|
_currentGenesis?.Logout();
|
|
}
|
|
// the password file is NOT in the password container
|
|
else
|
|
{
|
|
InfoProcessFailed(lblPasswordFileCheck, Resources.StrPasswordNotDelivered);
|
|
_processState = errorExitState;
|
|
}
|
|
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
return;
|
|
}
|
|
|
|
InfoProcessFailed(lblPasswordFileCheck, Resources.StrPasswordFileRestoreFailed);
|
|
_processState = errorExitState;
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
}
|
|
|
|
#endregion --------------------------------------- File Access ------------------------------------------------
|
|
|
|
#region ------------------------------------------ Register Restorer ------------------------------------------
|
|
/// <summary>
|
|
/// Execution of register restore, Genesis has to be logged in.
|
|
/// </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 RegisterRestore(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(InitRegisterRestore, ExecRegisterRestore, FinalizeRegisterRestore,
|
|
_cultureInfo, exitProcessStateObjects);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialization of register backup.
|
|
/// </summary>
|
|
/// <remarks date="2020-Dec-18" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private void InitRegisterRestore()
|
|
{
|
|
SetControlsAllButtonsDisabled();
|
|
InfoProcessActive(lblRegisterRestore, Resources.StrRegisterAccessActive);
|
|
//set the process bars
|
|
SetOverallProgressDisplayOn(Resources.StrRegisterAccessActive);
|
|
_registerRestorer.OnProcessUpdate += ProcessUpdate_Event;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execution of register after update read.
|
|
/// </summary>
|
|
/// <returns>true if successfully executed</returns>
|
|
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private Boolean ExecRegisterRestore()
|
|
{
|
|
var success = _registerRestorer.PostUpdateReadRegisters();
|
|
if (success)
|
|
success = _registerRestorer.CompareRegisters();
|
|
return success;
|
|
}
|
|
|
|
/// <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.
|
|
/// </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 FinalizeRegisterRestore(Boolean 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)
|
|
{
|
|
//TODO use this message if registers will be restored
|
|
//InfoProcessSuccess(lblRegisterRestore, Resources.StrRegisterRestoreSucceeded);
|
|
// meanwhile output of register read for logging
|
|
InfoProcessSuccess(lblRegisterRestore, Resources.StrRegisterReadoutSucceeded);
|
|
_processState = successExitState;
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
return;
|
|
}
|
|
|
|
InfoProcessFailed(lblRegisterRestore, Resources.StrRegisterRestoreFailed);
|
|
_processState = errorExitState;
|
|
// change first the process state, otherwise the timer may switch the bar on again
|
|
SetOverallProgressDisplayOff();
|
|
}
|
|
#endregion --------------------------------------- Register Restorer ------------------------------------------
|
|
|
|
#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 Boolean ExecFwUpdate()
|
|
{
|
|
return _meterFwUpdate.UpdateMeterFw();
|
|
}
|
|
|
|
/// <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(Boolean 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)
|
|
{
|
|
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))
|
|
{
|
|
// CustomerName_OrderNumber
|
|
var customerOrderSplit =
|
|
_basePath.CustomerAndOrderNumber.Split(FwUpdateConfig.FwUpdateFileFieldSeparator);
|
|
LogText(StrSeparator);
|
|
InfoProcessSuccess(null, $"{Resources.StrTestReportDate} {DateTime.Now.ToShortDateString()}");
|
|
InfoProcessSuccess(null, $"{Resources.StrTestReportSwNameVersion} " +
|
|
$"{_version.Major}.{_version.Minor}.{_version.Build}");
|
|
InfoProcessSuccess(null, $"{Resources.StrTestReportCustomerName} {customerOrderSplit[0]}");
|
|
InfoProcessSuccess(null, $"{Resources.StrTestReportOrderNumber} {customerOrderSplit[1]}");
|
|
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.QuickConnect;
|
|
ResetAllStatusLabels();
|
|
lblFwUpdateInfo.Text = $@"Version: {_version.Major}.{_version.Minor}.{_version.Build}";
|
|
// Display new history
|
|
ClearHistoryWindow();
|
|
}
|
|
|
|
#endregion --------------------------------------- Language ---------------------------------------------------
|
|
|
|
}
|
|
}
|
|
|