From 0272761db8d57d208d64054ffdaec97afc665a9f Mon Sep 17 00:00:00 2001 From: Thomas Wiedebusch Date: Fri, 13 Feb 2026 10:42:33 +0100 Subject: [PATCH] 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);