diff --git a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs
index 46b1f90e..f49a4985 100644
--- a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs
+++ b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs
@@ -144,12 +144,42 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
///
private static CmdType SharedCmdTypeReminderLastCmd { get; set; }
-
///
/// Reminder of last individual address of FM2014 to initiate a new communication to another device.
///
private static Int32 ReminderLastIndividualCommFm2014Address { get; set; }
+ ///
+ /// Number of processing steps for this actual process progress
+ ///
+ private static Int32 MaxActualProcessProgress { get; set; }
+
+ ///
+ /// Progress of actual process converted to percent
+ ///
+ private static Double _actualProcessProgress_percent;
+
+ ///
+ /// Progress of actual process
+ ///
+ private static Int32 _actualProcessProgress;
+
+ ///
+ /// Actual process progress of subroutine
+ ///
+ private static Int32 ActualProcessProgress
+ {
+ get => _actualProcessProgress;
+ set
+ {
+ _actualProcessProgress = value;
+ if (MaxActualProcessProgress == 0)
+ MaxActualProcessProgress = 1;
+ if (_actualProcessProgress > MaxActualProcessProgress)
+ _actualProcessProgress = MaxActualProcessProgress;
+ _actualProcessProgress_percent = 100.0 * _actualProcessProgress / MaxActualProcessProgress;
+ }
+ }
#endregion ---------------------------------------- static properties -----------------------------------------
#region ------------------------------------------- object properties -----------------------------------------
@@ -210,11 +240,11 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// Private tolerance percentage
///
private Int32 _tolerance_percent = DEFAULT_TOLERANCE_percent;
-
+
///
/// Actual tolerance scale to convert raw value from FM2014 to percent
///
- private Single _toleranceRawToPercentScale = 3.3f / 255;
+ private Single ToleranceRawToPercentScale { set; get; } = 3.3f / 255;
///
/// Tolerance output of current loop in percent:
@@ -235,7 +265,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
else
percentage = 3.3f;
_tolerance_percent = value;
- _toleranceRawToPercentScale = percentage / 255;
+ ToleranceRawToPercentScale = percentage / 255;
}
}
@@ -254,44 +284,92 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
///
public const UInt32 TimeMeasurementMax_pulses = 0xFFFF;
- private UInt32 _refTimeMeasurement_pulses;
+ private UInt32 _refRequiredTimeMeasurement_pulses;
///
- /// REF pulses for time measurement.
+ /// Required REF pulses for time measurement.
///
- public UInt32 RefTimeMeasurement_pulses
+ public UInt32 RefRequiredTimeMeasurement_pulses
{
- get => _refTimeMeasurement_pulses;
+ get => _refRequiredTimeMeasurement_pulses;
private set
{
// Check limits and equality
if (value < TimeMeasurementMin_pulses ||
value > TimeMeasurementMax_pulses ||
- value == _refTimeMeasurement_pulses)
+ value == _refRequiredTimeMeasurement_pulses)
return;
- _refTimeMeasurement_pulses = value;
+ _refRequiredTimeMeasurement_pulses = value;
}
}
- private UInt32 _dutTimeMeasurement_pulses;
+ private UInt32 _dutRequiredTimeMeasurement_pulses;
///
- /// DUT pulses for time measurement.
+ /// Required DUT pulses for time measurement.
///
- public UInt32 DutTimeMeasurement_pulses
+ public UInt32 DutRequiredTimeMeasurement_pulses
{
- get => _dutTimeMeasurement_pulses;
+ get => _dutRequiredTimeMeasurement_pulses;
private set
{
// Check limits and equality
if (value < TimeMeasurementMin_pulses ||
value > TimeMeasurementMax_pulses ||
- value == _dutTimeMeasurement_pulses)
+ value == _dutRequiredTimeMeasurement_pulses)
return;
- _dutTimeMeasurement_pulses = value;
+ _dutRequiredTimeMeasurement_pulses = value;
}
}
+ private Int32 _refMeasuredTimer_ticks;
+ ///
+ /// REF timer ticks of measured pulses
+ ///
+ public Int32 RefMeasuredTimer_ticks
+ {
+ get => _refMeasuredTimer_ticks;
+ private set
+ {
+ _refMeasuredTimer_ticks = value;
+ RefMeasuredTime_s = value * TMR_RESOLUTION_s;
+ }
+ }
+
+ private Int32 _dutMeasuredTimer_ticks;
+ ///
+ /// DUT timer ticks of measured pulses
+ ///
+ public Int32 DutMeasuredTimer_ticks
+ {
+ get => _dutMeasuredTimer_ticks;
+ private set
+ {
+ _dutMeasuredTimer_ticks = value;
+ DutMeasuredTime_s = value * TMR_RESOLUTION_s;
+ }
+ }
+
+ ///
+ /// Remaining REF pulses for time measurement.
+ ///
+ public UInt32 RefRemainingTimeMeasurement_pulses { get; private set; }
+
+ ///
+ /// Remaining DT pulses for time measurement.
+ ///
+ public UInt32 DutRemainingTimeMeasurement_pulses { get; private set; }
+
+ ///
+ /// Measured REF time of required pulses.
+ ///
+ public Double RefMeasuredTime_s { get; private set; }
+
+ ///
+ /// Measured DUT time of required pulses.
+ ///
+ public Double DutMeasuredTime_s { get; private set; }
+
private UInt32 _ref_pulse_per_cm;
///
/// Reference pulses per cubic-meter
@@ -350,7 +428,11 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// "1000K3" = 100.0
/// Max value is "9999K3" = 999.9
///
- private String _refToDutScaleStr = "1000K+2";
+ private String RefToDutScaleStr { get; set; } = "1000K+2";
+
+ ///
+ /// REF to DUT scale converted to meaningful human interpretable value
+ ///
private Double _refToDutScale_norm = 10.0f;
///
@@ -379,7 +461,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
} while (mantissa < MANTISSA_SCALE_MIN);
// The pre-generated string will be used to send it to the FM2014 directly
- _refToDutScaleStr = $"{mantissa}{GetCmdStr(CmdName.CMD_REF_SET_SCALE)}{exponent}";
+ RefToDutScaleStr = $"{mantissa}{GetCmdStr(CmdName.CMD_REF_SET_SCALE)}{exponent}";
}
}
@@ -425,40 +507,73 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
///
public Boolean RequestDebugInformation { get; set; }
- ///
- /// Number of processing steps for this actual process progress
- ///
- private static Int32 MaxActualProcessProgress { get; set; }
-
- ///
- /// Progress of actual process converted to percent
- ///
- private static Double _actualProcessProgress_percent;
-
- ///
- /// Progress of actual process
- ///
- private static Int32 _actualProcessProgress;
-
- ///
- /// Actual process progress of subroutine
- ///
- private static Int32 ActualProcessProgress
- {
- get => _actualProcessProgress;
- set
- {
- _actualProcessProgress = value;
- if (MaxActualProcessProgress == 0)
- MaxActualProcessProgress = 1;
- if (_actualProcessProgress > MaxActualProcessProgress)
- _actualProcessProgress = MaxActualProcessProgress;
- _actualProcessProgress_percent = 100.0 * _actualProcessProgress / MaxActualProcessProgress;
- }
- }
#endregion ---------------------------------------- object properties -----------------------------------------
#region ------------------------------------------- static methods --------------------------------------------
+ ///
+ /// Reset the measurement but only if receive task is active:
+ /// - This should prepare the hardware for a new measurement which takes about 4 s.
+ /// - It is a broadcast command which will reset all connected devices!
+ ///
+ ///
+ ///
+ /// - Initial.
+ ///
+ ///
+ /// - Delayed reset to allow other tasks to finish.
+ ///
+ ///
+ /// - Support for multiple FM2014s.
+ ///
+ ///
+ /// - Statics.
+ ///
+ public static Boolean ResetHardwareAllDevices()
+ {
+ if (SharedCyclicMeasSequ == CyclicMeasSequ.IDLE)
+ return true;
+
+ var firstActiveFm2014 = GetFirstConnectedAndLoggedInFm2014();
+ if (firstActiveFm2014 == null)
+ return false;
+
+ InitActualProcessProgress();
+ // Exit tasks
+ SharedCyclicMeasSequ = CyclicMeasSequ.IDLE;
+
+ // Schedule to other tasks to avoid side effects through the reset command
+ Task.Factory.StartNew(() =>
+ {
+ Thread.Sleep(1000);
+ }).ContinueWith(delegate
+ {
+ // Reset measurement
+ var infoStr = GetCmdInfo(CmdName.CMD_RST_MEAS);
+ if (!Write(CmdName.CMD_RST_MEAS, firstActiveFm2014))
+ return;
+
+ // Wait until FM2014 calibration finished
+ var resetDelayCtr_ms = 4000;
+ const Int32 loopTime_ms = 500;
+ MaxActualProcessProgress = resetDelayCtr_ms / loopTime_ms;
+ do
+ {
+ // Change the SI unit to ms instead of seconds
+ var response = new CmdResponse(CmdName.CMD_RST_MEAS, infoStr,
+ resetDelayCtr_ms, siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME));
+ PublishResponse(firstActiveFm2014, new ProcessExecEventArgs(infoStr,
+ actualProcessMessage: infoStr,
+ actualProcessPercent: _actualProcessProgress_percent,
+ specificInfoObj: response));
+ Thread.Sleep(loopTime_ms);
+ resetDelayCtr_ms -= loopTime_ms;
+ ActualProcessProgress++;
+ } while (resetDelayCtr_ms >= 0);
+ }, SharedCancellationToken);
+
+ return true;
+ }
+
///
/// Executes all StoreConfiguration and StoreCalibration for each FM2014 which is logged on.
///
@@ -483,6 +598,20 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
return retVal;
}
+ ///
+ /// Get the first FM2014 which is connected and logged in for common service routines
+ /// using a broadcast command
+ ///
+ ///
+ private static FM2014 GetFirstConnectedAndLoggedInFm2014()
+ {
+ if (RegisteredFm2014s == null || RegisteredFm2014s.Count == 0 ||
+ RegisteredFm2014s.All(fm2014 => !fm2014.IsLoggedOn))
+ return null;
+
+ return RegisteredFm2014s.FirstOrDefault(fm2014 => fm2014.IsLoggedOn);
+ }
+
///
/// Common routine to set up the counter for max process progress based on connected
/// and logged in devices and set the actual process progress to zero.
@@ -501,7 +630,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
return;
}
- foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn))
+ foreach (var unused in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn))
{
MaxActualProcessProgress++;
}
@@ -521,6 +650,256 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
fm2014?.OnRawRecordReceived?.Invoke(fm2014, processExecEventArgs);
}
+ ///
+ /// The regulation measurement compares the DUT to REF tolerance:
+ /// Preconditions:
+ /// - The has to be executed in advance,
+ /// - The REF pulses per cubic meter has to be preset,
+ /// - The DUT pulses per cubic meter has to be preset,
+ /// - The Scale for REF to DUT pulses has to be preset.
+ /// Initial setup:
+ /// - Setup double impulse,
+ /// - Setup damping to attenuate the result,
+ /// - Setup scale between REF to DUT pulses,
+ /// - 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 to calculate
+ /// the 'Flow rate'.
+ /// Exit:
+ /// - Set to false.
+ ///
+ ///
+ ///
+ /// - Initial.
+ ///
+ ///
+ /// - Support for multiple FM2014s.
+ ///
+ ///
+ /// - Static.
+ ///
+ public static Boolean RegulationMeasurement()
+ {
+ // If the cyclic task has already been started everything is fine
+ if (SharedCyclicMeasSequ == CyclicMeasSequ.REGULATION)
+ return true;
+
+ // Marked measurement isn't pulse counter measurement, other FM2014s cannot activate this
+ if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE)
+ return false;
+
+ var firstActiveFm2014 = GetFirstConnectedAndLoggedInFm2014();
+ if (firstActiveFm2014 == null)
+ return false;
+
+ // Check if regulation can be done
+ if (firstActiveFm2014.Ref_pulse_per_cm == 0 ||
+ firstActiveFm2014.Dut_pulse_per_cm == 0 ||
+ 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;
+ InitActualProcessProgress(3);
+ // Prepare regulation measurement
+ try
+ {
+ cmdName = CmdName.CMD_SET_DBPL_UNLOCK;
+ if (!Write(cmdName, firstActiveFm2014))
+ {
+ return false;
+ }
+ ActualProcessProgress++;
+ cmdName = CmdName.CMD_MEAS_SET_ATTN;
+ if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.Attenuation))
+ {
+ return false;
+ }
+ ActualProcessProgress++;
+ cmdName = CmdName.CMD_REF_SET_SCALE;
+ if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.RefToDutScaleStr))
+ {
+ return false;
+ }
+ ActualProcessProgress++;
+ }
+ catch (Exception e)
+ {
+ var response = new CmdResponse(cmdName, e.Message);
+ PublishResponse(firstActiveFm2014, new ProcessExecEventArgs(Resources.StrError,
+ specificInfoObj: response, statusReturn: StatusReturn.Failed));
+ return false;
+ }
+
+ // Measurement Loop
+ Task.Run(() =>
+ {
+ // Mark regulation measurement as active
+ SharedCyclicMeasSequ = CyclicMeasSequ.REGULATION;
+
+ do
+ {
+ InitActualProcessProgress();
+ foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn))
+ {
+ try
+ {
+ // 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;
+ 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))
+ {
+ // 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))
+ {
+ var response = new CmdResponse(cmdName, info,
+ doubleValue: measTolerance * fm2014.ToleranceRawToPercentScale * signMultiplier,
+ siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.PERCENTAGE_NON_SI));
+ PublishResponse(fm2014,
+ new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
+ specificInfoObj: response));
+ }
+ }
+ }
+
+ // Read the REF period and calculate the frequency [Hz] and flow rate [m³/h] based on this period
+ cmdName = CmdName.CMD_GET_REF_PERIOD;
+ info = GetCmdInfo(cmdName);
+ if (fm2014.RequestDebugInformation && SharedCyclicMeasSequ != CyclicMeasSequ.IDLE &&
+ Write(cmdName, fm2014))
+ {
+ if (Read(cmdName, fm2014, out responseStr) &&
+ int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var period))
+ {
+ // Publish the period [ms]
+ var response = new CmdResponse(cmdName, info,
+ doubleValue: TMR_RESOLUTION_s * 1000.0 * period,
+ siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME));
+ PublishResponse(fm2014,
+ new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
+ specificInfoObj: response));
+
+ // Publish the frequency in [Hz]
+ cmdName = CmdName.CMD_CAL_FREQU_REF_PERIOD;
+ info = GetCmdInfo(cmdName);
+ response = new CmdResponse(cmdName, info,
+ doubleValue: 1.0 / (TMR_RESOLUTION_s * period),
+ siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY));
+ PublishResponse(fm2014,
+ new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
+ 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;
+ // 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,
+ siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FLOW_RATE_NON_SI));
+ PublishResponse(fm2014,
+ new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
+ specificInfoObj: response));
+ }
+ }
+
+
+ // Read the DUT period and calculate the frequency [Hz] and flow rate [m³/h] based on this period
+ cmdName = CmdName.CMD_GET_DUT_PERIOD;
+ info = GetCmdInfo(cmdName);
+ if (fm2014.RequestDebugInformation &&
+ SharedCyclicMeasSequ != CyclicMeasSequ.IDLE &&
+ Write(cmdName, fm2014))
+ {
+ if (Read(cmdName, fm2014, out responseStr) &&
+ int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var period))
+ {
+ // Publish the period [ms]
+ var response = new CmdResponse(cmdName, info,
+ doubleValue: TMR_RESOLUTION_s * 1000.0 * period,
+ siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME));
+ PublishResponse(fm2014,
+ new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
+ specificInfoObj: response));
+
+ // Publish the frequency in [Hz]
+ cmdName = CmdName.CMD_CAL_FREQU_DUT_PERIOD;
+ info = GetCmdInfo(cmdName);
+ response = new CmdResponse(cmdName, info,
+ doubleValue: 1.0 / (TMR_RESOLUTION_s * period),
+ siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY));
+ PublishResponse(fm2014,
+ new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
+ 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;
+ // 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,
+ siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FLOW_RATE_NON_SI));
+ PublishResponse(fm2014,
+ new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
+ specificInfoObj: 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) &&
+ int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"),
+ out var refFrequency))
+ {
+ // Publish the frequency in [Hz]
+ var response = new CmdResponse(cmdName, info, refFrequency,
+ siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY));
+ PublishResponse(fm2014, new ProcessExecEventArgs("",
+ actualProcessMessage: measurementInfo,
+ 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;
+ // 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,
+ siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FLOW_RATE_NON_SI));
+ PublishResponse(fm2014,
+ new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
+ specificInfoObj: response));
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ var response = new CmdResponse(cmdName, e.Message);
+ PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
+ specificInfoObj: response, statusReturn: StatusReturn.Failed));
+ }
+ }
+ } while (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE);
+
+ }, SharedCancellationToken);
+
+ return true;
+ }
+
///
/// Read FM2014:
/// - Accesses directly to the 'SerialPort.ReadTo',
@@ -998,249 +1377,6 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
return true;
}
- ///
- /// The regulation measurement compares the DUT to REF tolerance:
- /// Preconditions:
- /// - The has to be executed in advance,
- /// - The REF pulses per cubic meter has to be preset,
- /// - The DUT pulses per cubic meter has to be preset,
- /// - The Scale for REF to DUT pulses has to be preset.
- /// Initial setup:
- /// - Setup double impulse,
- /// - Setup damping to attenuate the result,
- /// - Setup scale between REF to DUT pulses,
- /// - 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 to calculate
- /// the 'Flow rate'.
- /// Exit:
- /// - Set to false.
- ///
- ///
- ///
- /// - Initial.
- ///
- ///
- /// - Support for multiple FM2014s.
- ///
- public Boolean RegulationMeasurement()
- {
- if (Ref_pulse_per_cm == 0 ||
- Dut_pulse_per_cm == 0 ||
- RefToDutScale_norm < REF_TO_DUT_SCALE_MIN ||
- RefToDutScale_norm > REF_TO_DUT_SCALE_MAX ||
- !IsLoggedOn)
- {
- return false;
- }
-
- // If the cyclic task has already been started everything is fine
- if (SharedCyclicMeasSequ == CyclicMeasSequ.REGULATION)
- return true;
-
- // Marked measurement isn't pulse counter measurement, other FM2014s cannot activate this
- if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE)
- return false;
-
-
- var cmdName = CmdName.CMD_NA;
- var measurementInfo = Resources.StrMeasMsgRegulation;
- InitActualProcessProgress(3);
- // Prepare regulation measurement
- try
- {
- cmdName = CmdName.CMD_SET_DBPL_UNLOCK;
- if (!Write(cmdName, this))
- {
- return false;
- }
- ActualProcessProgress++;
- cmdName = CmdName.CMD_MEAS_SET_ATTN;
- if (!Write(cmdName, this, Attenuation))
- {
- return false;
- }
- ActualProcessProgress++;
- cmdName = CmdName.CMD_REF_SET_SCALE;
- if (!Write(cmdName, this, _refToDutScaleStr))
- {
- return false;
- }
- ActualProcessProgress++;
- }
- catch (Exception e)
- {
- var response = new CmdResponse(cmdName, e.Message);
- PublishResponse(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
- statusReturn: StatusReturn.Failed));
- return false;
- }
-
- // Measurement Loop
- Task.Run(() =>
- {
- // Mark regulation measurement as active
- SharedCyclicMeasSequ = CyclicMeasSequ.REGULATION;
-
- do
- {
- InitActualProcessProgress();
- foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn))
- {
- try
- {
- // 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;
- cmdName = 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))
- {
- // 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))
- {
- var response = new CmdResponse(cmdName, info,
- doubleValue: measTolerance * _toleranceRawToPercentScale * signMultiplier,
- siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.PERCENTAGE_NON_SI));
- PublishResponse(fm2014,
- new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
- specificInfoObj: response));
- }
- }
- }
-
- // Read the REF period and calculate the frequency [Hz] and flow rate [m³/h] based on this period
- cmdName = CmdName.CMD_GET_REF_PERIOD;
- info = GetCmdInfo(cmdName);
- if (RequestDebugInformation && SharedCyclicMeasSequ != CyclicMeasSequ.IDLE &&
- Write(cmdName, fm2014))
- {
- if (Read(cmdName, fm2014, out responseStr) &&
- int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var period))
- {
- // Publish the period [ms]
- var response = new CmdResponse(cmdName, info,
- doubleValue: TMR_RESOLUTION_s * 1000.0 * period,
- siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME));
- PublishResponse(fm2014,
- new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
- specificInfoObj: response));
-
- // Publish the frequency in [Hz]
- cmdName = CmdName.CMD_CAL_FREQU_REF_PERIOD;
- info = GetCmdInfo(cmdName);
- response = new CmdResponse(cmdName, info,
- doubleValue: 1.0 / (TMR_RESOLUTION_s * period),
- siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY));
- PublishResponse(fm2014,
- new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
- specificInfoObj: response));
-
- // Convert to m³/h (3600 s/h) based on REF pulse rate
- RefFlowRate_cm_per_h = 3600.0 / (period * TMR_RESOLUTION_s) / Ref_pulse_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: RefFlowRate_cm_per_h,
- siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FLOW_RATE_NON_SI));
- PublishResponse(fm2014,
- new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
- specificInfoObj: response));
- }
- }
-
-
- // Read the DUT period and calculate the frequency [Hz] and flow rate [m³/h] based on this period
- cmdName = CmdName.CMD_GET_DUT_PERIOD;
- info = GetCmdInfo(cmdName);
- if (RequestDebugInformation &&
- SharedCyclicMeasSequ != CyclicMeasSequ.IDLE &&
- Write(cmdName, fm2014))
- {
- if (Read(cmdName, fm2014, out responseStr) &&
- int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var period))
- {
- // Publish the period [ms]
- var response = new CmdResponse(cmdName, info,
- doubleValue: TMR_RESOLUTION_s * 1000.0 * period,
- siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME));
- PublishResponse(fm2014,
- new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
- specificInfoObj: response));
-
- // Publish the frequency in [Hz]
- cmdName = CmdName.CMD_CAL_FREQU_DUT_PERIOD;
- info = GetCmdInfo(cmdName);
- response = new CmdResponse(cmdName, info,
- doubleValue: 1.0 / (TMR_RESOLUTION_s * period),
- siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY));
- PublishResponse(fm2014,
- new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
- specificInfoObj: response));
-
- // Convert to m³/h (3600 s/h) based on DUT pulse rate
- DutFlowRate_cm_per_h = 3600.0 / (period * TMR_RESOLUTION_s) / Dut_pulse_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: DutFlowRate_cm_per_h,
- siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FLOW_RATE_NON_SI));
- PublishResponse(fm2014,
- new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
- specificInfoObj: 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) &&
- int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"),
- out var refFrequency))
- {
- // Publish the frequency in [Hz]
- var response = new CmdResponse(cmdName, info, refFrequency,
- siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY));
- PublishResponse(fm2014, new ProcessExecEventArgs("",
- actualProcessMessage: measurementInfo,
- specificInfoObj: response));
-
- // Convert to m³/h (3600 s/h) and save the value
- RefFlowRate_cm_per_h = 3600.0 * refFrequency / Ref_pulse_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: RefFlowRate_cm_per_h,
- siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FLOW_RATE_NON_SI));
- PublishResponse(fm2014,
- new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
- specificInfoObj: response));
- }
- }
- }
- catch (Exception e)
- {
- var response = new CmdResponse(cmdName, e.Message);
- PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
- specificInfoObj: response, statusReturn: StatusReturn.Failed));
- }
- }
- } while (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE);
-
- }, SharedCancellationToken);
-
- return true;
- }
-
///
/// Transfer all standalone settings to FM2014 RAM and safe those to nonvolatile EEPROM
///
@@ -1279,7 +1415,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
cmdName = CmdName.CMD_REF_SET_SCALE;
// During setup of the 'Ref_pulse_per_cm' the '_refToDutScaleStr' will be generated
- retVal = Write(cmdName, this, _refToDutScaleStr);
+ retVal = Write(cmdName, this, RefToDutScaleStr);
}
if (retVal)
@@ -1454,63 +1590,6 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
return InitiateIndividualComm(this);
}
- ///
- /// Reset the measurement but only if receive task is active:
- /// - This should prepare the hardware for a new measurement which takes about 4 s.
- /// - It is a broadcast command which will reset all connected devices!
- ///
- ///
- ///
- /// - Initial.
- ///
- ///
- /// - Delayed reset to allow other tasks to finish.
- ///
- ///
- /// - Support for multiple FM2014s.
- ///
- public Boolean ResetHardwareAllDevices()
- {
- if (SharedCyclicMeasSequ == CyclicMeasSequ.IDLE)
- return true;
-
- InitActualProcessProgress();
- // Exit tasks
- SharedCyclicMeasSequ = CyclicMeasSequ.IDLE;
-
- // Schedule to other tasks to avoid side effects through the reset command
- Task.Factory.StartNew(() =>
- {
- Thread.Sleep(1000);
- }).ContinueWith(delegate
- {
- // Reset measurement
- var infoStr = GetCmdInfo(CmdName.CMD_RST_MEAS);
- if (!Write(CmdName.CMD_RST_MEAS, this))
- return;
-
- // Wait until FM2014 calibration finished
- var resetDelayCtr_ms = 4000;
- const Int32 loopTime_ms = 500;
- MaxActualProcessProgress = resetDelayCtr_ms / loopTime_ms;
- do
- {
- // Change the SI unit to ms instead of seconds
- var response = new CmdResponse(CmdName.CMD_RST_MEAS, infoStr,
- resetDelayCtr_ms, siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME));
- PublishResponse(this, new ProcessExecEventArgs(infoStr,
- actualProcessMessage: infoStr,
- actualProcessPercent: _actualProcessProgress_percent,
- specificInfoObj: response));
- Thread.Sleep(loopTime_ms);
- resetDelayCtr_ms -= loopTime_ms;
- ActualProcessProgress++;
- } while (resetDelayCtr_ms >= 0);
- }, SharedCancellationToken);
-
- return true;
- }
-
///
/// Common routine for reboot being able to override this routine which will be called in
/// to simulate a reboot.
@@ -1530,9 +1609,18 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// as this should be used to prepare the hardware for
/// the new measurement after it being able to start the new measurement immediately!
///
+ ///
+ /// - Initial.
+ ///
private static void ClearMeasurementResultsForAllDevices()
{
-
+ foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn))
+ {
+ fm2014.RefMeasuredTimer_ticks = 0;
+ fm2014.DutMeasuredTimer_ticks = 0;
+ fm2014.RefFlowRate_cm_per_h = 0;
+ fm2014.DutFlowRate_cm_per_h = 0;
+ }
}
///
@@ -1562,7 +1650,6 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
SharedSerialPort.ReadTimeout = 1000;
SharedSerialPort.WriteTimeout = 1000;
}
-
var cmdName = CmdName.CMD_SERIAL;
try
{
diff --git a/FM2014TestApp/Ui/FM2014TestBenchWinFrms/FrmFm2014TestApp.cs b/FM2014TestApp/Ui/FM2014TestBenchWinFrms/FrmFm2014TestApp.cs
index 41cedd3f..32b15770 100644
--- a/FM2014TestApp/Ui/FM2014TestBenchWinFrms/FrmFm2014TestApp.cs
+++ b/FM2014TestApp/Ui/FM2014TestBenchWinFrms/FrmFm2014TestApp.cs
@@ -777,7 +777,7 @@ namespace Sensus.Ui.FM2014TestApp
{
ActionControl(true);
_autoProgressBar = true;
- if (Fm2014.RegulationMeasurement())
+ if (FM2014.RegulationMeasurement())
{
btnDutToRefRegulation.Text = Resources.StrBtnStopRegulation;
btnDutToRefRegulation.Enabled = true;
@@ -788,7 +788,7 @@ namespace Sensus.Ui.FM2014TestApp
_autoProgressBar = false;
// Deactivate the button temporary to avoid repeated execution as the reset takes a certain time
btnDutToRefRegulation.Enabled = false;
- Fm2014.ResetHardwareAllDevices();
+ FM2014.ResetHardwareAllDevices();
ActionControl(false);
btnDutToRefRegulation.Text = Resources.StrBtnStartRegulation;
}
@@ -839,7 +839,7 @@ namespace Sensus.Ui.FM2014TestApp
_autoProgressBar = false;
// Deactivate the button temporary to avoid repeated execution as the reset takes a certain time
btnManualRefCalibration.Enabled = false;
- Fm2014.ResetHardwareAllDevices();
+ FM2014.ResetHardwareAllDevices();
ActionControl(false);
btnManualRefCalibration.Text = Resources.StrBtnStartCalibration;
}
diff --git a/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml b/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml
index 91384c38..24f19b15 100644
--- a/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml
+++ b/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml
@@ -15,15 +15,15 @@