FM2014TestBench: - initial setup to test multiple FM2014 - 14.Step

This commit is contained in:
Thomas Wiedebusch 2026-02-11 09:53:00 +01:00
parent e0108493e4
commit d39c71c7e7
3 changed files with 354 additions and 271 deletions

View File

@ -387,7 +387,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
public UInt16 RefPulsesRequired
{
get => _refPulsesRequired;
set
private set
{
// Check limits and equality
if (value < TIME_MEASUREMENT_INPUT_MIN_pulses ||
@ -405,7 +405,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
public UInt16 DutPulsesRequired
{
get => _dutPulsesRequired;
set
private set
{
// Check limits and equality
if (value < TIME_MEASUREMENT_INPUT_MIN_pulses ||
@ -475,46 +475,46 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// </summary>
public Double DutTimeMeasured_s { get; private set; }
private UInt32 _ref_pulse_per_cm;
private UInt32 _refPulses_per_cm;
/// <summary>
/// Reference pulses per cubic-meter
/// </summary>
public UInt32 Ref_pulse_per_cm
public UInt32 RefPulses_per_cm
{
get => _ref_pulse_per_cm;
get => _refPulses_per_cm;
set
{
// Check limits and equality
if (value < PULSES_PER_CM_REGULATION_SETUP_MIN ||
value > REF_PULSES_PER_CM_REGULATION_INPUT_MAX ||
value == _ref_pulse_per_cm)
value == _refPulses_per_cm)
return;
_ref_pulse_per_cm = value;
if (_dut_pulse_per_cm != 0 && _ref_pulse_per_cm != 0)
RefToDutScale_norm = Math.Round((Double)_ref_pulse_per_cm / _dut_pulse_per_cm, 5);
_refPulses_per_cm = value;
if (_dutPulses_per_cm != 0 && _refPulses_per_cm != 0)
RefToDutScale_norm = Math.Round((Double)_refPulses_per_cm / _dutPulses_per_cm, 5);
}
}
private UInt16 _dut_pulse_per_cm;
private UInt16 _dutPulses_per_cm;
/// <summary>
/// Device under test pulses per cubic-meter
/// </summary>
public UInt16 Dut_pulse_per_cm
public UInt16 DutPulses_per_cm
{
get => _dut_pulse_per_cm;
set
get => _dutPulses_per_cm;
private set
{
// Check limits and equality, the DUT max input is 9999 as this will be
// directly stored to FM2014'V' - 'DUT pulses per volume'
if (value < PULSES_PER_CM_REGULATION_SETUP_MIN ||
value > PULSES_PER_CM_REGULATION_SETUP_MAX ||
value == _dut_pulse_per_cm)
value == _dutPulses_per_cm)
return;
_dut_pulse_per_cm = value;
if (_dut_pulse_per_cm != 0 && _ref_pulse_per_cm != 0)
RefToDutScale_norm = Math.Round((Double)_ref_pulse_per_cm / _dut_pulse_per_cm, 5);
_dutPulses_per_cm = value;
if (_dutPulses_per_cm != 0 && _refPulses_per_cm != 0)
RefToDutScale_norm = Math.Round((Double)_refPulses_per_cm / _dutPulses_per_cm, 5);
}
}
@ -570,20 +570,20 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
}
}
private Byte _fm2014CurrentOutputAttenuation = 3;
private Byte _currentOutputAttenuation = 3;
/// <summary>
/// Filter value to attenuate the velocity of the current output on the tolerance display.
/// This value will be sent to the FM2014
/// </summary>
public Byte FM2014CurrentOutputAttenuation
public Byte CurrentOutputAttenuation
{
get => _fm2014CurrentOutputAttenuation;
set
get => _currentOutputAttenuation;
private set
{
if (value < ATTENUATION_MIN || value > ATTENUATION_MAX)
return;
_fm2014CurrentOutputAttenuation = value;
_currentOutputAttenuation = value;
}
}
@ -594,7 +594,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
public UInt16 DoublePulseDeadtime_ms
{
get => _doublePulseDeadtime_ms;
set
private set
{
if (value > DOUBLE_PULSE_DEADTIME_MAX_ms)
return;
@ -669,6 +669,11 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// </summary>
public Boolean MeasurementIsReady { get; private set; }
/// <summary>
/// The FM2014 has a misfunction
/// </summary>
public Boolean ErrorIsDetected { get; private set; }
/// <summary>
/// FM2014 application firmware version.
/// </summary>
@ -693,18 +698,9 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// - It is a broadcast command which will reset all connected devices!
/// </summary>
/// <returns></returns>
/// <remarks date="2026-Jan-12..14" author="Thomas Wiedebusch">
/// <remarks date="2026-Jan-12..Feb-10" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2026-Jan-27" author="Thomas Wiedebusch">
/// - Delayed reset to allow other tasks to finish.
/// </remarks>
/// <remarks date="2026-Jan-28..30" author="Thomas Wiedebusch">
/// - Support for multiple FM2014s.
/// </remarks>
/// <remarks date="2026-Feb-05" author="Thomas Wiedebusch">
/// - Statics.
/// </remarks>
public static Boolean ResetHardwareAllDevices()
{
if (SharedCyclicMeasSequ == CyclicMeasSequ.IDLE)
@ -842,7 +838,11 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// - REF counter,
/// - DUT counter,
/// - REF and DUT counters together using the synchronized backup.
/// - Each FM2014 can use a different REF and/or DUT setting as the REF is parallel for all FM2014s.
/// - If REF and DUT pulse counters are on the cyclic sequence:
/// - 'q' synchronized backup of both pulse counters,
/// - 'l' read REF backup pulses,
/// - 'u' read DUT backup pulses
/// will be used.
/// Preconditions:
/// - The <see cref="ResetHardwareAllDevices"/> has to be executed in advance.
/// - REF and/or DUT counter have to be activated
@ -853,29 +853,15 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// Exit:
/// - Set <see cref="SharedCyclicMeasSequ"/> to false.
/// </summary>
/// <param name="refPulseCtrOn"></param>
/// <param name="dutPulseCtrOn"></param>
/// <returns>true if initialization successfully executed or
/// <see cref="SharedCyclicMeasSequ"/> marks the already started cyclic requests
/// for all registered FM2014s</returns>
/// <remarks date="2026-Jan-10..14" author="Thomas Wiedebusch">
/// <remarks date="2026-Jan-10..Feb-11" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2026-Jan-27" author="Thomas Wiedebusch">
/// - If REF and DUT pulse counters are on the cyclic sequence:
/// - 'q' synchronized backup of both pulse counters,
/// - 'l' read REF backup pulses,
/// - 'u' read DUT backup pulses
/// will be used.
/// </remarks>
/// <remarks date="2026-Jan-28..29" author="Thomas Wiedebusch">
/// - Support for multiple FM2014s.
/// </remarks>
/// <remarks date="2026-Jan-31" author="Thomas Wiedebusch">
/// - If any FM2014 has REF and DUT pulse counter on then use for ALL the synchronized readout.
/// </remarks>
/// <remarks date="2026-Feb-09" author="Thomas Wiedebusch">
/// - Static.
/// </remarks>
public static Boolean PulseCounterMeasurement()
public static Boolean PulseCounterMeasurement(Boolean refPulseCtrOn, Boolean dutPulseCtrOn)
{
// If the cyclic task has already been started everything is fine
if (SharedCyclicMeasSequ == CyclicMeasSequ.PULSE_CTR)
@ -1026,7 +1012,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
}
/// <summary>
/// The regulation measurement compares the DUT to REF tolerance for all FM2014s which are connected and logged in.
/// The regulation measurement compares the DUT to REF tolerance for all FM2014s which are
/// connected and logged in.
///
/// ATTENTION:
/// The settings from the first listed FM2014 will be used for ALL FM2014s!
@ -1034,9 +1021,6 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
///
/// Preconditions:
/// - The <see cref="ResetHardwareAllDevices"/> has to be executed in advance,
/// - The <see cref="Ref_pulse_per_cm"/>REF pulses per cubic meter has to be preset,
/// - The <see cref="Dut_pulse_per_cm"/>DUT pulses per cubic meter has to be preset,
/// - The <see cref="RefToDutScale_norm"/>Scale for REF to DUT pulses has to be preset.
/// Initial setup:
/// - Setup double impulse deadtime,
/// - Setup damping to attenuate the result,
@ -1044,22 +1028,21 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// - Setup REF pulse counting mode to get REF pulses for flow rate.
/// Cyclic:
/// - Request damped or undamped tolerance,
/// - Request REF pulse counter value and uses <see cref="Ref_pulse_per_cm"/> to calculate
/// - Request REF pulse counter value and uses <see cref="RefPulses_per_cm"/> to calculate
/// <see cref="RefFlowRate_cm_per_h"/>the 'Flow rate'.
/// Exit:
/// - Set <see cref="SharedCyclicMeasSequ"/> to false.
/// </summary>
/// <param name="refPulses_per_cm"></param>
/// <param name="dutPulses_per_cm"></param>
/// <param name="doublePulseDeadtime_ms"></param>
/// <param name="currentOutputAttenuation"></param>
/// <returns></returns>
/// <remarks date="2026-Jan-14..21" author="Thomas Wiedebusch">
/// <remarks date="2026-Jan-14..Feb-11" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2026-Jan-28..29" author="Thomas Wiedebusch">
/// - Support for multiple FM2014s.
/// </remarks>
/// <remarks date="2026-Feb-05" author="Thomas Wiedebusch">
/// - Static.
/// </remarks>
public static Boolean RegulationMeasurement()
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
if (SharedCyclicMeasSequ == CyclicMeasSequ.REGULATION)
@ -1069,22 +1052,36 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE)
return false;
// Initially, check if any device is connected and logged in, if not => exit
var firstActiveFm2014 = GetFirstConnectedAndLoggedInFm2014();
if (firstActiveFm2014 == null)
return false;
// Publish setting to all connected and logged in FM2014s as those will be delivered
// for all via broadcast
foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn))
{
fm2014.RefPulses_per_cm = refPulses_per_cm;
fm2014.DutPulses_per_cm = dutPulses_per_cm;
fm2014.DoublePulseDeadtime_ms = doublePulseDeadtime_ms;
fm2014.CurrentOutputAttenuation = currentOutputAttenuation;
// Remove the measurement ready lock used for cyclic request
fm2014.MeasurementIsReady = false;
// Remove error marker
fm2014.ErrorIsDetected = false;
}
// Check if regulation can be done
if (firstActiveFm2014.Ref_pulse_per_cm < PULSES_PER_CM_REGULATION_SETUP_MIN ||
firstActiveFm2014.Dut_pulse_per_cm < PULSES_PER_CM_REGULATION_SETUP_MIN ||
firstActiveFm2014.Ref_pulse_per_cm > PULSES_PER_CM_REGULATION_SETUP_MAX ||
firstActiveFm2014.Dut_pulse_per_cm > PULSES_PER_CM_REGULATION_SETUP_MAX ||
if (refPulses_per_cm < PULSES_PER_CM_REGULATION_SETUP_MIN ||
dutPulses_per_cm < PULSES_PER_CM_REGULATION_SETUP_MIN ||
refPulses_per_cm > REF_PULSES_PER_CM_REGULATION_INPUT_MAX ||
dutPulses_per_cm > PULSES_PER_CM_REGULATION_SETUP_MAX ||
firstActiveFm2014.RefToDutScale_norm < REF_TO_DUT_SCALE_MIN ||
firstActiveFm2014.RefToDutScale_norm > REF_TO_DUT_SCALE_MAX)
{
return false;
}
var cmdName = CmdName.CMD_NA;
var measurementInfo = Resources.StrMeasMsgRegulation;
// Prepare regulation measurement
@ -1097,7 +1094,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
}
cmdName = CmdName.CMD_MEAS_SET_ATTN;
if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.FM2014CurrentOutputAttenuation))
if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.CurrentOutputAttenuation))
{
return false;
}
@ -1180,7 +1177,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
specificInfoObj: 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.Ref_pulse_per_cm;
fm2014.RefFlowRate_cm_per_h = 3600.0 / (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);
@ -1222,7 +1219,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
specificInfoObj: 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.Dut_pulse_per_cm;
fm2014.DutFlowRate_cm_per_h = 3600.0 / (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);
@ -1252,7 +1249,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
specificInfoObj: response));
// Convert to m³/h (3600 s/h) and save the value
fm2014.RefFlowRate_cm_per_h = 3600.0 * refFrequency / fm2014.Ref_pulse_per_cm;
fm2014.RefFlowRate_cm_per_h = 3600.0 * refFrequency / fm2014.RefPulses_per_cm;
// Publish the calculated flow rate [m³/h]
cmdName = CmdName.CMD_CAL_FLOW_REF_FREQU;
info = GetCmdInfo(cmdName);
@ -1279,40 +1276,41 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
}
/// <summary>
/// The calibration measurement compares the DUT to REF tolerance for all FM2014s which are connected and logged in.
/// The pulse time measurement counts the timer ticks for the REF and/or DUT for all connected and
/// logged in FM2014.
///
/// It supports:
/// - REF time measurement alone,
/// - DUT time measurement alone,
/// - REF and DUT time measurement synchronized to the rising edge of first impulse after setup of
/// required DUT pulses.
///
/// ATTENTION:
/// The settings from the first listed FM2014 will be used for ALL FM2014s!
/// The settings will be used for ALL FM2014s!
/// These settings will be delivered via broadcast to all connected and logged in devices!
///
/// Preconditions:
/// - The <see cref="ResetHardwareAllDevices"/> has to be executed in advance,
/// - The <see cref="RefPulsesRequired"/>REF pulses to count has to be preset,
/// - The <see cref="DutPulsesRequired"/>DUT pulses to count has to be preset,
/// - Setup double impulse deadtime using the <see cref="DoublePulseDeadtime_ms"/> whichautomatically
/// calculates the <see cref="DoublePulseMultiplier"/> and <see cref="DoublePulseBaseDeadTime_ms"/>.
/// Initial setup:
/// - Setup double pulse deadtime,
/// - Setup REF pulses to count,
/// - Setup DUT pulses to count.
/// - Setup REF pulses required to count down and measure the time of it,
/// - Setup DUT pulses required to count down and measure the time of it.
/// Cyclic:
/// - Request damped or undamped tolerance,
/// - Request REF pulse counter value and uses <see cref="Ref_pulse_per_cm"/> to calculate
/// <see cref="RefFlowRate_cm_per_h"/>the 'Flow rate'.
/// - Request remaining pulses for REF and DUT.
/// Final:
/// - Read out timer ticks for REF and/or DUT and convert it to a time in seconds.
/// Exit:
/// - Set <see cref="SharedCyclicMeasSequ"/> to false.
/// - Set <see cref="SharedCyclicMeasSequ"/> to Idle.
/// <param name="refPulsesRequired">null if not active, limited by number format to 65535</param>
/// <param name="dutPulsesRequired">null if not active, limited by number format to 65535</param>
/// <param name="doublePulseDeadtime_ms">0 if double impulse deadtime detection is off</param>
/// </summary>
/// <returns></returns>
/// <remarks date="2026-Jan-14..21" author="Thomas Wiedebusch">
/// <remarks date="2026-Jan-14..Feb-11" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2026-Jan-28..29" author="Thomas Wiedebusch">
/// - Support for multiple FM2014s.
/// </remarks>
/// <remarks date="2026-Feb-05..09" author="Thomas Wiedebusch">
/// - Static.
/// </remarks>
public static Boolean CalibrationMeasurement()
public static Boolean PulseTimeMeasurement(UInt16? refPulsesRequired = null, UInt16? dutPulsesRequired = null,
UInt16 doublePulseDeadtime_ms = 0)
{
// If the cyclic task has already been started everything is fine
if (SharedCyclicMeasSequ == CyclicMeasSequ.CALIBRATION)
@ -1322,25 +1320,41 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE)
return false;
// Initially, check if any device is connected and logged in, if not => exit
var firstActiveFm2014 = GetFirstConnectedAndLoggedInFm2014();
if (firstActiveFm2014 == null)
return false;
// Check if regulation can be done
if (firstActiveFm2014.RefPulsesRequired < TIME_MEASUREMENT_INPUT_MIN_pulses ||
firstActiveFm2014.DutPulsesRequired < TIME_MEASUREMENT_INPUT_MIN_pulses)
if ((refPulsesRequired == null && dutPulsesRequired == null) ||
refPulsesRequired < TIME_MEASUREMENT_INPUT_MIN_pulses ||
dutPulsesRequired < TIME_MEASUREMENT_INPUT_MIN_pulses ||
doublePulseDeadtime_ms > DOUBLE_PULSE_DEADTIME_MAX_ms)
{
return false;
}
// Publish setting to all connected and logged in FM2014s as those will be delivered
// for all via broadcast
foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn))
{
fm2014.RefPulsesRequired = refPulsesRequired ?? 0;
fm2014.DutPulsesRequired = dutPulsesRequired ?? 0;
fm2014.DoublePulseDeadtime_ms = doublePulseDeadtime_ms;
// Remove the measurement ready lock used for cyclic request
fm2014.MeasurementIsReady = false;
// Remove error marker
fm2014.ErrorIsDetected = false;
}
var cmdName = CmdName.CMD_NA;
var measurementInfo = Resources.StrMeasMsgCalibration;
// Prepare calibration measurement
// Prepare pulse time measurement
try
{
// Check if double pulse deadtime has been set
if (Math.Abs(DOUBLE_PULSE_DEADTIME_MIN_ms - firstActiveFm2014.DoublePulseDeadtime_ms) < 0.9)
// 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
@ -1365,15 +1379,22 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
}
}
cmdName = CmdName.CMD_REF_SET_PLS;
if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.RefPulsesRequired, true))
if (firstActiveFm2014.RefPulsesRequired != 0)
{
return false;
cmdName = CmdName.CMD_REF_SET_PLS;
if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.RefPulsesRequired, true))
{
return false;
}
}
cmdName = CmdName.CMD_DUT_SET_PLS;
if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.DutPulsesRequired, true))
if (firstActiveFm2014.DutPulsesRequired != 0)
{
return false;
cmdName = CmdName.CMD_DUT_SET_PLS;
if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.DutPulsesRequired, true))
{
return false;
}
}
}
catch (Exception e)
@ -1389,14 +1410,12 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
// Mark calibration measurement as active
SharedCyclicMeasSequ = CyclicMeasSequ.CALIBRATION;
InitActualProcessProgress(firstActiveFm2014.RefPulsesRequired, disableWriteReadIncrease: true);
// Remove all measurement ready markers as those are taken to lock the repeated readout of
// the measurement results
foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn))
{
fm2014.MeasurementIsReady = false;
}
// 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);
do
{
@ -1404,35 +1423,53 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
try
{
ActualProcessProgress = firstActiveFm2014.RefPulsesRequired - firstActiveFm2014.RefPulsesRemaining;
cmdName = CmdName.CMD_REF_GET_PLS_RMN;
var info = GetCmdInfo(cmdName);
String info;
String responseStr;
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014))
// Read the remaining REF pulses if required
if (firstActiveFm2014.RefPulsesRequired != 0)
{
if (Read(cmdName, fm2014, out responseStr) &&
ushort.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var pulses))
// Feed the progress counter
ActualProcessProgress = firstActiveFm2014.RefPulsesRequired -
firstActiveFm2014.RefPulsesRemaining;
cmdName = CmdName.CMD_REF_GET_PLS_RMN;
info = GetCmdInfo(cmdName);
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014))
{
fm2014.RefPulsesRemaining = pulses;
var response = new CmdResponse(cmdName, info, pulses);
PublishResponse(fm2014, new ProcessExecEventArgs("",
actualProcessPercent: ActualProcessProgress_percent,
actualProcessMessage: measurementInfo, specificInfoObj: response));
if (Read(cmdName, fm2014, out responseStr) &&
ushort.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"),
out var pulses))
{
fm2014.RefPulsesRemaining = pulses;
var response = new CmdResponse(cmdName, info, pulses);
PublishResponse(fm2014, new ProcessExecEventArgs("",
actualProcessPercent: ActualProcessProgress_percent,
actualProcessMessage: measurementInfo, specificInfoObj: response));
}
}
}
cmdName = CmdName.CMD_DUT_GET_PLS_RMN;
info = GetCmdInfo(cmdName);
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014))
// Read the remaining DUT pulses if required
if (firstActiveFm2014.DutPulsesRequired != 0)
{
if (Read(cmdName, fm2014, out responseStr) &&
ushort.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var pulses))
// Feed the progress counter but only if not done for REF pulses
if (firstActiveFm2014.RefPulsesRequired == 0)
ActualProcessProgress = firstActiveFm2014.DutPulsesRequired -
firstActiveFm2014.DutPulsesRemaining;
cmdName = CmdName.CMD_DUT_GET_PLS_RMN;
info = GetCmdInfo(cmdName);
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014))
{
fm2014.DutPulsesRemaining = pulses;
var response = new CmdResponse(cmdName, info, pulses);
PublishResponse(fm2014, new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
if (Read(cmdName, fm2014, out responseStr) &&
ushort.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"),
out var pulses))
{
fm2014.DutPulsesRemaining = pulses;
var response = new CmdResponse(cmdName, info, pulses);
PublishResponse(fm2014, new ProcessExecEventArgs("",
actualProcessMessage: measurementInfo,
specificInfoObj: response));
}
}
}
@ -1441,73 +1478,88 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
if (fm2014.RefPulsesRemaining == 0 && fm2014.DutPulsesRemaining == 0)
{
var retVal = true;
// Read out the result
cmdName = CmdName.CMD_REF_GET_TMR;
info = GetCmdInfo(cmdName);
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014))
// Read the REF timer ticks for the counted pulses
if (firstActiveFm2014.RefPulsesRequired != 0)
{
if (Read(cmdName, fm2014, out responseStr) &&
uint.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var timerTicks))
cmdName = CmdName.CMD_REF_GET_TMR;
info = GetCmdInfo(cmdName);
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014))
{
// Is converting to a real time in seconds
fm2014.RefTimerTicksMeasured = 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));
if (Read(cmdName, fm2014, out responseStr) &&
uint.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"),
out var timerTicks))
{
// Is converting to a real time in seconds
fm2014.RefTimerTicksMeasured = 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));
}
else
{
retVal = false;
}
}
else
{
retVal = false;
}
}
else
// Read the DUT timer ticks for the counted pulses
if (firstActiveFm2014.DutPulsesRequired != 0)
{
retVal = false;
cmdName = CmdName.CMD_DUT_GET_TMR;
info = GetCmdInfo(cmdName);
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014))
{
if (Read(cmdName, fm2014, out responseStr) &&
uint.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"),
out var timerTicks))
{
// Is converting to a real time in seconds
fm2014.DutTimerTicksMeasured = 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));
}
else
{
retVal = false;
}
}
else
{
retVal = false;
}
}
cmdName = CmdName.CMD_DUT_GET_TMR;
info = GetCmdInfo(cmdName);
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014))
// Calculate the DUT to REF tolerance if both timer had been active
if (firstActiveFm2014.RefPulsesRequired != 0 && firstActiveFm2014.DutPulsesRequired != 0)
{
if (Read(cmdName, fm2014, out responseStr) &&
uint.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"),
out var timerTicks))
cmdName = CmdName.CMD_CAL_DUT_TO_REF_TOL;
info = GetCmdInfo(cmdName);
// Is converting to a real time in seconds
if (fm2014.RefTimeMeasured_s > 0.0)
{
// Is converting to a real time in seconds
fm2014.DutTimerTicksMeasured = timerTicks;
var response = new CmdResponse(cmdName, info, doubleValue: fm2014.DutTimeMeasured_s,
unit: Units.GetInfo(Units.Name.TIME_s));
fm2014.DutToRefToleranceMeasured_percent =
(1.0 - fm2014.DutTimeMeasured_s / fm2014.RefTimeMeasured_s) * 100.0;
var response = new CmdResponse(cmdName, info,
doubleValue: fm2014.DutToRefToleranceMeasured_percent,
unit: Units.GetInfo(Units.Name.PERCENT));
PublishResponse(fm2014, new ProcessExecEventArgs("",
actualProcessMessage: measurementInfo,
specificInfoObj: response));
}
else
{
retVal = false;
}
}
else
{
retVal = false;
}
cmdName = CmdName.CMD_CAL_DUT_TO_REF_TOL;
info = GetCmdInfo(cmdName);
// Is converting to a real time in seconds
if (fm2014.RefTimeMeasured_s > 0.0)
{
fm2014.DutToRefToleranceMeasured_percent =
(1.0 - fm2014.DutTimeMeasured_s / fm2014.RefTimeMeasured_s) * 100.0;
var response = new CmdResponse(cmdName, info,
doubleValue: fm2014.DutToRefToleranceMeasured_percent,
unit: Units.GetInfo(Units.Name.PERCENT));
PublishResponse(fm2014, new ProcessExecEventArgs("",
actualProcessMessage: measurementInfo,
specificInfoObj: response));
}
if (retVal)
fm2014.MeasurementIsReady = true;
// Exclude this FM2014 from further looping as the measurement is completed
if (retVal) fm2014.MeasurementIsReady = true;
}
}
catch (Exception e)
@ -1520,7 +1572,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
//TODO THW rework to skip device which are not logged in and logout if timeout
//TODO THW rework to take timeout for unresponsive device into account
if (RegisteredFm2014s.All(fm2014 => fm2014.RefPulsesRemaining == 0 &&
fm2014.DutPulsesRemaining == 0))
{
@ -1877,14 +1929,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
// Set to zero if it doesn't fit to the limit which can be stored as indicator for invalid value
// which should be recovered by 'Ref_pulses_per_cm = Dut_pulses_per_cm * RefToDutScale_norm'
var limitedRefPulsesPerCm = Ref_pulse_per_cm <= PULSES_PER_CM_REGULATION_SETUP_MAX ?
Ref_pulse_per_cm : REF_PULSES_PER_VOLUME_INVALID_MARKER;
var limitedRefPulsesPerCm = RefPulses_per_cm <= PULSES_PER_CM_REGULATION_SETUP_MAX ?
RefPulses_per_cm : REF_PULSES_PER_VOLUME_INVALID_MARKER;
var retVal = Write(cmdName, this, (UInt16)limitedRefPulsesPerCm);
if (retVal)
{
cmdName = CmdName.CMD_DUT_LPP_SCALE;
retVal = Write(cmdName, this, Dut_pulse_per_cm);
retVal = Write(cmdName, this, DutPulses_per_cm);
}
if (retVal)
@ -1897,7 +1949,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
if (retVal)
{
cmdName = CmdName.CMD_MEAS_SET_ATTN;
retVal = Write(cmdName, this, FM2014CurrentOutputAttenuation);
retVal = Write(cmdName, this, CurrentOutputAttenuation);
}
if (retVal)
@ -1983,8 +2035,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
cmdName = CmdName.CMD_MEAS_SET_ATTN;
var info = GetCmdInfo(cmdName);
FM2014CurrentOutputAttenuation = attenuation;
var response = new CmdResponse(cmdName, info, FM2014CurrentOutputAttenuation);
CurrentOutputAttenuation = attenuation;
var response = new CmdResponse(cmdName, info, CurrentOutputAttenuation);
PublishResponse(this, new ProcessExecEventArgs("",
actualProcessMessage: measurementInfoStr, specificInfoObj: response));
}
@ -2012,8 +2064,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
cmdName = CmdName.CMD_DUT_LPP_SCALE;
var info = GetCmdInfo(cmdName);
Dut_pulse_per_cm = value;
var response = new CmdResponse(cmdName, info, Dut_pulse_per_cm);
DutPulses_per_cm = value;
var response = new CmdResponse(cmdName, info, DutPulses_per_cm);
PublishResponse(this,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr,
specificInfoObj: response));
@ -2021,8 +2073,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
cmdName = CmdName.CMD_REF_LPP_SCALE;
info = GetCmdInfo(cmdName);
// Calculate REF pulses per cubic meter
Ref_pulse_per_cm = (UInt32)Math.Round(Dut_pulse_per_cm * RefToDutScale_norm, 1);
response = new CmdResponse(cmdName, info, (Int32)Ref_pulse_per_cm);
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));

View File

@ -326,11 +326,11 @@
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label x:Name="lblRefPulsesPerCm" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="1" Grid.Column="17" Grid.ColumnSpan="5" Content="REF Pulse Ratio [pulses/m³]:" />
<Label x:Name="lblDutPulsesPerCm" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="2" Grid.Column="17" Grid.ColumnSpan="5" Content="DUT Pulse Ratio [pulses/m³]:" />
<Label x:Name="lblScaleRefToDut" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="3" Grid.Column="17" Grid.ColumnSpan="5" Content="REF/DUT Scale [norm]:" />
<Label x:Name="lblDisplayAttenuation" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="4" Grid.Column="17" Grid.ColumnSpan="5" Content="Display Attenuation []:" />
<Label x:Name="lblToleranceCalc" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="5" Grid.Column="17" Grid.ColumnSpan="5" Content="Tolerance Setup [%]:" />
<Label x:Name="lblRefPulsesPerCm" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="1" Grid.Column="17" Grid.ColumnSpan="6" Content="REF Pulse Ratio [pulses/m³]:" />
<Label x:Name="lblDutPulsesPerCm" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="2" Grid.Column="17" Grid.ColumnSpan="6" Content="DUT Pulse Ratio [pulses/m³]:" />
<Label x:Name="lblScaleRefToDut" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="3" Grid.Column="17" Grid.ColumnSpan="6" Content="REF/DUT Scale [norm]:" />
<Label x:Name="lblDisplayAttenuation" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="4" Grid.Column="17" Grid.ColumnSpan="6" Content="Display Attenuation []:" />
<Label x:Name="lblToleranceCalc" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="5" Grid.Column="17" Grid.ColumnSpan="6" Content="Tolerance Setup [%]:" />
<TextBox x:Name="tbxRefPulsesPerCm" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="1" Grid.Column="23" Grid.ColumnSpan="2" Text="10000" MaxLines="1" TabIndex="102" HorizontalContentAlignment="Right" KeyDown="tbxRefPulsesPerCm_KeyDown" LostFocus="tbxRefPulsesPerCm_LostFocus" TextChanged="tbxRefPulsesPerCm_TextChanged" />
<TextBox x:Name="tbxDutPulsesPerCm" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="2" Grid.Column="23" Grid.ColumnSpan="2" Text="1000" MaxLines="1" TabIndex="103" HorizontalContentAlignment="Right" KeyDown="tbxDutPulsesPerCm_KeyDown" LostFocus="tbxDutPulsesPerCm_LostFocus" TextChanged="tbxDutPulsesPerCm_TextChanged"/>
<TextBox x:Name="tbxScaleRefToDut" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="3" Grid.Column="23" Grid.ColumnSpan="2" Text="10.0" MaxLines="1" HorizontalContentAlignment="Right" IsTabStop="False"/>

View File

@ -854,7 +854,7 @@ namespace Sensus.Ui.FM2014TestBench
UpdateTextBox(tbxScaleRefToDut, $@"{firstFm2014.RefToDutScale_norm:F4}");
// Display error if scale doesn't fit
var tempRefToDutScale_norm = (Double)firstFm2014.Ref_pulse_per_cm / firstFm2014.Dut_pulse_per_cm;
var tempRefToDutScale_norm = (Double)firstFm2014.RefPulses_per_cm / firstFm2014.DutPulses_per_cm;
if (tempRefToDutScale_norm < FM2014.REF_TO_DUT_SCALE_MIN ||
tempRefToDutScale_norm > FM2014.REF_TO_DUT_SCALE_MAX ||
firstFm2014.RefToDutScale_norm < FM2014.REF_TO_DUT_SCALE_MIN ||
@ -1086,6 +1086,8 @@ namespace Sensus.Ui.FM2014TestBench
}
else return;
var firstFm2014 = FM2014.GetFirstConnectedAndLoggedInFm2014();
if (e.ActualProcessMessage != null)
{
UpdateContentControl(lblActualProcessText, e.ActualProcessMessage);
@ -1126,7 +1128,8 @@ namespace Sensus.Ui.FM2014TestBench
break;
case CmdName.CMD_REF_GET_PLS_CTR:
case CmdName.CMD_REF_GET_PLS_CTR_BU:
UpdateTextBox(tbxRefPulsesMeasured, $"{resp.IntValue:D}");
if (firstFm2014 != null && firstFm2014.Address == fm2014.Address )
UpdateTextBox(tbxRefPulsesMeasured, $"{resp.IntValue:D}");
ucFm2014.SetValue(CTR_LBL_IDX_REF_PLS, $"{resp.IntValue:D}");
break;
case CmdName.CMD_DUT_GET_PLS_CTR:
@ -1244,7 +1247,7 @@ namespace Sensus.Ui.FM2014TestBench
/// <summary>
/// Start and stop the DUT to REF calibration measurement.
/// </summary>
/// <remarks date="2026-Feb-07" author="Thomas Wiedebusch">
/// <remarks date="2026-Feb-07..11" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void btnDutToRefCalibration_Click(Object sender, EventArgs e)
@ -1256,13 +1259,6 @@ namespace Sensus.Ui.FM2014TestBench
foreach (var measLbl in MeasurementLabels)
UiElmEnable(measLbl, false, false);
// Take all calibration settings and publish this to all FM2014s
tbxRefPulsesRequired_LostFocus(this, null);
tbxDutPulsesRequired_LostFocus(this, null);
tbxDoublePulseDeadtime_LostFocus(this, null);
tbxDutToRefToleranceMax_LostFocus(this, null);
tbxDutToRefToleranceMin_LostFocus(this, null);
// Backup if meanwhile anything has changed
if (_configSetupHasChanged || _calibrationSetupHasChanged)
StoreFM2014SettingsToConfigFile();
@ -1285,6 +1281,46 @@ namespace Sensus.Ui.FM2014TestBench
if (FM2014.RegisteredFm2014s == null || FM2014.RegisteredFm2014s.Count == 0)
return;
// Take the settings from the inputs
UInt16? refPulsesRequired = null;
UInt16? dutPulsesRequired = null;
UInt16 doublePulseDeadtime_ms = 0;
Double tolerenceThresholdMax_percent = 1.0;
Double tolerenceThresholdMin_percent = -0.7;
Dispatcher.Invoke(() =>
{
if (int.TryParse(tbxRefPulsesRequired.Text, out var pulses) &&
FM2014.TIME_MEASUREMENT_INPUT_MIN_pulses <= pulses &&
FM2014.TIME_MEASUREMENT_INPUT_MAX_pulses >= 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)
{
dutPulsesRequired = (UInt16)pulses;
}
if (int.TryParse(tbxDoublePulseDeadtime.Text, out var deadtime_ms) &&
FM2014.DOUBLE_PULSE_DEADTIME_MIN_ms <= deadtime_ms &&
FM2014.DOUBLE_PULSE_DEADTIME_MAX_ms >= deadtime_ms)
{
doublePulseDeadtime_ms = (UInt16)deadtime_ms;
}
var englishNumberFormat = tbxDutToRefTolMax.Text.Replace(',', '.');
if (double.TryParse(englishNumberFormat, NumberStyles.Any, new CultureInfo("en"), out var tolerance))
{
tolerenceThresholdMax_percent = tolerance;
}
englishNumberFormat = tbxDutToRefTolMin.Text.Replace(',', '.');
if (double.TryParse(englishNumberFormat, NumberStyles.Any, new CultureInfo("en"), out tolerance))
{
tolerenceThresholdMin_percent = tolerance;
}
});
// Adjust the user control
foreach (var fm2014 in FM2014.RegisteredFm2014s.Where(x => x.IsLoggedOn))
{
@ -1293,11 +1329,15 @@ namespace Sensus.Ui.FM2014TestBench
ucFM2014.EnableMeasurements(CAL_MEASURE_COUNT);
// Set initial required pulses as those are static upon the entire measurement
ucFM2014.SetValue(CAL_LBL_IDX_REF_REQ_PLS, $"{fm2014.RefPulsesRequired}");
ucFM2014.SetValue(CAL_LBL_IDX_DUT_REQ_PLS, $"{fm2014.DutPulsesRequired}");
ucFM2014.SetValue(CAL_LBL_IDX_REF_REQ_PLS, refPulsesRequired != null ? $"{refPulsesRequired}" : "---");
ucFM2014.SetValue(CAL_LBL_IDX_DUT_REQ_PLS, dutPulsesRequired != null ? $"{dutPulsesRequired}" : "---");
// Set thresholds for tolerance verification
fm2014.DutToRefCalibrationToleranceMax_percent = tolerenceThresholdMax_percent;
fm2014.DutToRefCalibrationToleranceMin_percent = tolerenceThresholdMin_percent;
}
if (FM2014.CalibrationMeasurement())
if (FM2014.PulseTimeMeasurement(refPulsesRequired, dutPulsesRequired, doublePulseDeadtime_ms))
{
_autoProgressBar = false;
ActionControl(true);
@ -1317,7 +1357,7 @@ namespace Sensus.Ui.FM2014TestBench
/// <summary>
/// Start and stop the DUT to REF regulation measurement.
/// </summary>
/// <remarks date="2026-Jan-14" author="Thomas Wiedebusch">
/// <remarks date="2026-Jan-14..Feb-11" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void btnDutToRefRegulation_Click(Object sender, EventArgs e)
@ -1329,12 +1369,6 @@ namespace Sensus.Ui.FM2014TestBench
foreach (var measLbl in MeasurementLabels)
UiElmEnable(measLbl, false, false);
// Take all regulation settings and publish this to all FM2014s
tbxRefPulsesPerCm_LostFocus(this, null);
tbxDutPulsesPerCm_LostFocus(this, null);
cbxAttenuation_SelectedIndexChanged(this, null);
cbxToleranceCalc_SelectedIndexChanged(this, null);
// Set the measurement labels
UpdateContentControl(MeasurementLabels[REG_LBL_IDX_REF_FREQU], Properties.Resources.StrLblRefFrequencyHz);
UpdateContentControl(MeasurementLabels[REG_LBL_IDX_FLOW_RATE], Properties.Resources.StrLblFlowRateMeasured);
@ -1346,6 +1380,41 @@ namespace Sensus.Ui.FM2014TestBench
if (FM2014.RegisteredFm2014s == null || FM2014.RegisteredFm2014s.Count == 0)
return;
// Take the settings from the inputs
UInt32 refPulses_per_cm = 1000;
UInt16 dutPulses_per_cm = 100;
UInt16 doublePulseDeadtime_ms = 0;
Byte currentOutputAttenuation = 1;
Dispatcher.Invoke(() =>
{
if (int.TryParse(tbxRefPulsesPerCm.Text, out var pulses_per_cm) &&
FM2014.PULSES_PER_CM_REGULATION_SETUP_MIN <= pulses_per_cm &&
FM2014.REF_PULSES_PER_CM_REGULATION_INPUT_MAX >= pulses_per_cm)
{
refPulses_per_cm = (UInt32)pulses_per_cm;
}
if (int.TryParse(tbxDutPulsesPerCm.Text, out pulses_per_cm) &&
FM2014.PULSES_PER_CM_REGULATION_SETUP_MIN <= pulses_per_cm &&
FM2014.PULSES_PER_CM_REGULATION_SETUP_MAX >= pulses_per_cm)
{
dutPulses_per_cm = (UInt16)pulses_per_cm;
}
if (int.TryParse(tbxDoublePulseDeadtime.Text, out var deadtime_ms) &&
FM2014.DOUBLE_PULSE_DEADTIME_MIN_ms <= deadtime_ms &&
FM2014.DOUBLE_PULSE_DEADTIME_MAX_ms >= deadtime_ms)
{
doublePulseDeadtime_ms = (UInt16)deadtime_ms;
}
if (byte.TryParse(cbxDisplayAttenuation.SelectedItem.ToString(), out var attenuation) &&
attenuation >= FM2014.ATTENUATION_MIN &&
attenuation <= FM2014.ATTENUATION_MAX)
{
currentOutputAttenuation = attenuation;
}
});
// Adjust the user control
foreach (var fm2014 in FM2014.RegisteredFm2014s.Where(x => x.IsLoggedOn))
{
@ -1354,7 +1423,8 @@ namespace Sensus.Ui.FM2014TestBench
ucFM2014.EnableMeasurements(REG_MEASURE_COUNT);
}
if (FM2014.RegulationMeasurement())
if (FM2014.RegulationMeasurement(refPulses_per_cm, dutPulses_per_cm, doublePulseDeadtime_ms,
currentOutputAttenuation))
{
_autoProgressBar = true;
ActionControl(true);
@ -1374,7 +1444,7 @@ namespace Sensus.Ui.FM2014TestBench
/// <summary>
/// Start and stop the manual REF calibration measurement.
/// </summary>
/// <remarks date="2026-Jan-04..12" author="Thomas Wiedebusch">
/// <remarks date="2026-Jan-04..Feb-11" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void btnRefToVolumeCalibration_Click(Object sender, EventArgs e)
@ -1402,6 +1472,7 @@ namespace Sensus.Ui.FM2014TestBench
var measureDutPulses = false;
Dispatcher.Invoke(() => measureDutPulses = chkDutPulsesMeasured.IsChecked ?? false);
// Adjust the user control
foreach (var fm2014 in FM2014.RegisteredFm2014s.Where(x => x.IsLoggedOn))
{
@ -1412,7 +1483,7 @@ namespace Sensus.Ui.FM2014TestBench
fm2014.DutPulseCtrOn = measureDutPulses;
}
if (FM2014.PulseCounterMeasurement())
if (FM2014.PulseCounterMeasurement(true, measureDutPulses))
{
_autoProgressBar = true;
ActionControl(true);
@ -1426,7 +1497,7 @@ namespace Sensus.Ui.FM2014TestBench
UiElmEnable(btnRefToVolumeCalibration, false);
_autoProgressBar = false;
FM2014.ResetHardwareAllDevices();
}
}
}
/// <summary>
@ -1515,14 +1586,8 @@ namespace Sensus.Ui.FM2014TestBench
if (byte.TryParse(cbxDisplayAttenuation.SelectedItem.ToString(), out var attenuation) &&
attenuation >= FM2014.ATTENUATION_MIN &&
attenuation <= FM2014.ATTENUATION_MAX &&
firstFm2014.FM2014CurrentOutputAttenuation != attenuation)
firstFm2014.CurrentOutputAttenuation != attenuation)
{
firstFm2014.FM2014CurrentOutputAttenuation = attenuation;
// Dispatch settings to all connected FM2014
foreach (var fm2014 in FM2014.RegisteredFm2014s)
{
fm2014.FM2014CurrentOutputAttenuation = attenuation;
}
_regulationSetupHasChanged = true;
btnSaveRegulationSetup.IsEnabled = true;
}
@ -1709,12 +1774,6 @@ namespace Sensus.Ui.FM2014TestBench
_calibrationSetupHasChanged = true;
tbxRefPulsesRequired.Background = ColorStandardInputField;
// Dispatch settings to all connected FM2014!!!!
foreach (var fm2014 in FM2014.RegisteredFm2014s)
{
fm2014.RefPulsesRequired = (UInt16)pulses;
}
}
else
{
@ -1810,12 +1869,6 @@ namespace Sensus.Ui.FM2014TestBench
_calibrationSetupHasChanged = true;
tbxDutPulsesRequired.Background = ColorStandardInputField;
// Dispatch settings to all connected FM2014!!!!
foreach (var fm2014 in FM2014.RegisteredFm2014s)
{
fm2014.DutPulsesRequired = (UInt16)pulses;
}
}
else
{
@ -1902,12 +1955,6 @@ namespace Sensus.Ui.FM2014TestBench
_calibrationSetupHasChanged = true;
tbxDoublePulseDeadtime.Background = ColorStandardInputField;
// Dispatch settings to all connected FM2014!!!!
foreach (var fm2014 in FM2014.RegisteredFm2014s)
{
fm2014.DoublePulseDeadtime_ms = (UInt16)deadtime;
}
}
else
{
@ -2174,19 +2221,14 @@ namespace Sensus.Ui.FM2014TestBench
}
//Backup the actual setting to detect changes
var backupPulses_per_cm = firstFm2014.Ref_pulse_per_cm;
var backupPulses_per_cm = firstFm2014.RefPulses_per_cm;
// Parse and check limits
if (int.TryParse(tbxRefPulsesPerCm.Text, out var pulses_per_cm) &&
FM2014.PULSES_PER_CM_REGULATION_SETUP_MIN <= pulses_per_cm &&
FM2014.REF_PULSES_PER_CM_REGULATION_INPUT_MAX >= pulses_per_cm)
{
// Dispatch settings to all connected FM2014!!!!
foreach (var fm2014 in FM2014.RegisteredFm2014s)
{
fm2014.Ref_pulse_per_cm = (UInt32)pulses_per_cm;
}
// During setup of the 'Ref_pulse_per_cm' the 'RefToDutScaleStr' will be generated
// During setup of the 'Ref_pulse_per_cm' the 'RefToDutScaleStr' will be generated
if (!UpdateRefToDutScale())
{
tbxRefPulsesPerCm.Background = ColorProcessFailed;
@ -2276,18 +2318,13 @@ namespace Sensus.Ui.FM2014TestBench
}
//Backup the actual setting to detect changes
var backupPulses_per_cm = firstFm2014.Dut_pulse_per_cm;
var backupPulses_per_cm = firstFm2014.DutPulses_per_cm;
// Parse and check limits
if (int.TryParse(tbxDutPulsesPerCm.Text, out var pulses_per_cm) &&
FM2014.PULSES_PER_CM_REGULATION_SETUP_MIN <= pulses_per_cm &&
FM2014.PULSES_PER_CM_REGULATION_SETUP_MAX >= pulses_per_cm)
{
// Dispatch settings to all connected FM2014!!!!
foreach (var fm2014 in FM2014.RegisteredFm2014s)
{
fm2014.Dut_pulse_per_cm = (UInt16)pulses_per_cm;
}
// During setup of the 'Dut_pulse_per_cm' the 'RefToDutScaleStr' will be generated
if (!UpdateRefToDutScale())
{
@ -2380,7 +2417,7 @@ namespace Sensus.Ui.FM2014TestBench
}
// Backup the old value
var backup_pulses_per_cm = firstFm2014.Dut_pulse_per_cm;
var backup_pulses_per_cm = firstFm2014.DutPulses_per_cm;
// Calculate REF pulses per cubic meter
if (int.TryParse(tbxRefPulsesMeasured.Text, out var pulses) && pulses > 0 &&
@ -2390,20 +2427,14 @@ namespace Sensus.Ui.FM2014TestBench
var pulses_per_cm = (UInt32)(pulses / (liters / 1000.0) + 0.5);
// Try to set the new calculated REF pulses limited by the property setter
firstFm2014.Ref_pulse_per_cm = pulses_per_cm;
// Dispatch settings to all connected FM2014!!!!
foreach (var fm2014 in FM2014.RegisteredFm2014s)
{
fm2014.Ref_pulse_per_cm = pulses_per_cm;
}
firstFm2014.RefPulses_per_cm = pulses_per_cm;
// If this succeeded, then update the new manual calibrated value
if (pulses_per_cm == firstFm2014.Ref_pulse_per_cm)
if (pulses_per_cm == firstFm2014.RefPulses_per_cm)
{
tbxRefPulsesPerCm.Text = $@"{firstFm2014.Ref_pulse_per_cm:D}";
tbxRefPulsesPerCm.Text = $@"{firstFm2014.RefPulses_per_cm:D}";
tbxRefPulsesPerCm.Background = ColorStandardDisplayField;
tbxRefPulsesPerCmCalib.Text = $@"{firstFm2014.Ref_pulse_per_cm:D}";
tbxRefPulsesPerCmCalib.Text = $@"{firstFm2014.RefPulses_per_cm:D}";
tbxRefPulsesPerCmCalib.Background = ColorStandardDisplayField;
tbxVolumeMeasuredLiters.Background = ColorStandardInputField;
@ -2415,7 +2446,7 @@ namespace Sensus.Ui.FM2014TestBench
}
// Check if values have changed and need to be updated in the standalone setup
if (backup_pulses_per_cm != firstFm2014.Ref_pulse_per_cm)
if (backup_pulses_per_cm != firstFm2014.RefPulses_per_cm)
{
DutToRefRegulationSetupHasChanged();
}