Merge
Common\Hardware\WaterMeter\Genesis\GenesisFile\MeterFile.cs
This commit is contained in:
@@ -630,6 +630,11 @@ namespace Xylem.Common.Hardware.Interfaces.Ports.SerialPorts
|
||||
/// <remarks date="2018-Oct-23" author="T.Wiedebusch">
|
||||
/// - Doubling of sync byte in protocol behind sync byte itself implemented
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Feb-25" author="T.Wiedebusch, R.Drabesch">
|
||||
/// - Flush buffer as MOXA sometimes takes two messages and combines these two one!
|
||||
/// As the Genesis needs a separated wake-up message with a following delay before the
|
||||
/// real payload message, this causes a lot of trouble.
|
||||
/// </remarks>
|
||||
protected void PhysicalWrite(Byte[] txBuffer)
|
||||
{
|
||||
try
|
||||
@@ -649,18 +654,20 @@ namespace Xylem.Common.Hardware.Interfaces.Ports.SerialPorts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_serialPort.Write(txByteList.ToArray(), 0, txByteList.Count);
|
||||
|
||||
// flush the buffer for MOXA, to avoid two subsequent communications assembled to one communication!
|
||||
_serialPort.BaseStream.Flush();
|
||||
|
||||
OnRawRecordSendOut?.Invoke(this, new ListBytePortDataEventArgs(txByteList, DateTimeOffset.UtcNow));
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_byteDataLogger.Error($"{Ident} {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
#region Prop
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -24,17 +24,17 @@ namespace Xylem.Common.Hardware.Interfaces.Ports.SerialPorts
|
||||
/// </remarks>
|
||||
protected override void SpecificPortWrite(Byte[] tx)
|
||||
{
|
||||
//send wake up burst with two 0xF0 byte
|
||||
//var wakeUpBurst = new Byte[] { 0xF0, 0xF0 };
|
||||
var wakeUpBurst = new Byte[] { 0x01, 0x01, 0x01, 0x01, };
|
||||
var delayPattern = new Byte[] { 0x0F, 0x0F, 0x0F, 0x0F , 0x0F, 0x0F, 0x0F, 0x0F };
|
||||
//send wake up burst
|
||||
var wakeUpBurst = new Byte[] { 0x01, 0x01, 0x01, 0x01 };
|
||||
PhysicalWrite(wakeUpBurst);
|
||||
Thread.Sleep(1);
|
||||
//delay at least 500 µs with 0f *8
|
||||
//PhysicalWrite(delayPattern);
|
||||
//Thread.Sleep(1);
|
||||
|
||||
|
||||
//delay at least 500 µs
|
||||
Thread.Sleep(1);
|
||||
|
||||
//send the IrDA record
|
||||
PhysicalWrite(tx);
|
||||
}
|
||||
|
||||
@@ -1141,6 +1141,21 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
/// </summary>
|
||||
public String CoreRevision;
|
||||
|
||||
/// <summary>
|
||||
/// This is the FLEXNET version which describes the entire packet.
|
||||
/// </summary>
|
||||
public String FwVersion;
|
||||
|
||||
/// <summary>
|
||||
/// This is the Metrology Lookup Table CRC.
|
||||
/// </summary>
|
||||
public String LutCrc;
|
||||
|
||||
/// <summary>
|
||||
/// This is the meter size.
|
||||
/// </summary>
|
||||
public String MeterSize;
|
||||
|
||||
/// <summary>
|
||||
/// Region (EMEA or NA) and Radio frequency (433 or 868 or null).
|
||||
/// </summary>
|
||||
@@ -1165,11 +1180,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
/// </remarks>
|
||||
private void ReadMeterFirmwareAndAssignRegisters()
|
||||
{
|
||||
//if (true)
|
||||
//{
|
||||
|
||||
// return;
|
||||
//}
|
||||
//avoid overwriting of list if this already exits
|
||||
if (MeterAppListVersion.Any())
|
||||
{
|
||||
@@ -1196,16 +1206,17 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
//read version, skip retries on SYSTEM 0x04-> FW nor present is a valid feedback
|
||||
var readFwVersionBytes = ReadRegister(Register.System.CheckFwPresence, 4, 0x04);
|
||||
|
||||
//the FW version as Uint32 will be used for comparison with the configuration.json
|
||||
if (readFwVersionBytes != null && readFwVersionBytes.Length > 0)
|
||||
{
|
||||
readFwVersion = (UInt32)(((readFwVersionBytes[1] & 0xF0) >> 4) * 10 +
|
||||
(readFwVersionBytes[1] & 0x0F)) * 100;
|
||||
readFwVersion += (UInt32)(((readFwVersionBytes[0] & 0xF0) >> 4) * 10 +
|
||||
(readFwVersionBytes[0] & 0x0F));
|
||||
//build the version string, because e.g. FLEXNETVERSION is one application which does not
|
||||
//follow the same rule. After the upper conversion, the hexadecimal outline will be lost
|
||||
versionString = BuildFwVersionString(readFwVersionBytes[1], readFwVersionBytes[0]);
|
||||
//the FW version as Uint32 will be used for comparison with the configuration.json
|
||||
if (readFwVersionBytes != null && readFwVersionBytes.Length > 0)
|
||||
{
|
||||
readFwVersion = (UInt32)(((readFwVersionBytes[1] & 0xF0) >> 4) * 10 +
|
||||
(readFwVersionBytes[1] & 0x0F)) * 100;
|
||||
readFwVersion += (UInt32)(((readFwVersionBytes[0] & 0xF0) >> 4) * 10 +
|
||||
(readFwVersionBytes[0] & 0x0F));
|
||||
//build the version string, because e.g. FLEXNETVERSION is one application which does not
|
||||
//follow the same rule. After the upper conversion, the hexadecimal outline will be lost
|
||||
versionString = BuildFwVersionString(readFwVersionBytes[1], readFwVersionBytes[0]);
|
||||
if (appVersionToRead.AppName == "FLEXNETVERSION") FwVersion = versionString;
|
||||
|
||||
if (readFwVersion != 0)
|
||||
{
|
||||
@@ -1220,7 +1231,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
}
|
||||
}
|
||||
}
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
|
||||
//add application always, even if this is not installed. This will be used for FW updates
|
||||
@@ -1240,6 +1250,15 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
MetrologyUpgradePermission = RegisterConverter.ConvertTo<Byte>(
|
||||
ReadRegister(Register.System.MetrologyUpgradePermission));
|
||||
|
||||
//read metrology lookup table CRC
|
||||
var lutCrc = RegisterConverter.ConvertTo<UInt32>(ReadRegister(Register.Genesisflow.LookupFileCrc));
|
||||
LutCrc = $@"0x{lutCrc:X4}";
|
||||
|
||||
|
||||
//read meter size
|
||||
MeterSize = MeterSizeConverter.ConvertMeterSizeEnumToSizeName((MeterSize)RegisterConverter.ConvertTo<Byte>(
|
||||
ReadRegister(Register.Genesisflow.MeterSize)));
|
||||
|
||||
//read core revision
|
||||
var coreRegisterRead = RegisterConverter.ConvertTo<String>(ReadRegister(Register.System.CoreRevision, 16));
|
||||
var coreRevision = coreRegisterRead?.Split('\0');
|
||||
@@ -1288,10 +1307,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
}
|
||||
}
|
||||
|
||||
//read radio frequency
|
||||
|
||||
_logger.Info("Region and radio frequency[Mhz]: " + RegionRadio);
|
||||
//safe temporary registers from file to compare first and last version with application version read from meter and
|
||||
_logger.Info($"Meter size: {MeterSize}");
|
||||
//remove unused registers with invalid version
|
||||
var tempConfigRegisters = _configRegister.MeterRegisterDic.ToList();
|
||||
//tempConfigRegisters.ToList()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.Consts
|
||||
{
|
||||
/// <summary>
|
||||
/// Genesis meter lookup table update states
|
||||
/// </summary>
|
||||
public enum LutUpdateState
|
||||
{
|
||||
/// <summary>
|
||||
/// Metrology lookup table update state Idle
|
||||
/// </summary>
|
||||
Idle,
|
||||
/// <summary>
|
||||
/// Single step failed
|
||||
/// </summary>
|
||||
StepFailed,
|
||||
/// <summary>
|
||||
/// Entire process failed
|
||||
/// </summary>
|
||||
UpdateFailed,
|
||||
/// <summary>
|
||||
/// Start initial FW update
|
||||
/// </summary>
|
||||
StartInitial,
|
||||
/// <summary>
|
||||
/// Repeat update
|
||||
/// </summary>
|
||||
RepeatLutUpdate,
|
||||
/// <summary>
|
||||
/// Upload lookup table file
|
||||
/// </summary>
|
||||
UploadLutFile,
|
||||
/// <summary>
|
||||
/// Download lookup table file
|
||||
/// </summary>
|
||||
DownloadLutFile,
|
||||
/// <summary>
|
||||
/// Compare lookup table file
|
||||
/// </summary>
|
||||
CompareLutFile,
|
||||
/// <summary>
|
||||
/// Download the CRC, the meter size and store calibration as preparation for the lookup table
|
||||
/// </summary>
|
||||
FinalizeLutDownload,
|
||||
/// <summary>
|
||||
/// Erase lookup table file
|
||||
/// </summary>
|
||||
EraseLutFile,
|
||||
/// <summary>
|
||||
/// Delimiter for list
|
||||
/// </summary>
|
||||
LutUpdateStateListDelimiter
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.Properties;
|
||||
|
||||
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.Consts
|
||||
{
|
||||
/// <summary>
|
||||
/// Concatenation of state and string
|
||||
/// </summary>
|
||||
public static class LutUpdateStateInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Build state information
|
||||
/// </summary>
|
||||
private static readonly LutUpdateStateText[] StateInfos;
|
||||
|
||||
/// <summary>
|
||||
/// Ctor
|
||||
/// </summary>
|
||||
static LutUpdateStateInfo()
|
||||
{
|
||||
StateInfos = new LutUpdateStateText[(Int32)LutUpdateState.LutUpdateStateListDelimiter];
|
||||
StateInfos[0].State = LutUpdateState.Idle;
|
||||
StateInfos[0].Message = "";
|
||||
|
||||
StateInfos[1].State = LutUpdateState.StepFailed;
|
||||
StateInfos[1].Message = "Processing Step Failed";
|
||||
|
||||
StateInfos[2].State = LutUpdateState.UpdateFailed;
|
||||
StateInfos[2].Message = "LUT Update Failed";
|
||||
|
||||
StateInfos[3].State = LutUpdateState.StartInitial;
|
||||
StateInfos[3].Message = "Initial start";
|
||||
|
||||
StateInfos[4].State = LutUpdateState.RepeatLutUpdate;
|
||||
StateInfos[4].Message = "Repeat update";
|
||||
|
||||
StateInfos[5].State = LutUpdateState.UploadLutFile;
|
||||
StateInfos[5].Message = "Upload LUT file from meter";
|
||||
|
||||
StateInfos[6].State = LutUpdateState.DownloadLutFile;
|
||||
StateInfos[6].Message = "Download LUT file to meter";
|
||||
|
||||
StateInfos[7].State = LutUpdateState.CompareLutFile;
|
||||
StateInfos[7].Message = "Compare LUT file";
|
||||
|
||||
StateInfos[8].State = LutUpdateState.FinalizeLutDownload;
|
||||
StateInfos[8].Message = "Finalize LUT file installation";
|
||||
|
||||
StateInfos[9].State = LutUpdateState.EraseLutFile;
|
||||
StateInfos[9].Message = "Erase LUT file in meter";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current information
|
||||
/// </summary>
|
||||
/// <param name="state">state to search for information text</param>
|
||||
public static String GetTextFromState(LutUpdateState state)
|
||||
{
|
||||
return (from stateInfo in StateInfos
|
||||
where stateInfo.State == state select stateInfo.Message).FirstOrDefault();
|
||||
}
|
||||
/// <summary>
|
||||
/// Get the state of the text
|
||||
/// </summary>
|
||||
/// <param name="text">information text to search if state is assigned</param>
|
||||
public static LutUpdateState GetStateFromText(String text)
|
||||
{
|
||||
return (from stateInfo in StateInfos
|
||||
where stateInfo.Message == text select stateInfo.State).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.Consts
|
||||
{
|
||||
/// <summary>
|
||||
/// Concatenation between lokup table update state and text
|
||||
/// </summary>
|
||||
public struct LutUpdateStateText
|
||||
{
|
||||
/// <summary>
|
||||
/// The state of the firmware update
|
||||
/// </summary>
|
||||
public LutUpdateState State;
|
||||
/// <summary>
|
||||
/// The message text of the lookup table update state
|
||||
/// </summary>
|
||||
public String Message;
|
||||
}
|
||||
}
|
||||
@@ -96,11 +96,16 @@
|
||||
<Compile Include="..\..\..\..\.shared\SharedAssemblyInfo.cs">
|
||||
<Link>Properties\SharedAssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Consts\LutUpdateStateText.cs" />
|
||||
<Compile Include="Consts\LutUpdateStateInfo.cs" />
|
||||
<Compile Include="Consts\LutUpdateState.cs" />
|
||||
<Compile Include="Consts\FwUpdateState.cs" />
|
||||
<Compile Include="Consts\FwUpdateStateInfo.cs" />
|
||||
<Compile Include="Consts\FwUpdateStateText.cs" />
|
||||
<Compile Include="MeterLutFile.cs" />
|
||||
<Compile Include="MeterFileRestore.cs" />
|
||||
<Compile Include="MeterFilesEraseRestore.cs" />
|
||||
<Compile Include="MeterLutUpdate.cs" />
|
||||
<Compile Include="MeterFwUpdate.cs" />
|
||||
<Compile Include="MeterFwUpdateCapability.cs" />
|
||||
<Compile Include="MeterFwUpdateRuler.cs" />
|
||||
@@ -119,6 +124,14 @@
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\Logic.PrdoctionToProductMapper\Logic.ProductionToProductMapper.csproj">
|
||||
<Project>{D0C859D9-D55E-4E85-AC50-0FF31B1CE50D}</Project>
|
||||
<Name>Logic.ProductionToProductMapper</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\..\Utils\Crc16Ccitt\Crc16Ccitt.csproj">
|
||||
<Project>{91DEF0C0-AA4F-40AE-8F18-CB717FDE7E7F}</Project>
|
||||
<Name>Crc16Ccitt</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\..\Utils\Logging\Logging.csproj">
|
||||
<Project>{4B88B0D3-E791-4774-9521-EABEACDC420E}</Project>
|
||||
<Name>Logging</Name>
|
||||
|
||||
@@ -29,9 +29,16 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
private Int32 _fileWriteTimeOutMs = ExtendedFileIoTimeoutMs;
|
||||
|
||||
private readonly GenesisMeter _currentGenesis;
|
||||
private Int32 _filePointer;
|
||||
//TODO THW test only private const String StrNewFileReadWriteMode = "wb+";
|
||||
//wb open file for writing in binary mode, if file exists its contents will be overwritten,
|
||||
//if file does not exist it will be created
|
||||
private const String StrNewFileReadWriteMode = "wb";
|
||||
//Open for reading in binary mode. If the file does not exist, fopen( ) returns NULL
|
||||
public Int32 _filePointer;
|
||||
private const String StrNewFileReadWriteMode = "wb+";
|
||||
private const String StrOpenFileReadOnlyMode = "rb";
|
||||
//Open for both reading and writing in binary mode. If the file does not exist, fopen( ) returns NULL
|
||||
private const String StrOpenFileReadWriteMode = "rb+";
|
||||
private String _fileName;
|
||||
private Int32 _overallBytesCtr;
|
||||
@@ -55,7 +62,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
/// Wildcard for search all.
|
||||
/// </summary>
|
||||
public const Char WildcardAll = '*';
|
||||
// public const String StrWildcardAll = "*";
|
||||
// public const String StrWildcardAll = "*";
|
||||
|
||||
/// <summary>
|
||||
/// Processed bytes counter
|
||||
@@ -171,6 +178,9 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
/// <remarks date="2020-Dec-19" author="Thomas Wiedebusch">
|
||||
/// - Dynamic timeout load from extended to extreme timeout on retries.
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Apr-01" author="Thomas Wiedebusch">
|
||||
/// - Add a NULL at the end of the file name.
|
||||
/// </remarks>
|
||||
public Boolean WriteMeterFile(String fileName, Byte[] dataBytes, Int32 fileOffset = 0)
|
||||
{
|
||||
var returnValue = false;
|
||||
@@ -181,8 +191,11 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", 0));
|
||||
_overallBytesCtr = dataBytes.Length;
|
||||
|
||||
//add file name delimiter
|
||||
fileName += StringDelimiter;
|
||||
//check if file is unlocked for write
|
||||
if (_fileName != fileName) return false;
|
||||
if (_fileName != fileName)
|
||||
return false;
|
||||
|
||||
if (fileOffset == 0 && OpenMeterFile(fileName, StrNewFileReadWriteMode)
|
||||
|| fileOffset > 0 && OpenMeterFile(fileName, StrOpenFileReadWriteMode))
|
||||
@@ -319,18 +332,20 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
/// <remarks date="2020-Dec-10" author="Thomas Wiedebusch">
|
||||
/// - Event on changed process counter.
|
||||
/// </remarks>
|
||||
public Boolean ReadMeterFile(String fileName, out List<Byte> readBackData, Int32? readBackCount = null, string OpenFileReadOnlyModeOver = null)
|
||||
/// <remarks date="2022-Apr-01" author="Thomas Wiedebusch">
|
||||
/// - Add a NULL at the end of the file name.
|
||||
/// </remarks>
|
||||
public Boolean ReadMeterFile(String fileName, out List<Byte> readBackData, Int32 readBackCount)
|
||||
{
|
||||
if (String.IsNullOrEmpty(OpenFileReadOnlyModeOver))
|
||||
{
|
||||
OpenFileReadOnlyModeOver = StrOpenFileReadOnlyMode;
|
||||
}
|
||||
//set processing counter
|
||||
ProcessedBytesCtr = 0;
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", 0));
|
||||
|
||||
//add file name delimiter
|
||||
fileName += StringDelimiter;
|
||||
|
||||
readBackData = new List<Byte>();
|
||||
var returnValue = OpenMeterFile(fileName, OpenFileReadOnlyModeOver);
|
||||
var returnValue = OpenMeterFile(fileName, StrOpenFileReadOnlyMode);
|
||||
|
||||
if (returnValue)
|
||||
{
|
||||
@@ -343,29 +358,29 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
CommunicationConfig.RequestRetries = 0;
|
||||
_currentGenesis.TransmitProtocol.SetResponseTimeout(ExtendedFileIoTimeoutMs);
|
||||
|
||||
int fixedReadBackCount;
|
||||
//int fixedReadBackCount;
|
||||
|
||||
if (readBackCount.HasValue)
|
||||
{
|
||||
fixedReadBackCount = readBackCount.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentGenesis.WriteRegister("CONFIGEXCHANGE_FSeek", _filePointer, false);
|
||||
_currentGenesis.WriteRegister("CONFIGEXCHANGE_FSeek", 0, false);
|
||||
_currentGenesis.WriteRegister("CONFIGEXCHANGE_FSeek", 2, false);
|
||||
//if (readBackCount.HasValue)
|
||||
//{
|
||||
// fixedReadBackCount = readBackCount.Value;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// _currentGenesis.WriteRegister("CONFIGEXCHANGE_FSeek", _filePointer, false);
|
||||
// _currentGenesis.WriteRegister("CONFIGEXCHANGE_FSeek", 0, false);
|
||||
// _currentGenesis.WriteRegister("CONFIGEXCHANGE_FSeek", 2, false);
|
||||
|
||||
|
||||
|
||||
_currentGenesis.WriteRegister("CONFIGEXCHANGE_FTell", _filePointer, false);
|
||||
fixedReadBackCount = RegisterConverter.ConvertTo<Int32>(_currentGenesis.ReadRegister("CONFIGEXCHANGE_FTell")) + 1;
|
||||
// _currentGenesis.WriteRegister("CONFIGEXCHANGE_FTell", _filePointer, false);
|
||||
// fixedReadBackCount = RegisterConverter.ConvertTo<Int32>(_currentGenesis.ReadRegister("CONFIGEXCHANGE_FTell")) + 1;
|
||||
|
||||
_currentGenesis.WriteRegister("CONFIGEXCHANGE_FSeek", _filePointer, false);
|
||||
_currentGenesis.WriteRegister("CONFIGEXCHANGE_FSeek", 0, false);
|
||||
_currentGenesis.WriteRegister("CONFIGEXCHANGE_FSeek", 0, false);
|
||||
// _currentGenesis.WriteRegister("CONFIGEXCHANGE_FSeek", _filePointer, false);
|
||||
// _currentGenesis.WriteRegister("CONFIGEXCHANGE_FSeek", 0, false);
|
||||
// _currentGenesis.WriteRegister("CONFIGEXCHANGE_FSeek", 0, false);
|
||||
|
||||
|
||||
}
|
||||
//}
|
||||
//write size to 1
|
||||
_currentGenesis.WriteRegister(Register.Configexchange.FileRead, 1, false);
|
||||
//write counts to expected length
|
||||
@@ -374,10 +389,8 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
_currentGenesis.WriteRegister(Register.Configexchange.FileRead, _filePointer);
|
||||
|
||||
//the preceding routine needs to wait for response, else this is always true!
|
||||
while (fixedReadBackCount > 0 && returnValue)
|
||||
while (readBackCount > 0 && !_stopProcess) // while (fixedReadBackCount > 0 && returnValue)
|
||||
{
|
||||
returnValue = _stopProcess;
|
||||
|
||||
var remainingData = RegisterConverter.ConvertTo<Int32>(
|
||||
_currentGenesis.ReadRegister(Register.Configexchange.FileRead));
|
||||
|
||||
@@ -465,7 +478,8 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
_currentGenesis.ReadRegister(Register.Configexchange.Catalogue));
|
||||
}
|
||||
|
||||
if (key == lastKey) break;
|
||||
if (key == lastKey)
|
||||
break;
|
||||
// remove the "\0"
|
||||
var strMeterFile = readData.Split(StringDelimiter);
|
||||
// avoid identical files repetition
|
||||
@@ -485,12 +499,19 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
/// <remarks date="2021-Jun-20" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Apr-01" author="Thomas Wiedebusch">
|
||||
/// - Add a NULL at the end of the file name.
|
||||
/// </remarks>
|
||||
public Boolean CreateEmptyMeterFile(String fileName, Int32 fileSize = 0)
|
||||
{
|
||||
var returnValue = false;
|
||||
|
||||
//add file name delimiter
|
||||
fileName += StringDelimiter;
|
||||
|
||||
//check if file is unlocked for write
|
||||
if (_fileName != fileName) return false;
|
||||
if (_fileName != fileName)
|
||||
return false;
|
||||
|
||||
if (OpenMeterFile(fileName, StrNewFileReadWriteMode))
|
||||
{
|
||||
@@ -543,7 +564,8 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
|
||||
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
|
||||
|
||||
if (returnedFileOffset != fileSize) returnValue = false;
|
||||
if (returnedFileOffset != fileSize)
|
||||
returnValue = false;
|
||||
}
|
||||
}//file open
|
||||
|
||||
@@ -560,9 +582,16 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Apr-01" author="Thomas Wiedebusch">
|
||||
/// - Add a NULL at the end of the file name.
|
||||
/// </remarks>
|
||||
public Boolean VerifyMeterFile(String fileName, Byte[] dataBytes)
|
||||
{
|
||||
if (!OpenMeterFile(fileName, StrOpenFileReadOnlyMode)) return false;
|
||||
//add file name delimiter
|
||||
fileName += StringDelimiter;
|
||||
|
||||
if (!OpenMeterFile(fileName, StrOpenFileReadOnlyMode))
|
||||
return false;
|
||||
//TODO THW implement
|
||||
//read raw data from meter
|
||||
return true;
|
||||
@@ -582,17 +611,25 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
/// <remarks date="2021-May-18" author="Thomas Wiedebusch">
|
||||
/// - Removed retries to speed up erasure as it returns error code 0x040B.
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Apr-01" author="Thomas Wiedebusch">
|
||||
/// - Add a NULL at the end of the file name.
|
||||
/// </remarks>
|
||||
public Boolean EraseMeterFile(String fileName)
|
||||
{
|
||||
//add file name delimiter
|
||||
fileName += StringDelimiter;
|
||||
//check if file is unlocked for erase
|
||||
if (_fileName != fileName) return false;
|
||||
if (_fileName != fileName)
|
||||
return false;
|
||||
//check if logged on to meter and access level is as expected
|
||||
if (_currentGenesis == null || !_currentGenesis.IsLoggedOn) return false;
|
||||
if (_currentGenesis == null || !_currentGenesis.IsLoggedOn)
|
||||
return false;
|
||||
|
||||
//set extended timeout for file operations
|
||||
_currentGenesis.TransmitProtocol.SetResponseTimeout(ExtendedFileIoTimeoutMs);
|
||||
CommunicationConfig.RequestRetries = 0;
|
||||
//write "remove-command" to meter, wait for result and read back (read back returns false because of unreadable content)
|
||||
//write "remove-command" to meter, wait for result and read back
|
||||
//ATTENTION: read back returns FALSE because of unreadable content
|
||||
_currentGenesis.WriteRegister(Register.Configexchange.FileRemove, fileName, true, true);
|
||||
//set timeout back to default value
|
||||
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
|
||||
@@ -609,10 +646,16 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Apr-01" author="Thomas Wiedebusch">
|
||||
/// - Add a NULL at the end of the file name.
|
||||
/// </remarks>
|
||||
public Boolean UnlockEraseWriteMeterFile(String fileName)
|
||||
{
|
||||
//add file name delimiter
|
||||
fileName += StringDelimiter;
|
||||
//check if logged on to meter and access level is as expected
|
||||
if (_currentGenesis == null || !_currentGenesis.IsLoggedOn) return false;
|
||||
if (_currentGenesis == null || !_currentGenesis.IsLoggedOn)
|
||||
return false;
|
||||
//remind file name being unlocked
|
||||
_fileName = fileName;
|
||||
return true;
|
||||
@@ -746,7 +789,8 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
public Boolean OpenMeterFile(String fileName, String accessMode)
|
||||
{
|
||||
//check if logged on to meter and access level is as expected
|
||||
if (_currentGenesis == null || !_currentGenesis.IsLoggedOn) return false;
|
||||
if (_currentGenesis == null || !_currentGenesis.IsLoggedOn)
|
||||
return false;
|
||||
Boolean returnValue;
|
||||
var retryCtr = 0;
|
||||
|
||||
@@ -806,25 +850,23 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
/// <remarks date="2020-Jan-22" author="Thomas Wiedebusch">
|
||||
/// - Wait after writing for response before reading!
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Apr-01" author="Thomas Wiedebusch">
|
||||
/// - Ignore file close write return!
|
||||
/// </remarks>
|
||||
private Boolean CloseMeterFile()
|
||||
{
|
||||
//avoid file close if file pointer assignment failed
|
||||
if (_filePointer <= 0) return false;
|
||||
if (_filePointer <= 0)
|
||||
return false;
|
||||
|
||||
//set extended timeout for file operations
|
||||
_currentGenesis.TransmitProtocol.SetResponseTimeout(ExtendedFileIoTimeoutMs);
|
||||
CommunicationConfig.RequestRetries = 0;
|
||||
_currentGenesis.WriteRegister(Register.Configexchange.FileClose, _filePointer);
|
||||
|
||||
var returnValue = _currentGenesis.WriteRegister(Register.Configexchange.FileClose,
|
||||
_filePointer);
|
||||
|
||||
//the preceding routine needs to wait for response, else this is always true!
|
||||
if (returnValue)
|
||||
{
|
||||
//function call returns 0 if FClose successfully executed
|
||||
returnValue &= 0 == RegisterConverter.ConvertTo<Int32>(
|
||||
_currentGenesis.ReadRegister(Register.Configexchange.FileClose));
|
||||
}
|
||||
//function call returns 0 if FClose successfully executed
|
||||
var returnValue = 0 == RegisterConverter.ConvertTo<Int32>(
|
||||
_currentGenesis.ReadRegister(Register.Configexchange.FileClose));
|
||||
//set timeout back to default value
|
||||
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
|
||||
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications;
|
||||
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Const;
|
||||
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
|
||||
@@ -139,6 +140,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
// Text for caller to inform about actual process being carried out
|
||||
private String _actualOperation;
|
||||
private FwUpdateState _fwUpdateState;
|
||||
private FwUpdateState _fwBackupUpdateState;
|
||||
|
||||
// Process counter for caller to monitor actual progress
|
||||
private Int32 _processedBytesCtr;
|
||||
@@ -150,6 +152,10 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
//the package file describes all files needed for a specific release
|
||||
private String _packageDescriptionFile;
|
||||
|
||||
// manual stop of update process
|
||||
private Boolean _stopUpdateProcess;
|
||||
private Int32 _actualUpdateFileCtr;
|
||||
|
||||
/// <summary>
|
||||
/// Enable partial file writes of single parts with gaps in between
|
||||
/// </summary>
|
||||
@@ -624,7 +630,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
return _actualOperation;
|
||||
}
|
||||
|
||||
private Int32 _actualUpdateFileCtr;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the FW Update state
|
||||
@@ -677,8 +682,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
_genesisMeter?.ReLogin();
|
||||
}
|
||||
|
||||
private Boolean _stopUpdateProcess;
|
||||
|
||||
/// <summary>
|
||||
/// Stop the update process
|
||||
/// </summary>
|
||||
@@ -724,6 +727,9 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
/// <remarks date="2019-Jun-29" author="Thomas Wiedebusch">
|
||||
/// - Exit preparation added
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Mar-04" author="Thomas Wiedebusch">
|
||||
/// - Introduced lock for repeated execution of state machine
|
||||
/// </remarks>
|
||||
public Boolean UpdateMeterFw()
|
||||
{
|
||||
if (!UpdatePreparation())
|
||||
@@ -732,6 +738,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
}
|
||||
|
||||
_fwUpdateState = FwUpdateState.StartInitial;
|
||||
_fwBackupUpdateState = FwUpdateState.Idle;
|
||||
while (_fwUpdateState != FwUpdateState.Idle && !StopUpdateProcess)
|
||||
{
|
||||
FwUpdateStateMachine();
|
||||
@@ -897,6 +904,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
|
||||
//set state machine for automatic FW update
|
||||
_fwUpdateState = FwUpdateState.Idle;
|
||||
_fwBackupUpdateState = FwUpdateState.Idle;
|
||||
_actualOperation = "";
|
||||
|
||||
//update file information
|
||||
@@ -979,7 +987,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
var writeBackValue = _genesisMeter.ReadRegister(item.Key);
|
||||
_registersAfterUpdate.Add(item.Key, writeBackValue);
|
||||
_genesisMeter.WriteRegister(item.Key, item.Value);
|
||||
|
||||
}
|
||||
|
||||
//remove the update stop action
|
||||
@@ -1059,97 +1066,110 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
/// 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>
|
||||
/// <remarks date="2022-Mar-04" author="Thomas Wiedebusch">
|
||||
/// - Introduced lock for repeated execution of state machine
|
||||
/// </remarks>
|
||||
private void FwUpdateStateMachine()
|
||||
{
|
||||
switch (_fwUpdateState)
|
||||
if (_fwBackupUpdateState == _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;
|
||||
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// remind backup state to avoid repeated execution and side effects
|
||||
_fwBackupUpdateState = _fwUpdateState;
|
||||
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;
|
||||
|
||||
}
|
||||
}// lock repeated execution of identical state
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Logic.ProductionToProductMapper.Cordonel;
|
||||
|
||||
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
{
|
||||
/// <summary>
|
||||
/// File for metrology look up table
|
||||
/// </summary>
|
||||
public class MeterLutFile
|
||||
{
|
||||
/// <summary>
|
||||
/// File name of the file on PC
|
||||
/// </summary>
|
||||
public String LutPcFileName;
|
||||
|
||||
/// <summary>
|
||||
/// Ctor file applications
|
||||
/// </summary>
|
||||
/// <param name="lutPcFileName"></param>
|
||||
public MeterLutFile(String lutPcFileName)
|
||||
{
|
||||
LutPcFileName = lutPcFileName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// File ID for LUT file
|
||||
/// </summary>
|
||||
public String StrLutFileId;
|
||||
|
||||
/// <summary>
|
||||
/// Data length
|
||||
/// </summary>
|
||||
public UInt32 DataLength;
|
||||
|
||||
/// <summary>
|
||||
/// Version of LUT file
|
||||
/// </summary>
|
||||
public UInt16 Version;
|
||||
|
||||
/// <summary>
|
||||
/// Check of LUT file for verification and register write, read from file
|
||||
/// </summary>
|
||||
public UInt16 FileCrc;
|
||||
|
||||
/// <summary>
|
||||
/// Calculated during file processing for verification
|
||||
/// </summary>
|
||||
public UInt16 CalculatedCrc;
|
||||
|
||||
/// <summary>
|
||||
/// Validation of LUT file
|
||||
/// </summary>
|
||||
public Boolean IsValid;
|
||||
|
||||
/// <summary>
|
||||
/// Binary file content
|
||||
/// </summary>
|
||||
public List<Byte> BinData = new List<Byte>();
|
||||
|
||||
/// <summary>
|
||||
/// Meter size
|
||||
/// </summary>
|
||||
public MeterSize MeterSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,980 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Logic.ProductionToProductMapper.Cordonel;
|
||||
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.Registers;
|
||||
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore.Consts;
|
||||
using Xylem.Common.Utils.Crc16Ccitt;
|
||||
using Xylem.Common.Utils.ProcessExec;
|
||||
using Xylem.Common.Utils.ProcessExec.EventArguments;
|
||||
|
||||
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Meter lookup table for metrology update procedure
|
||||
/// </summary>
|
||||
public class MeterLutUpdate : IProcessState
|
||||
{
|
||||
#region Variables
|
||||
|
||||
/// <summary>
|
||||
/// Port scan result event for message dispatcher to caller
|
||||
/// </summary>
|
||||
public event EventHandler<ProcessExecEventArgs> OnProcessUpdate;
|
||||
|
||||
//error mask for unsuccessfully written LUT CRC
|
||||
private const UInt32 LutCrcErrorMask = 0x8000000;
|
||||
|
||||
//disk and name of lookup file
|
||||
private const String StrLutMeterFileName = "1\\mettbl";
|
||||
|
||||
//disk and name of lookup file
|
||||
private const String StrLutBackupMeterFileName = "1\\mettbl_b";
|
||||
|
||||
// identifier for Genesis lookup table
|
||||
private const String StrLutFileId = "GEN_LUT\0";
|
||||
private const Int32 IndexLutFileId = 0;
|
||||
private const Int32 LengthLutFileId = 8;
|
||||
|
||||
// data length, calculated from version field to the end of the data section
|
||||
private const Int32 IndexDataLength = 8;
|
||||
private const Int32 LengthDataLength = 4;
|
||||
|
||||
// CCITT-CRC-16 , calculated from version field to the end of the data section
|
||||
private const Int32 IndexCrc = 12;
|
||||
private const Int32 LengthCrc = 2;
|
||||
|
||||
// version
|
||||
private const Int32 IndexVersion = 14;
|
||||
private const Int32 LengthVersion = 2;
|
||||
|
||||
// meter size as enumeration
|
||||
private const Int32 IndexMeterSize = 16;
|
||||
|
||||
// header size of file
|
||||
private const Int32 LutFileHeaderLength = LengthLutFileId + LengthDataLength + LengthCrc + LengthVersion;
|
||||
|
||||
// data size of file
|
||||
private const Int32 LutFileDataLength = 730;
|
||||
|
||||
// LUT file entire file size
|
||||
private const Int32 SizeLutFile = LutFileHeaderLength + LutFileDataLength;
|
||||
|
||||
//trigger update retries
|
||||
private const Int32 RegisterWriteRetries = 0;
|
||||
|
||||
//update reties for files
|
||||
private const Int32 FileWriteRetries = 1;
|
||||
|
||||
//retry of entire update procedure
|
||||
private const Int32 UpdateProcedureRetries = 1;
|
||||
|
||||
//threshold to increase the file write timing
|
||||
private const Int32 IncreaseTimeoutUpdateProcedureRetries = 4;
|
||||
|
||||
private Int32 _updateProcedureRetryCtr;
|
||||
|
||||
private GenesisMeter _genesisMeter;
|
||||
private MeterFile _meterFile;
|
||||
|
||||
// Text for caller to inform about actual process being carried out
|
||||
private String _actualOperation;
|
||||
private LutUpdateState _lutUpdateState;
|
||||
private LutUpdateState _lutBackupUpdateState;
|
||||
|
||||
// Look up table file
|
||||
public MeterLutFile LutFile;
|
||||
|
||||
// list of names of installed meter files on drive 1
|
||||
private List<String> _meterFilesDrive1;
|
||||
|
||||
// number of operations for over all access
|
||||
private const Int32 NumberOfOperations = 11;
|
||||
private Int32 _operationCtr;
|
||||
|
||||
|
||||
// error message for display of error reason
|
||||
public String ErrorMessage;
|
||||
|
||||
// manual stop of update process
|
||||
private Boolean _stopUpdateProcess;
|
||||
|
||||
|
||||
/// <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(_overallOperation,
|
||||
// OverallProcessCtrPercent, _actualOperation, e.OverallProcessPercent));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Information
|
||||
|
||||
/// <summary>
|
||||
/// Process counter in percent
|
||||
/// </summary>
|
||||
/// <remarks date="2022-Mar-03" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public Double OverallProcessCtrPercent => 100.0 * _operationCtr / NumberOfOperations;
|
||||
|
||||
/// <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="2022-Feb-16" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public MeterLutUpdate(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
|
||||
/// </summary>
|
||||
/// <param name="genesisMeter"></param>
|
||||
/// <remarks date="2022-Feb-16" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public void AssignGenesis(GenesisMeter genesisMeter)
|
||||
{
|
||||
_genesisMeter = genesisMeter;
|
||||
_actualOperation = "";
|
||||
_lutUpdateState = LutUpdateState.Idle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_meterFile == null)
|
||||
return;
|
||||
_meterFile.OnProcessUpdate -= ProcessUpdate_Event;
|
||||
_meterFile = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ProcessInformation
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the LUT Update state
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks date="2022-Feb-25" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public String GetLutUpdateStateOperation()
|
||||
{
|
||||
// search text assigned to current state
|
||||
var text = LutUpdateStateInfo.GetTextFromState(_lutUpdateState);
|
||||
if (_lutUpdateState == LutUpdateState.Idle)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
text += _updateProcedureRetryCtr > 0 ? $"Retry {_updateProcedureRetryCtr}" : "";
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the update process
|
||||
/// </summary>
|
||||
public Boolean StopUpdateProcess
|
||||
{
|
||||
get => _stopUpdateProcess;
|
||||
set
|
||||
{
|
||||
_stopUpdateProcess = value;
|
||||
if (_meterFile != null)
|
||||
{
|
||||
_meterFile.StopProcess = value;
|
||||
}
|
||||
|
||||
_lutUpdateState = LutUpdateState.UpdateFailed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update meter lookup table
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks date="2022-Feb-16" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public Boolean UpdateMeterLut()
|
||||
{
|
||||
if (!UpdatePreparation())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_operationCtr = 0;
|
||||
_actualOperation = "";
|
||||
ErrorMessage = "";
|
||||
_lutUpdateState = LutUpdateState.StartInitial;
|
||||
_lutBackupUpdateState = LutUpdateState.Idle;
|
||||
while (_lutUpdateState != LutUpdateState.Idle && !StopUpdateProcess)
|
||||
{
|
||||
LutUpdateStateMachine();
|
||||
}
|
||||
|
||||
return ExitPreparation(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Upload lookup table file and compare with the loaded from file system
|
||||
/// </summary>
|
||||
/// <remarks date="2019-Jun-29" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public Boolean VerifyLutFile()
|
||||
{
|
||||
if (!UpdatePreparation())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ExitPreparation(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset lookup table update process and all lists and counters
|
||||
/// </summary>
|
||||
/// <remarks date="2022-Feb-16" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public void ResetAll()
|
||||
{
|
||||
LutFile = null;
|
||||
//remove the update stop action
|
||||
StopUpdateProcess = false;
|
||||
|
||||
//reset all counters
|
||||
_updateProcedureRetryCtr = 0;
|
||||
|
||||
//set state machine for automatic FW update
|
||||
_lutUpdateState = LutUpdateState.Idle;
|
||||
_lutBackupUpdateState = LutUpdateState.Idle;
|
||||
_actualOperation = "";
|
||||
_operationCtr = 0;
|
||||
|
||||
//logout/login/auto-login
|
||||
ReestablishMeterFileSystem();
|
||||
_genesisMeter?.Logout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Common update preparation and check
|
||||
/// </summary>
|
||||
/// <remarks date="2022-Feb-16" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public Boolean UpdatePreparation()
|
||||
{
|
||||
_actualOperation = "Update preparation";
|
||||
_lutUpdateState = LutUpdateState.Idle;
|
||||
|
||||
_meterFile = new MeterFile(_genesisMeter);
|
||||
if (_meterFile != null)
|
||||
_meterFile.OnProcessUpdate += ProcessUpdate_Event;
|
||||
_genesisMeter?.ReLogin();
|
||||
_genesisMeter?.SetProcessState(DisplayCodes.FwUpdateActive, false);
|
||||
|
||||
if (_genesisMeter == null || !_genesisMeter.IsLoggedOn)
|
||||
{
|
||||
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="2022-Feb-16" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public Boolean ExitPreparation(Boolean returnValue)
|
||||
{
|
||||
if (_meterFile != null)
|
||||
_meterFile.OnProcessUpdate -= ProcessUpdate_Event;
|
||||
_meterFile = null;
|
||||
ReestablishMeterFileSystem();
|
||||
|
||||
foreach (var item in _registersAfterUpdate)
|
||||
{
|
||||
_genesisMeter.WriteRegister(item.Key, item.Value);
|
||||
}
|
||||
|
||||
_genesisMeter?.Logout();
|
||||
if (!StopUpdateProcess)
|
||||
{
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
_actualOperation = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region StateMachine
|
||||
|
||||
/// <summary>
|
||||
/// State machine for metrology lookup table update process
|
||||
/// </summary>
|
||||
/// <remarks date="2022-Feb-16" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Mar-04" author="Thomas Wiedebusch">
|
||||
/// - Introduced lock for repeated execution of state machine
|
||||
/// </remarks>
|
||||
private void LutUpdateStateMachine()
|
||||
{
|
||||
if (_lutBackupUpdateState == _lutUpdateState)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// remind backup state to avoid repeated execution and side effects
|
||||
_lutBackupUpdateState = _lutUpdateState;
|
||||
switch (_lutUpdateState)
|
||||
{
|
||||
case LutUpdateState.Idle:
|
||||
break;
|
||||
|
||||
case LutUpdateState.StartInitial:
|
||||
_updateProcedureRetryCtr = 0;
|
||||
_lutUpdateState = LutUpdateState.EraseLutFile;
|
||||
break;
|
||||
|
||||
case LutUpdateState.EraseLutFile:
|
||||
_lutUpdateState = EraseLutFiles() ? LutUpdateState.DownloadLutFile : LutUpdateState.StepFailed;
|
||||
break;
|
||||
|
||||
case LutUpdateState.DownloadLutFile:
|
||||
_lutUpdateState = DownloadLutFiles() ? LutUpdateState.UploadLutFile : LutUpdateState.StepFailed;
|
||||
break;
|
||||
|
||||
case LutUpdateState.UploadLutFile:
|
||||
_lutUpdateState = UploadAndCompareLutFiles() ? LutUpdateState.FinalizeLutDownload : LutUpdateState.StepFailed;
|
||||
break;
|
||||
|
||||
case LutUpdateState.FinalizeLutDownload:
|
||||
_lutUpdateState =
|
||||
FinalizeLutDownload() ? LutUpdateState.Idle : LutUpdateState.StepFailed;
|
||||
break;
|
||||
|
||||
case LutUpdateState.CompareLutFile:
|
||||
break;
|
||||
|
||||
case LutUpdateState.StepFailed:
|
||||
_lutUpdateState = _updateProcedureRetryCtr++ < UpdateProcedureRetries
|
||||
? LutUpdateState.RepeatLutUpdate
|
||||
: LutUpdateState.UpdateFailed;
|
||||
break;
|
||||
|
||||
case LutUpdateState.RepeatLutUpdate:
|
||||
_lutUpdateState = LutUpdateState.DownloadLutFile;
|
||||
if (_updateProcedureRetryCtr >= IncreaseTimeoutUpdateProcedureRetries)
|
||||
{
|
||||
_meterFile?.SetExtremeFileWriteTimeout();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LutUpdateState.UpdateFailed:
|
||||
StopUpdateProcess = true;
|
||||
_lutUpdateState = LutUpdateState.Idle;
|
||||
break;
|
||||
|
||||
case LutUpdateState.LutUpdateStateListDelimiter:
|
||||
_lutUpdateState = LutUpdateState.Idle;
|
||||
break;
|
||||
|
||||
default:
|
||||
_lutUpdateState = LutUpdateState.Idle;
|
||||
break;
|
||||
}
|
||||
}// lock for identical state
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region LutFile
|
||||
/// <summary>
|
||||
/// Check the content of the metrology lookup table file
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks date="2022-Feb-25" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
private Boolean ValidateLutFile()
|
||||
{
|
||||
// check if LUT file contains data
|
||||
if (LutFile?.BinData == null || LutFile.BinData.Count < SizeLutFile)
|
||||
{
|
||||
ErrorMessage = "File size mismatch!";
|
||||
return false;
|
||||
}
|
||||
|
||||
LutFile.IsValid = false;
|
||||
// extract the identifier and check, if this is a LUT file
|
||||
LutFile.StrLutFileId =
|
||||
Encoding.UTF8.GetString(LutFile.BinData.ToArray(), IndexLutFileId, IndexDataLength);
|
||||
if (LutFile.StrLutFileId != StrLutFileId)
|
||||
return false;
|
||||
|
||||
LutFile.MeterSize = (MeterSize)LutFile.BinData[IndexMeterSize];
|
||||
LutFile.FileCrc = (UInt16)(LutFile.BinData[IndexCrc] + (LutFile.BinData[IndexCrc + 1] << 8));
|
||||
LutFile.Version = (UInt16)(LutFile.BinData[IndexVersion] + (LutFile.BinData[IndexVersion + 1] << 8));
|
||||
LutFile.DataLength = (UInt32)(LutFile.BinData[IndexDataLength]
|
||||
+ (LutFile.BinData[IndexDataLength + 1] << 8)
|
||||
+ (LutFile.BinData[IndexDataLength + 2] << 16)
|
||||
+ (LutFile.BinData[IndexDataLength + 3] << 24));
|
||||
// calculate CRC and compare this with the file CRC
|
||||
var entireFile = new List<Byte>();
|
||||
entireFile.AddRange(LutFile.BinData);
|
||||
var payload = new List<Byte>();
|
||||
payload.AddRange(entireFile.GetRange(IndexVersion, LutFileDataLength + LengthVersion));
|
||||
var calculatedCrc = Crc16Ccitt.CalculateMsb1021(payload.ToArray());
|
||||
if (calculatedCrc != LutFile.FileCrc)
|
||||
{
|
||||
ErrorMessage = "CRC mismatch!";
|
||||
return false;
|
||||
}
|
||||
|
||||
LutFile.CalculatedCrc = calculatedCrc;
|
||||
LutFile.IsValid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download the CRC, the meter size and store calibration as preparation for the lookup table
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks date="2022-Feb-16" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
private Boolean FinalizeLutDownload()
|
||||
{
|
||||
var returnValue = true;
|
||||
|
||||
var retryCtr = 0;
|
||||
if (LutFile == null || _genesisMeter == null || _meterFile == null)
|
||||
return false;
|
||||
// set extended timeout for trigger upgrade response delay
|
||||
_genesisMeter.TransmitProtocol.SetResponseTimeout(5000);
|
||||
do
|
||||
{
|
||||
// for repeated entry
|
||||
if (!returnValue)
|
||||
{
|
||||
ReestablishMeterFileSystem();
|
||||
}
|
||||
|
||||
_actualOperation = "Write Lut CRC to meter";
|
||||
_operationCtr++;
|
||||
returnValue = _genesisMeter.WriteRegister<UInt32>(Register.Genesisflow.LookupFileCrc, LutFile.FileCrc);
|
||||
if (!returnValue)
|
||||
{
|
||||
const String txt = "Meter LUT CRC could not be set!";
|
||||
ErrorMessage = txt;
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(txt));
|
||||
_operationCtr--;
|
||||
continue;
|
||||
}
|
||||
|
||||
_operationCtr++;
|
||||
_actualOperation = "Write meter size";
|
||||
returnValue =
|
||||
_genesisMeter.WriteRegister(Register.Genesisflow.MeterSize, new[] { (Byte)LutFile.MeterSize });
|
||||
if (!returnValue)
|
||||
{
|
||||
const String txt = "Meter size could not be set!";
|
||||
ErrorMessage = txt;
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(txt));
|
||||
_operationCtr--;
|
||||
continue;
|
||||
}
|
||||
|
||||
_operationCtr++;
|
||||
_actualOperation = "Store calibration to meter";
|
||||
returnValue = _genesisMeter.WriteRegister(Register.Genesisflow.StoreCalibration, 1);
|
||||
if (!returnValue)
|
||||
{
|
||||
const String txt = "Meter store calibration failed!";
|
||||
ErrorMessage = txt;
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(txt));
|
||||
_operationCtr--;
|
||||
continue;
|
||||
}
|
||||
|
||||
_operationCtr++;
|
||||
_actualOperation = "Read back LUT CRC from meter";
|
||||
var lutCrc = RegisterConverter.ConvertTo<UInt32>(
|
||||
_genesisMeter.ReadRegister(Register.Genesisflow.LookupFileCrc));
|
||||
|
||||
if ((lutCrc & LutCrcErrorMask) != 0)
|
||||
{
|
||||
const String txt = "Meter LUT CRC could not be set!";
|
||||
ErrorMessage = txt;
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(txt));
|
||||
_operationCtr--;
|
||||
returnValue = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
OnProcessUpdate?.Invoke(this,
|
||||
new ProcessExecEventArgs($"Read back and validated LUT CRC 0x{lutCrc:X4}"));
|
||||
|
||||
_operationCtr++;
|
||||
_actualOperation = "Read back meter size from meter";
|
||||
var meterSize = (MeterSize)RegisterConverter.ConvertTo<Byte>(
|
||||
_genesisMeter.ReadRegister(Register.Genesisflow.MeterSize));
|
||||
|
||||
if (meterSize != LutFile.MeterSize)
|
||||
{
|
||||
const String txt = "Meter size could not be set!";
|
||||
ErrorMessage = txt;
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(txt));
|
||||
_operationCtr--;
|
||||
returnValue = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
ErrorMessage = "";
|
||||
OnProcessUpdate?.Invoke(this,
|
||||
new ProcessExecEventArgs("Read back and validated meter size " +
|
||||
$"{MeterSizeConverter.ConvertMeterSizeEnumToSizeName(meterSize)}"));
|
||||
|
||||
} while (!returnValue && retryCtr++ < RegisterWriteRetries && !StopUpdateProcess);
|
||||
|
||||
// set timeout back to default value
|
||||
_genesisMeter.TransmitProtocol.SetDefaultResponseTimeout();
|
||||
|
||||
return returnValue && !StopUpdateProcess;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read meter files of drive 1
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks date="2022-Mar-04" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
private Boolean ListMeterFilesDrive1()
|
||||
{
|
||||
// logout and re-login to clear all ongoing processes and clear the Genesis buffers
|
||||
ReestablishMeterFileSystem();
|
||||
|
||||
if (_meterFilesDrive1 == null)
|
||||
{
|
||||
_meterFilesDrive1 = new List<String>();
|
||||
}
|
||||
_meterFilesDrive1.Clear();
|
||||
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("List meter files drive 1"));
|
||||
if (!_meterFile.ReadMeterFileCatalog(out var meterFilesDrive1, MeterFile.StrMeterDrive1,
|
||||
MeterFile.WildcardAll.ToString()))
|
||||
return false;
|
||||
|
||||
_meterFilesDrive1.AddRange(meterFilesDrive1);
|
||||
|
||||
// inform overlay system of found files
|
||||
foreach (var meterFile in _meterFilesDrive1)
|
||||
{
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(meterFile));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Erase file and check if done!
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks date="2022-Mar-04" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
private Boolean EraseFile(String fileDriveName)
|
||||
{
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs($"Erasing {fileDriveName}"));
|
||||
var returnValue = _meterFile.UnlockEraseWriteMeterFile(fileDriveName);
|
||||
// erase returns always TRUE, even if the erasure failed! Check with catalogue
|
||||
_meterFile.EraseMeterFile(fileDriveName);
|
||||
|
||||
//if (ListMeterFilesDrive1())
|
||||
//{
|
||||
// if (_meterFilesDrive1.Contains(fileDriveName))
|
||||
// {
|
||||
// returnValue = false;
|
||||
// var txt = $"Failed to erase {fileDriveName}!";
|
||||
// ErrorMessage = txt;
|
||||
// OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(txt));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// ErrorMessage = "";
|
||||
// OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs($"Successfully erased {fileDriveName}"));
|
||||
// }
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// returnValue = false;
|
||||
// const String txt = "Meter files read error!";
|
||||
// ErrorMessage = txt;
|
||||
// OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(txt));
|
||||
//}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Erase old LUT files from meter
|
||||
/// </summary>
|
||||
/// <remarks date="2022-Feb-16" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public Boolean EraseLutFiles()
|
||||
{
|
||||
//counters for actual process information
|
||||
var retryCtr = 0;
|
||||
if (LutFile == null)
|
||||
return false;
|
||||
|
||||
//set extended timeout for trigger upgrade response delay
|
||||
_genesisMeter.TransmitProtocol.SetResponseTimeout(10000);
|
||||
var returnValue = true;
|
||||
|
||||
//erase loop and retry if not successful
|
||||
do
|
||||
{
|
||||
var filesToErase = new[] { StrLutMeterFileName, StrLutBackupMeterFileName };
|
||||
|
||||
// read all files which are already installed
|
||||
_actualOperation = "Catalogue installed meter files drive 1";
|
||||
_operationCtr++;
|
||||
|
||||
// read files on drive 1 to verify the LUT installation
|
||||
if (ListMeterFilesDrive1())
|
||||
{
|
||||
foreach (var fileToErase in filesToErase)
|
||||
{
|
||||
if (!_meterFilesDrive1.Contains(fileToErase))
|
||||
continue;
|
||||
|
||||
_operationCtr++;
|
||||
_actualOperation = $"Erase {fileToErase}";
|
||||
if (EraseFile(fileToErase))
|
||||
{
|
||||
ErrorMessage = "";
|
||||
continue;
|
||||
}
|
||||
|
||||
ErrorMessage = $"LUT file {fileToErase} not erased!";
|
||||
returnValue = false;
|
||||
_operationCtr--;
|
||||
}
|
||||
}
|
||||
|
||||
if (!returnValue)
|
||||
{
|
||||
ReestablishMeterFileSystem();
|
||||
}
|
||||
} while (!returnValue && retryCtr++ < FileWriteRetries && !StopUpdateProcess);
|
||||
|
||||
//set timeout back to default value
|
||||
_genesisMeter.TransmitProtocol.SetDefaultResponseTimeout();
|
||||
|
||||
return !StopUpdateProcess && returnValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download the metrology lookup table files and read the catalog of drive 1
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks date="2022-Feb-16" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
private Boolean DownloadLutFiles()
|
||||
{
|
||||
//counters for actual process information
|
||||
var retryCtr = 0;
|
||||
if (LutFile == null)
|
||||
return false;
|
||||
|
||||
//set extended timeout for trigger upgrade response delay
|
||||
_genesisMeter.TransmitProtocol.SetResponseTimeout(1000);
|
||||
var returnValue = true;
|
||||
|
||||
//write LUT files and retry if not successful
|
||||
do
|
||||
{
|
||||
var filesToDownload = new[] { StrLutMeterFileName, StrLutBackupMeterFileName };
|
||||
|
||||
foreach (var fileToDownload in filesToDownload)
|
||||
{
|
||||
_operationCtr++;
|
||||
_actualOperation = $"Write {fileToDownload}";
|
||||
|
||||
// logout and re-login to clear all ongoing processes and clear the Genesis buffers
|
||||
ReestablishMeterFileSystem();
|
||||
|
||||
_meterFile.UnlockEraseWriteMeterFile(fileToDownload);
|
||||
var txt = $"{fileToDownload} successfully written";
|
||||
if (_meterFile.WriteMeterFile(fileToDownload, LutFile.BinData.ToArray()))
|
||||
{
|
||||
ErrorMessage = "";
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(txt));
|
||||
continue;
|
||||
}
|
||||
|
||||
txt = $"Writing of {fileToDownload} failed!";
|
||||
ErrorMessage = txt;
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(txt));
|
||||
returnValue = false;
|
||||
_operationCtr--;
|
||||
}
|
||||
|
||||
// read all files on drive 1
|
||||
_operationCtr++;
|
||||
|
||||
if (ListMeterFilesDrive1())
|
||||
{
|
||||
foreach (var checkInstalledFile in filesToDownload)
|
||||
{
|
||||
if (_meterFilesDrive1.Contains(checkInstalledFile))
|
||||
{
|
||||
ErrorMessage = "";
|
||||
var txt = $"{checkInstalledFile} found in meter";
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(txt));
|
||||
continue;
|
||||
}
|
||||
|
||||
ErrorMessage = $"LUT file {StrLutMeterFileName} not installed!\n";
|
||||
returnValue = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
returnValue = false;
|
||||
}
|
||||
|
||||
if (!returnValue)
|
||||
{
|
||||
ReestablishMeterFileSystem();
|
||||
}
|
||||
} while (!returnValue && retryCtr++ < FileWriteRetries && !StopUpdateProcess);
|
||||
|
||||
//set timeout back to default value
|
||||
_genesisMeter.TransmitProtocol.SetDefaultResponseTimeout();
|
||||
|
||||
return !StopUpdateProcess && returnValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Upload the metrology lookup table files and read the catalog of drive 1
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks date="2022-Mar-04" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Apr-01" author="Thomas Wiedebusch">
|
||||
/// - Take the different size of original and receive buffer, which is an integer division by 4, into account.
|
||||
/// </remarks>
|
||||
private Boolean UploadAndCompareLutFiles()
|
||||
{
|
||||
//counters for actual process information
|
||||
var retryCtr = 0;
|
||||
if (LutFile == null)
|
||||
return false;
|
||||
|
||||
//set extended timeout for trigger upgrade response delay
|
||||
_genesisMeter.TransmitProtocol.SetResponseTimeout(1000);
|
||||
var returnValue = true;
|
||||
|
||||
//write LUT files and retry if not successful
|
||||
do
|
||||
{
|
||||
var filesToUpload = new[] { StrLutMeterFileName, StrLutBackupMeterFileName };
|
||||
|
||||
foreach (var fileToUpload in filesToUpload)
|
||||
{
|
||||
_operationCtr++;
|
||||
_actualOperation = $"Read {fileToUpload}";
|
||||
|
||||
// logout and re-login to clear all ongoing processes and clear the Genesis buffers
|
||||
ReestablishMeterFileSystem();
|
||||
|
||||
if (_meterFile.ReadMeterFile(fileToUpload, out var binData, SizeLutFile))
|
||||
{
|
||||
// compare the entire buffer which also includes the header of the file, remind the original
|
||||
// buffer may be smaller due to the chunk size of the communication (always dividable by 4),
|
||||
// meaning the receive buffer is always and integer of this chunk size!
|
||||
if (LutFile.BinData.Where((t, ctr) => t != binData[ctr]).Any())
|
||||
{
|
||||
ErrorMessage = $"Meter LUT file is unequal to downloaded {fileToUpload}!";
|
||||
returnValue = false;
|
||||
_operationCtr--;
|
||||
}
|
||||
|
||||
if (returnValue)
|
||||
{
|
||||
OnProcessUpdate?.Invoke(this,
|
||||
new ProcessExecEventArgs($"LUT file {fileToUpload} of meter successfully compared!"));
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
ErrorMessage += $"Reading of {fileToUpload} failed!";
|
||||
returnValue = false;
|
||||
_operationCtr--;
|
||||
}
|
||||
}
|
||||
|
||||
if (!returnValue)
|
||||
{
|
||||
ReestablishMeterFileSystem();
|
||||
}
|
||||
} while (!returnValue && retryCtr++ < FileWriteRetries && !StopUpdateProcess);
|
||||
|
||||
//set timeout back to default value
|
||||
_genesisMeter.TransmitProtocol.SetDefaultResponseTimeout();
|
||||
|
||||
return !StopUpdateProcess && returnValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load file from memory
|
||||
/// </summary>
|
||||
/// <param name="filePathName">to binary files</param>
|
||||
/// <returns></returns>
|
||||
/// <remarks date="2022-Feb-15" author="Thomas Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public Boolean LoadLutFile(String filePathName)
|
||||
{
|
||||
if (!File.Exists(filePathName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LutFile = null;
|
||||
LutFile = new MeterLutFile(filePathName)
|
||||
{
|
||||
BinData = new List<Byte>(File.ReadAllBytes(filePathName))
|
||||
};
|
||||
|
||||
// validate the file
|
||||
return ValidateLutFile();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers
|
||||
public static readonly String StartHit = "GENESISFLOW_StartHit";
|
||||
|
||||
public static readonly String NumFirePulses = "GENESISFLOW_NumFirePulses";
|
||||
|
||||
public static readonly String LookupFileCrc = "GENESISFLOW_LookupFileCrc";
|
||||
|
||||
};
|
||||
|
||||
@@ -121,6 +121,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers
|
||||
Configexchange.FileClose,
|
||||
Configexchange.FileWrite,
|
||||
Configexchange.FileRead,
|
||||
Configexchange.Catalogue,
|
||||
Configexchange.GetFilePointerOffset,
|
||||
Configexchange.SetFilePointerOffset,
|
||||
Configexchange.FileRemove,
|
||||
@@ -135,6 +136,9 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers
|
||||
Genesisflow.TriggerIdle,
|
||||
Genesisflow.LedMode,
|
||||
Genesisflow.SampleRate,
|
||||
Genesisflow.MeterSize,
|
||||
Genesisflow.LookupFileCrc,
|
||||
Genesisflow.StoreCalibration,
|
||||
|
||||
Sensusradio.WakeupInterval,
|
||||
Sensusradio.FrequencyIndicator
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
46bd00889699f5a20a4981a22f62b66368323855
|
||||
7ac73abb7afd9e6ddc0520a1568581c7ed0afd36
|
||||
|
||||
Reference in New Issue
Block a user