From b6655ae270e641a6d6eb74421289e29541ef6590 Mon Sep 17 00:00:00 2001 From: Thomas Wiedebusch Date: Fri, 13 Jan 2023 12:27:29 +0100 Subject: [PATCH 1/2] MeterFile improved: - Multiple write used for all file operations where single sequential writes applied, Cordonel "Low Level Tools": - List files, - List log files and analyze content, FwUpdate: - Erase tstfile, as this consumes 252 kB of space --- Common/.vs/config/applicationhost.config | 2 +- Common/Common.sln.DotSettings | 1 + .../TransmitProtocol/UartTransmitProtocol.cs | 4 +- .../GenesisFile/Consts/FwUpdateState.cs | 4 + .../GenesisFile/Consts/FwUpdateStateInfo.cs | 3 + .../Genesis/GenesisFile/MeterFile.cs | 724 +++++++++++------- .../Genesis/GenesisFile/MeterFwUpdate.cs | 42 +- .../Properties/Resources.Designer.cs | 9 + .../GenesisFile/Properties/Resources.de.resx | 3 + .../GenesisFile/Properties/Resources.resx | 5 +- ...ToXlsHelper.csproj.CoreCompileInputs.cache | 2 +- .../Ui/GenesisToolBox/FrmFwUpdate.Designer.cs | 2 +- Common/Ui/GenesisToolBox/FrmLowLevelTools.cs | 105 ++- .../ServiceFwUpdateSw/FrmServiceFwUpdateSw.cs | 2 +- 14 files changed, 585 insertions(+), 323 deletions(-) diff --git a/Common/.vs/config/applicationhost.config b/Common/.vs/config/applicationhost.config index ebc79eca..1b9332b4 100644 --- a/Common/.vs/config/applicationhost.config +++ b/Common/.vs/config/applicationhost.config @@ -155,7 +155,7 @@ - + diff --git a/Common/Common.sln.DotSettings b/Common/Common.sln.DotSettings index eded7968..588a89aa 100644 --- a/Common/Common.sln.DotSettings +++ b/Common/Common.sln.DotSettings @@ -18,6 +18,7 @@ True True True + True True True True diff --git a/Common/Hardware/Interfaces/Protocols/TransmitProtocol/UartTransmitProtocol.cs b/Common/Hardware/Interfaces/Protocols/TransmitProtocol/UartTransmitProtocol.cs index 12ef54f8..d7ac86d3 100644 --- a/Common/Hardware/Interfaces/Protocols/TransmitProtocol/UartTransmitProtocol.cs +++ b/Common/Hardware/Interfaces/Protocols/TransmitProtocol/UartTransmitProtocol.cs @@ -25,9 +25,7 @@ namespace Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol private const Int32 UartSyncByteLength = 1; //length of syncByte private const Int32 UartHeaderLength = 4; //length excluding syncByte and CRC private const Int32 UartCrcLength = 2; - private const Int32 UartProtocolFrameLength = UartSyncByteLength + UartHeaderLength - + UartCrcLength; - + private const Int32 UartProtocolFrameLength = UartSyncByteLength + UartHeaderLength + UartCrcLength; private const Byte UartSyncByte = 0x5B; //The UART length covers all bytes excluding the syncByte length diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisFile/Consts/FwUpdateState.cs b/Common/Hardware/WaterMeter/Genesis/GenesisFile/Consts/FwUpdateState.cs index 0adee651..5db4099a 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisFile/Consts/FwUpdateState.cs +++ b/Common/Hardware/WaterMeter/Genesis/GenesisFile/Consts/FwUpdateState.cs @@ -22,6 +22,10 @@ /// StartInitial, /// + /// Erase test file as this reserves space for update over the air + /// + EraseTstFile, + /// /// Repeat FW update /// RepeatFwUpdate, diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisFile/Consts/FwUpdateStateInfo.cs b/Common/Hardware/WaterMeter/Genesis/GenesisFile/Consts/FwUpdateStateInfo.cs index d6cf07e1..6ae3570a 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisFile/Consts/FwUpdateStateInfo.cs +++ b/Common/Hardware/WaterMeter/Genesis/GenesisFile/Consts/FwUpdateStateInfo.cs @@ -32,6 +32,9 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.Consts StateInfos[(Int32)FwUpdateState.StartInitial].State = FwUpdateState.StartInitial; StateInfos[(Int32)FwUpdateState.StartInitial].Message = Resources.StrFwUpdateStateStartInitial; + StateInfos[(Int32)FwUpdateState.EraseTstFile].State = FwUpdateState.EraseTstFile; + StateInfos[(Int32)FwUpdateState.EraseTstFile].Message = Resources.StrEraseTstFile; + StateInfos[(Int32)FwUpdateState.RepeatFwUpdate].State = FwUpdateState.RepeatFwUpdate; StateInfos[(Int32)FwUpdateState.RepeatFwUpdate].Message = Resources.StrFwUpdateStateRepeatFwUpdate; diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterFile.cs b/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterFile.cs index 2a7eec25..64eda829 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterFile.cs +++ b/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterFile.cs @@ -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+"; /// /// FSeek for start of file @@ -57,6 +59,11 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// 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. /// @@ -80,7 +87,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// Wildcard for search all. /// - public const Char WildcardAll = '*'; + public const String WildcardAll = "*"; /// /// Processed bytes counter @@ -199,10 +206,17 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// - 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. + /// 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 +224,136 @@ 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(); - 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.ConvertTo( - _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); + + ////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(); + 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.ConvertTo( + _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; } + /// + /// 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 + /// + public 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 /// @@ -353,6 +397,16 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// - 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 @@ -360,63 +414,60 @@ 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(); - // 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); + + ////write size to 1 + //_currentGenesis.WriteRegister(Register.Configexchange.FileRead, 1, false); + ////write counts to expected length + //_currentGenesis.WriteRegister(Register.Configexchange.FileRead, fileSize, false); + ////write file pointer + //_currentGenesis.WriteRegister(Register.Configexchange.FileRead, _filePointer); + + //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( + _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( - _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 +475,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(); } /// @@ -441,10 +495,13 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// - Default wildcard. /// - public Boolean ReadMeterFileCatalog(out List files, String drive, Char wildcard = WildcardAll) + public Boolean ReadMeterFileCatalog(out List files, String drive, String wildcard = WildcardAll) { files = new List(); + //add file name delimiter + if (!wildcard.Contains(StringDelimiter)) + wildcard += StringDelimiter; //write the initial key var key = 0; _stopProcess = false; @@ -486,6 +543,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile files.Add(filePathName); } + files.Sort(); return files.Count > 0; } @@ -501,104 +559,140 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// - 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. + /// 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 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) { - // adjust file pointer to the end of file - returnValue = SetFilePointerOffset(fileSize - 1); - //read file pointer offset - returnValue &= (fileSize - 1) == ReadFilePointerOffset(); + RegisterConverter.ConvertTo( + _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( - _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; } /// - /// Uses FSeek to get the file size. + /// Read the file size. /// /// file name as string /// file size of the given file - /// true: file found + /// 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 - 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; } + /// + /// 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 @@ -613,12 +707,20 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// - 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 - fileName += StringDelimiter; + if (!fileName.Contains(StringDelimiter)) + fileName += StringDelimiter; if (!ReadMeterFile(fileName, out var binData) || binData == null || binData.Count == 0) return false; @@ -653,25 +755,29 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// - Add a NULL at the end of the file name. /// + /// + /// - Removed extended timeout. + /// 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 //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 +797,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; } /// - /// 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. /// /// @@ -723,6 +829,12 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// - 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; @@ -734,31 +846,30 @@ 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); + //if (retryCtr > 0) + //{ + // //read DUMMY to overcome FW malfunction + // _currentGenesis.ReadRegister(Register.System.MonotonicSeconds); + // Thread.Sleep(1000); + //} + //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); + + //returnValue = _currentGenesis.WriteRegister(Register.Configexchange.SetFilePointerOffset, + // _filePointer, false); + //returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.SetFilePointerOffset, + // filePointerOffset, false); + //returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.SetFilePointerOffset, + // fSeekPara, false); //function call returns 0 if FSeek successfully set returnValue &= 0 == RegisterConverter.ConvertTo(_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 +893,38 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// - Retries changed for and back, /// - Timeout changed for and back. /// - private Int32 ReadFilePointerOffset() + /// + /// - Modified for return. + /// + /// + /// - Removed extended timeout. + /// + 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; - + //if (retryCtr > 0) + //{ + // //read DUMMY to overcome FW malfunction + // _currentGenesis.ReadRegister(Register.System.MonotonicSeconds); + // Thread.Sleep(1000); + //} //read file pointer offset - returnValue = _currentGenesis.WriteRegister(Register.Configexchange.GetFilePointerOffset, + _currentGenesis.WriteRegister(Register.Configexchange.GetFilePointerOffset, _filePointer, false); filePointerOffset = RegisterConverter.ConvertTo(_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; } /// @@ -822,7 +933,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// /// read, write eg, - /// disable high timeouts /// true if filePointer higher than zero /// /// - Initial @@ -838,48 +948,79 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// - Extended timings removed if high speed is active. /// - private Boolean OpenMeterFile(String fileName, String accessMode, Boolean highSpeed = false) + /// + /// - 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; - 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(); + 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) + //if (retryCtr > 0) + //{ + // //read DUMMY to overcome FW malfunction + // _currentGenesis.ReadRegister(Register.System.MonotonicSeconds); + // Thread.Sleep(1000); + //} + + //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); + + ////write file name to meter with meter open command, don't wait for response + //retValue = _currentGenesis.WriteRegister(Register.Configexchange.FileOpen, fileName, false); + ////write access flag + //retValue &= _currentGenesis.WriteRegister(Register.Configexchange.FileOpen, accessMode, 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(_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(_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; } /// @@ -910,26 +1051,41 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// - Removed extended timeout. /// + /// + /// - FClose retries. + /// 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( - _currentGenesis.ReadRegister(Register.Configexchange.FileClose)); + var retryCtr = 0; + Boolean retValue; + + do + { + //if (retryCtr > 0) + //{ + // //read DUMMY to overcome FW malfunction + // _currentGenesis.ReadRegister(Register.System.MonotonicSeconds); + // Thread.Sleep(1000); + //} + + retValue = _currentGenesis.WriteRegister(Register.Configexchange.FileClose, _filePointer); + + //function call returns 0 if FClose successfully executed + retValue &= 0 == RegisterConverter.ConvertTo( + _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; } - } } \ No newline at end of file diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterFwUpdate.cs b/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterFwUpdate.cs index 45e10074..57a18ed3 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterFwUpdate.cs +++ b/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterFwUpdate.cs @@ -98,11 +98,17 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// Core revision minimum as int32? with major * 100 + minor for comparison /// - public Int32? CoreRevisionMinimum { get; private set; } + public Int32? CoreRevisionMinimum + { + get; private set; + } /// /// Core revision maximum as int32? with major * 100 + minor for comparison /// - public Int32? CoreRevisionMaximum { get; private set; } + public Int32? CoreRevisionMaximum + { + get; private set; + } //reminder for the core minimum requirement as string private String _strCoreRevisionMinimum; @@ -635,7 +641,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// - Initial /// public String GetActualOperation() => _actualOperation; - + /// /// Returns the FW Update state /// @@ -1113,6 +1119,9 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// /// - Upload config file removed. /// + /// + /// - Erase test file introduced. + /// private void FwUpdateStateMachine() { if (_fwBackupUpdateState == _fwUpdateState) @@ -1130,6 +1139,11 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile case FwUpdateState.StartInitial: _updateProcedureRetryCtr = 0; + _fwUpdateState = FwUpdateState.EraseTstFile; + break; + + case FwUpdateState.EraseTstFile: + EraseTstFile(); _fwUpdateState = FwUpdateState.BuildFileApps; break; @@ -1495,6 +1509,28 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile } } + /// + /// Erase the test file which is an EMEA placeholder for update over the air. + /// This has a very large size to keep the space reserved for all applications + /// needed to be downloaded. This file can be erased as it will be automatically + /// regenerated after reboot by the SENSUSRADIO app. + /// + /// + /// + /// - Initial + /// + private void EraseTstFile() + { + if (_meterFile == null || _genesisMeter == null) + return; + // erase always, because it may be left as artefact by changing from NA to EMEA or vice versa! + //if (_genesisMeter.Region.Contains("EMEA")) + { + _meterFile.UnlockEraseWriteMeterFile(MeterFile.StrMeterTstFile); + _meterFile.EraseMeterFile(MeterFile.StrMeterTstFile); + } + } + /// /// Download and result handling for update files /// diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.Designer.cs b/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.Designer.cs index 3bb58940..d08a31f0 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.Designer.cs +++ b/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.Designer.cs @@ -60,6 +60,15 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.Properties { } } + /// + /// Looks up a localized string similar to Erase test file. + /// + public static string StrEraseTstFile { + get { + return ResourceManager.GetString("StrEraseTstFile", resourceCulture); + } + } + /// /// Looks up a localized string similar to Build Update Files. /// diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.de.resx b/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.de.resx index 29e6c483..98af0467 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.de.resx +++ b/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.de.resx @@ -186,4 +186,7 @@ Lade Einstellungen + + Lösche Testdatei + \ No newline at end of file diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.resx b/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.resx index ddfa14c6..8491c106 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.resx +++ b/Common/Hardware/WaterMeter/Genesis/GenesisFile/Properties/Resources.resx @@ -156,7 +156,7 @@ Upload Update Files - + Waiting.... @@ -186,4 +186,7 @@ Upload configuration file + + Erase test file + \ No newline at end of file diff --git a/Common/Tools/Tools.JsonToXlsHelper/obj/Debug/Tools.JsonToXlsHelper.csproj.CoreCompileInputs.cache b/Common/Tools/Tools.JsonToXlsHelper/obj/Debug/Tools.JsonToXlsHelper.csproj.CoreCompileInputs.cache index 2634cdb8..0028f628 100644 --- a/Common/Tools/Tools.JsonToXlsHelper/obj/Debug/Tools.JsonToXlsHelper.csproj.CoreCompileInputs.cache +++ b/Common/Tools/Tools.JsonToXlsHelper/obj/Debug/Tools.JsonToXlsHelper.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -81d60b90addc793525740e9993e19bf6665bd401 +f64d563def603c89cafafa61a3219ba97e1e344c diff --git a/Common/Ui/GenesisToolBox/FrmFwUpdate.Designer.cs b/Common/Ui/GenesisToolBox/FrmFwUpdate.Designer.cs index e7fb4a55..22a98395 100644 --- a/Common/Ui/GenesisToolBox/FrmFwUpdate.Designer.cs +++ b/Common/Ui/GenesisToolBox/FrmFwUpdate.Designer.cs @@ -260,7 +260,7 @@ this.btnClearDisplay.Name = "btnClearDisplay"; this.btnClearDisplay.Size = new System.Drawing.Size(116, 25); this.btnClearDisplay.TabIndex = 0; - this.btnClearDisplay.Text = "Clear Display"; + this.btnClearDisplay.Text = "Release Display"; this.btnClearDisplay.UseVisualStyleBackColor = true; this.btnClearDisplay.Click += new System.EventHandler(this.btnClearDisplay_Click); // diff --git a/Common/Ui/GenesisToolBox/FrmLowLevelTools.cs b/Common/Ui/GenesisToolBox/FrmLowLevelTools.cs index aa348cf5..a07345a8 100644 --- a/Common/Ui/GenesisToolBox/FrmLowLevelTools.cs +++ b/Common/Ui/GenesisToolBox/FrmLowLevelTools.cs @@ -535,19 +535,28 @@ namespace Xylem.Common.Ui.GenesisToolBox var msg = $@"{dt:yyyy-MM-dd hh:mm:ss}"; LogText($@"PC date time [UTC]: {msg}"); - var secSince2000 = - RegisterConverter.ConvertTo(_currentGenesis.ReadRegister("SYSTEM_CalendarSeconds")); - var ts = TimeSpan.FromSeconds(secSince2000); - var meterDateTime = new DateTime(2000, 1, 1, 0, 0, 0).Add(ts); - msg = $@"{meterDateTime:yyyy-MM-dd hh:mm:ss}"; - tbxDateTime.Text = msg; - LogText($@"Meter date time [UTC]: {msg}"); - + CalculateMeterDateTime(RegisterConverter.ConvertTo( + _currentGenesis.ReadRegister("SYSTEM_CalendarSeconds"))); //add a blank line to separate next operation LogText(""); _currentGenesis.Logout(); } + /// + /// Calculate and log Cordonel time. + /// + /// + /// - Init. + /// + private void CalculateMeterDateTime(Int32 secSince2000) + { + var ts = TimeSpan.FromSeconds(secSince2000); + var meterDateTime = new DateTime(2000, 1, 1, 0, 0, 0).Add(ts); + var msg = $@"{meterDateTime:yyyy-MM-dd hh:mm:ss}"; + Invoke(new Action(() => { tbxDateTime.Text = msg; })); + LogText($@"Meter date time [UTC]: {msg}"); + } + /// /// Clear history window. /// @@ -799,6 +808,9 @@ namespace Xylem.Common.Ui.GenesisToolBox if (_currentGenesis == null) return; + //reset timer + _startTime = DateTimeOffset.UtcNow; + var msg = @"Fix battery settings"; LogText(msg); lblOverall.Text = msg; @@ -843,6 +855,9 @@ namespace Xylem.Common.Ui.GenesisToolBox if (_currentGenesis == null) return; + //reset timer + _startTime = DateTimeOffset.UtcNow; + var msg = @"Store all configurations in meter"; LogText(msg); lblActualProcess.Text = msg; @@ -874,7 +889,7 @@ namespace Xylem.Common.Ui.GenesisToolBox return; } - // reset the time + //reset timer _startTime = DateTimeOffset.UtcNow; var msg = @"Reboot meter with pseudo FW update"; @@ -921,6 +936,9 @@ namespace Xylem.Common.Ui.GenesisToolBox if (_meterFile == null) _meterFile = new MeterFile(_currentGenesis); + //reset timer + _startTime = DateTimeOffset.UtcNow; + LowLevelActionControl(true); var msg = @"Upload configuration file from meter"; @@ -1013,13 +1031,17 @@ namespace Xylem.Common.Ui.GenesisToolBox if (_meterFile == null) _meterFile = new MeterFile(_currentGenesis); + //reset timer + _startTime = DateTimeOffset.UtcNow; + var statusMap = new Dictionary(); if (File.Exists("Status.json")) { var dict = JsonConvert.DeserializeObject>(File.ReadAllText("Status.json")); foreach (var item in dict) { - statusMap.Add(int.Parse(item.Value.First.Values().First().ToString()), item.Key); + if (item.Value.First != null) + statusMap.Add(int.Parse(item.Value.First.Values().First().ToString()), item.Key); } } @@ -1036,6 +1058,9 @@ namespace Xylem.Common.Ui.GenesisToolBox SetActualProcessAndLog(@"Flushing the logger"); _currentGenesis.WriteRegister("LOGGER_TriggerLogFlush", 1); + CalculateMeterDateTime(RegisterConverter.ConvertTo( + _currentGenesis.ReadRegister("SYSTEM_CalendarSeconds"))); + SetActualProcessAndLog(@"Reading log index file"); _meterFile.ReadMeterFile(MeterFile.StrMeterEngLogIndexFile, out var data); @@ -1054,14 +1079,24 @@ namespace Xylem.Common.Ui.GenesisToolBox fileNames.Add(name); } + fileNames.Sort(); + foreach (var fileName in fileNames) { msg = $"1\\{fileName}"; SetActualProcessAndLog($@"Reading {msg}"); - _meterFile.ReadMeterFile(msg, out data); + var success = _meterFile.ReadMeterFile(msg, out data); - LogText("Data:"); - LogText($"{ByteArrayStyle.ByteStyler.ToString(data.ToArray())}"); + if (!success) + { + _currentGenesis.Logout(); + _currentGenesis.ReLogin(); + } + + if (data.Count > 0) + { + LogText($"Data: {ByteArrayStyle.ByteStyler.ToString(data.ToArray())}"); + } if (data.Count >= 5) { @@ -1083,22 +1118,31 @@ namespace Xylem.Common.Ui.GenesisToolBox case 2: msg = $"({logType}) Critical"; break; + default: + msg = $"({logType}) Unknown log type"; + break; } - LogText($"Log type:{msg}"); + LogText($"Log type: {msg}"); LogText(statusMap.ContainsKey(status) ? $"Status: ({status}) {statusMap[status]}" - : $"Status:{status}"); - LogText($"Count:{count}"); - LogText($"Extra data size:{extraDataSize}"); + : $"Status: {status}"); + LogText($"Count: {count}"); + LogText($"Extra data size: {extraDataSize}"); + + //avoid access beyond the buffer and check for data to display + if (ctr + 5 + extraDataSize < data.Count && extraDataSize > 0) + { + var extraData = data.GetRange(ctr + 5, extraDataSize); + LogText($"Extra data: {ByteArrayStyle.ByteStyler.ToString(extraData.ToArray())}"); + } - var extraData = data.GetRange(ctr + 5, extraDataSize); - LogText("Extra data:"); - LogText($"{ByteArrayStyle.ByteStyler.ToString(extraData.ToArray())}"); LogText(" "); + ctr += (5 + extraDataSize); //exit if index for next run is out of range - if (ctr + 5 >= data.Count) break; + if (ctr + 5 >= data.Count) + break; } } else @@ -1107,13 +1151,12 @@ namespace Xylem.Common.Ui.GenesisToolBox } } }).ContinueWith(delegate - { + { //add a blank line to separate next operation LogText(""); - _currentGenesis.Logout(); - LowLevelActionControl(false); - }); - + _currentGenesis.Logout(); + LowLevelActionControl(false); + }); } /// @@ -1129,6 +1172,9 @@ namespace Xylem.Common.Ui.GenesisToolBox if (_meterFile == null) _meterFile = new MeterFile(_currentGenesis); + //reset timer + _startTime = DateTimeOffset.UtcNow; + LowLevelActionControl(true); var msg = @"List file details from meter"; @@ -1163,11 +1209,14 @@ namespace Xylem.Common.Ui.GenesisToolBox LogText(success ? $@"File name: {fileName}, File size: {fileSize} byte" : $@"File name: {fileName} Unable to access file"); + if (success) continue; + _currentGenesis.Logout(); + _currentGenesis.ReLogin(); } }).ContinueWith(delegate { - //add a blank line to separate next operation - LogText(""); + //add a blank line to separate next operation + LogText(""); _currentGenesis.Logout(); LowLevelActionControl(false); }); diff --git a/ServiceFwUpdate/Ui/ServiceFwUpdateSw/FrmServiceFwUpdateSw.cs b/ServiceFwUpdate/Ui/ServiceFwUpdateSw/FrmServiceFwUpdateSw.cs index 3f9a62b7..5b7c0911 100644 --- a/ServiceFwUpdate/Ui/ServiceFwUpdateSw/FrmServiceFwUpdateSw.cs +++ b/ServiceFwUpdate/Ui/ServiceFwUpdateSw/FrmServiceFwUpdateSw.cs @@ -2947,7 +2947,7 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw foreach (var meterFileToEraseMask in _meterFilesEraseRestore.Erase.Where( x => x.Contains(MeterFile.WildcardAll))) { - var wildcardMask = meterFileToEraseMask.Split(MeterFile.WildcardAll); + var wildcardMask = meterFileToEraseMask.Split('*'); wildcardList.Add(wildcardMask[0]); } From 18db1974e98d502179be4744db71c6537211b3dd Mon Sep 17 00:00:00 2001 From: Thomas Wiedebusch Date: Sat, 14 Jan 2023 14:38:44 +0100 Subject: [PATCH 2/2] GTB LowLevelTools: - Tidy files implemented --- .../Genesis/GenesisFile/MeterFile.cs | 82 ++--- .../FrmLowLevelTools.Designer.cs | 31 +- Common/Ui/GenesisToolBox/FrmLowLevelTools.cs | 305 ++++++++++++++---- 3 files changed, 282 insertions(+), 136 deletions(-) diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterFile.cs b/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterFile.cs index 64eda829..fffb75f2 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterFile.cs +++ b/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterFile.cs @@ -69,6 +69,28 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile /// 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" }; + + /// + /// 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. /// @@ -262,13 +284,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile var rpc = BuildRpcInt32(1, dataBytes.Length, _filePointer); returnValue = _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, rpc); - ////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) @@ -439,13 +454,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile var rpc = BuildRpcInt32(1, fileSize, _filePointer); _currentGenesis.WriteRegister(Register.Configexchange.FileRead, rpc); - ////write size to 1 - //_currentGenesis.WriteRegister(Register.Configexchange.FileRead, 1, false); - ////write counts to expected length - //_currentGenesis.WriteRegister(Register.Configexchange.FileRead, fileSize, false); - ////write file pointer - //_currentGenesis.WriteRegister(Register.Configexchange.FileRead, _filePointer); - //the preceding routine needs to wait for response, else this is always true! while (fileSize > 0 && !_stopProcess) { @@ -605,12 +613,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile var rpc = BuildRpcInt32(1, 1, _filePointer); returnValue = _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, rpc); - ////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); @@ -774,7 +776,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile //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 @@ -846,25 +848,12 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile do { - //if (retryCtr > 0) - //{ - // //read DUMMY to overcome FW malfunction - // _currentGenesis.ReadRegister(Register.System.MonotonicSeconds); - // Thread.Sleep(1000); - //} //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); - //returnValue = _currentGenesis.WriteRegister(Register.Configexchange.SetFilePointerOffset, - // _filePointer, false); - //returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.SetFilePointerOffset, - // filePointerOffset, false); - //returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.SetFilePointerOffset, - // fSeekPara, false); - //function call returns 0 if FSeek successfully set returnValue &= 0 == RegisterConverter.ConvertTo(_currentGenesis.ReadRegister( Register.Configexchange.SetFilePointerOffset)); @@ -907,12 +896,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile do { - //if (retryCtr > 0) - //{ - // //read DUMMY to overcome FW malfunction - // _currentGenesis.ReadRegister(Register.System.MonotonicSeconds); - // Thread.Sleep(1000); - //} //read file pointer offset _currentGenesis.WriteRegister(Register.Configexchange.GetFilePointerOffset, _filePointer, false); @@ -988,13 +971,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile do { - //if (retryCtr > 0) - //{ - // //read DUMMY to overcome FW malfunction - // _currentGenesis.ReadRegister(Register.System.MonotonicSeconds); - // Thread.Sleep(1000); - //} - //write file name to meter with meter open command, don't wait for response var rpcString = new String(rpc.ToArray()); rpcString += accessMode; @@ -1002,11 +978,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile // wait for response! retValue = _currentGenesis.WriteRegister(Register.Configexchange.FileOpen, rpcString, false); - ////write file name to meter with meter open command, don't wait for response - //retValue = _currentGenesis.WriteRegister(Register.Configexchange.FileOpen, fileName, false); - ////write access flag - //retValue &= _currentGenesis.WriteRegister(Register.Configexchange.FileOpen, accessMode, false); - if (retValue) { //get the file pointer @@ -1068,13 +1039,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile do { - //if (retryCtr > 0) - //{ - // //read DUMMY to overcome FW malfunction - // _currentGenesis.ReadRegister(Register.System.MonotonicSeconds); - // Thread.Sleep(1000); - //} - retValue = _currentGenesis.WriteRegister(Register.Configexchange.FileClose, _filePointer); //function call returns 0 if FClose successfully executed diff --git a/Common/Ui/GenesisToolBox/FrmLowLevelTools.Designer.cs b/Common/Ui/GenesisToolBox/FrmLowLevelTools.Designer.cs index 1e37333c..4e08351d 100644 --- a/Common/Ui/GenesisToolBox/FrmLowLevelTools.Designer.cs +++ b/Common/Ui/GenesisToolBox/FrmLowLevelTools.Designer.cs @@ -68,8 +68,9 @@ this.label7 = new System.Windows.Forms.Label(); this.saveConfigFile = new System.Windows.Forms.SaveFileDialog(); this.groupBox2 = new System.Windows.Forms.GroupBox(); - this.btnReadLogFiles = new System.Windows.Forms.Button(); this.btnListFileDetails = new System.Windows.Forms.Button(); + this.btnReadLogFiles = new System.Windows.Forms.Button(); + this.btnTidyFile = new System.Windows.Forms.Button(); this.grpSetup.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); this.groupBox1.SuspendLayout(); @@ -348,6 +349,7 @@ // // groupBox1 // + this.groupBox1.Controls.Add(this.btnTidyFile); this.groupBox1.Controls.Add(this.btnGetDateTime); this.groupBox1.Controls.Add(this.btnSetDateTime); this.groupBox1.Controls.Add(this.btnRebootMeter); @@ -465,6 +467,16 @@ this.groupBox2.TabStop = false; this.groupBox2.Text = "Log"; // + // btnListFileDetails + // + this.btnListFileDetails.Location = new System.Drawing.Point(10, 84); + this.btnListFileDetails.Name = "btnListFileDetails"; + this.btnListFileDetails.Size = new System.Drawing.Size(116, 25); + this.btnListFileDetails.TabIndex = 39; + this.btnListFileDetails.Text = "List File Details"; + this.btnListFileDetails.UseVisualStyleBackColor = true; + this.btnListFileDetails.Click += new System.EventHandler(this.btnListFileDetails_Click); + // // btnReadLogFiles // this.btnReadLogFiles.Location = new System.Drawing.Point(10, 54); @@ -475,15 +487,15 @@ this.btnReadLogFiles.UseVisualStyleBackColor = true; this.btnReadLogFiles.Click += new System.EventHandler(this.btnReadLogFiles_Click); // - // btnListFileDetails + // btnTidyFile // - this.btnListFileDetails.Location = new System.Drawing.Point(10, 84); - this.btnListFileDetails.Name = "btnListFileDetails"; - this.btnListFileDetails.Size = new System.Drawing.Size(116, 25); - this.btnListFileDetails.TabIndex = 39; - this.btnListFileDetails.Text = "List File Details"; - this.btnListFileDetails.UseVisualStyleBackColor = true; - this.btnListFileDetails.Click += new System.EventHandler(this.btnListFileDetails_Click); + this.btnTidyFile.Location = new System.Drawing.Point(10, 174); + this.btnTidyFile.Name = "btnTidyFile"; + this.btnTidyFile.Size = new System.Drawing.Size(116, 25); + this.btnTidyFile.TabIndex = 39; + this.btnTidyFile.Text = "Tidy File System"; + this.btnTidyFile.UseVisualStyleBackColor = true; + this.btnTidyFile.Click += new System.EventHandler(this.btnTidyFile_Click); // // FrmLowLevelTools // @@ -568,5 +580,6 @@ private System.Windows.Forms.Button btnGetDateTime; private System.Windows.Forms.Button btnReadLogFiles; private System.Windows.Forms.Button btnListFileDetails; + private System.Windows.Forms.Button btnTidyFile; } } \ No newline at end of file diff --git a/Common/Ui/GenesisToolBox/FrmLowLevelTools.cs b/Common/Ui/GenesisToolBox/FrmLowLevelTools.cs index a07345a8..3e03dfe8 100644 --- a/Common/Ui/GenesisToolBox/FrmLowLevelTools.cs +++ b/Common/Ui/GenesisToolBox/FrmLowLevelTools.cs @@ -16,6 +16,7 @@ using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile; using Xylem.Common.Hardware.WaterMeter.Genesis.Registers; using Xylem.Common.Hardware.WaterMeter.WaterMeterCore; using Xylem.Common.Utils.ProcessExec.EventArguments; +using String = System.String; namespace Xylem.Common.Ui.GenesisToolBox @@ -35,8 +36,8 @@ namespace Xylem.Common.Ui.GenesisToolBox private MeterFile _meterFile; - private String _lastFwUpdateState; - private Int32 _lastSingleProgress; + //private String _lastFwUpdateState; + //private Int32 _lastSingleProgress; private Boolean _progressBarOn; private DateTimeOffset _startTime; private Boolean _resetTimeMeasurement; @@ -155,6 +156,7 @@ namespace Xylem.Common.Ui.GenesisToolBox btnUploadConfig.Enabled = false; btnReadLogFiles.Enabled = false; btnListFileDetails.Enabled = false; + btnTidyFile.Enabled = false; } /// @@ -171,6 +173,7 @@ namespace Xylem.Common.Ui.GenesisToolBox btnUploadConfig.Enabled = true; btnReadLogFiles.Enabled = true; btnListFileDetails.Enabled = true; + btnTidyFile.Enabled = true; } /// @@ -436,7 +439,7 @@ namespace Xylem.Common.Ui.GenesisToolBox lblOverall.Visible = true; barOverallProgressUpdate.Visible = true; barSingleProgressUpdate.Visible = true; - _lastFwUpdateState = ""; + //_lastFwUpdateState = ""; tmrProgressUpdate.Enabled = true; SetControlsLowLevelOpOngoing(); } @@ -447,7 +450,7 @@ namespace Xylem.Common.Ui.GenesisToolBox barOverallProgressUpdate.Visible = false; barSingleProgressUpdate.Visible = false; tmrProgressUpdate.Enabled = false; - _lastFwUpdateState = ""; + //_lastFwUpdateState = ""; lblWaitingForMeterResponse.Visible = false; lblActualProcess.Text = ""; lblOverall.Text = ""; @@ -470,7 +473,7 @@ namespace Xylem.Common.Ui.GenesisToolBox { Invoke(new Action(() => { - _lastFwUpdateState = ""; + //_lastFwUpdateState = ""; ViewProcessControl(true); tmrProgressUpdate.Enabled = true; })); @@ -635,7 +638,7 @@ namespace Xylem.Common.Ui.GenesisToolBox #endregion - #region Checks + #region Tools private void CheckUpdateEnabled() { @@ -649,6 +652,111 @@ namespace Xylem.Common.Ui.GenesisToolBox SetCordonelAccessLocked(); } + /// + /// List all meter files + /// + /// + /// - Initial + /// + private List ListMeterFiles() + { + SetActualProcessAndLog(@"List meter files drive 0"); + _meterFile.ReadMeterFileCatalog(out var fileNamesDrive0, MeterFile.StrMeterDrive0); + if (fileNamesDrive0.Count == 0) + { + LogText(@"Could not read drive 0"); + } + else + { + foreach (var fileName in fileNamesDrive0) + { + LogText(fileName); + } + } + + SetActualProcessAndLog(@"List meter files drive 1"); + _meterFile.ReadMeterFileCatalog(out var fileNamesDrive1, MeterFile.StrMeterDrive1); + + if (fileNamesDrive1.Count == 0) + { + LogText(@"Could not read drive 1"); + } + else + { + foreach (var fileName in fileNamesDrive1) + { + LogText(fileName); + } + } + + var fileNames = new List(); + fileNames.AddRange(fileNamesDrive0); + fileNames.AddRange(fileNamesDrive1); + + return fileNames; + } + /// + /// List all meter log files covered by the log index file + /// + /// + /// - Initial + /// + private List ListLogFiles() + { + SetActualProcessAndLog(@"Flushing the logger"); + _currentGenesis.WriteRegister("LOGGER_TriggerLogFlush", 1); + + CalculateMeterDateTime(RegisterConverter.ConvertTo( + _currentGenesis.ReadRegister("SYSTEM_CalendarSeconds"))); + + SetActualProcessAndLog(@"Reading log index file"); + _meterFile.ReadMeterFile(MeterFile.StrMeterEngLogIndexFile, out var data); + + var fileNames = new List(); + var rawData = data.ToArray(); + var sample = new Byte[4]; + for (var ctr = 0; ctr < data.Count; ctr += 4) + { + for (var x = 0; x < 4; x++) + { + sample[x] = rawData[ctr + x]; + } + + var name = $"1\\{RegisterConverter.ConvertTo(sample):X8}"; + LogText($"Log file catalogue: {name}"); + fileNames.Add(name); + } + + fileNames.Sort(); + return fileNames; + } + + /// + /// Analyze all meter files + /// + /// + /// - Initial + /// + private void AnalyzeMeterFiles(List meterFiles) + { + if (meterFiles.Count == 0) + return; + + LogText("Analyzing meter files"); + foreach (var fileName in meterFiles) + { + Invoke(new Action(() => { lblActualProcess.Text = $@"Analyze file {fileName}"; })); + var success = _meterFile.GetFileSize(fileName, out var fileSize); + + LogText(success + ? $@"File name: {fileName}, File size: {fileSize} byte" + : $@"File name: {fileName} Unable to access file"); + if (success) + continue; + _currentGenesis.Logout(); + _currentGenesis.ReLogin(); + } + } #endregion #region Events @@ -690,7 +798,7 @@ namespace Xylem.Common.Ui.GenesisToolBox var singleProgress = (Int32)(_meterResetPsu?.SingleFileProcessCtrPercent ?? 0); barSingleProgressUpdate.Value = singleProgress > 100 ? 100 : singleProgress; - _lastSingleProgress = singleProgress; + //_lastSingleProgress = singleProgress; barSingleProgressUpdate.Update(); } @@ -1019,7 +1127,7 @@ namespace Xylem.Common.Ui.GenesisToolBox } /// - /// Read engineering log file from meter. + /// Read engineering log files from meter and analyzes the contents. /// /// /// - Initial @@ -1055,37 +1163,12 @@ namespace Xylem.Common.Ui.GenesisToolBox { _currentGenesis.ReLogin(); - SetActualProcessAndLog(@"Flushing the logger"); - _currentGenesis.WriteRegister("LOGGER_TriggerLogFlush", 1); - - CalculateMeterDateTime(RegisterConverter.ConvertTo( - _currentGenesis.ReadRegister("SYSTEM_CalendarSeconds"))); - - SetActualProcessAndLog(@"Reading log index file"); - _meterFile.ReadMeterFile(MeterFile.StrMeterEngLogIndexFile, out var data); - - var fileNames = new List(); - var rawData = data.ToArray(); - var sample = new Byte[4]; - for (var ctr = 0; ctr < data.Count; ctr += 4) - { - for (var x = 0; x < 4; x++) - { - sample[x] = rawData[ctr + x]; - } - - var name = RegisterConverter.ConvertTo(sample).ToString("X8"); - LogText(name); - fileNames.Add(name); - } - - fileNames.Sort(); + var fileNames = ListLogFiles(); foreach (var fileName in fileNames) { - msg = $"1\\{fileName}"; - SetActualProcessAndLog($@"Reading {msg}"); - var success = _meterFile.ReadMeterFile(msg, out data); + SetActualProcessAndLog($@"Reading and analyzing {fileName}"); + var success = _meterFile.ReadMeterFile(fileName, out var data); if (!success) { @@ -1162,7 +1245,7 @@ namespace Xylem.Common.Ui.GenesisToolBox /// /// List file details from meter. /// - /// + /// /// - Initial /// private void btnListFileDetails_Click(Object sender, EventArgs e) @@ -1177,42 +1260,15 @@ namespace Xylem.Common.Ui.GenesisToolBox LowLevelActionControl(true); - var msg = @"List file details from meter"; + var msg = @"List file details of meter"; LogText(msg); lblOverall.Text = msg; Task.Factory.StartNew(() => { _currentGenesis.ReLogin(); - var fileNames = new List(); - Invoke(new Action(() => { lblActualProcess.Text = @"List meter files drive 0"; })); - _meterFile.ReadMeterFileCatalog(out var fileNamesDrive0, MeterFile.StrMeterDrive0); - if (fileNamesDrive0.Count == 0) - { - LogText(@"Could not read drive 0"); - } - - Invoke(new Action(() => { lblActualProcess.Text = @"List meter files drive 1"; })); - _meterFile.ReadMeterFileCatalog(out var fileNamesDrive1, MeterFile.StrMeterDrive1); - - if (fileNamesDrive1.Count == 0) - { - LogText(@"Could not read drive 1"); - } - - fileNames.AddRange(fileNamesDrive0); - fileNames.AddRange(fileNamesDrive1); - foreach (var fileName in fileNames) - { - Invoke(new Action(() => { lblActualProcess.Text = $@"Analyze file {fileName}"; })); - var success = _meterFile.GetFileSize(fileName, out var fileSize); - - LogText(success ? $@"File name: {fileName}, File size: {fileSize} byte" - : $@"File name: {fileName} Unable to access file"); - if (success) continue; - _currentGenesis.Logout(); - _currentGenesis.ReLogin(); - } + var fileNames = ListMeterFiles(); + AnalyzeMeterFiles(fileNames); }).ContinueWith(delegate { //add a blank line to separate next operation @@ -1221,8 +1277,121 @@ namespace Xylem.Common.Ui.GenesisToolBox LowLevelActionControl(false); }); } - #endregion + /// + /// Tidy the file system: + /// - Erasing upgrade files left over from unsuccessfully FW update, + /// - Erase logging files for versions not covered by the actual region as + /// during development a reprogramming from EMEA to NA and vice versa will + /// leave the logs for thr other version in, + /// - Remove the test file for EMEA, as this is the placeholder for the FW + /// update over the air to keep the space reserved for this process (250 kB), + /// - Keep important files listed in the log index. + /// + /// + /// - Initial + /// + private void btnTidyFile_Click(Object sender, EventArgs e) + { + if (_currentGenesis == null) + return; + if (_meterFile == null) + _meterFile = new MeterFile(_currentGenesis); + + //reset timer + _startTime = DateTimeOffset.UtcNow; + + LowLevelActionControl(true); + + var msg = @"Tidy file system"; + LogText(msg); + lblOverall.Text = msg; + + Task.Factory.StartNew(() => + { + _currentGenesis.ReLogin(); + + // catalogue before cleaning + SetActualProcessAndLog(@"List meter files before cleaning"); + + // build file erase candidates for all releases + var fileEraseCandidates = new List(); + + // put initially all files in as erase candidates + var meterFiles = ListMeterFiles(); + AnalyzeMeterFiles(meterFiles); + fileEraseCandidates.AddRange(meterFiles); + + // build list of files needed to keep + var filesToKeep = new List(); + filesToKeep.AddRange(MeterFile.FilesToKeep); + + // for EMEA the NALogs are waste + if (_currentGenesis.Region.Contains("EMEA")) + { + LogText("EMEA version detected"); + filesToKeep.AddRange(MeterFile.EmeaLogs); + } + + // for EMEA the EMEALogs are waste + if (_currentGenesis.Region.Contains("NA")) + { + LogText("NA version detected"); + filesToKeep.AddRange(MeterFile.NaLogs); + } + + // red the log files and leave it in + var logFiles = ListLogFiles(); + filesToKeep.AddRange(logFiles); + + // remove files which should be kept in place + foreach (var meterFile in meterFiles) + { + foreach (var fileToKeep in filesToKeep) + { + if (meterFile.Contains(fileToKeep)) + fileEraseCandidates.Remove(meterFile); + } + } + // check if something to clean + if (fileEraseCandidates.Count == 0) + { + LogText("File system is clean"); + } + + else + { + // erase files + foreach (var fileToErase in fileEraseCandidates) + { + Invoke(new Action(() => { lblActualProcess.Text = $@"Erase {fileToErase}"; })); + _meterFile.UnlockEraseWriteMeterFile(fileToErase); + var success = _meterFile.EraseMeterFile(fileToErase); + + LogText(success + ? $@"File {fileToErase} successfully erased" + : $@"File {fileToErase} erasure failed"); + + if (success) continue; + _currentGenesis.Logout(); + _currentGenesis.ReLogin(); + } + // catalogue after cleaning + SetActualProcessAndLog(@"List meter files after cleaning"); + ListMeterFiles(); + + LogText("File system is cleaned up"); + } + }).ContinueWith(delegate + { + //add a blank line to separate next operation + LogText(""); + _currentGenesis.Logout(); + LowLevelActionControl(false); + }); + + } + #endregion } }