Merge branch 'Dev_ProdUiCord'

This commit is contained in:
Thomas Wiedebusch
2025-06-18 12:04:50 +02:00
26 changed files with 761 additions and 360 deletions
@@ -1,73 +0,0 @@
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum
{
/// <summary>
/// Genesis meter final test states
/// </summary>
public enum PickingProcessState
{
/// <summary>
/// Idle (nothing to do)
/// </summary>
Idle,
/// <summary>
/// Idle (nothing to do)
/// </summary>
ScanOrder,
/// <summary>
/// New meter detected for marker to clear all process contents like production status
/// and programming parameters.
/// </summary>
CheckNewMeter,
/// <summary>
/// Error handling
/// </summary>
Error,
/// <summary>
/// Abort final test execution
/// </summary>
Abort,
/// <summary>
/// Repeat the entire final test
/// </summary>
RepeatFailedTests,
/// <summary>
/// Final test failed
/// </summary>
PickingFailed,
/// <summary>
/// User stopped process
/// </summary>
Stop,
/// <summary>
/// Check the actual production state if all steps are executed
/// </summary>
DetectCordonel,
/// <summary>
/// Connect to Cordonel
/// </summary>
ConnectCordonel,
/// <summary>
/// Request the serial number and radio address from DB
/// </summary>
GetPickingParameters,
/// <summary>
/// Check the radio functionality
/// </summary>
CompareMeterDetails,
InstallLut,
//todo ad excisting processstates
PrintReturnReport,
/// <summary>
/// Print success report
/// </summary>
PrintSuccessReport,
/// <summary>
/// Delimiter for list
/// </summary>
StateListDelimiter
}
}
@@ -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;
@@ -29,7 +28,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <summary>
/// Maximum of expected processing steps for this process to feed the GUI progress bar.
/// </summary>
private const Int32 MaxActualProgressSteps = 22;
private const Int32 MaxActualProgressSteps = 23;
/// <summary>
/// Successfully test on flow test bench will return this const value.
@@ -121,13 +120,13 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
ActualProcessProgress++;
// Get clustered FileId of firmware package for production or on special requirement release candidates
var isSpecialActive = ProductionRequirements.IsSpecialActive != null &&
var isSpecialActive = ProductionRequirements.IsSpecialActive != null &&
(Boolean)ProductionRequirements.IsSpecialActive &&
ProductionRequirements.IsSpecialApproved != null &&
ProductionRequirements.IsSpecialApproved != null &&
(Boolean)ProductionRequirements.IsSpecialApproved;
if (string.IsNullOrEmpty(ProductionRequirements.FwVersion) ||
!MeterFwUpdate.SearchProdFwInDb(ProductionRequirements.FwVersion, out var parentFileId, out error,
!MeterFwUpdate.SearchProdFwInDb(ProductionRequirements.FwVersion, out var parentFileId, out error,
!isSpecialActive) || parentFileId == null)
{
Meter.WriteLog(error);
@@ -168,9 +167,22 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
ActualProcessProgress++;
// Get the meter length from VAKO needed for radio parameter message
if (!GenericChecks.GetMeterLengthFromDb(Meter))
{
// Meter size unknown
ErrorMsgDispatcher(Resources.StrErrorMsgMeterLengthUnknown);
return StatusReturn.Failed;
}
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
ActualProcessProgress++;
// Check if requirement will force skipping of calibration
if (!ProductionRequirements.SkipCalibration)
{
@@ -186,7 +198,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
ActualProcessProgress++;
// Get the pre- and final flow test state.
@@ -202,7 +214,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
// Exit if meanwhile cancellation is required
if (CancellationToken.IsCancellationRequested)
return CancellationProcedure();
ActualProcessProgress++;
// Fill the results to calibration check register restorer which will be used to read the actual
@@ -251,6 +263,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
CalibCheckRegisterRestorer.OnProcessUpdate -= ProcessUpdate_Handler;
}// Not Skip calibration loop
ActualProcessProgress++;
return StatusReturn.Okay;
}
@@ -315,6 +329,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 +421,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 +539,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);
@@ -527,17 +560,17 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
erroneousSequenceStep = true;
}
#endregion -------------------------------- Check Calibration Values --------------------------------------
#region ----------------------------------- Summarize Check Results ---------------------------------------
// One or more checks failed
if (erroneousSequenceStep)
return StatusReturn.Failed;
// One or more checks generated a warning
if (warningSequenceStep)
return StatusReturn.Skipped;
// Here everything went fine
SuccessMsgDispatcher(feedbackMsg);
@@ -656,11 +689,11 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
EolProgress.SpecialRequirementId = ProductionRequirements.SpecialId;
EolProgress.SpecialRequirementVersion = ProductionRequirements.SpecialVersion;
feedbackMsg = isSpecialActive &&
ProductionRequirements.SpecialId != null &&
ProductionRequirements.SpecialVersion != null ?
Resources.StrWarningMsgSpecialRequirementActive +
$" Id.Ver: {ProductionRequirements.SpecialId}.{ProductionRequirements.SpecialVersion}" :
feedbackMsg = isSpecialActive &&
ProductionRequirements.SpecialId != null &&
ProductionRequirements.SpecialVersion != null ?
Resources.StrWarningMsgSpecialRequirementActive +
$" Id.Ver: {ProductionRequirements.SpecialId}.{ProductionRequirements.SpecialVersion}" :
Resources.StrSuccessMsgRequirementActive +
$" Id.Ver {ProductionRequirements.StandardId}.{ProductionRequirements.StandardVersion}";
@@ -855,6 +888,24 @@ 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, 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
@@ -866,7 +917,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 +925,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 +957,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 +1058,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 +1122,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 +1198,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>
@@ -1354,7 +1337,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
// Allow overwriting of calibration successfully checked to okay
if (EolProgress.ProductionStatusCalibrationChecked == null)
EolProgress.ProductionStatusCalibrationChecked = EOLStatus.OK;
// Stop logging
CalibCheckRegisterRestorer.OnProcessUpdate -= ProcessUpdate_Handler;
@@ -1366,7 +1349,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 +1411,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>
@@ -1,12 +1,13 @@
using System;
using LaaPackages.Features.Cordonel.Models;
using Newtonsoft.Json.Linq;
using SIRTCOM;
using System;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Threading;
using System.Windows.Threading;
using LaaPackages.Features.Cordonel.Models;
using Newtonsoft.Json.Linq;
using SIRTCOM;
using System.Xml.Linq;
using Xylem.Common.CommonCore.Configuration;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
@@ -15,6 +16,7 @@ using Xylem.Common.Logic.SoftwareAccessHelper;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
using Xylem.Common.Utils.Logging;
using Button = System.Windows.Controls.Button;
// ReSharper disable UnusedAutoPropertyAccessor.Local
@@ -652,6 +654,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
/// <remarks date="2023-07-11" author="Thomas Wiedebusch">
/// - Optimized based on new documentations.
/// </remarks>
/// <remarks date="2025-06-17" author="Thomas Wiedebusch">
/// - Logging path changed.
/// </remarks>
private void SendPamListenSemi(Int32 radioAddress, Int32 byteComPort, String strKey)
{
if (_dataContainer == null || _stateMachine == null)
@@ -665,9 +670,27 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EolPr
_dataContainer.RadioAdress = radioAddress;
_stateMachine.initialise((Byte)byteComPort);
var date = DateTime.Now.Date;
var loggingPath = $"C:\\GenesisLog\\{date:yyyy-MM-dd}\\ProductionUiCordonel\\{Meter.PcbId}_RadioCheck.log";
_stateMachine.StartLoggin(loggingPath);
try
{
var path = NLogHelper.GetApplicationDataPath();
if (string.IsNullOrEmpty(path))
{
path = NLogHelper.GetCurrentApplicationFolder();
}
var a = XElement.Load(path);
var baseLoggingPath = a.Elements().First(s => s.Name.LocalName == "variable").Attributes()
.First(s => s.Value == "BasePath").NextAttribute.Value;
var loggingPath =
$"{baseLoggingPath}\\{DateTime.Now:yyyy-MM-dd}\\ProductionUiCordonel\\{Meter.PcbId}_RadioCheck.log";
_stateMachine.StartLoggin(loggingPath);
}
catch (Exception)
{
// ignore
}
LogNewStatusMsg($"{Resources.StrRadioMsgSendPam}: {radioAddress.ToString()}");
@@ -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,205 @@
using LaaPackages.Features.Cordonel.Models;
using Logic.ProductionToProductMapper.Files.Fw;
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Xylem.Common.CommonCore.Configuration;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.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.Logic.ProductionOrderCore.Vako;
using Xylem.Common.Logic.SoftwareAccessHelper;
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="skipRadioCheck"></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,
Boolean skipRadioCheck, out String feedbackMsg)
{
feedbackMsg = "";
if (meter == null ||
string.IsNullOrEmpty(meter.MeterSize) ||
string.IsNullOrEmpty(meter.Region))
{
return StatusReturn.Failed;
}
// The NA region has no radio or radio check should be ignored
if (meter.Region.Equals("NA") || skipRadioCheck)
{
return StatusReturn.Skipped;
}
var pressureSensorAssembled =
meter.PressureSensorAssembled ? $", {Resources.StrLblPressureSensorAssembledTxt}" : "";
var meterInfo = $"{Resources.StrLblRadioFrequencyTxt}: {meter.RadioFrequencyMhz} MHz," +
$"{Resources.StrLblMeterSizeTxt}: {meter.MeterSize}, " +
$"{Resources.StrLblMeterLengthTxt}: {meter.MeterLength} mm" +
$"{pressureSensorAssembled}";
// 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.StrSuccessMsgRadioSignalLevels + ". " + meterInfo;
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;
}
/// <summary>
/// Get the meter length form database as single VAKO parameter
/// </summary>
/// <param name="meter"></param>
/// <returns></returns>
/// <remarks date="2025-Jun-17" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static Boolean GetMeterLengthFromDb(IGenesisMeter meter)
{
if (meter == null)
return false;
try
{
var a = LocalWebRequest.GetRequest(
$"{ServiceUrls.GenesisFinalCheckServiceUrl()}GetSingleVakoKey?ProductionOrderNumber=" +
$"{meter.OrderNumber}&KeyName=C_GEN_BAULAENGE");
var response = JsonConvert.DeserializeObject<VakoKeyValue>(a);
if (response == null || string.IsNullOrEmpty(response.Value))
{
// Meter size unknown
return false;
}
meter.MeterLength = response.Value;
return true;
}
catch (Exception)
{
// Meter size unknown
return false;
}
}
}
}
@@ -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;
@@ -23,7 +21,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
/// <summary>
/// Maximum of expected processing steps for this process to feed the GUI progress bar.
/// </summary>
private const Int32 MaxActualProgressSteps = 13;
private const Int32 MaxActualProgressSteps = 14;
/// <summary>
/// FW update package containing the ABC and the ADF.
@@ -124,6 +122,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Picki
ActualProcessProgress++;
// Get the meter length from VAKO needed for radio parameter message
if (!GenericChecks.GetMeterLengthFromDb(Meter))
{
// Meter size unknown
ErrorMsgDispatcher(Resources.StrErrorMsgMeterLengthUnknown);
return StatusReturn.Failed;
}
ActualProcessProgress++;
return StatusReturn.Okay;
}
@@ -229,6 +236,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 +554,25 @@ 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, 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
@@ -542,7 +584,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 +592,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 +613,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 +702,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 +753,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
@@ -653,7 +653,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
/// <remarks date="2025-Apr-15" author="Thomas Wiedebusch">
/// - Feed with processStateStruct.
/// </remarks>
protected void StartProcess(ProcessStateStruct processStateStruct)
private void StartProcess(ProcessStateStruct processStateStruct)
{
// deny access if uninitialized
if (Processes == null || processStateStruct.ProductionProcess == null)
@@ -259,6 +259,7 @@
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
@@ -284,8 +285,8 @@
<DependentUpon>ctlMultiModePressure.xaml</DependentUpon>
</Compile>
<Compile Include="Model\EOLHandler.cs" />
<Compile Include="ProductionProcesses\Enum\PickingProcessState.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" />
@@ -299,6 +300,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" />
@@ -312,12 +314,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" />
@@ -112,6 +112,7 @@
<Label Content="Firmware:" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="2,2,2,2" Grid.Row="0" Grid.Column="5"/>
<Label x:Name="lblLutText" Content="LUT CRC:" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="2,2,2,2" Grid.Row="1" Grid.Column="5"/>
<Label x:Name="lblMeterSizeText" Content="Meter Size:" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="2,2,2,2" Grid.Row="2" Grid.Column="5"/>
<Label x:Name="lblMeterLengthText" Content="Meter Length:" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="2,2,2,2" Grid.Row="3" Grid.Column="5"/>
<Label Content="" x:Name="lblSerialNumberValue" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="2,2,2,2" Grid.Row="0" Grid.Column="3"/>
<Label Content="" x:Name="lblOrderNumberValue" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="2,2,2,2" Grid.Row="1" Grid.Column="3"/>
@@ -120,6 +121,7 @@
<Label Content="" x:Name="lblFwValue" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="2,2,2,2" Grid.Row="0" Grid.Column="6"/>
<Label Content="" x:Name="lblLutValue" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="2,2,2,2" Grid.Row="1" Grid.Column="6"/>
<Label Content="" x:Name="lblMeterSizeValue" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="2,2,2,2" Grid.Row="2" Grid.Column="6"/>
<Label Content="" x:Name="lblMeterLengthValue" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="2,2,2,2" Grid.Row="3" Grid.Column="6"/>
<!--Pictures-->
<Border Grid.Row="0" Grid.RowSpan="4" Grid.Column="0" Margin="2,2,2,2" BorderThickness="1" BorderBrush="Black" Grid.ColumnSpan="2">
@@ -13,6 +13,7 @@ using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Threading;
using System.Xml.Linq;
using LaaPackages.Features.Cordonel.Models;
using Newtonsoft.Json;
using NLog;
@@ -44,6 +45,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel
private static readonly Version _version = _assemblyName.Version;
private readonly String _strSoftwareNameVersion;
private RegisterLDAPUser _registerForm;
private Boolean _lockGenesisInfo;
private Int32 Slot
{
@@ -59,7 +61,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel
private String _lastLoggingTextToAvoidRepetition;
private CordonelRequirements _productionRequirements;
private UserAccessWindow _userAccessWindow;
private IProcessStateDef _processStateDef;
private readonly IProcessStateDef _processStateDef;
#endregion
@@ -104,6 +106,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel
UpdateContentControl(lblRadioAddressText, Properties.Resources.StrLblRadioAddressTxt + ":");
UpdateContentControl(lblOrderNumberText, Properties.Resources.StrLblOrderNumberTxt + ":");
UpdateContentControl(lblMeterSizeText, Properties.Resources.StrLblMeterSizeTxt + ":");
UpdateContentControl(lblMeterLengthText, Properties.Resources.StrLblMeterLengthTxt + ":");
UpdateContentControl(lblSingleProcessText, Properties.Resources.StrLblSingleProcessText + ":");
UpdateContentControl(lblTotalProcessText, Properties.Resources.StrLblTotalProcessText + ":");
optionsMenu.Header = Properties.Resources.StrMenuOptionsText;
@@ -446,6 +449,11 @@ namespace Xylem.Common.Production.ProductionUiCordonel
PrintSuccessNote();
BtnStop_Click(this, null);
}
// Enable display of Genesis information on new detected
if (e.State == ProcessState.ConnectCordonel)
{
_lockGenesisInfo = false;
}
}
/// <summary>
@@ -694,15 +702,18 @@ namespace Xylem.Common.Production.ProductionUiCordonel
/// <remarks date="????" author="Roland Drabesch">
/// - Initial
/// </remarks>
/// <remarks date="2024-07-15" author="Thomas Wiedebusch">
/// <remarks date="2024-Jul-15" author="Thomas Wiedebusch">
/// - Additional info of Cordonel.
/// </remarks>
/// <remarks date="2025-Jun-18" author="Thomas Wiedebusch">
/// - Lock display of OLD Genesis after connect until the Genesis has been detected.
/// </remarks>
private void UpdateGuiWithProductInfos()
{
var genesisMeter = _processCtrl?.GetGenesisMeter();
_productionRequirements = _processCtrl?.GetProductionRequirements();
if (genesisMeter == null)
if (genesisMeter == null || _lockGenesisInfo)
return;
UpdateContentControl(lblOrderNumberValue,
@@ -719,6 +730,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel
UpdateContentControl(lblMeterSizeValue,
!string.IsNullOrEmpty(genesisMeter.MeterSize) ? genesisMeter.MeterSize : "?");
UpdateContentControl(lblMeterLengthValue,
!string.IsNullOrEmpty(genesisMeter.MeterLength) ? genesisMeter.MeterLength + " mm": "?");
UpdateContentControl(lblLutValue,
!string.IsNullOrEmpty(genesisMeter.LutCrc) ? genesisMeter.LutCrc : "?");
@@ -798,6 +812,8 @@ namespace Xylem.Common.Production.ProductionUiCordonel
_startTime = DateTimeOffset.UtcNow;
_timer?.Start();
// Avoid display of genesis info before the detect new meter has been executed
_lockGenesisInfo = true;
// remove last userControl
dpSubProcess.Children.Clear();
@@ -809,6 +825,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel
UpdateContentControl(lblRadioAddressText, "");
UpdateContentControl(lblFwValue, "?");
UpdateContentControl(lblMeterSizeValue, "?");
UpdateContentControl(lblMeterLengthValue, "?");
UpdateContentControl(lblLutValue, "?");
UpdateContentControl(lblSerialNumberValue, "?");
UpdateContentControl(lblRegionValue, "?");
@@ -835,6 +852,9 @@ namespace Xylem.Common.Production.ProductionUiCordonel
/// <remarks date="2025-Apr-10" author="Thomas Wiedebusch">
/// - Reduced printout.
/// </remarks>
/// <remarks date="2025-Jun-17" author="Thomas Wiedebusch">
/// - Base logging path taken from NLogHelper as log-path setting made in GTB.
/// </remarks>
private void CreateFile(String headerInfo, Boolean shortReport = false, Boolean printReport = false)
{
var sb = new StringBuilder();
@@ -853,50 +873,64 @@ namespace Xylem.Common.Production.ProductionUiCordonel
sb.AppendLine($"{Properties.Resources.StrLblOperatorNameText}: {Software.UserName}");
sb.AppendLine("------------------------------------------------------------------------");
//TODO THW take the log path from setup (done with GTB)
var loggingPath = $"C:\\GenesisLog\\{DateTime.Now:yyyy-MM-dd}\\ProductionUiCordonel\\";
var fileName = $"{loggingPath}{DateTime.Now.ToString("yyyyMMddHHmmss")}_ExtendedReport_{genesisMeter.PcbId}.txt";
// Copy the header to the compressed string builder
var compressedSb = new StringBuilder();
if (shortReport)
try
{
compressedSb.Append(sb);
var path = NLogHelper.GetApplicationDataPath();
if (string.IsNullOrEmpty(path))
{
path = NLogHelper.GetCurrentApplicationFolder();
}
var a = XElement.Load(path);
var baseLoggingPath = a.Elements().First(s => s.Name.LocalName == "variable").Attributes().First(s => s.Value == "BasePath").NextAttribute.Value;
var loggingPath = $"{baseLoggingPath}\\{DateTime.Now:yyyy-MM-dd}\\ProductionUiCordonel\\";
var fileName = $"{loggingPath}{DateTime.Now.ToString("yyyyMMddHHmmss")}_ExtendedReport_{genesisMeter.PcbId}.txt";
// Copy the header to the compressed string builder
var compressedSb = new StringBuilder();
if (shortReport)
{
compressedSb.Append(sb);
}
var rtbText = new TextRange(
rtbLog.Document.ContentStart,
rtbLog.Document.ContentEnd
);
sb.Append($"{rtbText.Text}");
// Write the complete report to the file
File.WriteAllText(fileName, sb.ToString());
// Reduce sb content to compress it for printout
if (shortReport)
{
fileName = $"{loggingPath}{DateTime.Now.ToString("yyyyMMddHHmmss")}" +
$"_ShortReport_{genesisMeter.PcbId}.txt";
var sr = new StringReader(rtbText.Text);
var srLine = sr.ReadLine();
while (srLine != null)
{
if (srLine.Contains(Properties.Resources.StrProcessStateError) ||
srLine.Contains(Properties.Resources.StrProcessStateDone) ||
srLine.Contains(Properties.Resources.StrProcessStateWarning) ||
srLine.Contains(Properties.Resources.StrProcessStateSkipped) ||
srLine.Contains(Properties.Resources.StrWarningMsgSpecialRequirementActive))
compressedSb.AppendLine(srLine);
srLine = sr.ReadLine();
}
// Returns the compressed file for error report printout
File.WriteAllText(fileName, compressedSb.ToString());
if (printReport)
{
Process.Start(fileName);
}
}
}
var rtbText = new TextRange(
rtbLog.Document.ContentStart,
rtbLog.Document.ContentEnd
);
sb.Append($"{rtbText.Text}");
// Write the complete report to the file
File.WriteAllText(fileName, sb.ToString());
// Reduce sb content to compress it for printout
if (shortReport)
catch (Exception e)
{
fileName = $"{loggingPath}{DateTime.Now.ToString("yyyyMMddHHmmss")}" +
$"_ShortReport_{genesisMeter.PcbId}.txt";
var sr = new StringReader(rtbText.Text);
var srLine = sr.ReadLine();
while (srLine != null)
{
if (srLine.Contains(Properties.Resources.StrProcessStateError) ||
srLine.Contains(Properties.Resources.StrProcessStateDone) ||
srLine.Contains(Properties.Resources.StrProcessStateWarning) ||
srLine.Contains(Properties.Resources.StrProcessStateSkipped) ||
srLine.Contains(Properties.Resources.StrWarningMsgSpecialRequirementActive))
compressedSb.AppendLine(srLine);
srLine = sr.ReadLine();
}
// Returns the compressed file for error report printout
File.WriteAllText(fileName, compressedSb.ToString());
if (printReport)
{
Process.Start(fileName);
}
Console.WriteLine(e.Message);
}
}
@@ -962,7 +996,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel
}
catch (Exception exception)
{
Console.WriteLine(exception);
Console.WriteLine(exception.Message);
}
}
@@ -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 {
@@ -519,6 +519,15 @@ namespace Xylem.Common.Production.ProductionUiCordonel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Cordonel length is unknown.
/// </summary>
internal static string StrErrorMsgMeterLengthUnknown {
get {
return ResourceManager.GetString("StrErrorMsgMeterLengthUnknown", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Communication port is in use or not present. The software is going to exit!.
/// </summary>
@@ -672,6 +681,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 +1068,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 +1122,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 +1149,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 +1986,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>
@@ -366,6 +366,9 @@
</data>
<data name="StrErrorMsgLifeTimeCalculation" xml:space="preserve">
<value>FEHLER: Restlaufzeitberechnung fehlgeschlagen</value>
</data>
<data name="StrErrorMsgMeterLengthUnknown" xml:space="preserve">
<value>FEHLER: Cordonel Baulänge ist unbekannt</value>
</data>
<data name="StrStateFinalTestFailed" xml:space="preserve">
<value>FEHLER: Versandfertigmachen fehlgeschlagen</value>
@@ -400,6 +403,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 +421,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 +631,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 +694,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>
@@ -364,9 +364,12 @@
<data name="StrErrorMsgRemainingLifeTime" xml:space="preserve">
<value>ERROR: Remaining lifetime too short</value>
</data>
<data name="StrErrorMsgLifeTimeCalculation" xml:space="preserve">
<data name="StrErrorMsgLifeTimeCalculation" xml:space="preserve">
<value>ERROR: Lifetime calculation failed</value>
</data>
<data name="StrErrorMsgMeterLengthUnknown" xml:space="preserve">
<value>ERROR: Cordonel length is unknown</value>
</data>
<data name="StrStateFinalTestFailed" xml:space="preserve">
<value>ERROR: Preparation for shipping failed</value>
</data>
@@ -400,6 +403,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 +421,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 +628,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 +694,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>