Parametrization including DB request for EOL and CUST in RegisterRestorer

This commit is contained in:
Thomas Wiedebusch 2024-01-17 12:42:09 +01:00
parent c3de65047a
commit e87bfc498f
3 changed files with 182 additions and 130 deletions

View File

@ -28,7 +28,7 @@
<s:Boolean x:Key="/Default/Environment/ExcludedFiles/FilesAndFoldersToSkip/=1057AD21_002DBA22_002D4CAB_002DB3FE_002D88C5C9980E6A_002Fd_003AScripts_002Ff_003Ajquery_002D1_002E10_002E2_002Eintellisense_002Ejs/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/Environment/Hierarchy/Build/SolBuilderDuo/UseMsbuildSolutionBuilder/@EntryValue">NewVersion</s:String>
<s:Boolean x:Key="/Default/Environment/Hierarchy/EntityFrameworkOptions/IsAlreadyNotifiedAboutEntityFramework/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/Environment/Highlighting/HighlightingSourceSnapshotLocation/@EntryValue">C:\Users\Thomas\AppData\Local\Temp\JetBrains\ReSharperPlatformVs15\vAny_33007af5\CoverageData\_Common.-1885154465\Snapshot\snapshot.utdcvr</s:String>

View File

@ -1,13 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Xylem.Common.CommonCore.Configuration;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes;
using Xylem.Common.Logic.ProductionOrderCore;
using Xylem.Common.Logic.SoftwareAccessHelper;
using Xylem.Common.Utils.ProcessExec;
using Xylem.Common.Utils.ProcessExec.EventArguments;
using Access = Xylem.Common.Hardware.WaterMeter.Genesis.Registers.Access;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
{
@ -57,16 +61,16 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
public event EventHandler<ProcessExecEventArgs> OnProcessUpdate;
/// <summary>
/// List for parameters which need to be removed from parameter list
/// List for parameters which need to be removed from parameter list as they mustn't be accessed
/// </summary>
public static readonly List<String> ProhibitedParameters = new List<String>
private static readonly List<String> _prohibitedParameters = new List<String>
{
"CUSTOMER_AppArrangement"
};
/// <summary>
/// List for un-reversed parameters, all others need to be swapped byte-wise
/// </summary>
public static readonly List<String> UnRevertedParameters = new List<String>
private static readonly List<String> _unRevertedParameters = new List<String>
{
"SENSUSRADIO_EncryptionKey",
"GENESISFLOW_DisplayPow10",
@ -75,7 +79,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// <summary>
///
/// </summary>
public static readonly List<String> ExcludedFromCompareParameters = new List<String>
private static readonly List<String> _excludedFromCompareParameters = new List<String>
{
"SENSUSRADIO_UpgFWVersion" // After a FW-Update the FW version has changed and the compare will fail
};
@ -83,7 +87,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// Pre programming parameters for all devices defined here, will be filled with radio parameters
/// if meter has radio
/// </summary>
public static readonly List<ProgrammingParameters> PrepareProgramming = new List<ProgrammingParameters>
private static readonly List<ProgrammingParameters> _prepareProgramming = new List<ProgrammingParameters>
{
// The display needs to be unsealed for change of unit and resolution
new ProgrammingParameters("GENESISFLOW_SealDisplay", new Byte[] { 0x00 })
@ -92,7 +96,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// <summary>
/// Post programming parameters for radio only
/// </summary>
public static readonly List<ProgrammingParameters> FinalizeProgrammingRadioOnly = new List<ProgrammingParameters>
private static readonly List<ProgrammingParameters> _finalizeProgrammingRadioOnly = new List<ProgrammingParameters>
{
new ProgrammingParameters("LOGGER_TriggerLogFlush", new Byte[] { 0x01 }),
new ProgrammingParameters("PERIODICLOG_ResetCounter", new Byte[] { 0x00 }),
@ -105,12 +109,21 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// <summary>
/// Post programming parameters for all devices
/// </summary>
public static readonly List<ProgrammingParameters> FinalizeProgramming = new List<ProgrammingParameters>
private static readonly List<ProgrammingParameters> _finalizeProgramming = new List<ProgrammingParameters>
{
new ProgrammingParameters("GENESISFLOW_SealDisplay", new Byte[] { 0x01 }),
new ProgrammingParameters("GENESISFLOW_TriggerIdle", new Byte[] { 0x00 }),
};
/// <summary>
/// Post programming parameters for EOL (end of line, prepare for shipping workplace)
/// </summary>
private static readonly List<ProgrammingParameters> _finalizeProgrammingEol = new List<ProgrammingParameters>
{
new ProgrammingParameters("GENESISFLOW_ResetAccumulators", new Byte[] { 0x01 }),
new ProgrammingParameters("NFC_EraseRMA", new Byte[] { 0x01 })
};
/// <summary>
/// Actual Port Counter
/// </summary>
@ -147,6 +160,108 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
_currentGenesis = genesisMeter;
}
/// <summary>
/// Collect programming parameters from DB.
/// </summary>
/// <param name="pcbId"></param>
/// <param name="recoveryRegisters"></param>
/// <returns>recovery registers</returns>
/// <remarks date="2021-Feb-01" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2023-10-19" author="Thomas Wiedebusch">
/// - Sort the registers from DB but keep the PrepareProgramming and FinalizeProgramming in the required sequence.
/// </remarks>
/// <remarks date="2023-10-25" author="Thomas Wiedebusch">
/// - Removed preparation and finalization of register recovery to remove sequencing as this will be done in the
/// RegisterRestorer by the FwUpdateSw worker giving the ability to log this in the report.
/// </remarks>
/// <remarks date="2024-01-17" author="Thomas Wiedebusch">
/// - Data base access imported to have a common interface for programming parameters for EOL (shipping) and CUST.
/// </remarks>
public static Boolean DownloadProgrammingParametersFromDb(String pcbId, out List<RecoveryRegisterItem> recoveryRegisters)
{
recoveryRegisters = new List<RecoveryRegisterItem>();
try
{
var url = $"{ServiceUrls.GenesisFinalCheckServiceUrl()}GetProgrammingParameters?PcbID={pcbId}";
var ppJson = LocalWebRequest.GetRequest(url, 30000);
if (string.IsNullOrEmpty(ppJson))
{
return false;
}
var programmingParameters = JsonConvert.DeserializeObject<List<ProgrammingParameters>>(ppJson);
if (programmingParameters == null || programmingParameters.Count == 0)
{
return false;
}
// Temporary storage for sorting before assembling those registers with the preparation and
// finalization of programming and therefore building a sequence
var sortableProgrammingParameters = new List<ProgrammingParameters>();
foreach (var proPar in programmingParameters)
{
// reverse byte order for all parameters except those listed below
if (!string.IsNullOrEmpty(proPar.RegisterName))
{
// ATTENTION: These parameters are coming out of the DB and need to be removed from list
if (RegisterRestorer._prohibitedParameters.Contains(proPar.RegisterName))
{
}
// ATTENTION: These parameters are coming out of the DB in wrong order
if (RegisterRestorer._unRevertedParameters.Contains(proPar.RegisterName))
{
// skip VAKO if equally register is set in CSD or on read only access
if (proPar.Source == ProgrammingSource.Vako && sortableProgrammingParameters.Any(a =>
a.Source == ProgrammingSource.Csd && proPar.RegisterName.Equals(a.RegisterName)))
continue;
sortableProgrammingParameters.Add(proPar);
}
else
{
// skip VAKO if equally register is set in CSD or on read only access
if (proPar.Source == ProgrammingSource.Vako && sortableProgrammingParameters.Any(a =>
a.Source == ProgrammingSource.Csd && proPar.RegisterName.Equals(a.RegisterName)))
continue;
sortableProgrammingParameters.Add(new ProgrammingParameters(proPar.RegisterName,
proPar.RegisterValue.Reverse().ToArray(), proPar.Source));
}
} // register for this FW is defined
else
{
return false;
}
}
// Sort the programming parameters coming from DB (VAKO/CSD)
sortableProgrammingParameters.Sort((x, y) => string.Compare(x.RegisterName, y.RegisterName,
StringComparison.Ordinal));
// convert to RecoveryRegisterItem
if (sortableProgrammingParameters.Count > 0)
{
foreach (var proPar in sortableProgrammingParameters)
{
var recReg = new RecoveryRegisterItem
{
RegisterIdent = proPar.RegisterName,
WriteValue = proPar.RegisterValue
};
recoveryRegisters.Add(recReg);
}
}
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// Avoid doubling of register restore on subsequent FW-Update trials.
/// </summary>
@ -183,15 +298,28 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
}
/// <summary>
/// Recover all registers set from caller in the original sequence as they may prepare and finalize
/// settings.
///
/// ATTENTION:
/// Do NOT sort the recovery registers as those needed to be executed in the correct sequence,
/// as those are preparing the meter to a special mode to take the values and finalize the settings.
///
/// EOL (end of line) programming contains a reset of the accumulators. Else it is identical with recover
/// registers.
/// </summary>
/// <param name="recoveryRegisters">list of registers to update</param>
/// <param name="recoveryRegistersDb">list of registers to update from data base without special sequence
/// </param>
/// <returns>true if setup succeeded</returns>
/// <remarks date="2024-Jan-17" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public Boolean EolProgrammingRegisters(List<RecoveryRegisterItem> recoveryRegistersDb)
{
// mark EOL as true to force Accu reset
return RecoverRegisters(recoveryRegistersDb, true);
}
/// <summary>
/// Recover all registers set from caller. Additional preparation and finalization will be added.
/// settings.
/// </summary>
/// <param name="recoveryRegistersDb">list of registers to update from data base without special sequence
/// </param>
/// <param name="eolProgramming">EOL (end of line) programming with accu reset</param>
/// <returns>true if restoring succeeded</returns>
/// <remarks date="2023-Oct-09" author="Thomas Wiedebusch">
/// - Initial.
@ -210,16 +338,21 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// <remarks date="2023-Dec-11" author="Thomas Wiedebusch">
/// - Logging of some exception messages.
/// </remarks>
public Boolean RecoverRegisters(List<RecoveryRegisterItem> recoveryRegisters)
/// <remarks date="2024-Jan-17" author="Thomas Wiedebusch">
/// - EOL sequence for accumulators reset,
/// - Remind pcbId as already recovered before the true return,
/// - Exclude lock of repeated execution for EOL.
/// </remarks>
public Boolean RecoverRegisters(List<RecoveryRegisterItem> recoveryRegistersDb, Boolean eolProgramming = false)
{
if (_currentGenesis == null ||
string.IsNullOrEmpty(_currentGenesis.PcbId) ||
recoveryRegisters == null ||
recoveryRegisters.Count == 0)
recoveryRegistersDb == null ||
recoveryRegistersDb.Count == 0)
return false;
// if already recovered skip this with positive feedback
if (_currentGenesis.PcbId == _pcbIdRecoveryRegisters)
// if not EOL processing and already recovered skip this with positive feedback
if (!eolProgramming && _currentGenesis.PcbId == _pcbIdRecoveryRegisters)
return true;
// remind pcbId of this run to avoid endless repetition
@ -238,21 +371,25 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
// prepare list for finalization depending on SENSUSRADIO installation
var finalizeProgrammingAll = new List<ProgrammingParameters>();
finalizeProgrammingAll.AddRange(FinalizeProgramming);
if (recoveryRegisters.Any(x => x.RegisterIdent.Contains("SENSUSRADIO")))
if (recoveryRegistersDb.Any(x => x.RegisterIdent.Contains("SENSUSRADIO")))
{
finalizeProgrammingAll.AddRange(FinalizeProgrammingRadioOnly);
finalizeProgrammingAll.AddRange(_finalizeProgrammingRadioOnly);
}
// add EOL sequence if called
if (eolProgramming)
finalizeProgrammingAll.AddRange(_finalizeProgrammingEol);
// standard end of programming sequence
finalizeProgrammingAll.AddRange(_finalizeProgramming);
// set the process counter
var recRegistersCount = recoveryRegisters.Count > 0 ? recoveryRegisters.Count : 1;
recRegistersCount += PrepareProgramming.Count;
var recRegistersCount = recoveryRegistersDb.Count > 0 ? recoveryRegistersDb.Count : 1;
recRegistersCount += _prepareProgramming.Count;
recRegistersCount += finalizeProgrammingAll.Count;
// prepare recovery
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(Resources.StrRegisterRecoveryPreparation,
actualProcessMessage: Resources.StrRegisterRecoveryPreparation));
foreach (var prepRecReg in PrepareProgramming)
foreach (var prepRecReg in _prepareProgramming)
{
ActualRegisterCtr++;
var overallProcessCtrPercent = 100.0 * ActualRegisterCtr / recRegistersCount;
@ -294,7 +431,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
// register recovery
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(Resources.StrRegisterRecoveryExec,
actualProcessMessage: Resources.StrRegisterRecoveryExec));
foreach (var recReg in recoveryRegisters)
foreach (var recReg in recoveryRegistersDb)
{
ActualRegisterCtr++;
var overallProcessCtrPercent = 100.0 * ActualRegisterCtr / recRegistersCount;
@ -378,6 +515,9 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
}
}// finalize recovery
// remind pcbId of this run to avoid endless repetition
_pcbIdRecoveryRegisters = _currentGenesis.PcbId;
_currentGenesis?.Logout();
return true;
}
@ -397,7 +537,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// "StaticType": “approximate”.
/// </remarks>
/// <remarks date="2023-Dec-08" author="Thomas Wiedebusch">
/// - Removed <see cref="ExcludedFromCompareParameters"/> as those will change on e.g. FW update.
/// - Removed <see cref="_excludedFromCompareParameters"/> as those will change on e.g. FW update.
/// </remarks>
/// <remarks date="2023-Dec-11" author="Thomas Wiedebusch">
/// - Avoid message event if message is empty.
@ -416,7 +556,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
// build a string list of the registers being able to sort those
var registerNames = _restoreRegisters.MeterRegisterDic.Select(register => register.Key.GetIdent()).ToList();
// Remove all excluded registers
foreach (var regName in ExcludedFromCompareParameters.Where(regName => registerNames.Any(reg => reg == regName)))
foreach (var regName in _excludedFromCompareParameters.Where(regName => registerNames.Any(reg => reg == regName)))
{
registerNames.Remove(regName);
}
@ -442,7 +582,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
// take the size into account as a read value will be filled up to a chunk of 4 bytes
var dataSize = RegisterConverter.SizeOf(regDef);
if (restorableRawRegister == null)
continue;
@ -499,7 +639,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
if (restorableRawRegister[c] != finalReadRawRegister[c])
{
isIdentical = false;
break;
break;
}
isIdentical = true;
}
@ -581,7 +721,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
if (_currentGenesis == null)
return false;
_pcbIdFinalRegisterRead = _currentGenesis.PcbId;
_pcbIdFinalRegisterRead = _currentGenesis.PcbId;
// clear the contents of the restore registers if the water meter backed up was not identical
if (_pcbIdInitialRegisterRead != _pcbIdFinalRegisterRead)
{

View File

@ -13,7 +13,6 @@ using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Logic.ProductionOrderCore;
using Xylem.Common.Logic.ProductionOrderCore.FW;
using Xylem.Common.Logic.SoftwareAccessHelper;
using Xylem.Common.Utils.DateTimeServer;
@ -35,14 +34,6 @@ namespace Xylem.ServiceFwUpdate.Common.FwUpdateDb
get; private set;
}
/// <summary>
/// Programming parameters from DB.
/// </summary>
public List<ProgrammingParameters> DbProgrammingParameters
{
get; private set;
}
/// <summary>
/// Complete information of all builder operators registered in DB.
/// </summary>
@ -255,6 +246,7 @@ namespace Xylem.ServiceFwUpdate.Common.FwUpdateDb
return false;
}
}
/// <summary>
/// Collect programming parameters from DB.
/// </summary>
@ -271,99 +263,19 @@ namespace Xylem.ServiceFwUpdate.Common.FwUpdateDb
/// - Removed preparation and finalization of register recovery to remove sequencing as this will be done in the
/// RegisterRestorer by the FwUpdateSw worker giving the ability to log this in the report.
/// </remarks>
/// <remarks date="2024-01-17" author="Thomas Wiedebusch">
/// - Use common interface for programming parameters for EOL (shipping) and CUST from <see cref="RegisterRestorer"/>.
/// </remarks>
public Boolean DownloadCordonelRecoveryRegistersFromDb(String pcbId, out List<RecoveryRegisterItem> recoveryRegisters)
{
recoveryRegisters = new List<RecoveryRegisterItem>();
try
{
var url = $"{ServiceUrls.GenesisFinalCheckServiceUrl()}GetProgrammingParameters?PcbID={pcbId}";
var csdRet = LocalWebRequest.GetRequest(url, 30000);
if (string.IsNullOrEmpty(csdRet))
{
DbIsConnected = false;
return false;
}
var programmingParameters = JsonConvert.DeserializeObject<List<ProgrammingParameters>>(csdRet);
if (programmingParameters == null || programmingParameters.Count == 0)
{
return false;
}
DbIsConnected = true;
// Temporary storage for sorting before assembling those registers with the preparation and
// finalization of programming and therefore building a sequence
var sortableProgrammingParameters = new List<ProgrammingParameters>();
foreach (var proPar in programmingParameters)
{
// reverse byte order for all parameters except those listed below
if (!string.IsNullOrEmpty(proPar.RegisterName))
{
// ATTENTION: These parameters are coming out of the DB and need to be removed from list
if (RegisterRestorer.ProhibitedParameters.Contains(proPar.RegisterName))
{
}
// ATTENTION: These parameters are coming out of the DB in wrong order
if (RegisterRestorer.UnRevertedParameters.Contains(proPar.RegisterName))
{
// skip VAKO if equally register is set in CSD or on read only access
if (proPar.Source == ProgrammingSource.Vako && sortableProgrammingParameters.Any(a =>
a.Source == ProgrammingSource.Csd && proPar.RegisterName.Equals(a.RegisterName)))
continue;
sortableProgrammingParameters.Add(proPar);
}
else
{
// skip VAKO if equally register is set in CSD or on read only access
if (proPar.Source == ProgrammingSource.Vako && sortableProgrammingParameters.Any(a =>
a.Source == ProgrammingSource.Csd && proPar.RegisterName.Equals(a.RegisterName)))
continue;
sortableProgrammingParameters.Add(new ProgrammingParameters(proPar.RegisterName,
proPar.RegisterValue.Reverse().ToArray(), proPar.Source));
}
} // register for this FW is defined
else
{
return false;
}
}
// Sort the programming parameters coming from DB (VAKO/CSD)
sortableProgrammingParameters.Sort(( x,y) => string.Compare(x.RegisterName, y.RegisterName,
StringComparison.Ordinal));
// Clean or create new programming parameters
DbProgrammingParameters?.Clear();
if (DbProgrammingParameters == null)
DbProgrammingParameters = new List<ProgrammingParameters>();
// Add the settings which should be dispatched to the meter to reconfigure it
DbProgrammingParameters.AddRange(sortableProgrammingParameters);
// convert to RecoveryRegisterItem
if (DbProgrammingParameters.Count > 0)
{
foreach (var proPar in DbProgrammingParameters)
{
var recReg = new RecoveryRegisterItem
{
RegisterIdent = proPar.RegisterName,
WriteValue = proPar.RegisterValue
};
recoveryRegisters.Add(recReg);
}
}
return true;
}
catch (Exception)
if (!RegisterRestorer.DownloadProgrammingParametersFromDb(pcbId, out recoveryRegisters))
{
DbIsConnected = false;
return false;
}
DbIsConnected = true;
return true;
}
/// <summary>
@ -908,7 +820,7 @@ namespace Xylem.ServiceFwUpdate.Common.FwUpdateDb
DbIsConnected = true;
DbCordonelCustomerOrders =
JsonConvert.DeserializeObject<List<CordonelCustomerOrderDb>>(requestResponse);
DbCordonelCustomerOrders?.Sort( (a, b) => a.CustomerOrderNumber.CompareTo(b.CustomerOrderNumber));
DbCordonelCustomerOrders?.Sort((a, b) => a.CustomerOrderNumber.CompareTo(b.CustomerOrderNumber));
return DbCordonelCustomerOrders != null && DbCordonelCustomerOrders.Count != 0;
}
catch (Exception)