ProductionUiCordonel: - generic checks, - Kitron radio parameter

This commit is contained in:
Thomas Wiedebusch
2025-05-28 16:06:03 +02:00
parent e73799f7a1
commit c81be68d8c
20 changed files with 548 additions and 213 deletions
@@ -4,7 +4,7 @@ using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
{
public class CheckOrderNumber : BaseProductionProcess
{
@@ -6,8 +6,6 @@ using System.Windows;
using LaaPackages.Features.Cordonel.Models;
using Logic.ProductionToProductMapper.Files.Fw;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Const;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisStatus;
@@ -16,6 +14,7 @@ using Xylem.Common.Logic.ProductionOrderCore.OrderData;
using Xylem.Common.Logic.ProductionOrderCore.TestResults;
using Xylem.Common.Production.ProductionUiCordonel.Model;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericModules;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
@@ -315,6 +314,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// - Collect all requirement statuses before signalizing failed to summarize results starting with battery
/// load.
/// </remarks>
/// <remarks date="2025-Mai-28" author="Thomas Wiedebusch">
/// - Check radio signal levels in parameter list.
/// </remarks>
public override StatusReturn ExecuteProcess()
{
#region ----------------------------------- Check EOL Progress --------------------------------------------
@@ -404,6 +406,22 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check Kitron Radio Signal Levels ------------------------------
var statusReturn = CheckKitronRadioSignalLevels(out feedbackMsg);
if (StatusReturn.Failed == statusReturn)
{
ErrorMsgDispatcher(feedbackMsg);
return StatusReturn.Failed;
}
if (StatusReturn.Okay == statusReturn)
{
SuccessMsgDispatcher(feedbackMsg);
}
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check Firmware and Apps ---------------------------------------
if (string.IsNullOrEmpty(CheckFwAndApps(out feedbackMsg)))
@@ -506,7 +524,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
// 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.
var statusReturn = CheckCalibrationValues(out feedbackMsg);
statusReturn = CheckCalibrationValues(out feedbackMsg);
if (StatusReturn.Skipped == statusReturn)
{
WarningMsgDispatcher(feedbackMsg);
@@ -855,6 +873,23 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
return EolProgress.ProductionStatusMeterSize;
}
/// <summary>
/// Check the measurements from Kitron for this specific Cordonel if it is an EMEA region.
///
/// Software Requirement Specification:
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_008]_Check_Kitron_Radio_Levels
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-28" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private StatusReturn CheckKitronRadioSignalLevels(out String feedbackMsg)
{
return GenericChecks.CheckKitronRadioSignalLevels(Meter, RegisterRestorer, out feedbackMsg);
}
/// <summary>
/// Check the installed firmware (FW) and all individual applications (Apps) installed to the meter:
/// - Take the FLEXNETVERSION as base information which FW is installed in the Cordonel and compare this with
@@ -866,7 +901,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// Cordonel operational system (Breeze).
///
/// Software Requirement Specification:
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_008]_Check_FW_and_Apps
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_009]_Check_FW_and_Apps
///
/// </summary>
/// <param name="feedbackMsg"></param>
@@ -874,91 +909,23 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2024-Nov-15" author="Thomas Wiedebusch">
/// - Extracted from <see cref="ExecuteProcess"/> for better testability.
/// </remarks>
/// <remarks date="2025-Mai-28" author="Thomas Wiedebusch">
/// - Exported to generic tests.
/// </remarks>
private String CheckFwAndApps(out String feedbackMsg)
{
feedbackMsg = "";
// Check installed FW version with FLEXNET information
var fwUpdate = new MeterFwUpdate(Meter);
if (EolProgress == null ||
Meter == null ||
ProductionRequirements == null ||
string.IsNullOrEmpty(ProductionRequirements.FwVersion) ||
string.IsNullOrEmpty(Meter.FwVersion) ||
!Meter.FwVersion.Equals(ProductionRequirements.FwVersion) ||
// Check if FW package has been loaded and contains at least the minimal required files
FwUpdatePackage == null ||
FwUpdatePackage.Count < MeterFwUpdate.MinAppsRequiredForOperation)
var fwCheckMsg = GenericChecks.CheckFwAndApps(Meter, ProductionRequirements, FwUpdatePackage,
out feedbackMsg);
// Fill the EOL progress
if (string.IsNullOrEmpty(fwCheckMsg))
{
if (EolProgress != null)
EolProgress.ProductionStatusFwVersion = null;
feedbackMsg += $"{Resources.StrErrorMsgRequirementFw}\n";
return "";
}
var fileApps = new List<FileApplications>();
if (FwUpdatePackage != null)
{
foreach (var file in FwUpdatePackage)
{
// load the ABC (application binary container)
if (file.FileName.Contains("binfile") &&
file.FileName.EndsWith(".bin") &&
// Sometimes the * complete *.bin is part of the binaries containing all app in one file.
// As this is not part of the ADF it has to be skipped from loading!!!!
!file.FileName.Contains("complete"))
{
var fileApplication = new FileApplications(file.FileName)
{
BinData = new List<Byte>(file.FileContent.ToList())
};
fileApps.Add(fileApplication);
}
// Load the ADF (application description file, containing the information
// of each application)
else if (file.FileName.Contains("product") && file.FileName.EndsWith(".txt"))
{
var enc = new System.Text.ASCIIEncoding();
var adf = enc.GetString(file.FileContent.ToArray());
fwUpdate.LoadPackageFileFromText(adf);
}
}
fwUpdate.LoadFileApps(fileApps);
}
var checkSucceeded = true;
// Compare every installed application for version and CRC with the required to be defined by the downloaded
// ABC (application binary container)
fwUpdate.CompareAllMeterAndFileApps();
if (// Validate package description file with required Apps (fileApps) and build the CORE REVISIONS MIN MAX
!fwUpdate.ValidateFileAppsWithPackageFile() ||
// Check if any meter Apps is outdated or does contain an un-matching CRC
Meter.MeterAppListVersion.Any(app => app.Status != MeterAppState.MeterAppUpToDate &&
app.Status != MeterAppState.MeterAppNotRequired))
{
feedbackMsg += $"{Resources.StrErrorMsgAppVersionOrCrc}\n";
checkSucceeded = false;
// Do not immediately return to summarize all errors following up
}
// The package description file has to be parsed in advance using the fwUpdate.ValidateFileAppsWithPackageFile()!!
if (!fwUpdate.CheckCoreRevision(Meter.CoreRevision))
{
feedbackMsg += $"{Resources.StrErrorMsgCoreRevision}\n";
// Do not immediately return to summarize all errors following up
checkSucceeded = false;
}
if (!checkSucceeded)
{
EolProgress.ProductionStatusFwVersion = null;
feedbackMsg += $"{Resources.StrErrorMsgRequirementFw}\n";
return EolProgress.ProductionStatusFwVersion;
}
EolProgress.ProductionStatusFwVersion = Meter.FwVersion;
feedbackMsg = $"{Resources.StrSuccessMsgRequirementFw} - Firmware: {Meter.FwVersion}";
return EolProgress.ProductionStatusFwVersion;
return fwCheckMsg;
}
/// <summary>
@@ -974,7 +941,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// calculation is being used.
///
/// Software Requirement Specification:
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_009]_Check_Remaining_Battery_Load
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_010]_Check_Remaining_Battery_Load
///
/// </summary>
/// <param name="feedbackMsg"></param>
@@ -1075,7 +1042,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// Check the remaining lifetime
///
/// Software Requirement Specification:
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_010]_Check_Remaining_Lifetime
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_011]_Check_Remaining_Lifetime
///
/// </summary>
/// <param name="feedbackMsg"></param>
@@ -1139,7 +1106,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// Check the months between first battery activation and now
///
/// Software Requirement Specification:
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_011]_Check_Storage_Months
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_012]_Check_Storage_Months
///
/// </summary>
/// <param name="feedbackMsg"></param>
@@ -1215,7 +1182,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// don't match, the check of valid range and set to default values doesn't come in focus!
///
/// Software Requirement Specification:
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_012]_Check_Calibration_Values
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_013]_Check_Calibration_Values
///
/// </summary>
/// <param name="feedbackMsg"></param>
@@ -1366,7 +1333,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// the according step has been successfully executed.
///
/// Software Requirement Specification:
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_013]_Check_Preadjustment
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_014]_Check_Preadjustment
///
/// </summary>
/// <param name="feedbackMsg"></param>
@@ -1428,7 +1395,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// the according step has been successfully executed.
///
/// Software Requirement Specification:
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_014]_Check_Flow_Test_State
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_015]_Check_Flow_Test_State
///
/// </summary>
/// <param name="feedbackMsg"></param>
@@ -4,7 +4,7 @@ using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
{
public class CheckSerialNumber : BaseProductionProcess
{
@@ -151,25 +151,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
// Kick off parallel process execution
kickOffParallelState: ProcessState.GetProgrammingParameters),
// This scans the order number, it has to be executed exclusively with automatic
new ProcessStateStruct(
productionProcess: new CheckOrderNumber(Resources.StrStateCheckOrderNumber),
processState: ProcessState.CheckOrderNumber,
processInfo: Resources.StrStateCheckOrderNumber,
productionProcessNo: 11,
nextStateOnSuccess: ProcessState.CheckSerialNumber),
// This scans the serial number, it has to be executed exclusively with automatic
new ProcessStateStruct(
productionProcess: new CheckSerialNumber( Resources.StrStateCheckSerialNumber),
processState: ProcessState.CheckSerialNumber,
processInfo: Resources.StrStateCheckSerialNumber,
productionProcessNo: 12,
nextStateOnSuccess: ProcessState.CheckAttachments),
#endregion -------------------------------- GENERIC-STATES --------------------------------------------
#region ----------------------------------- EOL-STATES ------------------------------------------------
// Precondition: - The connection process has to be completed as a readout of some register
// values from the Cordonel are needed to check the update capability:
// - Wait on completed connection process before starting this process.
@@ -186,6 +167,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
// Wait until this process has been successfully completed
waitForSingleProcess: ProcessState.ConnectCordonel),
#endregion -------------------------------- GENERIC-STATES --------------------------------------------
#region ----------------------------------- EOL-STATES ------------------------------------------------
// Precondition: - The connection process has to be completed as a readout of some register
// values from the Cordonel are needed to check the update capability:
// - Wait on completed connection process before starting this process.
@@ -201,7 +185,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
kickOffParallelState: ProcessState.ProcessPasswordFile,
// Wait until this process has been successfully completed
waitForSingleProcess: ProcessState.ConnectCordonel),
// 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
@@ -265,6 +248,22 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
productionProcessNo: 10,
nextStateOnSuccess: ProcessState.CheckOrderNumber),
// This scans the order number, it has to be executed exclusively with automatic
new ProcessStateStruct(
productionProcess: new CheckOrderNumber(Resources.StrStateCheckOrderNumber),
processState: ProcessState.CheckOrderNumber,
processInfo: Resources.StrStateCheckOrderNumber,
productionProcessNo: 11,
nextStateOnSuccess: ProcessState.CheckSerialNumber),
// This scans the serial number, it has to be executed exclusively with automatic
new ProcessStateStruct(
productionProcess: new CheckSerialNumber( Resources.StrStateCheckSerialNumber),
processState: ProcessState.CheckSerialNumber,
processInfo: Resources.StrStateCheckSerialNumber,
productionProcessNo: 12,
nextStateOnSuccess: ProcessState.CheckAttachments),
// Precondition: - All checks have to be executed with positive result
new ProcessStateStruct(
productionProcess: new CheckAttachments(Resources.StrStateCheckAttachments),
@@ -3,6 +3,7 @@ using System.Linq;
using LaaPackages.Features.Cordonel.Models;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
@@ -0,0 +1,164 @@
using LaaPackages.Features.Cordonel.Models;
using Logic.ProductionToProductMapper.Files.Fw;
using System;
using System.Collections.Generic;
using System.Linq;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Const;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericModules
{
internal static class GenericChecks
{
/// <summary>
/// Check the measurements from Kitron for this specific Cordonel if it is an EMEA region.
/// </summary>
/// <param name="registerRestorer"></param>
/// <param name="feedbackMsg"></param>
/// <param name="meter"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-28" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2025-Mai-28" author="Thomas Wiedebusch">
/// - Import from CheckProductionState for common use.
/// </remarks>
public static StatusReturn CheckKitronRadioSignalLevels(IGenesisMeter meter, RegisterRestorer registerRestorer,
out String feedbackMsg)
{
feedbackMsg = "";
if (meter == null ||
string.IsNullOrEmpty(meter.MeterSize) ||
string.IsNullOrEmpty(meter.Region))
{
return StatusReturn.Failed;
}
// The NA region has no radio
if (meter.Region.Equals("NA"))
{
return StatusReturn.Skipped;
}
// On unknown radio signal level parameters from Kitron those are not in the list of the programming parameters
if (!registerRestorer.RecoveryRegisters.Any(x => x.RegisterIdent.Equals("SENSUSRADIO_PowerLevel")))
{
var pressureSensorAssembled = meter.PressureSensorAssembled ?
$", {Resources.StrLblPressureSensorAssembledTxt}":
"";
feedbackMsg = $"{Resources.StrErrorMsgRadioSignalLevels} " +
$"{Resources.StrLblRadioFrequencyTxt}: {meter.RadioFrequencyMhz} MHz," +
$"{Resources.StrLblMeterSizeTxt}: {meter.MeterSize}, " +
$"{Resources.StrLblMeterLengthTxt}: {meter.MeterLength} mm" +
$"{pressureSensorAssembled}";
return StatusReturn.Failed;
}
feedbackMsg = Resources.StrSuccessMsgRadioSignalLevels;
return StatusReturn.Okay;
}
/// <summary>
/// Check the installed firmware (FW) and all individual applications (Apps) installed to the meter:
/// - Take the FLEXNETVERSION as base information which FW is installed in the Cordonel and compare this with
/// the production-requirement,
/// - The downloaded FW package containing all Apps as application-binary-container (ABC) will be compared with
/// the reported Apps by the Cordonel for their version and CRC using the "<see cref="MeterFwUpdate"/> class,
/// - Crosscheck if the application-description-file (ADF) states all Apps which are installed in the Cordonel,
/// - Check the Core-Revision of the Cordonel against the ADF to guarantee that this FW is supported by the
/// Cordonel operational system (Breeze).
/// </summary>
/// <param name="meter"></param>
/// <param name="requirements"></param>
/// <param name="fwUpdatePackage"></param>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2025-Mai-28" author="Thomas Wiedebusch">
/// - Import from CheckProductionState for common use.
/// </remarks>
public static String CheckFwAndApps(IGenesisMeter meter, CordonelRequirements requirements,
List<FilePart> fwUpdatePackage, out String feedbackMsg)
{
feedbackMsg = "";
// Check installed FW version with FLEXNET information
var fwUpdate = new MeterFwUpdate(meter);
if (meter == null ||
requirements == null ||
string.IsNullOrEmpty(requirements.FwVersion) ||
string.IsNullOrEmpty(meter.FwVersion) ||
!meter.FwVersion.Equals(requirements.FwVersion) ||
// Check if FW package has been loaded and contains at least the minimal required files
fwUpdatePackage == null ||
fwUpdatePackage.Count < MeterFwUpdate.MinAppsRequiredForOperation)
{
feedbackMsg += $"{Resources.StrErrorMsgRequirementFw}\n";
return "";
}
var fileApps = new List<FileApplications>();
foreach (var file in fwUpdatePackage)
{
// load the ABC (application binary container)
if (file.FileName.Contains("binfile") &&
file.FileName.EndsWith(".bin") &&
// Sometimes the * complete *.bin is part of the binaries containing all app in one file.
// As this is not part of the ADF it has to be skipped from loading!!!!
!file.FileName.Contains("complete"))
{
var fileApplication = new FileApplications(file.FileName)
{
BinData = new List<Byte>(file.FileContent.ToList())
};
fileApps.Add(fileApplication);
}
// Load the ADF (application description file, containing the information
// of each application)
else if (file.FileName.Contains("product") && file.FileName.EndsWith(".txt"))
{
var enc = new System.Text.ASCIIEncoding();
var adf = enc.GetString(file.FileContent.ToArray());
fwUpdate.LoadPackageFileFromText(adf);
}
}
fwUpdate.LoadFileApps(fileApps);
var checkSucceeded = true;
// Compare every installed application for version and CRC with the required to be defined by the downloaded
// ABC (application binary container)
fwUpdate.CompareAllMeterAndFileApps();
if (// Validate package description file with required Apps (fileApps) and build the CORE REVISIONS MIN MAX
!fwUpdate.ValidateFileAppsWithPackageFile() ||
// Check if any meter Apps is outdated or does contain an un-matching CRC
meter.MeterAppListVersion.Any(app => app.Status != MeterAppState.MeterAppUpToDate &&
app.Status != MeterAppState.MeterAppNotRequired))
{
feedbackMsg += $"{Resources.StrErrorMsgAppVersionOrCrc}\n";
checkSucceeded = false;
// Do not immediately return to summarize all errors following up
}
// The package description file has to be parsed in advance using the fwUpdate.ValidateFileAppsWithPackageFile()!!
if (!fwUpdate.CheckCoreRevision(meter.CoreRevision))
{
feedbackMsg += $"{Resources.StrErrorMsgCoreRevision}\n";
// Do not immediately return to summarize all errors following up
checkSucceeded = false;
}
if (!checkSucceeded)
{
feedbackMsg += $"{Resources.StrErrorMsgRequirementFw}\n";
return "";
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementFw} - Firmware: {meter.FwVersion}";
return meter.FwVersion;
}
}
}
@@ -7,10 +7,10 @@ using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Logic.SoftwareAccessHelper;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolProcesses
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses
{
/// <summary>
/// Request for parameters for final programming including VAKO and CSD
@@ -0,0 +1,96 @@
using System;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.PickingProcesses
{
public class CheckOrderNumber : BaseProductionProcess
{
// string from scanner with string delimiter
private String _scan = "";
// marker for scan input received
private Boolean _scanInputReceived;
/// <summary>
/// Ctor
/// </summary>
public CheckOrderNumber(String name) : base( name)
{
}
/// <inheritdoc/>
protected override void InitUserControl()
{
UserControl = new UcBarcodeInput
{
lblHeadLine = { Content = Resources.StrInstructionScanOrder },
tbxScan = { Text = "" }
};
}
/// <summary>
/// Event handler taking the input from text box which should be scanned but also can be input manually.
/// </summary>
/// <returns></returns>
/// <remarks date="2023-Jun-21" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
private void UserControlInput_Handler(Object sender, String input)
{
_scan = input;
_scanInputReceived = true;
}
/// <summary>
/// Scanning of order number from production order sheet and comparing with DB.
/// </summary>
/// <returns></returns>
/// <remarks date="2023-Jun-21" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2023-Jul-07" author="Thomas Wiedebusch">
/// - Removed signals for <see cref="SingleProcessState.Error"/> and <see cref="SingleProcessState.Succeeded"/> as
/// these will be automatically set based on return value in
/// <see cref="BaseProductionProcess.FinalizeProcess"/>.
/// </remarks>
public override StatusReturn ExecuteProcess()
{
if (Meter == null)
{
return StatusReturn.Failed;
}
SignalRunningState();
if (UserControl == null)
{
ErrorMsgDispatcher(Resources.StrErrorMsgScanOrderNumber);
return StatusReturn.Failed;
}
var ucBarCode = (UcBarcodeInput)UserControl;
ucBarCode.OnBarcodeInput += UserControlInput_Handler;
// set focus to text input for scanner
ucBarCode.FocusMe();
_scanInputReceived = false;
// wait for new line
while (!_scanInputReceived)
{
}
ucBarCode.OnBarcodeInput -= UserControlInput_Handler;
if (!string.IsNullOrEmpty(_scan) && _scan.Length > 5)
{
//TODO Roland, Do something with the scanned order
return StatusReturn.Okay;
}
ErrorMsgDispatcher(Resources.StrErrorMsgScanOrderNumber);
return StatusReturn.Failed;
}
}
}
@@ -1,15 +1,13 @@
using System;
using Logic.ProductionToProductMapper.Files.Fw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Logic.ProductionToProductMapper.Files.Fw;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Const;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisStatus;
using Xylem.Common.Logic.ProductionOrderCore.OrderData;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericModules;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.GenericProcesses;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
@@ -229,6 +227,22 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check Kitron Radio Signal Levels ------------------------------
statusReturn = CheckKitronRadioSignalLevels(out feedbackMsg);
if (StatusReturn.Failed == statusReturn)
{
ErrorMsgDispatcher(feedbackMsg);
return StatusReturn.Failed;
}
if (StatusReturn.Okay == statusReturn)
{
SuccessMsgDispatcher(feedbackMsg);
}
ActualProcessProgress++;
#endregion
#region ----------------------------------- Check Firmware and Apps ---------------------------------------
if (string.IsNullOrEmpty(CheckFwAndApps(out feedbackMsg)))
@@ -531,6 +545,24 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
return ProductionRequirements.MeterSize;
}
/// <summary>
/// Check the measurements from Kitron for this specific Cordonel if it is an EMEA region.
///
/// Software Requirement Specification:
/// [SRS_TBF_EOL_PP_CHECK_PROD_STATE_007]_Check_Kitron_Radio_Levels
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-28" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private StatusReturn CheckKitronRadioSignalLevels(out String feedbackMsg)
{
return GenericChecks.CheckKitronRadioSignalLevels(Meter, RegisterRestorer, out feedbackMsg);
}
/// <summary>
/// Check the installed firmware (FW) and all individual applications (Apps) installed to the meter:
/// - Take the FLEXNETVERSION as base information which FW is installed in the Cordonel and compare this with
@@ -542,7 +574,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
/// Cordonel operational system (Breeze).
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_007]_Check_FW_and_Apps
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_008]_Check_FW_and_Apps
///
/// </summary>
/// <param name="feedbackMsg"></param>
@@ -550,86 +582,12 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2025-Mai-28" author="Thomas Wiedebusch">
/// - Exported to generic tests.
/// </remarks>
private String CheckFwAndApps(out String feedbackMsg)
{
feedbackMsg = "";
// Check installed FW version with FLEXNET information
var fwUpdate = new MeterFwUpdate(Meter);
if (Meter == null ||
ProductionRequirements == null ||
string.IsNullOrEmpty(ProductionRequirements.FwVersion) ||
string.IsNullOrEmpty(Meter.FwVersion) ||
!Meter.FwVersion.Equals(ProductionRequirements.FwVersion) ||
// Check if FW package has been loaded and contains at least the minimal required files
FwUpdatePackage == null ||
FwUpdatePackage.Count < MeterFwUpdate.MinAppsRequiredForOperation)
{
feedbackMsg += $"{Resources.StrErrorMsgRequirementFw}\n";
return "";
}
var fileApps = new List<FileApplications>();
if (FwUpdatePackage != null)
{
foreach (var file in FwUpdatePackage)
{
// load the ABC (application binary container)
if (file.FileName.Contains("binfile") &&
file.FileName.EndsWith(".bin") &&
// Sometimes the * complete *.bin is part of the binaries containing all app in one file.
// As this is not part of the ADF it has to be skipped from loading!!!!
!file.FileName.Contains("complete"))
{
var fileApplication = new FileApplications(file.FileName)
{
BinData = new List<Byte>(file.FileContent.ToList())
};
fileApps.Add(fileApplication);
}
// Load the ADF (application description file, containing the information
// of each application)
else if (file.FileName.Contains("product") && file.FileName.EndsWith(".txt"))
{
var enc = new System.Text.ASCIIEncoding();
var adf = enc.GetString(file.FileContent.ToArray());
fwUpdate.LoadPackageFileFromText(adf);
}
}
fwUpdate.LoadFileApps(fileApps);
}
var checkSucceeded = true;
// Compare every installed application for version and CRC with the required to be defined by the downloaded
// ABC (application binary container)
fwUpdate.CompareAllMeterAndFileApps();
if (// Validate package description file with required Apps (fileApps) and build the CORE REVISIONS MIN MAX
!fwUpdate.ValidateFileAppsWithPackageFile() ||
// Check if any meter Apps is outdated or does contain an un-matching CRC
Meter.MeterAppListVersion.Any(app => app.Status != MeterAppState.MeterAppUpToDate &&
app.Status != MeterAppState.MeterAppNotRequired))
{
feedbackMsg += $"{Resources.StrErrorMsgAppVersionOrCrc}\n";
checkSucceeded = false;
// Do not immediately return to summarize all errors following up
}
// The package description file has to be parsed in advance using the fwUpdate.ValidateFileAppsWithPackageFile()!!
if (!fwUpdate.CheckCoreRevision(Meter.CoreRevision))
{
feedbackMsg += $"{Resources.StrErrorMsgCoreRevision}\n";
// Do not immediately return to summarize all errors following up
checkSucceeded = false;
}
if (!checkSucceeded)
{
feedbackMsg += $"{Resources.StrErrorMsgRequirementFw}\n";
return "";
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementFw} - Firmware: {Meter.FwVersion}";
return Meter.FwVersion;
return GenericChecks.CheckFwAndApps(Meter, ProductionRequirements, FwUpdatePackage, out feedbackMsg);
}
/// <summary>
@@ -645,7 +603,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
/// calculation is being used.
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_008]_Check_Remaining_Battery_Load
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_009]_Check_Remaining_Battery_Load
///
/// </summary>
/// <param name="feedbackMsg"></param>
@@ -734,7 +692,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
/// Check the remaining lifetime
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_009]_Check_Remaining_Lifetime
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_010]_Check_Remaining_Lifetime
///
/// </summary>
/// <param name="feedbackMsg"></param>
@@ -785,7 +743,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
/// Check the months between first battery activation and now
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_010]_Check_Storage_Months
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_011]_Check_Storage_Months
///
/// </summary>
/// <param name="feedbackMsg"></param>
@@ -146,20 +146,33 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
processState: ProcessState.GetSerialNumberAndRadioAddress,
processInfo: Resources.StrStateGetSerialAndRadioAddress,
productionProcessNo: 2,
nextStateOnSuccess: ProcessState.CheckOrderNumber,
waitForAllProcesses: true),
// Avoid automatic change after completion as parallel process is active
nextStateOnSuccess: BaseProductionProcess.DoNotChangeStateAfterCompletion,
// Kick off parallel process execution
kickOffParallelState: ProcessState.GetProgrammingParameters),
// Precondition: - The connection process has to be completed as a readout of some register
// values from the Cordonel are needed to check the update capability:
// - Wait on completed connection process before starting this process.
new ProcessStateStruct(
productionProcess: new GetFinalParametrization(Resources.StrStateGetProgrammingParameters),
processState: ProcessState.GetProgrammingParameters,
processInfo: Resources.StrStateGetProgrammingParameters,
productionProcessNo: 3,
nextStateOnSuccess: ProcessState.CheckOrderNumber,
// Wait until this process has been successfully completed
waitForSingleProcess: ProcessState.ConnectCordonel),
#endregion -------------------------------- GENERIC-STATES --------------------------------------------
#region ----------------------------------- PICKING-STATES --------------------------------------------
// This scans the order number, it has to be executed exclusively with automatic
new ProcessStateStruct(
productionProcess: new CheckOrderNumber(Resources.StrStateCheckOrderNumber),
processState: ProcessState.CheckOrderNumber,
processInfo: Resources.StrStateCheckOrderNumber,
productionProcessNo: 3,
productionProcessNo: 4,
nextStateOnSuccess: ProcessState.CheckProductionState),
#endregion -------------------------------- GENERIC-STATES --------------------------------------------
#region ----------------------------------- PICKING-STATES --------------------------------------------
// Precondition: - The connection process has to be completed as a readout of some register
// values from the Cordonel are needed to check the update capability:
// - Wait on completed connection process before starting this process.
@@ -167,7 +180,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
productionProcess: new CheckProductionState(Resources.StrStateCheckProductionState),
processState: ProcessState.CheckProductionState,
processInfo: Resources.StrStateCheckProductionState,
productionProcessNo: 4,
productionProcessNo: 5,
nextStateOnSuccess: ProcessState.Idle),
//TODO Roland: implement your states here
@@ -9,7 +9,6 @@ using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisStatus;
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.ProductionProcesses.GenericProcesses;
using Timer = System.Threading.Timer;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
@@ -285,6 +285,7 @@
</Compile>
<Compile Include="Model\EOLHandler.cs" />
<Compile Include="ProductionProcesses\EolProcesses\VisualInspection.cs" />
<Compile Include="ProductionProcesses\GenericModules\GenericChecks.cs" />
<Compile Include="ProductionProcesses\IProcessStateDef.cs" />
<Compile Include="ProductionProcesses\BaseProcessStatesDef.cs" />
<Compile Include="ProductionProcesses\GenericProcesses\ErrorHandler.cs" />
@@ -298,6 +299,7 @@
<Compile Include="ProductionProcesses\Enum\ProcessState.cs" />
<Compile Include="ProductionProcesses\EventArgs\SingleProcessStateArgs.cs" />
<Compile Include="ProductionProcesses\EolProcesses\EolProcessStatesDef.cs" />
<Compile Include="ProductionProcesses\PickingProcesses\CheckOrderNumber.cs" />
<Compile Include="ProductionProcesses\PickingProcesses\CheckProductionState.cs" />
<Compile Include="ProductionProcesses\PickingProcesses\PickingProcessStatesDef.cs" />
<Compile Include="ProductionProcesses\ProcessStateStruct.cs" />
@@ -311,12 +313,12 @@
<Compile Include="Model\PickingMultiModeViewModel.cs" />
<Compile Include="Model\PickingViewModel.cs" />
<Compile Include="Model\IMultiModeChildViewModel.cs" />
<Compile Include="ProductionProcesses\GenericProcesses\CheckSerialNumber.cs" />
<Compile Include="ProductionProcesses\GenericProcesses\CheckOrderNumber.cs" />
<Compile Include="ProductionProcesses\EolProcesses\CheckSerialNumber.cs" />
<Compile Include="ProductionProcesses\EolProcesses\CheckOrderNumber.cs" />
<Compile Include="ProductionProcesses\EolProcesses\CheckAttachments.cs" />
<Compile Include="ProductionProcesses\EolProcesses\CheckProductionState.cs" />
<Compile Include="ProductionProcesses\EolProcesses\CheckFinalParametrization.cs" />
<Compile Include="ProductionProcesses\EolProcesses\GetFinalParametrization.cs" />
<Compile Include="ProductionProcesses\GenericProcesses\GetFinalParametrization.cs" />
<Compile Include="ProductionProcesses\EolProcesses\ExecFinalParametrization.cs" />
<Compile Include="ProductionProcesses\EolProcesses\CheckRadio.cs" />
<Compile Include="ProductionProcesses\GenericProcesses\ConnectCordonel.cs" />
@@ -19,7 +19,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
@@ -672,6 +672,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Missing radio signal levels from Kitron!.
/// </summary>
internal static string StrErrorMsgRadioSignalLevels {
get {
return ResourceManager.GetString("StrErrorMsgRadioSignalLevels", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Read failed.
/// </summary>
@@ -1050,6 +1059,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Meter Length.
/// </summary>
internal static string StrLblMeterLengthTxt {
get {
return ResourceManager.GetString("StrLblMeterLengthTxt", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Meter Size.
/// </summary>
@@ -1095,6 +1113,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Pressure Sensor Assembled.
/// </summary>
internal static string StrLblPressureSensorAssembledTxt {
get {
return ResourceManager.GetString("StrLblPressureSensorAssembledTxt", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to RadioAddress.
/// </summary>
@@ -1113,6 +1140,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Radio Frequency.
/// </summary>
internal static string StrLblRadioFrequencyTxt {
get {
return ResourceManager.GetString("StrLblRadioFrequencyTxt", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to SerialNumber.
/// </summary>
@@ -1941,6 +1977,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Success: Cordonel radio signal levels successfully loaded.
/// </summary>
internal static string StrSuccessMsgRadioSignalLevels {
get {
return ResourceManager.GetString("StrSuccessMsgRadioSignalLevels", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Success: Successfully read.
/// </summary>
@@ -400,6 +400,12 @@
<data name="StrLblMeterSizeTxt" xml:space="preserve">
<value>Baugröße</value>
</data>
<data name="StrLblMeterLengthTxt" xml:space="preserve">
<value>Baulänge</value>
</data>
<data name="StrLblPressureSensorAssembledTxt" xml:space="preserve">
<value>Drucksensor eingebaut</value>
</data>
<data name="StrLblKitronProductionDate" xml:space="preserve">
<value>Kitron Produktionsdatum</value>
</data>
@@ -412,6 +418,9 @@
<data name="StrLblMonths" xml:space="preserve">
<value>Monate</value>
</data>
<data name="StrLblRadioFrequencyTxt" xml:space="preserve">
<value>Funkfrequenz</value>
</data>
<data name="StrLblRadioAddressTxt" xml:space="preserve">
<value>Funkadresse</value>
</data>
@@ -619,6 +628,9 @@
<data name="StrErrorMsgRequirementMeterSize" xml:space="preserve">
<value>FEHLER: Falsche Nennweite</value>
</data>
<data name="StrErrorMsgRadioSignalLevels" xml:space="preserve">
<value>FEHLER: Fehlende Funksignalpegel von Kitron!</value>
</data>
<data name="StrErrorMsgRequirementProductionOrder" xml:space="preserve">
<value>FEHLER: Falsche Produktionsnummer</value>
</data>
@@ -679,6 +691,9 @@
<data name="StrSuccessMsgRequirementMaximumStorageMonth" xml:space="preserve">
<value>Erfolg: Maximale Lagerzeit eingehalten</value>
</data>
<data name="StrSuccessMsgRadioSignalLevels" xml:space="preserve">
<value>Erfolg: Cordonel Funksignalpegel erfolgreich geladen</value>
</data>
<data name="StrSuccessMsgRequirementMeterSize" xml:space="preserve">
<value>Erfolg: Nennweite erfolgreich validiert</value>
</data>
@@ -400,6 +400,12 @@
<data name="StrLblMeterSizeTxt" xml:space="preserve">
<value>Meter Size</value>
</data>
<data name="StrLblMeterLengthTxt" xml:space="preserve">
<value>Meter Length</value>
</data>
<data name="StrLblPressureSensorAssembledTxt" xml:space="preserve">
<value>Pressure Sensor Assembled</value>
</data>
<data name="StrLblKitronProductionDate" xml:space="preserve">
<value>Kitron production date</value>
</data>
@@ -412,6 +418,9 @@
<data name="StrLblMonths" xml:space="preserve">
<value>Months</value>
</data>
<data name="StrLblRadioFrequencyTxt" xml:space="preserve">
<value>Radio Frequency</value>
</data>
<data name="StrLblRadioAddressTxt" xml:space="preserve">
<value>RadioAddress</value>
</data>
@@ -616,9 +625,12 @@
<data name="StrErrorMsgRequirementMaximumStorageMonthUnknown" xml:space="preserve">
<value>ERROR: Maximum Storage Month or battery assembly date unknown</value>
</data>
<data name="StrErrorMsgRequirementMeterSize" xml:space="preserve">
<data name="StrErrorMsgRequirementMeterSize" xml:space="preserve">
<value>ERROR: Requirement Meter Size does not match</value>
</data>
<data name="StrErrorMsgRadioSignalLevels" xml:space="preserve">
<value>ERROR: Missing radio signal levels from Kitron!</value>
</data>
<data name="StrErrorMsgRequirementProductionOrder" xml:space="preserve">
<value>ERROR: Requirement Production Order does not match</value>
</data>
@@ -679,6 +691,9 @@
<data name="StrSuccessMsgRequirementMaximumStorageMonth" xml:space="preserve">
<value>Success: Requirement for maximum storage time kept</value>
</data>
<data name="StrSuccessMsgRadioSignalLevels" xml:space="preserve">
<value>Success: Cordonel radio signal levels successfully loaded</value>
</data>
<data name="StrSuccessMsgRequirementMeterSize" xml:space="preserve">
<value>Success: Requirement and Cordonel meter size successfully validated</value>
</data>