|
|
|
@@ -1,7 +1,6 @@
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
|
|
|
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
|
|
|
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig;
|
|
|
|
@@ -33,11 +32,14 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
//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";
|
|
|
|
|
//private const String StrNewFileReadWriteMode = "wb";
|
|
|
|
|
private const String StrNewFileReadWriteMode = "w";
|
|
|
|
|
//Open for reading in binary mode. If the file does not exist, FOpen returns NULL
|
|
|
|
|
private const String StrOpenFileReadOnlyMode = "rb";
|
|
|
|
|
//private const String StrOpenFileReadOnlyMode = "rb";
|
|
|
|
|
private const String StrOpenFileReadOnlyMode = "r";
|
|
|
|
|
//Open for both reading and writing in binary mode. If the file does not exist, FOpen returns NULL
|
|
|
|
|
private const String StrOpenFileReadWriteMode = "rb+";
|
|
|
|
|
//private const String StrOpenFileReadWriteMode = "rb+";
|
|
|
|
|
private const String StrOpenFileReadWriteMode = "r+";
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// FSeek for start of file
|
|
|
|
@@ -57,11 +59,38 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const String StrMeterConfigFile = "0\\config";
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The test file is for SENSUSRADIO as placeholder for update over the air.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const String StrMeterTstFile = "1\\tstfile";
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Engineering log index file of meter.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const String StrMeterEngLogIndexFile = "1\\logindex";
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// EMEA log files which can be removed on NA installation
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly String[] EmeaLogs = { "1\\fdrdata", "1\\logdata", "1\\evtdata" };
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// NA log files which can be removed on EMEA installation
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly String[] NaLogs =
|
|
|
|
|
{ "1\\NAalarm", "1\\NAconfig", "1\\NApress", "1\\NAstate", "1\\NAtempC", "1\\NAwater" };
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Essential files which have to be kept
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly String[] FilesToKeep =
|
|
|
|
|
{ "0\\password", StrMeterConfigFile, "1\\logindex", "1\\mettbl", "1\\mettbl_b" };
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Files which always can be removed to clean the file system up "tidy file"
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly String[] FilesToRemove = { "1\\upg", "1\\upgrade", StrMeterTstFile };
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Drive zero of meter.
|
|
|
|
|
/// </summary>
|
|
|
|
@@ -80,7 +109,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Wildcard for search all.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const Char WildcardAll = '*';
|
|
|
|
|
public const String WildcardAll = "*";
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Processed bytes counter
|
|
|
|
@@ -199,10 +228,17 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
/// <remarks date="2022-Apr-01" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Add a NULL at the end of the file name.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-06" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Adapted to new ReadFilePointerOffset.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-12" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Combined FOpen to one file open function,
|
|
|
|
|
/// - Using <see cref="BuildRpcInt32"></see> for multiple write access to register instead of 3 writes.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public Boolean WriteMeterFile(String fileName, Byte[] dataBytes, Int32 fileOffset = 0)
|
|
|
|
|
{
|
|
|
|
|
var returnValue = false;
|
|
|
|
|
|
|
|
|
|
//set to true to enter the cyclic write routines, will be overwritten with file pointer adjustment
|
|
|
|
|
var returnValue = true;
|
|
|
|
|
//set processing counter
|
|
|
|
|
ProcessedBytesCtr = 0;
|
|
|
|
|
//inform the caller of changed statue
|
|
|
|
@@ -210,106 +246,129 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
_overallBytesCtr = dataBytes.Length;
|
|
|
|
|
|
|
|
|
|
//add file name delimiter
|
|
|
|
|
fileName += StringDelimiter;
|
|
|
|
|
//check if file is unlocked for write
|
|
|
|
|
if (!fileName.Contains(StringDelimiter))
|
|
|
|
|
fileName += StringDelimiter;
|
|
|
|
|
|
|
|
|
|
//check if file is unlocked for write access
|
|
|
|
|
if (_fileName != fileName)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if ((fileOffset == 0 && OpenMeterFile(fileName, StrNewFileReadWriteMode))
|
|
|
|
|
|| (fileOffset > 0 && OpenMeterFile(fileName, StrOpenFileReadWriteMode)))
|
|
|
|
|
var accessMode = fileOffset == 0 ? StrNewFileReadWriteMode : StrOpenFileReadWriteMode;
|
|
|
|
|
|
|
|
|
|
//file open as new or additional write with immediately return if file cannot be opened
|
|
|
|
|
if (!OpenMeterFile(fileName, accessMode))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//adjust file pointer if offset is required
|
|
|
|
|
if (fileOffset > 0)
|
|
|
|
|
{
|
|
|
|
|
returnValue = true;
|
|
|
|
|
returnValue = SetFilePointerOffset(fileOffset);
|
|
|
|
|
|
|
|
|
|
//adjust file pointer if offset is required
|
|
|
|
|
if (fileOffset > 0)
|
|
|
|
|
if (returnValue)
|
|
|
|
|
{
|
|
|
|
|
returnValue = SetFilePointerOffset(fileOffset);
|
|
|
|
|
|
|
|
|
|
//read file pointer offset
|
|
|
|
|
returnValue &= fileOffset == ReadFilePointerOffset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (returnValue && !_stopProcess)
|
|
|
|
|
{
|
|
|
|
|
//set extended timeout for file operations to extended timeout
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetResponseTimeout(_fileWriteTimeOutMs);
|
|
|
|
|
//set register write retries to 0 to avoid doubling of records, if wakeup message from
|
|
|
|
|
//serial communication routine received, one retry will kicked off in the request protocol
|
|
|
|
|
CommunicationConfig.RequestRetries = 0;
|
|
|
|
|
|
|
|
|
|
//write size to 1
|
|
|
|
|
returnValue = _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, 1, false);
|
|
|
|
|
//write counts to data size
|
|
|
|
|
returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, dataBytes.Length, false);
|
|
|
|
|
//write file pointer
|
|
|
|
|
returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, _filePointer);
|
|
|
|
|
|
|
|
|
|
//calculate records to send based on MaxRawDataSize
|
|
|
|
|
var chunkCounts = dataBytes.Length / MaxRawDataSize;
|
|
|
|
|
if (0 != dataBytes.Length % MaxRawDataSize)
|
|
|
|
|
{
|
|
|
|
|
chunkCounts += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var dataByteList = new List<Byte>();
|
|
|
|
|
dataByteList.AddRange(dataBytes);
|
|
|
|
|
var dataChunk = new List<Byte>();
|
|
|
|
|
var ctr = 0;
|
|
|
|
|
|
|
|
|
|
//the preceding routine needs to wait for response, else this is always true!
|
|
|
|
|
while (returnValue && ctr < chunkCounts && !_stopProcess)
|
|
|
|
|
{
|
|
|
|
|
var index = ctr * MaxRawDataSize;
|
|
|
|
|
var size = index + MaxRawDataSize > dataByteList.Count ? dataByteList.Count - index : MaxRawDataSize;
|
|
|
|
|
|
|
|
|
|
//prepare data to write
|
|
|
|
|
dataChunk.Clear();
|
|
|
|
|
dataChunk.AddRange(dataByteList.GetRange(index, size));
|
|
|
|
|
|
|
|
|
|
//break on failed write of this part
|
|
|
|
|
returnValue = _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, dataChunk.ToArray());
|
|
|
|
|
|
|
|
|
|
//set processed bytes for write loop
|
|
|
|
|
ProcessedBytesCtr += size;
|
|
|
|
|
|
|
|
|
|
//inform the caller of changed statue
|
|
|
|
|
var processCtrPercent = 100.0 * ProcessedBytesCtr / (_overallBytesCtr > 0 ? _overallBytesCtr : 1);
|
|
|
|
|
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", processCtrPercent));
|
|
|
|
|
|
|
|
|
|
ctr++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//read back bytes written
|
|
|
|
|
var writtenBytes = 0;
|
|
|
|
|
var returnedFileOffset = 0;
|
|
|
|
|
|
|
|
|
|
//the preceding routine needs to wait for response, else this is always true!
|
|
|
|
|
if (returnValue)
|
|
|
|
|
{
|
|
|
|
|
writtenBytes = RegisterConverter.ConvertTo<Int32>(
|
|
|
|
|
_currentGenesis.ReadRegister(Register.Configexchange.FileWrite));
|
|
|
|
|
returnedFileOffset = ReadFilePointerOffset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//set timeout back to default value
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
|
|
|
|
|
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
|
|
|
|
|
|
|
|
|
|
if (writtenBytes != ProcessedBytesCtr || returnedFileOffset != fileOffset + writtenBytes)
|
|
|
|
|
if (!ReadFilePointerOffset(out var readBackFilePointerOffset) ||
|
|
|
|
|
fileOffset != readBackFilePointerOffset)
|
|
|
|
|
returnValue = false;
|
|
|
|
|
}
|
|
|
|
|
}//file open
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
returnValue &= CloseMeterFile();
|
|
|
|
|
if (returnValue && !_stopProcess)
|
|
|
|
|
{
|
|
|
|
|
//set extended timeout for file operations to extended timeout
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetResponseTimeout(_fileWriteTimeOutMs);
|
|
|
|
|
//set register write retries to 0 to avoid doubling of records, if wakeup message from
|
|
|
|
|
//serial communication routine received, one retry will kicked off in the request protocol
|
|
|
|
|
CommunicationConfig.RequestRetries = 0;
|
|
|
|
|
|
|
|
|
|
var rpc = BuildRpcInt32(1, dataBytes.Length, _filePointer);
|
|
|
|
|
returnValue = _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, rpc);
|
|
|
|
|
|
|
|
|
|
//calculate records to send based on MaxRawDataSize
|
|
|
|
|
var chunkCounts = dataBytes.Length / MaxRawDataSize;
|
|
|
|
|
if (0 != dataBytes.Length % MaxRawDataSize)
|
|
|
|
|
{
|
|
|
|
|
chunkCounts += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var dataByteList = new List<Byte>();
|
|
|
|
|
dataByteList.AddRange(dataBytes);
|
|
|
|
|
var dataChunk = new List<Byte>();
|
|
|
|
|
var ctr = 0;
|
|
|
|
|
|
|
|
|
|
//the preceding routine needs to wait for response, else this is always true!
|
|
|
|
|
while (returnValue && ctr < chunkCounts && !_stopProcess)
|
|
|
|
|
{
|
|
|
|
|
var index = ctr * MaxRawDataSize;
|
|
|
|
|
var size = index + MaxRawDataSize > dataByteList.Count ? dataByteList.Count - index : MaxRawDataSize;
|
|
|
|
|
|
|
|
|
|
//prepare data to write
|
|
|
|
|
dataChunk.Clear();
|
|
|
|
|
dataChunk.AddRange(dataByteList.GetRange(index, size));
|
|
|
|
|
|
|
|
|
|
//break on failed write of this part
|
|
|
|
|
returnValue = _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, dataChunk.ToArray());
|
|
|
|
|
|
|
|
|
|
//set processed bytes for write loop
|
|
|
|
|
ProcessedBytesCtr += size;
|
|
|
|
|
|
|
|
|
|
//inform the caller of changed statue
|
|
|
|
|
var processCtrPercent = 100.0 * ProcessedBytesCtr / (_overallBytesCtr > 0 ? _overallBytesCtr : 1);
|
|
|
|
|
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", processCtrPercent));
|
|
|
|
|
|
|
|
|
|
ctr++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//read back bytes written
|
|
|
|
|
var writtenBytes = 0;
|
|
|
|
|
var returnedFileOffset = 0;
|
|
|
|
|
|
|
|
|
|
//the preceding routine needs to wait for response, else this is always true!
|
|
|
|
|
if (returnValue)
|
|
|
|
|
{
|
|
|
|
|
writtenBytes = RegisterConverter.ConvertTo<Int32>(
|
|
|
|
|
_currentGenesis.ReadRegister(Register.Configexchange.FileWrite));
|
|
|
|
|
ReadFilePointerOffset(out returnedFileOffset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//set timeout back to default value
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
|
|
|
|
|
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
|
|
|
|
|
|
|
|
|
|
if (writtenBytes != ProcessedBytesCtr || returnedFileOffset != fileOffset + writtenBytes)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//processed bytes not longer needed
|
|
|
|
|
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", 0));
|
|
|
|
|
ProcessedBytesCtr = 0;
|
|
|
|
|
_overallBytesCtr = 0;
|
|
|
|
|
|
|
|
|
|
returnValue &= CloseMeterFile();
|
|
|
|
|
return returnValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Build RPC (remote procedure call) for 3 Int32 parameters.
|
|
|
|
|
/// This routine concatenates these 3 Int32 to one byte array to have a multiple read or
|
|
|
|
|
/// write operation to the register instead of having 3 simple register write accesses.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="first"></param>
|
|
|
|
|
/// <param name="second"></param>
|
|
|
|
|
/// <param name="last"></param>
|
|
|
|
|
/// <returns>file content as byte array</returns>
|
|
|
|
|
/// <remarks date="2023-Jan-12" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Initial
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public static Byte[] BuildRpcInt32(Int32 first, Int32 second, Int32 last)
|
|
|
|
|
{
|
|
|
|
|
var rpc = new List<Byte>();
|
|
|
|
|
rpc.AddRange(BitConverter.GetBytes(first));
|
|
|
|
|
rpc.AddRange(BitConverter.GetBytes(second));
|
|
|
|
|
rpc.AddRange(BitConverter.GetBytes(last));
|
|
|
|
|
return rpc.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Read file from the meter
|
|
|
|
|
/// </summary>
|
|
|
|
@@ -353,6 +412,16 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
/// <remarks date="2023-Jan-03" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Speed up FOpen as read is not so critical.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-06" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Avoid reading of file size from FSeek is 0.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-07" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Removed extended timeout.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-12" author="Thomas Wiedebusch">
|
|
|
|
|
/// - FClose added on failed GetFileSizeInternal.
|
|
|
|
|
/// - Using <see cref="BuildRpcInt32"></see> for multiple write access to register instead of 3 writes.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public Boolean ReadMeterFile(String fileName, out List<Byte> readBackData)
|
|
|
|
|
{
|
|
|
|
|
//set processing counter
|
|
|
|
@@ -360,63 +429,53 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", 0));
|
|
|
|
|
|
|
|
|
|
//add file name delimiter
|
|
|
|
|
fileName += StringDelimiter;
|
|
|
|
|
if (!fileName.Contains(StringDelimiter))
|
|
|
|
|
fileName += StringDelimiter;
|
|
|
|
|
|
|
|
|
|
readBackData = new List<Byte>();
|
|
|
|
|
|
|
|
|
|
// Get the current file size
|
|
|
|
|
var returnValue = GetFileSize(fileName, out var readBackCount);
|
|
|
|
|
//use high speed access
|
|
|
|
|
if (!OpenMeterFile(fileName, StrOpenFileReadOnlyMode))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (returnValue)
|
|
|
|
|
//get the current file size, the size of 0 is a valid file size, but reading is not necessary
|
|
|
|
|
if (!GetFileSizeInternal(fileName, out var fileSize))
|
|
|
|
|
{
|
|
|
|
|
// here the file is already opened and has to be closed to avoid error 0x040E "TOO_MANY_OPEN"
|
|
|
|
|
CloseMeterFile();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//use high speed access
|
|
|
|
|
returnValue &= OpenMeterFile(fileName, StrOpenFileReadOnlyMode, true);
|
|
|
|
|
//set register write retries to 0 to avoid doubling of records
|
|
|
|
|
CommunicationConfig.RequestRetries = 0;
|
|
|
|
|
|
|
|
|
|
if (returnValue)
|
|
|
|
|
if (fileSize > 0)
|
|
|
|
|
{
|
|
|
|
|
var rpc = BuildRpcInt32(1, fileSize, _filePointer);
|
|
|
|
|
_currentGenesis.WriteRegister(Register.Configexchange.FileRead, rpc);
|
|
|
|
|
|
|
|
|
|
//the preceding routine needs to wait for response, else this is always true!
|
|
|
|
|
while (fileSize > 0 && !_stopProcess)
|
|
|
|
|
{
|
|
|
|
|
//set register write retries to 0 to avoid doubling of records
|
|
|
|
|
CommunicationConfig.RequestRetries = 0;
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetResponseTimeout(ExtendedFileIoTimeoutMs);
|
|
|
|
|
var remainingData = RegisterConverter.ConvertTo<Int32>(
|
|
|
|
|
_currentGenesis.ReadRegister(Register.Configexchange.FileRead));
|
|
|
|
|
|
|
|
|
|
//write size to 1
|
|
|
|
|
_currentGenesis.WriteRegister(Register.Configexchange.FileRead, 1, false);
|
|
|
|
|
//write counts to expected length
|
|
|
|
|
_currentGenesis.WriteRegister(Register.Configexchange.FileRead, readBackCount, false);
|
|
|
|
|
//write file pointer
|
|
|
|
|
_currentGenesis.WriteRegister(Register.Configexchange.FileRead, _filePointer);
|
|
|
|
|
fileSize = remainingData > 0 ? fileSize - remainingData : 0;
|
|
|
|
|
|
|
|
|
|
//the preceding routine needs to wait for response, else this is always true!
|
|
|
|
|
while (readBackCount > 0 && !_stopProcess)
|
|
|
|
|
while (remainingData > 0)
|
|
|
|
|
{
|
|
|
|
|
var remainingData = RegisterConverter.ConvertTo<Int32>(
|
|
|
|
|
_currentGenesis.ReadRegister(Register.Configexchange.FileRead));
|
|
|
|
|
var size = remainingData > MaxRawDataSize ? MaxRawDataSize : remainingData;
|
|
|
|
|
readBackData.AddRange(_currentGenesis.ReadRegister(Register.Configexchange.FileRead, size));
|
|
|
|
|
remainingData -= size;
|
|
|
|
|
|
|
|
|
|
readBackCount = remainingData > 0 ? readBackCount - remainingData : 0;
|
|
|
|
|
//set processed bytes for read loop
|
|
|
|
|
ProcessedBytesCtr += size;
|
|
|
|
|
|
|
|
|
|
while (remainingData > 0)
|
|
|
|
|
{
|
|
|
|
|
var size = remainingData > MaxRawDataSize ? MaxRawDataSize : remainingData;
|
|
|
|
|
readBackData.AddRange(_currentGenesis.ReadRegister(Register.Configexchange.FileRead, size));
|
|
|
|
|
remainingData -= size;
|
|
|
|
|
|
|
|
|
|
//set processed bytes for read loop
|
|
|
|
|
ProcessedBytesCtr += size;
|
|
|
|
|
|
|
|
|
|
//inform the caller of changed statue
|
|
|
|
|
var processCtrPercent = 100.0 * ProcessedBytesCtr / (_overallBytesCtr > 0 ? _overallBytesCtr : 1);
|
|
|
|
|
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", processCtrPercent));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
//inform the caller of changed statue
|
|
|
|
|
var processCtrPercent = 100.0 * ProcessedBytesCtr / (_overallBytesCtr > 0 ? _overallBytesCtr : 1);
|
|
|
|
|
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", processCtrPercent));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//set timeout back to default value
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
|
|
|
|
|
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
|
|
|
|
|
|
|
|
|
|
}//file open succeeded
|
|
|
|
|
|
|
|
|
|
returnValue &= CloseMeterFile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}// file size unequal to 0
|
|
|
|
|
|
|
|
|
@@ -424,8 +483,11 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
ProcessedBytesCtr = 0;
|
|
|
|
|
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", 0));
|
|
|
|
|
|
|
|
|
|
//set timeout back to default value
|
|
|
|
|
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
|
|
|
|
|
|
|
|
|
|
//read raw data from meter
|
|
|
|
|
return returnValue;
|
|
|
|
|
return CloseMeterFile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@@ -441,10 +503,13 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
/// <remarks date="2023-Jan-04" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Default wildcard.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public Boolean ReadMeterFileCatalog(out List<String> files, String drive, Char wildcard = WildcardAll)
|
|
|
|
|
public Boolean ReadMeterFileCatalog(out List<String> files, String drive, String wildcard = WildcardAll)
|
|
|
|
|
{
|
|
|
|
|
files = new List<String>();
|
|
|
|
|
|
|
|
|
|
//add file name delimiter
|
|
|
|
|
if (!wildcard.Contains(StringDelimiter))
|
|
|
|
|
wildcard += StringDelimiter;
|
|
|
|
|
//write the initial key
|
|
|
|
|
var key = 0;
|
|
|
|
|
_stopProcess = false;
|
|
|
|
@@ -486,6 +551,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
files.Add(filePathName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
files.Sort();
|
|
|
|
|
return files.Count > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -501,104 +567,134 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
/// <remarks date="2022-Apr-01" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Add a NULL at the end of the file name.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-06" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Adapted to new ReadFilePointerOffset.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-12" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Using <see cref="BuildRpcInt32"></see> for multiple write access to register instead of 3 writes.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public Boolean CreateEmptyMeterFile(String fileName, Int32 fileSize = 0)
|
|
|
|
|
{
|
|
|
|
|
var returnValue = false;
|
|
|
|
|
//check if logged on to meter and access level is as expected
|
|
|
|
|
if (_currentGenesis == null || !_currentGenesis.IsLoggedOn)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//add file name delimiter
|
|
|
|
|
fileName += StringDelimiter;
|
|
|
|
|
if (!fileName.Contains(StringDelimiter))
|
|
|
|
|
fileName += StringDelimiter;
|
|
|
|
|
|
|
|
|
|
//check if file is unlocked for write
|
|
|
|
|
if (_fileName != fileName)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//exit immediately if file cannot be opened
|
|
|
|
|
if (!OpenMeterFile(fileName, StrNewFileReadWriteMode))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (OpenMeterFile(fileName, StrNewFileReadWriteMode))
|
|
|
|
|
var returnValue = true;
|
|
|
|
|
|
|
|
|
|
//adjust file pointer if offset is required
|
|
|
|
|
if (fileSize > 0)
|
|
|
|
|
{
|
|
|
|
|
returnValue = true;
|
|
|
|
|
// adjust file pointer to the end of file
|
|
|
|
|
returnValue = SetFilePointerOffset(fileSize - 1);
|
|
|
|
|
//read file pointer offset
|
|
|
|
|
returnValue &= ReadFilePointerOffset(out var readBackFileSize);
|
|
|
|
|
returnValue &= (fileSize - 1) == readBackFileSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//adjust file pointer if offset is required
|
|
|
|
|
if (fileSize > 0)
|
|
|
|
|
if (returnValue && !_stopProcess)
|
|
|
|
|
{
|
|
|
|
|
//set extended timeout for file operations to extended timeout
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetResponseTimeout(_fileWriteTimeOutMs);
|
|
|
|
|
//set register write retries to 0 to avoid doubling of records, if wakeup message from
|
|
|
|
|
//serial communication routine received, one retry will kicked off in the request protocol
|
|
|
|
|
CommunicationConfig.RequestRetries = 0;
|
|
|
|
|
|
|
|
|
|
var rpc = BuildRpcInt32(1, 1, _filePointer);
|
|
|
|
|
returnValue = _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, rpc);
|
|
|
|
|
|
|
|
|
|
//write one byte at the end of file
|
|
|
|
|
returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, 0xFF);
|
|
|
|
|
|
|
|
|
|
//read back bytes written
|
|
|
|
|
var returnedFileOffset = 0;
|
|
|
|
|
|
|
|
|
|
//the preceding routine needs to wait for response, else this is always true!
|
|
|
|
|
if (returnValue)
|
|
|
|
|
{
|
|
|
|
|
// adjust file pointer to the end of file
|
|
|
|
|
returnValue = SetFilePointerOffset(fileSize - 1);
|
|
|
|
|
//read file pointer offset
|
|
|
|
|
returnValue &= (fileSize - 1) == ReadFilePointerOffset();
|
|
|
|
|
RegisterConverter.ConvertTo<Int32>(
|
|
|
|
|
_currentGenesis.ReadRegister(Register.Configexchange.FileWrite));
|
|
|
|
|
ReadFilePointerOffset(out returnedFileOffset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (returnValue && !_stopProcess)
|
|
|
|
|
{
|
|
|
|
|
//set extended timeout for file operations to extended timeout
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetResponseTimeout(_fileWriteTimeOutMs);
|
|
|
|
|
//set register write retries to 0 to avoid doubling of records, if wakeup message from
|
|
|
|
|
//serial communication routine received, one retry will kicked off in the request protocol
|
|
|
|
|
CommunicationConfig.RequestRetries = 0;
|
|
|
|
|
//set timeout back to default value
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
|
|
|
|
|
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
|
|
|
|
|
|
|
|
|
|
//write size to 1
|
|
|
|
|
returnValue = _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, 1, false);
|
|
|
|
|
//write block of data to 1 as only one byte needed to be at the end of file
|
|
|
|
|
returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, 1, false);
|
|
|
|
|
//write file pointer
|
|
|
|
|
returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, _filePointer);
|
|
|
|
|
|
|
|
|
|
//write one byte at the end of file
|
|
|
|
|
returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, 0xFF);
|
|
|
|
|
|
|
|
|
|
//read back bytes written
|
|
|
|
|
var returnedFileOffset = 0;
|
|
|
|
|
|
|
|
|
|
//the preceding routine needs to wait for response, else this is always true!
|
|
|
|
|
if (returnValue)
|
|
|
|
|
{
|
|
|
|
|
RegisterConverter.ConvertTo<Int32>(
|
|
|
|
|
_currentGenesis.ReadRegister(Register.Configexchange.FileWrite));
|
|
|
|
|
returnedFileOffset = ReadFilePointerOffset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//set timeout back to default value
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
|
|
|
|
|
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
|
|
|
|
|
|
|
|
|
|
if (returnedFileOffset != fileSize)
|
|
|
|
|
returnValue = false;
|
|
|
|
|
}
|
|
|
|
|
}//file open
|
|
|
|
|
if (returnedFileOffset != fileSize)
|
|
|
|
|
returnValue = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
returnValue &= CloseMeterFile();
|
|
|
|
|
return returnValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Uses FSeek to get the file size.
|
|
|
|
|
/// Read the file size.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="fileName">file name as string</param>
|
|
|
|
|
/// <param name="fileSize">file size of the given file</param>
|
|
|
|
|
/// <returns>true: file found </returns>
|
|
|
|
|
/// <returns>true: file size could be read </returns>
|
|
|
|
|
/// <remarks date="2023-Jan-02" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Initial
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-06" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Modified returns.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public Boolean GetFileSize(String fileName, out Int32 fileSize)
|
|
|
|
|
{
|
|
|
|
|
fileSize = 0;
|
|
|
|
|
//check if logged on to meter and access level is as expected
|
|
|
|
|
if (_currentGenesis == null || !_currentGenesis.IsLoggedOn)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//add file name delimiter
|
|
|
|
|
fileName += StringDelimiter;
|
|
|
|
|
if (!fileName.Contains(StringDelimiter))
|
|
|
|
|
fileName += StringDelimiter;
|
|
|
|
|
|
|
|
|
|
var returnValue = OpenMeterFile(fileName, StrOpenFileReadOnlyMode, true);
|
|
|
|
|
|
|
|
|
|
if (returnValue)
|
|
|
|
|
{
|
|
|
|
|
returnValue &= SetFilePointerOffset(fSeekPara: FSeekEndOfFile);
|
|
|
|
|
|
|
|
|
|
if (returnValue)
|
|
|
|
|
{
|
|
|
|
|
fileSize = ReadFilePointerOffset();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//return immediately, if file cannot be opened
|
|
|
|
|
if (!OpenMeterFile(fileName, StrOpenFileReadOnlyMode))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
var returnValue = SetFilePointerOffset(fSeekPara: FSeekEndOfFile);
|
|
|
|
|
returnValue &= ReadFilePointerOffset(out fileSize);
|
|
|
|
|
returnValue &= CloseMeterFile();
|
|
|
|
|
return returnValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Uses FSeek to get the file size.
|
|
|
|
|
/// For this function the file has to be opened in advance!
|
|
|
|
|
/// For correct access, the file pointer has to be set after detection of the
|
|
|
|
|
/// file size back to the start of the file!!!!!
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="fileName">file name as string</param>
|
|
|
|
|
/// <param name="fileSize">file size of the given file</param>
|
|
|
|
|
/// <returns>true: file found </returns>
|
|
|
|
|
/// <remarks date="2023-Jan-06" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Initial
|
|
|
|
|
/// </remarks>
|
|
|
|
|
private Boolean GetFileSizeInternal(String fileName, out Int32 fileSize)
|
|
|
|
|
{
|
|
|
|
|
fileSize = 0;
|
|
|
|
|
var returnValue = SetFilePointerOffset(fSeekPara: FSeekEndOfFile);
|
|
|
|
|
returnValue &= ReadFilePointerOffset(out fileSize);
|
|
|
|
|
//set the file pointer back to the start of file!!!!!
|
|
|
|
|
returnValue &= SetFilePointerOffset();
|
|
|
|
|
return returnValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Meter file verification against given data
|
|
|
|
|
/// Takes into account, that the read file is always integer dividable by 4 due to the chunk size
|
|
|
|
@@ -613,12 +709,20 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
/// <remarks date="2022-Apr-21" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Changed return value to false, if file could not be read.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-07" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Compare byte by byte until length of given buffer, as the returned buffer is always filled
|
|
|
|
|
/// up to the chunk size of 4.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public Boolean VerifyMeterFile(String fileName, Byte[] dataBytes)
|
|
|
|
|
{
|
|
|
|
|
//check if logged on to meter and access level is as expected
|
|
|
|
|
if (_currentGenesis == null || !_currentGenesis.IsLoggedOn)
|
|
|
|
|
return false;
|
|
|
|
|
var returnValue = true;
|
|
|
|
|
|
|
|
|
|
//add file name delimiter
|
|
|
|
|
fileName += StringDelimiter;
|
|
|
|
|
if (!fileName.Contains(StringDelimiter))
|
|
|
|
|
fileName += StringDelimiter;
|
|
|
|
|
|
|
|
|
|
if (!ReadMeterFile(fileName, out var binData) || binData == null || binData.Count == 0)
|
|
|
|
|
return false;
|
|
|
|
@@ -653,25 +757,29 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
/// <remarks date="2022-Apr-01" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Add a NULL at the end of the file name.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-07" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Removed extended timeout.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public Boolean EraseMeterFile(String fileName)
|
|
|
|
|
{
|
|
|
|
|
//add file name delimiter
|
|
|
|
|
fileName += StringDelimiter;
|
|
|
|
|
//check if file is unlocked for erase
|
|
|
|
|
if (_fileName != fileName)
|
|
|
|
|
return false;
|
|
|
|
|
//check if logged on to meter and access level is as expected
|
|
|
|
|
if (_currentGenesis == null || !_currentGenesis.IsLoggedOn)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//set extended timeout for file operations
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetResponseTimeout(ExtendedFileIoTimeoutMs);
|
|
|
|
|
//add file name delimiter
|
|
|
|
|
if (!fileName.Contains(StringDelimiter))
|
|
|
|
|
fileName += StringDelimiter;
|
|
|
|
|
|
|
|
|
|
//check if file is unlocked for erase
|
|
|
|
|
if (_fileName != fileName)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//request retries on single write register access, needs to be zero to hold the sequence
|
|
|
|
|
CommunicationConfig.RequestRetries = 0;
|
|
|
|
|
//write "remove-command" to meter, wait for result and read back
|
|
|
|
|
//write "remove-command" to meter, return is always false!
|
|
|
|
|
//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();
|
|
|
|
|
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
@@ -691,17 +799,17 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
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 (!fileName.Contains(StringDelimiter))
|
|
|
|
|
fileName += StringDelimiter;
|
|
|
|
|
|
|
|
|
|
//remind file name being unlocked
|
|
|
|
|
_fileName = fileName;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// FSeek implementation of genesis meter. FOpen has to be executed in advance to get
|
|
|
|
|
/// FSeek implementation of genesis meter.
|
|
|
|
|
/// ATTENTION: FOpen has to be executed in advance to get
|
|
|
|
|
/// the file pointer.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="filePointerOffset"></param>
|
|
|
|
@@ -723,6 +831,12 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
/// <remarks date="2023-Jan-02" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Extended FSeek for file end positioning.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-07" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Removed extended timeout.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-12" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Using <see cref="BuildRpcInt32"></see> for multiple write access to register instead of 3 writes.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
private Boolean SetFilePointerOffset(Int32 filePointerOffset = 0, Int32 fSeekPara = FSeekStartOfFile)
|
|
|
|
|
{
|
|
|
|
|
Boolean returnValue;
|
|
|
|
@@ -734,31 +848,17 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if (retryCtr > 0)
|
|
|
|
|
{
|
|
|
|
|
//read DUMMY to overcome FW malfunction
|
|
|
|
|
//_currentGenesis.ReadRegister(Register.Genesisflow.LedMode);
|
|
|
|
|
_currentGenesis.ReadRegister(Register.System.MonotonicSeconds);
|
|
|
|
|
Thread.Sleep(1000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//set extended timeout for file operations
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetResponseTimeout(ExtendedFileIoTimeoutMs);
|
|
|
|
|
//request retries on single write register access, needs to be zero to hold the sequence
|
|
|
|
|
CommunicationConfig.RequestRetries = 0;
|
|
|
|
|
|
|
|
|
|
returnValue = _currentGenesis.WriteRegister(Register.Configexchange.SetFilePointerOffset,
|
|
|
|
|
_filePointer, false);
|
|
|
|
|
returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.SetFilePointerOffset,
|
|
|
|
|
filePointerOffset, false);
|
|
|
|
|
returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.SetFilePointerOffset,
|
|
|
|
|
fSeekPara, false);
|
|
|
|
|
var rpc = BuildRpcInt32(_filePointer, filePointerOffset, fSeekPara);
|
|
|
|
|
returnValue = _currentGenesis.WriteRegister(Register.Configexchange.SetFilePointerOffset, rpc);
|
|
|
|
|
|
|
|
|
|
//function call returns 0 if FSeek successfully set
|
|
|
|
|
returnValue &= 0 == RegisterConverter.ConvertTo<UInt32>(_currentGenesis.ReadRegister(
|
|
|
|
|
Register.Configexchange.SetFilePointerOffset));
|
|
|
|
|
|
|
|
|
|
//set timeout back to default value
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
|
|
|
|
|
//set back to default value
|
|
|
|
|
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
|
|
|
|
|
} while (!returnValue && retryCtr++ < CommandWriteRetries);
|
|
|
|
|
|
|
|
|
@@ -782,38 +882,32 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
/// - Retries changed for and back,
|
|
|
|
|
/// - Timeout changed for and back.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
private Int32 ReadFilePointerOffset()
|
|
|
|
|
/// <remarks date="2023-Jan-06" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Modified for return.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-07" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Removed extended timeout.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
private Boolean ReadFilePointerOffset(out Int32 filePointerOffset)
|
|
|
|
|
{
|
|
|
|
|
Boolean returnValue;
|
|
|
|
|
var retryCtr = 0;
|
|
|
|
|
Int32 filePointerOffset;
|
|
|
|
|
//request retries on single write register access, needs to be zero to hold the sequence
|
|
|
|
|
CommunicationConfig.RequestRetries = 0;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if (retryCtr > 0)
|
|
|
|
|
{
|
|
|
|
|
//read DUMMY to overcome FW malfunction
|
|
|
|
|
//_currentGenesis.ReadRegister(Register.Genesisflow.LedMode);
|
|
|
|
|
_currentGenesis.ReadRegister(Register.System.MonotonicSeconds);
|
|
|
|
|
Thread.Sleep(1000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//set extended timeout for file operations
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetResponseTimeout(ExtendedFileIoTimeoutMs);
|
|
|
|
|
CommunicationConfig.RequestRetries = 0;
|
|
|
|
|
|
|
|
|
|
//read file pointer offset
|
|
|
|
|
returnValue = _currentGenesis.WriteRegister(Register.Configexchange.GetFilePointerOffset,
|
|
|
|
|
_currentGenesis.WriteRegister(Register.Configexchange.GetFilePointerOffset,
|
|
|
|
|
_filePointer, false);
|
|
|
|
|
filePointerOffset = RegisterConverter.ConvertTo<Int32>(_currentGenesis.ReadRegister(
|
|
|
|
|
Register.Configexchange.GetFilePointerOffset));
|
|
|
|
|
|
|
|
|
|
//set timeout back to default value
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
|
|
|
|
|
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
|
|
|
|
|
} while (!returnValue && retryCtr++ < CommandWriteRetries);
|
|
|
|
|
} while (filePointerOffset <= 0 && retryCtr++ < CommandWriteRetries);
|
|
|
|
|
|
|
|
|
|
return filePointerOffset;
|
|
|
|
|
//set timeout back to default value
|
|
|
|
|
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
|
|
|
|
|
|
|
|
|
|
return filePointerOffset > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@@ -822,7 +916,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="fileName"></param>
|
|
|
|
|
/// <param name="accessMode">read, write eg,</param>
|
|
|
|
|
/// <param name="highSpeed">disable high timeouts</param>
|
|
|
|
|
/// <returns>true if filePointer higher than zero</returns>
|
|
|
|
|
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Initial
|
|
|
|
@@ -838,48 +931,67 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
/// <remarks date="2023-Jan-03" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Extended timings removed if high speed is active.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
private Boolean OpenMeterFile(String fileName, String accessMode, Boolean highSpeed = false)
|
|
|
|
|
/// <remarks date="2023-Jan-07" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Removed retry dummy read.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-07" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Removed extended timeout.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-12" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Make sure NULL terminator is at the end of the file name and access mode.
|
|
|
|
|
/// - Combined filename and access mode to use one write access to the register
|
|
|
|
|
/// instead of two.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
private 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;
|
|
|
|
|
Boolean returnValue;
|
|
|
|
|
|
|
|
|
|
//add file name delimiter
|
|
|
|
|
if (!fileName.Contains(StringDelimiter))
|
|
|
|
|
fileName += StringDelimiter;
|
|
|
|
|
//add access mode delimiter
|
|
|
|
|
if (!accessMode.Contains(StringDelimiter))
|
|
|
|
|
accessMode += StringDelimiter;
|
|
|
|
|
|
|
|
|
|
var rpc = new List<Char>();
|
|
|
|
|
rpc.AddRange(fileName.ToCharArray());
|
|
|
|
|
// fill it padded to the chunk size
|
|
|
|
|
while (rpc.Count % RegisterDefinition.ChunkSize != 0)
|
|
|
|
|
{
|
|
|
|
|
rpc.Add(StringDelimiter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var retryCtr = 0;
|
|
|
|
|
Boolean retValue;
|
|
|
|
|
|
|
|
|
|
//request retries on single write register access, needs to be zero to hold the sequence
|
|
|
|
|
CommunicationConfig.RequestRetries = 0;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if (retryCtr > 0)
|
|
|
|
|
//write file name to meter with meter open command, don't wait for response
|
|
|
|
|
var rpcString = new String(rpc.ToArray());
|
|
|
|
|
rpcString += accessMode;
|
|
|
|
|
|
|
|
|
|
// wait for response!
|
|
|
|
|
retValue = _currentGenesis.WriteRegister(Register.Configexchange.FileOpen, rpcString, false);
|
|
|
|
|
|
|
|
|
|
if (retValue)
|
|
|
|
|
{
|
|
|
|
|
//read DUMMY to overcome FW malfunction
|
|
|
|
|
//_currentGenesis.ReadRegister(Register.Genesisflow.LedMode);
|
|
|
|
|
_currentGenesis.ReadRegister(Register.System.MonotonicSeconds);
|
|
|
|
|
Thread.Sleep(1000);
|
|
|
|
|
//get the file pointer
|
|
|
|
|
_filePointer = RegisterConverter.ConvertTo<Int32>(_currentGenesis.ReadRegister(
|
|
|
|
|
Register.Configexchange.FileOpen));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//set extended timeout for file operations
|
|
|
|
|
if (highSpeed)
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetResponseTimeout(ExtendedFileIoTimeoutMs);
|
|
|
|
|
|
|
|
|
|
CommunicationConfig.RequestRetries = 0;
|
|
|
|
|
} while ((_filePointer <= 0 || retValue == false) && retryCtr++ < CommandWriteRetries);
|
|
|
|
|
|
|
|
|
|
//write file name to meter with meter open command, don't wait for response
|
|
|
|
|
_currentGenesis.WriteRegister(Register.Configexchange.FileOpen, fileName, false);
|
|
|
|
|
//write access flag
|
|
|
|
|
_currentGenesis.WriteRegister(Register.Configexchange.FileOpen, accessMode, false);
|
|
|
|
|
//set retries back to default value
|
|
|
|
|
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
|
|
|
|
|
|
|
|
|
|
//get the file pointer
|
|
|
|
|
_filePointer = RegisterConverter.ConvertTo<Int32>(_currentGenesis.ReadRegister(
|
|
|
|
|
Register.Configexchange.FileOpen));
|
|
|
|
|
|
|
|
|
|
//set timeout back to default value
|
|
|
|
|
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
|
|
|
|
|
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
|
|
|
|
|
|
|
|
|
|
returnValue = 0 < _filePointer;
|
|
|
|
|
|
|
|
|
|
} while (!returnValue && retryCtr++ < CommandWriteRetries);
|
|
|
|
|
|
|
|
|
|
return returnValue;
|
|
|
|
|
return _filePointer > 0 && retValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@@ -910,26 +1022,34 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
|
|
|
|
/// <remarks date="2023-Jan-04" author="Thomas Wiedebusch">
|
|
|
|
|
/// - Removed extended timeout.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <remarks date="2023-Jan-12" author="Thomas Wiedebusch">
|
|
|
|
|
/// - FClose retries.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
private Boolean CloseMeterFile()
|
|
|
|
|
{
|
|
|
|
|
//avoid file close if file pointer assignment failed
|
|
|
|
|
if (_filePointer <= 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//set extended timeout for file operations
|
|
|
|
|
//_currentGenesis.TransmitProtocol.SetResponseTimeout(ExtendedFileIoTimeoutMs);
|
|
|
|
|
//request retries on single write register access, needs to be zero to hold the sequence
|
|
|
|
|
CommunicationConfig.RequestRetries = 0;
|
|
|
|
|
_currentGenesis.WriteRegister(Register.Configexchange.FileClose, _filePointer);
|
|
|
|
|
|
|
|
|
|
//function call returns 0 if FClose successfully executed
|
|
|
|
|
var returnValue = 0 == RegisterConverter.ConvertTo<Int32>(
|
|
|
|
|
_currentGenesis.ReadRegister(Register.Configexchange.FileClose));
|
|
|
|
|
var retryCtr = 0;
|
|
|
|
|
Boolean retValue;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
retValue = _currentGenesis.WriteRegister(Register.Configexchange.FileClose, _filePointer);
|
|
|
|
|
|
|
|
|
|
//function call returns 0 if FClose successfully executed
|
|
|
|
|
retValue &= 0 == RegisterConverter.ConvertTo<Int32>(
|
|
|
|
|
_currentGenesis.ReadRegister(Register.Configexchange.FileClose));
|
|
|
|
|
}while ( retValue == false && retryCtr++ < CommandWriteRetries);
|
|
|
|
|
|
|
|
|
|
//set timeout back to default value
|
|
|
|
|
//_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
|
|
|
|
|
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
|
|
|
|
|
|
|
|
|
|
return returnValue;
|
|
|
|
|
return retValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|