From ea35d1ee755c6151ec21c83b20f9821441a304ef Mon Sep 17 00:00:00 2001 From: Thomas Wiedebusch Date: Thu, 12 Feb 2026 15:39:57 +0100 Subject: [PATCH 1/7] ProductionUiCordonel: - reboot counter check skipped on retry if passed at first run --- .../Genesis/GenesisCore/GenesisMeter.cs | 10 +++++++ .../Genesis/GenesisCore/IGenesisMeter.cs | 11 ++++++++ .../GenericProcesses/ConnectCordonel.cs | 27 ++++++++++++------- .../ProductionProcesses/ProcessController.cs | 7 ++--- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisCore/GenesisMeter.cs b/Common/Hardware/WaterMeter/Genesis/GenesisCore/GenesisMeter.cs index 85f7f574..ac6708ce 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisCore/GenesisMeter.cs +++ b/Common/Hardware/WaterMeter/Genesis/GenesisCore/GenesisMeter.cs @@ -77,6 +77,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore public GenesisMeter() { SetLogger(); + CheckRebootCtr = true; } //initially do not signal event @@ -100,6 +101,8 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore { Logger = NLogHelper.CreateOrGetMultiLogger($"Slot:{slot}", "Slot", "Meter", "MeterBase", "MeterBase"); SetupGenesisMeter(slot, requestPort, streamingPort, ignoreCorruptedData, password); + CheckRebootCtr = true; + } #endregion @@ -195,6 +198,13 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore get; protected set; } + /// + public Boolean CheckRebootCtr + { + get; + set; + } + /// public List MeterAppListVersion { diff --git a/Common/Hardware/WaterMeter/Genesis/GenesisCore/IGenesisMeter.cs b/Common/Hardware/WaterMeter/Genesis/GenesisCore/IGenesisMeter.cs index 310c6e4b..c77e6511 100644 --- a/Common/Hardware/WaterMeter/Genesis/GenesisCore/IGenesisMeter.cs +++ b/Common/Hardware/WaterMeter/Genesis/GenesisCore/IGenesisMeter.cs @@ -25,6 +25,17 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore get; } + /// + /// A new meter has to be checked for the reboot counter. On retries the reboot counter + /// may have increased on unsuccessfully after-reboot procedure. This would force failing + /// the 'ConnectCordonel'. + /// + Boolean CheckRebootCtr + { + get; + set; + } + /// /// Interface information /// diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/GenericProcesses/ConnectCordonel.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/GenericProcesses/ConnectCordonel.cs index 5d4045b9..cdad399e 100644 --- a/Common/Production/ProductionUiCordonel/ProductionProcesses/GenericProcesses/ConnectCordonel.cs +++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/GenericProcesses/ConnectCordonel.cs @@ -73,18 +73,27 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener // Read reboot count and compare with hardcoded maximum value var rebootCount = RegisterConverter.ByteArrayToValue(Meter.ReadRegister("CUSTOMER_RebootCount")); - if (rebootCount > MeterResetPsu.MAX_REBOOTS_FOR_HEALTHY_HW) + + + if (Meter.CheckRebootCtr) { - ErrorMsgDispatcher(Resources.StrErrorMsgTooManyReboots + - $" - {Resources.StrProcessStateAllowed}: {MeterResetPsu.MAX_REBOOTS_FOR_HEALTHY_HW}" + - $" - {Resources.StrProcessStateDetected}: {rebootCount}"); - return StatusReturn.Failed; + if (rebootCount > MeterResetPsu.MAX_REBOOTS_FOR_HEALTHY_HW) + { + ErrorMsgDispatcher(Resources.StrErrorMsgTooManyReboots + + $" - {Resources.StrProcessStateAllowed}: {MeterResetPsu.MAX_REBOOTS_FOR_HEALTHY_HW}" + + $" - {Resources.StrProcessStateDetected}: {rebootCount}"); + return StatusReturn.Failed; + } + + // Skip reboot counter check on reties as it passed here the initial phase as a retry may follow an + // unsuccessfully hardware-reboot-procedure + Meter.CheckRebootCtr = false; + + SuccessMsgDispatcher(Resources.StrSuccessMsgRebootCounts + + $" - {Resources.StrProcessStateAllowed}: {MeterResetPsu.MAX_REBOOTS_FOR_HEALTHY_HW}" + + $" - {Resources.StrProcessStateDetected}: {rebootCount}"); } - SuccessMsgDispatcher(Resources.StrSuccessMsgRebootCounts + - $" - {Resources.StrProcessStateAllowed}: {MeterResetPsu.MAX_REBOOTS_FOR_HEALTHY_HW}" + - $" - {Resources.StrProcessStateDetected}: {rebootCount}"); - // If interface is outdated if (!Meter.InterfaceSupportsFwVersion) { diff --git a/Common/Production/ProductionUiCordonel/ProductionProcesses/ProcessController.cs b/Common/Production/ProductionUiCordonel/ProductionProcesses/ProcessController.cs index 001a8ed4..32de106b 100644 --- a/Common/Production/ProductionUiCordonel/ProductionProcesses/ProcessController.cs +++ b/Common/Production/ProductionUiCordonel/ProductionProcesses/ProcessController.cs @@ -38,6 +38,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses private ProcessState _lockedStateMachineState; // sender of state change event to evaluate in e.g. error process private IProductionProcess _stateChangeRequestProcess; + // List of production processes public List Processes { @@ -147,7 +148,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses { // Timer for cyclic call of state machine _processCtrlTimer = new Timer(TmrProcessCtrlCycle_Elapsed, null, ProcessTimerCycleMs, ProcessTimerCycleMs); - + // Delay timer between final-param-exec finished and start of 'StoreConfiguration' _paramExeFinishedToStoreConfigDelayTimer = new DispatcherTimer(); _paramExeFinishedToStoreConfigDelayTimer.Tick += TmrParamExeFinishedToStoreConfigCycle_Tick; @@ -281,7 +282,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses break; } // Delay timer init and start if parametrization exec finished but only for 'EMEA' region - if (pp != null && + if (pp != null && pp.ProcessName != null && pp.Meter != null && pp.Meter.Region.Equals("EMEA") && @@ -834,7 +835,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses _stateMachineState = (ProcessState)processStateStruct.NextStateOnSuccess; return; } - + // Process is waiting for execution after init, this overrides the eventually abort state processStateStruct.ProductionProcess.InitProcess(); From 26dc9dbe45ca5f1ff23d1f6e9d04a120b098520f Mon Sep 17 00:00:00 2001 From: Thomas Wiedebusch Date: Thu, 12 Feb 2026 16:00:00 +0100 Subject: [PATCH 2/7] SharedAssembly to 2.8.18 --- Common/.shared/SharedAssemblyInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/.shared/SharedAssemblyInfo.cs b/Common/.shared/SharedAssemblyInfo.cs index 480f78e9..ccd86aed 100644 --- a/Common/.shared/SharedAssemblyInfo.cs +++ b/Common/.shared/SharedAssemblyInfo.cs @@ -13,4 +13,4 @@ using System.Reflection; // //[assembly: AssemblyVersion("1.2.*.0")] -[assembly: AssemblyVersion("2.8.17.*")] +[assembly: AssemblyVersion("2.8.18.*")] From 0272761db8d57d208d64054ffdaec97afc665a9f Mon Sep 17 00:00:00 2001 From: Thomas Wiedebusch Date: Fri, 13 Feb 2026 10:42:33 +0100 Subject: [PATCH 3/7] FM2014: - measurement interpretation --- Common/CommonCore/Consts/StatusReturn.cs | 7 ++- .../FM2014/FM2014Core/Consts/FM2014CmdDef.cs | 4 +- .../FM2014/FM2014Core/FM2014.cs | 47 ++++++++++++++---- .../Fm2014sTest/FM2014TestBenchWindow.xaml.cs | 48 +++++++++---------- 4 files changed, 68 insertions(+), 38 deletions(-) diff --git a/Common/CommonCore/Consts/StatusReturn.cs b/Common/CommonCore/Consts/StatusReturn.cs index 04722a29..702d4c77 100644 --- a/Common/CommonCore/Consts/StatusReturn.cs +++ b/Common/CommonCore/Consts/StatusReturn.cs @@ -28,6 +28,11 @@ /// /// inspection needed as warning returns /// - Warning + Warning, + + /// + /// the measurement is out of range + /// + MeasurementOutOfRange } } \ No newline at end of file diff --git a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs index e74d910a..d12bda30 100644 --- a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs +++ b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs @@ -507,9 +507,9 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co private const String UNICODE_DC2 = "\u0013"; /// - /// Complete command table + /// Command table for communication with FM2014 combines the name with the command code and additional info. /// - public static List CmdTbl = new List + public static readonly List CmdTbl = new List { // FM2014 control and information new Cmd(CmdName.CMD_CONNECT, "", CmdType.INDIVIDUAL, Resources.StrCmdMsgConnect), diff --git a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs index 0e9be667..0021f414 100644 --- a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs +++ b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs @@ -299,12 +299,12 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// REF pulse counter is switched active /// - public Boolean RefPulseCtrOn { set; get; } + private Boolean RefPulseCtrOn { set; get; } /// /// DUT pulse counter is switched active /// - public Boolean DutPulseCtrOn { set; get; } + private Boolean DutPulseCtrOn { set; get; } /// /// Serial number of the FM2014 @@ -580,7 +580,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core get => _currentOutputAttenuation; private set { - if (value < CURRENT_OUTPUT_ATTENUATION_MIN || + if (value < CURRENT_OUTPUT_ATTENUATION_MIN || value > CURRENT_OUTPUT_ATTENUATION_MAX) return; @@ -1054,7 +1054,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// - Initial. /// - public static Boolean RegulationMeasurement(UInt32 refPulses_per_cm, UInt16 dutPulses_per_cm, + public static Boolean RegulationMeasurement(UInt32 refPulses_per_cm, UInt16 dutPulses_per_cm, UInt16 doublePulseDeadtime_ms, Byte currentOutputAttenuation) { // If the cyclic task has already been started everything is fine @@ -1140,6 +1140,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core // Read out the damped/undamped tolerance and convert the raw value (0, +/-1..255 digits) to the // tolerance in percent depending on the setup to 3 % (3.3) of 5 % (5.5) String responseStr; + StatusReturn statusReturn; cmdName = fm2014.UseDampedTolerance ? CmdName.CMD_GET_DTLC : CmdName.CMD_GET_UDTLC; var info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) @@ -1152,11 +1153,21 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core if (int.TryParse(cleanedStr, NumberStyles.HexNumber, new CultureInfo("en"), out var measTolerance)) { + fm2014.DutToRefToleranceMeasured_percent = + measTolerance * fm2014.ToleranceRawToPercentScale * signMultiplier; var response = new CmdResponse(cmdName, info, - doubleValue: measTolerance * fm2014.ToleranceRawToPercentScale * signMultiplier, + doubleValue: fm2014.DutToRefToleranceMeasured_percent, unit: Units.GetInfo(Units.Name.PERCENT)); + + // Check the threshold for OOR + if (Math.Abs(fm2014.DutToRefToleranceMeasured_percent) > fm2014.Tolerance_percent) + statusReturn = StatusReturn.MeasurementOutOfRange; + else statusReturn = StatusReturn.Okay; + PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + new ProcessExecEventArgs("", + actualProcessMessage: measurementInfo, + statusReturn: statusReturn, specificInfoObj: response)); } } @@ -1254,11 +1265,17 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var refFrequency)) { + // Check the threshold for OOR + if (refFrequency < REF_FREQU_MIN || refFrequency > REF_FREQU_MAX) + statusReturn = StatusReturn.MeasurementOutOfRange; + else statusReturn = StatusReturn.Okay; + // Publish the frequency in [Hz] var response = new CmdResponse(cmdName, info, refFrequency, unit: Units.GetInfo(Units.Name.FREQUENCY_Hz)); PublishResponse(fm2014, new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + statusReturn: statusReturn, specificInfoObj: response)); // Convert to m³/h (3600 s/h) and save the value @@ -1269,7 +1286,9 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core response = new CmdResponse(cmdName, info, doubleValue: fm2014.RefFlowRate_cm_per_h, unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h)); PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + new ProcessExecEventArgs("", + actualProcessMessage: measurementInfo, + statusReturn: statusReturn, specificInfoObj: response)); } } @@ -1438,7 +1457,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { String info; String responseStr; - + StatusReturn statusReturn; // Read the remaining REF pulses if required if (firstActiveFm2014.RefPulsesRequired != 0) { @@ -1491,7 +1510,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core if (fm2014.RefPulsesRemaining == 0 && fm2014.DutPulsesRemaining == 0) { var retVal = true; - + // Read the REF timer ticks for the counted pulses if (firstActiveFm2014.RefPulsesRequired != 0) { @@ -1562,11 +1581,21 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { fm2014.DutToRefToleranceMeasured_percent = (1.0 - fm2014.DutTimeMeasured_s / fm2014.RefTimeMeasured_s) * 100.0; + + // Check the threshold for OOR + if (fm2014.DutToRefToleranceMeasured_percent > + fm2014.DutToRefCalibrationToleranceMax_percent || + fm2014.DutToRefToleranceMeasured_percent < + fm2014.DutToRefCalibrationToleranceMin_percent) + statusReturn = StatusReturn.MeasurementOutOfRange; + else statusReturn = StatusReturn.Okay; + var response = new CmdResponse(cmdName, info, doubleValue: fm2014.DutToRefToleranceMeasured_percent, unit: Units.GetInfo(Units.Name.PERCENT)); PublishResponse(fm2014, new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + statusReturn: statusReturn, specificInfoObj: response)); } } diff --git a/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs b/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs index 5c38ef4a..94b7c718 100644 --- a/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs +++ b/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs @@ -1076,7 +1076,7 @@ namespace Sensus.Ui.FM2014TestBench /// /// Feedback from FM2014being parsed to GUI /// - /// + /// /// - Initial. /// private void DataReceived_Handler(Object sender, ProcessExecEventArgs e) @@ -1112,6 +1112,8 @@ namespace Sensus.Ui.FM2014TestBench // Data dispatcher var resp = (CmdResponse)e.SpecificInfoObj; String valueStr; + // Used as marker for error, null means measurement is in range or status or health is good + Brush statusColor = null; if (e.StatusReturn == StatusReturn.Failed) { @@ -1124,42 +1126,41 @@ namespace Sensus.Ui.FM2014TestBench } else if (resp.IntValue != null) { + if (e.StatusReturn == StatusReturn.MeasurementOutOfRange) + statusColor = ColorProcessFailed; LogText($"{resp.AnswerStr}: {resp.IntValue:D} {resp.Unit}"); switch (resp.CmdName) { case CmdName.CMD_REF_GET_PLS_RMN: - ucFm2014.SetValue(CAL_LBL_IDX_REF_RMN_PLS, $"{resp.IntValue:D}"); + ucFm2014.SetValue(CAL_LBL_IDX_REF_RMN_PLS, $"{resp.IntValue:D}", statusColor); break; case CmdName.CMD_DUT_GET_PLS_RMN: - ucFm2014.SetValue(CAL_LBL_IDX_DUT_RMN_PLS, $"{resp.IntValue:D}"); + ucFm2014.SetValue(CAL_LBL_IDX_DUT_RMN_PLS, $"{resp.IntValue:D}", statusColor); break; case CmdName.CMD_REF_GET_PLS_CTR: case CmdName.CMD_REF_GET_PLS_CTR_BU: if (firstFm2014 != null && firstFm2014.Address == fm2014.Address) - UpdateTextBox(tbxRefPulsesMeasured, $"{resp.IntValue:D}"); - ucFm2014.SetValue(CTR_LBL_IDX_REF_PLS, $"{resp.IntValue:D}"); + UpdateTextBox(tbxRefPulsesMeasured, $"{resp.IntValue:D}", statusColor); + ucFm2014.SetValue(CTR_LBL_IDX_REF_PLS, $"{resp.IntValue:D}", statusColor); break; case CmdName.CMD_DUT_GET_PLS_CTR: case CmdName.CMD_DUT_GET_PLS_CTR_BU: if (firstFm2014 != null && firstFm2014.Address == fm2014.Address) - UpdateTextBox(tbxDutPulsesMeasured, $"{resp.IntValue:D}"); - ucFm2014.SetValue(CTR_LBL_IDX_DUT_PLS, $"{resp.IntValue:D}"); + UpdateTextBox(tbxDutPulsesMeasured, $"{resp.IntValue:D}", statusColor); + ucFm2014.SetValue(CTR_LBL_IDX_DUT_PLS, $"{resp.IntValue:D}", statusColor); break; case CmdName.CMD_REF_LPP_SCALE: - UpdateTextBox(tbxRefPulsesPerCm, $"{resp.IntValue:D}"); + UpdateTextBox(tbxRefPulsesPerCm, $"{resp.IntValue:D}", statusColor); break; case CmdName.CMD_DUT_LPP_SCALE: - UpdateTextBox(tbxDutPulsesPerCm, $"{resp.IntValue:D}"); + UpdateTextBox(tbxDutPulsesPerCm, $"{resp.IntValue:D}", statusColor); break; case CmdName.CMD_MEAS_SET_ATTN: Dispatcher.Invoke(() => cbxDisplayAttenuation.SelectedItem = $"{resp.IntValue}"); break; case CmdName.CMD_GET_REF_FREQU: valueStr = $"{resp.IntValue:D}"; - if (resp.IntValue < FM2014.REF_FREQU_MIN || resp.IntValue > FM2014.REF_FREQU_MAX) - ucFm2014.SetValue(REG_LBL_IDX_REF_FREQU, valueStr, ColorProcessFailed); - else - ucFm2014.SetValue(REG_LBL_IDX_REF_FREQU, valueStr); + ucFm2014.SetValue(REG_LBL_IDX_REF_FREQU, valueStr, statusColor); break; case CmdName.CMD_RST_MEAS: // Immediately lock all buttons and input fields until reset is finished @@ -1172,35 +1173,30 @@ namespace Sensus.Ui.FM2014TestBench } else if (resp.DoubleValue != null) { + if (e.StatusReturn == StatusReturn.MeasurementOutOfRange) + statusColor = ColorProcessFailed; LogText($"{resp.AnswerStr}: {resp.DoubleValue:F2} {resp.Unit}"); switch (resp.CmdName) { case CmdName.CMD_REF_GET_TMR: valueStr = $"{resp.DoubleValue:F3}"; - ucFm2014.SetValue(CAL_LBL_IDX_REF_MEAS_TMR, valueStr); + ucFm2014.SetValue(CAL_LBL_IDX_REF_MEAS_TMR, valueStr, statusColor); break; case CmdName.CMD_DUT_GET_TMR: valueStr = $"{resp.DoubleValue:F3}"; - ucFm2014.SetValue(CAL_LBL_IDX_DUT_MEAS_TMR, valueStr); + ucFm2014.SetValue(CAL_LBL_IDX_DUT_MEAS_TMR, valueStr, statusColor); break; case CmdName.CMD_CAL_DUT_TO_REF_TOL: valueStr = $"{resp.DoubleValue:F3}"; - if ((Double)resp.DoubleValue <= fm2014.DutToRefCalibrationToleranceMax_percent && - (Double)resp.DoubleValue >= fm2014.DutToRefCalibrationToleranceMin_percent) - ucFm2014.SetValue(CAL_LBL_IDX_DUT_TO_REF_TOL, valueStr, ColorSuccess); - else - ucFm2014.SetValue(CAL_LBL_IDX_DUT_TO_REF_TOL, valueStr, ColorProcessFailed); + ucFm2014.SetValue(CAL_LBL_IDX_DUT_TO_REF_TOL, valueStr, statusColor); break; case CmdName.CMD_GET_UDTLC: case CmdName.CMD_GET_DTLC: valueStr = $"{resp.DoubleValue:F2}"; - if (Math.Abs((Double)resp.DoubleValue) > fm2014.Tolerance_percent) - ucFm2014.SetValue(REG_LBL_IDX_DUT_TO_REF_TOL, valueStr, ColorProcessFailed); - else - ucFm2014.SetValue(REG_LBL_IDX_DUT_TO_REF_TOL, valueStr); + ucFm2014.SetValue(REG_LBL_IDX_DUT_TO_REF_TOL, valueStr, statusColor); break; case CmdName.CMD_REF_SET_SCALE: - UpdateTextBox(tbxScaleRefToDut, $"{resp.DoubleValue:F4}"); + UpdateTextBox(tbxScaleRefToDut, $"{resp.DoubleValue:F4}", statusColor); break; // // DEBUG // case FM2014CmdDef.CmdName.CMD_GET_REF_PERIOD: @@ -1217,7 +1213,7 @@ namespace Sensus.Ui.FM2014TestBench // break; case CmdName.CMD_CAL_FLOW_REF_FREQU: valueStr = $"{resp.DoubleValue:F3}"; - ucFm2014.SetValue(REG_LBL_IDX_FLOW_RATE, valueStr); + ucFm2014.SetValue(REG_LBL_IDX_FLOW_RATE, valueStr, statusColor); //UpdateTextBox(tbxRefFlowRateFromRefFrequencyCmPerH, valueStr); // TODO THW Check if the actual flow shall be taken based on 'REF Frequency' //UpdateTextBox(tbxActualFlowRateCmPerHour, valueStr); From 49c7cc5cc62890d2cb87bb9b15f88186392db313 Mon Sep 17 00:00:00 2001 From: Thomas Wiedebusch Date: Fri, 13 Feb 2026 16:56:42 +0100 Subject: [PATCH 4/7] FM2014: - time measurement automatic stop --- .../FM2014/FM2014Core/FM2014.cs | 182 ++++++++++-------- .../Fm2014sTest/FM2014TestBenchWindow.xaml.cs | 36 ++-- 2 files changed, 119 insertions(+), 99 deletions(-) diff --git a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs index 0021f414..9fead8d5 100644 --- a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs +++ b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs @@ -77,7 +77,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// The mantissa for the scale value has to be in the range from 1000 to 9999. /// - private const UInt16 MANTISSA_SCALE_MIN = 1000; + private const UInt16 REF_TO_DUT_SCALE_MANTISSA_MIN = 1000; /// /// REF to DUT scale conversion factor to meaningful human interpretable value @@ -87,29 +87,29 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// Default tolerance if not properly setup. /// - public const Int32 STANDARD_NOMINAL_TOLERANCE_INPUT_percent = 3; + public const Int32 CURRENT_OUTPUT_TOLERANCE_STANDARD_NOMINAL_INT_percent = 3; /// /// Default tolerance if not properly setup. /// - public const Int32 EXTENDED_NOMINAL_TOLERANCE_INPUT_percent = 5; + public const Int32 CURRENT_OUTPUT_TOLERANCE_EXTENDED_VALUE_INT_percent = 5; /// /// Standard tolerance (nominal 3 %) multiplicator for calculation of value based on feedback from FM2014 /// (+/- 255 digits). /// - private const Double STANDARD_TOLERANCE_VALUE_percent = 3.3; + private const Double CURRENT_OUTPUT_TOLERANCE_STANDARD_VALUE_DOUBLE_percent = 3.3; /// /// Extended tolerance (nominal 5 %) multiplicator for calculation of value based on feedback from FM2014 /// (+/- 255 digits). /// - private const Double EXTENDED_TOLERANCE_VALUE_percent = 5.5; + private const Double CURRENT_OUTPUT_TOLERANCE_EXTENDED_VALUE_DOUBLE_percent = 5.5; /// /// Resolution of tolerance raw value feedback from FM2014 (+/- 255 digits). /// - private const Double RESOLUTION_TOLERANCE_RAW_VALUE = 255; + private const Double CURRENT_OUTPUT_TOLERANCE_RESOLUTION_PLUS_MINUS_BYTE = 255; /// /// Minimal value for attenuation. @@ -159,12 +159,12 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// FM2014 minimal pulses for REF and DUT time measurement. /// - public const UInt16 TIME_MEASUREMENT_INPUT_MIN_pulses = 1; + public const UInt16 PULSES_TIME_MEASUREMENT_INPUT_MIN = 1; /// /// FM2014 maximal pulses for REF and DUT time measurement. /// - public const UInt16 TIME_MEASUREMENT_INPUT_MAX_pulses = 0xFFFF; + public const UInt16 PULSES_TIME_MEASUREMENT_INPUT_MAX = 0xFFFF; /// /// Minimal baudrate for serial communication. @@ -247,7 +247,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// Progress of actual process converted to percent /// - private static Double ActualProcessProgress_percent { get; set; } + private static Double? ActualProcessProgress_percent { get; set; } private static Int32 _actualProcessProgress; /// @@ -343,35 +343,35 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// Private tolerance percentage /// - private Int32 _tolerance_percent = STANDARD_NOMINAL_TOLERANCE_INPUT_percent; + private Int32 _currentOutputToleranceNominalInt_percent = CURRENT_OUTPUT_TOLERANCE_STANDARD_NOMINAL_INT_percent; /// /// Actual tolerance scale to convert raw value from FM2014 to percent /// - private Double ToleranceRawToPercentScale { set; get; } = - STANDARD_TOLERANCE_VALUE_percent / RESOLUTION_TOLERANCE_RAW_VALUE; + private Double CurrentOutputToleranceNominalToRealScale { set; get; } = + CURRENT_OUTPUT_TOLERANCE_STANDARD_VALUE_DOUBLE_percent / CURRENT_OUTPUT_TOLERANCE_RESOLUTION_PLUS_MINUS_BYTE; /// - /// Tolerance output of current loop in percent: + /// Nominal tolerance output of current loop in percent: /// - hardware dependent 3% or 5% /// - public Int32 Tolerance_percent + public Int32 CurrentOutputToleranceNominalInt_percent { - get => _tolerance_percent; + get => _currentOutputToleranceNominalInt_percent; set { // Limit input to discrete nominal setting of 3 or 5 %, // Internally those values are going to be scaled for higher resolution to 3.3 or 5.5 Double percentage; - if (value == STANDARD_NOMINAL_TOLERANCE_INPUT_percent) - percentage = STANDARD_TOLERANCE_VALUE_percent; - else if (value == EXTENDED_NOMINAL_TOLERANCE_INPUT_percent) - percentage = EXTENDED_TOLERANCE_VALUE_percent; + if (value == CURRENT_OUTPUT_TOLERANCE_STANDARD_NOMINAL_INT_percent) + percentage = CURRENT_OUTPUT_TOLERANCE_STANDARD_VALUE_DOUBLE_percent; + else if (value == CURRENT_OUTPUT_TOLERANCE_EXTENDED_VALUE_INT_percent) + percentage = CURRENT_OUTPUT_TOLERANCE_EXTENDED_VALUE_DOUBLE_percent; else - percentage = STANDARD_TOLERANCE_VALUE_percent; + percentage = CURRENT_OUTPUT_TOLERANCE_STANDARD_VALUE_DOUBLE_percent; - _tolerance_percent = value; - ToleranceRawToPercentScale = percentage / RESOLUTION_TOLERANCE_RAW_VALUE; + _currentOutputToleranceNominalInt_percent = value; + CurrentOutputToleranceNominalToRealScale = percentage / CURRENT_OUTPUT_TOLERANCE_RESOLUTION_PLUS_MINUS_BYTE; } } @@ -390,7 +390,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core private set { // Check limits and equality - if (value < TIME_MEASUREMENT_INPUT_MIN_pulses || + if (value < PULSES_TIME_MEASUREMENT_INPUT_MIN || value == _refPulsesRequired) return; @@ -408,7 +408,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core private set { // Check limits and equality - if (value < TIME_MEASUREMENT_INPUT_MIN_pulses || + if (value < PULSES_TIME_MEASUREMENT_INPUT_MIN || value == _dutPulsesRequired) return; @@ -563,7 +563,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core number *= REF_TO_DUT_SCALE_CONVERSION; mantissa = (UInt16)(number + 0.5); exponent--; - } while (mantissa < MANTISSA_SCALE_MIN); + } while (mantissa < REF_TO_DUT_SCALE_MANTISSA_MIN); // The pre-generated string will be used to send it to the FM2014 directly RefToDutScaleStr = $"{mantissa}{GetCmdStr(CmdName.CMD_REF_SET_SCALE)}{exponent}"; @@ -688,7 +688,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// Measured tolerance during calibration /// - public Double DutToRefToleranceMeasured_percent { get; private set; } + public Double? DutToRefToleranceMeasured_percent { get; private set; } #endregion ---------------------------------------- object properties ----------------------------------------- @@ -777,7 +777,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// Get the first FM2014 which is connected and logged in for common service routines /// using a broadcast command /// - /// + /// null if no device is connected and logged in /// /// - Initial. /// @@ -1150,17 +1150,18 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core // The response contains a sign! var signMultiplier = responseStr.Contains("+") ? 1.0f : -1.0f; var cleanedStr = responseStr.Replace("+", "").Replace("-", ""); - if (int.TryParse(cleanedStr, NumberStyles.HexNumber, new CultureInfo("en"), - out var measTolerance)) + if (int.TryParse(cleanedStr, NumberStyles.HexNumber, + new CultureInfo("en"), out var measTolerance)) { fm2014.DutToRefToleranceMeasured_percent = - measTolerance * fm2014.ToleranceRawToPercentScale * signMultiplier; + measTolerance * fm2014.CurrentOutputToleranceNominalToRealScale * signMultiplier; var response = new CmdResponse(cmdName, info, doubleValue: fm2014.DutToRefToleranceMeasured_percent, unit: Units.GetInfo(Units.Name.PERCENT)); // Check the threshold for OOR - if (Math.Abs(fm2014.DutToRefToleranceMeasured_percent) > fm2014.Tolerance_percent) + if (Math.Abs((Double)fm2014.DutToRefToleranceMeasured_percent) > + fm2014.CurrentOutputToleranceNominalInt_percent) statusReturn = StatusReturn.MeasurementOutOfRange; else statusReturn = StatusReturn.Okay; @@ -1338,7 +1339,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// 0 if double impulse deadtime detection is off /// /// - /// + /// /// - Initial. /// public static Boolean PulseTimeMeasurement(UInt16? refPulsesRequired = null, UInt16? dutPulsesRequired = null, @@ -1359,8 +1360,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core // Check if regulation can be done if ((refPulsesRequired == null && dutPulsesRequired == null) || - refPulsesRequired < TIME_MEASUREMENT_INPUT_MIN_pulses || - dutPulsesRequired < TIME_MEASUREMENT_INPUT_MIN_pulses || + refPulsesRequired < PULSES_TIME_MEASUREMENT_INPUT_MIN || + dutPulsesRequired < PULSES_TIME_MEASUREMENT_INPUT_MIN || doublePulseDeadtime_ms > DOUBLE_PULSE_DEADTIME_MAX_ms) { return false; @@ -1458,8 +1459,26 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core String info; String responseStr; StatusReturn statusReturn; + + // Read the device status to check for timeout between pulses + cmdName = CmdName.CMD_GET_STATUS; + info = GetCmdInfo(cmdName); + if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) + { + if (Read(cmdName, fm2014, out responseStr) && + int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), + out var status)) + { + info += $": {responseStr}"; + var response = new CmdResponse(cmdName, info); + PublishResponse(fm2014, new ProcessExecEventArgs("", + actualProcessPercent: ActualProcessProgress_percent, + actualProcessMessage: measurementInfo, specificInfoObj: response)); + } + } + // Read the remaining REF pulses if required - if (firstActiveFm2014.RefPulsesRequired != 0) + if (fm2014.RefPulsesRequired != 0) { // Feed the progress counter ActualProcessProgress = firstActiveFm2014.RefPulsesRequired - @@ -1473,16 +1492,30 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core out var pulses)) { fm2014.RefPulsesRemaining = pulses; + + // Check if remaining pulses are going to count down based on required pulses + if (RegisteredFm2014s.Any(fm => fm.RefPulsesRemaining * 1.1 < fm.RefPulsesRequired) && + fm2014.RefPulsesRemaining == fm2014.RefPulsesRequired) + { + statusReturn = StatusReturn.MeasurementOutOfRange; + fm2014.ErrorIsDetected = true; + } + else + { + statusReturn = StatusReturn.Okay; + } + var response = new CmdResponse(cmdName, info, pulses); PublishResponse(fm2014, new ProcessExecEventArgs("", actualProcessPercent: ActualProcessProgress_percent, + statusReturn: statusReturn, actualProcessMessage: measurementInfo, specificInfoObj: response)); } } } // Read the remaining DUT pulses if required - if (firstActiveFm2014.DutPulsesRequired != 0) + if (fm2014.DutPulsesRequired != 0) { // Feed the progress counter but only if not done for REF pulses if (firstActiveFm2014.RefPulsesRequired == 0) @@ -1497,9 +1530,23 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core out var pulses)) { fm2014.DutPulsesRemaining = pulses; + + // Check if remaining pulses are going to count down based on required pulses + if (RegisteredFm2014s.Any(fm => fm.DutPulsesRemaining * 1.1 < fm.DutPulsesRequired) && + fm2014.DutPulsesRemaining == fm2014.DutPulsesRequired) + { + statusReturn = StatusReturn.MeasurementOutOfRange; + fm2014.ErrorIsDetected = true; + } + else + { + statusReturn = StatusReturn.Okay; + } + var response = new CmdResponse(cmdName, info, pulses); PublishResponse(fm2014, new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + statusReturn: statusReturn, specificInfoObj: response)); } } @@ -1512,7 +1559,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var retVal = true; // Read the REF timer ticks for the counted pulses - if (firstActiveFm2014.RefPulsesRequired != 0) + if (fm2014.RefPulsesRequired != 0) { cmdName = CmdName.CMD_REF_GET_TMR; info = GetCmdInfo(cmdName); @@ -1542,7 +1589,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } // Read the DUT timer ticks for the counted pulses - if (firstActiveFm2014.DutPulsesRequired != 0) + if (fm2014.DutPulsesRequired != 0) { cmdName = CmdName.CMD_DUT_GET_TMR; info = GetCmdInfo(cmdName); @@ -1572,7 +1619,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } // Calculate the DUT to REF tolerance if both timer had been active - if (firstActiveFm2014.RefPulsesRequired != 0 && firstActiveFm2014.DutPulsesRequired != 0) + if (fm2014.RefPulsesRequired != 0 && fm2014.DutPulsesRequired != 0) { cmdName = CmdName.CMD_CAL_DUT_TO_REF_TOL; info = GetCmdInfo(cmdName); @@ -1614,9 +1661,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core // Check all remaining DUT and REF pulses are counted on all connected devices and finalize process by // reading out all results - //TODO THW take timeout for unresponsive device into account and if REF and/or DUT - if (RegisteredFm2014s.All(fm2014 => fm2014.RefPulsesRemaining == 0 && - fm2014.DutPulsesRemaining == 0)) + if (RegisteredFm2014s.All(fm2014 => fm2014.MeasurementIsReady || fm2014.ErrorIsDetected || + !fm2014.IsLoggedOn)) { ResetHardwareAllDevices(); // Exit this task @@ -1696,12 +1742,9 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// /// - /// + /// /// - Initial. /// - /// - /// - Support for multiple FM2014s. - /// private static Boolean Write(CmdName cmdName, FM2014 fm2014, Object dataObj = null, Boolean isHexFormat = false) { if (fm2014 == null) @@ -1795,12 +1838,9 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// /// - /// + /// /// - Initial. /// - /// - /// - Support for multiple FM2014s. - /// private static Boolean InitiateBroadcastComm(FM2014 fm2014) { if (fm2014 == null) @@ -1858,12 +1898,9 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// /// - /// + /// /// - Initial. /// - /// - /// - Support for multiple FM2014s. - /// private static Boolean InitiateIndividualComm(FM2014 fm2014) { if (fm2014 == null) @@ -1951,15 +1988,9 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// Transfer all standalone settings to FM2014 RAM and safe those to nonvolatile EEPROM /// /// - /// + /// /// - Initial. /// - /// - /// - REF pulse rate can be 0 as 'invalid marker'. - /// - /// - /// - Support for multiple FM2014s. - /// private Boolean SaveStandAloneMeasurement() { // If the cyclic task has already been started everything is fine @@ -2014,19 +2045,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// Handle the standalone measurement which will be executed after BOOT of the FM2014: /// - Read the setup stored to nonvolatile memory (EEPROM), /// - Read the actual setup kept in RAM. - /// - /// - /// - /// - /// - Initial. - /// - /// /// - 'Ref_pulse_per_cm' will be calculated by 'Ref_pulses_per_cm = Dut_pulses_per_cm * RefToDutScale_norm' /// to avoid an overflow as the REF pulses per volume can store max 9999 pulses. As the Dut_pulse_per_cm /// is for the smallest meter 1000 Impulses/m³ - /// - /// - /// - Support for multiple FM2014s. + /// + /// + /// + /// + /// - Initial. /// private Boolean ReadStandAloneMeasurement(CmdName cmdName) { @@ -2140,16 +2166,9 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// Login to individual address. /// /// - /// + /// /// - Initial. /// - /// - /// - Support for multiple FM2014s. - /// - /// - /// - Login with , - /// - Other communications moved to - /// public Boolean Login() { // If the cyclic task has already been started the login isn't allowed anymore @@ -2186,8 +2205,9 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn)) { - fm2014.RefTimerTicksMeasured = 0; - fm2014.DutTimerTicksMeasured = 0; + fm2014.RefTimeMeasured_s = 0; + fm2014.DutTimeMeasured_s = 0; + fm2014.DutToRefToleranceMeasured_percent = null; fm2014.RefFlowRate_cm_per_h = 0; fm2014.DutFlowRate_cm_per_h = 0; } diff --git a/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs b/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs index 94b7c718..6caa69d8 100644 --- a/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs +++ b/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs @@ -503,8 +503,8 @@ namespace Sensus.Ui.FM2014TestBench if (_fm2014Config?.IndividualTolerancePercent?[ctr] != null && _fm2014Config.IndividualTolerancePercent.Count == MaxFm2014s) { - fm2014.Tolerance_percent = _fm2014Config?.IndividualTolerancePercent[ctr] ?? - FM2014.STANDARD_NOMINAL_TOLERANCE_INPUT_percent; + fm2014.CurrentOutputToleranceNominalInt_percent = _fm2014Config?.IndividualTolerancePercent[ctr] ?? + FM2014.CURRENT_OUTPUT_TOLERANCE_STANDARD_NOMINAL_INT_percent; } } if (_fm2014Config != null) @@ -512,7 +512,7 @@ namespace Sensus.Ui.FM2014TestBench // Restore baudrate FM2014.Baudrate = _fm2014Config.Baudrate; - // Restore Calibration settings for user convenience + // Restore calibration settings for user convenience tbxRefPulsesRequired.Text = $"{_fm2014Config.RefPulsesRequired:D}"; tbxDutPulsesRequired.Text = $"{_fm2014Config.DutPulsesRequired:D}"; tbxDoublePulseDeadtime.Text = $"{_fm2014Config.DoublePulseDeadtime:D}"; @@ -646,7 +646,7 @@ namespace Sensus.Ui.FM2014TestBench var fm2014 = Fm2014s[ctr]; if (fm2014 == null) return; - _fm2014Config.IndividualTolerancePercent.Add(fm2014.Tolerance_percent); + _fm2014Config.IndividualTolerancePercent.Add(fm2014.CurrentOutputToleranceNominalInt_percent); _fm2014Config.SlotIsSelected.Add(SlotSelections[ctr].IsChecked); } @@ -1291,14 +1291,14 @@ namespace Sensus.Ui.FM2014TestBench Dispatcher.Invoke(() => { if (int.TryParse(tbxRefPulsesRequired.Text, out var pulses) && - FM2014.TIME_MEASUREMENT_INPUT_MIN_pulses <= pulses && - FM2014.TIME_MEASUREMENT_INPUT_MAX_pulses >= pulses) + FM2014.PULSES_TIME_MEASUREMENT_INPUT_MIN <= pulses && + FM2014.PULSES_TIME_MEASUREMENT_INPUT_MAX >= pulses) { refPulsesRequired = (UInt16)pulses; } if (int.TryParse(tbxDutPulsesRequired.Text, out pulses) && - FM2014.TIME_MEASUREMENT_INPUT_MIN_pulses <= pulses && - FM2014.TIME_MEASUREMENT_INPUT_MAX_pulses >= pulses) + FM2014.PULSES_TIME_MEASUREMENT_INPUT_MIN <= pulses && + FM2014.PULSES_TIME_MEASUREMENT_INPUT_MAX >= pulses) { dutPulsesRequired = (UInt16)pulses; } @@ -1420,8 +1420,8 @@ namespace Sensus.Ui.FM2014TestBench } if (int.TryParse(cbxToleranceCalc.SelectedItem.ToString(), out var tolerance) && - (tolerance == FM2014.STANDARD_NOMINAL_TOLERANCE_INPUT_percent || - tolerance == FM2014.EXTENDED_NOMINAL_TOLERANCE_INPUT_percent)) + (tolerance == FM2014.CURRENT_OUTPUT_TOLERANCE_STANDARD_NOMINAL_INT_percent || + tolerance == FM2014.CURRENT_OUTPUT_TOLERANCE_EXTENDED_VALUE_INT_percent)) { toleranceSetup = tolerance; } @@ -1433,7 +1433,7 @@ namespace Sensus.Ui.FM2014TestBench var idx = fm2014.Address - 1; var ucFM2014 = (UcFM2014Device)UcFM2014s[idx]; ucFM2014.EnableMeasurements(REG_MEASURE_COUNT); - fm2014.Tolerance_percent = toleranceSetup; + fm2014.CurrentOutputToleranceNominalInt_percent = toleranceSetup; } if (FM2014.RegulationMeasurement(refPulses_per_cm, dutPulses_per_cm, doublePulseDeadtime_ms, @@ -1547,9 +1547,9 @@ namespace Sensus.Ui.FM2014TestBench Dispatcher.Invoke(() => { if (byte.TryParse(cbxToleranceCalc.SelectedItem.ToString(), out var tolerance) && - (tolerance == FM2014.STANDARD_NOMINAL_TOLERANCE_INPUT_percent || - tolerance == FM2014.EXTENDED_NOMINAL_TOLERANCE_INPUT_percent) && - firstFm2014.Tolerance_percent != tolerance) + (tolerance == FM2014.CURRENT_OUTPUT_TOLERANCE_STANDARD_NOMINAL_INT_percent || + tolerance == FM2014.CURRENT_OUTPUT_TOLERANCE_EXTENDED_VALUE_INT_percent) && + firstFm2014.CurrentOutputToleranceNominalInt_percent != tolerance) { _regulationSetupHasChanged = true; btnSaveRegulationSetup.IsEnabled = true; @@ -1789,8 +1789,8 @@ namespace Sensus.Ui.FM2014TestBench // Convert the input and check limits if (int.TryParse(tbxRefPulsesRequired.Text, out var pulses) && - FM2014.TIME_MEASUREMENT_INPUT_MIN_pulses <= pulses && - FM2014.TIME_MEASUREMENT_INPUT_MAX_pulses >= pulses) + FM2014.PULSES_TIME_MEASUREMENT_INPUT_MIN <= pulses && + FM2014.PULSES_TIME_MEASUREMENT_INPUT_MAX >= pulses) { if (backupPulsesRequired != pulses) _calibrationSetupHasChanged = true; @@ -1884,8 +1884,8 @@ namespace Sensus.Ui.FM2014TestBench // Convert the input and check limits if (int.TryParse(tbxDutPulsesRequired.Text, out var pulses) && - FM2014.TIME_MEASUREMENT_INPUT_MIN_pulses <= pulses && - FM2014.TIME_MEASUREMENT_INPUT_MAX_pulses >= pulses) + FM2014.PULSES_TIME_MEASUREMENT_INPUT_MIN <= pulses && + FM2014.PULSES_TIME_MEASUREMENT_INPUT_MAX >= pulses) { if (backupPulsesRequired != pulses) _calibrationSetupHasChanged = true; From 20ab81eba7b03ae56082b8ef4825a2b64563cf4e Mon Sep 17 00:00:00 2001 From: Thomas Wiedebusch Date: Fri, 13 Feb 2026 19:26:06 +0100 Subject: [PATCH 5/7] FM2014: - optimization --- .../FM2014/FM2014Core/Consts/FM2014CmdDef.cs | 165 +++++++++----- .../FM2014/FM2014Core/FM2014.cs | 201 ++++++++++-------- 2 files changed, 226 insertions(+), 140 deletions(-) diff --git a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs index d12bda30..fc7ecae5 100644 --- a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs +++ b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs @@ -415,6 +415,42 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co } + /// + /// Parameter format to convert the response or the request + /// + public enum ParaFormat + { + /// + /// String taken directly to send or as received + /// + STRING, + + /// + /// Double format + /// + DOUBLE, + + /// + /// Hexadecimal integer + /// + INT_HEX, + + /// + /// Decimal integer + /// + INT_DEZ, + + /// + /// Individual formatting required + /// + INDIVIDUAL, + + /// + /// Parameterless means the command will be sent without any parameter + /// + PARAMETERLESS + } + /// /// Assembly of command information and functional code /// @@ -435,6 +471,11 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co /// public readonly CmdType CmdType; + /// + /// Command parameter style for raw data conversion from response string or for transmission + /// + public readonly ParaFormat ParamFormat; + /// /// Multilingual command information string /// @@ -446,12 +487,15 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co /// Enumerator of command name acronym /// Code being sent to FM2014 as string /// Command type regarding the address (broadcast or individual) and answer + /// parameter format for conversion /// Multilingual command information string - public Cmd(CmdName cmdName, String cmdCodeStr, CmdType cmdType, String cmdInfoStr) + public Cmd(CmdName cmdName, String cmdCodeStr, CmdType cmdType, ParaFormat paraFormat, + String cmdInfoStr) { CmdName = cmdName; CmdCodeStr = cmdCodeStr; CmdType = cmdType; + ParamFormat = paraFormat; CmdInfoStr = cmdInfoStr; } } @@ -512,65 +556,65 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co public static readonly List CmdTbl = new List { // FM2014 control and information - new Cmd(CmdName.CMD_CONNECT, "", CmdType.INDIVIDUAL, Resources.StrCmdMsgConnect), - new Cmd(CmdName.CMD_UNLOCK_SETUP, "&U@", CmdType.INDIVIDUAL, Resources.StrCmdMsgUnlock), - new Cmd(CmdName.CMD_ERASE_SETUP, "&E@", CmdType.INDIVIDUAL, Resources.StrCmdMsgErase), - new Cmd(CmdName.CMD_READ_EEP_MEAS_SETUP, "%D@", CmdType.INDIVIDUAL, Resources.StrCmdMsgReadDefMeasSetup), - new Cmd(CmdName.CMD_READ_RAM_MEAS_SETUP, "%A@", CmdType.INDIVIDUAL, Resources.StrCmdMsgActualDefMeasSetup), - new Cmd(CmdName.CMD_SAVE_RAM_TO_EEP_MEAS_SETUP, "%S@", CmdType.INDIVIDUAL, Resources.StrCmdMsgStoreDefMeasSetup), - new Cmd(CmdName.CMD_BOOT_MODE, "$B@", CmdType.INDIVIDUAL, Resources.StrCmdMsgBootMode), - new Cmd(CmdName.CMD_PRINT_APP_INFO, "?A", CmdType.INDIVIDUAL, Resources.StrCmdMsgPrintAppInfo), - new Cmd(CmdName.CMD_PRINT_BOOT_INFO, "?B", CmdType.INDIVIDUAL, Resources.StrCmdMsgPrintBootInfo), - new Cmd(CmdName.CMD_BAUDRATE_PC, "=BP", CmdType.INDIVIDUAL, Resources.StrCmdMsgBaudrateAll), - new Cmd(CmdName.CMD_GET_VIRTUAL_ADDRESS, " ", CmdType.INDIVIDUAL, Resources.StrCmdMsgGetVirtualAddress), - new Cmd(CmdName.CMD_SERIAL, "#", CmdType.INDIVIDUAL, Resources.StrCmdMsgSerial), - new Cmd(CmdName.CMD_GET_STATUS, UNICODE_ACK, CmdType.INDIVIDUAL, Resources.StrCmdMsgGetStatus), - new Cmd(CmdName.CMD_SET_VIRTUAL_ADDRESS, "N", CmdType.INDIVIDUAL, Resources.StrCmdMsgSetVirtualAddress), - new Cmd(CmdName.CMD_PRINT_STATISTICS, "!", CmdType.INDIVIDUAL, Resources.StrCmdMsgPrintStatistics), + new Cmd(CmdName.CMD_CONNECT, "", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgConnect), + new Cmd(CmdName.CMD_UNLOCK_SETUP, "&U@", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgUnlock), + new Cmd(CmdName.CMD_ERASE_SETUP, "&E@", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgErase), + new Cmd(CmdName.CMD_READ_EEP_MEAS_SETUP, "%D@", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgReadDefMeasSetup), + new Cmd(CmdName.CMD_READ_RAM_MEAS_SETUP, "%A@", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgActualDefMeasSetup), + new Cmd(CmdName.CMD_SAVE_RAM_TO_EEP_MEAS_SETUP, "%S@", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgStoreDefMeasSetup), + new Cmd(CmdName.CMD_BOOT_MODE, "$B@", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgBootMode), + new Cmd(CmdName.CMD_PRINT_APP_INFO, "?A", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgPrintAppInfo), + new Cmd(CmdName.CMD_PRINT_BOOT_INFO, "?B", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgPrintBootInfo), + new Cmd(CmdName.CMD_BAUDRATE_PC, "=BP", CmdType.INDIVIDUAL, ParaFormat.INT_DEZ, Resources.StrCmdMsgBaudrateAll), + new Cmd(CmdName.CMD_GET_VIRTUAL_ADDRESS, " ", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgGetVirtualAddress), + new Cmd(CmdName.CMD_SERIAL, "#", CmdType.INDIVIDUAL, ParaFormat.INT_DEZ, Resources.StrCmdMsgSerial), + new Cmd(CmdName.CMD_GET_STATUS, UNICODE_ACK, CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgGetStatus), + new Cmd(CmdName.CMD_SET_VIRTUAL_ADDRESS, "N", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgSetVirtualAddress), + new Cmd(CmdName.CMD_PRINT_STATISTICS, "!", CmdType.INDIVIDUAL, ParaFormat.STRING, Resources.StrCmdMsgPrintStatistics), //measurement reset - new Cmd(CmdName.CMD_RST_MEAS, "R", CmdType.BROADCAST, Resources.StrCmdMsgRstMeas), + new Cmd(CmdName.CMD_RST_MEAS, "R", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgRstMeas), // Double impulse detection settings - new Cmd(CmdName.CMD_SET_DPLS_LOCK_TMR, "S", CmdType.BROADCAST, Resources.StrCmdMsgDplsLockTmr), - new Cmd(CmdName.CMD_SET_DPLS_LOCK_MULT, "s", CmdType.BROADCAST, Resources.StrCmdMsgDplsLockMult), - new Cmd(CmdName.CMD_SET_DBPL_UNLOCK, "G", CmdType.BROADCAST, Resources.StrCmdMsgSetDbplUnlock), + new Cmd(CmdName.CMD_SET_DPLS_LOCK_TMR, "S", CmdType.BROADCAST, ParaFormat.INT_DEZ, Resources.StrCmdMsgDplsLockTmr), + new Cmd(CmdName.CMD_SET_DPLS_LOCK_MULT, "s", CmdType.BROADCAST, ParaFormat.INT_DEZ, Resources.StrCmdMsgDplsLockMult), + new Cmd(CmdName.CMD_SET_DBPL_UNLOCK, "G", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgSetDbplUnlock), // Main measurement pulse settings and readout - new Cmd(CmdName.CMD_DUT_SET_PLS, "H", CmdType.BROADCAST, Resources.StrCmdMsgDutSetPls), - new Cmd(CmdName.CMD_REF_SET_PLS, "M", CmdType.BROADCAST, Resources.StrCmdMsgRefSetPls), - new Cmd(CmdName.CMD_DUT_GET_PLS_RMN, "I", CmdType.INDIVIDUAL, Resources.StrCmdMsgDutGetPlsRmn), - new Cmd(CmdName.CMD_REF_GET_PLS_RMN, "J", CmdType.INDIVIDUAL, Resources.StrCmdMsgRefGetPlsRmn), + new Cmd(CmdName.CMD_DUT_SET_PLS, "H", CmdType.BROADCAST, ParaFormat.INT_HEX, Resources.StrCmdMsgDutSetPls), + new Cmd(CmdName.CMD_REF_SET_PLS, "M", CmdType.BROADCAST, ParaFormat.INT_HEX, Resources.StrCmdMsgRefSetPls), + new Cmd(CmdName.CMD_DUT_GET_PLS_RMN, "I", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgDutGetPlsRmn), + new Cmd(CmdName.CMD_REF_GET_PLS_RMN, "J", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgRefGetPlsRmn), // Pulse counters start, readout and backup - new Cmd(CmdName.CMD_REF_STR_PLS_CTR, "O", CmdType.BROADCAST, Resources.StrCmdMsgRefStrPlsCtr), - new Cmd(CmdName.CMD_DUT_STR_PLS_CTR, "Q", CmdType.BROADCAST, Resources.StrCmdMsgDutStrPlsCtr), - new Cmd(CmdName.CMD_REF_GET_PLS_CTR, "L", CmdType.INDIVIDUAL, Resources.StrCmdMsgRefGetPlsCtr), - new Cmd(CmdName.CMD_DUT_GET_PLS_CTR, "U", CmdType.INDIVIDUAL, Resources.StrCmdMsgDutGetPlsCtr), - new Cmd(CmdName.CMD_REF_GET_PLS_CTR_BU, "l", CmdType.INDIVIDUAL, Resources.StrCmdMsgRefGetPlsCtrBu), - new Cmd(CmdName.CMD_DUT_GET_PLS_CTR_BU, "u", CmdType.INDIVIDUAL, Resources.StrCmdMsgDutGetPlsCtrBu), - new Cmd(CmdName.CMD_REF_DUT_STR_PLS_CTR, "o", CmdType.BROADCAST, Resources.StrCmdMsgRefDutStrPlsCtr), - new Cmd(CmdName.CMD_REF_DUT_BU_PLS_CTR, "q", CmdType.BROADCAST, Resources.StrCmdMsgRefDutBuPlsCtr), + new Cmd(CmdName.CMD_REF_STR_PLS_CTR, "O", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgRefStrPlsCtr), + new Cmd(CmdName.CMD_DUT_STR_PLS_CTR, "Q", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgDutStrPlsCtr), + new Cmd(CmdName.CMD_REF_GET_PLS_CTR, "L", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgRefGetPlsCtr), + new Cmd(CmdName.CMD_DUT_GET_PLS_CTR, "U", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgDutGetPlsCtr), + new Cmd(CmdName.CMD_REF_GET_PLS_CTR_BU, "l", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgRefGetPlsCtrBu), + new Cmd(CmdName.CMD_DUT_GET_PLS_CTR_BU, "u", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgDutGetPlsCtrBu), + new Cmd(CmdName.CMD_REF_DUT_STR_PLS_CTR, "o", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgRefDutStrPlsCtr), + new Cmd(CmdName.CMD_REF_DUT_BU_PLS_CTR, "q", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgRefDutBuPlsCtr), // General measurement setup - new Cmd(CmdName.CMD_REF_SET_SCALE, "K", CmdType.BROADCAST, Resources.StrCmdMsgRefSetScale), - new Cmd(CmdName.CMD_DUT_LPP_SCALE, "V", CmdType.INDIVIDUAL_UNRESPONSIVE, Resources.StrCmdMsgDutLppScale), - new Cmd(CmdName.CMD_REF_LPP_SCALE, "W", CmdType.INDIVIDUAL_UNRESPONSIVE, Resources.StrCmdMsgRefLppScale), - new Cmd(CmdName.CMD_MEAS_SET_ATTN, "T", CmdType.BROADCAST, Resources.StrCmdMsgMeasSetAttn), + new Cmd(CmdName.CMD_REF_SET_SCALE, "K", CmdType.BROADCAST, ParaFormat.INDIVIDUAL, Resources.StrCmdMsgRefSetScale), + new Cmd(CmdName.CMD_DUT_LPP_SCALE, "V", CmdType.INDIVIDUAL_UNRESPONSIVE, ParaFormat.INT_DEZ, Resources.StrCmdMsgDutLppScale), + new Cmd(CmdName.CMD_REF_LPP_SCALE, "W", CmdType.INDIVIDUAL_UNRESPONSIVE, ParaFormat.INT_DEZ, Resources.StrCmdMsgRefLppScale), + new Cmd(CmdName.CMD_MEAS_SET_ATTN, "T", CmdType.BROADCAST, ParaFormat.INT_DEZ, Resources.StrCmdMsgMeasSetAttn), // Measurement result request - new Cmd(CmdName.CMD_DUT_GET_TMR, "X", CmdType.INDIVIDUAL, Resources.StrCmdMsgDutGetTmr), - new Cmd(CmdName.CMD_REF_GET_TMR, "Y", CmdType.INDIVIDUAL, Resources.StrCmdMsgRefGetTmr), - new Cmd(CmdName.CMD_CAL_DUT_TO_REF_TOL, "", CmdType.INDIVIDUAL, Resources.StrCalDutToRefTol), - new Cmd(CmdName.CMD_GET_UDTLC, "Z", CmdType.INDIVIDUAL, Resources.StrCmdMsgGetUdtlc), - new Cmd(CmdName.CMD_GET_DTLC, "z", CmdType.INDIVIDUAL, Resources.StrCmdMsgGetDtlc), - new Cmd(CmdName.CMD_GET_REF_PERIOD, UNICODE_DLE, CmdType.INDIVIDUAL, Resources.StrCmdMsgGetRefPeriod), - new Cmd(CmdName.CMD_GET_DUT_PERIOD, UNICODE_DC1, CmdType.INDIVIDUAL, Resources.StrCmdMsgGetDutPeriod), - new Cmd(CmdName.CMD_GET_REF_FREQU, UNICODE_DC2, CmdType.INDIVIDUAL, Resources.StrCmdMsgGetRefFrequ), - new Cmd(CmdName.CMD_CAL_FREQU_REF_PERIOD, "", CmdType.INDIVIDUAL, Resources.StrCalRefFrequFromRefPeriod), - new Cmd(CmdName.CMD_CAL_FREQU_DUT_PERIOD, "", CmdType.INDIVIDUAL, Resources.StrCalDutFrequFromDutPeriod), - new Cmd(CmdName.CMD_CAL_FLOW_REF_FREQU, "", CmdType.INDIVIDUAL, Resources.StrCalRefFlowRateFromRefFrequ), - new Cmd(CmdName.CMD_CAL_FLOW_REF_PERIOD, "", CmdType.INDIVIDUAL, Resources.StrCalRefFlowRateFromRefPeriod), - new Cmd(CmdName.CMD_CAL_FLOW_DUT_PERIOD, "", CmdType.INDIVIDUAL, Resources.StrCalDutFlowRateFromDutPeriod), + new Cmd(CmdName.CMD_DUT_GET_TMR, "X", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgDutGetTmr), + new Cmd(CmdName.CMD_REF_GET_TMR, "Y", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgRefGetTmr), + new Cmd(CmdName.CMD_CAL_DUT_TO_REF_TOL, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalDutToRefTol), + new Cmd(CmdName.CMD_GET_UDTLC, "Z", CmdType.INDIVIDUAL, ParaFormat.INDIVIDUAL, Resources.StrCmdMsgGetUdtlc), + new Cmd(CmdName.CMD_GET_DTLC, "z", CmdType.INDIVIDUAL, ParaFormat.INDIVIDUAL, Resources.StrCmdMsgGetDtlc), + new Cmd(CmdName.CMD_GET_REF_PERIOD, UNICODE_DLE, CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgGetRefPeriod), + new Cmd(CmdName.CMD_GET_DUT_PERIOD, UNICODE_DC1, CmdType.INDIVIDUAL,ParaFormat.INT_HEX, Resources.StrCmdMsgGetDutPeriod), + new Cmd(CmdName.CMD_GET_REF_FREQU, UNICODE_DC2, CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgGetRefFrequ), + new Cmd(CmdName.CMD_CAL_FREQU_REF_PERIOD, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalRefFrequFromRefPeriod), + new Cmd(CmdName.CMD_CAL_FREQU_DUT_PERIOD, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalDutFrequFromDutPeriod), + new Cmd(CmdName.CMD_CAL_FLOW_REF_FREQU, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalRefFlowRateFromRefFrequ), + new Cmd(CmdName.CMD_CAL_FLOW_REF_PERIOD, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalRefFlowRateFromRefPeriod), + new Cmd(CmdName.CMD_CAL_FLOW_DUT_PERIOD, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalDutFlowRateFromDutPeriod), // Adjustment, place here Ids which may represent digital numbers from A to F - new Cmd(CmdName.CMD_COR_CURRENT, "C", CmdType.BROADCAST, Resources.StrCmdMsgCorCurrent), - new Cmd(CmdName.CMD_SET_XTR_MAX, "+", CmdType.BROADCAST, Resources.StrCmdMsgSetXtrMax), - new Cmd(CmdName.CMD_SET_XTR_MIN, "-", CmdType.BROADCAST, Resources.StrCmdMsgSetXtrMin), - new Cmd(CmdName.CMD_ADDR_PC, "A", CmdType.INDIVIDUAL, Resources.StrCmdMsgAddrPc) + new Cmd(CmdName.CMD_COR_CURRENT, "C", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgCorCurrent), + new Cmd(CmdName.CMD_SET_XTR_MAX, "+", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgSetXtrMax), + new Cmd(CmdName.CMD_SET_XTR_MIN, "-", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgSetXtrMin), + new Cmd(CmdName.CMD_ADDR_PC, "A", CmdType.INDIVIDUAL, ParaFormat.INT_DEZ, Resources.StrCmdMsgAddrPc) }; /// @@ -721,5 +765,20 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co where ct.CmdName == cmdName select ct.CmdType).FirstOrDefault(); } + + /// + /// Get the parameter format + /// + /// + /// Command type + /// + /// - Initial. + /// + public static ParaFormat GetParaFormat(CmdName cmdName) + { + return (from ct in CmdTbl + where ct.CmdName == cmdName + select ct.ParamFormat).FirstOrDefault(); + } } } diff --git a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs index 9fead8d5..260ae508 100644 --- a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs +++ b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs @@ -197,17 +197,23 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// private const Int32 PROCESSES_SAVE_STAND_ALONE_MEASUREMENT = 5; #endregion ---------------------------------------- constants ------------------------------------------------- - #region ------------------------------------------- events ---------------------------------------------------- /// /// Received data feedback /// public event EventHandler OnRawRecordReceived; #endregion ---------------------------------------- events ---------------------------------------------------- - #region ------------------------------------------- static properties ----------------------------------------- + /// + /// Reminder of actual ongoing measurement information + /// + private static String MeasurementInfo { get; set; } + + /// + /// + /// + private static Boolean DisableWriteReadActualProcessIncreasing { get; set; } - private static Boolean _disableWriteReadActualProcessIncreasing; /// /// Shared cancellation token /// @@ -803,7 +809,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core private static void InitActualProcessProgress(Int32? maxInitProcesses = null, Boolean broadcast = false, Boolean disableWriteReadIncrease = false) { - _disableWriteReadActualProcessIncreasing = disableWriteReadIncrease; + DisableWriteReadActualProcessIncreasing = disableWriteReadIncrease; MaxActualProcessProgress = 1; ActualProcessProgress = 0; @@ -820,6 +826,27 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } } + /// + /// Common handler to publish the response. + /// + /// + /// + /// + /// + /// + /// - Initial. + /// + private static void ResponseEvent(FM2014 fm2014, CmdResponse cmdResponse, + StatusReturn statusReturn = StatusReturn.Unknown) + { + var processExecEventArgs = new ProcessExecEventArgs("", + actualProcessMessage: MeasurementInfo, + actualProcessPercent: ActualProcessProgress, + statusReturn: statusReturn, + specificInfoObj: cmdResponse); + fm2014?.OnRawRecordReceived?.Invoke(fm2014, processExecEventArgs); + } + /// /// Common handler to publish the response. /// @@ -895,7 +922,6 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } var cmdName = CmdName.CMD_NA; - String measurementInfoStr; // Prepare REF and/or DUT pulse counter measurement, the first call activates if one or both are started! try @@ -904,15 +930,15 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { case true when firstActiveFm2014.DutPulseCtrOn: cmdName = CmdName.CMD_REF_DUT_STR_PLS_CTR; - measurementInfoStr = Resources.StrMeasMsgPulseCtrRefDut; + MeasurementInfo = Resources.StrMeasMsgPulseCtrRefDut; break; case true: cmdName = CmdName.CMD_REF_STR_PLS_CTR; - measurementInfoStr = Resources.StrMeasMsgPulseCtrRef; + MeasurementInfo = Resources.StrMeasMsgPulseCtrRef; break; default: cmdName = CmdName.CMD_DUT_STR_PLS_CTR; - measurementInfoStr = Resources.StrMeasMsgPulseCtrDut; + MeasurementInfo = Resources.StrMeasMsgPulseCtrDut; break; } @@ -972,32 +998,23 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { try { - if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE) - { - if (fm2014.RefPulseCtrOn && Write(cmdNameRef, fm2014)) - { - if (Read(cmdNameRef, fm2014, out var responseStr) && - int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), - out var intValue)) - { - var response = new CmdResponse(cmdNameRef, infoRef, intValue); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr, - specificInfoObj: response)); - } - } + if (SharedCyclicMeasSequ == CyclicMeasSequ.IDLE) continue; - if (fm2014.DutPulseCtrOn && Write(cmdNameDut, fm2014)) + if (fm2014.RefPulseCtrOn && Write(cmdNameRef, fm2014)) + { + if (Read(cmdNameRef, fm2014, out _, out var intValue)) { - if (Read(cmdNameDut, fm2014, out var responseStr) && - int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), - out var intValue)) - { - var response = new CmdResponse(cmdNameDut, infoDut, intValue); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr, - specificInfoObj: response)); - } + var response = new CmdResponse(cmdNameRef, infoRef, intValue); + ResponseEvent(fm2014, response); + } + } + + if (fm2014.DutPulseCtrOn && Write(cmdNameDut, fm2014)) + { + if (Read(cmdNameDut, fm2014, out _, out var intValue)) + { + var response = new CmdResponse(cmdNameDut, infoDut, intValue); + ResponseEvent(fm2014, response); } } } @@ -1096,7 +1113,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } var cmdName = CmdName.CMD_NA; - var measurementInfo = Resources.StrMeasMsgRegulation; + MeasurementInfo = Resources.StrMeasMsgRegulation; // Prepare regulation measurement try { @@ -1145,12 +1162,12 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr)) + if (Read(cmdName, fm2014, out responseStr, out _)) { // The response contains a sign! var signMultiplier = responseStr.Contains("+") ? 1.0f : -1.0f; var cleanedStr = responseStr.Replace("+", "").Replace("-", ""); - if (int.TryParse(cleanedStr, NumberStyles.HexNumber, + if (int.TryParse(cleanedStr, NumberStyles.HexNumber, new CultureInfo("en"), out var measTolerance)) { fm2014.DutToRefToleranceMeasured_percent = @@ -1158,16 +1175,16 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var response = new CmdResponse(cmdName, info, doubleValue: fm2014.DutToRefToleranceMeasured_percent, unit: Units.GetInfo(Units.Name.PERCENT)); - + // Check the threshold for OOR - if (Math.Abs((Double)fm2014.DutToRefToleranceMeasured_percent) > + if (Math.Abs((Double)fm2014.DutToRefToleranceMeasured_percent) > fm2014.CurrentOutputToleranceNominalInt_percent) statusReturn = StatusReturn.MeasurementOutOfRange; else statusReturn = StatusReturn.Okay; PublishResponse(fm2014, - new ProcessExecEventArgs("", - actualProcessMessage: measurementInfo, + new ProcessExecEventArgs("", + actualProcessMessage: MeasurementInfo, statusReturn: statusReturn, specificInfoObj: response)); } @@ -1180,7 +1197,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core if (fm2014.RequestDebugInformation && SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr) && + if (Read(cmdName, fm2014, out responseStr, out _) && int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var period)) { // Publish the period [ms] @@ -1188,7 +1205,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core doubleValue: TMR_RESOLUTION_s * 1000.0 * period, unit: "m" + Units.GetInfo(Units.Name.TIME_s)); PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + new ProcessExecEventArgs("", actualProcessMessage: MeasurementInfo, specificInfoObj: response)); // Publish the frequency in [Hz] @@ -1198,7 +1215,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core doubleValue: 1.0 / (TMR_RESOLUTION_s * period), unit: Units.GetInfo(Units.Name.FREQUENCY_Hz)); PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + new ProcessExecEventArgs("", actualProcessMessage: MeasurementInfo, specificInfoObj: response)); // Convert to m³/h (3600 s/h) based on REF pulse rate @@ -1209,7 +1226,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core response = new CmdResponse(cmdName, info, doubleValue: fm2014.RefFlowRate_cm_per_h, unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h)); PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + new ProcessExecEventArgs("", actualProcessMessage: MeasurementInfo, specificInfoObj: response)); } } @@ -1222,7 +1239,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr) && + if (Read(cmdName, fm2014, out responseStr, out _) && int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var period)) { // Publish the period [ms] @@ -1230,7 +1247,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core doubleValue: TMR_RESOLUTION_s * 1000.0 * period, unit: "m" + Units.GetInfo(Units.Name.TIME_s)); PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + new ProcessExecEventArgs("", actualProcessMessage: MeasurementInfo, specificInfoObj: response)); // Publish the frequency in [Hz] @@ -1240,7 +1257,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core doubleValue: 1.0 / (TMR_RESOLUTION_s * period), unit: Units.GetInfo(Units.Name.FREQUENCY_Hz)); PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + new ProcessExecEventArgs("", actualProcessMessage: MeasurementInfo, specificInfoObj: response)); // Convert to m³/h (3600 s/h) based on DUT pulse rate @@ -1251,7 +1268,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core response = new CmdResponse(cmdName, info, doubleValue: fm2014.DutFlowRate_cm_per_h, unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h)); PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + new ProcessExecEventArgs("", actualProcessMessage: MeasurementInfo, specificInfoObj: response)); } } @@ -1262,7 +1279,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr) && + if (Read(cmdName, fm2014, out responseStr, out _) && int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var refFrequency)) { @@ -1270,12 +1287,12 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core if (refFrequency < REF_FREQU_MIN || refFrequency > REF_FREQU_MAX) statusReturn = StatusReturn.MeasurementOutOfRange; else statusReturn = StatusReturn.Okay; - + // Publish the frequency in [Hz] var response = new CmdResponse(cmdName, info, refFrequency, unit: Units.GetInfo(Units.Name.FREQUENCY_Hz)); PublishResponse(fm2014, new ProcessExecEventArgs("", - actualProcessMessage: measurementInfo, + actualProcessMessage: MeasurementInfo, statusReturn: statusReturn, specificInfoObj: response)); @@ -1288,7 +1305,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h)); PublishResponse(fm2014, new ProcessExecEventArgs("", - actualProcessMessage: measurementInfo, + actualProcessMessage: MeasurementInfo, statusReturn: statusReturn, specificInfoObj: response)); } @@ -1381,9 +1398,9 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } var cmdName = CmdName.CMD_NA; - var measurementInfo = Resources.StrMeasMsgCalibration; + MeasurementInfo = Resources.StrMeasMsgCalibration; - // Prepare pulse time measurement + // Prepare pulse time measurement with all broadcast command - avoid read try { // Check if double pulse deadtime has been set or si switched off @@ -1415,7 +1432,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core if (firstActiveFm2014.RefPulsesRequired != 0) { cmdName = CmdName.CMD_REF_SET_PLS; - if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.RefPulsesRequired, true)) + if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.RefPulsesRequired)) { return false; } @@ -1424,7 +1441,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core if (firstActiveFm2014.DutPulsesRequired != 0) { cmdName = CmdName.CMD_DUT_SET_PLS; - if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.DutPulsesRequired, true)) + if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.DutPulsesRequired)) { return false; } @@ -1459,21 +1476,21 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core String info; String responseStr; StatusReturn statusReturn; - + // Read the device status to check for timeout between pulses cmdName = CmdName.CMD_GET_STATUS; info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr) && + if (Read(cmdName, fm2014, out responseStr, out _) && int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var status)) { info += $": {responseStr}"; - var response = new CmdResponse(cmdName, info); + var response = new CmdResponse(cmdName, info, status); PublishResponse(fm2014, new ProcessExecEventArgs("", actualProcessPercent: ActualProcessProgress_percent, - actualProcessMessage: measurementInfo, specificInfoObj: response)); + actualProcessMessage: MeasurementInfo, specificInfoObj: response)); } } @@ -1487,7 +1504,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr) && + if (Read(cmdName, fm2014, out responseStr, out _) && ushort.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var pulses)) { @@ -1509,7 +1526,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core PublishResponse(fm2014, new ProcessExecEventArgs("", actualProcessPercent: ActualProcessProgress_percent, statusReturn: statusReturn, - actualProcessMessage: measurementInfo, specificInfoObj: response)); + actualProcessMessage: MeasurementInfo, specificInfoObj: response)); } } } @@ -1525,7 +1542,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr) && + if (Read(cmdName, fm2014, out responseStr, out _) && ushort.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var pulses)) { @@ -1545,7 +1562,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var response = new CmdResponse(cmdName, info, pulses); PublishResponse(fm2014, new ProcessExecEventArgs("", - actualProcessMessage: measurementInfo, + actualProcessMessage: MeasurementInfo, statusReturn: statusReturn, specificInfoObj: response)); } @@ -1565,7 +1582,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr) && + if (Read(cmdName, fm2014, out responseStr, out _) && uint.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var timerTicks)) { @@ -1574,7 +1591,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var response = new CmdResponse(cmdName, info, doubleValue: fm2014.RefTimeMeasured_s, unit: Units.GetInfo(Units.Name.TIME_s)); PublishResponse(fm2014, new ProcessExecEventArgs("", - actualProcessMessage: measurementInfo, + actualProcessMessage: MeasurementInfo, specificInfoObj: response)); } else @@ -1595,7 +1612,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr) && + if (Read(cmdName, fm2014, out responseStr, out _) && uint.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var timerTicks)) { @@ -1604,7 +1621,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var response = new CmdResponse(cmdName, info, doubleValue: fm2014.DutTimeMeasured_s, unit: Units.GetInfo(Units.Name.TIME_s)); PublishResponse(fm2014, new ProcessExecEventArgs("", - actualProcessMessage: measurementInfo, + actualProcessMessage: MeasurementInfo, specificInfoObj: response)); } else @@ -1630,18 +1647,18 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core (1.0 - fm2014.DutTimeMeasured_s / fm2014.RefTimeMeasured_s) * 100.0; // Check the threshold for OOR - if (fm2014.DutToRefToleranceMeasured_percent > + if (fm2014.DutToRefToleranceMeasured_percent > fm2014.DutToRefCalibrationToleranceMax_percent || - fm2014.DutToRefToleranceMeasured_percent < + fm2014.DutToRefToleranceMeasured_percent < fm2014.DutToRefCalibrationToleranceMin_percent) statusReturn = StatusReturn.MeasurementOutOfRange; else statusReturn = StatusReturn.Okay; - + var response = new CmdResponse(cmdName, info, doubleValue: fm2014.DutToRefToleranceMeasured_percent, unit: Units.GetInfo(Units.Name.PERCENT)); PublishResponse(fm2014, new ProcessExecEventArgs("", - actualProcessMessage: measurementInfo, + actualProcessMessage: MeasurementInfo, statusReturn: statusReturn, specificInfoObj: response)); } @@ -1661,7 +1678,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core // Check all remaining DUT and REF pulses are counted on all connected devices and finalize process by // reading out all results - if (RegisteredFm2014s.All(fm2014 => fm2014.MeasurementIsReady || fm2014.ErrorIsDetected || + if (RegisteredFm2014s.All(fm2014 => fm2014.MeasurementIsReady || fm2014.ErrorIsDetected || !fm2014.IsLoggedOn)) { ResetHardwareAllDevices(); @@ -1682,20 +1699,19 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// /// - /// + /// output of pure pure raw data response from FM2014 + /// output of parsed response to integer value /// - /// + /// /// - Initial. /// - /// - /// - Support for multiple FM2014s. - /// - private static Boolean Read(CmdName cmdName, FM2014 fm2014, out String responseStr) + private static Boolean Read(CmdName cmdName, FM2014 fm2014, out String responseStr, out Int32? intValue) { responseStr = ""; + intValue = null; var info = $"{Resources.StrCommResponse}: {GetCmdInfo(cmdName)}"; var statusReturn = StatusReturn.Failed; - if (!_disableWriteReadActualProcessIncreasing) + if (!DisableWriteReadActualProcessIncreasing) ActualProcessProgress++; // Check for individual command without response @@ -1724,6 +1740,17 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var response = new CmdResponse(cmdName, $"{isErrorStr}{info} {responseStr}"); PublishResponse(fm2014, new ProcessExecEventArgs("", specificInfoObj: response, statusReturn: statusReturn)); + var numberStyle = NumberStyles.None; + if (GetParaFormat(cmdName) == ParaFormat.INT_HEX) + { + numberStyle = NumberStyles.HexNumber; + } + + if (int.TryParse(responseStr, numberStyle, new CultureInfo("en"), out var intParsedResponse)) + { + intValue = intParsedResponse; + } + return StatusReturn.Failed != statusReturn; } @@ -1740,12 +1767,11 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// /// - /// /// - /// + /// /// - Initial. /// - private static Boolean Write(CmdName cmdName, FM2014 fm2014, Object dataObj = null, Boolean isHexFormat = false) + private static Boolean Write(CmdName cmdName, FM2014 fm2014, Object dataObj = null) { if (fm2014 == null) return false; @@ -1757,7 +1783,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core SharedCyclicMeasSequ = CyclicMeasSequ.IDLE; return false; } - if (!_disableWriteReadActualProcessIncreasing) + if (!DisableWriteReadActualProcessIncreasing) ActualProcessProgress++; // Initiate communication using the individual or broadcast address @@ -1788,6 +1814,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var cmdCodeStr = GetCmdStr(cmdName); var info = GetCmdInfo(cmdName) + $": {cmdCodeStr}"; + var isHexFormat = GetParaFormat(cmdName) == ParaFormat.INT_HEX; CmdResponse response; // Without data the command alone needs to be sent if (dataObj == null) @@ -2078,7 +2105,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var loopCtr = 4; do { - if (Read(cmdName, this, out var responseStr)) + if (Read(cmdName, this, out var responseStr, out _)) responseStrs.Add(responseStr); } while (loopCtr-- > 0); } @@ -2088,7 +2115,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var loopCtr = 5; do { - if (Read(cmdName, this, out var responseStr)) + if (Read(cmdName, this, out var responseStr, out _)) responseStrs.Add(responseStr); } while (loopCtr-- > 0); } @@ -2267,7 +2294,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core IsLoggedOn = true; - if (Read(cmdName, this, out var responseStr)) + if (Read(cmdName, this, out var responseStr, out _)) SerialNumber = responseStr; if (!string.IsNullOrEmpty(SerialNumber)) @@ -2282,7 +2309,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core return false; } - if (Read(cmdName, this, out responseStr)) + if (Read(cmdName, this, out responseStr, out _)) { var cleanedResponse = Regex.Replace(responseStr, "[^0-9]", string.Empty); if (int.TryParse(cleanedResponse, out var intValue)) From 8998f9058f051171eba59a30d4c59961958ee2e6 Mon Sep 17 00:00:00 2001 From: Thomas Wiedebusch Date: Sat, 14 Feb 2026 06:21:03 +0100 Subject: [PATCH 6/7] FM2014: - message handling improved --- .../FM2014/FM2014Core/Consts/FM2014CmdDef.cs | 65 ++-- .../FM2014/FM2014Core/FM2014.cs | 303 ++++++++---------- .../Fm2014sTest/FM2014TestBenchWindow.xaml.cs | 6 +- 3 files changed, 175 insertions(+), 199 deletions(-) diff --git a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs index fc7ecae5..8a30207a 100644 --- a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs +++ b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs @@ -433,12 +433,17 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co /// /// Hexadecimal integer /// - INT_HEX, + UINT_HEX, + + /// + /// Hexadecimal byte plus additional sign + /// + BYTE_HEX_PLUS_SIGN, /// /// Decimal integer /// - INT_DEZ, + UINT_DEZ, /// /// Individual formatting required @@ -565,46 +570,46 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co new Cmd(CmdName.CMD_BOOT_MODE, "$B@", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgBootMode), new Cmd(CmdName.CMD_PRINT_APP_INFO, "?A", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgPrintAppInfo), new Cmd(CmdName.CMD_PRINT_BOOT_INFO, "?B", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgPrintBootInfo), - new Cmd(CmdName.CMD_BAUDRATE_PC, "=BP", CmdType.INDIVIDUAL, ParaFormat.INT_DEZ, Resources.StrCmdMsgBaudrateAll), - new Cmd(CmdName.CMD_GET_VIRTUAL_ADDRESS, " ", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgGetVirtualAddress), - new Cmd(CmdName.CMD_SERIAL, "#", CmdType.INDIVIDUAL, ParaFormat.INT_DEZ, Resources.StrCmdMsgSerial), - new Cmd(CmdName.CMD_GET_STATUS, UNICODE_ACK, CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgGetStatus), - new Cmd(CmdName.CMD_SET_VIRTUAL_ADDRESS, "N", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgSetVirtualAddress), + new Cmd(CmdName.CMD_BAUDRATE_PC, "=BP", CmdType.INDIVIDUAL, ParaFormat.UINT_DEZ, Resources.StrCmdMsgBaudrateAll), + new Cmd(CmdName.CMD_GET_VIRTUAL_ADDRESS, " ", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgGetVirtualAddress), + new Cmd(CmdName.CMD_SERIAL, "#", CmdType.INDIVIDUAL, ParaFormat.UINT_DEZ, Resources.StrCmdMsgSerial), + new Cmd(CmdName.CMD_GET_STATUS, UNICODE_ACK, CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgGetStatus), + new Cmd(CmdName.CMD_SET_VIRTUAL_ADDRESS, "N", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgSetVirtualAddress), new Cmd(CmdName.CMD_PRINT_STATISTICS, "!", CmdType.INDIVIDUAL, ParaFormat.STRING, Resources.StrCmdMsgPrintStatistics), //measurement reset new Cmd(CmdName.CMD_RST_MEAS, "R", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgRstMeas), // Double impulse detection settings - new Cmd(CmdName.CMD_SET_DPLS_LOCK_TMR, "S", CmdType.BROADCAST, ParaFormat.INT_DEZ, Resources.StrCmdMsgDplsLockTmr), - new Cmd(CmdName.CMD_SET_DPLS_LOCK_MULT, "s", CmdType.BROADCAST, ParaFormat.INT_DEZ, Resources.StrCmdMsgDplsLockMult), + new Cmd(CmdName.CMD_SET_DPLS_LOCK_TMR, "S", CmdType.BROADCAST, ParaFormat.UINT_DEZ, Resources.StrCmdMsgDplsLockTmr), + new Cmd(CmdName.CMD_SET_DPLS_LOCK_MULT, "s", CmdType.BROADCAST, ParaFormat.UINT_DEZ, Resources.StrCmdMsgDplsLockMult), new Cmd(CmdName.CMD_SET_DBPL_UNLOCK, "G", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgSetDbplUnlock), // Main measurement pulse settings and readout - new Cmd(CmdName.CMD_DUT_SET_PLS, "H", CmdType.BROADCAST, ParaFormat.INT_HEX, Resources.StrCmdMsgDutSetPls), - new Cmd(CmdName.CMD_REF_SET_PLS, "M", CmdType.BROADCAST, ParaFormat.INT_HEX, Resources.StrCmdMsgRefSetPls), - new Cmd(CmdName.CMD_DUT_GET_PLS_RMN, "I", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgDutGetPlsRmn), - new Cmd(CmdName.CMD_REF_GET_PLS_RMN, "J", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgRefGetPlsRmn), + new Cmd(CmdName.CMD_DUT_SET_PLS, "H", CmdType.BROADCAST, ParaFormat.UINT_HEX, Resources.StrCmdMsgDutSetPls), + new Cmd(CmdName.CMD_REF_SET_PLS, "M", CmdType.BROADCAST, ParaFormat.UINT_HEX, Resources.StrCmdMsgRefSetPls), + new Cmd(CmdName.CMD_DUT_GET_PLS_RMN, "I", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgDutGetPlsRmn), + new Cmd(CmdName.CMD_REF_GET_PLS_RMN, "J", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgRefGetPlsRmn), // Pulse counters start, readout and backup new Cmd(CmdName.CMD_REF_STR_PLS_CTR, "O", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgRefStrPlsCtr), new Cmd(CmdName.CMD_DUT_STR_PLS_CTR, "Q", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgDutStrPlsCtr), - new Cmd(CmdName.CMD_REF_GET_PLS_CTR, "L", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgRefGetPlsCtr), - new Cmd(CmdName.CMD_DUT_GET_PLS_CTR, "U", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgDutGetPlsCtr), - new Cmd(CmdName.CMD_REF_GET_PLS_CTR_BU, "l", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgRefGetPlsCtrBu), - new Cmd(CmdName.CMD_DUT_GET_PLS_CTR_BU, "u", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgDutGetPlsCtrBu), + new Cmd(CmdName.CMD_REF_GET_PLS_CTR, "L", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgRefGetPlsCtr), + new Cmd(CmdName.CMD_DUT_GET_PLS_CTR, "U", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgDutGetPlsCtr), + new Cmd(CmdName.CMD_REF_GET_PLS_CTR_BU, "l", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgRefGetPlsCtrBu), + new Cmd(CmdName.CMD_DUT_GET_PLS_CTR_BU, "u", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgDutGetPlsCtrBu), new Cmd(CmdName.CMD_REF_DUT_STR_PLS_CTR, "o", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgRefDutStrPlsCtr), new Cmd(CmdName.CMD_REF_DUT_BU_PLS_CTR, "q", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgRefDutBuPlsCtr), // General measurement setup new Cmd(CmdName.CMD_REF_SET_SCALE, "K", CmdType.BROADCAST, ParaFormat.INDIVIDUAL, Resources.StrCmdMsgRefSetScale), - new Cmd(CmdName.CMD_DUT_LPP_SCALE, "V", CmdType.INDIVIDUAL_UNRESPONSIVE, ParaFormat.INT_DEZ, Resources.StrCmdMsgDutLppScale), - new Cmd(CmdName.CMD_REF_LPP_SCALE, "W", CmdType.INDIVIDUAL_UNRESPONSIVE, ParaFormat.INT_DEZ, Resources.StrCmdMsgRefLppScale), - new Cmd(CmdName.CMD_MEAS_SET_ATTN, "T", CmdType.BROADCAST, ParaFormat.INT_DEZ, Resources.StrCmdMsgMeasSetAttn), + new Cmd(CmdName.CMD_DUT_LPP_SCALE, "V", CmdType.INDIVIDUAL_UNRESPONSIVE, ParaFormat.UINT_DEZ, Resources.StrCmdMsgDutLppScale), + new Cmd(CmdName.CMD_REF_LPP_SCALE, "W", CmdType.INDIVIDUAL_UNRESPONSIVE, ParaFormat.UINT_DEZ, Resources.StrCmdMsgRefLppScale), + new Cmd(CmdName.CMD_MEAS_SET_ATTN, "T", CmdType.BROADCAST, ParaFormat.UINT_DEZ, Resources.StrCmdMsgMeasSetAttn), // Measurement result request - new Cmd(CmdName.CMD_DUT_GET_TMR, "X", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgDutGetTmr), - new Cmd(CmdName.CMD_REF_GET_TMR, "Y", CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgRefGetTmr), + new Cmd(CmdName.CMD_DUT_GET_TMR, "X", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgDutGetTmr), + new Cmd(CmdName.CMD_REF_GET_TMR, "Y", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgRefGetTmr), new Cmd(CmdName.CMD_CAL_DUT_TO_REF_TOL, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalDutToRefTol), - new Cmd(CmdName.CMD_GET_UDTLC, "Z", CmdType.INDIVIDUAL, ParaFormat.INDIVIDUAL, Resources.StrCmdMsgGetUdtlc), - new Cmd(CmdName.CMD_GET_DTLC, "z", CmdType.INDIVIDUAL, ParaFormat.INDIVIDUAL, Resources.StrCmdMsgGetDtlc), - new Cmd(CmdName.CMD_GET_REF_PERIOD, UNICODE_DLE, CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgGetRefPeriod), - new Cmd(CmdName.CMD_GET_DUT_PERIOD, UNICODE_DC1, CmdType.INDIVIDUAL,ParaFormat.INT_HEX, Resources.StrCmdMsgGetDutPeriod), - new Cmd(CmdName.CMD_GET_REF_FREQU, UNICODE_DC2, CmdType.INDIVIDUAL, ParaFormat.INT_HEX, Resources.StrCmdMsgGetRefFrequ), + new Cmd(CmdName.CMD_GET_UDTLC, "Z", CmdType.INDIVIDUAL, ParaFormat.BYTE_HEX_PLUS_SIGN, Resources.StrCmdMsgGetUdtlc), + new Cmd(CmdName.CMD_GET_DTLC, "z", CmdType.INDIVIDUAL, ParaFormat.BYTE_HEX_PLUS_SIGN, Resources.StrCmdMsgGetDtlc), + new Cmd(CmdName.CMD_GET_REF_PERIOD, UNICODE_DLE, CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgGetRefPeriod), + new Cmd(CmdName.CMD_GET_DUT_PERIOD, UNICODE_DC1, CmdType.INDIVIDUAL,ParaFormat.UINT_HEX, Resources.StrCmdMsgGetDutPeriod), + new Cmd(CmdName.CMD_GET_REF_FREQU, UNICODE_DC2, CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgGetRefFrequ), new Cmd(CmdName.CMD_CAL_FREQU_REF_PERIOD, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalRefFrequFromRefPeriod), new Cmd(CmdName.CMD_CAL_FREQU_DUT_PERIOD, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalDutFrequFromDutPeriod), new Cmd(CmdName.CMD_CAL_FLOW_REF_FREQU, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalRefFlowRateFromRefFrequ), @@ -614,7 +619,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co new Cmd(CmdName.CMD_COR_CURRENT, "C", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgCorCurrent), new Cmd(CmdName.CMD_SET_XTR_MAX, "+", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgSetXtrMax), new Cmd(CmdName.CMD_SET_XTR_MIN, "-", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgSetXtrMin), - new Cmd(CmdName.CMD_ADDR_PC, "A", CmdType.INDIVIDUAL, ParaFormat.INT_DEZ, Resources.StrCmdMsgAddrPc) + new Cmd(CmdName.CMD_ADDR_PC, "A", CmdType.INDIVIDUAL, ParaFormat.UINT_DEZ, Resources.StrCmdMsgAddrPc) }; /// @@ -675,7 +680,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co /// /// Optional integer value /// - public Int32? IntValue { get; private set; } + public UInt32? IntValue { get; private set; } /// /// Optional double value @@ -695,7 +700,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co /// /// /// - public CmdResponse(CmdName cmdName, String answerStr, Int32? intValue = null, Double? doubleValue = null, + public CmdResponse(CmdName cmdName, String answerStr, UInt32? intValue = null, Double? doubleValue = null, String unit = "") { CmdName = cmdName; diff --git a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs index 260ae508..78e71fa9 100644 --- a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs +++ b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs @@ -386,17 +386,18 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// public Boolean UseDampedTolerance { get; set; } - private UInt16 _refPulsesRequired; + private UInt32 _refPulsesRequired; /// /// Required REF pulses for time measurement. /// - public UInt16 RefPulsesRequired + public UInt32 RefPulsesRequired { get => _refPulsesRequired; private set { // Check limits and equality if (value < PULSES_TIME_MEASUREMENT_INPUT_MIN || + value > PULSES_TIME_MEASUREMENT_INPUT_MAX || value == _refPulsesRequired) return; @@ -404,17 +405,18 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } } - private UInt16 _dutPulsesRequired; + private UInt32 _dutPulsesRequired; /// /// Required DUT pulses for time measurement. /// - public UInt16 DutPulsesRequired + public UInt32 DutPulsesRequired { get => _dutPulsesRequired; private set { // Check limits and equality if (value < PULSES_TIME_MEASUREMENT_INPUT_MIN || + value > PULSES_TIME_MEASUREMENT_INPUT_MAX || value == _dutPulsesRequired) return; @@ -738,7 +740,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core do { // Change the SI unit to ms instead of seconds - var response = new CmdResponse(CmdName.CMD_RST_MEAS, infoStr, resetDelayCtr_ms, + var response = new CmdResponse(CmdName.CMD_RST_MEAS, infoStr, (UInt32)resetDelayCtr_ms, unit: "m" + Units.GetInfo(Units.Name.TIME_s)); PublishResponse(firstActiveFm2014, new ProcessExecEventArgs("", actualProcessMessage: infoStr, @@ -836,12 +838,12 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// - Initial. /// - private static void ResponseEvent(FM2014 fm2014, CmdResponse cmdResponse, + private static void ResponseEvent(FM2014 fm2014, CmdResponse cmdResponse, StatusReturn statusReturn = StatusReturn.Unknown) { var processExecEventArgs = new ProcessExecEventArgs("", actualProcessMessage: MeasurementInfo, - actualProcessPercent: ActualProcessProgress, + actualProcessPercent: ActualProcessProgress_percent, statusReturn: statusReturn, specificInfoObj: cmdResponse); fm2014?.OnRawRecordReceived?.Invoke(fm2014, processExecEventArgs); @@ -886,7 +888,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// true if initialization successfully executed or /// marks the already started cyclic requests /// for all registered FM2014s - /// + /// /// - Initial. /// public static Boolean PulseCounterMeasurement(Boolean refPulseCtrOn, Boolean dutPulseCtrOn) @@ -1002,7 +1004,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core if (fm2014.RefPulseCtrOn && Write(cmdNameRef, fm2014)) { - if (Read(cmdNameRef, fm2014, out _, out var intValue)) + if (Read(cmdNameRef, fm2014, out _, out var intValue, out _)) { var response = new CmdResponse(cmdNameRef, infoRef, intValue); ResponseEvent(fm2014, response); @@ -1011,7 +1013,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core if (fm2014.DutPulseCtrOn && Write(cmdNameDut, fm2014)) { - if (Read(cmdNameDut, fm2014, out _, out var intValue)) + if (Read(cmdNameDut, fm2014, out _, out var intValue, out _)) { var response = new CmdResponse(cmdNameDut, infoDut, intValue); ResponseEvent(fm2014, response); @@ -1068,7 +1070,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// /// - /// + /// /// - Initial. /// public static Boolean RegulationMeasurement(UInt32 refPulses_per_cm, UInt16 dutPulses_per_cm, @@ -1156,38 +1158,26 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { // Read out the damped/undamped tolerance and convert the raw value (0, +/-1..255 digits) to the // tolerance in percent depending on the setup to 3 % (3.3) of 5 % (5.5) - String responseStr; StatusReturn statusReturn; cmdName = fm2014.UseDampedTolerance ? CmdName.CMD_GET_DTLC : CmdName.CMD_GET_UDTLC; var info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr, out _)) + if (Read(cmdName, fm2014, out _, out _, out var doubleValue)) { - // The response contains a sign! - var signMultiplier = responseStr.Contains("+") ? 1.0f : -1.0f; - var cleanedStr = responseStr.Replace("+", "").Replace("-", ""); - if (int.TryParse(cleanedStr, NumberStyles.HexNumber, - new CultureInfo("en"), out var measTolerance)) - { - fm2014.DutToRefToleranceMeasured_percent = - measTolerance * fm2014.CurrentOutputToleranceNominalToRealScale * signMultiplier; - var response = new CmdResponse(cmdName, info, - doubleValue: fm2014.DutToRefToleranceMeasured_percent, - unit: Units.GetInfo(Units.Name.PERCENT)); + fm2014.DutToRefToleranceMeasured_percent = doubleValue * + fm2014.CurrentOutputToleranceNominalToRealScale; + var response = new CmdResponse(cmdName, info, + doubleValue: fm2014.DutToRefToleranceMeasured_percent, + unit: Units.GetInfo(Units.Name.PERCENT)); - // Check the threshold for OOR - if (Math.Abs((Double)fm2014.DutToRefToleranceMeasured_percent) > - fm2014.CurrentOutputToleranceNominalInt_percent) - statusReturn = StatusReturn.MeasurementOutOfRange; - else statusReturn = StatusReturn.Okay; + // Check the threshold for OOR + if (Math.Abs((Double)fm2014.DutToRefToleranceMeasured_percent) > + fm2014.CurrentOutputToleranceNominalInt_percent) + statusReturn = StatusReturn.MeasurementOutOfRange; + else statusReturn = StatusReturn.Okay; - PublishResponse(fm2014, - new ProcessExecEventArgs("", - actualProcessMessage: MeasurementInfo, - statusReturn: statusReturn, - specificInfoObj: response)); - } + ResponseEvent(fm2014, response, statusReturn); } } @@ -1197,16 +1187,13 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core if (fm2014.RequestDebugInformation && SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr, out _) && - int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var period)) + if (Read(cmdName, fm2014, out _, out var period, out _)) { // Publish the period [ms] var response = new CmdResponse(cmdName, info, doubleValue: TMR_RESOLUTION_s * 1000.0 * period, unit: "m" + Units.GetInfo(Units.Name.TIME_s)); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: MeasurementInfo, - specificInfoObj: response)); + ResponseEvent(fm2014, response); // Publish the frequency in [Hz] cmdName = CmdName.CMD_CAL_FREQU_REF_PERIOD; @@ -1214,20 +1201,16 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core response = new CmdResponse(cmdName, info, doubleValue: 1.0 / (TMR_RESOLUTION_s * period), unit: Units.GetInfo(Units.Name.FREQUENCY_Hz)); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: MeasurementInfo, - specificInfoObj: response)); + ResponseEvent(fm2014, response); // Convert to m³/h (3600 s/h) based on REF pulse rate - fm2014.RefFlowRate_cm_per_h = 3600.0 / (period * TMR_RESOLUTION_s) / fm2014.RefPulses_per_cm; + fm2014.RefFlowRate_cm_per_h = 3600.0 / ((Double)period * TMR_RESOLUTION_s) / fm2014.RefPulses_per_cm; // Publish the calculated flow rate [m³/h] cmdName = CmdName.CMD_CAL_FLOW_REF_PERIOD; info = GetCmdInfo(cmdName); response = new CmdResponse(cmdName, info, doubleValue: fm2014.RefFlowRate_cm_per_h, unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h)); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: MeasurementInfo, - specificInfoObj: response)); + ResponseEvent(fm2014, response); } } @@ -1239,16 +1222,13 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr, out _) && - int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var period)) + if (Read(cmdName, fm2014, out _, out var period, out _)) { // Publish the period [ms] var response = new CmdResponse(cmdName, info, doubleValue: TMR_RESOLUTION_s * 1000.0 * period, unit: "m" + Units.GetInfo(Units.Name.TIME_s)); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: MeasurementInfo, - specificInfoObj: response)); + ResponseEvent(fm2014, response); // Publish the frequency in [Hz] cmdName = CmdName.CMD_CAL_FREQU_DUT_PERIOD; @@ -1256,32 +1236,25 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core response = new CmdResponse(cmdName, info, doubleValue: 1.0 / (TMR_RESOLUTION_s * period), unit: Units.GetInfo(Units.Name.FREQUENCY_Hz)); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: MeasurementInfo, - specificInfoObj: response)); + ResponseEvent(fm2014, response); // Convert to m³/h (3600 s/h) based on DUT pulse rate - fm2014.DutFlowRate_cm_per_h = 3600.0 / (period * TMR_RESOLUTION_s) / fm2014.DutPulses_per_cm; + fm2014.DutFlowRate_cm_per_h = 3600.0 / ((Double)period * TMR_RESOLUTION_s) / fm2014.DutPulses_per_cm; // Publish the calculated flow rate [m³/h] cmdName = CmdName.CMD_CAL_FLOW_DUT_PERIOD; info = GetCmdInfo(cmdName); response = new CmdResponse(cmdName, info, doubleValue: fm2014.DutFlowRate_cm_per_h, unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h)); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: MeasurementInfo, - specificInfoObj: response)); + ResponseEvent(fm2014, response); } } - // Read the REF frequency [Hz] directly and calculate the flow rate [m³/h] based on this frequency cmdName = CmdName.CMD_GET_REF_FREQU; info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr, out _) && - int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), - out var refFrequency)) + if (Read(cmdName, fm2014, out _, out var refFrequency, out _)) { // Check the threshold for OOR if (refFrequency < REF_FREQU_MIN || refFrequency > REF_FREQU_MAX) @@ -1289,25 +1262,18 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core else statusReturn = StatusReturn.Okay; // Publish the frequency in [Hz] - var response = new CmdResponse(cmdName, info, refFrequency, + var response = new CmdResponse(cmdName, info, (UInt32)refFrequency, unit: Units.GetInfo(Units.Name.FREQUENCY_Hz)); - PublishResponse(fm2014, new ProcessExecEventArgs("", - actualProcessMessage: MeasurementInfo, - statusReturn: statusReturn, - specificInfoObj: response)); + ResponseEvent(fm2014, response, statusReturn); // Convert to m³/h (3600 s/h) and save the value - fm2014.RefFlowRate_cm_per_h = 3600.0 * refFrequency / fm2014.RefPulses_per_cm; + fm2014.RefFlowRate_cm_per_h = 3600.0 * (Double)refFrequency / fm2014.RefPulses_per_cm; // Publish the calculated flow rate [m³/h] cmdName = CmdName.CMD_CAL_FLOW_REF_FREQU; info = GetCmdInfo(cmdName); response = new CmdResponse(cmdName, info, doubleValue: fm2014.RefFlowRate_cm_per_h, unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h)); - PublishResponse(fm2014, - new ProcessExecEventArgs("", - actualProcessMessage: MeasurementInfo, - statusReturn: statusReturn, - specificInfoObj: response)); + ResponseEvent(fm2014, response, statusReturn); } } } @@ -1356,7 +1322,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// 0 if double impulse deadtime detection is off /// /// - /// + /// /// - Initial. /// public static Boolean PulseTimeMeasurement(UInt16? refPulsesRequired = null, UInt16? dutPulsesRequired = null, @@ -1463,9 +1429,9 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core // Set up the progress maximal counter value base on required pulses for REF or DUT InitActualProcessProgress( - firstActiveFm2014.RefPulsesRequired != 0 - ? firstActiveFm2014.RefPulsesRequired - : firstActiveFm2014.DutPulsesRequired, disableWriteReadIncrease: true); + (Int32)firstActiveFm2014.RefPulsesRequired != 0 + ? (Int32)firstActiveFm2014.RefPulsesRequired + : (Int32)firstActiveFm2014.DutPulsesRequired, disableWriteReadIncrease: true); do { @@ -1473,24 +1439,19 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { try { - String info; String responseStr; StatusReturn statusReturn; // Read the device status to check for timeout between pulses cmdName = CmdName.CMD_GET_STATUS; - info = GetCmdInfo(cmdName); + var info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr, out _) && - int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), - out var status)) + if (Read(cmdName, fm2014, out responseStr, out var status, out _)) { info += $": {responseStr}"; var response = new CmdResponse(cmdName, info, status); - PublishResponse(fm2014, new ProcessExecEventArgs("", - actualProcessPercent: ActualProcessProgress_percent, - actualProcessMessage: MeasurementInfo, specificInfoObj: response)); + ResponseEvent(fm2014, response); } } @@ -1498,17 +1459,15 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core if (fm2014.RefPulsesRequired != 0) { // Feed the progress counter - ActualProcessProgress = firstActiveFm2014.RefPulsesRequired - + ActualProcessProgress = (Int32)firstActiveFm2014.RefPulsesRequired - firstActiveFm2014.RefPulsesRemaining; cmdName = CmdName.CMD_REF_GET_PLS_RMN; info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr, out _) && - ushort.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), - out var pulses)) + if (Read(cmdName, fm2014, out responseStr, out var pulses, out _)) { - fm2014.RefPulsesRemaining = pulses; + fm2014.RefPulsesRemaining = (UInt16)pulses; // Check if remaining pulses are going to count down based on required pulses if (RegisteredFm2014s.Any(fm => fm.RefPulsesRemaining * 1.1 < fm.RefPulsesRequired) && @@ -1523,10 +1482,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } var response = new CmdResponse(cmdName, info, pulses); - PublishResponse(fm2014, new ProcessExecEventArgs("", - actualProcessPercent: ActualProcessProgress_percent, - statusReturn: statusReturn, - actualProcessMessage: MeasurementInfo, specificInfoObj: response)); + ResponseEvent(fm2014, response, statusReturn); } } } @@ -1536,17 +1492,15 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { // Feed the progress counter but only if not done for REF pulses if (firstActiveFm2014.RefPulsesRequired == 0) - ActualProcessProgress = firstActiveFm2014.DutPulsesRequired - + ActualProcessProgress = (Int32)firstActiveFm2014.DutPulsesRequired - firstActiveFm2014.DutPulsesRemaining; cmdName = CmdName.CMD_DUT_GET_PLS_RMN; info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr, out _) && - ushort.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), - out var pulses)) + if (Read(cmdName, fm2014, out responseStr, out var pulses, out _)) { - fm2014.DutPulsesRemaining = pulses; + fm2014.DutPulsesRemaining = (UInt16)pulses; // Check if remaining pulses are going to count down based on required pulses if (RegisteredFm2014s.Any(fm => fm.DutPulsesRemaining * 1.1 < fm.DutPulsesRequired) && @@ -1561,10 +1515,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } var response = new CmdResponse(cmdName, info, pulses); - PublishResponse(fm2014, new ProcessExecEventArgs("", - actualProcessMessage: MeasurementInfo, - statusReturn: statusReturn, - specificInfoObj: response)); + ResponseEvent(fm2014, response, statusReturn); } } } @@ -1582,27 +1533,19 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr, out _) && - uint.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), - out var timerTicks)) + if (Read(cmdName, fm2014, out responseStr, out var timerTicks, out _)) { // Is converting to a real time in seconds - fm2014.RefTimerTicksMeasured = timerTicks; + fm2014.RefTimerTicksMeasured = (UInt16)timerTicks; var response = new CmdResponse(cmdName, info, doubleValue: fm2014.RefTimeMeasured_s, unit: Units.GetInfo(Units.Name.TIME_s)); - PublishResponse(fm2014, new ProcessExecEventArgs("", - actualProcessMessage: MeasurementInfo, - specificInfoObj: response)); + ResponseEvent(fm2014, response); } else { retVal = false; } } - else - { - retVal = false; - } } // Read the DUT timer ticks for the counted pulses @@ -1612,27 +1555,19 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) { - if (Read(cmdName, fm2014, out responseStr, out _) && - uint.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), - out var timerTicks)) + if (Read(cmdName, fm2014, out responseStr, out var timerTicks, out _)) { // Is converting to a real time in seconds - fm2014.DutTimerTicksMeasured = timerTicks; + fm2014.DutTimerTicksMeasured = (UInt16)timerTicks; var response = new CmdResponse(cmdName, info, doubleValue: fm2014.DutTimeMeasured_s, unit: Units.GetInfo(Units.Name.TIME_s)); - PublishResponse(fm2014, new ProcessExecEventArgs("", - actualProcessMessage: MeasurementInfo, - specificInfoObj: response)); + ResponseEvent(fm2014, response); } else { retVal = false; } } - else - { - retVal = false; - } } // Calculate the DUT to REF tolerance if both timer had been active @@ -1657,10 +1592,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var response = new CmdResponse(cmdName, info, doubleValue: fm2014.DutToRefToleranceMeasured_percent, unit: Units.GetInfo(Units.Name.PERCENT)); - PublishResponse(fm2014, new ProcessExecEventArgs("", - actualProcessMessage: MeasurementInfo, - statusReturn: statusReturn, - specificInfoObj: response)); + ResponseEvent(fm2014, response, statusReturn); } } @@ -1692,6 +1624,55 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core return true; } + /// + /// Common routine to parse the response from the FM2014 + /// + /// + /// + /// + /// + /// + /// + /// - Initial. + /// + private static StatusReturn ParseResponse(CmdName cmdName, String responseStr, out UInt32? intValue, + out Double? doubleValue) + { + intValue = null; + doubleValue = null; + var statusReturn = StatusReturn.Unknown; + var numberStyle = NumberStyles.None; + var paraFormat = GetParaFormat(cmdName); + if (paraFormat == ParaFormat.UINT_HEX || paraFormat == ParaFormat.BYTE_HEX_PLUS_SIGN) + { + numberStyle = NumberStyles.HexNumber; + } + + switch (paraFormat) + { + case ParaFormat.BYTE_HEX_PLUS_SIGN: + // The response contains a sign! + var signMultiplier = responseStr.Contains("+") ? 1.0 : -1.0; + var cleanedStr = responseStr.Replace("+", "").Replace("-", ""); + if (uint.TryParse(cleanedStr, numberStyle, new CultureInfo("en"), out var intValueParsed)) + { + doubleValue = intValueParsed * signMultiplier; + } + break; + + case ParaFormat.UINT_HEX: + case ParaFormat.UINT_DEZ: + if (uint.TryParse(responseStr, numberStyle, new CultureInfo("en"), out intValueParsed)) + { + intValue = intValueParsed; + statusReturn = StatusReturn.Okay; + } + break; + } + + return statusReturn; + } + /// /// Read FM2014: /// - Accesses directly to the 'SerialPort.ReadTo', @@ -1701,14 +1682,17 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// output of pure pure raw data response from FM2014 /// output of parsed response to integer value + /// /// - /// + /// /// - Initial. /// - private static Boolean Read(CmdName cmdName, FM2014 fm2014, out String responseStr, out Int32? intValue) + private static Boolean Read(CmdName cmdName, FM2014 fm2014, out String responseStr, out UInt32? intValue, + out Double? doubleValue) { responseStr = ""; intValue = null; + doubleValue = null; var info = $"{Resources.StrCommResponse}: {GetCmdInfo(cmdName)}"; var statusReturn = StatusReturn.Failed; if (!DisableWriteReadActualProcessIncreasing) @@ -1738,19 +1722,11 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var isErrorStr = StatusReturn.Failed == statusReturn ? Resources.StrError + ": " : ""; var response = new CmdResponse(cmdName, $"{isErrorStr}{info} {responseStr}"); - PublishResponse(fm2014, new ProcessExecEventArgs("", specificInfoObj: response, statusReturn: statusReturn)); - - var numberStyle = NumberStyles.None; - if (GetParaFormat(cmdName) == ParaFormat.INT_HEX) - { - numberStyle = NumberStyles.HexNumber; - } - - if (int.TryParse(responseStr, numberStyle, new CultureInfo("en"), out var intParsedResponse)) - { - intValue = intParsedResponse; - } + // Publish the raw response for logging + ResponseEvent(fm2014, response, statusReturn); + // Parse the raw value to get a useful result + statusReturn = ParseResponse(cmdName, responseStr, out intValue, out doubleValue); return StatusReturn.Failed != statusReturn; } @@ -1768,7 +1744,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// /// - /// + /// /// - Initial. /// private static Boolean Write(CmdName cmdName, FM2014 fm2014, Object dataObj = null) @@ -1814,7 +1790,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var cmdCodeStr = GetCmdStr(cmdName); var info = GetCmdInfo(cmdName) + $": {cmdCodeStr}"; - var isHexFormat = GetParaFormat(cmdName) == ParaFormat.INT_HEX; + var isHexFormat = GetParaFormat(cmdName) == ParaFormat.UINT_HEX; CmdResponse response; // Without data the command alone needs to be sent if (dataObj == null) @@ -1835,7 +1811,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } // The data contains the preceding setup, the command code and string delimiter will be added here - else if (dataObj is UInt16 || dataObj is Byte) + else if (dataObj is UInt16 || dataObj is UInt32 || dataObj is Byte) { var valueStr = isHexFormat ? $"{dataObj:X}{cmdCodeStr}" : $"{dataObj}{cmdCodeStr}"; SharedSerialPort.Write($"{valueStr}{END_OF_STR}"); @@ -1853,7 +1829,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } var statusReturn = retVal ? StatusReturn.Okay : StatusReturn.Failed; - PublishResponse(fm2014, new ProcessExecEventArgs("", specificInfoObj: response, statusReturn: statusReturn)); + ResponseEvent(fm2014, response, statusReturn); + return retVal; } @@ -2095,7 +2072,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } try { - var measurementInfoStr = Resources.StrMeasMsgStanaloneRegulation; + MeasurementInfo = Resources.StrMeasMsgStanaloneRegulation; var responseStrs = new List(); if (Write(cmdName, this)) { @@ -2105,7 +2082,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var loopCtr = 4; do { - if (Read(cmdName, this, out var responseStr, out _)) + if (Read(cmdName, this, out var responseStr, out _, out _)) responseStrs.Add(responseStr); } while (loopCtr-- > 0); } @@ -2115,7 +2092,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var loopCtr = 5; do { - if (Read(cmdName, this, out var responseStr, out _)) + if (Read(cmdName, this, out var responseStr, out _, out _)) responseStrs.Add(responseStr); } while (loopCtr-- > 0); } @@ -2126,14 +2103,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { var strCleaned = Regex.Replace(str, "[^0-9]", string.Empty); if (byte.TryParse(strCleaned, out var attenuation) && - attenuation > 0 && attenuation <= 9) + attenuation >= CURRENT_OUTPUT_ATTENUATION_MIN && + attenuation <= CURRENT_OUTPUT_ATTENUATION_MAX) { cmdName = CmdName.CMD_MEAS_SET_ATTN; var info = GetCmdInfo(cmdName); CurrentOutputAttenuation = attenuation; var response = new CmdResponse(cmdName, info, CurrentOutputAttenuation); - PublishResponse(this, new ProcessExecEventArgs("", - actualProcessMessage: measurementInfoStr, specificInfoObj: response)); + ResponseEvent(this, response); } } if (str.Contains(StandAlonePartSearchScaleStr)) @@ -2146,9 +2123,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var info = GetCmdInfo(cmdName); RefToDutScale_norm = value; var response = new CmdResponse(cmdName, info, doubleValue: RefToDutScale_norm); - PublishResponse(this, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr, - specificInfoObj: response)); + ResponseEvent(this, response); } } @@ -2161,18 +2136,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var info = GetCmdInfo(cmdName); DutPulses_per_cm = value; var response = new CmdResponse(cmdName, info, DutPulses_per_cm); - PublishResponse(this, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr, - specificInfoObj: response)); + ResponseEvent(this, response); // Added 2026-Jan-01 to overcome limitation of max 9999 Impulses per volume cmdName = CmdName.CMD_REF_LPP_SCALE; info = GetCmdInfo(cmdName); // Calculate REF pulses per cubic meter RefPulses_per_cm = (UInt32)Math.Round(DutPulses_per_cm * RefToDutScale_norm, 1); - response = new CmdResponse(cmdName, info, (Int32)RefPulses_per_cm); - PublishResponse(this, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr, - specificInfoObj: response)); + response = new CmdResponse(cmdName, info, RefPulses_per_cm); + ResponseEvent(this, response); } } } @@ -2294,7 +2265,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core IsLoggedOn = true; - if (Read(cmdName, this, out var responseStr, out _)) + if (Read(cmdName, this, out var responseStr, out _, out _)) SerialNumber = responseStr; if (!string.IsNullOrEmpty(SerialNumber)) @@ -2309,7 +2280,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core return false; } - if (Read(cmdName, this, out responseStr, out _)) + if (Read(cmdName, this, out responseStr, out _, out _)) { var cleanedResponse = Regex.Replace(responseStr, "[^0-9]", string.Empty); if (int.TryParse(cleanedResponse, out var intValue)) diff --git a/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs b/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs index 6caa69d8..8e93b3e4 100644 --- a/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs +++ b/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs @@ -655,8 +655,8 @@ namespace Sensus.Ui.FM2014TestBench if (null == firstFm2014) return; - _fm2014Config.RefPulsesRequired = firstFm2014.RefPulsesRequired; - _fm2014Config.DutPulsesRequired = firstFm2014.DutPulsesRequired; + _fm2014Config.RefPulsesRequired = (UInt16)firstFm2014.RefPulsesRequired; + _fm2014Config.DutPulsesRequired = (UInt16)firstFm2014.DutPulsesRequired; _fm2014Config.DoublePulseDeadtime = firstFm2014.DoublePulseDeadtime_ms; _fm2014Config.DutToRefToleranceMax = firstFm2014.DutToRefCalibrationToleranceMax_percent; _fm2014Config.DutToRefToleranceMin = firstFm2014.DutToRefCalibrationToleranceMin_percent; @@ -1100,7 +1100,7 @@ namespace Sensus.Ui.FM2014TestBench UpdateContentControl(lblActualProcessText, e.ActualProcessMessage); } - if (e.ActualProcessPercent != null) + if (e.ActualProcessPercent != null && !_autoProgressBar) { UpdateProgressBar(pbActualProgress, (Double)e.ActualProcessPercent); } From 312195ef8606737fe79a199c07f1d8a38aa5f42f Mon Sep 17 00:00:00 2001 From: Thomas Wiedebusch Date: Sat, 14 Feb 2026 11:17:10 +0100 Subject: [PATCH 7/7] FM2014: - improvements --- .../FM2014/FM2014Core/FM2014.cs | 25 +++++++++++-------- .../Fm2014sTest/FM2014TestBenchWindow.xaml.cs | 18 +++++++------ 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs index 78e71fa9..df224b43 100644 --- a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs +++ b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs @@ -814,6 +814,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core DisableWriteReadActualProcessIncreasing = disableWriteReadIncrease; MaxActualProcessProgress = 1; ActualProcessProgress = 0; + ActualProcessProgress_percent = null; if (maxInitProcesses != null) { @@ -1363,6 +1364,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core fm2014.ErrorIsDetected = false; } + var cmdName = CmdName.CMD_NA; MeasurementInfo = Resources.StrMeasMsgCalibration; @@ -1372,7 +1374,6 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core // Check if double pulse deadtime has been set or si switched off if (DOUBLE_PULSE_DEADTIME_MIN_ms == firstActiveFm2014.DoublePulseDeadtime_ms) { - InitActualProcessProgress(3, broadcast: true); // This is a command without parameter cmdName = CmdName.CMD_SET_DBPL_UNLOCK; if (!Write(cmdName, firstActiveFm2014)) @@ -1382,7 +1383,6 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } else { - InitActualProcessProgress(4, broadcast: true); cmdName = CmdName.CMD_SET_DPLS_LOCK_TMR; if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.DoublePulseBaseDeadTime_ms)) { @@ -1458,9 +1458,6 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core // Read the remaining REF pulses if required if (fm2014.RefPulsesRequired != 0) { - // Feed the progress counter - ActualProcessProgress = (Int32)firstActiveFm2014.RefPulsesRequired - - firstActiveFm2014.RefPulsesRemaining; cmdName = CmdName.CMD_REF_GET_PLS_RMN; info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) @@ -1483,6 +1480,12 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var response = new CmdResponse(cmdName, info, pulses); ResponseEvent(fm2014, response, statusReturn); + + // Feed the progress counter but only if not done for DUT pulses as with the first + // DUT pulse the RefPulsesRemaining will be reset to the initial value + if (firstActiveFm2014.DutPulsesRequired == 0) + ActualProcessProgress = (Int32)firstActiveFm2014.RefPulsesRequired - + firstActiveFm2014.RefPulsesRemaining; } } } @@ -1490,10 +1493,6 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core // Read the remaining DUT pulses if required if (fm2014.DutPulsesRequired != 0) { - // Feed the progress counter but only if not done for REF pulses - if (firstActiveFm2014.RefPulsesRequired == 0) - ActualProcessProgress = (Int32)firstActiveFm2014.DutPulsesRequired - - firstActiveFm2014.DutPulsesRemaining; cmdName = CmdName.CMD_DUT_GET_PLS_RMN; info = GetCmdInfo(cmdName); if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) @@ -1516,6 +1515,10 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var response = new CmdResponse(cmdName, info, pulses); ResponseEvent(fm2014, response, statusReturn); + + // Feed the progress counter + ActualProcessProgress = (Int32)firstActiveFm2014.DutPulsesRequired - + firstActiveFm2014.DutPulsesRemaining; } } } @@ -1830,7 +1833,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var statusReturn = retVal ? StatusReturn.Okay : StatusReturn.Failed; ResponseEvent(fm2014, response, statusReturn); - + return retVal; } @@ -2103,7 +2106,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { var strCleaned = Regex.Replace(str, "[^0-9]", string.Empty); if (byte.TryParse(strCleaned, out var attenuation) && - attenuation >= CURRENT_OUTPUT_ATTENUATION_MIN && + attenuation >= CURRENT_OUTPUT_ATTENUATION_MIN && attenuation <= CURRENT_OUTPUT_ATTENUATION_MAX) { cmdName = CmdName.CMD_MEAS_SET_ATTN; diff --git a/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs b/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs index 8e93b3e4..734e3b17 100644 --- a/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs +++ b/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml.cs @@ -759,7 +759,7 @@ namespace Sensus.Ui.FM2014TestBench /// /// Common method to (de-)activate controls and timer. /// - /// + /// /// - Initial. /// private void ActionControl(Boolean isActive) @@ -771,7 +771,7 @@ namespace Sensus.Ui.FM2014TestBench //UiElmEnable(lblTotalProcessText, true); //UiElmEnable(lblActualProcessLabel, true); //UiElmEnable(lblTotalProcessLabel, true); - UiElmEnable(lblActualProgressPercent, true); + UiElmEnable(lblActualProgressPercent, !_autoProgressBar, !_autoProgressBar); //UiElmEnable(lblTotalProgressPercent, true); UiElmEnable(pbActualProgress, true); //UiElmEnable(pbTotalProgress, true); @@ -1076,7 +1076,7 @@ namespace Sensus.Ui.FM2014TestBench /// /// Feedback from FM2014being parsed to GUI /// - /// + /// /// - Initial. /// private void DataReceived_Handler(Object sender, ProcessExecEventArgs e) @@ -1105,7 +1105,10 @@ namespace Sensus.Ui.FM2014TestBench UpdateProgressBar(pbActualProgress, (Double)e.ActualProcessPercent); } - Dispatcher.Invoke(() => lblActualProgressPercent.Content = $@"{pbActualProgress.Value:##0.0} %"); + // A circulating process bar will be used to observe an activity, but as it restarts at the beginning + // after reaching the end, a value isn't of importance + if (!_autoProgressBar) + Dispatcher.Invoke(() => lblActualProgressPercent.Content = $@"{pbActualProgress.Value:##0.0} %"); SetTimeDisplay(); @@ -1165,6 +1168,7 @@ namespace Sensus.Ui.FM2014TestBench case CmdName.CMD_RST_MEAS: // Immediately lock all buttons and input fields until reset is finished SetFM2014AccessLocked(); + UiElmEnable(lblActualProgressPercent, true); // Check countdown of reset method if (resp.IntValue == 0) ActionControl(false); @@ -1342,7 +1346,7 @@ namespace Sensus.Ui.FM2014TestBench // Backup if meanwhile anything has changed if (_configSetupHasChanged || _calibrationSetupHasChanged) StoreFM2014SettingsToConfigFile(); - + _autoProgressBar = false; ActionControl(true); UpdateContentControl(btnDutToRefCalibration, Properties.Resources.StrBtnStopCalibration); @@ -1492,8 +1496,6 @@ namespace Sensus.Ui.FM2014TestBench var idx = fm2014.Address - 1; var ucFM2014 = (UcFM2014Device)UcFM2014s[idx]; ucFM2014.EnableMeasurements(CTR_MEASURE_COUNT); - //fm2014.RefPulseCtrOn = true; - //fm2014.DutPulseCtrOn = measureDutPulses; } if (FM2014.PulseCounterMeasurement(true, measureDutPulses)) @@ -2353,7 +2355,7 @@ namespace Sensus.Ui.FM2014TestBench { fm2014.DutPulses_per_cm = (UInt16)pulses_per_cm; } - + if (!UpdateRefToDutScale()) { tbxDutPulsesPerCm.Background = ColorProcessFailed;