This commit is contained in:
Stoyan Zlatev
2025-11-28 10:01:56 +01:00
43 changed files with 25676 additions and 24856 deletions
+1 -1
View File
@@ -13,4 +13,4 @@ using System.Reflection;
//
//[assembly: AssemblyVersion("1.2.*.0")]
[assembly: AssemblyVersion("2.8.11.*")]
[assembly: AssemblyVersion("2.8.12.*")]
+1 -1
View File
@@ -187,7 +187,7 @@
</site>
<site name="MeterProcessState(3)" id="6">
<application path="/" applicationPool="Clr4IntegratedAppPool">
<virtualDirectory path="/" physicalPath="C:\Users\SZLATEV\source\repos\laa_production\Common\Service\MeterProcessState" />
<virtualDirectory path="/" physicalPath="D:\Projekte\SENSUS_GitLab\LAA_PRODUCTION\Common\Service\MeterProcessState" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:50075:localhost" />
+27
View File
@@ -1,16 +1,40 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeEditing/ContextActionTable/DisabledContextActions/=JetBrains_002EReSharper_002EAI_002ECore_002EContextActions_002EOpenAIAssistantContextAction/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/CodeEditing/Intellisense/CodeCompletion/ParenthesesInsertType/@EntryValue">Left</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeMissingParentheses/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeRedundantParentheses/@EntryIndexedValue">SUGGESTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedLocalFunction/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedLocalFunction_002ECompiler/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedMember_002ELocal/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedType_002ELocal/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/PARENTHESES_GROUP_NON_OBVIOUS_OPERATIONS/@EntryValue">Arithmetic, Relational, Conditional</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/PARENTHESES_NON_OBVIOUS_OPERATIONS/@EntryValue">Arithmetic, Shift, Relational, Equality, Bitwise, Conditional, NullCoalescing, Lowest</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/PARENTHESES_REDUNDANCY_STYLE/@EntryValue">Remove</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/PREFER_ROSLYN_RULES_FOR_PARENTHESES_CLARITY/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ACCESSOR_OWNER_DECLARATION_BRACES/@EntryValue">NEXT_LINE</s:String>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">120</s:Int64>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DN/@EntryIndexedValue">DN</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FW/@EntryIndexedValue">FW</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LUT/@EntryIndexedValue">LUT</s:String>
<s:Boolean x:Key="/Default/Environment/AIAssistantOptions/AiRenameSuggestions/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/AIAssistantOptions/IsAccessToCodeEnabled/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/AIAssistantOptions/IsContextActionsEnabled/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/AIAssistantOptions/IsContextSlidingWindowEnabled/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/AIAssistantOptions/IsExplainExceptionEnabled/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/AIInplaceSuggestionHintsOptions/ShowInPlaceSuggestionHints/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/GrayCodeCompletion/IsEnabled/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/MemoryUsageIndicator/IsVisible/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SearchAndNavigation/SaveExtendedSearch/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SearchAndNavigation/UseReSharperMouseBackForwardNavigation/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/GrammarAndSpelling/GrammarChecking/Exceptions/=na2walarms/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/GrammarAndSpelling/GrammarChecking/LanguagesStates/=Asturianu/@EntryIndexedValue">Disabled</s:String>
@@ -48,6 +72,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Flowrate/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Fwdl/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=GENESISFLOW/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Gensis/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=GENSISFLOW/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Irda/@EntryIndexedValue">True</s:Boolean>
@@ -59,6 +84,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Milli/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Modbus/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=MOXA/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=naproduct/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=OPTICALINTERFACE/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PCBID/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=perc/@EntryIndexedValue">True</s:Boolean>
@@ -70,6 +96,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pwds/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=restorable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=RFID/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=rowproduct/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Sensus/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=SENSUSRADIO/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Sirt/@EntryIndexedValue">True</s:Boolean>
+8 -1
View File
@@ -1,5 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeMissingParentheses/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedMember_002ELocal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/PREFER_ROSLYN_RULES_FOR_PARENTHESES_CLARITY/@EntryValue">True</s:Boolean>
<s:Int64 x:Key="/Default/CodeStyle/Naming/CSharpAutoNaming/AutoNamingCompletedVersion/@EntryValue">2</s:Int64>
<s:Boolean x:Key="/Default/CodeStyle/Naming/CSharpAutoNaming/IsNamingAutoDetectionCompleted/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/Naming/CSharpAutoNaming/IsNotificationDisabled/@EntryValue">True</s:Boolean>
@@ -36,7 +38,12 @@
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=d5d84997_002D9b95_002D490a_002Da9a3_002Da461c5e0d514/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
&lt;Solution /&gt;&#xD;
&lt;Not&gt;&#xD;
&lt;TestAncestor&gt;&#xD;
&lt;TestId&gt;VsTest::48DA0272-9B14-4CC8-976F-A91E42DE4680::.NETFramework,Version=v4.8.1::executor://nunit3testexecutor/#Xylem.Common.Production.ProductionUiCordonel.UnitTests.Features.CheckProductionStateFeature&lt;/TestId&gt;&#xD;
&lt;TestId&gt;VsTest::48DA0272-9B14-4CC8-976F-A91E42DE4680::.NETFramework,Version=v4.8.1::executor://nunit3testexecutor/#Xylem.Common.Production.ProductionUiCordonel.UnitTests.Features.FinalParamSequence_Write_Store_Reboot_Read_VerifyFeature&lt;/TestId&gt;&#xD;
&lt;/TestAncestor&gt;&#xD;
&lt;/Not&gt;&#xD;
&lt;/SessionState&gt;</s:String>
+1 -1
View File
@@ -32,7 +32,7 @@
<s:Boolean x:Key="/Default/Environment/Hierarchy/EntityFrameworkOptions/IsAlreadyNotifiedAboutEntityFramework/@EntryValue">True</s:Boolean>
<<<<<<< HEAD
=======
<s:String x:Key="/Default/Environment/Highlighting/HighlightingSourceSnapshotLocation/@EntryValue">C:\Users\Thomas Müller\AppData\Local\Temp\JetBrains\ReSharperPlatformVs17\vAny_ab6433f6\CoverageData\_Common.-1885154465\Snapshot\snapshot.utdcvr</s:String>
<s:String x:Key="/Default/Environment/Highlighting/HighlightingSourceSnapshotLocation/@EntryValue">C:\Users\Thomas\AppData\Local\Temp\JetBrains\ReSharperPlatformVs17\vAny_ab6433f6\CoverageData\_Common.-1885154465\Snapshot\snapshot.utdcvr</s:String>
>>>>>>> origin/main
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
@@ -10,7 +10,7 @@ namespace Xylem.Common.CommonCore.Configuration
public static class ServiceUrls
{
//#if URI_OLD_DNS // this is defined in build configuration 'VPN_Debug' as the new DNS cannot be reached via VPN
// private const String ServerDns = "http://sla12iis01.emea.sensus.net";
// private const String ServerDns = "http://sla12iis01.emea.sensus.net";
//#else
private const String ServerDns = "http://delaz1web01.world.fluidtechnology.net";
//#endif
@@ -619,7 +619,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
try
{
Logger.Trace($"Slot:{Slot} - Registers definitions loaded from {location}");
if (Configuration.AutoUpdateFiles)
if (Configuration != null && Configuration.AutoUpdateFiles)
{
Logger.Trace($"Slot:{Slot} - Check for updates of {configFilePath}");
var fi = new FileInfo(configFilePath);
@@ -2608,7 +2608,14 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// <inheritdoc />
public void WriteLog(String text)
{
Logger.Info($"Slot:{Slot} - {text}");
try
{
Logger.Info($"Slot:{Slot} - {text}");
}
catch (Exception)
{
// ignored
}
}
/// <inheritdoc />
@@ -43,9 +43,14 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
private String _pcbIdFinalRegisterRead = Constants.StrUnknown;
/// <summary>
/// Collection of recovery registers and values
/// Collection of recovery registers - programming parameters
/// </summary>
public List<RecoveryRegisterItem> RecoveryRegisters;
public List<RecoveryRegisterItem> RecoveryRegisters { set; get; }
/// <summary>
/// Marker for restore after reboot parameters
/// </summary>
public Boolean HasRestoreRegistersAfterRebootIfCompareFailed { private set; get; }
/// <summary>
/// Register content on initial connect to keep those during update
@@ -100,6 +105,19 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
"SENSUSRADIO_UpgFWVersion" // After a FW-Update the FW version has changed
};
/// <summary>
/// Registers manually explicit marked to be restored after reboot if compare failed. Those registers will
/// temporary setup values which do not survive a reboot. This is done to overcome a FW-bug which overwrites
/// those values after the reboot procedure!
/// </summary>
/// <remarks date="2025-Nov-17" author="Thomas Wiedebusch">
/// - Initial to restore values which do not survive the reboot as workaround for R1.4.22 and below.
/// </remarks>
private static readonly List<String> _restoreAfterRebootIfCompareFailed = new List<String>
{
"CUSTOMER_AlarmEnableMask" // Restored to hard coded value up to FW R1.4.22
};
/// <summary>
/// Registers manually explicit excluded for in-field updatable parameters for the CUST to prevent inadvertent
/// modifications resulting in potential MID violations.
@@ -109,6 +127,9 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// All commands defined here will not be accepted from 'external' to adjust. Program internally, those
/// commands are potentially allowed if those do not change any MID dependent 'values'.
/// </summary>
/// <remarks date="2025-Nov-18" author="Thomas Wiedebusch">
/// - SENSUSRADIO_WakeupInterval added to keep radio active after field update if it was active before.
/// </remarks>
private static readonly List<String> _fieldUpdateBlacklist = new List<String>
{
"CUSTOMER_AlarmVisualMask",
@@ -120,7 +141,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
"FLEXNETSERIAL_FwdlState",
"FLEXNETSERIAL_UpgState",
"GENESISFLOW_AmplitudePeakDetectEnd", // 1
"GENESISFLOW_ArrowThreshold", // 2
"GENESISFLOW_CalFactor1", // 3
@@ -206,7 +227,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
"PERIODICLOG_PeriodicLogLifeTimeCounter",
"PERIODICLOG_ResetCounter",
"POWERMON_TotalUsedCharge",
"POWERMON_TotalUsedCharge",
"POWERMON_TotalUsedSeconds",
"SENSUSRADIO_AverageFlowPeriod", // 1
@@ -232,6 +253,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
"SENSUSRADIO_Tfx_Structure", //21
"SENSUSRADIO_UpgFWVersion", //22
"SENSUSRADIO_UtcTimeOffset", //23
"SENSUSRADIO_WakeupInterval", //24
"SYSTEM_CalendarSeconds", // 1
"SYSTEM_CheckPresence", // 2
@@ -276,8 +298,16 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
new RecoveryRegisterItem("GENESISFLOW_SealDisplay", new Byte[] { 0x00 })
};
// ReSharper disable once CollectionNeverQueried.Local IS FOR DEBUG
private readonly List<RecoveryRegisterItem> _failedComparisonRegisters = new List<RecoveryRegisterItem>();
/// <summary>
/// Registers which failed the comparison
/// </summary>
public List<RecoveryRegisterItem> FailedComparisonRegisters { get; }
/// <summary>
/// Name of registers which should be restored after reboot if the compare failed.
/// </summary>
public List<RecoveryRegisterItem> RestoreRegistersAfterReboot { get; }
// ReSharper disable once CollectionNeverQueried.Local IS FOR DEBUG
private List<String> _dbParameterRegisterList = new List<String>();
// ReSharper disable once CollectionNeverQueried.Local IS FOR DEBUG
@@ -302,13 +332,21 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// Post programming parameters for EMEA region as NA region doesn't have radio and seal display shouldn't
/// be executed.
/// </summary>
/// <remarks date="2025-Nov-20" author="Thomas Wiedebusch">
/// - Removed 'SENSUSRADIO_SystemState' as this will synchronize all parameters between this app and the
/// 'PERIODICLOG' app. Meaning, the 'SENSUSRADIO' app will overwrite the previously adjusted parameters of
/// the 'PERIODICLOG'. After writing the last 'PERIODICLOG' parameter a delay timer between finish exec-
/// parametrization and store all configs needs at least 20 s to update contents.
/// </remarks>
private static readonly List<RecoveryRegisterItem> _finalizeProgrammingEmea = new List<RecoveryRegisterItem>
{
// Set counter to 0 as this is a value how often the radio detected a reset
new RecoveryRegisterItem("SENSUSRADIO_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!
// ATTENTION: logout-delay-login required after this step!
// ATTENTION: 'PERIODICLOG' parameters which will be saved after at least 20 s and the 'SENSUSRADIO' will
// synchronize all parameters and therefore overwrite the previously written values.
new RecoveryRegisterItem("SENSUSRADIO_SystemState", new Byte[] { 0xFF }),
// Seal the display to lock against changes
new RecoveryRegisterItem("GENESISFLOW_SealDisplay", new Byte[] { 0x01 }),
@@ -358,8 +396,18 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
public RegisterRestorer(IGenesisMeter currentGenesis)
{
_currentGenesis = currentGenesis;
RecoveryRegisters = null;
RecoveryRegisters = new List<RecoveryRegisterItem>();
RestoreRegistersAfterReboot = new List<RecoveryRegisterItem>();
FailedComparisonRegisters = new List<RecoveryRegisterItem>();
}
/// <summary>
/// Renew meter for Unit tests based on different meter
/// </summary>
/// <param name="currentGenesis"></param>
public void RenewMeter(IGenesisMeter currentGenesis)
{
_currentGenesis = currentGenesis;
}
/// <summary>
@@ -556,7 +604,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
continue;
}
// Fill DEBUG lists START with all registers even if they are multiple assigned
// Fill DEBUG lists START with all registers even if they are multiple assigned
sortableRegisterList.Add(proPar.RegisterName);
switch (proPar.Source)
@@ -572,21 +620,21 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
break;
}
// Take this parameter as is
//TODO THW take configuration.json and use for String and Array type, see RegisterConverter
//take the register properties from configuration.json
//get the pre-defined dictionary
//var meterRegisters = MockGenesis based on FW version .GetConfigRegistersDefinitions();
//var regDef = meterRegisters.GetRegisterDefinitionByName(proPar.RegisterName);
//if (regDef.DataType.Name.Equals("Int32"))
//{
// Add parameter if it is not in the list
//if (sortableProgrammingParameters.All(a => !proPar.RegisterName.Equals(a.RegisterName)))
//{
// sortableProgrammingParameters.Add(proPar);
// continue;
//}
//}
// Take this parameter as is
//TODO THW take configuration.json and use for String and Array type, see RegisterConverter
//take the register properties from configuration.json
//get the pre-defined dictionary
//var meterRegisters = MockGenesis based on FW version .GetConfigRegistersDefinitions();
//var regDef = meterRegisters.GetRegisterDefinitionByName(proPar.RegisterName);
//if (regDef.DataType.Name.Equals("Int32"))
//{
// Add parameter if it is not in the list
//if (sortableProgrammingParameters.All(a => !proPar.RegisterName.Equals(a.RegisterName)))
//{
// sortableProgrammingParameters.Add(proPar);
// continue;
//}
//}
@@ -780,7 +828,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
return StatusReturn.Failed;
}
return RawProgrammingParametersFilterAndSort(programmingParameters, recoveryRegistersDb, out errorMsg,
return RawProgrammingParametersFilterAndSort(programmingParameters, recoveryRegistersDb, out errorMsg,
fieldUpdate);
}
catch (Exception)
@@ -789,21 +837,21 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
}
}
/// <summary>
/// Avoid doubling of register restore on subsequent FW-Update trials.
/// </summary>
/// <returns>true if registers already backed up</returns>
/// <remarks date="2020-Dec-21" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public Boolean RegisterRestoreExecuted()
{
if (_currentGenesis == null)
return false;
if (_currentGenesis.PcbId != _pcbIdFinalRegisterRead)
return false;
return _finalRegisters.Count > 0;
}
///// <summary>
///// Avoid doubling of register restore on subsequent FW-Update trials.
///// </summary>
///// <returns>true if registers already backed up</returns>
///// <remarks date="2020-Dec-21" author="Thomas Wiedebusch">
///// - Initial.
///// </remarks>
//public Boolean RegisterRestoreExecuted()
//{
// if (_currentGenesis == null)
// return false;
// if (_currentGenesis.PcbId != _pcbIdFinalRegisterRead)
// return false;
// return _finalRegisters.Count > 0;
//}
/// <summary>
/// EOL (end of line) programming contains a reset of the accumulators. Else it is identical with recover
@@ -819,7 +867,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// <remarks date="2025-Apr-07" author="Thomas Wiedebusch">
/// - CancellationToken.
/// </remarks>
public StatusReturn EolProgrammingRegisters(List<RecoveryRegisterItem> recoveryRegistersDb,
public StatusReturn EolProgrammingRegisters(List<RecoveryRegisterItem> recoveryRegistersDb,
CancellationToken cancellationToken)
{
// Mark field update as false to set EOL as true to force accu reset
@@ -926,7 +974,10 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// - Remove '_fieldUpdateBlacklist' parameters for the CUST to prevent inadvertent modifications resulting in potential
/// MID violations.
/// </remarks>
public StatusReturn WriteRegisters(List<RecoveryRegisterItem> recoveryRegistersDb, CancellationToken cancellationToken,
/// <remarks date="2025-Nov-17" author="Thomas Wiedebusch">
/// - Login/Logout moved to <see cref="WriteRegisterParameterSet"/>.
/// </remarks>
public StatusReturn WriteRegisters(List<RecoveryRegisterItem> recoveryRegistersDb, CancellationToken cancellationToken,
Boolean fieldUpdate)
{
if (_currentGenesis == null ||
@@ -935,7 +986,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
recoveryRegistersDb.Count == 0)
return StatusReturn.Failed;
// preset recovery registers if not already set
//TODO THW preset recovery registers if not already set
if (RecoveryRegisters == null)
{
RecoveryRegisters = new List<RecoveryRegisterItem>();
@@ -952,11 +1003,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
_dbParameterRegisterList = recoveryRegistersDb.Select(register => register.RegisterIdent).ToList();
_dbParameterRegisterList.Sort();
// write values to Genesis
_currentGenesis.ReLogin();
if (!_currentGenesis.IsLoggedOn)
return StatusReturn.Failed;
// get the pre-defined dictionary
var meterRegisters = _currentGenesis.GetConfigRegistersDefinitions();
@@ -991,11 +1037,9 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(Resources.StrRegisterRecoveryPreparation,
actualProcessMessage: Resources.StrRegisterRecoveryPreparation));
if (StatusReturn.Failed == WriteRegisterParameterSet(_prepareProgramming, recRegistersCount, meterRegisters,
if (StatusReturn.Failed == WriteRegisterParameterSet(_prepareProgramming, recRegistersCount, meterRegisters,
cancellationToken, fieldUpdate))
{
// the logout will force the radio app to activate the settings
_currentGenesis?.Logout();
return StatusReturn.Failed;
}
@@ -1003,32 +1047,25 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(Resources.StrRegisterRecoveryExec,
actualProcessMessage: Resources.StrRegisterRecoveryExec));
if (StatusReturn.Failed == WriteRegisterParameterSet(recoveryRegistersDb, recRegistersCount, meterRegisters,
if (StatusReturn.Failed == WriteRegisterParameterSet(recoveryRegistersDb, recRegistersCount, meterRegisters,
cancellationToken, fieldUpdate))
{
// the logout will force the radio app to activate the settings
_currentGenesis?.Logout();
return StatusReturn.Failed;
return StatusReturn.Failed;
}
// finalize recovery
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(Resources.StrRegisterRecoveryFinalization,
actualProcessMessage: Resources.StrRegisterRecoveryFinalization));
if (StatusReturn.Failed == WriteRegisterParameterSet(finalizeProgrammingAll, recRegistersCount, meterRegisters,
if (StatusReturn.Failed == WriteRegisterParameterSet(finalizeProgrammingAll, recRegistersCount, meterRegisters,
cancellationToken, fieldUpdate))
{
// the logout will force the radio app to activate the settings
_currentGenesis?.Logout();
return StatusReturn.Failed;
}
// remind pcbId of this run to avoid endless repetition
_pcbIdWriteRegisters = _currentGenesis.PcbId;
// the logout will force the radio app to activate the settings
_currentGenesis?.Logout();
// Wait for completion as storing may need a certain time
Thread.Sleep(1000);
@@ -1062,19 +1099,20 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// <remarks date="2025-Jul-22" author="Thomas Wiedebusch">
/// - Excluded un-comparable registers from write turned back on ('blind' writing enabled again).
/// </remarks>
private StatusReturn WriteRegisterParameterSet(List<RecoveryRegisterItem> recoveryRegistersDb, Int32 recRegistersCount,
MeterRegisters meterRegisters, CancellationToken cancellationToken, Boolean fieldUpdate)
/// <remarks date="2025-Nov-17" author="Thomas Wiedebusch">
/// - Login/Logout moved here.
/// </remarks>
private StatusReturn WriteRegisterParameterSet(List<RecoveryRegisterItem> recoveryRegistersDb,
Int32 recRegistersCount, MeterRegisters meterRegisters, CancellationToken cancellationToken, Boolean fieldUpdate)
{
_currentGenesis.ReLogin();
if (!_currentGenesis.IsLoggedOn)
return StatusReturn.Failed;
foreach (var recReg in recoveryRegistersDb)
{
//TODO THW Check
// Temporary excluded un-comparable registers from write as this make no sense to write them blind being
// unable to read the correct value back.
//if (_excludedFromCompareParameters.Any( regName => recReg.RegisterIdent.Equals(regName)))
// continue;
// Remove all parameters from parameter list if used for field update
if (fieldUpdate && _fieldUpdateBlacklist.Any( regName => recReg.RegisterIdent.Equals(regName)))
if (fieldUpdate && _fieldUpdateBlacklist.Any(regName => recReg.RegisterIdent.Equals(regName)))
continue;
_actualRegisterCtr++;
@@ -1099,6 +1137,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
$@"{Resources.StrWriteRegister} {_actualRegisterCtr} / {recRegistersCount}",
overallProcessCtrPercent, Resources.StrErrorMsgRegisterNotFound + " Register: " +
recReg.RegisterIdent));
_currentGenesis?.Logout();
return StatusReturn.Failed;
}
@@ -1115,6 +1154,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(
$@"{Resources.StrWriteRegister} {_actualRegisterCtr} / {recRegistersCount}",
overallProcessCtrPercent, Resources.StrRegisterValueOutOfRange + " " + errorMessage));
_currentGenesis?.Logout();
return StatusReturn.Failed;
}
@@ -1124,6 +1164,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
$@"{Resources.StrWriteRegister} {_actualRegisterCtr} / {recRegistersCount}",
overallProcessCtrPercent, Resources.StrErrorMsgRegisterWrite + " Register: " +
recReg.RegisterIdent));
_currentGenesis?.Logout();
return StatusReturn.Failed;
}
@@ -1149,17 +1190,68 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
if (cancellationToken.IsCancellationRequested)
break;
}
_currentGenesis?.Logout();
return StatusReturn.Okay;
}
/// <summary>
/// Compare all registers which can be restored without new access to meter.
/// Precondition:
/// - The _restoreRegisters has to be filled with all registers being able to restore (RW),
/// - The _finalReadRegisters has to be filled.
/// Restore and compare all registers which did not survive a reboot and are listed in the
/// <see cref="_restoreAfterRebootIfCompareFailed"/>
/// - This is a workaround for a FW-bug in R1.4.22 and below.
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <remarks date="2025-Nov-17" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2025-Nov-19" author="Thomas Wiedebusch">
/// - Leave the initial 'RecoveryRegisters' intact.
/// </remarks>
public Boolean RestoreAndCompareRegistersAfterReboot(CancellationToken cancellationToken)
{
if (!HasRestoreRegistersAfterRebootIfCompareFailed)
return false;
HasRestoreRegistersAfterRebootIfCompareFailed = false;
FailedComparisonRegisters.Clear();
// get the pre-defined dictionary
var meterRegisters = _currentGenesis.GetConfigRegistersDefinitions();
_actualRegisterCtr = 0;
// Expected access counter is 3 times the content for write, read and compare
var maxCounter = 3 * RecoveryRegisters.Count;
// Write after reboot
if (StatusReturn.Failed == WriteRegisterParameterSet(RestoreRegistersAfterReboot, maxCounter,
meterRegisters, cancellationToken, false))
{
return false;
}
// executes login read logout
if (!ReadPresetRegisters(RestoreRegistersAfterReboot, cancellationToken))
{
return false;
}
// Compare register content
if (!CompareRegisters(RestoreRegistersAfterReboot, cancellationToken))
{
return false;
}
// Wait for completion as storing may need a certain time
Thread.Sleep(1000);
return true;
}
/// <summary>
/// Compare all registers which can be restored without new access to meter.
/// </summary>
/// <param name="recoveryRegisters"></param>
/// <param name="cancellationToken"></param>
/// <returns>true if equal</returns>
/// <remarks date="2023-Nov-20" author="Thomas Wiedebusch">
/// - Initial.
@@ -1203,20 +1295,31 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// <remarks date="2025-Aug-12" author="Thomas Wiedebusch">
/// - Take 'null' in readBack into account due to communication error.
/// </remarks>
public Boolean CompareRegisters(CancellationToken cancellationToken)
/// <remarks date="2025-Nov-17" author="Thomas Wiedebusch">
/// - _restoreAfterRebootIfCompareFailed.
/// </remarks>
/// <remarks date="2025-Nov-17" author="Thomas Wiedebusch">
/// - Made method static.
/// </remarks>
/// <remarks date="2025-Nov-19" author="Thomas Wiedebusch">
/// - Avoid clearing of failed registers
/// </remarks>
/// <remarks date="2025-Nov-19" author="Thomas Wiedebusch">
/// - Leave the initial 'RecoveryRegisters' intact.
/// </remarks>
public Boolean CompareRegisters(List<RecoveryRegisterItem> recoveryRegisters, CancellationToken cancellationToken)
{
if (_pcbIdFinalRegisterRead != _pcbIdWriteRegisters ||
RecoveryRegisters == null)
recoveryRegisters == null)
return false;
_actualRegisterCtr = 0;
var retVal = true;
// DEBUG
_failedComparisonRegisters?.Clear();
// build a string list of the registers being able to sort those
var registerNames = RecoveryRegisters.Select(register => register.RegisterIdent).ToList();
//var registerNames = RecoveryRegisters.Select(register => register.RegisterIdent).ToList();
var registerNames = recoveryRegisters.Select(register => register.RegisterIdent).ToList();
// Remove all excluded registers
foreach (var regName in _excludedFromCompareParameters.Where(regName => registerNames.Any(reg => reg == regName)))
{
@@ -1259,18 +1362,19 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
try
{
// Get the raw byte array of this register
var recReg = RecoveryRegisters.First(reg => reg.RegisterIdent.Equals(regName));
//var recReg = RecoveryRegisters.First(reg => reg.RegisterIdent.Equals(regName));
var recReg = recoveryRegisters.First(reg => reg.RegisterIdent.Equals(regName));
var writtenValue = recReg.WriteValue;
var readBackValue = recReg.ReadBackValue;
var strRequiredRegisterValue = "";
var isApproximated = false;
var isIdentical = false;
try
{
// ATTENTION: The size of the writtenValue and readBackValue may deviate!
var dataSize = writtenValue.Length;
var isApproximated = false;
var isIdentical = false;
// Process values only if not 'null'
if (writtenValue != null && readBackValue != null)
{
@@ -1331,15 +1435,21 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
}
}// Process register values only if not 'null'
// convert register content to text with raw and converted value depending on data type
strRequiredRegisterValue =
RegisterConverter.GetRegisterContentText(regDef, writtenValue);
// Convert register content to text with raw and converted value depending on data type
strRequiredRegisterValue = RegisterConverter.GetRegisterContentText(regDef, writtenValue);
if (readBackValue != null && (isIdentical || isApproximated))
{
strRequiredRegisterValue += $" - {Resources.StrRegisterCompareSucceeded}";
}
else
{
// Check registers which should be restored after reboot if failed
if (_restoreAfterRebootIfCompareFailed.Any(reg => reg.Equals(regName)))
{
HasRestoreRegistersAfterRebootIfCompareFailed = true;
RestoreRegistersAfterReboot.Add(recReg);
}
var encryptRawData = regName.Contains("EncryptionKey") || regName.Contains("Password");
var strReadBackRegisterValue = RegisterConverter.GetRegisterRawText(regDef, readBackValue,
@@ -1347,7 +1457,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
strRequiredRegisterValue += $" - {Resources.StrMeterValue} ({strReadBackRegisterValue})h" +
$" - {Resources.StrRegisterCompareFailed}";
_failedComparisonRegisters?.Add(new RecoveryRegisterItem(regName, writtenValue,
FailedComparisonRegisters?.Add(new RecoveryRegisterItem(regName, writtenValue,
readBackValue));
retVal = false;
}
@@ -1366,7 +1476,8 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
{
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs($@"{Resources.StrRegisterCompare}" +
$@" {_actualRegisterCtr} / {registersCount}",
overallProcessCtrPercent, actualProcessMsg));
overallProcessCtrPercent, actualProcessMsg,
statusReturn: isIdentical || isApproximated ? StatusReturn.Okay : StatusReturn.Failed));
}
}
@@ -1419,6 +1530,10 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// <remarks date="2025-Apr-07" author="Thomas Wiedebusch">
/// - CancellationToken.
/// </remarks>
/// <remarks date="2025-Nov-19" author="Thomas Wiedebusch">
/// - Cleared all failed compare registers and the restore after reboot registers as a new reading
/// will give it a new chance to compare it.
/// </remarks>
public Boolean FinalReadRegisters(CancellationToken cancellationToken)
{
if (_currentGenesis == null)
@@ -1427,6 +1542,9 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
Boolean retVal;
try
{
// Clear all preceding failed register lists
RestoreRegistersAfterReboot.Clear();
FailedComparisonRegisters.Clear();
// create a dictionary for final read being able to compare those with the initial read and restored
if (!BuildReadableRegisterDictionary(ref _finalRegisters))
@@ -1525,8 +1643,8 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
// remind the PCB ID before update for comparison
_pcbIdInitialRegisterRead = _currentGenesis.PcbId;
// create the dictionaries out of the configuration.json
if (!BuildReadableRegisterDictionary(ref _initialRegisters))
// create a dictionary for final read being able to compare those with the initial read and restored
if (!BuildReadableRegisterDictionary(ref _finalRegisters))
return false;
// log to report file the initial register read string
@@ -1559,14 +1677,15 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// <remarks date="2024-Feb-23" author="Thomas Wiedebusch">
/// - Excluded registers which do not contain useful information.
/// </remarks>
/// <remarks date="2025-Nov-17" author="Thomas Wiedebusch">
/// - Sequence resorted.
/// </remarks>
public Boolean BuildReadableRegisterDictionary(ref List<RecoveryRegisterItem> registers)
{
if (_currentGenesis == null || registers == null)
return false;
registers.Clear();
_readableRegisterList.Clear();
// 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
@@ -1579,11 +1698,14 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
{
var registerIdent = reg.Key.GetIdent();
registers.Add(new RecoveryRegisterItem(registerIdent, reg.Value));
_readableRegisterList.Add(registerIdent);
}
_readableRegisterList.Sort();
registers.Sort((a, b) => String.Compare(a.RegisterIdent, b.RegisterIdent, StringComparison.Ordinal));
// DEBUG information
_readableRegisterList.Clear();
_readableRegisterList.AddRange(_finalRegisterList);
return registers.Count > 0;
}
@@ -1612,7 +1734,8 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
// temporary list of registers just for read and log without keeping them alive
var registers = new List<RecoveryRegisterItem>();
if (!BuildReadableRegisterDictionary(ref registers))
// create a dictionary for final read being able to compare those with the initial read and restored
if (!BuildReadableRegisterDictionary(ref _finalRegisters))
return false;
// log to report file the initial register read string
@@ -1625,6 +1748,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// <summary>
/// Read predefined registers from RecoveryRegisters.
/// </summary>
/// <param name="recoveryRegisters"></param>
/// <param name="cancellationToken"></param>
/// <returns>true if registers could be read</returns>
/// <remarks date="2024-Sep-25" author="Thomas Wiedebusch">
@@ -1633,9 +1757,12 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// <remarks date="2025-Apr-07" author="Thomas Wiedebusch">
/// - CancellationToken.
/// </remarks>
public Boolean ReadPresetRegisters(CancellationToken cancellationToken)
/// <remarks date="2025-Nov-19" author="Thomas Wiedebusch">
/// - Leave the initial 'RecoveryRegisters' intact.
/// </remarks>
public Boolean ReadPresetRegisters(List<RecoveryRegisterItem> recoveryRegisters, CancellationToken cancellationToken)
{
if (_currentGenesis == null || RecoveryRegisters == null || RecoveryRegisters.Count == 0)
if (_currentGenesis == null || recoveryRegisters == null || recoveryRegisters.Count == 0)
return false;
// Assume that this meter shall be used for comparison even if nothing has been written.
@@ -1644,7 +1771,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
_pcbIdWriteRegisters = _currentGenesis.PcbId;
_pcbIdFinalRegisterRead = _currentGenesis.PcbId;
return ReadRegistersSet(ref RecoveryRegisters, cancellationToken);
return ReadRegistersSet(ref recoveryRegisters, cancellationToken);
}
/// <summary>
@@ -1692,7 +1819,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
/// <remarks date="2025-Oct-13" author="Thomas Wiedebusch">
/// - Date size directly from register object.
/// </remarks>
private Boolean ReadRegistersSet(ref List<RecoveryRegisterItem> registers, CancellationToken cancellationToken)
private Boolean ReadRegistersSet(ref List<RecoveryRegisterItem> registers, CancellationToken cancellationToken)
{
if (_currentGenesis == null)
return false;
File diff suppressed because it is too large Load Diff
@@ -74,7 +74,6 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
// position of version string
private const Int32 FlexVersionStringIndex = 0x66;
private Int32 _processRetryCtr;
private IGenesisMeter _genesisMeter;
@@ -101,6 +100,11 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
/// </summary>
public Boolean StoreConfigEnable = true;
/// <summary>
/// Threshold to waste the HW if reboots too high
/// </summary>
public const Int32 MAX_REBOOTS_FOR_HEALTHY_HW = 4;
#endregion
#region Events
@@ -8,6 +8,14 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes
/// </summary>
public class TimeT
{
/// <summary>
/// Ctor:
/// - Takes actual date time as preset value
/// </summary>
public TimeT()
{
DateTimeUtc = DateTime.UtcNow;
}
/// <summary>
/// 1. Get seconds since 01. Jan 2000 00:00:00 UTC previously set by any call by
/// <see cref="UtcNowToSecondsSince2000"/> or <see cref="UtcAnyToSecondsSince2000"/>
@@ -96,7 +104,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes
{
private set;
get;
} = _fixedDateTime2000;
}
/// <summary>
/// Get string of UTC date and time universal language with 24 hours format
@@ -35,7 +35,7 @@
*=======================================================================================\n
*/
using NLog;
//using NLog;
using System;
using System.IO.Ports;
using System.Threading;
@@ -52,10 +52,10 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// <summary>
/// Logger for raw data output
/// </summary>
protected ILogger Logger
{
get;
}
//protected ILogger Logger
//{
// get;
//}
/// <inheritdoc/>
public String SerialNumber
@@ -37,7 +37,7 @@
using System;
using System.IO.Ports;
using NLog;
//using NLog;
using Xylem.Common.CommonCore.Consts;
namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
@@ -1,8 +1,8 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by SpecFlow (https://www.specflow.org/).
// SpecFlow Version:3.3.0.0
// SpecFlow Generator Version:3.1.0.0
// SpecFlow Version:3.9.0.0
// SpecFlow Generator Version:3.9.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -17,7 +17,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Features
using System.Linq;
[System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.3.0.0")]
[System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.9.0.0")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("FinalParamSequence_Write_Store_Reboot_Read_Verify")]
@@ -26,7 +26,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Features
private TechTalk.SpecFlow.ITestRunner testRunner;
private string[] _featureTags = ((string[])(null));
private static string[] featureTags = ((string[])(null));
#line 1 "FinalParamSequence_Write_Store_Reboot_Verify.feature"
#line hidden
@@ -35,11 +35,11 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Features
public virtual void FeatureSetup()
{
testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner();
TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "FinalParamSequence_Write_Store_Reboot_Read_Verify", @"Test the sequence of execute final parametrization, store all settings to FLASH memory, reboot the Cordonel and finally read back
TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "FinalParamSequence_Write_Store_Reboot_Read_Verify", @"Test the sequence of execute final parametrization, store all settings to FLASH memory, reboot the Cordonel and finally read back
and verify the parameters which should remain persistent in non-volatile memory. This should avoid loosing of the parametrization
if the Cordonel accidentally reboots in the field. With the reboot it loads all values from FLASH, if those cannot be loaded, it
presets these values with defaults.This test used a MockGenesis and real data stored as MockRawParamsExamples for the DN50, the
DN80 and the US2.", ProgrammingLanguage.CSharp, ((string[])(null)));
DN80 and the US2.", ProgrammingLanguage.CSharp, featureTags);
testRunner.OnFeatureStart(featureInfo);
}
@@ -51,28 +51,28 @@ DN80 and the US2.", ProgrammingLanguage.CSharp, ((string[])(null)));
}
[NUnit.Framework.SetUpAttribute()]
public virtual void TestInitialize()
public void TestInitialize()
{
}
[NUnit.Framework.TearDownAttribute()]
public virtual void TestTearDown()
public void TestTearDown()
{
testRunner.OnScenarioEnd();
}
public virtual void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo)
public void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo)
{
testRunner.OnScenarioInitialize(scenarioInfo);
testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs<NUnit.Framework.TestContext>(NUnit.Framework.TestContext.CurrentContext);
}
public virtual void ScenarioStart()
public void ScenarioStart()
{
testRunner.OnScenarioStart();
}
public virtual void ScenarioCleanup()
public void ScenarioCleanup()
{
testRunner.CollectScenarioErrors();
}
@@ -80,7 +80,7 @@ DN80 and the US2.", ProgrammingLanguage.CSharp, ((string[])(null)));
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("[SRS_TBF_EOL_PP_CHECK_FINAL_PARAM_001]_Check_Param_Sequence")]
[NUnit.Framework.CategoryAttribute("CheckFinalParamSequence")]
public virtual void SRS_TBF_EOL_PP_CHECK_FINAL_PARAM_001_Check_Param_Sequence()
public void SRS_TBF_EOL_PP_CHECK_FINAL_PARAM_001_Check_Param_Sequence()
{
string[] tagsOfScenario = new string[] {
"CheckFinalParamSequence"};
@@ -113,21 +113,11 @@ DN80 and the US2.", ProgrammingLanguage.CSharp, ((string[])(null)));
"\". \r\nCOMMENT: NCrunch needs programming parameters in a specific folder \r\n " +
" \"[SolutionPath]\\Hardware\\WaterMeter\\Genesis\\MockGenesis\\MockRawParameterExa" +
"mples\" as it copies all assemblies to a \r\n special NCrunch working fold" +
"er which is total separated from the project specific \"\\bin\\Debug\" folder!", tagsOfScenario, argumentsOfScenario);
"er which is total separated from the project specific \"\\bin\\Debug\" folder!", tagsOfScenario, argumentsOfScenario, featureTags);
#line 11
this.ScenarioInitialize(scenarioInfo);
#line hidden
bool isScenarioIgnored = default(bool);
bool isFeatureIgnored = default(bool);
if ((tagsOfScenario != null))
{
isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((this._featureTags != null))
{
isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
}
if ((isScenarioIgnored || isFeatureIgnored))
if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags)))
{
testRunner.SkipScenario();
}
@@ -76,7 +76,37 @@ Then Check the generic test results
# ================================================================================================================================
@CheckProductionProperties
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_003]_Check_ProductionOrderNo
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_003]_Check_CSD_Status
FUNCTION: Check the Customer Specific Document (CSD) for its status.
PRESETS: none
INPUTS: "CSD ID" the identification number of the CSD,
"CSD Name" a string for verbose identification,
"CSD Code" the code in SAP,
"CSD Status" giving the status "Null", "New", "Blocked", "Known" or "Released".
OUTPUT: "StatusReturn" with results "Okay", "Failed" or "Warning"
#--------------------------------- Prepare Tests ----------------------------------------------------------------------------
Given Initialize new test scenario for production process 'CheckProductionState'
And Name the object type holding the property 'StatusReturn'
And Name the private function to be checked for functionality 'ValidateCsd'
#--------------------------------- Define Test-Cases -------------------------------------------------------------------------
And Input matrix defining the test cases and results
| TestNo | CSD ID | CSD Name | CSD Code | CSD Status | StatusReturn | Comment |
| 1 | 3100782 | CSD 767978 Canal de Isabel | O1 | Released | Okay | Released CSD approved for production |
| 2 | 3100782 | CSD 767978 Canal de Isabel | O1 | Known | Warning | Known CSD temporary for production |
| 3 | 3100782 | CSD 767978 Canal de Isabel | O1 | New | Failed | New CSD invalid for production |
| 4 | 3100782 | CSD 767978 Canal de Isabel | O1 | Blocked | Failed | Blocked CSD invalid for production |
| 5 | null | CSD 767978 Canal de Isabel | O1 | Released | Failed | Invalid CSD ID invalid for production |
| 6 | 3100782 | null | O1 | Released | Failed | Invalid CSD Name invalid for production |
| 7 | 3100782 | CSD 767978 Canal de Isabel | null | Released | Failed | Invalid CSD Code invalid for production |
When Execute the generic tests
Then Check the generic test results
# ================================================================================================================================
@CheckProductionProperties
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_004]_Check_ProductionOrderNo
FUNCTION: Compare the Cordonel production-order with the production-order from the requirement to validate that the correct requirements
going to be used for the check of the actual production state.
PRESETS: none
@@ -100,7 +130,7 @@ Then Check the generic test results
# ================================================================================================================================
@CheckCordonelProperties
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_004]_Check_PcbId
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_005]_Check_PcbId
FUNCTION: Compare the PcbId from Cordonel, EOL-progress and requirement.
PRESETS: none
INPUTS: "Cordonel PcbId", EOL PcbId and "Requirement PcbId". If the requirement-PcbId id is "null" then the requirement is order based
@@ -129,7 +159,7 @@ Then Check the generic test results
# ================================================================================================================================
@CheckCordonelProperties
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_005]_Check_Region
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_006]_Check_Region
FUNCTION: Compare the Region from Cordonel and requirement.
PRESETS: none
INPUTS: "Cordonel Region" and "Requirement Region".
@@ -155,7 +185,7 @@ Then Check the generic test results
# ================================================================================================================================
@CheckCordonelProperties
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_006]_Check_LUT_CRC
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_007]_Check_LUT_CRC
FUNCTION: Check for the checksum (cyclic redundancy check - CRC) of the metrology lookup-table.
PRESETS: none
INPUTS: "Cordonel LUT CRC" and "Requirement LUT CRC" just a string representing a number in hex e.g. "0xEB7C.
@@ -182,7 +212,7 @@ Then Check the generic test results
# ================================================================================================================================
@CheckCordonelProperties
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_007]_Check_Meter_Size
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_008]_Check_Meter_Size
FUNCTION: Compare the Meter Size from Cordonel and requirement.
PRESETS: none
INPUTS: "Cordonel Meter Size" and "Requirement Meter Size" as strings naming the size as e.g. "DN50", "US1_5" or "1 1/2 INC".
@@ -209,7 +239,7 @@ Then Check the generic test results
# ================================================================================================================================
@CheckCordonelProperties
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_008]_Check_FW_and_Apps
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_009]_Check_FW_and_Apps
FUNCTION: Compare Cordonel installed Firmware and Apps with required Firmware.
All Apps will be verified for their versions and CRC.
The Core-Revision will be validated.
@@ -255,7 +285,7 @@ Then Check the generic test results
# ================================================================================================================================
@CheckKitronRadioSignalLevels
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_009]_Check_Kitron_Radio_Signal_Levels
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_010]_Check_Kitron_Radio_Signal_Levels
FUNCTION: Check if radio signal levels have been defined (contained in parameter list) FOR THIS cORDONEL SIZE AND LENGHT.
PRESETS: none
INPUTS: .
@@ -283,7 +313,7 @@ Then Check the generic test results
# ================================================================================================================================
@CheckLifetimes
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_010]_Check_Remaining_Battery_Load
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_011]_Check_Remaining_Battery_Load
FUNCTION: Compare the remaining battery load from Cordonel and the requirement.
PRESETS: none
INPUTS: "Normal Current [uA]" multiplied with exceeded lifetime together with "Production Drained [%] for an alternative threshold,
@@ -319,7 +349,7 @@ Then Check the generic test results
# ================================================================================================================================
@CheckLifetimes
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_011]_Check_Remaining_Lifetime
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_012]_Check_Remaining_Lifetime
FUNCTION: Compare the remaining life time reported by the Cordonel and the requirement.
PRESETS: none
INPUTS: "Actual Lifetime [days]" is the time in days the Cordonel after Kitron production,
@@ -347,7 +377,7 @@ Then Check the generic test results
# ================================================================================================================================
@CheckLifetimeS
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_012]_Check_Storage_Months
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_013]_Check_Storage_Months
FUNCTION: Compare the production date reported by the Cordonel with the actual date and check the requirement.
PRESETS: none
INPUTS: "Assembly Months before Now" as zero or negative floating point number or invalid if set to "null".
@@ -375,7 +405,7 @@ Then Check the generic test results
# ================================================================================================================================
@CheckCalibrations
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_013]_Check_Preadjustment
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_014]_Check_Preadjustment
FUNCTION: Check the database content reporting a flow-test preadjustment.
PRESETS: none
INPUTS: "Test Bench Fields" the array of test bench fields
@@ -405,7 +435,7 @@ Then Check the generic test results
# ================================================================================================================================
@CheckCalibrations
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_014]_Check_Flow_Test_State
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_015]_Check_Flow_Test_State
FUNCTION: Check the database content reporting the flow-test state.
PRESETS: none
INPUTS: "Test Bench Fields" the array of test bench fields
@@ -436,7 +466,7 @@ Then Check the generic test results
# ================================================================================================================================
@CheckCalibrations
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_015]_Check_Calib_Factors
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_016]_Check_Calib_Factors
FUNCTION: Compare the calibration values reported by the Cordonel with the flow-test calibration values reported by the database.
PRESETS: All calibration values not covered by this test in valid range.
INPUTS: "Meter Calib Fact 1/2/3" Meter calibration factors 1, 2 and 3 stored in and read out from the Cordonel,
@@ -466,7 +496,7 @@ Then Check the generic test results
# ================================================================================================================================
@CheckProductionStateProcess
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_016]_Check_Complete_Flow_Production_State
Scenario: [SRS_TBF_EOL_PP_CHECK_PROD_STATE_017]_Check_Complete_Flow_Production_State
FUNCTION: Check the entire production process sequence except the database calls with all underlaying sub-processes.
PRESETS: Preset all values with valid parameters to pass the test as "Okay" needed to successfully pass all underlaying tests:
- EOL Id | EOL StartDate | Cordonel PcbId | EOL PcbId
@@ -512,6 +542,10 @@ And Input initializations for the test matrix and presets to pass the preceding
| Standard Id | Standard Version | Standard IsActive | Standard IsApproved | Special Id | Special Version | Comment |
| 10 | 5 | X | X | null | null | Standard Requirement is active and approved |
And Input initializations for the test matrix and presets to pass the preceding tests
| CSD ID | CSD Name | CSD Code | CSD Status | Comment |
| 3100782 | CSD 767978 Canal de Isabel | O1 | Released | Released CSD approved for production |
And Input initializations for the test matrix and presets to pass the preceding tests
| Cordonel ProdOrderNo | Requirement ProdOrderNo | Comment |
| 3118323 | 3118323 | Order number is identical |
@@ -10,13 +10,31 @@
<StartupObject />
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;VPN_Debug</Configurations>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<Optimize>False</Optimize>
<IsPublishable>False</IsPublishable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<IsPublishable>False</IsPublishable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='VPN_Debug|AnyCPU'">
<IsPublishable>False</IsPublishable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="8.3.0" />
<PackageReference Include="Cucumber.Messages" Version="31.0.0" />
<PackageReference Include="FluentAssertions" Version="8.8.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NCrunch.Framework" Version="5.11.0" />
<PackageReference Include="NUnit" Version="4.3.2" />
<PackageReference Include="SpecFlow.NUnit" Version="3.3.57" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="5.2.0" />
<PackageReference Include="SpecFlow.NUnit" Version="3.9.74" />
</ItemGroup>
<ItemGroup>
@@ -5,9 +5,11 @@ using System.Linq;
using System.Reflection;
using FluentAssertions;
using LaaPackages.Features.Cordonel.Models;
using LaaPackages.Features.Cordonel.Models.Values;
using Logic.ProductionToProductMapper.Files.Fw;
using NUnit.Framework;
using NCrunch.Framework;
using Newtonsoft.Json;
using TechTalk.SpecFlow;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
@@ -33,37 +35,55 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
/// - This will be used to assign dynamic a function to be checked,
/// - If it is null or empty, it is not assigned.
/// </summary>
private String MethodToTest { set; get; }
private String MethodToTest
{
set; get;
}
/// <summary>
/// Marker for a private method.
/// </summary>
private Boolean IsPrivateMethod { set; get; }
private Boolean IsPrivateMethod
{
set; get;
}
/// <summary>
/// Name of the property to be checked:
/// - This will be used to assign dynamic a property to be checked.
/// </summary>
private String PropertyToCheck { set; get; }
private String PropertyToCheck
{
set; get;
}
/// <summary>
/// Name the object type holding the results of the test including the property:
/// - This will be used to assign dynamic a property to be checked.
/// </summary>
private String ObjectTypeName { set; get; }
private String ObjectTypeName
{
set; get;
}
/// <summary>
/// Required to test the EOL progress and combine it with the individual result:
/// - The required EOL progress will be crosschecked with the result of the EOL progress by eventually
/// several individual methods or by preceding production processes.
/// </summary>
private Boolean TestEolProgress { set; get; }
private Boolean TestEolProgress
{
set; get;
}
/// <summary>
/// The name of the production process to test. This is needed to call the correct type in the
/// <see cref="ExecTests"></see>
/// </summary>
private String TestName { set; get; }
private String TestName
{
set; get;
}
/// <summary>
/// The exec path of the assembly for debug in environment.
@@ -87,19 +107,31 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
/// </summary>
private struct TestResult
{
public Int32 TestNo { set; get; }
public Int32 TestNo
{
set; get;
}
public EOLProgressModel EOLProgress { set; get; }
public EOLProgressModel EOLProgress
{
set; get;
}
/// <summary>
/// Used if property cannot be checked by an EOL progress.
/// </summary>
public StatusReturn StatusReturn { set; get; }
public StatusReturn StatusReturn
{
set; get;
}
/// <summary>
/// Feedback message from tested routines
/// </summary>
public String FeedbackMsg { set; get; }
public String FeedbackMsg
{
set; get;
}
}
/// <summary>
@@ -107,63 +139,102 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
/// </summary>
private struct TestRequirement
{
public Int32 TestNo { set; get; }
public Int32 TestNo
{
set; get;
}
public String Comment { set; get; }
public String Comment
{
set; get;
}
public MockGenesis? Meter { set; get; }
public MockGenesis? Meter
{
set; get;
}
/// <summary>
/// Production requirements for check of production status and special requirements
/// </summary>
public CordonelRequirements ProductionRequirements { set; get; }
public CordonelRequirements ProductionRequirements
{
set; get;
}
/// <summary>
/// EOL Progress as input where every single EOL property will be used to set the expected feedback
/// to fulfill the single test.
/// </summary>
public EOLProgressModel RequiredEOLProgress { set; get; }
public EOLProgressModel RequiredEOLProgress
{
set; get;
}
/// <summary>
/// Status of the Cordonel
/// </summary>
public GenesisStatus CordonelDeviceStatus { set; get; }
public GenesisStatus CordonelDeviceStatus
{
set; get;
}
/// <summary>
/// Calibration register restorer of Cordonel
/// </summary>
public RegisterRestorer CalibCheckRegisterRestorer { set; get; }
public RegisterRestorer CalibCheckRegisterRestorer
{
set; get;
}
/// <summary>
/// Register restorer of Cordonel for parametrization
/// </summary>
public RegisterRestorer RegisterRestorer { set; get; }
public RegisterRestorer RegisterRestorer
{
set; get;
}
/// <summary>
/// Production status of the Cordonel
/// </summary>
public List<String> TestBenchState { set; get; }
public List<String> TestBenchState
{
set; get;
}
/// <summary>
/// Used for partially or full compare on string result return
/// </summary>
public Boolean PartiallyCompareStringResult { set; get; }
public Boolean PartiallyCompareStringResult
{
set; get;
}
/// <summary>
/// Loaded FW package from local disk
/// </summary>
public List<FilePart> FwUpdatePackage { set; get; }
public List<FilePart> FwUpdatePackage
{
set; get;
}
/// <summary>
/// Used if property cannot be checked by an EOL progress.
/// </summary>
public StatusReturn RequiredStatusReturn { set; get; }
public StatusReturn RequiredStatusReturn
{
set; get;
}
/// <summary>
/// Control to fail the Kitron radio param test
/// </summary>
public Boolean KillRadioParam { set; get; }
public Boolean KillRadioParam
{
set; get;
}
}
/// <summary>
@@ -184,7 +255,11 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
/// <summary>
/// Presets needed to pass preceding tests or initialize some properties to a defined state
/// </summary>
private TestRequirement _presets;
private String _presetTestCaseJson = "";
/// <summary>
/// Presets needed to pass preceding tests or initialize some properties to a defined state
/// </summary>
private TestRequirement _presetTestCase;
#endregion ----------------------------- Production State Properties ------------------------------------------
@@ -240,11 +315,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
// As here a new test step will be initialized and possibly the test-case table needs to be pre-filled for
// more complex tests of a huge routine to keep running until the real test to be executed will be reached,
// the test-cases and results have to be cleared here!
_presets.Meter = null;
_testCases.Clear();
_testResults.Clear();
_ppNamesExecSequence.Clear();
_presetTestCaseJson = "";
}
[Given(@"Name the property to be checked for '(.*)'")]
@@ -739,6 +813,31 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
}
}
/// <summary>
/// Change naming string to CSD Status
/// </summary>
/// <param name="strCsdStatusReturn"></param>
/// <returns></returns>
/// <remarks date="2025-Nov-25" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private static CSDStatus GetCsdStatusReturnFromName(String strCsdStatusReturn)
{
switch (strCsdStatusReturn)
{
case "Blocked":
return CSDStatus.Blocked;
case "New":
return CSDStatus.New;
case "Known":
return CSDStatus.Known;
case "Released":
return CSDStatus.Released;
default:
return CSDStatus.Null;
}
}
/// <summary>
/// Change EOLStatus enum to string
/// </summary>
@@ -797,34 +896,54 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
/// <remarks date="2024-Dec-10" author="Thomas Wiedebusch">
/// - Login to build App list and registers.
/// </remarks>
/// <remarks date="2025-Nov-21" author="Thomas Wiedebusch">
/// - New object with deep copy of presetTestCase using the Json serializer.
/// </remarks>
/// <remarks date="2025-Nov-22" author="Thomas Wiedebusch">
/// - Moved meter login for new test requirement and register restorers modification to
/// <see cref="NewTestRequirement"/>.
/// </remarks>
/// <remarks date="2025-Nov-25" author="Thomas Wiedebusch">
/// - Init register restorers.
/// </remarks>
/// <remarks date="2025-Nov-26" author="Thomas Wiedebusch">
/// - CSD parsing.
/// </remarks>
private void ReadTestTable(Table table)
{
// Generate list of tests out of the "Given" table and create test-cases
foreach (var row in table.Rows)
{
var testCase = NewTestRequirement();
// Check if presets are required, if so copy all properties
if (_presets.Meter != null)
TestRequirement testCase;
// Check if presets are required, if so copy all properties using the Json.Deserializer to create a
// new object as deep copy!!!
if (_presetTestCase.Meter != null)
{
ClassAccess.CopyAllProperties(testCase.Meter, _presets.Meter);
ClassAccess.CopyAllProperties(testCase.ProductionRequirements, _presets.ProductionRequirements);
ClassAccess.CopyAllProperties(testCase.RequiredEOLProgress, _presets.RequiredEOLProgress);
ClassAccess.CopyAllProperties(testCase.CalibCheckRegisterRestorer,
_presets.CalibCheckRegisterRestorer);
ClassAccess.CopyAllProperties(testCase.RegisterRestorer, _presets.RegisterRestorer);
ClassAccess.CopyAllProperties(testCase.CordonelDeviceStatus, _presets.CordonelDeviceStatus);
testCase.FwUpdatePackage.AddRange(_presets.FwUpdatePackage);
testCase.TestBenchState.AddRange(_presets.TestBenchState);
if (string.IsNullOrEmpty(_presetTestCaseJson))
{
_presetTestCaseJson = JsonConvert.SerializeObject(_presetTestCase);
}
// Create a new object with a deep copy of the _presetTestCase
testCase = JsonConvert.DeserializeObject<TestRequirement>(_presetTestCaseJson);
// Special meter properties which are not include in the deep copy
ClassAccess.CopyAllProperties(testCase.Meter, _presetTestCase.Meter);
testCase.RegisterRestorer = new RegisterRestorer(testCase.Meter);
testCase.CalibCheckRegisterRestorer = new RegisterRestorer(testCase.Meter);
// Enable register compare even if not physically read from meter
testCase.RegisterRestorer.EnableCompareExternalSetRegisters();
testCase.CalibCheckRegisterRestorer.EnableCompareExternalSetRegisters();
// Login is needed for building the MeterAppListVersion and generating the accessible register list.
// This register list will be used to check for valid ranges.
testCase.Meter?.Login();
}
// Preset is not required, a single testCase has to be created
else
{
testCase = NewTestRequirement();
}
// Enable register compare even if not physically read from meter
testCase.RegisterRestorer.EnableCompareExternalSetRegisters();
testCase.CalibCheckRegisterRestorer.EnableCompareExternalSetRegisters();
// Login is needed for building the MeterAppListVersion and generating the accessible register list.
// This register list will be used to check for valid ranges.
testCase.Meter?.Login();
// This is the multiple row test table defining individual test cases
ReadTable(row, ref testCase);
@@ -844,18 +963,19 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
/// <remarks date="2024-Dec-10" author="Thomas Wiedebusch">
/// - Login to build App list and registers.
/// </remarks>
/// <remarks date="2025-Nov-22" author="Thomas Wiedebusch">
/// - Moved meter login to <see cref="NewTestRequirement"/>.
/// </remarks>
private void ReadPresetTable(Table table)
{
if (_presets.Meter == null)
_presets = NewTestRequirement();
// Login is needed for building the MeterAppListVersion and generating the accessible register list.
// This register list will be used to check for valid ranges.
_presets.Meter?.Login();
if (_presetTestCase.Meter == null)
{
_presetTestCase = NewTestRequirement();
}
// A preset table is limited to exclusively one row, those settings will be used for every test of the
// test-cases!
ReadTable(table.Rows[0], ref _presets);
ReadTable(table.Rows[0], ref _presetTestCase);
}
/// <summary>
@@ -899,7 +1019,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
if (ParseInt(tableRow, "EOL Id", out strValue, out var intValue))
{
if (intValue != null) parsedTableRow.RequiredEOLProgress.Id = (Int32)intValue;
if (intValue != null)
parsedTableRow.RequiredEOLProgress.Id = (Int32)intValue;
}
var strHeader = "EOL StartDate";
@@ -976,17 +1097,44 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
if (ParseInt(tableRow, "Requirement ProdOrderNo", out strValue, out intValue))
{
if (intValue != null) parsedTableRow.ProductionRequirements.ProductionOrderNr = (Int32)intValue;
if (intValue != null)
parsedTableRow.ProductionRequirements.ProductionOrderNr = (Int32)intValue;
parsedTableRow.RequiredEOLProgress.RequirementOrderChecked = EOLStatus.OK;
}
#endregion
#region ----------------------------------- Parse CSD -----------------------------------------------------
if (ParseInt(tableRow, "CSD ID", out strValue, out intValue))
{
if (intValue != null)
parsedTableRow.ProductionRequirements.CSD.IdentId = (Int32)intValue;
}
if (ParseString(tableRow, "CSD Name", out strValue))
{
parsedTableRow.ProductionRequirements.CSD.Name = strValue;
}
if (ParseString(tableRow, "CSD Code", out strValue))
{
parsedTableRow.ProductionRequirements.CSD.Code = strValue;
}
if (ParseString(tableRow, "CSD Status", out strValue))
{
parsedTableRow.ProductionRequirements.CSD.Status = GetCsdStatusReturnFromName(strValue);
}
#endregion
#region ----------------------------------- Parse PcbId ---------------------------------------------------
if (ParseInt(tableRow, "Cordonel PcbId", out strValue, out intValue))
{
if (parsedTableRow.Meter != null) parsedTableRow.Meter.PcbId = strValue;
if (parsedTableRow.Meter != null)
parsedTableRow.Meter.PcbId = strValue;
}
if (ParseInt(tableRow, "Requirement PcbId", out strValue, out intValue))
@@ -997,7 +1145,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
if (ParseInt(tableRow, "EOL PcbId", out strValue, out intValue))
{
if (intValue != null) parsedTableRow.RequiredEOLProgress.PcbId = (Int32)intValue;
if (intValue != null)
parsedTableRow.RequiredEOLProgress.PcbId = (Int32)intValue;
}
#endregion
@@ -1006,7 +1155,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
if (ParseString(tableRow, "Cordonel LUT CRC", out strValue))
{
if (parsedTableRow.Meter != null) parsedTableRow.Meter.LutCrc = strValue;
if (parsedTableRow.Meter != null)
parsedTableRow.Meter.LutCrc = strValue;
}
if (ParseString(tableRow, "Requirement LUT CRC", out strValue))
@@ -1021,7 +1171,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
if (ParseString(tableRow, "Cordonel Region", out strValue))
{
if (parsedTableRow.Meter != null) parsedTableRow.Meter.Region = strValue;
if (parsedTableRow.Meter != null)
parsedTableRow.Meter.Region = strValue;
}
if (ParseString(tableRow, "Requirement Region", out strValue))
@@ -1104,7 +1255,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
if (parsedTableRow.Meter != null)
{
parsedTableRow.Meter.Login();
if (floatValue != null) parsedTableRow.Meter.CoreRevision = (Int32)(floatValue * 100);
if (floatValue != null)
parsedTableRow.Meter.CoreRevision = (Int32)(floatValue * 100);
parsedTableRow.Meter.StrCoreRevision = strValue;
}
}
@@ -1173,7 +1325,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
if (ParseFloat(tableRow, "Actual Lifetime [days]", out strValue, out floatValue))
{
if (floatValue != null) parsedTableRow.CordonelDeviceStatus.ExceededLifeTime_s =
if (floatValue != null)
parsedTableRow.CordonelDeviceStatus.ExceededLifeTime_s =
(UInt64)(floatValue * 24 * 3600);
parsedTableRow.RequiredEOLProgress.ProductionStatusBatteryLoadChecked = EOLStatus.OK;
parsedTableRow.RequiredEOLProgress.ProductionStatusRemainingLifeTimeChecked = EOLStatus.OK;
@@ -1399,7 +1552,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X") && !singleSelectionLock)
{
singleSelectionLock = true;
if (parsedTableRow.Meter != null) parsedTableRow.Meter.OrderNumber = 0;
if (parsedTableRow.Meter != null)
parsedTableRow.Meter.OrderNumber = 0;
parsedTableRow.RequiredEOLProgress.RequirementOrderChecked = EOLStatus.FAIL;
}
}
@@ -1429,7 +1583,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X"))
{
singleSelectionLock = true;
if (parsedTableRow.Meter != null) parsedTableRow.Meter.Region = "?";
if (parsedTableRow.Meter != null)
parsedTableRow.Meter.Region = "?";
parsedTableRow.RequiredEOLProgress.RequirementRegionChecked = EOLStatus.FAIL;
}
}
@@ -1444,7 +1599,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X"))
{
singleSelectionLock = true;
if (parsedTableRow.Meter != null) parsedTableRow.Meter.LutCrc = "?";
if (parsedTableRow.Meter != null)
parsedTableRow.Meter.LutCrc = "?";
parsedTableRow.RequiredEOLProgress.ProductionStatusLutCrc = "";
}
}
@@ -1560,7 +1716,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
{
if (!string.IsNullOrEmpty(strValue) && strValue.Equals("X") && !singleSelectionLock)
{
if (parsedTableRow.Meter != null) parsedTableRow.Meter.ReLogin();
if (parsedTableRow.Meter != null)
parsedTableRow.Meter.ReLogin();
PresetRegisterRestorer(parsedTableRow.CalibCheckRegisterRestorer, "GENESISFLOW_CalFactor1",
RegisterConverter.ValueToByteArray((UInt16)0), RegisterConverter.ValueToByteArray((UInt16)1));
parsedTableRow.RequiredEOLProgress.ProductionStatusCalibrationChecked = EOLStatus.FAIL;
@@ -1649,6 +1806,12 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
/// <remarks date="2024-Nov-19" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2025-Nov-22" author="Thomas Wiedebusch">
/// - Init meter and enable change of values for register restorers.
/// </remarks>
/// <remarks date="2025-Nov-26" author="Thomas Wiedebusch">
/// - CSD.
/// </remarks>
private static TestRequirement NewTestRequirement()
{
var testCase = new TestRequirement
@@ -1664,8 +1827,16 @@ namespace Xylem.Common.Production.ProductionUiCordonel.UnitTests.Steps
BatteryQuantity = 2
}
};
testCase.ProductionRequirements.CSD = new CordonelCSD();
testCase.RegisterRestorer = new RegisterRestorer(testCase.Meter);
testCase.CalibCheckRegisterRestorer = new RegisterRestorer(testCase.Meter);
// Enable register compare even if not physically read from meter
testCase.RegisterRestorer.EnableCompareExternalSetRegisters();
testCase.CalibCheckRegisterRestorer.EnableCompareExternalSetRegisters();
// Login is needed for building the MeterAppListVersion and generating the accessible register list.
// This register list will be used to check for valid ranges.
testCase.Meter?.Login();
return testCase;
}
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Net;
using LaaPackages.Features.Cordonel.Models;
using LaaPackages.Features.Cordonel.Models.Values;
using Newtonsoft.Json;
using Xylem.Common.CommonCore.Configuration;
using Xylem.Common.Logic.SoftwareAccessHelper;
@@ -122,7 +122,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
/// <inheritdoc />
public IGenesisMeter Meter
{
protected get; set;
get; set;
}
/// <inheritdoc />
@@ -520,10 +520,27 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
/// <remarks date="2024-Jan-17" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2025-Nov-17" author="Thomas Wiedebusch">
/// - StatusReturn.
/// </remarks>
protected void ProcessUpdate_Handler(Object sender, ProcessExecEventArgs e)
{
ActualProcessProgress++;
LogNewStatusMsg(e.ActualProcessMessage);
switch (e.StatusReturn)
{
case StatusReturn.Okay:
SuccessMsgDispatcher(e.ActualProcessMessage);
break;
case StatusReturn.Warning:
WarningMsgDispatcher(e.ActualProcessMessage);
break;
case StatusReturn.Failed:
ErrorMsgDispatcher(e.ActualProcessMessage);
break;
default:
LogNewStatusMsg(e.ActualProcessMessage);
break;
}
}
/// <summary>
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading;
using LaaPackages.Features.Cordonel.Models;
using LaaPackages.Features.Cordonel.Models.Values;
using Newtonsoft.Json;
using Xylem.Common.CommonCore.Configuration;
using Xylem.Common.CommonCore.Consts;
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using LaaPackages.Features.Cordonel.Models;
using LaaPackages.Features.Cordonel.Models.Values;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
//using Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes;
@@ -14,7 +14,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <summary>
/// Ctor
/// </summary>
public CheckFinalParametrization(String name) : base( name)
public CheckFinalParametrization(String name) : base(name)
{
}
@@ -49,6 +49,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2025-Apr-07" author="Thomas Wiedebusch">
/// - CancellationToken.
/// </remarks>
/// <remarks date="2025-Nov-17..19" author="Thomas Wiedebusch">
/// - Restore registers after reboot if failed - temporary workaround for FW-bug.
/// </remarks>
public override StatusReturn ExecuteProcess()
{
// Uncheck executed process to leave the result open needed for retries
@@ -83,13 +86,13 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
RegisterRestorer.OnProcessUpdate += ProcessUpdate_Handler;
var retValBol = RegisterRestorer.FinalReadRegisters(CancellationToken);
RegisterRestorer.OnProcessUpdate -= ProcessUpdate_Handler;
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
RegisterRestorer.OnProcessUpdate += ProcessUpdate_Handler;
retValBol &= RegisterRestorer.CompareRegisters(CancellationToken);
retValBol &= RegisterRestorer.CompareRegisters(RegisterRestorer.RecoveryRegisters, CancellationToken);
RegisterRestorer.OnProcessUpdate -= ProcessUpdate_Handler;
if (retValBol)
@@ -98,6 +101,29 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
Meter?.Logout();
return StatusReturn.Okay;
}
// Restore registers after reboot if failed
if (RegisterRestorer.HasRestoreRegistersAfterRebootIfCompareFailed)
{
WarningMsgDispatcher(Resources.StrWarningMsgParameterCompare);
// Write and compare those registers
RegisterRestorer.OnProcessUpdate += ProcessUpdate_Handler;
retValBol = RegisterRestorer.RestoreAndCompareRegistersAfterReboot(CancellationToken);
RegisterRestorer.OnProcessUpdate -= ProcessUpdate_Handler;
if (retValBol)
{
if (RegisterRestorer.RestoreRegistersAfterReboot.Count ==
RegisterRestorer.FailedComparisonRegisters.Count)
{
EolProgress.ParametrizationCompareChecked = EOLStatus.OK;
Meter?.Logout();
// Display a warning for the 'after reboot' parameter recovery
return StatusReturn.Warning;
}
}
}
// No chance to repair anything
EolProgress.ParametrizationCompareChecked = EOLStatus.FAIL;
Meter?.Logout();
return StatusReturn.Failed;
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows;
using LaaPackages.Features.Cordonel.Models;
using LaaPackages.Features.Cordonel.Models.Values;
using Logic.ProductionToProductMapper.Files.Fw;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
@@ -26,7 +26,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <summary>
/// Maximum of expected processing steps for this process to feed the GUI progress bar.
/// </summary>
private const Int32 MaxActualProgressSteps = 23;
private const Int32 MaxActualProgressSteps = 24;
/// <summary>
/// Successfully test on flow test bench will return this const value.
@@ -82,8 +82,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// </remarks>
public override StatusReturn PreExecuteProcess()
{
if (Meter == null ||
string.IsNullOrEmpty(Meter.PcbId) ||
if (Meter == null ||
string.IsNullOrEmpty(Meter.PcbId) ||
Meter.PcbId.Length < GenesisMeter.MinPcbIdLength ||
EolProgress == null)
{
@@ -248,7 +248,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
// Activate logging to output
CalibCheckRegisterRestorer.OnProcessUpdate += ProcessUpdate_Handler;
// Read out all parameters from meter and compare it
if (!CalibCheckRegisterRestorer.ReadPresetRegisters(CancellationToken))
if (!CalibCheckRegisterRestorer.ReadPresetRegisters(CalibCheckRegisterRestorer.RecoveryRegisters,
CancellationToken))
{
// Stop logging
CalibCheckRegisterRestorer.OnProcessUpdate -= ProcessUpdate_Handler;
@@ -276,6 +277,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// be completed as the PcbId and the order number is needed.
/// Checks:
/// - Requirements are collected,
/// - Validate CSD,
/// - EOL progress for Sentinel can be generated,
/// - PcbId,
/// - Region,
@@ -331,8 +333,14 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2025-Mai-28" author="Thomas Wiedebusch">
/// - Check radio signal levels in parameter list.
/// </remarks>
/// <remarks date="2025-Nov-21" author="Thomas Wiedebusch">
/// - Validate CSD.
/// </remarks>
public override StatusReturn ExecuteProcess()
{
var erroneousSequenceStep = false;
var warningSequenceStep = false;
#region ----------------------------------- Check EOL Progress --------------------------------------------
if (string.IsNullOrEmpty(CheckEolProgress(out var feedbackMsg)))
{
@@ -344,7 +352,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
#endregion
#region ----------------------------------- Check Requirements --------------------------------------------
var eolStatus = CheckRequirements(out feedbackMsg);
if (EOLStatus.FAIL == eolStatus)
{
@@ -356,8 +363,30 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check Production Order ----------------------------------------
#region ----------------------------------- Validate CSD -------------------------------------------------
var statusReturn = ValidateCsd(out feedbackMsg);
if (StatusReturn.Failed == statusReturn)
{
ErrorMsgDispatcher(feedbackMsg);
//return StatusReturn.Failed;
// Do not return immediately to avoid multiple special-requirements on multiple errors
erroneousSequenceStep = true;
}
if (StatusReturn.Warning == statusReturn)
{
WarningMsgDispatcher(feedbackMsg);
//TODO THW removed until 'CSDStatus.Released' is the only valid status
//warningSequenceStep = true;
}
if (StatusReturn.Okay == statusReturn)
{
SuccessMsgDispatcher(feedbackMsg);
}
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check Production Order ----------------------------------------
// Production order
if (EOLStatus.FAIL == CheckProductionOrderNumber(out feedbackMsg))
{
@@ -385,7 +414,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
#endregion
#region ----------------------------------- Check Region --------------------------------------------------
if (EOLStatus.FAIL == CheckRegion(out feedbackMsg))
{
ErrorMsgDispatcher(feedbackMsg);
@@ -397,7 +425,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
#endregion
#region ----------------------------------- Check LUT CRC -------------------------------------------------
if (string.IsNullOrEmpty(CheckLutCrc(out feedbackMsg)))
{
ErrorMsgDispatcher(feedbackMsg);
@@ -409,7 +436,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
#endregion
#region ----------------------------------- Check Meter Size ----------------------------------------------
if (string.IsNullOrEmpty(CheckMeterSize(out feedbackMsg)))
{
ErrorMsgDispatcher(feedbackMsg);
@@ -421,7 +447,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
#endregion
#region ----------------------------------- Check Firmware and Apps ---------------------------------------
if (string.IsNullOrEmpty(CheckFwAndApps(out feedbackMsg)))
{
ErrorMsgDispatcher(feedbackMsg);
@@ -433,10 +458,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
#endregion
#region ----------------------------------- Check Kitron Radio Signal Levels ------------------------------
var erroneousSequenceStep = false;
var warningSequenceStep = false;
var statusReturn = CheckKitronRadioSignalLevels(out feedbackMsg);
statusReturn = CheckKitronRadioSignalLevels(out feedbackMsg);
if (StatusReturn.Failed == statusReturn)
{
@@ -459,7 +481,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
#endregion
#region ----------------------------------- Check Remaining Battery Load ----------------------------------
if (EOLStatus.FAIL == CheckRemainingBatteryLoad(out feedbackMsg))
{
ErrorMsgDispatcher(feedbackMsg);
@@ -476,7 +497,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
#endregion
#region ----------------------------------- Check Remaining Life Time -------------------------------------
if (EOLStatus.FAIL == CheckRemainingLifetime(out feedbackMsg))
{
ErrorMsgDispatcher(feedbackMsg);
@@ -493,7 +513,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
#endregion
#region ----------------------------------- Check Storage Months ------------------------------------------
if (EOLStatus.FAIL == CheckStorageMonths(out feedbackMsg))
{
ErrorMsgDispatcher(feedbackMsg);
@@ -510,7 +529,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
#endregion
#region ----------------------------------- Check Preadjusment --------------------------------------------
eolStatus = CheckPreadjustment(out feedbackMsg);
if (EOLStatus.FAIL == eolStatus)
{
@@ -533,7 +551,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
#endregion
#region ----------------------------------- Check Flow Test State -----------------------------------------
eolStatus = CheckFlowTestState(out feedbackMsg);
if (EOLStatus.FAIL == eolStatus)
{
@@ -556,7 +573,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
#endregion
#region ----------------------------------- Check Calibration Values --------------------------------------
// ATTENTION:
// This step may exit with "skipped" here if "special-requirement" forces to skip the check of calibration.
// All following checks will be skipped as well.
@@ -588,7 +604,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
#endregion
#region ----------------------------------- Summarize Check Results ---------------------------------------
// One or more checks failed
if (erroneousSequenceStep)
return StatusReturn.Failed;
@@ -707,6 +722,30 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
return EolProgress.RequirementRequestChecked;
}
/// <summary>
/// Validate the CSD
///
/// Software Requirement Specification:
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_016]_Validate_CSD
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Nov-21..26" author="Thomas Wiedebusch">
/// - initial.
/// </remarks>
private StatusReturn ValidateCsd(out String feedbackMsg)
{
feedbackMsg = "";
if (ProductionRequirements == null )
{
feedbackMsg = Resources.StrErrorMsgProductionRequirements;
return StatusReturn.Failed;
}
return GenericChecks.ValidateCsd(ProductionRequirements, out feedbackMsg);
}
/// <summary>
/// Check production order number between meter and requirements.
///
@@ -1142,7 +1181,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
CalibCheckRegisterRestorer.OnProcessUpdate += ProcessUpdate_Handler;
// Compare the database value and the meter value (read in advance in the PreExecuteProcess)
if (!CalibCheckRegisterRestorer.CompareRegisters(CancellationToken))
if (!CalibCheckRegisterRestorer.CompareRegisters(CalibCheckRegisterRestorer.RecoveryRegisters,
CancellationToken))
{
// Stop logging
CalibCheckRegisterRestorer.OnProcessUpdate -= ProcessUpdate_Handler;
@@ -1,5 +1,4 @@
using LaaPackages.Features.Cordonel.Models;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq;
using SIRTCOM;
using System;
using System.Collections.Generic;
@@ -8,6 +7,7 @@ using System.Net;
using System.Threading;
using System.Windows.Threading;
using System.Xml.Linq;
using LaaPackages.Features.Cordonel.Models.Values;
using Xylem.Common.CommonCore.Configuration;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig;
@@ -189,6 +189,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2025-Oct-13..14" author="Thomas Wiedebusch">
/// - Service SIRT verbose output and additional error handling and warning.
/// </remarks>
/// <remarks date="2025-Nov-26" author="Thomas Wiedebusch">
/// - Exit with error if database content for RSSI cannot be loaded.
/// </remarks>
public override StatusReturn ExecuteProcess()
{
// This will force to reset all values, Sentinel and Prepare shipping will fail based on EOL Progress
@@ -292,12 +295,12 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
}
var ServicePorts = new List<Int32>();
if (sirtConfig.ServiceSirtComport433MHz != null &&
if (sirtConfig.ServiceSirtComport433MHz != null &&
int.TryParse(sirtConfig.ServiceSirtComport433MHz.Replace("COM", ""), out var serviceSirtComport))
{
ServicePorts.Add(serviceSirtComport);
}
if (sirtConfig.ServiceSirtComport868MHz != null &&
if (sirtConfig.ServiceSirtComport868MHz != null &&
int.TryParse(sirtConfig.ServiceSirtComport868MHz.Replace("COM", ""), out serviceSirtComport))
{
ServicePorts.Add(serviceSirtComport);
@@ -351,7 +354,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
// Clear the measurement as on a retry started from main control the old values will be displayed
ClearRssiMeasurement();
// kick off RSSI test
StartRssiMeasurement();
if (!StartRssiMeasurement(out var errorMsg))
return SetTestFailed(errorMsg);
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
@@ -525,7 +529,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
private void UpdateUc()
{
_checkRadioUserControl.SetUi(new UcRadioCheck.UiContainer(_rssiPamSirtDbMin, _rssiSemiCordonelDbMin,
_rssiPamSirtMeasuredByCordonel, _rssiSemiCordonelMeasuredBySirt, _rssiPamSirtDbMax, _rssiSemiCordonelDbMax));
_rssiPamSirtMeasuredByCordonel, _rssiSemiCordonelMeasuredBySirt, _rssiPamSirtDbMax,
_rssiSemiCordonelDbMax));
}
/// <summary>
@@ -535,8 +540,12 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="????" author="Roland Drabesch">
/// - Initial
/// </remarks>
private void StartRssiMeasurement()
/// <remarks date="2025-Nov-26" author="Thomas Wiedebusch">
/// - Exit with error if database content for RSSI cannot be loaded.
/// </remarks>
private Boolean StartRssiMeasurement(out String errorMsg)
{
errorMsg = "";
// disable all buttons during ongoing measurement process
DisableAllButtons();
UpdateUc();
@@ -544,7 +553,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
try
{
ActualProcessProgress++;
GetRssiMinMaxValuesFromDb(_sirtBoxNr, Meter.OrderNumber);
if (!GetRssiMinMaxValuesFromDb(_sirtBoxNr, Meter.OrderNumber, out errorMsg))
return false;
SendPamListenSemi((Int32)_radioAddressCordonel, _rssiSirtComport, _radioEncryptionKey);
UpdateUc();
ActualProcessProgress++;
@@ -554,7 +564,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
_testFinished = true;
Meter?.WriteLog(ex.Message);
LogNewStatusMsg(Resources.StrErrorMsgRadioCheck);
return false;
}
return true;
}
/// <summary>
@@ -675,8 +688,12 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="????" author="Roland Drabesch">
/// - Initial
/// </remarks>
private void GetRssiMinMaxValuesFromDb(Byte BoxNr, Int32 productionOrderNo)
/// <remarks date="2025-Nov-26" author="Thomas Wiedebusch">
/// - Exit with error if database content for RSSI cannot be loaded.
/// </remarks>
private Boolean GetRssiMinMaxValuesFromDb(Byte BoxNr, Int32 productionOrderNo, out String errorMsg)
{
errorMsg = "";
try
{
if (_dataContainer != null)
@@ -685,8 +702,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
var body = LocalWebRequest.GetRequest(url, 300000, out var httpStatus);
if (httpStatus != HttpStatusCode.OK || string.IsNullOrEmpty(body))
{
ErrorMsgDispatcher(Resources.StrErrorMsgGetRadioRssiLimitsFromDb);
return;
errorMsg = Resources.StrErrorMsgGetRadioRssiLimitsFromDb;
return false;
}
var o = JObject.Parse(body);
@@ -707,8 +724,11 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
catch (Exception ex)
{
Meter?.WriteLog(ex.Message);
ErrorMsgDispatcher(Resources.StrErrorMsgGetRadioRssiLimitsFromDb);
errorMsg = Resources.StrErrorMsgGetRadioRssiLimitsFromDb;
return false;
}
return true;
}
/// <summary>
@@ -130,7 +130,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
productionProcessNo: 0,
// CheckNewMeter will exit with repeated detect or connect
nextStateOnSuccess: ProcessState.CheckNewMeter),
new ProcessStateStruct(
productionProcess: new ConnectCordonel(Resources.StrStateConnectCordonel),
processState: ProcessState.ConnectCordonel,
@@ -147,17 +147,14 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
nextStateOnSuccess: BaseProductionProcess.DoNotChangeStateAfterCompletion,
// Kick off parallel process execution
kickOffParallelState: ProcessState.GetProgrammingParameters),
new ProcessStateStruct(
productionProcess: new GetFinalParametrization(Resources.StrStateGetProgrammingParameters),
processState: ProcessState.GetProgrammingParameters,
processInfo: Resources.StrStateGetProgrammingParameters,
productionProcessNo: 3,
// Avoid automatic change after completion as parallel process is active
nextStateOnSuccess: BaseProductionProcess.DoNotChangeStateAfterCompletion,
// Kick off parallel process execution
kickOffParallelState: ProcessState.CreateEolProgress),
nextStateOnSuccess: ProcessState.CreateEolProgress),
#endregion -------------------------------- GENERIC-STATES --------------------------------------------
#region ----------------------------------- EOL-STATES ------------------------------------------------
@@ -168,7 +165,16 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
processState: ProcessState.CreateEolProgress,
processInfo: Resources.StrStateCreateEolProgress,
productionProcessNo: null,
nextStateOnSuccess: ProcessState.ProcessPasswordFile),
nextStateOnSuccess: ProcessState.ExecFinalParametrization),
new ProcessStateStruct(
productionProcess: new ExecFinalParametrization(Resources.StrStateFinalParametrization),
processState: ProcessState.ExecFinalParametrization,
processInfo: Resources.StrStateFinalParametrization,
productionProcessNo: 4,
nextStateOnSuccess: ProcessState.ProcessPasswordFile,
// Wait for completion of all processes to avoid side effects
waitForAllProcesses: true),
// Precondition: - The connection process has to be completed as a password change in the DB
// will take place: Skeleton to Password Level 8. Not waiting for completion may cause side
@@ -179,23 +185,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
productionProcess: new ExecPasswordFile(Resources.StrStatePasswordFile),
processState: ProcessState.ProcessPasswordFile,
processInfo: Resources.StrStatePasswordFile,
productionProcessNo: 4,
nextStateOnSuccess: ProcessState.ExecFinalParametrization,
// Wait for completion of all processes to avoid side effects
waitForAllProcesses: true),
new ProcessStateStruct(
productionProcess: new ExecFinalParametrization(Resources.StrStateFinalParametrization),
processState: ProcessState.ExecFinalParametrization,
processInfo: Resources.StrStateFinalParametrization,
productionProcessNo: 5,
nextStateOnSuccess: ProcessState.ExecStoreConfiguration),
new ProcessStateStruct(
productionProcess: new ExecStoreConfiguration(Resources.StrStateStoreConfiguration),
processState: ProcessState.ExecStoreConfiguration,
processInfo: Resources.StrStateStoreConfiguration,
productionProcessNo: 6,
nextStateOnSuccess: ProcessState.CheckProductionState),
// Parametrization has to be completed as those needed to check e.g. the remaining FW based lifetime.
@@ -203,8 +193,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
productionProcess: new CheckProductionState(Resources.StrStateCheckProductionState),
processState: ProcessState.CheckProductionState,
processInfo: Resources.StrStateCheckProductionState,
productionProcessNo: 7,
nextStateOnSuccess: ProcessState.RebootCordonel),
productionProcessNo: 6,
nextStateOnSuccess: ProcessState.ExecStoreConfiguration),
new ProcessStateStruct(
productionProcess: new ExecStoreConfiguration(Resources.StrStateStoreConfiguration),
processState: ProcessState.ExecStoreConfiguration,
processInfo: Resources.StrStateStoreConfiguration,
productionProcessNo: 7,
nextStateOnSuccess: ProcessState.RebootCordonel),
// Precondition: - The store configuration has to be executed to safe all settings
// to non-volatile memory and checked if remaining valid after the reboot.
@@ -1,6 +1,6 @@
using System;
using System.Linq;
using LaaPackages.Features.Cordonel.Models;
using LaaPackages.Features.Cordonel.Models.Values;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
@@ -2,7 +2,7 @@
using System.Linq;
using System.Text;
using System.Threading;
using LaaPackages.Features.Cordonel.Models;
using LaaPackages.Features.Cordonel.Models.Values;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd;
@@ -1,5 +1,5 @@
using System;
using LaaPackages.Features.Cordonel.Models;
using LaaPackages.Features.Cordonel.Models.Values;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
@@ -77,7 +77,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
return StatusReturn.Failed;
}
}
var ale = Meter.ReadRegister("CUSTOMER_AlarmEnableMask");
// Read and automatically log it to database if logging is active
Meter.ReadRegister("CUSTOMER_AlarmEnableMask");
#region ----------------------------------- Reboot ----------------------------------------------------
// reboot the meter and avoid doubled storage of configurations
var _meterResetPsu = new MeterResetPsu(Meter)
@@ -123,7 +125,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
ProgrammingSource.ProgramInternal));
ActualProcessProgress++;
// Reset reboot counter
retValBol &= WriteRegisterLogAndProcessCtr(new ProgrammingParameters("CUSTOMER_RebootCount",
retValBol &= WriteRegisterLogAndProcessCtr(new ProgrammingParameters("CUSTOMER_RebootCount",
new Byte[] { 0x00 }, ProgrammingSource.ProgramInternal));
// Reset alarms Customer
retValBol &= WriteRegisterLogAndProcessCtr(new ProgrammingParameters("CUSTOMER_TriggerAlarmCancel",
@@ -173,11 +175,11 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
var retryCtr = 2;
// Check all values for 0 after reset and followed reboot which may use the
var readBackScaledBilling =
var readBackScaledBilling =
RegisterConverter.ByteArrayToValue<Int64>(Meter.ReadRegister("GENESISFLOW_ScaledBilling"));
var readBackUnscaledFwd =
var readBackUnscaledFwd =
RegisterConverter.ByteArrayToValue<UInt64>(Meter.ReadRegister("GENESISFLOW_UnscaledFwd"));
var readBackUnscaledRev =
var readBackUnscaledRev =
RegisterConverter.ByteArrayToValue<UInt64>(Meter.ReadRegister("GENESISFLOW_UnscaledRev"));
while (retryCtr-- > 0 &&
@@ -186,11 +188,11 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
// Backup sealing state as for the NA region it may be unsealed
var displaySealingState =
RegisterConverter.ByteArrayToValue<Byte>(Meter.ReadRegister("GENESISFLOW_SealDisplay"));
// Unseal accumulator reset capability
retValBol = WriteRegisterLogAndProcessCtr(new ProgrammingParameters("GENESISFLOW_SealDisplay",
new Byte[] { 0x00 }, ProgrammingSource.ProgramInternal));
// Reset accumulators again
retValBol &= WriteRegisterLogAndProcessCtr(new ProgrammingParameters("GENESISFLOW_ResetAccumulators",
new Byte[] { 0x01 }, ProgrammingSource.ProgramInternal));
@@ -201,7 +203,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
// it doesn't have to be a change.
retValBol &= WriteRegisterLogAndProcessCtr(new ProgrammingParameters("GENESISFLOW_ForwardArrow",
new[] { forwardArrow }, ProgrammingSource.ProgramInternal));
// Seal accumulator reset capability
// ReSharper disable once RedundantAssignment as it is used for DEBUG
retValBol &= WriteRegisterLogAndProcessCtr(new ProgrammingParameters("GENESISFLOW_SealDisplay",
@@ -224,7 +226,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
EolProgress.RebootDoneChecked = EOLStatus.FAIL;
return StatusReturn.Failed;
}
#endregion
#endregion
SuccessMsgDispatcher(Resources.StrSuccessMsgAlarmReset);
EolProgress.RebootDoneChecked = EOLStatus.OK;
return StatusReturn.Okay;
@@ -1,5 +1,5 @@
using System;
using LaaPackages.Features.Cordonel.Models;
using LaaPackages.Features.Cordonel.Models.Values;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
@@ -4,6 +4,7 @@ using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using LaaPackages.Features.Cordonel.Models.Values;
using Xylem.Common.CommonCore.Configuration;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications;
@@ -40,10 +41,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
{
if (meter == null ||
productionRequirements == null ||
(assemblyLine && productionRequirements.RequiredLifeTimeYearsAssembly == null) ||
(assemblyLine && productionRequirements.RequiredLifeTimeYearsAssembly <= 0) ||
(!assemblyLine && productionRequirements.RequiredLifeTimeYearsShipping == null) ||
(!assemblyLine && productionRequirements.RequiredLifeTimeYearsShipping <= 0) ||
assemblyLine && productionRequirements.RequiredLifeTimeYearsAssembly == null ||
assemblyLine && productionRequirements.RequiredLifeTimeYearsAssembly <= 0 ||
!assemblyLine && productionRequirements.RequiredLifeTimeYearsShipping == null ||
!assemblyLine && productionRequirements.RequiredLifeTimeYearsShipping <= 0 ||
productionStatus == null)
{
feedbackMsg = Resources.StrErrorMsgLifeTimeCalculation;
@@ -65,16 +66,16 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
var strFwCalculatedLifeTimeYears = "";
if (productionStatus.FwCalculatedRemainingLifeTimeYears != null)
{
strFwCalculatedLifeTimeYears =
strFwCalculatedLifeTimeYears =
$@" - {Resources.StrMsgRemainingLifeTime}: " +
$"{productionStatus.FwCalculatedRemainingLifeTimeYears:F2} " +
$"{productionStatus.FwCalculatedRemainingLifeTimeYears:F2} " +
$"{Resources.StrMsgYears} (Firmware)";
}
// Build message for required versus calculated lifetime
var lifeTime = $"{Resources.StrMsgRemainingLifeTime}: " +
// Actual SW calculated lifetime based on readings from meter
$"{productionStatus.RemainingLifeTimeYears:F2} " +
$"{Resources.StrMsgYears} (Software)" +
$"{Resources.StrMsgYears} (Software)" +
// Optional FW calculation
strFwCalculatedLifeTimeYears +
$" - {Resources.StrMsgRequiredLifeTime}: " +
@@ -83,12 +84,12 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
$"{Resources.StrMsgYears}";
// Check required years for assembly
if ((assemblyLine &&
productionRequirements.RequiredLifeTimeYearsAssembly > productionStatus.RemainingLifeTimeYears) ||
(!assemblyLine &&
(productionRequirements.RequiredLifeTimeYearsShipping > productionStatus.RemainingLifeTimeYears ||
// internally in FW calculated remaining lifetime visual for customer in DIAVASO
productionRequirements.RequiredLifeTimeYearsShipping > productionStatus.FwCalculatedRemainingLifeTimeYears)))
if (assemblyLine &&
productionRequirements.RequiredLifeTimeYearsAssembly > productionStatus.RemainingLifeTimeYears ||
!assemblyLine &&
(productionRequirements.RequiredLifeTimeYearsShipping > productionStatus.RemainingLifeTimeYears ||
// internally in FW calculated remaining lifetime visual for customer in DIAVASO
productionRequirements.RequiredLifeTimeYearsShipping > productionStatus.FwCalculatedRemainingLifeTimeYears))
{
feedbackMsg = $"{Resources.StrErrorMsg}: {lifeTime}";
return StatusReturn.Failed;
@@ -134,8 +135,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
var actualDate = DateTime.UtcNow.Date;
var batteryAssemblyDate = productionStatus.BatteryAssemblyDateTimeUtc.Value.Date;
// Give one day more as rounding issue
var expirationDate = batteryAssemblyDate.AddDays((Double)productionRequirements.MaxStorageMonths *
// Allow one day more as rounding issue
var expirationDate = batteryAssemblyDate.AddDays((Double)productionRequirements.MaxStorageMonths *
GenesisStatusHandler.DAYS_PER_MONTH + 1.0);
var strStorageProduction = $"{Resources.StrLblKitronProductionDate}: " +
$"{productionStatus.BatteryAssemblyDateTimeUtc.Value:dd-MMM-yyyy} - " +
@@ -179,8 +180,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
{
if (meter == null ||
productionRequirements == null ||
(assemblyLine && productionRequirements.MaxDrainedBatteryLoadPercentAssembly == null) ||
(!assemblyLine && productionRequirements.MaxDrainedBatteryLoadPercentShipping == null) ||
assemblyLine && productionRequirements.MaxDrainedBatteryLoadPercentAssembly == null ||
!assemblyLine && productionRequirements.MaxDrainedBatteryLoadPercentShipping == null ||
productionStatus == null)
{
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
@@ -231,10 +232,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
var strStation = assemblyLine
? $"{productionRequirements.MaxDrainedBatteryLoadPercentAssembly:F2} %"
: $"{productionRequirements.MaxDrainedBatteryLoadPercentShipping:F2} %";
// Create an output message addon for alternative power consumption calculation
var strAlternativeCalc = productionStatus.AlternativeRequiredBatteryLoadPercent == null
? "" : $" - {Resources.StrMsgRequirementAbsolutLimitDrainedBattery}: " + strStation ;
? "" : $" - {Resources.StrMsgRequirementAbsolutLimitDrainedBattery}: " + strStation;
var strBatLoad = $"{Resources.StrMsgDrainedBattery}: " +
// Actual calculated battery load based on readings from meter
@@ -245,10 +246,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
// This is the absolut limit even if the "AlternativeRequiredBatteryLoadPercent" will be used
if ((assemblyLine && productionStatus.DrainedBatteryLoadPercent >
productionRequirements.MaxDrainedBatteryLoadPercentAssembly) ||
(!assemblyLine && productionStatus.DrainedBatteryLoadPercent >
productionRequirements.MaxDrainedBatteryLoadPercentShipping))
if (assemblyLine && productionStatus.DrainedBatteryLoadPercent >
productionRequirements.MaxDrainedBatteryLoadPercentAssembly ||
!assemblyLine && productionStatus.DrainedBatteryLoadPercent >
productionRequirements.MaxDrainedBatteryLoadPercentShipping)
{
feedbackMsg = $"{Resources.StrErrorMsg}: {strBatLoad}";
return StatusReturn.Failed;
@@ -450,12 +451,12 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
isStandardActive = true;
// At least one requirement needs to be active and a requirement number and version has to be set
if ((!isStandardActive && !isSpecialActive) ||
if (!isStandardActive && !isSpecialActive ||
// The standard Id and version have always to be set even if the special is active
productionRequirements.StandardId == null || productionRequirements.StandardVersion == null ||
// If the special is active the version and Id have to be defined
(isSpecialActive && (productionRequirements.SpecialId == null ||
productionRequirements.SpecialVersion == null)))
isSpecialActive && (productionRequirements.SpecialId == null ||
productionRequirements.SpecialVersion == null))
{
feedbackMsg = Resources.StrErrorMsgRequirementInactive;
return StatusReturn.Failed;
@@ -472,6 +473,67 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
return StatusReturn.Okay;
}
/// <summary>
/// Validate the CSD:
/// - A CSD number is needed,
/// - The CSD status has to allow production:
/// - <see cref="CSDStatus.Blocked"/> are not allowed for production
/// - <see cref="CSDStatus.New"/> are not allowed for production
/// - <see cref="CSDStatus.Known"/> are all the today's CSD for production 2025-Nov-21 (but not released)
/// - <see cref="CSDStatus.Released"/> are all the today's CSD for production 2025-Nov-21 (but not released)
/// </summary>
/// <param name="productionRequirements"></param>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Nov-21" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static StatusReturn ValidateCsd(CordonelRequirements productionRequirements, out String feedbackMsg)
{
feedbackMsg = "";
if (productionRequirements == null)
{
feedbackMsg = Resources.StrErrorMsgProductionRequirements;
return StatusReturn.Failed;
}
// Extract CSD information.
var csd = productionRequirements.CSD;
if (csd == null ||
csd.Status == CSDStatus.Null ||
string.IsNullOrEmpty(csd.Name) ||
string.IsNullOrEmpty(csd.Code) ||
csd.IdentId == 0)
{
feedbackMsg = Resources.StrErrorMsgCsdNotReceived;
return StatusReturn.Failed;
}
var csdId = $"{csd.Name} - Code: {csd.Code} - ID: {csd.IdentId}";
// Released is the best case
if (csd.Status == CSDStatus.Released)
{
feedbackMsg = $"{Resources.StrSuccessMsgCsdValid} - {csdId}";
return StatusReturn.Okay;
}
// Known are all the today's CSD for production 2025-Nov-21 (but not released)
if (csd.Status == CSDStatus.Known)
{
feedbackMsg = $"{Resources.StrWarningMsgCsdKnown} - {csdId}";
return StatusReturn.Warning;
}
if (csd.Status == CSDStatus.New)
{
feedbackMsg = $"{Resources.StrErrorMsgCsdNewNotApproved} - {csdId}";
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrErrorMsgCsdBlocked} - {csdId}";
return StatusReturn.Failed;
}
/// <summary>
/// Check the measurements from Kitron for this specific Cordonel if it is an EMEA region.
/// </summary>
@@ -521,8 +583,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
if (!registerRestorer.RecoveryRegisters.Any(x => x.RegisterIdent.Equals("SENSUSRADIO_PowerLevel")) ||
!registerRestorer.RecoveryRegisters.Any(x => x.RegisterIdent.Equals("SENSUSRADIO_PowerLevelOption")) ||
!registerRestorer.RecoveryRegisters.Any(x => x.RegisterIdent.Equals("SENSUSRADIO_ImpedanceCodeNew")) ||
!registerRestorer.RecoveryRegisters.Any(x => x.RegisterIdent.Equals("SENSUSRADIO_ImpedanceCodeOption")) )
{
!registerRestorer.RecoveryRegisters.Any(x => x.RegisterIdent.Equals("SENSUSRADIO_ImpedanceCodeOption")))
{
feedbackMsg = Resources.StrErrorMsgRadioSignalLevels + " " + meterInfo;
return StatusReturn.Failed;
}
@@ -1,5 +1,7 @@
using System;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
@@ -35,6 +37,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
/// <remarks date="2025-Sep-01" author="Thomas Wiedebusch">
/// - Early return if login failed.
/// </remarks>
/// <remarks date="2025-Nov-18" author="Thomas Wiedebusch">
/// - Threshold for allowed reboots to detect erroneous hardware.
/// </remarks>
public override StatusReturn ExecuteProcess()
{
Boolean retVal;
@@ -61,6 +66,17 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
ErrorMsgDispatcher(Resources.StrErrorMsgCordonelLogin);
return StatusReturn.Failed;
}
// Read reboot count and compare with hardcoded maximum value
var rebootCount =
RegisterConverter.ByteArrayToValue<UInt32>(Meter.ReadRegister("CUSTOMER_RebootCount"));
if (rebootCount > MeterResetPsu.MAX_REBOOTS_FOR_HEALTHY_HW)
{
ErrorMsgDispatcher(Resources.StrErrorMsgTooManyReboots +
$" - {Resources.StrProcessStateAllowed}: {MeterResetPsu.MAX_REBOOTS_FOR_HEALTHY_HW}" +
$" - {Resources.StrProcessStateDetected}: {rebootCount}");
return StatusReturn.Failed;
}
// If interface is outdated
if (!Meter.InterfaceSupportsFwVersion)
@@ -101,6 +101,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
IGenesisMeter Meter
{
set;
get;
}
/// <summary>
@@ -20,7 +20,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
/// <summary>
/// Maximum of expected processing steps for this process to feed the GUI progress bar.
/// </summary>
private const Int32 MaxActualProgressSteps = 14;
private const Int32 MaxActualProgressSteps = 15;
/// <summary>
/// FW update package containing the ABC and the ADF.
@@ -157,8 +157,13 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
/// <remarks date="2025-Jun-18" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2025-Nov-21" author="Thomas Wiedebusch">
/// - Validate CSD.
/// </remarks>
public override StatusReturn ExecuteProcess()
{
var erroneousSequenceStep = false;
var warningSequenceStep = false;
#region ----------------------------------- Check Requirements --------------------------------------------
var statusReturn = GenericChecks.CheckRequirements(ProductionRequirements, out var feedbackMsg);
@@ -172,6 +177,24 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
ActualProcessProgress++;
#endregion
#region ----------------------------------- Validate CSD --------------------------------------------------
statusReturn = GenericChecks.ValidateCsd(ProductionRequirements, out feedbackMsg);
if (StatusReturn.Failed == statusReturn)
{
ErrorMsgDispatcher(feedbackMsg);
return StatusReturn.Failed;
}
if (StatusReturn.Warning == statusReturn)
{
WarningMsgDispatcher(feedbackMsg);
}
SuccessMsgDispatcher(feedbackMsg);
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check Production Order ----------------------------------------
// Production order
@@ -252,8 +275,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
#region ----------------------------------- Check Kitron Radio Signal Levels ------------------------------
var erroneousSequenceStep = false;
var warningSequenceStep = false;
statusReturn = GenericChecks.CheckKitronRadioSignalLevels(Meter, RegisterRestorer,
ProductionRequirements.SkipRadioCheck, out feedbackMsg);
@@ -2,7 +2,6 @@
using System.Collections.Generic;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore.Consts;
using Xylem.Common.Logic.ProductionOrderCore.OrderData;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
@@ -1,14 +1,17 @@
using System;
using LaaPackages.Features.Cordonel.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using LaaPackages.Features.Cordonel.Models;
using System.Windows.Threading;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisStatus;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EventArgs;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Timer = System.Threading.Timer;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
@@ -98,6 +101,17 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
private Timer _processCtrlTimer;
private const Int32 ProcessTimerCycleMs = 250;
// Delay timer between finish exec-parametrization and 'StoreConfiguration' as 'PERIODICLOG' doesn't
// have 'StoreConfiguration' and needs at least 20 s to update contents before 'StoreConfiguration'
// can be dispatched to 'SENSUSRADIO'.
private DispatcherTimer _paramExeFinishedToStoreConfigDelayTimer;
// Cycle time in milliseconds
private const Int32 ParamExeFinishedToStoreConfigDelayCycleMs = 1000;
// Time to keep in seconds
private const Int32 ParamExeFinishedToStoreConfigTimeS = 22;
private Boolean _paramExeFinishedToStoreConfigDelayTimerExceeded;
private DateTimeOffset _paramExeFinishedToStoreConfigDelayTimerStartTime;
/// <summary>
/// Avoid waiting for a single process completion.
/// </summary>
@@ -126,9 +140,18 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
/// - Always RegisterWatchService on,
/// - Always MinMaxCHeck on.
/// </remarks>
/// <remarks date="2025-Nov-20" author="Thomas Wiedebusch">
/// - _paramExeFinishedToStoreConfigurationDelayTimer.
/// </remarks>
public ProcessController(Int32 slot, IProcessStateDef processSateDef)
{
// Timer for cyclic call of state machine
_processCtrlTimer = new Timer(TmrProcessCtrlCycle_Elapsed, null, ProcessTimerCycleMs, ProcessTimerCycleMs);
// Delay timer between final-param-exec finished and start of 'StoreConfiguration'
_paramExeFinishedToStoreConfigDelayTimer = new DispatcherTimer();
_paramExeFinishedToStoreConfigDelayTimer.Tick += TmrParamExeFinishedToStoreConfigCycle_Tick;
_paramExeFinishedToStoreConfigDelayTimer.Interval = TimeSpan.FromMilliseconds(ParamExeFinishedToStoreConfigDelayCycleMs);
_slot = slot;
_cancellationTokenSource = new CancellationTokenSource();
_cancellationToken = _cancellationTokenSource.Token;
@@ -204,6 +227,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
{
_processCtrlTimer?.Dispose();
_processCtrlTimer = null;
_paramExeFinishedToStoreConfigDelayTimer?.Stop();
_paramExeFinishedToStoreConfigDelayTimer = null;
_processStateDef = null;
KillProcesses();
RemoveMeter();
@@ -226,6 +251,11 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
/// <remarks date="2023-Jul-17" author="Thomas Wiedebusch">
/// - Remove process events if process is finished or skipped.
/// </remarks>
/// <remarks date="2025-Nov-20" author="Thomas Wiedebusch">
/// - Init and start the delay timer between finish exec-parametrization and 'StoreConfiguration
/// to all apps as 'PERIODICLOG' doesn't have 'StoreConfiguration' and needs at least 20 s to
/// update contents before 'SENSUSRADIO' tries to synchronize all settings.
/// </remarks>
private void ProcessStateChanged_Handler(Object sender, SingleProcessStateArgs e)
{
IProductionProcess pp = null;
@@ -250,6 +280,22 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
case SingleProcessState.Running:
break;
}
// Delay timer init and start if parametrization exec finished but only for 'EMEA' region
if (pp != null &&
pp.ProcessName != null &&
pp.Meter != null &&
pp.Meter.Region.Equals("EMEA") &&
pp.ProcessName.Equals(Resources.StrStateFinalParametrization) &&
e.SingleProcessState == SingleProcessState.Succeeded)
{
var teaTime = new TimeT();
_paramExeFinishedToStoreConfigDelayTimerExceeded = false;
_paramExeFinishedToStoreConfigDelayTimerStartTime = teaTime.DateTimeUtc;
_paramExeFinishedToStoreConfigDelayTimer.Start();
var msg = $"{Resources.StrMsgRebootDelayTimerStarted} {ParamExeFinishedToStoreConfigTimeS} s" +
$" - {teaTime}";
OnProcessLogRequest?.Invoke(sender, new ProcessLogArgs(msg));
}
// signal state change to GUI
OnProcessStateChanged?.Invoke(sender, e);
}
@@ -300,6 +346,30 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
StateMachine();
}
/// <summary>
/// Delay timer between parametrization exec finished and store config to support the 'PERIODICLOG' update
/// which needs at least 20 s after writing to update the internal contents. Not holding this time will cause
/// to take the value before the writing access as 'SENSUSRADIO' will synchronize all parameters if it receives
/// the 'StoreConfiguration' command.
/// </summary>
/// <param name="state"></param>
/// <param name="e"></param>
/// <remarks date="2025-Nov-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void TmrParamExeFinishedToStoreConfigCycle_Tick(Object state, System.EventArgs e)
{
var teaTime = new TimeT();
var timeSpan = teaTime.DateTimeUtc - _paramExeFinishedToStoreConfigDelayTimerStartTime;
if (timeSpan.Seconds >= ParamExeFinishedToStoreConfigTimeS)
{
_paramExeFinishedToStoreConfigDelayTimerExceeded = true;
_paramExeFinishedToStoreConfigDelayTimer.Stop();
var msg = $"{Resources.StrMsgRebootDelayTimerExceeded} {ParamExeFinishedToStoreConfigTimeS} s - {teaTime}";
OnProcessLogRequest?.Invoke(this, new ProcessLogArgs(msg));
}
}
#endregion --------------------------------------- Events -----------------------------------------------------
#region ------------------------------------------ State Machine ----------------------------------------------
@@ -732,25 +802,52 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
/// <remarks date="2025-Aug-11" author="Thomas Wiedebusch">
/// - Removed cancellationToken assignment.
/// </remarks>
/// <remarks date="2025-Nov-20" author="Thomas Wiedebusch">
/// Delay timer between finish exec-parametrization and 'StoreConfiguration' as 'PERIODICLOG' doesn't
/// have 'StoreConfiguration' and needs at least 20 s to update contents before 'StoreConfiguration'
/// can be dispatched to 'SENSUSRADIO'.
/// </remarks>
/// <remarks date="2025-Nov-21" author="Thomas Wiedebusch">
/// Abort enabled during waiting loop.
/// </remarks>
private void StartProcess(ProcessStateStruct processStateStruct)
{
// deny access if uninitialized
// Deny access if uninitialized
if (Processes == null || processStateStruct.ProductionProcess == null)
return;
// do not start a new process if any error or abort request is signaled
// Do not start a new process if any error or abort request is signaled
if (AnyProcessRequiresAbortion() && !processStateStruct.StartAlways)
{
return;
}
// process is waiting for execution after init, this overrides the eventually abort state
// Process is waiting for execution after init, this overrides the eventually abort state
processStateStruct.ProductionProcess.InitProcess();
// wait for a single process to complete before start of new depending on process
// Wait for '_paramExeFinishedToRebootDelayTimerExceeded' before 'StoreConfiguration' to all apps
while (processStateStruct.ProcessState == ProcessState.ExecStoreConfiguration &&
_paramExeFinishedToStoreConfigDelayTimer != null &&
_paramExeFinishedToStoreConfigDelayTimer.IsEnabled &&
!_paramExeFinishedToStoreConfigDelayTimerExceeded &&
!AnyProcessRequiresAbortion())
{
var teaTime = new TimeT();
var timeSpan = teaTime.DateTimeUtc - _paramExeFinishedToStoreConfigDelayTimerStartTime;
var msg = $"{Resources.StrMsgRebootDelayTimerActive} " +
$"{timeSpan.Seconds}/{ParamExeFinishedToStoreConfigTimeS} s - {teaTime}";
OnProcessLogRequest?.Invoke(this, new ProcessLogArgs(msg));
OnProcessProgressChanged?.Invoke(this, new ProcessProgressArgs(ParamExeFinishedToStoreConfigTimeS,
ProcessProgressArgs.ProcessProgressType.MaxActualProcessSteps));
OnProcessProgressChanged?.Invoke(this, new ProcessProgressArgs(timeSpan.Seconds,
ProcessProgressArgs.ProcessProgressType.ActualProcessSteps));
Thread.Sleep(1000);
}
// Wait for a single process to complete before start of new depending on process
if (processStateStruct.WaitForSingleProcess != null)
{
// each process will have its own timer until a timeout will change the state
// Each process will have its own timer until a timeout will change the state
while (Processes.Any(pp => !AnyProcessRequiresAbortion()
&& _processStateDef.GetStateOfProcess(pp) == processStateStruct.WaitForSingleProcess
&& WaitForProcess(pp)))
@@ -759,10 +856,10 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
}
}
// wait for all initialized processes to complete before start the new process
// Wait for all initialized processes to complete before start the new process
if (processStateStruct.WaitForAllProcesses)
{
// each process will have its own timer until a timeout will change the state,
// Each process will have its own timer until a timeout will change the state,
// avoid checking the new process, which is not started due to this waiting loop
while (Processes.Any(pp => pp.ProcessName != processStateStruct.ProductionProcess.ProcessName
&& WaitForProcess(pp)
@@ -772,7 +869,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
}
}
// if any process exits meanwhile with an error or abort request avoid a start of this process
// If any process exits meanwhile with an error or abort request avoid a start of this process
if ((!processStateStruct.StartAlways && AnyProcessRequiresAbortion())
|| processStateStruct.ProductionProcess.SingleProcessState == SingleProcessState.Idle
|| processStateStruct.ProductionProcess.SingleProcessState == SingleProcessState.Abort)
@@ -789,12 +886,11 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
processStateStruct.ProductionProcess.ProductionStatus = _productionStatus;
processStateStruct.ProductionProcess.ProductionRequirements = _cordonelRequirements;
processStateStruct.ProductionProcess.EolProgress = _cordonelEolProgress;
// processStateStruct.ProductionProcess.CancellationToken = _cancellationToken;
// kick off process execution, the breakExitState will not be used
// Kick off process execution, the breakExitState will not be used
processStateStruct.ProductionProcess.StartProcess(processStateStruct.NextStateOnSuccess,
processStateStruct.ErrorExitState, processStateStruct.BreakExitState);
// connect process events to actual process
// Connect process events to actual process
AddProcessEvents(processStateStruct.ProductionProcess);
}
@@ -845,7 +845,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel
!string.IsNullOrEmpty(genesisMeter.MeterSize) ? genesisMeter.MeterSize : "?");
UpdateContentControl(lblMeterLengthValue,
(!string.IsNullOrEmpty(genesisMeter.MeterLength) && !genesisMeter.MeterLength.Equals("?")) ?
!string.IsNullOrEmpty(genesisMeter.MeterLength) && !genesisMeter.MeterLength.Equals("?") ?
genesisMeter.MeterLength + " mm" : "?");
UpdateContentControl(lblInterfaceVersionValue,
@@ -492,6 +492,33 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: CSD is blocked for production.
/// </summary>
internal static string StrErrorMsgCsdBlocked {
get {
return ResourceManager.GetString("StrErrorMsgCsdBlocked", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: CSD is new and not approved for production.
/// </summary>
internal static string StrErrorMsgCsdNewNotApproved {
get {
return ResourceManager.GetString("StrErrorMsgCsdNewNotApproved", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: CSD status and number are unknown.
/// </summary>
internal static string StrErrorMsgCsdNotReceived {
get {
return ResourceManager.GetString("StrErrorMsgCsdNotReceived", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Data base access failed.
/// </summary>
@@ -1050,6 +1077,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Hardware reboots higher than allowed threshold.
/// </summary>
internal static string StrErrorMsgTooManyReboots {
get {
return ResourceManager.GetString("StrErrorMsgTooManyReboots", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Firmware is not supported.
/// </summary>
@@ -1635,6 +1671,33 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Expired delay timer between parametrization and store config.
/// </summary>
internal static string StrMsgRebootDelayTimerActive {
get {
return ResourceManager.GetString("StrMsgRebootDelayTimerActive", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Stop: Delay timer between parametrization and store config after.
/// </summary>
internal static string StrMsgRebootDelayTimerExceeded {
get {
return ResourceManager.GetString("StrMsgRebootDelayTimerExceeded", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Start: Delay timer between parametrization and store config for.
/// </summary>
internal static string StrMsgRebootDelayTimerStarted {
get {
return ResourceManager.GetString("StrMsgRebootDelayTimerStarted", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Estimated remaining lifetime.
/// </summary>
@@ -1734,6 +1797,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Allowed.
/// </summary>
internal static string StrProcessStateAllowed {
get {
return ResourceManager.GetString("StrProcessStateAllowed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ATTENTION.
/// </summary>
@@ -1752,6 +1824,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Detected.
/// </summary>
internal static string StrProcessStateDetected {
get {
return ResourceManager.GetString("StrProcessStateDetected", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Success.
/// </summary>
@@ -2265,6 +2346,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Success: CSD is approved for production.
/// </summary>
internal static string StrSuccessMsgCsdValid {
get {
return ResourceManager.GetString("StrSuccessMsgCsdValid", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Success: Database with Cordonel radio encryption key successfully crosschecked.
/// </summary>
@@ -2509,7 +2599,25 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
/// <summary>
/// Looks up a localized string similar to WARNING: Inspect results.
/// Looks up a localized string similar to WARNING: CSD is known and temporary used for production.
/// </summary>
internal static string StrWarningMsgCsdKnown {
get {
return ResourceManager.GetString("StrWarningMsgCsdKnown", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to WARNING: Parameter restored after reboot as it does not survive it.
/// </summary>
internal static string StrWarningMsgParameterCompare {
get {
return ResourceManager.GetString("StrWarningMsgParameterCompare", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to WARNING: Result needs inspection! Process.
/// </summary>
internal static string StrWarningMsgProcess {
get {
@@ -2527,7 +2635,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
/// <summary>
/// Looks up a localized string similar to WARNING: Service SIRT alarm mask setup skipped .
/// Looks up a localized string similar to WARNING: Service SIRT alarm mask check skipped .
/// </summary>
internal static string StrWarningMsgServiceSirtSkipped {
get {
@@ -437,7 +437,7 @@
<value>FEHLER: Service SIRT Start des Messageserves fehlgeschlagen </value>
</data>
<data name="StrWarningMsgServiceSirtSkipped" xml:space="preserve">
<value>WARNUNG: Service SIRT Alarmmaskeneinstellung übersprungen </value>
<value>WARNUNG: Service SIRT Alarmmaskenüberprüfung ausgelassen </value>
</data>
<data name="StrErrorMsgRadioKey" xml:space="preserve">
<value>FEHLER: Funkverschlüsselungscode fehlerhaft</value>
@@ -660,6 +660,9 @@
</data>
<data name="StrErrorMsgParameterCompare" xml:space="preserve">
<value>FEHLER: Parametervergleich fehlgeschlagen</value>
</data>
<data name="StrWarningMsgParameterCompare" xml:space="preserve">
<value>WARNUNG: Parameter überlebte den Neustart nicht und wurde danach wiederhergestellt</value>
</data>
<data name="StrErrorMsgParameterWrite" xml:space="preserve">
<value>FEHLER: Parameterschreiben fehlgeschlagen</value>
@@ -677,10 +680,43 @@
<value>WARNUNG</value>
</data>
<data name="StrWarningMsgProcess" xml:space="preserve">
<value>WARNUNG: Ergebnis manuell überprüfen</value>
<value>WARNUNG: Ergebnis m überprüft werden! Prozeß </value>
</data>
<data name="StrErrorMsgTooManyReboots" xml:space="preserve">
<value>FEHLER: Hardwareneustarts über der erlaubten Grenze</value>
</data>
<data name="StrErrorMsgCsdBlocked" xml:space="preserve">
<value>FEHLER: CSD ist für die Production gesperrt</value>
</data>
<data name="StrErrorMsgCsdNewNotApproved" xml:space="preserve">
<value>ERROR: CSD ist neu aber für die Production gesperrt</value>
</data>
<data name="StrSuccessMsgCsdValid" xml:space="preserve">
<value>Erfolg: CSD ist zur Produktion freigegeben</value>
</data>
<data name="StrWarningMsgCsdKnown" xml:space="preserve">
<value>WARNUNG: CSD ist bekannt und temporär zur Produktion freigegeben</value>
</data>
<data name="StrErrorMsgCsdNotReceived" xml:space="preserve">
<value>WARNUNG: CSD Status und Nummer sind unbekannt</value>
</data>
<data name="StrProcessStateAttention" xml:space="preserve">
<value>ACHTUNG</value>
</data>
<data name="StrMsgRebootDelayTimerStarted" xml:space="preserve">
<value>Start: Verzögerungszeit zwischen Parameterschreiben und Wertespeicherung für</value>
</data>
<data name="StrMsgRebootDelayTimerExceeded" xml:space="preserve">
<value>Stop: Verzögerungszeit zwischen Parameterschreiben und Wertespeicherung nach</value>
</data>
<data name="StrMsgRebootDelayTimerActive" xml:space="preserve">
<value>Abgelaufene Verzögerungszeit zwischen Parameterschreiben und Wertespeicherung</value>
</data>
<data name="StrProcessStateAllowed" xml:space="preserve">
<value>Erlaubt</value>
</data>
<data name="StrProcessStateDetected" xml:space="preserve">
<value>Erkannt</value>
</data>
<data name="StrLblSpecialRequirement" xml:space="preserve">
<value>SONDERFREIGABE</value>
@@ -437,7 +437,7 @@
<value>ERROR: Service SIRT message server start failed </value>
</data>
<data name="StrWarningMsgServiceSirtSkipped" xml:space="preserve">
<value>WARNING: Service SIRT alarm mask setup skipped </value>
<value>WARNING: Service SIRT alarm mask check skipped </value>
</data>
<data name="StrErrorMsgRadioKey" xml:space="preserve">
<value>ERROR: Radio encryption code wrong</value>
@@ -660,6 +660,9 @@
</data>
<data name="StrErrorMsgParameterCompare" xml:space="preserve">
<value>ERROR: Parameter compare failed</value>
</data>
<data name="StrWarningMsgParameterCompare" xml:space="preserve">
<value>WARNING: Parameter restored after reboot as it does not survive it</value>
</data>
<data name="StrErrorMsgParameterWrite" xml:space="preserve">
<value>ERROR: Parameter write failed</value>
@@ -675,12 +678,45 @@
</data>
<data name="StrProcessStateWarning" xml:space="preserve">
<value>WARNING</value>
</data>
<data name="StrWarningMsgProcess" xml:space="preserve">
<value>WARNING: Result needs inspection! Process</value>
</data>
<data name="StrErrorMsgTooManyReboots" xml:space="preserve">
<value>ERROR: Hardware reboots higher than allowed threshold</value>
</data>
<data name="StrErrorMsgCsdBlocked" xml:space="preserve">
<value>ERROR: CSD is blocked for production</value>
</data>
<data name="StrErrorMsgCsdNewNotApproved" xml:space="preserve">
<value>ERROR: CSD is new and not approved for production</value>
</data>
<data name="StrSuccessMsgCsdValid" xml:space="preserve">
<value>Success: CSD is approved for production</value>
</data>
<data name="StrWarningMsgCsdKnown" xml:space="preserve">
<value>WARNING: CSD is known and temporary used for production</value>
</data>
<data name="StrErrorMsgCsdNotReceived" xml:space="preserve">
<value>ERROR: CSD status and number are unknown</value>
</data>
<data name="StrProcessStateAttention" xml:space="preserve">
<value>ATTENTION</value>
</data>
<data name="StrMsgRebootDelayTimerStarted" xml:space="preserve">
<value>Start: Delay timer between parametrization and store config for</value>
</data>
<data name="StrWarningMsgProcess" xml:space="preserve">
<value>WARNING: Inspect results</value>
<data name="StrMsgRebootDelayTimerExceeded" xml:space="preserve">
<value>Stop: Delay timer between parametrization and store config after</value>
</data>
<data name="StrMsgRebootDelayTimerActive" xml:space="preserve">
<value>Expired delay timer between parametrization and store config</value>
</data>
<data name="StrProcessStateAllowed" xml:space="preserve">
<value>Allowed</value>
</data>
<data name="StrProcessStateDetected" xml:space="preserve">
<value>Detected</value>
</data>
<data name="StrLblSpecialRequirement" xml:space="preserve">
<value>SPECIAL APPROVAL</value>
-27
View File
@@ -1486,33 +1486,6 @@ namespace Xylem.Common.Ui.GenesisToolBox
{
return;
}
//_currentGenesis.WriteRegister("POWERMON_BatteryQuantity", 2, true, true);
//_currentGenesis.WriteRegister("POWERMON_StoreConfiguration", 1, true, true);
//var genesisStatus = new GenesisStatus();
//if (GenesisStatusHandler.BuildLifeTimeInformation(_currentGenesis, genesisStatus))
//{
// var sbMSG = new StringBuilder();
// sbMSG.AppendLine($"Pcb = {_currentGenesis.PcbId}");
// sbMSG.AppendLine($"POWERMON_TotalUsedSeconds = {genesisStatus.ExceededLifeTime_s}");
// sbMSG.AppendLine($"POWERMON_TotalUsedCharge = {genesisStatus.DrainedBatteryLoad_uAs}");
// sbMSG.AppendLine($"POWERMON_BatteryQuantity = {genesisStatus.BatteryQuantity}");
// sbMSG.AppendLine($"POWERMON_BatteryMilliAHrRating = {genesisStatus.InitialBatteryLoad_mAh}");
// sbMSG.AppendLine("");
// sbMSG.AppendLine($"Remaining lifetime in years {genesisStatus.RemainingLifeTimeYears:F2}");
// sbMSG.AppendLine($"Totally drained battery load in % {genesisStatus.DrainedBatteryLoadPercent:F2}");
// _currentGenesis.WriteLog(sbMSG.ToString());
// MessageBox.Show(sbMSG.ToString(), $"Battery for {_currentGenesis.PcbId} has remaining life " +
// $"time of {genesisStatus.RemainingLifeTimeYears:F2} years");
//}
//else
//{
// MessageBox.Show("Failed to get life time information");
//}
_currentGenesis.RequestProtocol.AdditionalRetryTimeoutMs = addRetryTimeoutMs;
Task.Factory.StartNew(() =>
@@ -1,4 +1,5 @@
using System;
using Xylem.Common.CommonCore.Consts;
namespace Xylem.Common.Utils.ProcessExec.EventArguments
{
@@ -28,6 +29,11 @@ namespace Xylem.Common.Utils.ProcessExec.EventArguments
/// </summary>
public Double? ActualProcessPercent { get; }
/// <summary>
/// Single process percentage 0..100
/// </summary>
public StatusReturn StatusReturn { get; }
/// <summary>
/// Ctor
/// </summary>
@@ -35,9 +41,10 @@ namespace Xylem.Common.Utils.ProcessExec.EventArguments
/// <param name="overallProcessPercent"></param>
/// <param name="actualProcessMessage"></param>
/// <param name="actualProcessPercent"></param>
/// <param name="statusReturn"></param>
public ProcessExecEventArgs(String overallProcessMessage,
Double? overallProcessPercent = null, String actualProcessMessage = null,
Double? actualProcessPercent = null)
Double? actualProcessPercent = null, StatusReturn statusReturn = StatusReturn.Unknown)
{
OverallProcessMessage = overallProcessMessage;
ActualProcessMessage = actualProcessMessage;
@@ -45,6 +52,7 @@ namespace Xylem.Common.Utils.ProcessExec.EventArguments
OverallProcessPercent = OverallProcessPercent < 0 ? 0 : OverallProcessPercent;
ActualProcessPercent = actualProcessPercent > 100.0 ? 100.0 : actualProcessPercent;
ActualProcessPercent = ActualProcessPercent < 0 ? 0 : ActualProcessPercent;
StatusReturn = statusReturn;
}
}
}
@@ -4218,7 +4218,8 @@ namespace Xylem.ServiceFwUpdate.Ui.ServiceFwUpdateSw
{
SetOverallProgressDisplayOn(Resources.StrRegisterReadoutActive);
InfoProcessActive(lblRegisterReadout, Resources.StrRegisterCompareActive);
retVal = _registerRestorer.CompareRegisters(_cancellationToken);
retVal = _registerRestorer.CompareRegisters(_registerRestorer.RecoveryRegisters,
_cancellationToken);
}
else
{