merge
This commit is contained in:
@@ -14,5 +14,5 @@ using System.Reflection;
|
||||
//[assembly: AssemblyVersion("1.2.*.0")]
|
||||
|
||||
|
||||
[assembly: AssemblyVersion("2.6.6.*")]
|
||||
[assembly: AssemblyFileVersion("2.6.6.0")]
|
||||
[assembly: AssemblyVersion("2.6.8.*")]
|
||||
[assembly: AssemblyFileVersion("2.6.8.0")]
|
||||
@@ -155,7 +155,7 @@
|
||||
</site>
|
||||
<site name="MeterProcessState" id="2">
|
||||
<application path="/" applicationPool="Clr4IntegratedAppPool">
|
||||
<virtualDirectory path="/" physicalPath="C:\Users\drabesch_ro\Repo\lab\la_operations\laa_production\Common\Service\MeterProcessState" />
|
||||
<virtualDirectory path="/" physicalPath="C:\Users\SZLATEV\source\repos\laa_production\Common\Service\MeterProcessState" />
|
||||
</application>
|
||||
<bindings>
|
||||
<binding protocol="http" bindingInformation="*:56011:localhost" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
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
|
||||
{
|
||||
@@ -24,49 +29,47 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
/// <summary>
|
||||
/// PCB ID for pre update read for comparison
|
||||
/// </summary>
|
||||
private String _pcbIdInitialRegisterRead = "X";
|
||||
private String _pcbIdInitialRegisterRead = Constants.StrUnknown;
|
||||
|
||||
/// <summary>
|
||||
/// PCB ID for pre update write for comparison
|
||||
/// </summary>
|
||||
private String _pcbIdRecoveryRegisters = "X";
|
||||
private String _pcbIdRecoveryRegisters = Constants.StrUnknown;
|
||||
|
||||
/// <summary>
|
||||
/// PCB ID for post update for comparison
|
||||
/// </summary>
|
||||
private String _pcbIdFinalRegisterRead = Constants.StrUnknown;
|
||||
|
||||
/// <summary>
|
||||
/// Collection of recovery registers and values
|
||||
/// </summary>
|
||||
public List<RecoveryRegisterItem> RegistersSetup;
|
||||
|
||||
/// <summary>
|
||||
/// Register content on initial connect to keep those during update
|
||||
/// </summary>
|
||||
private readonly MeterRegisters _initialReadRegisters = new MeterRegisters();
|
||||
private MeterRegisters _initialReadRegisters = new MeterRegisters();
|
||||
|
||||
/// <summary>
|
||||
/// Register content after all operation had been performed
|
||||
/// </summary>
|
||||
private readonly MeterRegisters _finalReadRegisters = new MeterRegisters();
|
||||
private MeterRegisters _finalReadRegisters = new MeterRegisters();
|
||||
|
||||
/// <summary>
|
||||
/// Register which shall be restored after change of FW or after register recovery
|
||||
/// </summary>
|
||||
private readonly MeterRegisters _restoreRegisters = new MeterRegisters();
|
||||
private readonly MeterRegisters _recoveryRegisters = new MeterRegisters();
|
||||
|
||||
/// <summary>
|
||||
/// Register access event for message dispatcher to caller
|
||||
/// </summary>
|
||||
public event EventHandler<ProcessExecEventArgs> OnProcessUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// List for parameters which need to be removed from parameter list
|
||||
/// </summary>
|
||||
public 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,44 +78,72 @@ 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
|
||||
"SENSUSRADIO_UpgFWVersion" // After a FW-Update the FW version has changed and the compare will fail
|
||||
};
|
||||
/// <summary>
|
||||
/// 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<RecoveryRegisterItem> _prepareProgramming = new List<RecoveryRegisterItem>
|
||||
{
|
||||
// the display needs to be sealed for change of unit and resolution
|
||||
new ProgrammingParameters("GENESISFLOW_SealDisplay", new Byte[] { 0x00 })
|
||||
// The display needs to be unsealed for change of unit and resolution
|
||||
new RecoveryRegisterItem("GENESISFLOW_SealDisplay", new Byte[] { 0x00 })
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
// ReSharper disable once CollectionNeverQueried.Local IS FOR DEBUG
|
||||
private readonly List<RecoveryRegisterItem> _failedComparisonRegisters = new List<RecoveryRegisterItem>();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
// ReSharper disable once CollectionNeverQueried.Local IS FOR DEBUG
|
||||
private List<String> _dbParameterRegisterList = new List<String>();
|
||||
private readonly List<String> _recoveryRegisterList = new List<String>();
|
||||
private List<String> _initialRegisterList = new List<String>();
|
||||
private List<String> _finalRegisterList = new List<String>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Post programming parameters for radio only
|
||||
/// </summary>
|
||||
public static readonly List<ProgrammingParameters> FinalizeProgrammingRadioOnly = new List<ProgrammingParameters>
|
||||
private static readonly List<RecoveryRegisterItem> _finalizeProgrammingRadioOnly = new List<RecoveryRegisterItem>
|
||||
{
|
||||
new ProgrammingParameters("LOGGER_TriggerLogFlush", new Byte[] { 0x01 }),
|
||||
new ProgrammingParameters("PERIODICLOG_ResetCounter", new Byte[] { 0x00 }),
|
||||
new RecoveryRegisterItem("LOGGER_TriggerLogFlush", new Byte[] { 0x01 }),
|
||||
// set counter to 0 as this is a value how often the radio detected a reset
|
||||
new RecoveryRegisterItem("PERIODICLOG_ResetCounter", new Byte[] { 0x00 }),
|
||||
// System state 0xFF will force a storage of all parameters and reset, followed by system state 0x02
|
||||
// for customer mode, the reset needs only a few milliseconds
|
||||
// Attention logout-delay-login required after this step!
|
||||
new ProgrammingParameters("SENSUSRADIO_SystemState", new Byte[] { 0xFF })
|
||||
new RecoveryRegisterItem("SENSUSRADIO_SystemState", new Byte[] { 0xFF })
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Post programming parameters for all devices
|
||||
/// </summary>
|
||||
public static readonly List<ProgrammingParameters> FinalizeProgramming = new List<ProgrammingParameters>
|
||||
private static readonly List<RecoveryRegisterItem> _finalizeProgramming = new List<RecoveryRegisterItem>
|
||||
{
|
||||
new ProgrammingParameters("GENESISFLOW_SealDisplay", new Byte[] { 0x01 }),
|
||||
new ProgrammingParameters("GENESISFLOW_TriggerIdle", new Byte[] { 0x00 }),
|
||||
new RecoveryRegisterItem("GENESISFLOW_SealDisplay", new Byte[] { 0x01 }),
|
||||
// trigger idle 0 releases the display to normal operation
|
||||
new RecoveryRegisterItem("GENESISFLOW_TriggerIdle", new Byte[] { 0x00 }),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Actual Port Counter
|
||||
/// Post programming parameters for EOL (end of line, prepare for shipping workplace)
|
||||
/// </summary>
|
||||
private static readonly List<RecoveryRegisterItem> _finalizeProgrammingEol = new List<RecoveryRegisterItem>
|
||||
{
|
||||
new RecoveryRegisterItem("GENESISFLOW_ResetAccumulators", new Byte[] { 0x01 }),
|
||||
new RecoveryRegisterItem("NFC_EraseRMA", new Byte[] { 0x01 }),
|
||||
new RecoveryRegisterItem("CUSTOMER_RebootCount", new Byte[] {0x00})
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Actual register counter
|
||||
/// </summary>
|
||||
public Int32 ActualRegisterCtr;
|
||||
|
||||
@@ -147,6 +178,122 @@ 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>
|
||||
/// <remarks date="2024-01-29" author="Thomas Wiedebusch">
|
||||
/// - Removed prohibited parameters.
|
||||
/// </remarks>
|
||||
public static Boolean DownloadProgrammingParametersFromDb(String pcbId, List<RecoveryRegisterItem> recoveryRegisters)
|
||||
{
|
||||
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 in wrong order
|
||||
if (_unRevertedParameters.Contains(proPar.RegisterName))
|
||||
{
|
||||
// Add parameter if it is not in the list
|
||||
if (sortableProgrammingParameters.All(a => !proPar.RegisterName.Equals(a.RegisterName)))
|
||||
{
|
||||
sortableProgrammingParameters.Add(proPar);
|
||||
continue;
|
||||
}
|
||||
// if parameter from VAKO is in the list and the new is from CSD, replace this with CSD
|
||||
if (proPar.Source == ProgrammingSource.Csd && sortableProgrammingParameters.Any(a =>
|
||||
proPar.RegisterName.Equals(a.RegisterName) && a.Source == ProgrammingSource.Vako))
|
||||
{
|
||||
var removeProPar =
|
||||
sortableProgrammingParameters.Where(a => proPar.RegisterName.Equals(a.RegisterName));
|
||||
sortableProgrammingParameters.Remove(removeProPar.LastOrDefault());
|
||||
sortableProgrammingParameters.Add(proPar);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add parameter if it is not in the list
|
||||
if (sortableProgrammingParameters.All(a => !proPar.RegisterName.Equals(a.RegisterName)))
|
||||
{
|
||||
sortableProgrammingParameters.Add(new ProgrammingParameters(proPar.RegisterName,
|
||||
proPar.RegisterValue.Reverse().ToArray(), proPar.Source));
|
||||
continue;
|
||||
}
|
||||
// if parameter from VAKO is in the list and the new is from CSD, replace this with CSD
|
||||
if (proPar.Source == ProgrammingSource.Csd && sortableProgrammingParameters.Any(a =>
|
||||
proPar.RegisterName.Equals(a.RegisterName) && a.Source == ProgrammingSource.Vako))
|
||||
{
|
||||
var removeProPar = sortableProgrammingParameters.Where(a => proPar.RegisterName.Equals(a.RegisterName));
|
||||
sortableProgrammingParameters.Remove(removeProPar.LastOrDefault());
|
||||
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(proPar.RegisterName, 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 +330,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 +370,25 @@ 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>
|
||||
/// <remarks date="2024-Jan-29" author="Thomas Wiedebusch">
|
||||
/// - Avoid writing of parameters if not writable. The DB delivers parameters which exclusively have to be
|
||||
/// compared to the specified value.
|
||||
/// </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
|
||||
@@ -228,6 +397,11 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
StopRegisterAccess = false;
|
||||
ActualRegisterCtr = 0;
|
||||
|
||||
// Build string list for debug
|
||||
_dbParameterRegisterList.Clear();
|
||||
_dbParameterRegisterList = recoveryRegistersDb.Select(register => register.RegisterIdent).ToList();
|
||||
_dbParameterRegisterList.Sort();
|
||||
|
||||
// write values to Genesis
|
||||
_currentGenesis.ReLogin();
|
||||
if (!_currentGenesis.IsLoggedOn)
|
||||
@@ -237,37 +411,44 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
var meterRegisters = _currentGenesis.GetConfigRegistersDefinitions();
|
||||
|
||||
// prepare list for finalization depending on SENSUSRADIO installation
|
||||
var finalizeProgrammingAll = new List<ProgrammingParameters>();
|
||||
finalizeProgrammingAll.AddRange(FinalizeProgramming);
|
||||
if (recoveryRegisters.Any(x => x.RegisterIdent.Contains("SENSUSRADIO")))
|
||||
var finalizeProgrammingAll = new List<RecoveryRegisterItem>();
|
||||
if (recoveryRegistersDb.Any(x => x.RegisterIdent.Contains("SENSUSRADIO")))
|
||||
{
|
||||
finalizeProgrammingAll.AddRange(FinalizeProgrammingRadioOnly);
|
||||
finalizeProgrammingAll.AddRange(_finalizeProgrammingRadioOnly);
|
||||
}
|
||||
// add EOL (shipping reset of accumulators, alarms and setup of manufacturing date)
|
||||
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;
|
||||
|
||||
try
|
||||
{
|
||||
_currentGenesis.WriteRegister(prepRecReg.RegisterName, prepRecReg.RegisterValue);
|
||||
_currentGenesis.WriteRegister(prepRecReg.RegisterIdent, prepRecReg.WriteValue);
|
||||
|
||||
// if this call does not throw an exception, the register is present
|
||||
var regDef = meterRegisters.GetRegisterDefinitionByName(prepRecReg.RegisterName);
|
||||
var regDef = meterRegisters.GetRegisterDefinitionByName(prepRecReg.RegisterIdent);
|
||||
|
||||
var strRawAndConvertedValue = "";
|
||||
try
|
||||
{
|
||||
strRawAndConvertedValue = RegisterConverter.GetRegisterContentText(regDef, prepRecReg.RegisterValue);
|
||||
strRawAndConvertedValue = RegisterConverter.GetRegisterContentText(regDef, prepRecReg.WriteValue);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -294,29 +475,33 @@ 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;
|
||||
|
||||
try
|
||||
{
|
||||
_currentGenesis.WriteRegister(recReg.RegisterIdent, recReg.WriteValue);
|
||||
|
||||
// if this call does not throw an exception, the register is present
|
||||
var regDef = meterRegisters.GetRegisterDefinitionByName(recReg.RegisterIdent);
|
||||
|
||||
// avoid register write if not allowed
|
||||
if (regDef.RegisterDetail.Privilege.Lvl8 != Access.RW)
|
||||
continue;
|
||||
|
||||
_currentGenesis.WriteRegister(recReg.RegisterIdent, recReg.WriteValue);
|
||||
|
||||
var strRawAndConvertedValue = "";
|
||||
try
|
||||
{
|
||||
strRawAndConvertedValue = RegisterConverter.GetRegisterContentText(regDef, recReg.WriteValue);
|
||||
// replace value in _restoreRegister with the required recovery value
|
||||
_restoreRegisters?.Set(regDef, recReg.WriteValue);
|
||||
_recoveryRegisters?.Set(regDef, recReg.WriteValue);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_currentGenesis.WriteLog("Could not replace register value of " + recReg.RegisterIdent + "; " + ex.Message);
|
||||
// nothing to do
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -346,15 +531,15 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
|
||||
try
|
||||
{
|
||||
_currentGenesis.WriteRegister(finalRecReg.RegisterName, finalRecReg.RegisterValue);
|
||||
_currentGenesis.WriteRegister(finalRecReg.RegisterIdent, finalRecReg.WriteValue);
|
||||
|
||||
// if this call does not throw an exception, the register is present
|
||||
var regDef = meterRegisters.GetRegisterDefinitionByName(finalRecReg.RegisterName);
|
||||
var regDef = meterRegisters.GetRegisterDefinitionByName(finalRecReg.RegisterIdent);
|
||||
|
||||
var strRawAndConvertedValue = "";
|
||||
try
|
||||
{
|
||||
strRawAndConvertedValue = RegisterConverter.GetRegisterContentText(regDef, finalRecReg.RegisterValue);
|
||||
strRawAndConvertedValue = RegisterConverter.GetRegisterContentText(regDef, finalRecReg.WriteValue);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -378,7 +563,14 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
}
|
||||
}// finalize recovery
|
||||
|
||||
// remind pcbId of this run to avoid endless repetition
|
||||
_pcbIdRecoveryRegisters = _currentGenesis.PcbId;
|
||||
|
||||
_currentGenesis?.Logout();
|
||||
|
||||
// Wait for completion as storing needs a certain time
|
||||
Thread.Sleep(1000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -397,26 +589,30 @@ 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.
|
||||
/// </remarks>
|
||||
/// <remarks date="2024-Jan-23" author="Thomas Wiedebusch">
|
||||
/// - Remind registers which failed the comparison including values.
|
||||
/// </remarks>
|
||||
public Boolean CompareRegisters()
|
||||
{
|
||||
if (_pcbIdFinalRegisterRead != _pcbIdInitialRegisterRead ||
|
||||
_restoreRegisters == null ||
|
||||
_recoveryRegisters == null ||
|
||||
_finalReadRegisters == null)
|
||||
return false;
|
||||
|
||||
StopRegisterAccess = false;
|
||||
ActualRegisterCtr = 0;
|
||||
var retVal = true;
|
||||
_failedComparisonRegisters?.Clear();
|
||||
|
||||
// build a string list of the registers being able to sort those
|
||||
var registerNames = _restoreRegisters.MeterRegisterDic.Select(register => register.Key.GetIdent()).ToList();
|
||||
var registerNames = _recoveryRegisters.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);
|
||||
}
|
||||
@@ -430,19 +626,19 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
{
|
||||
ActualRegisterCtr++;
|
||||
|
||||
var regDef = _restoreRegisters.GetRegisterDefinitionByName(regName);
|
||||
var regDef = _recoveryRegisters.GetRegisterDefinitionByName(regName);
|
||||
|
||||
var overallProcessCtrPercent = 100.0 * ActualRegisterCtr / registersCount;
|
||||
|
||||
try
|
||||
{
|
||||
// Get the raw byte array of this register
|
||||
var restorableRawRegister = _restoreRegisters.Get(regName);
|
||||
var restorableRawRegister = _recoveryRegisters.Get(regName);
|
||||
var finalReadRawRegister = _finalReadRegisters.Get(regName);
|
||||
|
||||
// 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 +695,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
if (restorableRawRegister[c] != finalReadRawRegister[c])
|
||||
{
|
||||
isIdentical = false;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
isIdentical = true;
|
||||
}
|
||||
@@ -521,6 +717,8 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
|
||||
strRequiredRegisterValue += $" - {Resources.StrMeterValue}: ({strReadBackRegisterValue})" +
|
||||
$" - {Resources.StrRegisterCompareFailed}";
|
||||
_failedComparisonRegisters?.Add(new RecoveryRegisterItem(regName, restorableRawRegister,
|
||||
finalReadRawRegister));
|
||||
retVal = false;
|
||||
}
|
||||
}
|
||||
@@ -581,30 +779,26 @@ 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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the pre-defined dictionary
|
||||
var meterRegisters = _currentGenesis.GetConfigRegistersDefinitions();
|
||||
|
||||
// extract all registers which can be read and create two dictionaries for before update to safe program
|
||||
// internally the register content for comparison ability.
|
||||
foreach (var reg in meterRegisters.MeterRegisterDic.Where(r => r.Key.RegisterDetail?.Version.First != null
|
||||
&& r.Key.RegisterDetail.Version.Last != null && r.Key.RestoreCapability.StaticTypeValue != null
|
||||
&& r.Key.DataType != typeof(Rpc) && r.Key.IsAvailable &&
|
||||
(r.Key.RegisterDetail.Privilege.Lvl8 == Access.RW || r.Key.RegisterDetail.Privilege.Lvl8 == Access.RO)))
|
||||
{
|
||||
_finalReadRegisters.MeterRegisterDic.TryAdd(reg.Key, reg.Value);
|
||||
}
|
||||
// create a dictionary for final read being able to compare those with the initial read and restored
|
||||
if (!BuildReadableRegisterDictionary(ref _finalReadRegisters))
|
||||
return false;
|
||||
|
||||
// log to report file the after update register read string
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs($@"{Resources.StrReadRegister}", 0,
|
||||
Resources.StrReadRegistersAfterUpdate));
|
||||
|
||||
// Build string list for debug
|
||||
_finalRegisterList.Clear();
|
||||
_finalRegisterList = _finalReadRegisters.MeterRegisterDic.Select(register => register.Key.GetIdent()).ToList();
|
||||
_finalRegisterList.Sort();
|
||||
|
||||
// executes login read logout
|
||||
return ReadRegistersSet(_finalReadRegisters);
|
||||
}
|
||||
@@ -645,13 +839,83 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
// deny update of registers if already done
|
||||
if (_pcbIdInitialRegisterRead == _currentGenesis.PcbId &&
|
||||
_initialReadRegisters.MeterRegisterDic.Count > 0 &&
|
||||
_restoreRegisters.MeterRegisterDic.Count > 0)
|
||||
_recoveryRegisters.MeterRegisterDic.Count > 0)
|
||||
return true;
|
||||
|
||||
// remind the PCB ID before update for comparison
|
||||
_pcbIdInitialRegisterRead = _currentGenesis.PcbId;
|
||||
_initialReadRegisters.MeterRegisterDic.Clear();
|
||||
_restoreRegisters.MeterRegisterDic.Clear();
|
||||
|
||||
// create the dictionaries out of the configuration.json
|
||||
if (!BuildInitialDictionaries())
|
||||
return false;
|
||||
|
||||
// log to report file the initial register read string
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs($@"{Resources.StrReadRegister}", 0,
|
||||
Resources.StrReadRegistersBeforeUpdate));
|
||||
|
||||
return ReadRegistersSet(_initialReadRegisters, _recoveryRegisters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get DEFINED registers from GenesisMeter class and build a list all registers which are flagged
|
||||
/// with read write (RW) or read only (RO). Build a list of all registers being able to restore flagged
|
||||
/// with <see cref="StaticType"/> equals <see cref="StaticType.RestoreRequired"/>".
|
||||
/// The defined registers are based on the "configuration.json" and the installed application with
|
||||
/// a specific version.
|
||||
/// </summary>
|
||||
/// <returns>true if registers could be read</returns>
|
||||
/// <remarks date="2024-Jan-22" author="Thomas Wiedebusch">
|
||||
/// - Initial from <see cref="InitialReadRegisters"/>.
|
||||
/// </remarks>
|
||||
private Boolean BuildInitialDictionaries()
|
||||
{
|
||||
if (_currentGenesis == null)
|
||||
return false;
|
||||
|
||||
_recoveryRegisters.MeterRegisterDic.Clear();
|
||||
_recoveryRegisterList.Clear();
|
||||
|
||||
// create a dictionary for initial read being able to compare those with the final read and restored
|
||||
if (!BuildReadableRegisterDictionary(ref _initialReadRegisters))
|
||||
return false;
|
||||
|
||||
// take the pre-selected registers for final compare restore capability check even if the registers after
|
||||
// the update may have changed, only registers which are present before the update can be restored!
|
||||
foreach (var reg in _initialReadRegisters.MeterRegisterDic.Where(r =>
|
||||
r.Key.RestoreCapability.CheckRestoreCapability() &&
|
||||
r.Key.RegisterDetail.Privilege.Lvl8 == Access.RW))
|
||||
{
|
||||
_recoveryRegisters.MeterRegisterDic.TryAdd(reg.Key, reg.Value);
|
||||
_recoveryRegisterList.Add(reg.Key.GetIdent());
|
||||
}
|
||||
_recoveryRegisterList.Sort();
|
||||
|
||||
// Build string list for debug
|
||||
_initialRegisterList.Clear();
|
||||
_initialRegisterList = _initialReadRegisters.MeterRegisterDic.Select(register => register.Key.GetIdent()).ToList();
|
||||
_initialRegisterList.Sort();
|
||||
|
||||
return _recoveryRegisters.MeterRegisterDic.Count > 0 && _initialReadRegisters.MeterRegisterDic.Count > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get DEFINED registers from GenesisMeter class and build a list all registers which are flagged
|
||||
/// with read write (RW) or read only (RO).
|
||||
/// The defined registers are based on the "configuration.json" and the installed application with
|
||||
/// a specific version.
|
||||
/// </summary>
|
||||
/// <para name = "registers">all readable registers dictionary creation </para>
|
||||
/// <returns>true if register dictionary is not empty</returns>
|
||||
/// <returns>true if registers could be read</returns>
|
||||
/// <remarks date="2024-Jan-22" author="Thomas Wiedebusch">
|
||||
/// - Initial from <see cref="InitialReadRegisters"/>.
|
||||
/// </remarks>
|
||||
private Boolean BuildReadableRegisterDictionary(ref MeterRegisters registers)
|
||||
{
|
||||
if (_currentGenesis == null || registers?.MeterRegisterDic == null)
|
||||
return false;
|
||||
|
||||
registers.MeterRegisterDic.Clear();
|
||||
|
||||
// get the pre-defined dictionary
|
||||
var meterRegisters = _currentGenesis.GetConfigRegistersDefinitions();
|
||||
@@ -663,23 +927,10 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
||||
&& r.Key.DataType != typeof(Rpc) && r.Key.IsAvailable &&
|
||||
(r.Key.RegisterDetail.Privilege.Lvl8 == Access.RW || r.Key.RegisterDetail.Privilege.Lvl8 == Access.RO)))
|
||||
{
|
||||
_initialReadRegisters.MeterRegisterDic.TryAdd(reg.Key, reg.Value);
|
||||
registers.MeterRegisterDic.TryAdd(reg.Key, reg.Value);
|
||||
}
|
||||
|
||||
// take the pre-selected registers for final compare restore capability check even if the registers after
|
||||
// the update may have changed, only registers which are present before the update can be restored!
|
||||
foreach (var reg in _initialReadRegisters.MeterRegisterDic.Where(r =>
|
||||
r.Key.RestoreCapability.CheckRestoreCapability() &&
|
||||
r.Key.RegisterDetail.Privilege.Lvl8 == Access.RW))
|
||||
{
|
||||
_restoreRegisters.MeterRegisterDic.TryAdd(reg.Key, reg.Value);
|
||||
}
|
||||
|
||||
// log to report file the initial register read string
|
||||
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs($@"{Resources.StrReadRegister}", 0,
|
||||
Resources.StrReadRegistersBeforeUpdate));
|
||||
|
||||
return ReadRegistersSet(_initialReadRegisters, _restoreRegisters);
|
||||
return registers.MeterRegisterDic.Count > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -412,12 +412,16 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol
|
||||
/// <remarks date="2023-Sep-01" author="T.Wiedebusch">
|
||||
/// - HideDataInLog.
|
||||
/// </remarks>
|
||||
/// <remarks date="2024-Jan-24" author="T.Wiedebusch">
|
||||
/// - Ignore wakeup if message already acknowledged (avoid to set
|
||||
/// "_recordInProcess.Acknowledge = RequestAcknowledgeState.NotDecoded"),
|
||||
/// - Avoid to overwrite "_recordInProcess.Acknowledge = RequestAcknowledgeState.Acknowledge" with
|
||||
/// "RequestAcknowledgeState.WakeupMessage".
|
||||
/// </remarks>
|
||||
protected override void DecodeRecord(IPortDataEventArgs data)
|
||||
{
|
||||
if (_recordInProcess == null)
|
||||
return;
|
||||
//new request has to be analyzed
|
||||
_recordInProcess.Acknowledge = RequestAcknowledgeState.NotDecoded;
|
||||
//the request response record covers the entire request protocol
|
||||
var responseRecord = GetTransmitProtocol().DecodeDataForLogicLayer(_ident, (List<Byte>)data.GetData(),
|
||||
_recordInProcess.HideDataInLog);
|
||||
@@ -433,9 +437,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol
|
||||
return;
|
||||
}
|
||||
|
||||
//avoid wakeup retry on acknowledged record
|
||||
if (responseRecord.Count == WakeupMessage.Length
|
||||
&& RequestAcknowledgeState.Ok != _recordInProcess.Acknowledge)
|
||||
if (responseRecord.Count == WakeupMessage.Length)
|
||||
{
|
||||
// check for wakeup message
|
||||
var wakeUp = true;
|
||||
@@ -447,12 +449,15 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol
|
||||
|
||||
if (wakeUp)
|
||||
{
|
||||
_recordInProcess.Acknowledge = RequestAcknowledgeState.WakeupMessage;
|
||||
//avoid wakeup retry on acknowledged record
|
||||
if (RequestAcknowledgeState.Ok != _recordInProcess.Acknowledge)
|
||||
_recordInProcess.Acknowledge = RequestAcknowledgeState.WakeupMessage;
|
||||
//initiate a single retry on wakeup message response
|
||||
if (_recordInProcess.WakeupMessageRetryCtr < 2)
|
||||
{
|
||||
_recordInProcess.WakeupMessageRetryCtr++;
|
||||
_logger.Debug($"{_ident} Wakeup message({_recordInProcess.WakeupMessageRetryCtr}) received");
|
||||
_logger.Debug(
|
||||
$"{_ident} Wakeup message({_recordInProcess.WakeupMessageRetryCtr}) received");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -461,6 +466,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol
|
||||
_recordInProcess.Acknowledge = RequestAcknowledgeState.DecodingError;
|
||||
_logger.Debug($"{_ident} Message decoding error.");
|
||||
}
|
||||
|
||||
// reset retry counter to get all required retries
|
||||
_recordInProcess.RetryCtr = -1;
|
||||
//reset timeout to a normal value if timeout is extremely high but response received
|
||||
@@ -469,7 +475,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//log request protocol content
|
||||
_logger.Debug(_recordInProcess.HideDataInLog
|
||||
? $"{_ident} DecodedRecord(*****)"
|
||||
@@ -549,20 +554,69 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol
|
||||
new RequestResponseDataEventArgs { RequestResponseData = responseRecord });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to send basic Commands to Meter. Creates an integer of 4 bytes for the payload.
|
||||
/// Supported Commands are: <see cref="Commands.ReadData" />,<see cref="Commands.MultipleReadData" />,
|
||||
/// <see cref="Commands.WriteData" />, <see cref="Commands.MultipleWriteData" /> and <see cref="Commands.QueryCaps" />.
|
||||
/// Calculate CRCs and Command length and check if command is valid.
|
||||
/// Use <see cref="AddRecordToSendFifo" /> to push data to Port/Meter.
|
||||
/// </summary>
|
||||
/// <param name="command">
|
||||
/// Supported <see cref="Commands.ReadData" />,<see cref="Commands.MultipleReadData" />,
|
||||
/// <see cref="Commands.WriteData" />, <see cref="Commands.MultipleWriteData" /> and <see cref="Commands.QueryCaps" />.
|
||||
/// </param>
|
||||
/// <param name="meterRegister">Register to Read or Write, null for <see cref="Commands.QueryCaps" /></param>
|
||||
/// <param name="payload">
|
||||
/// Data to push into the <see cref="RegisterDefinition" />.
|
||||
/// must be null on Read Commands ( <see cref="Commands.ReadData" />,<see cref="Commands.MultipleReadData" />)
|
||||
/// and not null on WriteData (<see cref="Commands.WriteData" /> and <see cref="Commands.MultipleWriteData" />)
|
||||
/// </param>
|
||||
/// <param name="expectedLength"></param>
|
||||
/// <param name="hideDataInLog">hiding data in log file to avoid spying of passwords</param>
|
||||
/// <param name="skipRetryErrorCode">mask to skip reties on error</param>
|
||||
/// <returns>an new command just send to port/meter </returns>
|
||||
/// <remarks date="2018-Mar-15" author="R.Drahbesch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Dec-08" author="T.Wiedebusch">
|
||||
/// - Reworked to zero pad payload with chunks of 4 bytes, the caller needn't take care of the size
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Dec-09" author="T.Wiedebusch">
|
||||
/// - Corrected payload content in request protocol
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
|
||||
/// - Hide data in logging for e.g. passwords
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Jun-28" author="T.Wiedebusch">
|
||||
/// - Skip retries on specific error mask to speed up communication on functional errors
|
||||
/// or informational feed backs (e.g FW not installed 0x0004)
|
||||
/// <see cref="RequestRecord.ResponseErrorCode"/>
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Aug-28" author="R.Drabesch">
|
||||
/// - Multiple read for string split to simple reads.
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Jan-18" author="T.Wiedebusch">
|
||||
/// - Default <see cref="CommunicationConfig.SkipRetryErrorCode"/> set.
|
||||
/// </remarks>
|
||||
/// <remarks date="2023-Sep-06" author="T.Wiedebusch">
|
||||
/// - MultipleReadData based on data size.
|
||||
/// </remarks>
|
||||
/// <remarks date="2023-Nov-24" author="T.Wiedebusch">
|
||||
/// - Exit multiple read date if retries exceeded.
|
||||
/// </remarks>
|
||||
public RequestRecord CommandToMeter(Byte command, RegisterDefinition meterRegister = null,
|
||||
Byte[] payload = null, Int32? expectedLength = null, Boolean hideDataInLog = false,
|
||||
UInt16 skipRetryErrorCode = CommunicationConfig.SkipRetryErrorCode)
|
||||
Byte[] payload = null, Int32 expectedLength = 4, Boolean hideDataInLog = false,
|
||||
UInt16 skipRetryErrorCode = CommunicationConfig.SkipRetryErrorCode)
|
||||
{
|
||||
if (command == Commands.MultipleReadData && meterRegister != null && meterRegister.DataType == typeof(String))
|
||||
{
|
||||
var completeResponse = new List<Byte>();
|
||||
//TODO THW check this including the recursive call CommandToMeter
|
||||
while (true)
|
||||
while (!completeResponse.Contains(0) && _recordInProcess.RetryCtr < CommunicationConfig.RequestRetries)
|
||||
{
|
||||
var cRecord = CommandToMeter(Commands.ReadData, meterRegister, payload, expectedLength,
|
||||
hideDataInLog, skipRetryErrorCode);
|
||||
var cRecord = CommandToMeter(Commands.ReadData, meterRegister, payload, 4, hideDataInLog,
|
||||
skipRetryErrorCode);
|
||||
var rest = ProcessRecordList();
|
||||
if (rest == RequestAcknowledgeState.Ok && cRecord != null && cRecord.ResponsePayload != null)
|
||||
if (rest == RequestAcknowledgeState.Ok && cRecord?.ResponsePayload != null)
|
||||
{
|
||||
completeResponse.AddRange(cRecord.ResponsePayload);
|
||||
if (cRecord.ResponsePayload.Contains(0))
|
||||
@@ -570,40 +624,9 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol
|
||||
cRecord.ResponsePayload = completeResponse;
|
||||
OnMeterRegisterUpdated?.Invoke(this,
|
||||
new RegisterUpdatedEventArgs(meterRegister, completeResponse.ToArray()));
|
||||
if (expectedLength.HasValue && completeResponse.Count < expectedLength.Value)
|
||||
{
|
||||
_logger.Debug("Too Short. Payload contains zero");
|
||||
}
|
||||
|
||||
return cRecord;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (expectedLength.HasValue && completeResponse.Any() &&
|
||||
completeResponse.Count < expectedLength.Value)
|
||||
{
|
||||
if (rest != RequestAcknowledgeState.Ok)
|
||||
{
|
||||
_logger.Debug($"Too Short.Request Acknowledge State is {rest}");
|
||||
}
|
||||
else if (cRecord == null)
|
||||
{
|
||||
_logger.Debug("Too Short.Record is empty or null");
|
||||
}
|
||||
else if (cRecord.ResponsePayload == null)
|
||||
{
|
||||
_logger.Debug("Too Short.ResponsePayload is empty or null");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug("Too Short.Unkown reason");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return cRecord;
|
||||
}
|
||||
}
|
||||
}
|
||||
//the minimum payload is one 4 byte chunk
|
||||
@@ -648,20 +671,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol
|
||||
|
||||
if (command == Commands.MultipleReadData)
|
||||
{
|
||||
//TODO THW create expected size out of meterRegister.DataType
|
||||
if (!expectedLength.HasValue)
|
||||
{
|
||||
if ((meterRegister.DataType == typeof(UInt64) || meterRegister.DataType == typeof(Int64)))
|
||||
{
|
||||
expectedLength = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
expectedLength = 24;
|
||||
}
|
||||
}
|
||||
|
||||
var numberOfChunks = (UInt16)(expectedLength.Value / chunkSize);
|
||||
var numberOfChunks = (UInt16)(expectedLength / chunkSize);
|
||||
if (expectedLength % chunkSize != 0)
|
||||
numberOfChunks++;
|
||||
requestProtocol.Add((Byte)(numberOfChunks & 0xFF));
|
||||
@@ -676,138 +686,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol
|
||||
hideDataInLog, skipRetryErrorCode);
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Method to send basic Commands to Meter. Creates an integer of 4 bytes for the payload.
|
||||
///// Supported Commands are: <see cref="Commands.ReadData" />,<see cref="Commands.MultipleReadData" />,
|
||||
///// <see cref="Commands.WriteData" />, <see cref="Commands.MultipleWriteData" /> and <see cref="Commands.QueryCaps" />.
|
||||
///// Calculate CRCs and Command length and check if command is valid.
|
||||
///// Use <see cref="AddRecordToSendFifo" /> to push data to Port/Meter.
|
||||
///// </summary>
|
||||
///// <param name="command">
|
||||
///// Supported <see cref="Commands.ReadData" />,<see cref="Commands.MultipleReadData" />,
|
||||
///// <see cref="Commands.WriteData" />, <see cref="Commands.MultipleWriteData" /> and <see cref="Commands.QueryCaps" />.
|
||||
///// </param>
|
||||
///// <param name="meterRegister">Register to Read or Write, null for <see cref="Commands.QueryCaps" /></param>
|
||||
///// <param name="payload">
|
||||
///// Data to push into the <see cref="RegisterDefinition" />.
|
||||
///// must be null on Read Commands ( <see cref="Commands.ReadData" />,<see cref="Commands.MultipleReadData" />)
|
||||
///// and not null on WriteData (<see cref="Commands.WriteData" /> and <see cref="Commands.MultipleWriteData" />)
|
||||
///// </param>
|
||||
///// <param name="expectedLength"></param>
|
||||
///// <param name="hideDataInLog">hiding data in log file to avoid spying of passwords</param>
|
||||
///// <param name="skipRetryErrorCode">mask to skip reties on error</param>
|
||||
///// <returns>an new command just send to port/meter </returns>
|
||||
///// <remarks date="2018-Mar-15" author="R.Drahbesch">
|
||||
///// - Initial
|
||||
///// </remarks>
|
||||
///// <remarks date="2018-Dec-08" author="T.Wiedebusch">
|
||||
///// - Reworked to zero pad payload with chunks of 4 bytes, the caller needn't take care of the size
|
||||
///// </remarks>
|
||||
///// <remarks date="2018-Dec-09" author="T.Wiedebusch">
|
||||
///// - Corrected payload content in request protocol
|
||||
///// </remarks>
|
||||
///// <remarks date="2019-Apr-11" author="T.Wiedebusch">
|
||||
///// - Hide data in logging for e.g. passwords
|
||||
///// </remarks>
|
||||
///// <remarks date="2019-Jun-28" author="T.Wiedebusch">
|
||||
///// - Skip retries on specific error mask to speed up communication on functional errors
|
||||
///// or informational feed backs (e.g FW not installed 0x0004)
|
||||
///// <see cref="RequestRecord.ResponseErrorCode"/>
|
||||
///// </remarks>
|
||||
///// <remarks date="2019-Aug-28" author="R.Drabesch">
|
||||
///// - Multiple read for string split to simple reads.
|
||||
///// </remarks>
|
||||
///// <remarks date="2019-Jan-18" author="T.Wiedebusch">
|
||||
///// - Default <see cref="CommunicationConfig.SkipRetryErrorCode"/> set.
|
||||
///// </remarks>
|
||||
///// <remarks date="2023-Sep-06" author="T.Wiedebusch">
|
||||
///// - MultipleReadData based on data size.
|
||||
///// </remarks>
|
||||
///// <remarks date="2023-Nov-24" author="T.Wiedebusch">
|
||||
///// - Exit multiple read date if retries exceeded.
|
||||
///// </remarks>
|
||||
//public RequestRecord CommandToMeter(Byte command, RegisterDefinition meterRegister = null,
|
||||
// Byte[] payload = null, Int32 expectedLength = 4, Boolean hideDataInLog = false,
|
||||
// UInt16 skipRetryErrorCode = CommunicationConfig.SkipRetryErrorCode)
|
||||
//{
|
||||
// if (command == Commands.MultipleReadData && meterRegister != null && meterRegister.DataType == typeof(String))
|
||||
// {
|
||||
// var completeResponse = new List<Byte>();
|
||||
// while (!completeResponse.Contains(0) && _recordInProcess.RetryCtr < (CommunicationConfig.RequestRetries*3))
|
||||
// {
|
||||
// var cRecord = CommandToMeter(Commands.ReadData, meterRegister, payload, 4, hideDataInLog,
|
||||
// skipRetryErrorCode);
|
||||
// var rest = ProcessRecordList();
|
||||
// if (rest == RequestAcknowledgeState.Ok && cRecord?.ResponsePayload != null)
|
||||
// {
|
||||
// completeResponse.AddRange(cRecord.ResponsePayload);
|
||||
// if (cRecord.ResponsePayload.Contains(0))
|
||||
// {
|
||||
// cRecord.ResponsePayload = completeResponse;
|
||||
// OnMeterRegisterUpdated?.Invoke(this,
|
||||
// new RegisterUpdatedEventArgs(meterRegister, completeResponse.ToArray()));
|
||||
// return cRecord;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// //the minimum payload is one 4 byte chunk
|
||||
// var chunkSize = RegisterDefinition.ChunkSize;
|
||||
// if (payload == null)
|
||||
// {
|
||||
// payload = new Byte[chunkSize];
|
||||
// for (var i = 0; i < chunkSize; i++)
|
||||
// {
|
||||
// payload[i] = FillByte;
|
||||
// }
|
||||
// }
|
||||
|
||||
// //real payload based on 4 byte chunks
|
||||
// var requestPayload = new List<Byte>();
|
||||
// requestPayload.AddRange(payload);
|
||||
// //fill-byte padding
|
||||
// if (payload.Length % chunkSize != 0)
|
||||
// {
|
||||
// for (var i = 0; i < chunkSize - payload.Length % chunkSize; i++)
|
||||
// {
|
||||
// requestPayload.Add(FillByte);
|
||||
// }
|
||||
// }
|
||||
|
||||
// //request protocol excluding the command
|
||||
// var requestProtocol = new List<Byte>();
|
||||
|
||||
// if (meterRegister != null)
|
||||
// {
|
||||
// requestProtocol.Add(meterRegister.RegisterAddress[1]);
|
||||
// requestProtocol.Add(meterRegister.RegisterAddress[0]);
|
||||
|
||||
// if (command == Commands.MultipleWriteData)
|
||||
// {
|
||||
// //set counter to inform meter writes are expected
|
||||
// //the payload size is already padded to 4 byte chunks
|
||||
// var numberOfChunks = (UInt16)(requestPayload.Count / chunkSize);
|
||||
// requestProtocol.Add((Byte)(numberOfChunks & 0xFF));
|
||||
// requestProtocol.Add((Byte)((numberOfChunks >> 8) & 0xFF));
|
||||
// }
|
||||
|
||||
// if (command == Commands.MultipleReadData)
|
||||
// {
|
||||
// var numberOfChunks = (UInt16)(expectedLength / chunkSize);
|
||||
// if (expectedLength % chunkSize != 0)
|
||||
// numberOfChunks++;
|
||||
// requestProtocol.Add((Byte)(numberOfChunks & 0xFF));
|
||||
// requestProtocol.Add((Byte)((numberOfChunks >> 8) & 0xFF));
|
||||
// }
|
||||
|
||||
// //add always the payload to the request protocol
|
||||
// requestProtocol.AddRange(requestPayload);
|
||||
// }
|
||||
|
||||
// return AddRecordToSendFifo(command, requestProtocol.ToArray(), meterRegister,
|
||||
// hideDataInLog, skipRetryErrorCode);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Analyzes the error code
|
||||
/// </summary>
|
||||
|
||||
@@ -7,6 +7,24 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers
|
||||
/// </summary>
|
||||
public class RecoveryRegisterItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Ctor
|
||||
/// </summary>
|
||||
public RecoveryRegisterItem()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ctor for write access parameter setup
|
||||
/// </summary>
|
||||
public RecoveryRegisterItem(String registerIdent, Byte[] writeValue, Byte[] readBackValue = null)
|
||||
{
|
||||
RegisterIdent = registerIdent;
|
||||
WriteValue = writeValue;
|
||||
ReadBackValue = readBackValue;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Register name, use the latest configuration.json to make sure that the address will be correct
|
||||
/// </summary>
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
c43c40a030005a8f189e6179faf40a6d42bfef5c
|
||||
bf6bb8fceb3db5a9977dadc407cf576684bf7127
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
65d75c3322f8a137c69963cfdc0cb6f1ffacf62a
|
||||
247396738e2c5d26ac00f9df05c0cc5c61d7f33a
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
namespace Xylem.Common.Ui.GenesisToolBox.Ctls
|
||||
{
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using Xylem.Common.Ui.GenesisToolBox.Infrastructure;
|
||||
|
||||
public partial class LoginForm : UserControl
|
||||
{
|
||||
private readonly Action<string, string, object> submittedCallback;
|
||||
@@ -49,20 +49,4 @@
|
||||
internal void ShowError(string errorMessage)
|
||||
=> this.ErrorMessage.Text = errorMessage;
|
||||
}
|
||||
|
||||
internal static class PasswordExtensions
|
||||
{
|
||||
internal static string ComputeBase64Hash(this string plainText)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(plainText))
|
||||
{
|
||||
return plainText;
|
||||
}
|
||||
|
||||
var buffer = Encoding.UTF8.GetBytes(plainText);
|
||||
var hash = SHA256.Create().ComputeHash(buffer);
|
||||
|
||||
return Convert.ToBase64String(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
namespace Xylem.Common.Ui.GenesisToolBox.Infrastructure
|
||||
{
|
||||
internal class ADLoginModel
|
||||
internal class LDAPLoginModel
|
||||
{
|
||||
public string Assembly { get; set; }
|
||||
|
||||
+4
-4
@@ -5,10 +5,10 @@
|
||||
using System.DirectoryServices;
|
||||
using System.DirectoryServices.AccountManagement;
|
||||
|
||||
internal class SoftwareUser
|
||||
internal class LDAPUser
|
||||
{
|
||||
public SoftwareUser()
|
||||
=> this.InitializeSoftwareUser();
|
||||
public LDAPUser()
|
||||
=> this.InitializeLDAPUser();
|
||||
|
||||
public int EmployeeId { get; private set; }
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
public string Email { get; private set; }
|
||||
|
||||
private void InitializeSoftwareUser()
|
||||
private void InitializeLDAPUser()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -1,13 +1,16 @@
|
||||
namespace Xylem.Common.Ui.GenesisToolBox.Infrastructure
|
||||
{
|
||||
using CommonCore.Configuration;
|
||||
|
||||
using Logic.SoftwareAccessHelper;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using CommonCore.Configuration;
|
||||
using Logic.SoftwareAccessHelper;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// Holds information over current software and logged in user.
|
||||
@@ -24,9 +27,9 @@
|
||||
public static void Initialize()
|
||||
{
|
||||
var version = SoftwareVersion.Current;
|
||||
var user = new SoftwareUser();
|
||||
var user = new LDAPUser();
|
||||
|
||||
Login(new ADLoginModel
|
||||
Login(new LDAPLoginModel
|
||||
{
|
||||
Assembly = version.AssemblyName.Name,
|
||||
Email = user.Email,
|
||||
@@ -44,16 +47,17 @@
|
||||
/// <summary>
|
||||
/// Sends login request and obtains an bearer authorization token.
|
||||
/// </summary>
|
||||
/// <param name="model"><see cref="ADLoginModel"/> - containing all software and user info required for authentication.</param>
|
||||
public static void Login(ADLoginModel model)
|
||||
/// <param name="model"><see cref="LDAPLoginModel"/> - containing all software and user info required for authentication.</param>
|
||||
public static void Login(LDAPLoginModel model)
|
||||
{
|
||||
var authenticationResponse = LocalWebRequest.PostRequestAndGetResponseAsync(
|
||||
url: $"{ServiceUrls.LaaProductionAPI}/LaaProductionWeb/API/Login/AD",
|
||||
json: model);
|
||||
|
||||
bearer = authenticationResponse.Content;
|
||||
bearer = authenticationResponse
|
||||
?.Content
|
||||
?.Trim()
|
||||
?.Trim('"');
|
||||
softwareFunctions = GetFunctions();
|
||||
UserName = "Options";
|
||||
|
||||
if (HasUser)
|
||||
{
|
||||
@@ -82,10 +86,11 @@
|
||||
MinorV = SoftwareVersion.Current.MinorV,
|
||||
BuildV = SoftwareVersion.Current.BuildV,
|
||||
});
|
||||
|
||||
bearer = authenticationResponse.Content;
|
||||
bearer = authenticationResponse
|
||||
?.Content
|
||||
?.Trim()
|
||||
?.Trim('"');
|
||||
softwareFunctions = GetFunctions();
|
||||
UserName = "Options";
|
||||
|
||||
if (HasUser)
|
||||
{
|
||||
@@ -129,7 +134,7 @@
|
||||
return JsonConvert
|
||||
.DeserializeObject<IEnumerable<String>>(functionsResponse)
|
||||
?.Select(x => Enum.TryParse($"{x}", out SoftwareFunctions f) ? f : default(SoftwareFunctions))
|
||||
?? Array.Empty<SoftwareFunctions>();
|
||||
?.Distinct() ?? Array.Empty<SoftwareFunctions>();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -150,5 +155,18 @@
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
internal static string ComputeBase64Hash(this string plainText)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(plainText))
|
||||
{
|
||||
return plainText;
|
||||
}
|
||||
|
||||
var buffer = Encoding.UTF8.GetBytes(plainText);
|
||||
var hash = SHA256.Create().ComputeHash(buffer);
|
||||
|
||||
return Convert.ToBase64String(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,12 @@
|
||||
GENERAL_SETUP,
|
||||
GENERAL_STREAM_QUALITY,
|
||||
GENERAL_TEMP_METER,
|
||||
|
||||
|
||||
LAA_MAPPING_NA,
|
||||
LAA_PASSWORD,
|
||||
LAA_PARAMETERIZATION,
|
||||
LAA_PROCESS_PARAMETER,
|
||||
LAA_REGISTER_HISTORY,
|
||||
LAA_ADMIN,
|
||||
}
|
||||
}
|
||||
|
||||
+13
-3
@@ -54,6 +54,7 @@
|
||||
this.optionsMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.loginMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.logoutMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.registerMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.gpGerneral.SuspendLayout();
|
||||
this.gpLAA.SuspendLayout();
|
||||
this.gbDeveloper.SuspendLayout();
|
||||
@@ -334,7 +335,8 @@
|
||||
//
|
||||
this.optionsMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.loginMenuItem,
|
||||
this.logoutMenuItem});
|
||||
this.logoutMenuItem,
|
||||
this.registerMenuItem});
|
||||
this.optionsMenuItem.Name = "optionsMenuItem";
|
||||
this.optionsMenuItem.Size = new System.Drawing.Size(61, 20);
|
||||
this.optionsMenuItem.Text = "Options";
|
||||
@@ -343,17 +345,24 @@
|
||||
// loginMenuItem
|
||||
//
|
||||
this.loginMenuItem.Name = "loginMenuItem";
|
||||
this.loginMenuItem.Size = new System.Drawing.Size(112, 22);
|
||||
this.loginMenuItem.Size = new System.Drawing.Size(180, 22);
|
||||
this.loginMenuItem.Text = "Login";
|
||||
this.loginMenuItem.Click += new System.EventHandler(this.OnLoginClick);
|
||||
//
|
||||
// logoutMenuItem
|
||||
//
|
||||
this.logoutMenuItem.Name = "logoutMenuItem";
|
||||
this.logoutMenuItem.Size = new System.Drawing.Size(112, 22);
|
||||
this.logoutMenuItem.Size = new System.Drawing.Size(180, 22);
|
||||
this.logoutMenuItem.Text = "Logout";
|
||||
this.logoutMenuItem.Click += new System.EventHandler(this.OnLogoutClick);
|
||||
//
|
||||
// registerMenuItem
|
||||
//
|
||||
this.registerMenuItem.Name = "registerMenuItem";
|
||||
this.registerMenuItem.Size = new System.Drawing.Size(180, 22);
|
||||
this.registerMenuItem.Text = "Register";
|
||||
this.registerMenuItem.Click += new System.EventHandler(this.OnRegisterClick);
|
||||
//
|
||||
// FrmMainForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
@@ -410,6 +419,7 @@
|
||||
private System.Windows.Forms.ToolStripMenuItem optionsMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem loginMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem logoutMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem registerMenuItem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using global::GenesisToolBox;
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
@@ -406,6 +407,8 @@
|
||||
/// </summary>
|
||||
private void SetSoftwareFunctions()
|
||||
{
|
||||
registerMenuItem.Tag = SoftwareFunctions.LAA_ADMIN;
|
||||
|
||||
btnSetup.Tag = SoftwareFunctions.GENERAL_SETUP;
|
||||
btnAlarm.Tag = SoftwareFunctions.GENERAL_ALARM;
|
||||
btnRegisterStore.Tag = SoftwareFunctions.GENERAL_REGISTER_STORE;
|
||||
@@ -462,18 +465,53 @@
|
||||
if (button.Tag is SoftwareFunctions softwareFunction)
|
||||
{
|
||||
// enable all buttons for DEBUGGING, else use access rights of user
|
||||
button.Enabled = System.Diagnostics.Debugger.IsAttached || Software.IsEnabled(softwareFunction);
|
||||
button.Enabled = Debugger.IsAttached || Software.IsEnabled(softwareFunction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
groupBox.Show();
|
||||
}
|
||||
else if (control is MenuStrip menu && menu.Items != null)
|
||||
{
|
||||
foreach (var item in menu.Items)
|
||||
{
|
||||
if (item is ToolStripMenuItem toolStripMenuItem)
|
||||
{
|
||||
this.ShowAvailableMenuItems(toolStripMenuItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
optionsMenuItem.Text = Software.UserName;
|
||||
}
|
||||
|
||||
private void ShowAvailableMenuItems(ToolStripMenuItem toolStripMenuItem)
|
||||
{
|
||||
if (toolStripMenuItem is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (toolStripMenuItem.Tag is SoftwareFunctions softwareFunction)
|
||||
{
|
||||
toolStripMenuItem.Visible = Debugger.IsAttached || Software.IsEnabled(softwareFunction);
|
||||
}
|
||||
|
||||
var dropdownItems = toolStripMenuItem.DropDownItems;
|
||||
|
||||
if (dropdownItems != null && dropdownItems.Count > 0)
|
||||
{
|
||||
foreach (var item in dropdownItems)
|
||||
{
|
||||
if (item is ToolStripMenuItem _toolStripMenuItem)
|
||||
{
|
||||
this.ShowAvailableMenuItems(_toolStripMenuItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides all members but the login form if the app is initialized otherwise shows error message.
|
||||
/// In case that the app is initialized and user is authorized shows all buttons as enabled or disabled depending on the user permissions.
|
||||
@@ -558,5 +596,10 @@
|
||||
form.Closed += Form_Closed;
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void OnRegisterClick(Object _, EventArgs _1)
|
||||
{
|
||||
MessageBox.Show("Register new user");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user