ProductionUiCordonel: - generic checks pcbid, productoion order, lut crc, region

This commit is contained in:
Thomas Wiedebusch
2025-06-18 15:39:18 +02:00
parent c25391ab10
commit c5a600be64
3 changed files with 459 additions and 682 deletions
@@ -10,7 +10,6 @@ using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisStatus;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
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;
@@ -693,23 +692,22 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2024-Nov-06" author="Thomas Wiedebusch">
/// - Extracted from <see cref="ExecuteProcess"/> for better testability.
/// </remarks>
/// <remarks date="2025-Jun-18" author="Thomas Wiedebusch">
/// - Modified for generic checks.
/// </remarks>
private EOLStatus CheckProductionOrderNumber(out String feedbackMsg)
{
feedbackMsg = "";
if (Meter == null ||
ProductionRequirements == null ||
EolProgress == null ||
ProductionRequirements.ProductionOrderNr != Meter.OrderNumber)
if (EolProgress == null ||
StatusReturn.Failed == GenericChecks.CheckProductionOrderNumber(Meter, ProductionRequirements,
out feedbackMsg))
{
if (EolProgress != null)
EolProgress.RequirementOrderChecked = EOLStatus.FAIL;
feedbackMsg = Resources.StrErrorMsgRequirementProductionOrder;
return EOLStatus.FAIL;
}
EolProgress.RequirementOrderChecked = EOLStatus.OK;
feedbackMsg = $"{Resources.StrSuccessMsgRequirementProductionOrder} - " +
$"{Resources.StrLblOrderNumberTxt}: {Meter.OrderNumber}";
return EolProgress.RequirementOrderChecked;
}
@@ -727,38 +725,32 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2024-Nov-06" author="Thomas Wiedebusch">
/// - Extracted from <see cref="ExecuteProcess"/> for better testability.
/// </remarks>
/// <remarks date="2025-Jun-18" author="Thomas Wiedebusch">
/// - Modified for generic checks.
/// </remarks>
private EOLStatus CheckPcbId(out String feedbackMsg)
{
feedbackMsg = "";
if (Meter == null ||
ProductionRequirements == null ||
EolProgress == null ||
Meter.PcbId == null ||
Meter.PcbId.Length < GenesisMeter.MinPcbIdLength)
if (EolProgress == null ||
StatusReturn.Failed == GenericChecks.CheckPcbId(Meter, ProductionRequirements, out feedbackMsg))
{
if (EolProgress != null)
EolProgress.RequirementPcbIdChecked = EOLStatus.FAIL;
feedbackMsg = Resources.StrErrorMsgRequirementPcbId;
return EOLStatus.FAIL;
}
if (ProductionRequirements.PcbId == null)
{
EolProgress.RequirementPcbIdChecked = EOLStatus.NA;
feedbackMsg = Resources.StrSuccessMsgOrderBasedRequirements;
return EolProgress.RequirementPcbIdChecked;
}
if (Meter.PcbId.Equals(EolProgress.PcbId.ToString()) &&
Meter.PcbId.Equals(ProductionRequirements.PcbId.ToString()))
if (Meter.PcbId.Equals(EolProgress.PcbId.ToString()))
{
EolProgress.RequirementPcbIdChecked = EOLStatus.OK;
feedbackMsg = $"{Resources.StrSuccessMsgRequirementPcbId} - PcbId: {Meter.PcbId}";
return EolProgress.RequirementPcbIdChecked;
}
EolProgress.RequirementPcbIdChecked = EOLStatus.FAIL;
feedbackMsg = Resources.StrErrorMsgRequirementPcbId;
return EolProgress.RequirementPcbIdChecked;
}
@@ -774,23 +766,21 @@ 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-Jun-18" author="Thomas Wiedebusch">
/// - Modified for generic checks.
/// </remarks>
private EOLStatus CheckRegion(out String feedbackMsg)
{
feedbackMsg = "";
if (EolProgress == null ||
ProductionRequirements == null ||
string.IsNullOrEmpty(ProductionRequirements.Region) ||
string.IsNullOrEmpty(Meter.Region) ||
!Meter.Region.Equals(ProductionRequirements.Region))
StatusReturn.Failed == GenericChecks.CheckRegion(Meter, ProductionRequirements, out feedbackMsg))
{
if (EolProgress != null)
EolProgress.RequirementRegionChecked = EOLStatus.FAIL;
feedbackMsg = Resources.StrErrorMsgRequirementRegion;
return EOLStatus.FAIL;
}
EolProgress.RequirementRegionChecked = EOLStatus.OK;
feedbackMsg = $"{Resources.StrSuccessMsgRequirementRegion} - Region: {Meter.Region}";
return EolProgress.RequirementRegionChecked;
}
@@ -809,25 +799,21 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2024-Nov-18" author="Thomas Wiedebusch">
/// - Set result to null on error.
/// </remarks>
/// <remarks date="2025-Jun-18" author="Thomas Wiedebusch">
/// - Modified for generic checks.
/// </remarks>
private String CheckLutCrc(out String feedbackMsg)
{
feedbackMsg = "";
if (EolProgress == null ||
Meter == null ||
ProductionRequirements == null ||
string.IsNullOrEmpty(ProductionRequirements.LutCrc) ||
string.IsNullOrEmpty(Meter.LutCrc) ||
!Meter.LutCrc.Equals(ProductionRequirements.LutCrc))
string.IsNullOrEmpty(GenericChecks.CheckLutCrc(Meter, ProductionRequirements, out feedbackMsg)))
{
feedbackMsg = Resources.StrErrorMsgRequirementLut;
if (EolProgress != null)
EolProgress.ProductionStatusLutCrc = null;
return "";
}
EolProgress.ProductionStatusLutCrc = Meter.LutCrc;
feedbackMsg = $"{Resources.StrSuccessMsgRequirementLut} - LUT CRC: {Meter.LutCrc}";
return EolProgress.ProductionStatusLutCrc;
}
@@ -843,26 +829,21 @@ 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-Jun-18" author="Thomas Wiedebusch">
/// - Modified for generic checks.
/// </remarks>
private String CheckMeterSize(out String feedbackMsg)
{
feedbackMsg = "";
if (EolProgress == null ||
Meter == null ||
ProductionRequirements == null || string.IsNullOrEmpty(ProductionRequirements.MeterSize) ||
string.IsNullOrEmpty(Meter.MeterSize) ||
MeterSizeConverter.ConvertMeterSizeNameToEnum(Meter.MeterSize) !=
MeterSizeConverter.ConvertMeterSizeNameToEnum(ProductionRequirements.MeterSize))
string.IsNullOrEmpty(GenericChecks.CheckMeterSize(Meter, ProductionRequirements, out feedbackMsg)))
{
if (EolProgress != null)
EolProgress.ProductionStatusMeterSize = null;
feedbackMsg = Resources.StrErrorMsgRequirementMeterSize;
return "";
}
EolProgress.ProductionStatusMeterSize = ProductionRequirements.MeterSize;
feedbackMsg = $"{Resources.StrSuccessMsgRequirementMeterSize} - " +
$"{Resources.StrLblMeterSizeTxt}: {Meter.MeterSize}";
return EolProgress.ProductionStatusMeterSize;
}
@@ -946,90 +927,27 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2024-Nov-22" author="Thomas Wiedebusch">
/// - Absolut maximal threshold even if alternative power consumption calculation.
/// </remarks>
/// <remarks date="2025-Jun-18" author="Thomas Wiedebusch">
/// - Modified for generic checks.
/// </remarks>
private EOLStatus CheckRemainingBatteryLoad(out String feedbackMsg)
{
feedbackMsg = "";
if (EolProgress == null ||
Meter == null ||
ProductionRequirements?.MaxDrainedBatteryLoadPercentShipping == null ||
ProductionStatus == null)
StatusReturn.Failed == GenericChecks.CheckRemainingBatteryLoad(Meter, ProductionRequirements,
ProductionStatus, out feedbackMsg))
{
if (EolProgress != null)
{
EolProgress.ProductionStatusDrainedBatteryLoad = ProductionStatus.DrainedBatteryLoadPercent;
EolProgress.ProductionStatusBatteryLoadChecked = EOLStatus.FAIL;
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
}
return EOLStatus.FAIL;
}
// If the values for calculation of drained battery load and exceeded lifetime are not accessible
if (ProductionStatus.DrainedBatteryLoad_uAs == 0 || ProductionStatus.ExceededLifeTime_s == 0)
{
EolProgress.ProductionStatusBatteryLoadChecked = EOLStatus.FAIL;
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
return EolProgress.ProductionStatusBatteryLoadChecked;
}
// Set up the alternative calculation values needed for given current and supplement for production.
// These values will be set by the requirement and have a higher priority than the standard calculation
// based on the drained battery load and the estimated prediction based on this and the time of operation.
if (ProductionRequirements.QuiescentCurrent_uA > GenesisMeter.QuiescentCurrent_uA &&
ProductionRequirements.EstimatedProductionBatteryLoadPercent > 0)
{
ProductionStatus.QuiescentCurrent_uA = ProductionRequirements.QuiescentCurrent_uA;
ProductionStatus.EstimatedProductionBatteryLoadPercent =
ProductionRequirements.EstimatedProductionBatteryLoadPercent;
}
// Calculate the power consumption
if (!GenesisStatusHandler.CalculateLifeTime(ProductionStatus))
{
EolProgress.ProductionStatusBatteryLoadChecked = EOLStatus.FAIL;
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
return EolProgress.ProductionStatusBatteryLoadChecked;
}
// Check battery load in percent and compare with requirements for limits for drained battery load
// For the alternative calculation based quiescent current and production supplement this will be used.
// Build message for required versus calculated lifetime
var threshold = ProductionStatus.AlternativeRequiredBatteryLoadPercent ??
ProductionRequirements.MaxDrainedBatteryLoadPercentShipping;
// Create an output message addon for alternative power consumption calculation
var strAlternativeCalc = ProductionStatus.AlternativeRequiredBatteryLoadPercent == null
? ""
: $" - {Resources.StrMsgRequirementAbsolutLimitDrainedBattery}: " +
$"{ProductionRequirements.MaxDrainedBatteryLoadPercentShipping:F2} %" +
$" ({Resources.StrMsgAlternativeDrainedBatteryCalculation})";
var batteryLoad = $"{Resources.StrMsgDrainedBattery}: " +
// Actual calculated battery load based on readings from meter
$"{ProductionStatus.DrainedBatteryLoadPercent:F2} %" +
$" - {Resources.StrMsgRequirementLimitDrainedBattery}: " +
// Required battery load from standard or special requirement
$"{threshold:F2} % {strAlternativeCalc}";
EolProgress.ProductionStatusDrainedBatteryLoad = ProductionStatus.DrainedBatteryLoadPercent;
// This is the absolut limit even if the "AlternativeRequiredBatteryLoadPercent" will be used
if (ProductionStatus.DrainedBatteryLoadPercent >
ProductionRequirements.MaxDrainedBatteryLoadPercentShipping)
{
EolProgress.ProductionStatusBatteryLoadChecked = EOLStatus.FAIL;
feedbackMsg = $"{Resources.StrErrorMsg}: {batteryLoad}";
return EolProgress.ProductionStatusBatteryLoadChecked;
}
// This is the limit given by the "AlternativeRequiredBatteryLoadPercent"
if (ProductionStatus.AlternativeRequiredBatteryLoadPercent != null &&
ProductionStatus.DrainedBatteryLoadPercent > ProductionStatus.AlternativeRequiredBatteryLoadPercent)
{
EolProgress.ProductionStatusBatteryLoadChecked = EOLStatus.FAIL;
feedbackMsg = $"{Resources.StrErrorMsg}: {batteryLoad}";
return EolProgress.ProductionStatusBatteryLoadChecked;
}
EolProgress.ProductionStatusBatteryLoadChecked = EOLStatus.OK;
feedbackMsg = $"{Resources.StrSuccessMsg}: {batteryLoad}";
return EolProgress.ProductionStatusBatteryLoadChecked;
return EOLStatus.OK;
}
/// <summary>
@@ -1047,53 +965,27 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2024-Nov-22" author="Thomas Wiedebusch">
/// - Check required lifetime for unequal to 0.
/// </remarks>
/// <remarks date="2025-Jun-18" author="Thomas Wiedebusch">
/// - Modified for generic checks.
/// </remarks>
private EOLStatus CheckRemainingLifetime(out String feedbackMsg)
{
feedbackMsg = "";
if (EolProgress == null ||
Meter == null ||
ProductionRequirements?.RequiredLifeTimeYearsShipping == null ||
ProductionRequirements.RequiredLifeTimeYearsShipping <= 0 ||
ProductionStatus == null)
StatusReturn.Failed == GenericChecks.CheckRemainingLifetime(Meter, ProductionRequirements,
ProductionStatus, out feedbackMsg))
{
if (EolProgress != null)
{
EolProgress.ProductionStatusRemainingLifeTime = ProductionStatus.RemainingLifeTimeYears;
EolProgress.ProductionStatusRemainingLifeTimeChecked = EOLStatus.FAIL;
feedbackMsg = Resources.StrErrorMsgLifeTimeCalculation;
}
return EOLStatus.FAIL;
}
// Calculate the power consumption
if (!GenesisStatusHandler.CalculateLifeTime(ProductionStatus))
{
EolProgress.ProductionStatusRemainingLifeTimeChecked = EOLStatus.FAIL;
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
return EolProgress.ProductionStatusBatteryLoadChecked;
}
// Build message for required versus calculated lifetime
var lifeTime = $"{Resources.StrMsgRemainingLifeTime}: " +
// Actual calculated lifetime based on readings from meter
$"{ProductionStatus.RemainingLifeTimeYears:F2} " +
$"{Resources.StrMsgYears}" +
$" - {Resources.StrMsgRequiredLifeTime}: " +
// Required lifetime from special or standard requirement
$"{ProductionRequirements.RequiredLifeTimeYearsShipping:F2} " +
$"{Resources.StrMsgYears}";
EolProgress.ProductionStatusRemainingLifeTime = ProductionStatus.RemainingLifeTimeYears;
// Check required years for shipping
if (ProductionRequirements.RequiredLifeTimeYearsShipping > ProductionStatus.RemainingLifeTimeYears)
{
EolProgress.ProductionStatusRemainingLifeTimeChecked = EOLStatus.FAIL;
feedbackMsg = $"{Resources.StrErrorMsg}: {lifeTime}";
}
else
{
feedbackMsg = $"{Resources.StrSuccessMsg}: {lifeTime}";
EolProgress.ProductionStatusRemainingLifeTimeChecked = EOLStatus.OK;
}
return EolProgress.ProductionStatusRemainingLifeTimeChecked;
EolProgress.ProductionStatusRemainingLifeTimeChecked = EOLStatus.OK;
return EOLStatus.OK;
}
/// <summary>
@@ -1114,56 +1006,23 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2025-Jan-16" author="Thomas Wiedebusch">
/// - Changed from "StatusReturn" to "EOLStatus".
/// </remarks>
/// <remarks date="2025-Jun-18" author="Thomas Wiedebusch">
/// - Modified for generic checks.
/// </remarks>
private EOLStatus CheckStorageMonths(out String feedbackMsg)
{
// Based on first attached battery and the POWEMON_TotalUsedSeconds (Kitron production date) and actual date
feedbackMsg = "";
if (EolProgress == null ||
Meter == null ||
ProductionRequirements?.MaxStorageMonths == null ||
ProductionRequirements.MaxStorageMonths < 0.0 ||
ProductionStatus == null ||
ProductionStatus.DrainedBatteryLoad_uAs == 0 ||
ProductionStatus.ExceededLifeTime_s == 0)
StatusReturn.Failed == GenericChecks.CheckStorageMonths(Meter, ProductionRequirements,
ProductionStatus, out feedbackMsg))
{
if (EolProgress != null)
EolProgress.MaxStorageMonthsChecked = EOLStatus.FAIL;
// Storage months requirement or Kitron production date unknown
feedbackMsg = Resources.StrErrorMsgRequirementMaximumStorageMonthUnknown;
return EOLStatus.FAIL;
}
// Calculate the power consumption
if (!GenesisStatusHandler.CalculateLifeTime(ProductionStatus) || ProductionStatus.BatteryAssemblyDateTimeUtc == null)
{
EolProgress.MaxStorageMonthsChecked = EOLStatus.FAIL;
feedbackMsg = Resources.StrErrorMsgRequirementMaximumStorageMonthUnknown;
return EOLStatus.FAIL;
}
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 * GenesisStatusHandler.DAYS_PER_MONTH + 1.0);
var strStorageProduction = $"{Resources.StrLblKitronProductionDate}: " +
$"{ProductionStatus.BatteryAssemblyDateTimeUtc.Value:dd-MMM-yyyy} - " +
$"{Resources.StrLblExpirationDate}: {expirationDate:dd-MMM-yyyy} - " +
$"{Resources.StrLblMaxStorageMonths}: " +
$"{(Int32)ProductionRequirements.MaxStorageMonths} {Resources.StrLblMonths}";
EolProgress.MaxStorageMonths = ProductionRequirements.MaxStorageMonths;
if (actualDate >= expirationDate)
{
// Storage months exceeded
feedbackMsg = $"{Resources.StrErrorMsgRequirementMaximumStorageMonth} - " + strStorageProduction;
EolProgress.MaxStorageMonthsChecked = EOLStatus.FAIL;
}
else
{
feedbackMsg = $"{Resources.StrSuccessMsgRequirementMaximumStorageMonth} - " + strStorageProduction;
EolProgress.MaxStorageMonthsChecked = EOLStatus.OK;
}
return EolProgress.MaxStorageMonthsChecked;
EolProgress.MaxStorageMonthsChecked = EOLStatus.OK;
return EOLStatus.OK;
}
/// <summary>
@@ -1,16 +1,17 @@
using LaaPackages.Features.Cordonel.Models;
using Logic.ProductionToProductMapper.Files.Fw;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Newtonsoft.Json;
using Xylem.Common.CommonCore.Configuration;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.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.Logic.ProductionOrderCore.Vako;
using Xylem.Common.Logic.SoftwareAccessHelper;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
@@ -19,15 +20,394 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
{
internal static class GenericChecks
{
/// <summary>
/// Check the remaining lifetime
/// </summary>
/// <param name="productionStatus"></param>
/// <param name="feedbackMsg"></param>
/// <param name="meter"></param>
/// <param name="productionRequirements"></param>
/// <param name="assemblyLine">true == assemblyLine, false == EOL</param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static StatusReturn CheckRemainingLifetime(IGenesisMeter meter, CordonelRequirements productionRequirements,
GenesisStatus productionStatus, out String feedbackMsg, Boolean assemblyLine = false)
{
if (meter == null ||
productionRequirements == null ||
(assemblyLine && productionRequirements.RequiredLifeTimeYearsAssembly == null) ||
(assemblyLine && productionRequirements.RequiredLifeTimeYearsAssembly <= 0) ||
(!assemblyLine && productionRequirements.RequiredLifeTimeYearsShipping == null) ||
(!assemblyLine && productionRequirements.RequiredLifeTimeYearsShipping <= 0) ||
productionStatus == null)
{
feedbackMsg = Resources.StrErrorMsgLifeTimeCalculation;
return StatusReturn.Failed;
}
// Calculate the power consumption
if (!GenesisStatusHandler.CalculateLifeTime(productionStatus))
{
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
return StatusReturn.Failed;
}
// Decide which station Assembly-Line or Shipping-Line (EOL)
var strStation = assemblyLine
? $"{productionRequirements.RequiredLifeTimeYearsAssembly:F2} %"
: $"{productionRequirements.RequiredLifeTimeYearsShipping:F2} %";
// Build message for required versus calculated lifetime
var lifeTime = $"{Resources.StrMsgRemainingLifeTime}: " +
// Actual calculated lifetime based on readings from meter
$"{productionStatus.RemainingLifeTimeYears:F2} " +
$"{Resources.StrMsgYears}" +
$" - {Resources.StrMsgRequiredLifeTime}: " +
// Required lifetime from special or standard requirement
strStation +
$"{Resources.StrMsgYears}";
// Check required years for assembly
if ((assemblyLine &&
productionRequirements.RequiredLifeTimeYearsAssembly > productionStatus.RemainingLifeTimeYears) ||
(!assemblyLine &&
productionRequirements.RequiredLifeTimeYearsShipping > productionStatus.RemainingLifeTimeYears))
{
feedbackMsg = $"{Resources.StrErrorMsg}: {lifeTime}";
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrSuccessMsg}: {lifeTime}";
return StatusReturn.Okay;
}
/// <summary>
/// Check the months between first battery activation and now
/// </summary>
/// <param name="productionStatus"></param>
/// <param name="feedbackMsg"></param>
/// <param name="meter"></param>
/// <param name="productionRequirements"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static StatusReturn CheckStorageMonths(IGenesisMeter meter, CordonelRequirements productionRequirements,
GenesisStatus productionStatus, out String feedbackMsg)
{
// Based on first attached battery and the POWEMON_TotalUsedSeconds (Kitron production date) and actual date
if (meter == null ||
productionRequirements?.MaxStorageMonths == null ||
productionRequirements.MaxStorageMonths < 0.0 ||
productionStatus == null ||
productionStatus.DrainedBatteryLoad_uAs == 0 ||
productionStatus.ExceededLifeTime_s == 0)
{
// Storage months requirement or Kitron production date unknown
feedbackMsg = Resources.StrErrorMsgRequirementMaximumStorageMonthUnknown;
return StatusReturn.Failed;
}
// Calculate the power consumption
if (!GenesisStatusHandler.CalculateLifeTime(productionStatus) || productionStatus.BatteryAssemblyDateTimeUtc == null)
{
feedbackMsg = Resources.StrErrorMsgRequirementMaximumStorageMonthUnknown;
return StatusReturn.Failed;
}
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 *
GenesisStatusHandler.DAYS_PER_MONTH + 1.0);
var strStorageProduction = $"{Resources.StrLblKitronProductionDate}: " +
$"{productionStatus.BatteryAssemblyDateTimeUtc.Value:dd-MMM-yyyy} - " +
$"{Resources.StrLblExpirationDate}: {expirationDate:dd-MMM-yyyy} - " +
$"{Resources.StrLblMaxStorageMonths}: " +
$"{(Int32)productionRequirements.MaxStorageMonths} {Resources.StrLblMonths}";
if (actualDate >= expirationDate)
{
// Storage months exceeded
feedbackMsg = $"{Resources.StrErrorMsgRequirementMaximumStorageMonth} - " + strStorageProduction;
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementMaximumStorageMonth} - " + strStorageProduction;
return StatusReturn.Okay;
}
/// <summary>
/// Check the remaining battery load based on the thresholds which can be either:
/// - The AlternativeRequiredBatteryLoadPercent if the QuiescentCurrent_uA is set to a value greater than 50 uA
/// and the EstimatedProductionBatteryLoadPercent is greater than 0.0 %
/// and
/// - the MaxDrainedBatteryLoadPercentAssembly limits the threshold,
/// or only
/// - the MaxDrainedBatteryLoadPercentAssembly/Shipping limits the threshold as absolut limit.
///
/// ATTENTION: The MaxDrainedBatteryLoadPercentAssembly/Shipping forces the absolut limit even if the alternative
/// calculation is being used.
/// </summary>
/// <param name="productionStatus"></param>
/// <param name="feedbackMsg"></param>
/// <param name="meter"></param>
/// <param name="productionRequirements"></param>
/// <param name="assemblyLine">true == assemblyLine, false == EOL</param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static StatusReturn CheckRemainingBatteryLoad(IGenesisMeter meter, CordonelRequirements productionRequirements,
GenesisStatus productionStatus, out String feedbackMsg, Boolean assemblyLine = false)
{
if (meter == null ||
productionRequirements == null ||
(assemblyLine && productionRequirements.MaxDrainedBatteryLoadPercentAssembly == null) ||
(!assemblyLine && productionRequirements.MaxDrainedBatteryLoadPercentShipping == null) ||
productionStatus == null)
{
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
return StatusReturn.Failed;
}
// If the values for calculation of drained battery load and exceeded lifetime are not accessible
if (productionStatus.DrainedBatteryLoad_uAs == 0 || productionStatus.ExceededLifeTime_s == 0)
{
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
return StatusReturn.Failed;
}
// Set up the alternative calculation values needed for given current and supplement for production.
// These values will be set by the requirement and have a higher priority than the standard calculation
// based on the drained battery load and the estimated prediction based on this and the time of operation.
if (productionRequirements.QuiescentCurrent_uA > GenesisMeter.QuiescentCurrent_uA &&
productionRequirements.EstimatedProductionBatteryLoadPercent > 0)
{
productionStatus.QuiescentCurrent_uA = productionRequirements.QuiescentCurrent_uA;
productionStatus.EstimatedProductionBatteryLoadPercent =
productionRequirements.EstimatedProductionBatteryLoadPercent;
}
// Calculate the power consumption
if (!GenesisStatusHandler.CalculateLifeTime(productionStatus))
{
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
return StatusReturn.Failed;
}
// Check battery load in percent and compare with requirements for limits for drained battery load
// For the alternative calculation based quiescent current and production supplement this will be used.
// Build message for required versus calculated lifetime
Double? threshold;
if (assemblyLine)
{
threshold = productionStatus.AlternativeRequiredBatteryLoadPercent ??
productionRequirements.MaxDrainedBatteryLoadPercentAssembly;
}
else
{
threshold = productionStatus.AlternativeRequiredBatteryLoadPercent ??
productionRequirements.MaxDrainedBatteryLoadPercentShipping;
}
// Decide which station Assembly-Line or Shipping-Line (EOL)
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.StrMsgAlternativeDrainedBatteryCalculation})";
var strBatLoad = $"{Resources.StrMsgDrainedBattery}: " +
// Actual calculated battery load based on readings from meter
$"{productionStatus.DrainedBatteryLoadPercent:F2} %" +
$" - {Resources.StrMsgRequirementLimitDrainedBattery}: " +
// Required battery load from standard or special requirement
$"{threshold:F2} % {strAlternativeCalc}";
// This is the absolut limit even if the "AlternativeRequiredBatteryLoadPercent" will be used
if ((assemblyLine && productionStatus.DrainedBatteryLoadPercent >
productionRequirements.MaxDrainedBatteryLoadPercentAssembly) ||
(!assemblyLine && productionStatus.DrainedBatteryLoadPercent >
productionRequirements.MaxDrainedBatteryLoadPercentShipping))
{
feedbackMsg = $"{Resources.StrErrorMsg}: {strBatLoad}";
return StatusReturn.Failed;
}
// This is the limit given by the "AlternativeRequiredBatteryLoadPercent"
if (productionStatus.AlternativeRequiredBatteryLoadPercent != null &&
productionStatus.DrainedBatteryLoadPercent > productionStatus.AlternativeRequiredBatteryLoadPercent)
{
feedbackMsg = $"{Resources.StrErrorMsg}: {strBatLoad}";
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrSuccessMsg}: {strBatLoad}";
return StatusReturn.Okay;
}
/// <summary>
/// Check meter size
/// </summary>
/// <param name="meter"></param>
/// <param name="productionRequirements"></param>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static String CheckMeterSize(IGenesisMeter meter, CordonelRequirements productionRequirements,
out String feedbackMsg)
{
feedbackMsg = "";
if (meter == null ||
productionRequirements == null || string.IsNullOrEmpty(productionRequirements.MeterSize) ||
string.IsNullOrEmpty(meter.MeterSize) ||
MeterSizeConverter.ConvertMeterSizeNameToEnum(meter.MeterSize) !=
MeterSizeConverter.ConvertMeterSizeNameToEnum(productionRequirements.MeterSize))
{
feedbackMsg = Resources.StrErrorMsgRequirementMeterSize;
return "";
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementMeterSize} - " +
$"{Resources.StrLblMeterSizeTxt}: {meter.MeterSize}";
return productionRequirements.MeterSize;
}
/// <summary>
/// Check PcbId of meter and requirements. Mark PcbId as skipped if
/// production requirements PcbId is null as this is the indication that all Cordonels
/// assigned to this production order are produced according to this requirement.
/// <param name="productionRequirements"></param>
/// <param name="feedbackMsg"></param>
/// <param name="meter"></param>
/// </summary>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static StatusReturn CheckPcbId(IGenesisMeter meter, CordonelRequirements productionRequirements,
out String feedbackMsg)
{
feedbackMsg = "";
if (meter == null ||
productionRequirements == null ||
meter.PcbId == null ||
meter.PcbId.Length < GenesisMeter.MinPcbIdLength)
{
feedbackMsg = Resources.StrErrorMsgRequirementPcbId;
return StatusReturn.Failed;
}
if (productionRequirements.PcbId == null)
{
feedbackMsg = Resources.StrSuccessMsgOrderBasedRequirements;
return StatusReturn.Okay;
}
if (meter.PcbId.Equals(productionRequirements.PcbId.ToString()))
{
feedbackMsg = $"{Resources.StrSuccessMsgRequirementPcbId} - PcbId: {meter.PcbId}";
return StatusReturn.Okay;
}
feedbackMsg = Resources.StrErrorMsgRequirementPcbId;
return StatusReturn.Failed;
}
/// <summary>
/// Check production order number between meter and requirements.
/// </summary>
/// <param name="productionRequirements"></param>
/// <param name="feedbackMsg"></param>
/// <param name="meter"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static StatusReturn CheckProductionOrderNumber(IGenesisMeter meter, CordonelRequirements productionRequirements,
out String feedbackMsg)
{
feedbackMsg = "";
if (meter == null ||
productionRequirements == null ||
productionRequirements.ProductionOrderNr != meter.OrderNumber)
{
feedbackMsg = Resources.StrErrorMsgRequirementProductionOrder;
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementProductionOrder} - " +
$"{Resources.StrLblOrderNumberTxt}: {meter.OrderNumber}";
return StatusReturn.Okay;
}
/// <summary>
/// Check region between meter and requirements
/// </summary>
/// <param name="productionRequirements"></param>
/// <param name="feedbackMsg"></param>
/// <param name="meter"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static StatusReturn CheckRegion(IGenesisMeter meter, CordonelRequirements productionRequirements, out String feedbackMsg)
{
feedbackMsg = "";
if (productionRequirements == null ||
string.IsNullOrEmpty(productionRequirements.Region) ||
string.IsNullOrEmpty(meter.Region) ||
!meter.Region.Equals(productionRequirements.Region))
{
feedbackMsg = Resources.StrErrorMsgRequirementRegion;
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementRegion} - Region: {meter.Region}";
return StatusReturn.Okay;
}
/// <summary>
/// Check Look-Up-Table (LUT) for metrology calibration values for the checksum (CRC) of this table
/// </summary>
/// <param name="productionRequirements"></param>
/// <param name="feedbackMsg"></param>
/// <param name="meter"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static String CheckLutCrc(IGenesisMeter meter, CordonelRequirements productionRequirements, out String feedbackMsg)
{
feedbackMsg = "";
if (meter == null ||
productionRequirements == null ||
string.IsNullOrEmpty(productionRequirements.LutCrc) ||
string.IsNullOrEmpty(meter.LutCrc) ||
!meter.LutCrc.Equals(productionRequirements.LutCrc))
{
feedbackMsg = Resources.StrErrorMsgRequirementLut;
return "";
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementLut} - LUT CRC: {meter.LutCrc}";
return meter.LutCrc;
}
/// <summary>
/// Check for requirement permission special or standard or none:
/// - If the special requirement is active all fields a filled with the according values from this "special"
/// requirement,
/// - Else those fields contain the "standard" requirements!
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_001]_Check_Requirements
///
/// </summary>
/// <param name="productionRequirements"></param>
/// <param name="feedbackMsg"></param>
@@ -48,7 +428,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
// requirement.
var isSpecialActive = false;
var isStandardActive = false;
if (productionRequirements.IsSpecialActive != null && (Boolean)productionRequirements.IsSpecialActive &&
if (productionRequirements.IsSpecialActive != null && (Boolean)productionRequirements.IsSpecialActive &&
productionRequirements.IsSpecialApproved != null && (Boolean)productionRequirements.IsSpecialApproved)
isSpecialActive = true;
if (productionRequirements.IsStandardActive != null && (Boolean)productionRequirements.IsStandardActive &&
@@ -78,7 +458,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
return StatusReturn.Okay;
}
/// <summary>
/// <summary>
/// Check the measurements from Kitron for this specific Cordonel if it is an EMEA region.
/// </summary>
/// <param name="registerRestorer"></param>
@@ -117,8 +497,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
// 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")))
{
feedbackMsg = Resources.StrErrorMsgRadioSignalLevels + " " + meterInfo;
return StatusReturn.Failed;
feedbackMsg = Resources.StrErrorMsgRadioSignalLevels + " " + meterInfo;
return StatusReturn.Failed;
}
feedbackMsg = Resources.StrSuccessMsgRadioSignalLevels + " - " + meterInfo;
@@ -6,7 +6,6 @@ using Xylem.Common.CommonCore.Consts;
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;
@@ -175,7 +174,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
#region ----------------------------------- Check Production Order ----------------------------------------
// Production order
if (StatusReturn.Failed == CheckProductionOrderNumber(out feedbackMsg))
if (StatusReturn.Failed == GenericChecks.CheckProductionOrderNumber(Meter, ProductionRequirements,
out feedbackMsg))
{
ErrorMsgDispatcher(feedbackMsg);
return StatusReturn.Failed;
@@ -187,7 +187,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
#region ----------------------------------- Check PCBID ---------------------------------------------------
// PCBID read from meter compared with requirement PcbIds
statusReturn = CheckPcbId(out feedbackMsg);
statusReturn = GenericChecks.CheckPcbId(Meter, ProductionRequirements, out feedbackMsg);
// Exit here with error
if (StatusReturn.Failed == statusReturn)
@@ -202,7 +202,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
#region ----------------------------------- Check Region --------------------------------------------------
if (StatusReturn.Failed == CheckRegion(out feedbackMsg))
if (StatusReturn.Failed == GenericChecks.CheckRegion(Meter, ProductionRequirements, out feedbackMsg))
{
ErrorMsgDispatcher(feedbackMsg);
return StatusReturn.Failed;
@@ -214,7 +214,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
#region ----------------------------------- Check LUT CRC -------------------------------------------------
if (string.IsNullOrEmpty(CheckLutCrc(out feedbackMsg)))
if (string.IsNullOrEmpty(GenericChecks.CheckLutCrc(Meter, ProductionRequirements, out feedbackMsg)))
{
ErrorMsgDispatcher(feedbackMsg);
return StatusReturn.Failed;
@@ -226,7 +226,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
#region ----------------------------------- Check Meter Size ----------------------------------------------
if (string.IsNullOrEmpty(CheckMeterSize(out feedbackMsg)))
if (string.IsNullOrEmpty(GenericChecks.CheckMeterSize(Meter, ProductionRequirements, out feedbackMsg)))
{
ErrorMsgDispatcher(feedbackMsg);
return StatusReturn.Failed;
@@ -238,7 +238,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
#region ----------------------------------- Check Kitron Radio Signal Levels ------------------------------
statusReturn = CheckKitronRadioSignalLevels(out feedbackMsg);
statusReturn = GenericChecks.CheckKitronRadioSignalLevels(Meter, RegisterRestorer,
ProductionRequirements.SkipRadioCheck,out feedbackMsg);
if (StatusReturn.Failed == statusReturn)
{
ErrorMsgDispatcher(feedbackMsg);
@@ -254,7 +255,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
#region ----------------------------------- Check Firmware and Apps ---------------------------------------
if (string.IsNullOrEmpty(CheckFwAndApps(out feedbackMsg)))
if (string.IsNullOrEmpty(GenericChecks.CheckFwAndApps(Meter, ProductionRequirements, FwUpdatePackage,
out feedbackMsg)))
{
ErrorMsgDispatcher(feedbackMsg);
return StatusReturn.Failed;
@@ -267,7 +269,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
#region ----------------------------------- Check Remaining Battery Load ----------------------------------
var erroneousSequenceStep = false;
if (StatusReturn.Failed == CheckRemainingBatteryLoad(out feedbackMsg))
if (StatusReturn.Failed == GenericChecks.CheckRemainingBatteryLoad(Meter, ProductionRequirements,
ProductionStatus, out feedbackMsg, true))
{
ErrorMsgDispatcher(feedbackMsg);
//return StatusReturn.Failed;
@@ -281,7 +284,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
#region ----------------------------------- Check Remaining Life Time -------------------------------------
if (StatusReturn.Failed == CheckRemainingLifetime(out feedbackMsg))
if (StatusReturn.Failed == GenericChecks.CheckRemainingLifetime(Meter, ProductionRequirements,
ProductionStatus, out feedbackMsg, true))
{
ErrorMsgDispatcher(feedbackMsg);
//return StatusReturn.Failed;
@@ -295,7 +299,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
#region ----------------------------------- Check Storage Months ------------------------------------------
if (StatusReturn.Failed == CheckStorageMonths(out feedbackMsg))
if (StatusReturn.Failed == GenericChecks.CheckStorageMonths(Meter, ProductionRequirements,
ProductionStatus, out feedbackMsg))
{
ErrorMsgDispatcher(feedbackMsg);
//return StatusReturn.Failed;
@@ -336,473 +341,6 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
MessageBox.Show(errorMsg, Resources.StrProcessStateError, MessageBoxButton.OK, MessageBoxImage.Error);
return StatusReturn.Failed;
}
///// <summary>
///// Check for requirement permission special or standard or none:
///// - If the special requirement is active all fields a filled with the according values from this "special"
///// requirement,
///// - Else those fields contain the "standard" requirements!
/////
///// Software Requirement Specification:
///// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_001]_Check_Requirements
/////
///// </summary>
///// <param name="feedbackMsg"></param>
///// <returns></returns>
///// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
///// - Initial.
///// </remarks>
//private StatusReturn CheckRequirements(out String feedbackMsg)
//{
// feedbackMsg = "";
// if (ProductionRequirements == null)
// {
// feedbackMsg = Resources.StrErrorMsgProductionRequirements;
// return StatusReturn.Failed;
// }
// // Check if requirement is active (free for production) and approved by a second person. Priority special
// // requirement.
// var isSpecialActive = false;
// var isStandardActive = false;
// if (ProductionRequirements.IsSpecialActive != null && (Boolean)ProductionRequirements.IsSpecialActive &&
// ProductionRequirements.IsSpecialApproved != null && (Boolean)ProductionRequirements.IsSpecialApproved)
// isSpecialActive = true;
// if (ProductionRequirements.IsStandardActive != null && (Boolean)ProductionRequirements.IsStandardActive &&
// ProductionRequirements.IsStandardApproved != null && (Boolean)ProductionRequirements.IsStandardApproved)
// isStandardActive = true;
// // At least one requirement needs to be active and a requirement number and version has to be set
// 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)))
// {
// feedbackMsg = Resources.StrErrorMsgRequirementInactive;
// return StatusReturn.Failed;
// }
// feedbackMsg = isSpecialActive &&
// ProductionRequirements.SpecialId != null &&
// ProductionRequirements.SpecialVersion != null ?
// Resources.StrWarningMsgSpecialRequirementActive +
// $" Id.Ver: {ProductionRequirements.SpecialId}.{ProductionRequirements.SpecialVersion}" :
// Resources.StrSuccessMsgRequirementActive +
// $" Id.Ver {ProductionRequirements.StandardId}.{ProductionRequirements.StandardVersion}";
// return StatusReturn.Okay;
//}
/// <summary>
/// Check production order number between meter and requirements.
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_002]_Check_ProductionOrderNo
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private StatusReturn CheckProductionOrderNumber(out String feedbackMsg)
{
feedbackMsg = "";
if (Meter == null ||
ProductionRequirements == null ||
ProductionRequirements.ProductionOrderNr != Meter.OrderNumber)
{
feedbackMsg = Resources.StrErrorMsgRequirementProductionOrder;
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementProductionOrder} - " +
$"{Resources.StrLblOrderNumberTxt}: {Meter.OrderNumber}";
return StatusReturn.Okay;
}
/// <summary>
/// Check PcbId of meter and requirements. Mark PcbId as skipped if
/// production requirements PcbId is null as this is the indication that all Cordonels
/// assigned to this production order are produced according to this requirement.
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_003]_Check_PcbId
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private StatusReturn CheckPcbId(out String feedbackMsg)
{
feedbackMsg = "";
if (Meter == null ||
ProductionRequirements == null ||
Meter.PcbId == null ||
Meter.PcbId.Length < GenesisMeter.MinPcbIdLength)
{
feedbackMsg = Resources.StrErrorMsgRequirementPcbId;
return StatusReturn.Failed;
}
if (ProductionRequirements.PcbId == null)
{
feedbackMsg = Resources.StrSuccessMsgOrderBasedRequirements;
return StatusReturn.Okay;
}
if (Meter.PcbId.Equals(ProductionRequirements.PcbId.ToString()))
{
feedbackMsg = $"{Resources.StrSuccessMsgRequirementPcbId} - PcbId: {Meter.PcbId}";
return StatusReturn.Okay;
}
feedbackMsg = Resources.StrErrorMsgRequirementPcbId;
return StatusReturn.Failed;
}
/// <summary>
/// Check region between meter and requirements
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_004]_Check_Region
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private StatusReturn CheckRegion(out String feedbackMsg)
{
feedbackMsg = "";
if (ProductionRequirements == null ||
string.IsNullOrEmpty(ProductionRequirements.Region) ||
string.IsNullOrEmpty(Meter.Region) ||
!Meter.Region.Equals(ProductionRequirements.Region))
{
feedbackMsg = Resources.StrErrorMsgRequirementRegion;
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementRegion} - Region: {Meter.Region}";
return StatusReturn.Okay;
}
/// <summary>
/// Check Look-Up-Table (LUT) for metrology calibration values for the checksum (CRC) of this table
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_005]_Check_LUT_CRC
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private String CheckLutCrc(out String feedbackMsg)
{
feedbackMsg = "";
if (Meter == null ||
ProductionRequirements == null ||
string.IsNullOrEmpty(ProductionRequirements.LutCrc) ||
string.IsNullOrEmpty(Meter.LutCrc) ||
!Meter.LutCrc.Equals(ProductionRequirements.LutCrc))
{
feedbackMsg = Resources.StrErrorMsgRequirementLut;
return "";
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementLut} - LUT CRC: {Meter.LutCrc}";
return Meter.LutCrc;
}
/// <summary>
/// Check meter size
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_006]_Check_Meter_Size
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private String CheckMeterSize(out String feedbackMsg)
{
feedbackMsg = "";
if (Meter == null ||
ProductionRequirements == null || string.IsNullOrEmpty(ProductionRequirements.MeterSize) ||
string.IsNullOrEmpty(Meter.MeterSize) ||
MeterSizeConverter.ConvertMeterSizeNameToEnum(Meter.MeterSize) !=
MeterSizeConverter.ConvertMeterSizeNameToEnum(ProductionRequirements.MeterSize))
{
feedbackMsg = Resources.StrErrorMsgRequirementMeterSize;
return "";
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementMeterSize} - " +
$"{Resources.StrLblMeterSizeTxt}: {Meter.MeterSize}";
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, ProductionRequirements.SkipRadioCheck,
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
/// 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).
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_008]_Check_FW_and_Apps
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <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)
{
return GenericChecks.CheckFwAndApps(Meter, ProductionRequirements, FwUpdatePackage, out feedbackMsg);
}
/// <summary>
/// Check the remaining battery load based on the thresholds which can be either:
/// - The AlternativeRequiredBatteryLoadPercent if the QuiescentCurrent_uA is set to a value greater than 50 uA
/// and the EstimatedProductionBatteryLoadPercent is greater than 0.0 %
/// and
/// - the MaxDrainedBatteryLoadPercentAssembly limits the threshold,
/// or only
/// - the MaxDrainedBatteryLoadPercentAssembly limits the threshold as absolut limit.
///
/// ATTENTION: The MaxDrainedBatteryLoadPercentAssembly forces the absolut limit even if the alternative
/// calculation is being used.
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_009]_Check_Remaining_Battery_Load
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private StatusReturn CheckRemainingBatteryLoad(out String feedbackMsg)
{
if (Meter == null ||
ProductionRequirements?.MaxDrainedBatteryLoadPercentAssembly == null ||
ProductionStatus == null)
{
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
return StatusReturn.Failed;
}
// If the values for calculation of drained battery load and exceeded lifetime are not accessible
if (ProductionStatus.DrainedBatteryLoad_uAs == 0 || ProductionStatus.ExceededLifeTime_s == 0)
{
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
return StatusReturn.Failed;
}
// Set up the alternative calculation values needed for given current and supplement for production.
// These values will be set by the requirement and have a higher priority than the standard calculation
// based on the drained battery load and the estimated prediction based on this and the time of operation.
if (ProductionRequirements.QuiescentCurrent_uA > GenesisMeter.QuiescentCurrent_uA &&
ProductionRequirements.EstimatedProductionBatteryLoadPercent > 0)
{
ProductionStatus.QuiescentCurrent_uA = ProductionRequirements.QuiescentCurrent_uA;
ProductionStatus.EstimatedProductionBatteryLoadPercent =
ProductionRequirements.EstimatedProductionBatteryLoadPercent;
}
// Calculate the power consumption
if (!GenesisStatusHandler.CalculateLifeTime(ProductionStatus))
{
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
return StatusReturn.Failed;
}
// Check battery load in percent and compare with requirements for limits for drained battery load
// For the alternative calculation based quiescent current and production supplement this will be used.
// Build message for required versus calculated lifetime
var threshold = ProductionStatus.AlternativeRequiredBatteryLoadPercent ??
ProductionRequirements.MaxDrainedBatteryLoadPercentAssembly;
// Create an output message addon for alternative power consumption calculation
var strAlternativeCalc = ProductionStatus.AlternativeRequiredBatteryLoadPercent == null
? ""
: $" - {Resources.StrMsgRequirementAbsolutLimitDrainedBattery}: " +
$"{ProductionRequirements.MaxDrainedBatteryLoadPercentAssembly:F2} %" +
$" ({Resources.StrMsgAlternativeDrainedBatteryCalculation})";
var batteryLoad = $"{Resources.StrMsgDrainedBattery}: " +
// Actual calculated battery load based on readings from meter
$"{ProductionStatus.DrainedBatteryLoadPercent:F2} %" +
$" - {Resources.StrMsgRequirementLimitDrainedBattery}: " +
// Required battery load from standard or special requirement
$"{threshold:F2} % {strAlternativeCalc}";
// This is the absolut limit even if the "AlternativeRequiredBatteryLoadPercent" will be used
if (ProductionStatus.DrainedBatteryLoadPercent >
ProductionRequirements.MaxDrainedBatteryLoadPercentAssembly)
{
feedbackMsg = $"{Resources.StrErrorMsg}: {batteryLoad}";
return StatusReturn.Failed;
}
// This is the limit given by the "AlternativeRequiredBatteryLoadPercent"
if (ProductionStatus.AlternativeRequiredBatteryLoadPercent != null &&
ProductionStatus.DrainedBatteryLoadPercent > ProductionStatus.AlternativeRequiredBatteryLoadPercent)
{
feedbackMsg = $"{Resources.StrErrorMsg}: {batteryLoad}";
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrSuccessMsg}: {batteryLoad}";
return StatusReturn.Okay;
}
/// <summary>
/// Check the remaining lifetime
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_010]_Check_Remaining_Lifetime
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private StatusReturn CheckRemainingLifetime(out String feedbackMsg)
{
if (Meter == null ||
ProductionRequirements?.RequiredLifeTimeYearsAssembly == null ||
ProductionRequirements.RequiredLifeTimeYearsAssembly <= 0 ||
ProductionStatus == null)
{
feedbackMsg = Resources.StrErrorMsgLifeTimeCalculation;
return StatusReturn.Failed;
}
// Calculate the power consumption
if (!GenesisStatusHandler.CalculateLifeTime(ProductionStatus))
{
feedbackMsg = Resources.StrErrorMsgBatteryLoadCalculation;
return StatusReturn.Failed;
}
// Build message for required versus calculated lifetime
var lifeTime = $"{Resources.StrMsgRemainingLifeTime}: " +
// Actual calculated lifetime based on readings from meter
$"{ProductionStatus.RemainingLifeTimeYears:F2} " +
$"{Resources.StrMsgYears}" +
$" - {Resources.StrMsgRequiredLifeTime}: " +
// Required lifetime from special or standard requirement
$"{ProductionRequirements.RequiredLifeTimeYearsAssembly:F2} " +
$"{Resources.StrMsgYears}";
// Check required years for assembly
if (ProductionRequirements.RequiredLifeTimeYearsAssembly > ProductionStatus.RemainingLifeTimeYears)
{
feedbackMsg = $"{Resources.StrErrorMsg}: {lifeTime}";
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrSuccessMsg}: {lifeTime}";
return StatusReturn.Okay;
}
/// <summary>
/// Check the months between first battery activation and now
///
/// Software Requirement Specification:
/// [SRS_TBF_PICK_PP_CHECK_PROD_STATE_011]_Check_Storage_Months
///
/// </summary>
/// <param name="feedbackMsg"></param>
/// <returns></returns>
/// <remarks date="2025-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private StatusReturn CheckStorageMonths(out String feedbackMsg)
{
// Based on first attached battery and the POWEMON_TotalUsedSeconds (Kitron production date) and actual date
if (Meter == null ||
ProductionRequirements?.MaxStorageMonths == null ||
ProductionRequirements.MaxStorageMonths < 0.0 ||
ProductionStatus == null ||
ProductionStatus.DrainedBatteryLoad_uAs == 0 ||
ProductionStatus.ExceededLifeTime_s == 0)
{
// Storage months requirement or Kitron production date unknown
feedbackMsg = Resources.StrErrorMsgRequirementMaximumStorageMonthUnknown;
return StatusReturn.Failed;
}
// Calculate the power consumption
if (!GenesisStatusHandler.CalculateLifeTime(ProductionStatus) || ProductionStatus.BatteryAssemblyDateTimeUtc == null)
{
feedbackMsg = Resources.StrErrorMsgRequirementMaximumStorageMonthUnknown;
return StatusReturn.Failed;
}
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 * GenesisStatusHandler.DAYS_PER_MONTH + 1.0);
var strStorageProduction = $"{Resources.StrLblKitronProductionDate}: " +
$"{ProductionStatus.BatteryAssemblyDateTimeUtc.Value:dd-MMM-yyyy} - " +
$"{Resources.StrLblExpirationDate}: {expirationDate:dd-MMM-yyyy} - " +
$"{Resources.StrLblMaxStorageMonths}: " +
$"{(Int32)ProductionRequirements.MaxStorageMonths} {Resources.StrLblMonths}";
if (actualDate >= expirationDate)
{
// Storage months exceeded
feedbackMsg = $"{Resources.StrErrorMsgRequirementMaximumStorageMonth} - " + strStorageProduction;
return StatusReturn.Failed;
}
feedbackMsg = $"{Resources.StrSuccessMsgRequirementMaximumStorageMonth} - " + strStorageProduction;
return StatusReturn.Okay;
}
}
}