FM2014: - measurement interpretation

This commit is contained in:
Thomas Wiedebusch 2026-02-13 10:42:33 +01:00
parent 26dc9dbe45
commit 0272761db8
4 changed files with 68 additions and 38 deletions

View File

@ -28,6 +28,11 @@
/// <summary>
/// inspection needed as warning returns
/// </summary>
Warning
Warning,
/// <summary>
/// the measurement is out of range
/// </summary>
MeasurementOutOfRange
}
}

View File

@ -507,9 +507,9 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co
private const String UNICODE_DC2 = "\u0013";
/// <summary>
/// Complete command table
/// Command table for communication with FM2014 combines the name with the command code and additional info.
/// </summary>
public static List<Cmd> CmdTbl = new List<Cmd>
public static readonly List<Cmd> CmdTbl = new List<Cmd>
{
// FM2014 control and information
new Cmd(CmdName.CMD_CONNECT, "", CmdType.INDIVIDUAL, Resources.StrCmdMsgConnect),

View File

@ -299,12 +299,12 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// <summary>
/// REF pulse counter is switched active
/// </summary>
public Boolean RefPulseCtrOn { set; get; }
private Boolean RefPulseCtrOn { set; get; }
/// <summary>
/// DUT pulse counter is switched active
/// </summary>
public Boolean DutPulseCtrOn { set; get; }
private Boolean DutPulseCtrOn { set; get; }
/// <summary>
/// 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
/// <remarks date="2026-Jan-14..Feb-11" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
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));
}
}

View File

@ -1076,7 +1076,7 @@ namespace Sensus.Ui.FM2014TestBench
/// <summary>
/// Feedback from FM2014being parsed to GUI
/// </summary>
/// <remarks date="2026-Feb-02..10" author="Thomas Wiedebusch">
/// <remarks date="2026-Feb-02..13" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
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);