using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig;
using Xylem.Common.Utils.ProcessExec;
using Xylem.Common.Utils.ProcessExec.EventArguments;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
{
///
/// Genesis meter file operations
///
public class MeterFile : IProcessState
{
///
/// Port scan result event for message dispatcher to caller
///
public event EventHandler OnProcessUpdate;
//the raw data size is limited by the meter input buffer size, removed protocol length
private const Int32 MaxRawDataSize = 56;
private const Int32 ExtremeFileWriteTimeoutMs = 30000;
private const Int32 ExtendedFileIoTimeoutMs = 5000;
private const Int32 CommandWriteRetries = 2;
//timeout value loaded dynamically starting with extended timeout, on retry to extreme timeout
private Int32 _fileWriteTimeOutMs = ExtendedFileIoTimeoutMs;
private readonly IGenesisMeter _currentGenesis;
private Int32 _filePointer;
//private const String StrNewFileReadWriteMode = "wb";
//private const String StrOpenFileReadOnlyMode = "rb";
//private const String StrOpenFileReadWriteMode = "rb+";
private const String StrNewFileReadWriteMode = "w";
private const String StrOpenFileReadOnlyMode = "r";
private const String StrOpenFileReadWriteMode = "r+";
///
/// FSeek for start of file
///
private const Int32 FSeekStartOfFile = 0;
///
/// FSeek for end of file
///
private const Int32 FSeekEndOfFile = 2;
private String _fileName;
private Int32 _overallBytesCtr;
///
/// Configuration file of meter.
///
public const String StrMeterConfigFile = "0\\config";
///
/// The test file is for SENSUSRADIO as placeholder for update over the air.
///
public const String StrMeterTstFile = "1\\tstfile";
///
/// Engineering log index file of meter.
///
public const String StrMeterEngLogIndexFile = "1\\logindex";
///
/// EMEA log files which can be removed on NA installation
///
public static readonly String[] EmeaLogs = { "1\\fdrdata", "1\\logdata", "1\\evtdata" };
///
/// NA log files which can be removed on EMEA installation
///
public static readonly String[] NaLogs =
{ "1\\NAalarm", "1\\NAconfig", "1\\NApress", "1\\NAstate", "1\\NAtempC", "1\\NAwater" };
///
/// Essential files which have to be kept
///
public static readonly String[] FilesToKeep =
{ "0\\password", StrMeterConfigFile, "1\\logindex", "1\\mettbl", "1\\mettbl_b", "1\\powcorr" };
///
/// Files which always can be removed to clean the file system up "tidy file"
///
public static readonly String[] FilesToRemove = { "1\\upg", "1\\upgrade", StrMeterTstFile };
///
/// Drive zero of meter.
///
public const String StrMeterDrive0 = "0\\";
///
/// Drive one of meter.
///
public const String StrMeterDrive1 = "1\\";
///
/// String delimiter of files from Genesis.
///
public const Char StringDelimiter = '\0';
///
/// Processed bytes counter
///
public Int32 ProcessedBytesCtr;
private Boolean _stopProcess;
///
/// Increase timeout to extreme value
///
///
/// - Timeout decreased to speed up stop operation.
///
public void SetExtremeFileWriteTimeout()
{
_fileWriteTimeOutMs = ExtremeFileWriteTimeoutMs;
}
///
/// Set timeout back to default timeout
///
///
/// - Timeout decreased to speed up stop operation.
///
public void SetDefaultFileWriteTimeout()
{
_fileWriteTimeOutMs = ExtendedFileIoTimeoutMs;
}
///
/// Stop process
///
///
/// - Timeout decreased to speed up stop operation.
///
///
/// - Check transmit protocol for null.
///
public Boolean StopProcess
{
get => _stopProcess;
set
{
_currentGenesis?.TransmitProtocol?.SetDefaultResponseTimeout();
_stopProcess = value;
}
}
///
/// Ctor for genesis meter file operations
///
///
///
/// - Initial
///
///
/// - Stop process added
///
public MeterFile(IGenesisMeter genesisMeter)
{
_currentGenesis = genesisMeter;
_fileName = "";
_stopProcess = false;
}
///
/// Process counter in percent
///
///
/// - Initial
///
public Double ProcessCtrPercent => 100.0 * ProcessedBytesCtr / (_overallBytesCtr > 0 ? _overallBytesCtr : 1);
///
/// Writing a file to the meter
///
/// file name as string
/// data to store
/// for partial file write procedures this is the offset to the file pointer
/// true, if file can be find and stored and, if required, read back and verified
///
/// - Initial
///
///
/// - Early return on failed communication
///
///
/// - Added overall bytes counter for single file processing information
///
///
/// - Stop process added
///
///
/// - CHeck if file exists
///
///
/// - Extended response timeout
///
///
/// - Register read/write retry 0 implemented.
///
///
/// - Stop FWrite if FWrite failed,
/// - FClose if FWrite failed,
/// - avoid FTell if FWrite failed
///
///
/// - Retries changed for and back on different position,
/// - Timeout changed for and back on different position,
/// - Dummy read removed.
///
///
/// - Event on changed process counter.
///
///
/// - Dynamic timeout load from extended to extreme timeout on retries.
///
///
/// - Add a NULL at the end of the file name.
///
///
/// - Adapted to new ReadFilePointerOffset.
///
///
/// - Combined FOpen to one file open function,
/// - Using for multiple write access to register instead of 3 writes.
///
///
/// - Check transmit protocol for null.
///
public Boolean WriteMeterFile(String fileName, Byte[] dataBytes, Int32 fileOffset = 0)
{
//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
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", 0));
_overallBytesCtr = dataBytes.Length;
//add file name delimiter
if (!fileName.Contains(StringDelimiter))
fileName += StringDelimiter;
//check if file is unlocked for write access
if (_fileName != fileName)
return false;
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 = SetFilePointerOffset(fileOffset);
if (returnValue)
{
//read file pointer offset
if (!ReadFilePointerOffset(out var readBackFilePointerOffset) ||
fileOffset != readBackFilePointerOffset)
returnValue = false;
}
}
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();
dataByteList.AddRange(dataBytes);
var dataChunk = new List();
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.ByteArrayToValue(
_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 no longer needed
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", 0));
ProcessedBytesCtr = 0;
_overallBytesCtr = 0;
returnValue &= CloseMeterFile();
return returnValue;
}
///
/// 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.
///
///
///
///
/// file content as byte array
///
/// - Initial
///
private static Byte[] BuildRpcInt32(Int32 first, Int32 second, Int32 last)
{
var rpc = new List();
rpc.AddRange(BitConverter.GetBytes(first));
rpc.AddRange(BitConverter.GetBytes(second));
rpc.AddRange(BitConverter.GetBytes(last));
return rpc.ToArray();
}
///
/// Read file from the meter
///
/// file name as string
/// empty list of data
/// file content as byte array
///
/// - Initial
///
///
/// - Stop process added
///
///
/// - Register read/write retry 0 implemented,
/// - Extended communication timeout
///
///
/// - Command write retry 0 implemented,
/// - Extended timeout.
///
///
/// - Retries changed for and back,
/// - Timeout changed for and back,
/// - Dummy read removed.
///
///
/// - Wait after writing for response before reading!
///
///
/// - Check remaining size to abort operation.
///
///
/// - Event on changed process counter.
///
///
/// - Add a NULL at the end of the file name.
///
///
/// - Read data according to file size searched with FSeek.
///
///
/// - Speed up FOpen as read is not so critical.
///
///
/// - Avoid reading of file size from FSeek is 0.
///
///
/// - Removed extended timeout.
///
///
/// - FClose added on failed GetFileSizeInternal.
/// - Using for multiple write access to register instead of 3 writes.
///
public Boolean ReadMeterFile(String fileName, out List readBackData)
{
//set processing counter
ProcessedBytesCtr = 0;
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", 0));
//add file name delimiter
if (!fileName.Contains(StringDelimiter))
fileName += StringDelimiter;
readBackData = new List();
//use high speed access
if (!OpenMeterFile(fileName, StrOpenFileReadOnlyMode))
return false;
//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;
}
//set register write retries to 0 to avoid doubling of records
CommunicationConfig.RequestRetries = 0;
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)
{
var remainingData = RegisterConverter.ByteArrayToValue(
_currentGenesis.ReadRegister(Register.Configexchange.FileRead));
fileSize = remainingData > 0 ? fileSize - remainingData : 0;
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));
}
}
}// file size unequal to 0
//processed bytes no longer needed
ProcessedBytesCtr = 0;
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", 0));
//set timeout back to default value
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
//read raw data from meter
return CloseMeterFile();
}
///
/// Read catalog of all meter files
///
/// file names as string
/// e.g. "1\\" for drive 1
/// e.g. "upd*" all upgrade files on drive
///
///
/// - Initial.
///
///
/// - Default wildcard.
///
public Boolean ReadMeterFileCatalog(out List files, String drive, String wildcard = Constants.StrWildcard)
{
files = new List();
//add file name delimiter
if (!wildcard.Contains(StringDelimiter))
wildcard += StringDelimiter;
//write the initial key
var key = 0;
_stopProcess = false;
// if both key are identical, the last file has been listed
while (!_stopProcess)
{
Thread.Sleep(1);
// remind entry key as this will be changed with the read back
var lastKey = key;
var searchPattern = drive + wildcard;
_currentGenesis.WriteRegister(Register.Configexchange.Catalogue, searchPattern);
_currentGenesis.WriteRegister(Register.Configexchange.Catalogue, key);
// initial read
var readData = "";
String backupData;
do
{
backupData = readData;
readData += RegisterConverter.ByteArrayToValue(
_currentGenesis.ReadRegister(Register.Configexchange.Catalogue));
} while (!readData.Contains(StringDelimiter) && backupData != readData);
if (!string.IsNullOrEmpty(readData))
{
//read new key
key = RegisterConverter.ByteArrayToValue(
_currentGenesis.ReadRegister(Register.Configexchange.Catalogue));
}
if (key == lastKey)
break;
// remove the "\0"
var strMeterFile = readData.Split(StringDelimiter);
// avoid identical files repetition
var filePathName = drive + strMeterFile[0];
files.Add(filePathName);
}
files.Sort();
return files.Count > 0;
}
///
/// Create an empty meter file as placeholder for space in the file system.
///
/// file name as string
/// the file size for the file pointer
/// true, if file can be found and stored and, if required, read back and verified
///
/// - Initial
///
///
/// - Add a NULL at the end of the file name.
///
///
/// - Adapted to new ReadFilePointerOffset.
///
///
/// - Using for multiple write access to register instead of 3 writes.
///
///
/// - Check transmit protocol for null.
///
public Boolean CreateEmptyMeterFile(String fileName, Int32 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
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;
var returnValue = true;
//adjust file pointer if offset is required
if (fileSize > 0)
{
// 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;
}
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)
{
RegisterConverter.ByteArrayToValue(
_currentGenesis.ReadRegister(Register.Configexchange.FileWrite));
ReadFilePointerOffset(out returnedFileOffset);
}
//set timeout back to default value
_currentGenesis.TransmitProtocol?.SetDefaultResponseTimeout();
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
if (returnedFileOffset != fileSize)
returnValue = false;
}
returnValue &= CloseMeterFile();
return returnValue;
}
///
/// Read the file size.
///
/// file name as string
/// file size of the given file
/// true: file size could be read
///
/// - Initial
///
///
/// - Modified returns.
///
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
if (!fileName.Contains(StringDelimiter))
fileName += StringDelimiter;
//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;
}
///
/// 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!!!!!
///
/// file name as string
/// file size of the given file
/// true: file found
///
/// - Initial
///
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;
}
///
/// Meter file verification against given data
/// Takes into account, that the read file is always integer dividable by 4 due to the chunk size
/// of the write and read access to the Genesis.
///
/// file name as string
/// data to store
/// true: file found and verified
///
/// - Initial
///
///
/// - Changed return value to false, if file could not be read.
///
///
/// - Compare byte by byte until length of given buffer, as the returned buffer is always filled
/// up to the chunk size of 4.
///
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
if (!fileName.Contains(StringDelimiter))
fileName += StringDelimiter;
if (!ReadMeterFile(fileName, out var binData) || binData == null || binData.Count == 0)
return false;
// 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 an integer of this chunk size!
for (var ctr = 0; ctr < dataBytes.Length; ctr++)
{
if (binData[ctr] != dataBytes[ctr])
{
returnValue = false;
}
}
return returnValue;
}
///
/// Erase file from the meter
///
/// file name as string
///
///
/// - Initial
///
///
/// - Extended timeout.
///
///
/// - Removed retries to speed up erasure as it returns error code 0x040B.
///
///
/// - Add a NULL at the end of the file name.
///
///
/// - Removed extended timeout.
///
public Boolean EraseMeterFile(String fileName)
{
//check if logged on to meter and access level is as expected
if (_currentGenesis == null || !_currentGenesis.IsLoggedOn)
return false;
//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, 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
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
return true;
}
///
/// Two stage unlock sequence for meter file write or erase access
///
/// file name as string
///
///
/// - Initial
///
///
/// - Add a NULL at the end of the file name.
///
public Boolean UnlockEraseWriteMeterFile(String fileName)
{
//add file name delimiter
if (!fileName.Contains(StringDelimiter))
fileName += StringDelimiter;
//remind file name being unlocked
_fileName = fileName;
return true;
}
///
/// FSeek implementation of genesis meter.
/// ATTENTION: FOpen has to be executed in advance to get
/// the file pointer.
///
///
/// positioning to file start or end
///
///
/// - Initial
///
///
/// - Command write retry implemented.
///
///
/// - Extended timeout.
///
///
/// - Retries changed for and back,
/// - Timeout changed for and back.
///
///
/// - Extended FSeek for file end positioning.
///
///
/// - Removed extended timeout.
///
///
/// - Using for multiple write access to register instead of 3 writes.
///
private Boolean SetFilePointerOffset(Int32 filePointerOffset = 0, Int32 fSeekPara = FSeekStartOfFile)
{
Boolean returnValue;
var retryCtr = 0;
if (FSeekEndOfFile == fSeekPara)
{
filePointerOffset = 0;
}
do
{
//request retries on single write register access, needs to be zero to hold the sequence
CommunicationConfig.RequestRetries = 0;
var rpc = BuildRpcInt32(_filePointer, filePointerOffset, fSeekPara);
returnValue = _currentGenesis.WriteRegister(Register.Configexchange.SetFilePointerOffset, rpc);
//function call returns 0 if FSeek successfully set
returnValue &= 0 == RegisterConverter.ByteArrayToValue(_currentGenesis.ReadRegister(
Register.Configexchange.SetFilePointerOffset));
//set back to default value
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
} while (!returnValue && retryCtr++ < CommandWriteRetries);
return returnValue;
}
///
/// FTell implementation
///
/// file pointer offset
///
/// - Initial
///
///
/// - Command write retry implemented.
///
///
/// - Extended timeout.
///
///
/// - Retries changed for and back,
/// - Timeout changed for and back.
///
///
/// - Modified for return.
///
///
/// - Removed extended timeout.
///
private Boolean ReadFilePointerOffset(out Int32 filePointerOffset)
{
var retryCtr = 0;
//request retries on single write register access, needs to be zero to hold the sequence
CommunicationConfig.RequestRetries = 0;
do
{
//read file pointer offset
_currentGenesis.WriteRegister(Register.Configexchange.GetFilePointerOffset,
_filePointer, false);
filePointerOffset = RegisterConverter.ByteArrayToValue(_currentGenesis.ReadRegister(
Register.Configexchange.GetFilePointerOffset));
} while (filePointerOffset <= 0 && retryCtr++ < CommandWriteRetries);
//set timeout back to default value
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
return filePointerOffset > 0;
}
///
/// FOpen implementation of genesis meter
/// Open a file of the meter and sets the file pointer
///
///
/// read, write eg,
/// true if filePointer higher than zero
///
/// - Initial
///
///
/// - Extended timeouts,
/// - Read DUMMY to overcome FW malfunction.
///
///
/// - Retries changed for and back,
/// - Timeout changed for and back.
///
///
/// - Extended timings removed if high speed is active.
///
///
/// - Removed retry dummy read.
///
///
/// - Removed extended timeout.
///
///
/// - 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.
///
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;
//add file name delimiter
if (!fileName.Contains(StringDelimiter))
fileName += StringDelimiter;
//add access mode delimiter
if (!accessMode.Contains(StringDelimiter))
accessMode += StringDelimiter;
var rpc = new List();
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
{
//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)
{
//get the file pointer
_filePointer = RegisterConverter.ByteArrayToValue(_currentGenesis.ReadRegister(
Register.Configexchange.FileOpen));
}
} while ((_filePointer <= 0 || retValue == false) && retryCtr++ < CommandWriteRetries);
//set retries back to default value
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
return _filePointer > 0 && retValue;
}
///
/// FClose implementation of genesis meter
/// Close the _filePointer referenced file of the meter
///
///
///
/// - Initial
///
///
/// - Command write retry implemented.
///
///
/// - Extended timeout.
///
///
/// - Retries changed for and back,
/// - Timeout changed for and back,
/// - Command retry removed.
///
///
/// - Wait after writing for response before reading!
///
///
/// - Ignore file close write return!
///
///
/// - Removed extended timeout.
///
///
/// - FClose retries.
///
private Boolean CloseMeterFile()
{
//avoid file close if file pointer assignment failed
if (_filePointer <= 0)
return false;
//request retries on single write register access, needs to be zero to hold the sequence
CommunicationConfig.RequestRetries = 0;
var retryCtr = 0;
Boolean retValue;
do
{
retValue = _currentGenesis.WriteRegister(Register.Configexchange.FileClose, _filePointer);
//function call returns 0 if FClose successfully executed
retValue &= 0 == RegisterConverter.ByteArrayToValue(
_currentGenesis.ReadRegister(Register.Configexchange.FileClose));
}while ( retValue == false && retryCtr++ < CommandWriteRetries);
//set timeout back to default value
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
return retValue;
}
}
}