1979 lines
73 KiB
C#
1979 lines
73 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
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.Consts;
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.Properties;
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
|
|
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore.Consts;
|
|
using Xylem.Common.Utils.ProcessExec;
|
|
using Xylem.Common.Utils.ProcessExec.EventArguments;
|
|
|
|
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
{
|
|
/// <summary>
|
|
/// Meter FW update procedure
|
|
/// </summary>
|
|
public class MeterFwUpdate : IProcessState
|
|
{
|
|
#region Variables
|
|
|
|
/// <summary>
|
|
/// Port scan result event for message dispatcher to caller
|
|
/// </summary>
|
|
public event EventHandler<ProcessExecEventArgs> OnProcessUpdate;
|
|
|
|
//name for upgrade control file
|
|
private const String StrUpgradeCtrlFileName = "1\\upgrade";
|
|
|
|
//erase application
|
|
private const String StrEraseAppCtrlFilePost = "*\r";
|
|
|
|
//upgrade application
|
|
private const String StrInstallAppCtrlFilePost = ":\r";
|
|
|
|
//start identifier for upgrade file name (full name is "1\\upg02")
|
|
private const String StrUpgradeAppPartialFileName = "1\\upg";
|
|
|
|
//start of binary application file
|
|
private const String StrFileAppStartId = "APP>";
|
|
|
|
//position of CRC (LSB first) in binary file
|
|
private const Int32 FileAppCrcIndex = 0x04;
|
|
|
|
//position of version in binary file
|
|
private const Int32 FileAppVersionIndex = 0x06;
|
|
|
|
//position of application ID in binary file
|
|
private const Int32 FileAppAppIdIndex = 0x28;
|
|
|
|
//package file search string for applications including blank!!!!
|
|
private const String StrPackageFileAppId = "Id ";
|
|
private const String StrPackageFileAppCrc = "CRC ";
|
|
private const String StrPackageFileAppVersion = "version ";
|
|
|
|
//binary file name is a combination of "binfile" application ID underline version
|
|
//e.g. binfile0F_0268.bin
|
|
//private const String StrBinaryFilePre = "binfile";
|
|
//extension is always "bin"
|
|
private const String StrFileAppExtensionFilter = "*.bin";
|
|
//extension is always "bin"
|
|
//private const String StrPackageCtrlFileFileFilter = "*.txt";
|
|
//separator file name application to version
|
|
//private const String StrBinaryFileSepAppIdVersion = "_";
|
|
|
|
//multiplier for core major version (core major * x + minor)
|
|
private const Int32 MajorMultiply = 100;
|
|
//update reties for files
|
|
private const Int32 FileWriteRetries = 0;
|
|
//trigger update retries
|
|
private const Int32 TriggerWriteRetries = 2;
|
|
|
|
//retry of entire update procedure
|
|
private const Int32 UpdateProcedureRetries = 5;
|
|
//threshold to increase the file write timing
|
|
private const Int32 IncreaseTimeoutUpdateProcedureRetries = 4;
|
|
|
|
//retry threshold for retry of entire file instead of single file parts to minimize fragmentation
|
|
private const Int32 RetryEntireFileAfterFailedPartialRetries = 3;
|
|
|
|
private Int32 _updateProcedureRetryCtr;
|
|
|
|
/// <summary>
|
|
/// Size of parts for file splitting
|
|
/// </summary>
|
|
public Int32 MaxPartialFileDataSize = 2 * 1024;
|
|
|
|
/// <summary>
|
|
/// Core revision minimum as double for comparison
|
|
/// </summary>
|
|
public Double? CoreRevisionMinimum { get; private set; }
|
|
/// <summary>
|
|
/// Core revision maximum as double for comparison
|
|
/// </summary>
|
|
public Double? CoreRevisionMaximum { get; private set; }
|
|
|
|
//reminder for the core minimum requirement as string
|
|
private String _strCoreRevisionMinimum;
|
|
private String _strCoreRevisionMaximum;
|
|
//search string to find the minimum supported version in product*.txt
|
|
private const String StrCoreRevisionMinId = "Minimum supported core revision: ";
|
|
//search string to find the maximum supported version in product*.txt
|
|
private const String StrCoreRevisionMaxId = "Maximum supported core revision: ";
|
|
//search string to find the "Cordonel" identifier in product*.txt
|
|
private const String StrCoreRevisionCordonelId = "Cordonel ";
|
|
// example text: Minimum supported core revision: "Cordonel 1.62"
|
|
// example text: Maximum supported core revision: "Cordonel 1.66"
|
|
|
|
|
|
/// <summary>
|
|
/// Update files to write to meter.
|
|
/// An update package contains the complete set of files needed for a
|
|
/// tested release even if they already exist in the meter with this version.
|
|
/// </summary>
|
|
public readonly List<FileApplications> FileApps = new List<FileApplications>();
|
|
|
|
//update file(parts) list remaining
|
|
private readonly List<FileApplications> _remainingFileAppsParts = new List<FileApplications>();
|
|
|
|
//update file (parts) list which succeeded
|
|
private readonly List<FileApplications> _succeededFileAppsParts = new List<FileApplications>();
|
|
|
|
//update file(parts) list which failed
|
|
private readonly List<FileApplications> _failedFileAppsParts = new List<FileApplications>();
|
|
|
|
//update meter application list which failed
|
|
private readonly List<MeterApplications> _updatedMeterApps = new List<MeterApplications>();
|
|
|
|
private GenesisMeter _genesisMeter;
|
|
private String _pcbId;
|
|
private String _backupPcbId = "";
|
|
private MeterFile _meterFile;
|
|
|
|
// Text for caller to inform about actual process being carried out
|
|
private String _actualOperation;
|
|
private FwUpdateState _fwUpdateState;
|
|
|
|
// Process counter for caller to monitor actual progress
|
|
private Int32 _processedBytesCtr;
|
|
private Int32 _overallBytesCtr;
|
|
|
|
// Needed for repeated update of files to avoid overwriting of update control file
|
|
private String _upgradeCtrlFile;
|
|
|
|
//the package file describes all files needed for a specific release
|
|
private String _packageDescriptionFile;
|
|
|
|
/// <summary>
|
|
/// Enable partial file writes of single parts with gaps in between
|
|
/// </summary>
|
|
public Boolean SingleFilePartsRetryEnable = true;
|
|
|
|
/// <summary>
|
|
/// Enable partial file writes from first failed part to the end
|
|
/// </summary>
|
|
public Boolean ConsecutiveFilePartsRetryEnable = false;
|
|
|
|
/// <summary>
|
|
/// Register subset needed to adjust for update performance content before update
|
|
/// </summary>
|
|
private readonly Dictionary<String, Byte[]> _registersBeforeUpdate = new Dictionary<String, Byte[]>();
|
|
|
|
/// <summary>
|
|
/// Register to restore after update
|
|
/// </summary>
|
|
private Dictionary<String, Byte[]> _registersAfterUpdate = new Dictionary<String, Byte[]>();
|
|
|
|
#endregion
|
|
|
|
#region Events
|
|
/// <summary>
|
|
/// Process update event
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
/// <remarks date="2020-Dec-10" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
public virtual void ProcessUpdate_Event(Object sender, ProcessExecEventArgs e)
|
|
{
|
|
// copy text from overall messages to actual, because this is from sub-routine
|
|
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(GetFwUpdateStateOperation(),
|
|
OverallProcessCtrPercent, _actualOperation, e.OverallProcessPercent));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Information
|
|
|
|
/// <summary>
|
|
/// Process counter in percent
|
|
/// </summary>
|
|
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
public Double OverallProcessCtrPercent
|
|
{
|
|
get
|
|
{
|
|
var processFileBytes = _meterFile?.ProcessedBytesCtr ?? 0;
|
|
return 100.0 * (processFileBytes + _processedBytesCtr) /
|
|
(_overallBytesCtr > 0 ? _overallBytesCtr : 1);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Single file process counter in percent
|
|
/// </summary>
|
|
/// <remarks date="2019-Jun-24" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
public Double SingleFileProcessCtrPercent => _meterFile?.ProcessCtrPercent ?? 0;
|
|
|
|
#endregion
|
|
|
|
#region CtorAssignment
|
|
|
|
/// <summary>
|
|
/// Ctor
|
|
/// </summary>
|
|
/// <param name="genesisMeter"></param>
|
|
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Oct-28" author="Thomas Wiedebusch">
|
|
/// - Checked Genesis assignment before access.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-19" author="Thomas Wiedebusch">
|
|
/// - Check installed application before require access to registers.
|
|
/// </remarks>
|
|
public MeterFwUpdate(GenesisMeter genesisMeter)
|
|
{
|
|
if (genesisMeter == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AssignGenesis(genesisMeter);
|
|
|
|
if (genesisMeter.MeterAppListVersion.Any(f => f.AppName == "GENESISFLOW" && f.IsInstalled))
|
|
{
|
|
_registersBeforeUpdate.Add(Register.Genesisflow.LedMode, new[] { (Byte)LedMode.Off });
|
|
_registersBeforeUpdate.Add(Register.Genesisflow.SampleRate, new Byte[] { 1 });
|
|
}
|
|
if (genesisMeter.MeterAppListVersion.Any(f => f.AppName == "METROLOGYASST" && f.IsInstalled))
|
|
{
|
|
_registersBeforeUpdate.Add(Register.Mertrologyasst.PulseMode, new Byte[] { 0 });
|
|
}
|
|
//deny access if NA product, because this does not have the radio app installed
|
|
if (genesisMeter.MeterAppListVersion.Any(f => f.AppName == "SENSUSRADIO" && f.IsInstalled))
|
|
{
|
|
_registersBeforeUpdate.Add(Register.Sensusradio.WakeupInterval, new Byte[] { 6 });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Assign new genesis and force new update control file
|
|
/// </summary>
|
|
/// <param name="genesisMeter"></param>
|
|
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Dec-02" author="Thomas Wiedebusch">
|
|
/// - Moved Genesis preparation to <see cref="UpdatePreparation"/>
|
|
/// </remarks>
|
|
public void AssignGenesis(GenesisMeter genesisMeter)
|
|
{
|
|
_genesisMeter = genesisMeter;
|
|
_actualOperation = "";
|
|
_upgradeCtrlFile = "";
|
|
_fwUpdateState = FwUpdateState.Idle;
|
|
_pcbId = _genesisMeter?.PcbId;
|
|
_remainingFileAppsParts.Clear();
|
|
_succeededFileAppsParts.Clear();
|
|
_failedFileAppsParts.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dispose
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
_remainingFileAppsParts.Clear();
|
|
_succeededFileAppsParts.Clear();
|
|
_failedFileAppsParts.Clear();
|
|
_updatedMeterApps.Clear();
|
|
FileApps.Clear();
|
|
if (_meterFile == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_meterFile.OnProcessUpdate -= ProcessUpdate_Event;
|
|
_meterFile = null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Compare
|
|
|
|
/// <summary>
|
|
/// Compare file and meter applications
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-25" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-29" author="Thomas Wiedebusch">
|
|
/// - Single application comparison exported
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-01" author="Thomas Wiedebusch">
|
|
/// - Parameter from call removed, genesis is assigned!
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-02" author="Thomas Wiedebusch">
|
|
/// - Removed genesis meter has to be logged in
|
|
/// </remarks>
|
|
public void CompareAllMeterAndFileApps()
|
|
{
|
|
if (_genesisMeter == null || FileApps == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (var meterApp in _genesisMeter.MeterAppListVersion)
|
|
{
|
|
CompareMeterAppWithFileApp(meterApp);
|
|
}
|
|
if (!_genesisMeter.IsLoggedOn)
|
|
{
|
|
_genesisMeter.ReLogin();
|
|
//set display code to up to date if not needed to be installed or up to date
|
|
_genesisMeter.SetProcessState(_genesisMeter.MeterAppListVersion.All(
|
|
f => f.Status == MeterAppState.MeterAppUpToDate ||
|
|
f.Status == MeterAppState.MeterAppNotInstalled || f.Status == MeterAppState.Unknown)
|
|
? DisplayCodes.FwUpToDate
|
|
: DisplayCodes.FwUpdateFailed, false);
|
|
_genesisMeter.Logout();
|
|
}
|
|
else
|
|
{
|
|
//set display code to up to date if not needed to be installed or up to date
|
|
_genesisMeter.SetProcessState(_genesisMeter.MeterAppListVersion.All(
|
|
f => f.Status == MeterAppState.MeterAppUpToDate ||
|
|
f.Status == MeterAppState.MeterAppNotInstalled || f.Status == MeterAppState.Unknown)
|
|
? DisplayCodes.FwUpToDate
|
|
: DisplayCodes.FwUpdateFailed, false);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compare single meter application with file
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-29" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-30" author="Thomas Wiedebusch">
|
|
/// - Status text moved to meter and file
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-01" author="Thomas Wiedebusch">
|
|
/// - Compare changed
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-01" author="Thomas Wiedebusch">
|
|
/// - Meter state unknown if file apps not loaded
|
|
/// </remarks>
|
|
public void CompareMeterAppWithFileApp(MeterApplications meterApp)
|
|
{
|
|
meterApp.Update = false;
|
|
meterApp.Erase = false;
|
|
|
|
//CASE -1: meter application is installed but files are not loaded
|
|
if (meterApp.IsInstalled && FileApps.Count == 0)
|
|
{
|
|
meterApp.Status = MeterAppState.Unknown;
|
|
return;
|
|
}
|
|
|
|
//CASE 0: meter application is not installed and update files are not loaded
|
|
if (!meterApp.IsInstalled && FileApps.Count == 0)
|
|
{
|
|
meterApp.Status = MeterAppState.MeterAppNotInstalled;
|
|
return;
|
|
}
|
|
|
|
foreach (var fileApp in FileApps)
|
|
{
|
|
if (meterApp.AppId != fileApp.AppId)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//CASE 1: update file for application is invalid
|
|
// - in this case the entire package download will be denied,
|
|
// the information is going to be displayed in the status.
|
|
if (!fileApp.IsValid)
|
|
{
|
|
meterApp.Status = MeterAppState.FileAppInvalid;
|
|
return;
|
|
}
|
|
|
|
//CASE 2: meter application is installed
|
|
if (meterApp.IsInstalled)
|
|
{
|
|
//CASE 2.1 update file for application identical
|
|
if (meterApp.StrVersion == fileApp.StrVersion && meterApp.Crc == fileApp.Crc)
|
|
{
|
|
meterApp.Status = MeterAppState.MeterAppUpToDate;
|
|
return;
|
|
}
|
|
|
|
//CASE 2.2 update file download succeeded
|
|
//CASE 2.3 update file download failed
|
|
//CASE 2.4 update file download suspicious
|
|
if (CheckDownloadedMeterApps(meterApp))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//CASE 2.5 update file and meter application are not identical
|
|
if (meterApp.StrVersion != fileApp.StrVersion)
|
|
{
|
|
meterApp.Status = MeterAppState.MeterAppVersionOutdated;
|
|
meterApp.Update = true;
|
|
return;
|
|
}
|
|
|
|
//CASE 2.6 meter file to update file for application CRC mismatch
|
|
if (meterApp.Crc == fileApp.Crc)
|
|
{
|
|
return;
|
|
}
|
|
|
|
meterApp.Status = MeterAppState.InvalidCrc;
|
|
meterApp.Update = true;
|
|
return;
|
|
}
|
|
|
|
//CASE 3: meter application is not installed
|
|
else
|
|
{
|
|
//CASE 3.1 update file download succeeded
|
|
//CASE 3.2 update file download failed
|
|
//CASE 3.3 update file download suspicious
|
|
if (CheckDownloadedMeterApps(meterApp))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//CASE 3.4 update file exists and is not identical to downloaded version
|
|
meterApp.Status = MeterAppState.MeterAppInstallationRequired;
|
|
meterApp.Update = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
//CASE 4.1: update file application not found and meter application is installed
|
|
if (meterApp.IsInstalled)
|
|
{
|
|
meterApp.Status = MeterAppState.MeterAppErasureRequired;
|
|
meterApp.Erase = true;
|
|
return;
|
|
}
|
|
|
|
//CASE 4.2: update file application not found and meter application is not installed
|
|
meterApp.Status = MeterAppState.Unknown;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check last downloads
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jul-01" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
public Boolean CheckDownloadedMeterApps(MeterApplications meterApp)
|
|
{
|
|
if (_pcbId != _backupPcbId)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
foreach (var fileApp in FileApps)
|
|
{
|
|
if (meterApp.AppId != fileApp.AppId)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//Assuming on retries the latest must be the succeeded one and therefore is going to
|
|
//overwrite the failed status!
|
|
if (_updatedMeterApps.Any(f => f.AppId == meterApp.AppId))
|
|
{
|
|
foreach (var updateMeterApp in _updatedMeterApps)
|
|
{
|
|
if (fileApp.AppId != updateMeterApp.AppId)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (fileApp.StrVersion == updateMeterApp.StrVersion &&
|
|
fileApp.Version == updateMeterApp.Version &&
|
|
fileApp.Crc == updateMeterApp.Crc)
|
|
{
|
|
meterApp.Status = updateMeterApp.Status;
|
|
if (meterApp.Status == MeterAppState.MeterAppDownloadFailed)
|
|
{
|
|
meterApp.Update = true;
|
|
}
|
|
}
|
|
else if (meterApp.IsInstalled)
|
|
{
|
|
meterApp.Status = MeterAppState.MeterAppVersionOutdated;
|
|
meterApp.Update = true;
|
|
}
|
|
else
|
|
{
|
|
meterApp.Status = MeterAppState.MeterAppInstallationRequired;
|
|
meterApp.Update = true;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Remind result of download for reload of files, new connect or compare
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jul-01" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-02" author="Thomas Wiedebusch">
|
|
/// - List removal of elements simplified
|
|
/// </remarks>
|
|
private void RemindDownloadedFileApps()
|
|
{
|
|
//Check for downloaded update files for this PCB ID
|
|
if (_backupPcbId != _pcbId)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (var meterApp in _genesisMeter.MeterAppListVersion)
|
|
{
|
|
//build update files reminder
|
|
var updateMeterApp = new MeterApplications { AppId = meterApp.AppId, AppName = meterApp.AppName };
|
|
|
|
//check if at least one part of this file download failed
|
|
if (_failedFileAppsParts.Any(f => f.AppId == meterApp.AppId))
|
|
{
|
|
foreach (var fileApp in FileApps)
|
|
{
|
|
if (fileApp.AppId != meterApp.AppId)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//remove all update files which are overwritten
|
|
_updatedMeterApps.RemoveAll(f => f.AppId == meterApp.AppId);
|
|
updateMeterApp.Status = MeterAppState.MeterAppDownloadFailed;
|
|
updateMeterApp.IsInstalled = false;
|
|
updateMeterApp.StrVersion = fileApp.StrVersion;
|
|
updateMeterApp.Version = fileApp.Version;
|
|
updateMeterApp.Crc = fileApp.Crc;
|
|
_updatedMeterApps.Add(updateMeterApp);
|
|
}
|
|
}
|
|
//if the download succeeded
|
|
else if (_succeededFileAppsParts.Any(f => f.AppId == meterApp.AppId))
|
|
{
|
|
foreach (var fileApp in FileApps)
|
|
{
|
|
if (fileApp.AppId != meterApp.AppId)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//remove all update files which are overwritten
|
|
_updatedMeterApps.RemoveAll(f => f.AppId == meterApp.AppId);
|
|
updateMeterApp.Status = MeterAppState.MeterAppDownloadSucceeded;
|
|
updateMeterApp.IsInstalled = true;
|
|
updateMeterApp.StrVersion = fileApp.StrVersion;
|
|
updateMeterApp.Version = fileApp.Version;
|
|
updateMeterApp.Crc = fileApp.Crc;
|
|
_updatedMeterApps.Add(updateMeterApp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ProcessInformation
|
|
|
|
/// <summary>
|
|
/// Read the failed update files
|
|
/// </summary>
|
|
/// <remarks date="2019-Jul-04" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
public List<FileApplications> GetFailedFileApps() => _failedFileAppsParts;
|
|
|
|
/// <summary>
|
|
/// Returns the actual file in process for update
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
public String GetActualOperation()
|
|
{
|
|
return _actualOperation;
|
|
}
|
|
|
|
private Int32 _actualUpdateFileCtr;
|
|
|
|
/// <summary>
|
|
/// Returns the FW Update state
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-27" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2020-Nov-27" author="Thomas Wiedebusch">
|
|
/// - Changed to localizable language
|
|
/// </remarks>
|
|
public String GetFwUpdateStateOperation()
|
|
{
|
|
// search text assigned to current state
|
|
var text = FwUpdateStateInfo.GetTextFromState(_fwUpdateState);
|
|
if (_fwUpdateState == FwUpdateState.Idle)
|
|
{
|
|
return text;
|
|
}
|
|
|
|
text += _updateProcedureRetryCtr > 0 ? $"{Resources.StrProcessRetry}{_updateProcedureRetryCtr}" : "";
|
|
if (_remainingFileAppsParts.Count > 0)
|
|
{
|
|
text += $"{Resources.StrPartOfParts}{_actualUpdateFileCtr}/{_remainingFileAppsParts.Count}";
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Actions
|
|
|
|
/// <summary>
|
|
/// Try to re-establish the file system, therefor logout to close all open files
|
|
/// and login again.
|
|
/// </summary>
|
|
/// <remarks date="2019-Jun-21" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-28" author="Thomas Wiedebusch">
|
|
/// - Enable auto-logon added
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-03" author="Thomas Wiedebusch">
|
|
/// - Removed auto-logon
|
|
/// </remarks>
|
|
public void ReestablishMeterFileSystem()
|
|
{
|
|
_genesisMeter?.Logout();
|
|
_genesisMeter?.ReLogin();
|
|
}
|
|
|
|
private Boolean _stopUpdateProcess;
|
|
|
|
/// <summary>
|
|
/// Stop the update process
|
|
/// </summary>
|
|
public Boolean StopUpdateProcess
|
|
{
|
|
get => _stopUpdateProcess;
|
|
set
|
|
{
|
|
_stopUpdateProcess = value;
|
|
if (_meterFile != null)
|
|
{
|
|
_meterFile.StopProcess = value;
|
|
}
|
|
|
|
_remainingFileAppsParts.Clear();
|
|
_fwUpdateState = FwUpdateState.UpdateFailed;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update meter firmware
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-22" author="Thomas Wiedebusch">
|
|
/// - Build and send of upgrade control file split,
|
|
/// - OverallByteCounter re-calculated on remaining update file sizes.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-23" author="Thomas Wiedebusch">
|
|
/// - Repeated trigger update if this failed.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-25" author="Thomas Wiedebusch">
|
|
/// - Status.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-27" author="Thomas Wiedebusch">
|
|
/// - State machine implemented
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-28" author="Thomas Wiedebusch">
|
|
/// - Update preparation exported
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-29" author="Thomas Wiedebusch">
|
|
/// - Exit preparation added
|
|
/// </remarks>
|
|
public Boolean UpdateMeterFw()
|
|
{
|
|
if (!UpdatePreparation())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_fwUpdateState = FwUpdateState.StartInitial;
|
|
while (_fwUpdateState != FwUpdateState.Idle && !StopUpdateProcess)
|
|
{
|
|
FwUpdateStateMachine();
|
|
}
|
|
|
|
return ExitPreparation(true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Upload selected update files and compare them with update files
|
|
/// </summary>
|
|
/// <remarks date="2019-Jun-29" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
public Boolean ManualVerifyFiles()
|
|
{
|
|
if (!UpdatePreparation())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return ExitPreparation(true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Preselects the downloaded files for trigger upgrade
|
|
/// </summary>
|
|
/// <remarks date="2019-Jun-29" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-30" author="Thomas Wiedebusch">
|
|
/// - Compare changed
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-01" author="Thomas Wiedebusch">
|
|
/// - Prepare trigger changed to fulfill automatic FW update
|
|
/// </remarks>
|
|
public Boolean PrepareTrigger()
|
|
{
|
|
//mark download
|
|
foreach (var meterApp in _genesisMeter.MeterAppListVersion)
|
|
{
|
|
meterApp.Update = meterApp.Status == MeterAppState.MeterAppDownloadSucceeded;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Preselects the downloaded files for trigger upgrade
|
|
/// </summary>
|
|
/// <remarks date="2019-Jun-29" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
public Boolean ManualDownloadRemainingFileAppsParts()
|
|
{
|
|
if (!UpdatePreparation())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_fwUpdateState = FwUpdateState.DownloadFileApps;
|
|
var returnValue = DownloadFileApps();
|
|
_fwUpdateState = FwUpdateState.Idle;
|
|
return ExitPreparation(returnValue);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Download selected update files from beginning
|
|
/// </summary>
|
|
/// <remarks date="2019-Jun-28" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-29" author="Thomas Wiedebusch">
|
|
/// - Exit preparation added
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-30" author="Thomas Wiedebusch">
|
|
/// - Initial check changed
|
|
/// </remarks>
|
|
public Boolean ManualDownloadAllFileApps()
|
|
{
|
|
if (!ManualUpdatePreparation())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_updateProcedureRetryCtr = 0;
|
|
_fwUpdateState = FwUpdateState.BuildFileApps;
|
|
BuildFileApps();
|
|
_fwUpdateState = FwUpdateState.DownloadFileApps;
|
|
var returnValue = DownloadFileApps();
|
|
_fwUpdateState = FwUpdateState.Idle;
|
|
|
|
return ExitPreparation(returnValue);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build update control file and trigger update of selected files
|
|
/// </summary>
|
|
/// <remarks date="2019-Jun-28" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
public Boolean ManualTriggerUpgrade()
|
|
{
|
|
if (!ManualUpdatePreparation())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_fwUpdateState = FwUpdateState.BuildUpgradeControlFile;
|
|
var returnValue = BuildUpgradeCtrlFile();
|
|
if (!returnValue)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_fwUpdateState = FwUpdateState.DownloadUpgradeControlFile;
|
|
returnValue &= DownloadUpgradeCtrlFile();
|
|
if (!returnValue)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_fwUpdateState = FwUpdateState.TriggerUpgrade;
|
|
returnValue &= TriggerUpgrade();
|
|
_fwUpdateState = FwUpdateState.Idle;
|
|
return ExitPreparation(returnValue);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset FW update process and all lists and counters
|
|
/// </summary>
|
|
/// <remarks date="2019-Jun-30" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-01" author="Thomas Wiedebusch">
|
|
/// - New items added for clearance.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-02" author="Thomas Wiedebusch">
|
|
/// - Reestablish file system (logout/login/auto-login).
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-10" author="Thomas Wiedebusch">
|
|
/// - Logout added.
|
|
/// </remarks>
|
|
public void ResetAll()
|
|
{
|
|
|
|
//remove last update control file
|
|
_upgradeCtrlFile = "";
|
|
|
|
//clear all remaining file application parts for a fresh start
|
|
_remainingFileAppsParts.Clear();
|
|
_succeededFileAppsParts.Clear();
|
|
_failedFileAppsParts.Clear();
|
|
_updatedMeterApps.Clear();
|
|
|
|
//remove the update stop action
|
|
StopUpdateProcess = false;
|
|
|
|
//reset all counters
|
|
_processedBytesCtr = 0;
|
|
_overallBytesCtr = 0;
|
|
_updateProcedureRetryCtr = 0;
|
|
|
|
//set state machine for automatic FW update
|
|
_fwUpdateState = FwUpdateState.Idle;
|
|
_actualOperation = "";
|
|
|
|
//update file information
|
|
CompareAllMeterAndFileApps();
|
|
|
|
//logout/login/auto-login
|
|
ReestablishMeterFileSystem();
|
|
_genesisMeter?.Logout();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common update preparation and check for manual updates
|
|
/// </summary>
|
|
/// <remarks date="2019-Jun-28" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
public Boolean ManualUpdatePreparation()
|
|
{
|
|
if (!UpdatePreparation())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ReestablishMeterFileSystem();
|
|
//remove last update control file
|
|
_upgradeCtrlFile = "";
|
|
//clear all remaining file application parts for a fresh start
|
|
_remainingFileAppsParts.Clear();
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common update preparation and check
|
|
/// </summary>
|
|
/// <remarks date="2019-Jun-28" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-10" author="Thomas Wiedebusch">
|
|
/// - ReLogin added.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Aug-28" author="Thomas Wiedebusch">
|
|
/// - Display FW update codes in meter display added.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Dec-03" author="Thomas Wiedebusch">
|
|
/// - Genesis preparation to keep Cordonel-CPU load low.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-10" author="Thomas Wiedebusch">
|
|
/// - Installed ProcessUpdate_Event.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-19" author="Thomas Wiedebusch">
|
|
/// - Set file write timeout to default.
|
|
/// </remarks>
|
|
public Boolean UpdatePreparation()
|
|
{
|
|
_actualOperation = "";
|
|
_fwUpdateState = FwUpdateState.Idle;
|
|
|
|
_meterFile = new MeterFile(_genesisMeter);
|
|
if (_meterFile != null)
|
|
{
|
|
_meterFile.OnProcessUpdate += ProcessUpdate_Event;
|
|
}
|
|
|
|
_genesisMeter?.ReLogin();
|
|
_genesisMeter?.SetProcessState(DisplayCodes.FwUpdateActive, false);
|
|
|
|
if (_genesisMeter == null || !_genesisMeter.IsLoggedOn ||
|
|
FileApps == null || _meterFile == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//set default timeout for file write operation
|
|
_meterFile.SetDefaultFileWriteTimeout();
|
|
|
|
//switch LED off set sample rate to 1 -> 1Hz to slow down CPU load
|
|
_registersAfterUpdate = new Dictionary<String, Byte[]>();
|
|
foreach (var item in _registersBeforeUpdate)
|
|
{
|
|
var writeBackValue = _genesisMeter.ReadRegister(item.Key);
|
|
_registersAfterUpdate.Add(item.Key, writeBackValue);
|
|
_genesisMeter.WriteRegister(item.Key, item.Value);
|
|
|
|
}
|
|
|
|
//remove the update stop action
|
|
StopUpdateProcess = false;
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common exit routine
|
|
/// </summary>
|
|
/// <remarks date="2019-Jun-29" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-30" author="Thomas Wiedebusch">
|
|
/// - keep file information
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-02" author="Thomas Wiedebusch">
|
|
/// - Reestablish file system (logout/login/auto-login).
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-10" author="Thomas Wiedebusch">
|
|
/// - Logout added.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-10" author="Thomas Wiedebusch">
|
|
/// - Uninstalled ProcessUpdate_Event.
|
|
/// </remarks>
|
|
public Boolean ExitPreparation(Boolean returnValue)
|
|
{
|
|
if (_meterFile != null)
|
|
{
|
|
_meterFile.OnProcessUpdate -= ProcessUpdate_Event;
|
|
}
|
|
|
|
_meterFile = null;
|
|
ReestablishMeterFileSystem();
|
|
_genesisMeter?.Logout();
|
|
if (!StopUpdateProcess)
|
|
{
|
|
return returnValue;
|
|
}
|
|
|
|
_actualOperation = Resources.StrWaiting;
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region StateMachine
|
|
|
|
/// <summary>
|
|
/// State machine foe FW update process
|
|
/// </summary>
|
|
/// <remarks date="2019-Jun-27" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-28" author="Thomas Wiedebusch">
|
|
/// - Modified
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-01" author="Thomas Wiedebusch">
|
|
/// - Prepare trigger added to select all successfully downloaded applications
|
|
/// even if only a selection of application has been downloaded in this run.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-03" author="Thomas Wiedebusch">
|
|
/// - Removed remaining files if partial write denied,
|
|
/// - Sequences changed to build update control file after prepare trigger.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Nov-30" author="Thomas Wiedebusch">
|
|
/// - Register Retry setup removed here.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Jan-22" author="Thomas Wiedebusch">
|
|
/// - Retry entire file if some partial file writes failed (may be caused by FW file
|
|
/// system access error due to fragmentation) to create a new file on a different
|
|
/// location in the FW file system <see cref="RetryEntireFileAfterFailedPartialRetries"/>.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-19" author="Thomas Wiedebusch">
|
|
/// - Increased timeout for file write access after a procedure retry threshold
|
|
/// <see cref="IncreaseTimeoutUpdateProcedureRetries"/>. This threshold seems to be
|
|
/// useful as on increased size and number of applications needed to install in one run, the
|
|
/// file system is slowing down for write accesses.
|
|
/// </remarks>
|
|
private void FwUpdateStateMachine()
|
|
{
|
|
switch (_fwUpdateState)
|
|
{
|
|
case FwUpdateState.Idle:
|
|
break;
|
|
|
|
case FwUpdateState.StartInitial:
|
|
_updateProcedureRetryCtr = 0;
|
|
_fwUpdateState = FwUpdateState.BuildFileApps;
|
|
break;
|
|
|
|
case FwUpdateState.BuildFileApps:
|
|
if (!SingleFilePartsRetryEnable && !ConsecutiveFilePartsRetryEnable)
|
|
{
|
|
_remainingFileAppsParts.Clear();
|
|
}
|
|
|
|
BuildFileApps();
|
|
_fwUpdateState = FwUpdateState.DownloadFileApps;
|
|
break;
|
|
|
|
case FwUpdateState.DownloadFileApps:
|
|
_fwUpdateState = DownloadFileApps()
|
|
? FwUpdateState.BuildUpgradeControlFile
|
|
: FwUpdateState.StepFailed;
|
|
break;
|
|
|
|
case FwUpdateState.BuildUpgradeControlFile:
|
|
//prepare the trigger to select all successfully downloaded applications
|
|
PrepareTrigger();
|
|
_fwUpdateState = BuildUpgradeCtrlFile()
|
|
? FwUpdateState.DownloadUpgradeControlFile
|
|
: FwUpdateState.StepFailed;
|
|
break;
|
|
|
|
case FwUpdateState.DownloadUpgradeControlFile:
|
|
_fwUpdateState = DownloadUpgradeCtrlFile()
|
|
? FwUpdateState.TriggerUpgrade
|
|
: FwUpdateState.StepFailed;
|
|
break;
|
|
|
|
case FwUpdateState.TriggerUpgrade:
|
|
_fwUpdateState = TriggerUpgrade() ? FwUpdateState.Idle : FwUpdateState.StepFailed;
|
|
break;
|
|
|
|
case FwUpdateState.StepFailed:
|
|
_processedBytesCtr = 0;
|
|
_overallBytesCtr = 0;
|
|
//retry entire file if some partial file writes failed
|
|
if (RetryEntireFileAfterFailedPartialRetries == _updateProcedureRetryCtr)
|
|
{
|
|
_remainingFileAppsParts.Clear();
|
|
}
|
|
|
|
_fwUpdateState = _updateProcedureRetryCtr++ < UpdateProcedureRetries
|
|
? FwUpdateState.RepeatFwUpdate
|
|
: FwUpdateState.UpdateFailed;
|
|
break;
|
|
|
|
case FwUpdateState.RepeatFwUpdate:
|
|
_processedBytesCtr = 0;
|
|
_overallBytesCtr = 0;
|
|
_fwUpdateState = FwUpdateState.BuildFileApps;
|
|
if (_updateProcedureRetryCtr >= IncreaseTimeoutUpdateProcedureRetries)
|
|
{
|
|
_meterFile?.SetExtremeFileWriteTimeout();
|
|
}
|
|
break;
|
|
|
|
case FwUpdateState.UpdateFailed:
|
|
_processedBytesCtr = 0;
|
|
_overallBytesCtr = 0;
|
|
StopUpdateProcess = true;
|
|
_fwUpdateState = FwUpdateState.Idle;
|
|
break;
|
|
|
|
case FwUpdateState.UploadFileApps:
|
|
break;
|
|
|
|
case FwUpdateState.CompareFileApps:
|
|
break;
|
|
|
|
case FwUpdateState.EraseUpdateFile:
|
|
break;
|
|
|
|
default:
|
|
_fwUpdateState = FwUpdateState.Idle;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region FileApp
|
|
|
|
/// <summary>
|
|
/// Load all binary files from given path, this path has to contain all files for a specific build,
|
|
/// this has to be a tested combination of concatenated applications
|
|
/// </summary>
|
|
/// <param name="path">to binary files</param>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-27" author="Thomas Wiedebusch">
|
|
/// - Clear file lists on reload.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-29" author="Thomas Wiedebusch">
|
|
/// - Return false if one package file is invalid.
|
|
/// </remarks>
|
|
public Boolean LoadFileApps(String path)
|
|
{
|
|
var fileNames = Directory.GetFiles(path, StrFileAppExtensionFilter);
|
|
if (fileNames.Length == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
FileApps.Clear();
|
|
|
|
foreach (var fileName in fileNames)
|
|
{
|
|
if (!fileName.Contains("_complete.bin"))
|
|
{
|
|
|
|
|
|
var fileApplication = new FileApplications(fileName)
|
|
{
|
|
BinData = new List<Byte>(File.ReadAllBytes(fileName))
|
|
};
|
|
|
|
//add always to package files for later analysis
|
|
FileApps.Add(fileApplication);
|
|
}
|
|
}
|
|
|
|
return SetLoadedFiles();
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Load all binary files from from obj,
|
|
/// this has to be a tested combination of concatenated applications
|
|
/// </summary>
|
|
/// <param name="sourceFileApps">to binary files</param>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-27" author="Thomas Wiedebusch">
|
|
/// - Clear file lists on reload.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-29" author="Thomas Wiedebusch">
|
|
/// - Return false if one package file is invalid.
|
|
/// </remarks>
|
|
public Boolean LoadFileApps(List<FileApplications> sourceFileApps)
|
|
{
|
|
FileApps.Clear();
|
|
FileApps.AddRange(sourceFileApps);
|
|
return SetLoadedFiles();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load all binary files from given Dictionary, this Dictionary has to contain all files for a specific build,
|
|
/// this has to be a tested combination of concatenated applications
|
|
/// </summary>
|
|
/// <param name="apps"> file names with binary content </param>
|
|
/// <returns></returns>
|
|
/// <remarks date="2020-05-28" author="R:D">
|
|
/// - Initial
|
|
/// </remarks>
|
|
public Boolean LoadFileApps(Dictionary<String, List<Byte>> apps)
|
|
{
|
|
if (!apps.Any())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
FileApps.Clear();
|
|
|
|
foreach (var fileApplication in apps.Select(fileApp => new FileApplications(fileApp.Key)
|
|
{
|
|
BinData = fileApp.Value
|
|
}))
|
|
{
|
|
//add always to package files for later analysis
|
|
FileApps.Add(fileApplication);
|
|
}
|
|
|
|
return SetLoadedFiles();
|
|
}
|
|
|
|
|
|
private Boolean SetLoadedFiles()
|
|
{
|
|
var returnValue = true;
|
|
foreach (var fileApplication in FileApps)
|
|
{
|
|
|
|
fileApplication.IsValid = CheckFileApp(fileApplication);
|
|
if (!fileApplication.IsValid)
|
|
{
|
|
returnValue = false;
|
|
}
|
|
}
|
|
|
|
_remainingFileAppsParts.Clear();
|
|
_succeededFileAppsParts.Clear();
|
|
_failedFileAppsParts.Clear();
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check the content of the binaries:
|
|
/// - begins with "APP>",
|
|
/// - Application ID at position 0x28,
|
|
/// - CRC at position 0x04 LSB, 0x05 MSB
|
|
/// - Version at position 0x06 and 0x07 decimal
|
|
/// - Build upgrade meter file name
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-26" author="Thomas Wiedebusch">
|
|
/// - New file version string builder.
|
|
/// </remarks>
|
|
private static Boolean CheckFileApp(FileApplications fileApp)
|
|
{
|
|
fileApp.AppId = fileApp.BinData[FileAppAppIdIndex];
|
|
fileApp.Crc = (UInt16)(fileApp.BinData[FileAppCrcIndex] + (fileApp.BinData[FileAppCrcIndex + 1] << 8));
|
|
fileApp.Version = (UInt32)((fileApp.BinData[FileAppVersionIndex] & 0x0F)
|
|
+ ((fileApp.BinData[FileAppVersionIndex] & 0xF0) >> 4) * 10
|
|
+ (fileApp.BinData[FileAppVersionIndex + 1] & 0x0F) * 100
|
|
+ ((fileApp.BinData[FileAppVersionIndex + 1] & 0xF0) >> 4) * 1000);
|
|
|
|
fileApp.StrVersion = GenesisMeter.BuildFwVersionString(fileApp.BinData[FileAppVersionIndex + 1],
|
|
fileApp.BinData[FileAppVersionIndex]);
|
|
var fileAppStartId = Encoding.UTF8.GetString(fileApp.BinData.ToArray(), 0, 4);
|
|
//check update file start identifier
|
|
if (fileAppStartId != StrFileAppStartId)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//TODO THW check CRC
|
|
//TODO THW check upgrade file name against content to select a valid package
|
|
//the meter file name is needed for writing the file to the meter or reading it back
|
|
fileApp.MeterFilename = StrUpgradeAppPartialFileName + $"{fileApp.AppId:X2}";
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Collect FileApps out of FileApps and split them into
|
|
/// smaller pieces if the files are larger than a threshold for a single
|
|
/// write size. Each part of a partitioned file will be handles as separate
|
|
/// file in the _remainingFileAppsParts distinguished by their part number.
|
|
/// The update file generation will be skipped if _remainingFileAppsParts
|
|
/// is not zero or the file is marked as successfully downloaded!
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-27" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-30" author="Thomas Wiedebusch">
|
|
/// - Compare changed
|
|
/// </remarks>
|
|
private void BuildFileApps()
|
|
{
|
|
//avoid overwriting of valid update files being able to retry single files
|
|
if (_remainingFileAppsParts.Count != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//build update control file
|
|
foreach (var meterApp in _genesisMeter.MeterAppListVersion)
|
|
{
|
|
//check if update is required
|
|
if (!meterApp.Update)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//search valid update file in UpdatePackage
|
|
foreach (var updateFile in FileApps)
|
|
{
|
|
if (meterApp.AppId != updateFile.AppId || !updateFile.IsValid)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//copy application name
|
|
updateFile.AppName = meterApp.AppName;
|
|
//skip files which are successfully downloaded
|
|
if (meterApp.Status != MeterAppState.MeterAppDownloadSucceeded)
|
|
{
|
|
//add this update file to update file list
|
|
PartitionFileApp(updateFile);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build update files, large files will be split to several small pieces
|
|
/// handled as an individual update file
|
|
/// </summary>
|
|
/// <param name="fileApp"></param>
|
|
/// <remarks date="2019-Jun-21" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-26" author="Thomas Wiedebusch">
|
|
/// - Version string added
|
|
/// </remarks>
|
|
private void PartitionFileApp(FileApplications fileApp)
|
|
{
|
|
//split file to portions
|
|
var dataByteList = new List<Byte>();
|
|
dataByteList.AddRange(fileApp.BinData.ToArray());
|
|
|
|
//calculate records to send based on MaxPartialFileDataSize
|
|
var chunkCounts = dataByteList.Count / MaxPartialFileDataSize;
|
|
if (0 != dataByteList.Count % MaxPartialFileDataSize)
|
|
{
|
|
chunkCounts += 1;
|
|
}
|
|
|
|
var dataChunk = new List<Byte>();
|
|
for (var ctr = 0; ctr < chunkCounts; ctr++)
|
|
{
|
|
dataChunk.Clear();
|
|
var index = ctr * MaxPartialFileDataSize;
|
|
var size = index + MaxPartialFileDataSize > dataByteList.Count
|
|
? dataByteList.Count - index
|
|
: MaxPartialFileDataSize;
|
|
|
|
dataChunk.AddRange(dataByteList.GetRange(index, size));
|
|
var updateFileAppPart = new FileApplications("")
|
|
{
|
|
AppName = fileApp.AppName,
|
|
AppId = fileApp.AppId,
|
|
IsValid = fileApp.IsValid,
|
|
StrVersion = fileApp.StrVersion,
|
|
MeterFilename = fileApp.MeterFilename,
|
|
MeterFileOffset = index,
|
|
Part = ctr + 1,
|
|
Parts = chunkCounts
|
|
};
|
|
|
|
//copy binary data
|
|
updateFileAppPart.BinData.AddRange(dataChunk);
|
|
|
|
_remainingFileAppsParts.Add(updateFileAppPart);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Download and result handling for update files
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-26" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-28" author="Thomas Wiedebusch">
|
|
/// -Reestablish meter file system on failed file download,
|
|
/// information extended and automatism added.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-30" author="Thomas Wiedebusch">
|
|
/// -Compare changed.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-01" author="Thomas Wiedebusch">
|
|
/// -Exported file download marker,
|
|
/// -Stop file download at part 1 immediately, because this is needed
|
|
/// for file generation, a retry of part 1 will overwrite the rest
|
|
/// of the file! Retries for part 1 are only allowed together with all
|
|
/// other parts!
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-04" author="Thomas Wiedebusch">
|
|
/// - Partial file write single parts selectable!
|
|
/// - Partial file write after first error selectable!
|
|
/// </remarks>
|
|
private Boolean DownloadFileApps()
|
|
{
|
|
var returnValue = true;
|
|
|
|
//counters for actual process information
|
|
_processedBytesCtr = 0;
|
|
_overallBytesCtr = 0;
|
|
//counter for overall process information
|
|
_actualUpdateFileCtr = 0;
|
|
|
|
//clear markers of successfully and failed downloaded parts for this run
|
|
_succeededFileAppsParts.Clear();
|
|
_failedFileAppsParts.Clear();
|
|
|
|
//calculate the byte in advance!
|
|
foreach (var fileAppsPart in _remainingFileAppsParts)
|
|
{
|
|
//remind bytes to process
|
|
_overallBytesCtr += fileAppsPart.BinData.Count;
|
|
}
|
|
|
|
foreach (var meterApp in _genesisMeter.MeterAppListVersion)
|
|
{
|
|
//loop all upgrade files
|
|
foreach (var fileAppsPart in _remainingFileAppsParts)
|
|
{
|
|
if (meterApp.AppId != fileAppsPart.AppId)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
_actualUpdateFileCtr++;
|
|
|
|
//mark ongoing DOWNLOAD and mark this for caller
|
|
meterApp.Status = MeterAppState.MeterAppDownloadActive;
|
|
meterApp.Update = false;
|
|
|
|
//try to update files
|
|
if (_meterFile.UnlockEraseWriteMeterFile(fileAppsPart.MeterFilename) &&
|
|
DownloadFileAppPart(fileAppsPart))
|
|
{
|
|
//remind succeeded files for removal from update file list
|
|
_succeededFileAppsParts.Add(fileAppsPart);
|
|
}
|
|
else
|
|
{
|
|
//ReEstablishMeterFileSystem();
|
|
_failedFileAppsParts.Add(fileAppsPart);
|
|
|
|
//the update procedure has to be repeated, skipping the successful written files
|
|
returnValue = false;
|
|
}
|
|
|
|
//check if something was successful
|
|
if (_succeededFileAppsParts.Any(f => f.AppId == meterApp.AppId))
|
|
{
|
|
meterApp.Status = MeterAppState.MeterAppDownloadSucceeded;
|
|
}
|
|
|
|
//overwrite status to failed if one part failed
|
|
if (_failedFileAppsParts.Any(f => f.AppId == meterApp.AppId))
|
|
{
|
|
meterApp.Status = MeterAppState.MeterAppDownloadFailed;
|
|
//continue update of other parts except part 1
|
|
if (SingleFilePartsRetryEnable)
|
|
{
|
|
//break on file part 1 failed to avoid update of other parts because
|
|
//part one will always build a new file
|
|
if (fileAppsPart.Part == 1)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
//break if one file part failed all following parts to use the FSeek on next
|
|
//trial to position the remaining parts at the right position
|
|
else if (ConsecutiveFilePartsRetryEnable)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
//remove all file parts from succeeded file part list
|
|
_succeededFileAppsParts.RemoveAll(f => f.AppId == meterApp.AppId);
|
|
//stop update for entire file, retry of all file parts are required
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (StopUpdateProcess)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//remove all valid written files or parts of it from list to keep the outstanding
|
|
//update files in the list for a repeated update
|
|
foreach (var succeededFileAppsPart in _succeededFileAppsParts)
|
|
{
|
|
_remainingFileAppsParts.Remove(succeededFileAppsPart);
|
|
}
|
|
|
|
//remind PCBID for this run
|
|
_backupPcbId = _pcbId;
|
|
|
|
RemindDownloadedFileApps();
|
|
CompareAllMeterAndFileApps();
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write one update file to meter,
|
|
/// due to partitioning of large files this might be just one part of
|
|
/// the package file here handled as separate update file.
|
|
/// The file write routine will retry including re-establishing of
|
|
/// file system by logout and login again (this will close all open files).
|
|
/// </summary>
|
|
/// <param name="fileAppPart"></param>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-21" author="Thomas Wiedebusch">
|
|
/// - Partitioning of files exported
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-22" author="Thomas Wiedebusch">
|
|
/// - Re-establish file system added
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-01" author="Thomas Wiedebusch">
|
|
/// - Return value retry counter for 0 retries corrected
|
|
/// </remarks>
|
|
private Boolean DownloadFileAppPart(FileApplications fileAppPart)
|
|
{
|
|
var retryCtr = 0;
|
|
Boolean returnValue;
|
|
|
|
//write file portion and retry if not successful
|
|
do
|
|
{
|
|
//if a large file is split into several small files, the part information will
|
|
//be added
|
|
var strPart = fileAppPart.Parts > 1 ? $"{Resources.StrPartOfParts}{fileAppPart.Part}" +
|
|
$"/{fileAppPart.Parts}" : "";
|
|
var strRetries = retryCtr > 0 ? $"{Resources.StrProcessRetry}{retryCtr}" : "";
|
|
_actualOperation = $"{Resources.StrProcessDownload} {fileAppPart.AppName}" + strPart + strRetries;
|
|
|
|
returnValue = _meterFile.WriteMeterFile(fileAppPart.MeterFilename,
|
|
fileAppPart.BinData.ToArray(), fileAppPart.MeterFileOffset);
|
|
|
|
if (!returnValue)
|
|
{
|
|
ReestablishMeterFileSystem();
|
|
}
|
|
} while (!returnValue && retryCtr++ < FileWriteRetries && !StopUpdateProcess);
|
|
|
|
returnValue &= retryCtr <= FileWriteRetries;
|
|
|
|
//update information for process bar
|
|
_processedBytesCtr += fileAppPart.BinData.Count;
|
|
|
|
return !StopUpdateProcess && returnValue;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region UpgradeCtrlFile
|
|
|
|
/// <summary>
|
|
/// Build the upgrade control file.
|
|
/// The update control file generation will be skipped if the update control file
|
|
/// is set!
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-27" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
private Boolean BuildUpgradeCtrlFile()
|
|
{
|
|
//avoid overwriting of valid update control file being able to retry single files
|
|
if (_upgradeCtrlFile != "")
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//build update control file
|
|
foreach (var meterApp in _genesisMeter.MeterAppListVersion)
|
|
{
|
|
//check if update is required
|
|
if (meterApp.Update)
|
|
{
|
|
//add this update file requirement to control file
|
|
_upgradeCtrlFile += $"{meterApp.AppId:X2}" + StrInstallAppCtrlFilePost;
|
|
}
|
|
|
|
//check if erase is required
|
|
else if (meterApp.Erase && meterApp.IsInstalled)
|
|
{
|
|
//add this erase requirement to control file
|
|
_upgradeCtrlFile += $"{meterApp.AppId:X2}" + StrEraseAppCtrlFilePost;
|
|
}
|
|
}
|
|
|
|
return _upgradeCtrlFile != "";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send the upgrade control file to the meter
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-22" author="Thomas Wiedebusch">
|
|
/// - Exported writing of upgrade control file
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
|
|
/// - Restore performance registers before trigger upgrade.
|
|
/// </remarks>
|
|
private Boolean DownloadUpgradeCtrlFile()
|
|
{
|
|
//initiate upgrade by writing the upgrade control file
|
|
if (_upgradeCtrlFile == "" ||
|
|
!_meterFile.UnlockEraseWriteMeterFile(StrUpgradeCtrlFileName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_actualOperation = Resources.StrFwUpdateStateDownloadUpgradeControlFile;
|
|
|
|
var retryCtr = 0;
|
|
Boolean returnValue;
|
|
//write file and retry if not successful
|
|
do
|
|
{
|
|
returnValue = _meterFile.WriteMeterFile(StrUpgradeCtrlFileName,
|
|
Encoding.ASCII.GetBytes(_upgradeCtrlFile));
|
|
|
|
if (!returnValue)
|
|
{
|
|
ReestablishMeterFileSystem();
|
|
}
|
|
} while (!returnValue && retryCtr++ < FileWriteRetries && !StopUpdateProcess);
|
|
|
|
// restore register values before TriggerUpgrade, after TriggerUpgrade the Gensis
|
|
// cannot be reached for a certain time.
|
|
foreach (var item in _registersAfterUpdate)
|
|
{
|
|
_genesisMeter.WriteRegister(item.Key, item.Value);
|
|
}
|
|
|
|
return !StopUpdateProcess && returnValue;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region TriggerUpgrade
|
|
|
|
/// <summary>
|
|
/// Send the trigger update
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-24" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-28" author="Thomas Wiedebusch">
|
|
/// - If trigger upgrade succeeded mark all actually selected package files
|
|
/// as succeeded and remove update or erase marker
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jul-01" author="Thomas Wiedebusch">
|
|
/// - Delete downloaded (updated) meter application files, they are validated on
|
|
/// accepted trigger.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Dec-03/05" author="Thomas Wiedebusch">
|
|
/// - Extended timeout.
|
|
/// </remarks>
|
|
private Boolean TriggerUpgrade()
|
|
{
|
|
Boolean returnValue;
|
|
//trigger upgrade
|
|
_actualOperation = Resources.StrFwUpdateStateTriggerUpgrade;
|
|
var triggerCtr = 0;
|
|
|
|
//set extended timeout for trigger upgrade response delay
|
|
_genesisMeter.TransmitProtocol.SetResponseTimeout(5000);
|
|
do
|
|
{
|
|
returnValue = _genesisMeter.WriteRegister<Byte>(Register.System.TriggerFwUpgrade, 1);
|
|
|
|
if (!returnValue)
|
|
{
|
|
ReestablishMeterFileSystem();
|
|
}
|
|
} while (!returnValue && triggerCtr++ < TriggerWriteRetries && !StopUpdateProcess);
|
|
|
|
//set timeout back to default value
|
|
_genesisMeter.TransmitProtocol.SetDefaultResponseTimeout();
|
|
_actualOperation = returnValue ? Resources.StrMeterAppTriggerAccepted : Resources.StrMeterAppTriggerFailed;
|
|
|
|
if (!returnValue || StopUpdateProcess)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//prepare temporary information based on the trigger upgrade feedback,
|
|
//this information has to be validated with reading all FW versions
|
|
|
|
//mark package files which are in this update as succeeded
|
|
foreach (var meterApp in _genesisMeter.MeterAppListVersion)
|
|
{
|
|
//check if update is required and now acknowledged
|
|
if (meterApp.Update)
|
|
{
|
|
//search for according update file
|
|
foreach (var fileApp in FileApps)
|
|
{
|
|
if (meterApp.AppId != fileApp.AppId)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
meterApp.StrVersion = fileApp.StrVersion;
|
|
meterApp.Crc = fileApp.Crc;
|
|
meterApp.Version = fileApp.Version;
|
|
meterApp.IsInstalled = true;
|
|
}
|
|
}
|
|
|
|
//check if erase is require
|
|
else if (meterApp.Erase)
|
|
{
|
|
meterApp.IsInstalled = false;
|
|
meterApp.StrVersion = "";
|
|
meterApp.Version = 0;
|
|
meterApp.Crc = 0;
|
|
}
|
|
|
|
}
|
|
|
|
_updatedMeterApps.Clear();
|
|
CompareAllMeterAndFileApps();
|
|
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Conversion
|
|
|
|
/// <summary>
|
|
/// Build application id string
|
|
/// </summary>
|
|
/// <param name="appId"></param>
|
|
/// <returns></returns>
|
|
public static String ConvertAppIdToString(UInt32 appId)
|
|
{
|
|
return $"0x{appId:X2}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build a version string like 2.02
|
|
/// </summary>
|
|
/// <param name="version"></param>
|
|
/// <returns></returns>
|
|
public static String ConvertVersionToString(UInt32 version)
|
|
{
|
|
return $"{version / 100}.{version % 100:D2}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build the hexadecimal CRC string
|
|
/// </summary>
|
|
/// <param name="crc"></param>
|
|
/// <returns></returns>
|
|
public static String ConvertCrcToString(UInt32 crc)
|
|
{
|
|
return $"0x{crc:X4}";
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region PackageFile
|
|
|
|
/// <summary>
|
|
/// Load package control file
|
|
/// </summary>
|
|
/// <param name="filePathName">to binary files</param>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-24" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
public Boolean LoadPackageFile(String filePathName)
|
|
{
|
|
if (!File.Exists(filePathName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_packageDescriptionFile = File.ReadAllText(filePathName);
|
|
return _packageDescriptionFile.Length != 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load package control file
|
|
/// </summary>
|
|
/// <param name="text">control file content</param>
|
|
/// <returns></returns>
|
|
/// <remarks date="2010-AUG-11" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
public Boolean LoadPackageFileFromText(String text)
|
|
{
|
|
_packageDescriptionFile = text;
|
|
return _packageDescriptionFile.Length != 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate package file with update files
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Jun-24" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-26" author="Thomas Wiedebusch">
|
|
/// - Version string used instead of conversion from UInt32 to string
|
|
/// </remarks>
|
|
/// <remarks date="2019-Jun-29" author="Thomas Wiedebusch">
|
|
/// - Return false if one package file is checked being invalid.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-02" author="Thomas Wiedebusch">
|
|
/// - Extraction of valid core revisions min/max.
|
|
/// </remarks>
|
|
public Boolean ValidateFileAppsWithPackageFile()
|
|
{
|
|
if (string.IsNullOrEmpty(_packageDescriptionFile) || FileApps.Count == 0 ||
|
|
FileApps.Any(f => !f.IsValid))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var packageFileLines = _packageDescriptionFile.Split('\n');
|
|
var validAppsCtr = 0;
|
|
foreach (var fileApp in FileApps)
|
|
{
|
|
//find a line which contains all required information
|
|
validAppsCtr += packageFileLines.Count(line =>
|
|
line.Contains($"{StrPackageFileAppId}{ConvertAppIdToString(fileApp.AppId)}") &&
|
|
line.Contains($"{StrPackageFileAppCrc}{ConvertCrcToString(fileApp.Crc)}") &&
|
|
line.Contains($"{StrPackageFileAppVersion}{fileApp.StrVersion}"));
|
|
}
|
|
|
|
//extract the core revision fields
|
|
_strCoreRevisionMinimum = packageFileLines.First(line => line.Contains(StrCoreRevisionMinId)
|
|
&& line.Contains(StrCoreRevisionCordonelId));
|
|
_strCoreRevisionMaximum = packageFileLines.First(line => line.Contains(StrCoreRevisionMaxId)
|
|
&& line.Contains(StrCoreRevisionCordonelId));
|
|
CoreRevisionMinimum = ExtractVersionFromString(_strCoreRevisionMinimum);
|
|
CoreRevisionMaximum = ExtractVersionFromString(_strCoreRevisionMaximum);
|
|
|
|
return validAppsCtr == FileApps.Count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the core version is in range of required minimum to maximum version.
|
|
/// The package file has to be read in advance as this contains the min/max.
|
|
/// </summary>
|
|
/// <param name="coreVersionString">string containing the core version</param>
|
|
/// <returns>true if version is in range</returns>
|
|
/// <remarks date="2020-Dec-08" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-10" author="Thomas Wiedebusch">
|
|
/// - Removed "Cordonel " string check.
|
|
/// </remarks>
|
|
public Boolean CheckCoreRevision(String coreVersionString)
|
|
{
|
|
if (null == CoreRevisionMaximum || null == CoreRevisionMinimum || null == coreVersionString ||
|
|
null == _packageDescriptionFile)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var meterCoreRevision = ExtractVersionFromString(coreVersionString);
|
|
return meterCoreRevision >= CoreRevisionMinimum && meterCoreRevision <= CoreRevisionMaximum;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extract the version in form "1.23" from a string and returns it
|
|
/// as Int32 value 123 with major * 100 + minor
|
|
/// </summary>
|
|
/// <param name="versionString">string containing version</param>
|
|
/// <returns>version as double</returns>
|
|
/// <remarks date="2020-Dec-03" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Feb-24" author="Thomas Wiedebusch">
|
|
/// - Changed to Int32 with major * 1000 + minor.
|
|
/// </remarks>
|
|
/// <remarks date="2021-Mar-09" author="Thomas Wiedebusch">
|
|
/// - Changed to Int32 with major * 100 + minor.
|
|
/// </remarks>
|
|
public static Int32 ExtractVersionFromString(String versionString)
|
|
|
|
{
|
|
var majorVersion = 0;
|
|
var minorVersion = 0;
|
|
var strResults = Regex.Split(versionString, @"\D");
|
|
//the first number is the major version, the second the minor
|
|
var idx = 0;
|
|
for (; idx < strResults.Length - 1; idx++)
|
|
{
|
|
if (string.IsNullOrEmpty(strResults[idx]))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
majorVersion = int.Parse(strResults[idx]);
|
|
break;
|
|
}
|
|
|
|
for (; idx < strResults.Length; idx++)
|
|
{
|
|
if (string.IsNullOrEmpty(strResults[idx]))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
minorVersion = int.Parse(strResults[idx]);
|
|
}
|
|
var version = majorVersion * MajorMultiply + minorVersion;
|
|
return version;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|