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

This commit is contained in:
Thomas Wiedebusch 2026-02-09 20:42:00 +01:00
parent 45fbf4f854
commit c6d8cefc62
12 changed files with 1564 additions and 833 deletions

View File

@ -8,8 +8,7 @@
* @details <i>Implements the command table and interprocess-communication structs for the FM2014.</i>
*
* @copyright © SENSUS GmbH 2026. All rights reserved.
*********************************************************************************************************************/using Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core;
*********************************************************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
@ -642,7 +641,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co
/// <summary>
/// Measurement SI unit
/// </summary>
public String SiUnit { get; private set; }
public String Unit { get; private set; }
/// <summary>
/// Ctor
@ -651,15 +650,15 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co
/// <param name="answerStr"></param>
/// <param name="intValue"></param>
/// <param name="doubleValue"></param>
/// <param name="siUnit"></param>
/// <param name="unit"></param>
public CmdResponse(CmdName cmdName, String answerStr, Int32? intValue = null, Double? doubleValue = null,
String siUnit = "")
String unit = "")
{
CmdName = cmdName;
AnswerStr = answerStr;
IntValue = intValue;
DoubleValue = doubleValue;
SiUnit = siUnit;
Unit = unit;
}
}

View File

@ -51,19 +51,19 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// Minimal value which can be stored to the FM2014 with commands
/// 'V' - 'DUT pulses per volume' and 'W' - 'REF pulses per volume'!
/// </summary>
private const UInt32 PULSES_PER_CM_REGULATION_SETUP_MIN = 1;
public const UInt32 PULSES_PER_CM_REGULATION_SETUP_MIN = 1;
/// <summary>
/// Maximal value which can be stored to the FM2014 with command
/// 'V' - 'DUT pulses per volume' and 'W' - 'REF pulses per volume'!
/// </summary>
private const UInt32 PULSES_PER_CM_REGULATION_SETUP_MAX = 9999;
public const UInt32 PULSES_PER_CM_REGULATION_SETUP_MAX = 9999;
/// <summary>
/// This is the maximum input value for the REF which CANNOT be stored to FM2014
/// but will be used to calculate the 'RefToDutScale_norm'.
/// </summary>
private const UInt32 REF_PULSES_PER_CM_REGULATION_INPUT_MAX = 100000000;
public const UInt32 REF_PULSES_PER_CM_REGULATION_INPUT_MAX = 100000000;
/// <summary>
/// FM2014 minimum REF to DUT scale for error display.
@ -370,67 +370,67 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// </summary>
public Boolean UseDampedTolerance { get; set; }
private UInt16 _refRequiredTimeMeasurement_pulses;
private UInt16 _refPulsesRequired;
/// <summary>
/// Required REF pulses for time measurement.
/// </summary>
public UInt16 RefRequiredTimeMeasurement_pulses
public UInt16 RefPulsesRequired
{
get => _refRequiredTimeMeasurement_pulses;
get => _refPulsesRequired;
set
{
// Check limits and equality
if (value < TIME_MEASUREMENT_INPUT_MIN_pulses ||
value == _refRequiredTimeMeasurement_pulses)
value == _refPulsesRequired)
return;
_refRequiredTimeMeasurement_pulses = value;
_refPulsesRequired = value;
}
}
private UInt16 _dutRequiredTimeMeasurement_pulses;
private UInt16 _dutPulsesRequired;
/// <summary>
/// Required DUT pulses for time measurement.
/// </summary>
public UInt16 DutRequiredTimeMeasurement_pulses
public UInt16 DutPulsesRequired
{
get => _dutRequiredTimeMeasurement_pulses;
get => _dutPulsesRequired;
set
{
// Check limits and equality
if (value < TIME_MEASUREMENT_INPUT_MIN_pulses ||
value == _dutRequiredTimeMeasurement_pulses)
value == _dutPulsesRequired)
return;
_dutRequiredTimeMeasurement_pulses = value;
_dutPulsesRequired = value;
}
}
private UInt32 _refMeasuredTimer_ticks;
private UInt32 _refTimerTicksMeasured;
/// <summary>
/// REF timer ticks of measured pulses
/// </summary>
public UInt32 RefMeasuredTimer_ticks
public UInt32 RefTimerTicksMeasured
{
get => _refMeasuredTimer_ticks;
get => _refTimerTicksMeasured;
private set
{
_refMeasuredTimer_ticks = value;
RefMeasuredTime_s = value * TMR_RESOLUTION_s;
_refTimerTicksMeasured = value;
RefTimeMeasured_s = value * TMR_RESOLUTION_s;
}
}
private UInt32 _dutMeasuredTimer_ticks;
private UInt32 _dutTimerTicksMeasured;
/// <summary>
/// DUT timer ticks of measured pulses
/// </summary>
public UInt32 DutMeasuredTimer_ticks
public UInt32 DutTimerTicksMeasured
{
get => _dutMeasuredTimer_ticks;
get => _dutTimerTicksMeasured;
private set
{
_dutMeasuredTimer_ticks = value;
DutMeasuredTime_s = value * TMR_RESOLUTION_s;
_dutTimerTicksMeasured = value;
DutTimeMeasured_s = value * TMR_RESOLUTION_s;
}
}
@ -448,22 +448,22 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
///
/// Remaining REF pulses for time measurement.
/// </summary>
public UInt16 RefRemainingTimeMeasurement_pulses { get; private set; }
public UInt16 RefPulsesRemaining { get; private set; }
/// <summary>
/// Remaining DT pulses for time measurement.
/// </summary>
public UInt16 DutRemainingTimeMeasurement_pulses { get; private set; }
public UInt16 DutPulsesRemaining { get; private set; }
/// <summary>
/// Measured REF time of required pulses.
/// </summary>
public Double RefMeasuredTime_s { get; private set; }
public Double RefTimeMeasured_s { get; private set; }
/// <summary>
/// Measured DUT time of required pulses.
/// </summary>
public Double DutMeasuredTime_s { get; private set; }
public Double DutTimeMeasured_s { get; private set; }
private UInt32 _ref_pulse_per_cm;
/// <summary>
@ -577,7 +577,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
}
}
private Double _doublePulseDeadtime_ms = 0.0;
private Double _doublePulseDeadtime_ms;
/// <summary>
/// Double pulse deadtime in milliseconds to avoid on small oscillating input a repeated pulse.
/// </summary>
@ -592,14 +592,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
_doublePulseDeadtime_ms = value;
// Calculate and set up the values 'S' and 's' needed for the FM2014
var tempBase_ms = (UInt16) value;
var tempBase_ms = (UInt16)value;
var tempMultiplier = 1;
while (tempBase_ms > DOUBLE_PULSE_BASE_DEADTIME_MAX_ms &&
while (tempBase_ms > DOUBLE_PULSE_BASE_DEADTIME_MAX_ms &&
tempMultiplier < DOUBLE_PULSE_DEADTIME_MULTIPLIER_INPUT_MAX)
{
tempMultiplier++;
tempBase_ms = (UInt16)(value / tempMultiplier);
}
}
DoublePulseBaseDeadTime_ms = tempBase_ms;
DoublePulseMultiplier = (Byte)tempMultiplier;
}
@ -665,6 +665,11 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// </summary>
public Boolean RequestDebugInformation { get; set; }
/// <summary>
/// Measured tolerance during calibration
/// </summary>
public Double DutToRefToleranceMeasured_percent { get; private set; }
#endregion ---------------------------------------- object properties -----------------------------------------
#region ------------------------------------------- static methods --------------------------------------------
@ -718,7 +723,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
// 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));
resetDelayCtr_ms, unit: "m" + Units.GetInfo(Units.Name.TIME_s));
PublishResponse(firstActiveFm2014, new ProcessExecEventArgs(infoStr,
actualProcessMessage: infoStr,
actualProcessPercent: _actualProcessProgress_percent,
@ -820,6 +825,195 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
fm2014?.OnRawRecordReceived?.Invoke(fm2014, processExecEventArgs);
}
/// <summary>
/// Pulse counter measurement:
/// - REF counter (default ON),
/// - DUT counter (default OFF),
/// - 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.
/// Preconditions:
/// - The <see cref="ResetHardwareAllDevices"/> has to be executed in advance.
/// Initial setup:
/// - Starts the pulse counter measurement of REF and/or DUT.
/// Cyclic:
/// - Request REF and/or DUT pulse counter value(s).
/// Exit:
/// - Set <see cref="SharedCyclicMeasSequ"/> to false.
/// </summary>
/// <param name="refCtrOn">request to count REF pulses</param>
/// <param name="dutCtrOn">request to count DUT pulses</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">
/// - 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(Boolean refCtrOn = true, Boolean dutCtrOn = false)
{
// If the cyclic task has already been started everything is fine
if (SharedCyclicMeasSequ == CyclicMeasSequ.PULSE_CTR)
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 any pulse counter is required
if (!firstActiveFm2014.RefPulseCtrOn && !firstActiveFm2014.DutPulseCtrOn)
{
return false;
}
var cmdName = CmdName.CMD_NA;
String measurementInfoStr;
// Prepare REF and/or DUT pulse counter measurement, the first call activates if one or both are started!
try
{
switch (firstActiveFm2014.RefPulseCtrOn)
{
case true when firstActiveFm2014.DutPulseCtrOn:
cmdName = CmdName.CMD_REF_DUT_STR_PLS_CTR;
measurementInfoStr = Resources.StrMeasMsgPulseCtrRefDut;
break;
case true:
cmdName = CmdName.CMD_REF_STR_PLS_CTR;
measurementInfoStr = Resources.StrMeasMsgPulseCtrRef;
break;
default:
cmdName = CmdName.CMD_DUT_STR_PLS_CTR;
measurementInfoStr = Resources.StrMeasMsgPulseCtrDut;
break;
}
// Start the pulse counter measurement
if (!Write(cmdName, firstActiveFm2014))
{
return false;
}
}
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 pulse counter measurement as active
SharedCyclicMeasSequ = CyclicMeasSequ.PULSE_CTR;
do
{
CmdName cmdNameRef;
CmdName cmdNameDut;
Boolean syncPulseCtr;
// Check if any FM2014 has a synchronized request to synchronize all FM2014 pulse counter values
if (RegisteredFm2014s.Any(x => x.RefPulseCtrOn && x.DutPulseCtrOn))
{
cmdNameDut = CmdName.CMD_DUT_GET_PLS_CTR_BU;
cmdNameRef = CmdName.CMD_REF_GET_PLS_CTR_BU;
syncPulseCtr = true;
}
else
{
cmdNameDut = CmdName.CMD_DUT_GET_PLS_CTR;
cmdNameRef = CmdName.CMD_REF_GET_PLS_CTR;
syncPulseCtr = false;
}
// Get the process information
var infoRef = GetCmdInfo(cmdNameRef);
var infoDut = GetCmdInfo(cmdNameDut);
// Make a backup of the pulse counters to synchronize those and read out the backups
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && syncPulseCtr)
{
// Synchronized backup of REF and DUT pulse counter
Write(CmdName.CMD_REF_DUT_BU_PLS_CTR, firstActiveFm2014);
}
foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn))
{
try
{
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE)
{
if (fm2014.RefPulseCtrOn && Write(cmdNameRef, fm2014))
{
if (Read(cmdNameRef, fm2014, out var responseStr) &&
int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"),
out var intValue))
{
var response = new CmdResponse(cmdNameRef, infoRef, intValue);
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr,
specificInfoObj: response));
}
}
if (fm2014.DutPulseCtrOn && Write(cmdNameDut, fm2014))
{
if (Read(cmdNameDut, fm2014, out var responseStr) &&
int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"),
out var intValue))
{
var response = new CmdResponse(cmdNameDut, infoDut, intValue);
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr,
specificInfoObj: response));
}
}
}
}
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).ContinueWith(delegate
{
foreach (var fm2014 in RegisteredFm2014s)
{
if (fm2014 != null)
{
fm2014.DutPulseCtrOn = false;
fm2014.RefPulseCtrOn = false;
}
}
});
return true;
}
/// <summary>
/// The regulation measurement compares the DUT to REF tolerance
///
@ -943,7 +1137,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
var response = new CmdResponse(cmdName, info,
doubleValue: measTolerance * fm2014.ToleranceRawToPercentScale * signMultiplier,
siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.PERCENTAGE_NON_SI));
unit: Units.GetInfo(Units.Name.PERCENT));
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
@ -963,7 +1157,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
// Publish the period [ms]
var response = new CmdResponse(cmdName, info,
doubleValue: TMR_RESOLUTION_s * 1000.0 * period,
siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME));
unit: "m" + Units.GetInfo(Units.Name.TIME_s));
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
@ -973,7 +1167,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
info = GetCmdInfo(cmdName);
response = new CmdResponse(cmdName, info,
doubleValue: 1.0 / (TMR_RESOLUTION_s * period),
siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY));
unit: Units.GetInfo(Units.Name.FREQUENCY_Hz));
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
@ -984,7 +1178,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
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));
unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h));
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
@ -1005,7 +1199,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
// Publish the period [ms]
var response = new CmdResponse(cmdName, info,
doubleValue: TMR_RESOLUTION_s * 1000.0 * period,
siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME));
unit: "m" + Units.GetInfo(Units.Name.TIME_s));
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
@ -1015,7 +1209,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
info = GetCmdInfo(cmdName);
response = new CmdResponse(cmdName, info,
doubleValue: 1.0 / (TMR_RESOLUTION_s * period),
siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY));
unit: Units.GetInfo(Units.Name.FREQUENCY_Hz));
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
@ -1026,7 +1220,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
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));
unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h));
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
@ -1045,7 +1239,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
// Publish the frequency in [Hz]
var response = new CmdResponse(cmdName, info, refFrequency,
siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY));
unit: Units.GetInfo(Units.Name.FREQUENCY_Hz));
PublishResponse(fm2014, new ProcessExecEventArgs("",
actualProcessMessage: measurementInfo,
specificInfoObj: response));
@ -1056,7 +1250,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
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));
unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h));
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
@ -1086,8 +1280,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
///
/// Preconditions:
/// - The <see cref="ResetHardwareAllDevices"/> has to be executed in advance,
/// - The <see cref="RefRequiredTimeMeasurement_pulses"/>REF pulses to count has to be preset,
/// - The <see cref="DutRemainingTimeMeasurement_pulses"/>DUT pulses to count has to be preset,
/// - 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:
@ -1108,7 +1302,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// <remarks date="2026-Jan-28..29" author="Thomas Wiedebusch">
/// - Support for multiple FM2014s.
/// </remarks>
/// <remarks date="2026-Feb-05" author="Thomas Wiedebusch">
/// <remarks date="2026-Feb-05..09" author="Thomas Wiedebusch">
/// - Static.
/// </remarks>
public static Boolean CalibrationMeasurement()
@ -1126,8 +1320,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
return false;
// Check if regulation can be done
if (firstActiveFm2014.RefRequiredTimeMeasurement_pulses < TIME_MEASUREMENT_INPUT_MIN_pulses ||
firstActiveFm2014.DutRequiredTimeMeasurement_pulses < TIME_MEASUREMENT_INPUT_MIN_pulses)
if (firstActiveFm2014.RefPulsesRequired < TIME_MEASUREMENT_INPUT_MIN_pulses ||
firstActiveFm2014.DutPulsesRequired < TIME_MEASUREMENT_INPUT_MIN_pulses)
{
return false;
}
@ -1135,7 +1329,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
var cmdName = CmdName.CMD_NA;
var measurementInfo = Resources.StrMeasMsgCalibration;
InitActualProcessProgress(3);
// Prepare calibration measurement
try
{
@ -1165,13 +1359,13 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
ActualProcessProgress++;
cmdName = CmdName.CMD_REF_SET_PLS;
if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.RefRequiredTimeMeasurement_pulses, true))
if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.RefPulsesRequired, true))
{
return false;
}
ActualProcessProgress++;
cmdName = CmdName.CMD_DUT_SET_PLS;
if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.DutRequiredTimeMeasurement_pulses, true))
if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.DutPulsesRequired, true))
{
return false;
}
@ -1190,31 +1384,33 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
// Mark calibration measurement as active
SharedCyclicMeasSequ = CyclicMeasSequ.CALIBRATION;
InitActualProcessProgress(firstActiveFm2014.RefRequiredTimeMeasurement_pulses);
InitActualProcessProgress(firstActiveFm2014.RefPulsesRequired);
do
{
String responseStr;
String info;
foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn))
{
try
{
String responseStr;
cmdName = CmdName.CMD_REF_GET_PLS_RMN;
var info = GetCmdInfo(cmdName);
info = GetCmdInfo(cmdName);
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014))
{
if (Read(cmdName, fm2014, out responseStr) &&
ushort.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var pulses))
{
fm2014.RefRequiredTimeMeasurement_pulses = pulses;
fm2014.RefPulsesRemaining = pulses;
var response = new CmdResponse(cmdName, info, pulses);
PublishResponse(fm2014, new ProcessExecEventArgs("",
actualProcessMessage: measurementInfo,
specificInfoObj: response));
ActualProcessProgress = fm2014.RefRequiredTimeMeasurement_pulses -
fm2014.RefRemainingTimeMeasurement_pulses;
ActualProcessProgress = fm2014.RefPulsesRequired -
fm2014.RefPulsesRemaining;
}
}
cmdName = CmdName.CMD_DUT_GET_PLS_RMN;
info = GetCmdInfo(cmdName);
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014))
@ -1222,7 +1418,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
if (Read(cmdName, fm2014, out responseStr) &&
ushort.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var pulses))
{
fm2014.DutRemainingTimeMeasurement_pulses = pulses;
fm2014.DutPulsesRemaining = pulses;
var response = new CmdResponse(cmdName, info, pulses);
PublishResponse(fm2014, new ProcessExecEventArgs("",
actualProcessMessage: measurementInfo,
@ -1237,12 +1433,75 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
specificInfoObj: response, statusReturn: StatusReturn.Failed));
}
}
// Check all remaining DUT and REF pulses are counted on all connected devices and finalize process by
// reading out all results
if (RegisteredFm2014s.All(fm2014 => fm2014.RefRemainingTimeMeasurement_pulses == 0 &&
fm2014.DutRemainingTimeMeasurement_pulses == 0))
if (RegisteredFm2014s.All(fm2014 => fm2014.RefPulsesRemaining == 0 &&
fm2014.DutPulsesRemaining == 0))
{
// Read out the result
foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn))
{
try
{
// Read out the result
cmdName = CmdName.CMD_REF_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.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));
}
}
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));
}
}
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));
}
}
catch (Exception e)
{
var response = new CmdResponse(cmdName, e.Message);
PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, statusReturn: StatusReturn.Failed));
}
}
ResetHardwareAllDevices();
// Exit this task
SharedCyclicMeasSequ = CyclicMeasSequ.IDLE;
}
} while (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE);
@ -1567,190 +1826,6 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
#endregion ---------------------------------------- static methods --------------------------------------------
#region ------------------------------------------- object methods --------------------------------------------
/// <summary>
/// Pulse counter measurement:
/// - REF counter (default ON),
/// - DUT counter (default OFF),
/// - 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.
/// Preconditions:
/// - The <see cref="ResetHardwareAllDevices"/> has to be executed in advance.
/// Initial setup:
/// - Starts the pulse counter measurement of REF and/or DUT.
/// Cyclic:
/// - Request REF and/or DUT pulse counter value(s).
/// Exit:
/// - Set <see cref="SharedCyclicMeasSequ"/> to false.
/// </summary>
/// <param name="refCtrOn">request to count REF pulses</param>
/// <param name="dutCtrOn">request to count DUT pulses</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">
/// - 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>
public Boolean PulseCounterMeasurement(Boolean refCtrOn = true, Boolean dutCtrOn = false)
{
if (!IsLoggedOn || (!refCtrOn && !dutCtrOn))
return false;
// Init pulse counter settings, important to select different settings for other FM2014s than the first
// one e.g. The first may count the REF and DUT while the others need only the DUT as the REF is parallel
// as input for all devices.
RefPulseCtrOn = refCtrOn;
DutPulseCtrOn = dutCtrOn;
// If the cyclic task has already been started everything is fine
if (SharedCyclicMeasSequ == CyclicMeasSequ.PULSE_CTR)
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;
String measurementInfoStr;
// Prepare REF and/or DUT pulse counter measurement, the first call activates if one or both are started!
try
{
switch (RefPulseCtrOn)
{
case true when DutPulseCtrOn:
cmdName = CmdName.CMD_REF_DUT_STR_PLS_CTR;
measurementInfoStr = Resources.StrMeasMsgPulseCtrRefDut;
break;
case true:
cmdName = CmdName.CMD_REF_STR_PLS_CTR;
measurementInfoStr = Resources.StrMeasMsgPulseCtrRef;
break;
default:
cmdName = CmdName.CMD_DUT_STR_PLS_CTR;
measurementInfoStr = Resources.StrMeasMsgPulseCtrDut;
break;
}
// Start the pulse counter measurement
if (!Write(cmdName, this))
{
return false;
}
}
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 pulse counter measurement as active
SharedCyclicMeasSequ = CyclicMeasSequ.PULSE_CTR;
do
{
CmdName cmdNameRef;
CmdName cmdNameDut;
Boolean syncPulseCtr;
// Check if any FM2014 has a synchronized request to synchronize all FM2014 pulse counter values
if (RegisteredFm2014s.Any(x => x.RefPulseCtrOn && x.DutPulseCtrOn))
{
cmdNameDut = CmdName.CMD_DUT_GET_PLS_CTR_BU;
cmdNameRef = CmdName.CMD_REF_GET_PLS_CTR_BU;
syncPulseCtr = true;
}
else
{
cmdNameDut = CmdName.CMD_DUT_GET_PLS_CTR;
cmdNameRef = CmdName.CMD_REF_GET_PLS_CTR;
syncPulseCtr = false;
}
// Get the process information
var infoRef = GetCmdInfo(cmdNameRef);
var infoDut = GetCmdInfo(cmdNameDut);
try
{
// Make a backup of the pulse counters to synchronize those and read out the backups
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && syncPulseCtr)
{
// Synchronized backup of REF and DUT pulse counter
Write(CmdName.CMD_REF_DUT_BU_PLS_CTR, this);
}
foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn))
{
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE)
{
if (fm2014.RefPulseCtrOn && Write(cmdNameRef, fm2014))
{
if (Read(cmdNameRef, fm2014, out var responseStr) &&
int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"),
out var intValue))
{
var response = new CmdResponse(cmdNameRef, infoRef, intValue);
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr,
specificInfoObj: response));
}
}
if (fm2014.DutPulseCtrOn && Write(cmdNameDut, fm2014))
{
if (Read(cmdNameDut, fm2014, out var responseStr) &&
int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"),
out var intValue))
{
var response = new CmdResponse(cmdNameDut, infoDut, intValue);
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr,
specificInfoObj: response));
}
}
}
}
}
catch (Exception e)
{
var response = new CmdResponse(cmdName, e.Message);
PublishResponse(this, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, statusReturn: StatusReturn.Failed));
}
} while (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE);
}, SharedCancellationToken).ContinueWith(delegate
{
foreach (var fm2014 in RegisteredFm2014s)
{
if (fm2014 != null)
{
fm2014.DutPulseCtrOn = false;
fm2014.RefPulseCtrOn = false;
}
}
});
return true;
}
/// <summary>
/// Transfer all standalone settings to FM2014 RAM and safe those to nonvolatile EEPROM
/// </summary>
@ -1990,8 +2065,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn))
{
fm2014.RefMeasuredTimer_ticks = 0;
fm2014.DutMeasuredTimer_ticks = 0;
fm2014.RefTimerTicksMeasured = 0;
fm2014.DutTimerTicksMeasured = 0;
fm2014.RefFlowRate_cm_per_h = 0;
fm2014.DutFlowRate_cm_per_h = 0;
}

View File

@ -1,173 +0,0 @@
/****************************************************************************************/
/*!@file FM2014Commands.cs
* @brief Command list of the FM2014.
*
*=======================================================================================\n
* @copyright
*
*_______________________________________________________________________________________\n
* Copyright (c) 2026 SENSUS GmbH.\n
* All Rights Reserved.\n
* \n
* Confidential property of\n
* SENSUS GmbH,\n
* Meineckestr. 10, 30880 LAATZEN, GERMANY\n
* \n
*=======================================================================================\n
* @authors
*
*_______________________________________________________________________________________\n
* Thomas Wiedebusch\n
*
*
*=======================================================================================\n
* @version
*
*_______________________________________________________________________________________\n
* V1.00 10-Jan-2026 by Thomas Wiedebusch\n
* - Initial.
*
*
*=======================================================================================\n
*/
using System;
using System.Linq;
namespace Xylem.Common.Metrology.Measurements.Consts
{
/// <summary>
/// SI Units
/// </summary>
public static class SiUnits
{
/// <summary>
/// Unit definitions based on SI or derived SI or NON SI
/// </summary>
public enum SiUnitName
{
/// <summary>
///
/// </summary>
UNITLESS_NON_SI,
/// <summary>
///
/// </summary>
PERCENTAGE_NON_SI,
/// <summary>
///
/// </summary>
VOLTAGE,
/// <summary>
///
/// </summary>
CURRENT,
/// <summary>
///
/// </summary>
ELECTRICAL_POWER,
/// <summary>
///
/// </summary>
ELECTRICAL_LOAD,
/// <summary>
///
/// </summary>
DISTANCE,
/// <summary>
///
/// </summary>
FLOW_RATE_NON_SI,
/// <summary>
///
/// </summary>
TEMPERATURE,
/// <summary>
///
/// </summary>
TIME,
/// <summary>
///
/// </summary>
PRESSURE,
/// <summary>
///
/// </summary>
FREQUENCY
}
/// <summary>
/// Combine SI unit names with string value
/// </summary>
public struct SiUnit
{
/// <summary>
/// Hard coded return string from FM2014
/// </summary>
public readonly SiUnitName SiUnitName;
/// <summary>
/// SI unit string
/// </summary>
public readonly String SiUnitAbbreviationStr;
/// <summary>
/// Ctor
/// </summary>
/// <param name="siUnitName"></param>
/// <param name="siUnitAbbreviationStr"></param>
public SiUnit(SiUnitName siUnitName, String siUnitAbbreviationStr)
{
SiUnitName = siUnitName;
SiUnitAbbreviationStr = siUnitAbbreviationStr;
}
}
/// <summary>
/// Table of SI and NON-SI units
/// </summary>
public static readonly SiUnit[] SiUnitTbl =
{
new SiUnit(SiUnitName.UNITLESS_NON_SI, ""),
new SiUnit(SiUnitName.PERCENTAGE_NON_SI, "%"),
new SiUnit(SiUnitName.VOLTAGE, "V"),
new SiUnit(SiUnitName.CURRENT, "A"),
new SiUnit(SiUnitName.ELECTRICAL_POWER, "W"),
new SiUnit(SiUnitName.ELECTRICAL_LOAD, "Ah"),
new SiUnit(SiUnitName.DISTANCE, "m"),
new SiUnit(SiUnitName.FLOW_RATE_NON_SI, "m³/h"),
new SiUnit(SiUnitName.TEMPERATURE, "°C"),
new SiUnit(SiUnitName.TIME, "s"),
new SiUnit(SiUnitName.PRESSURE, "Pa"),
new SiUnit(SiUnitName.FREQUENCY, "Hz"),
};
/// <summary>
/// Get the information string
/// </summary>
/// <param name="siUnitName"></param>
/// <returns>Unit string</returns>
/// <remarks date="2026-Jan-10" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static String GetInfo(SiUnitName siUnitName)
{
return (from sit in SiUnitTbl
where sit.SiUnitName == siUnitName
select sit.SiUnitAbbreviationStr).FirstOrDefault();
}
}
}

View File

@ -0,0 +1,157 @@
/*********************************************************************************************************************/
/*! @file Unit.cs
* @brief <i>Units for measurements</i>
*
* @author Thomas Wiedebusch
* @date 2026-Jan-08
*
* @details <i>Implements the command table and interprocess-communication structs for the FM2014.</i>
*
* @copyright © SENSUS GmbH 2026. All rights reserved.
*********************************************************************************************************************/
using System;
using System.Linq;
namespace Xylem.Common.Metrology.Measurements.Consts
{
/// <summary>
/// SI Units
/// </summary>
public static class Units
{
/// <summary>
/// Unit definitions based on SI or derived SI or NON SI
/// </summary>
public enum Name
{
/// <summary>
/// Value without any attachable unit []
/// </summary>
UNITLESS,
/// <summary>
/// Normalized to +/- 1.000 [norm]
/// </summary>
NORMALIZED,
/// <summary>
/// Percent [%]
/// </summary>
PERCENT,
/// <summary>
/// Voltage in Volts [V]
/// </summary>
VOLTAGE_V,
/// <summary>
/// Current in Amperes [A]
/// </summary>
CURRENT_A,
/// <summary>
/// Electrical Power in Watts [W]
/// </summary>
ELECTRICAL_POWER_W,
/// <summary>
/// Electrical Load in Ampere hours [Ah]
/// </summary>
ELECTRICAL_LOAD_Ah,
/// <summary>
/// Distance in Meters [m]
/// </summary>
DISTANCE_m,
/// <summary>
/// Flow rate in cubic-meters per hour [m³/h]
/// </summary>
FLOW_RATE_cm_per_h,
/// <summary>
/// Temperature in degree Celsius [°C]
/// </summary>
TEMPERATURE_degC,
/// <summary>
/// Time in seconds [s]
/// </summary>
TIME_s,
/// <summary>
/// Pressure in Pascal [Pa]
/// </summary>
PRESSURE_Pa,
/// <summary>
/// Frequency in Hertz [Hz]
/// </summary>
FREQUENCY_Hz
}
/// <summary>
/// Combine SI unit names with string value
/// </summary>
public struct SiUnit
{
/// <summary>
/// Hard coded return string from FM2014
/// </summary>
public readonly Name Name;
/// <summary>
/// SI unit string
/// </summary>
public readonly String AbbreviationStr;
/// <summary>
/// Ctor
/// </summary>
/// <param name="name"></param>
/// <param name="abbreviationStr"></param>
public SiUnit(Name name, String abbreviationStr)
{
Name = name;
AbbreviationStr = abbreviationStr;
}
}
/// <summary>
/// Table of SI and NON-SI units
/// </summary>
public static readonly SiUnit[] SiUnitTbl =
{
new SiUnit(Name.UNITLESS, ""),
new SiUnit(Name.NORMALIZED, "norm"),
new SiUnit(Name.PERCENT, "%"),
new SiUnit(Name.VOLTAGE_V, "V"),
new SiUnit(Name.CURRENT_A, "A"),
new SiUnit(Name.ELECTRICAL_POWER_W, "W"),
new SiUnit(Name.ELECTRICAL_LOAD_Ah, "Ah"),
new SiUnit(Name.DISTANCE_m, "m"),
new SiUnit(Name.FLOW_RATE_cm_per_h, "m³/h"),
new SiUnit(Name.TEMPERATURE_degC, "°C"),
new SiUnit(Name.TIME_s, "s"),
new SiUnit(Name.PRESSURE_Pa, "Pa"),
new SiUnit(Name.FREQUENCY_Hz, "Hz"),
};
/// <summary>
/// Get the information string
/// </summary>
/// <param name="name"></param>
/// <returns>Unit string</returns>
/// <remarks date="2026-Jan-10" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static String GetInfo(Name name)
{
return (from sit in SiUnitTbl
where sit.Name == name
select sit.AbbreviationStr).FirstOrDefault();
}
}
}

View File

@ -212,7 +212,7 @@
<Compile Include="Consts\MeasurementResultsStates.cs" />
<Compile Include="Consts\MeasurementStates.cs" />
<Compile Include="Consts\ProcessStates.cs" />
<Compile Include="Consts\SiUnits.cs" />
<Compile Include="Consts\Units.cs" />
<Compile Include="IMeasurement.cs" />
<Compile Include="IMeasurementRecord.cs" />
<Compile Include="MeasurementRecord.cs" />

View File

@ -1092,12 +1092,12 @@ namespace Sensus.Ui.FM2014TestApp
tbxDutPulsePerCm.Text = $@"{Fm2014.Dut_pulse_per_cm:D}";
}
// Setup FM2014
if (int.TryParse(tbxDutPulsePerCm.Text, out var pulses_per_cm))
if (ushort.TryParse(tbxDutPulsePerCm.Text, out var pulses_per_cm))
{
//Backup the actual setting to detect changes
var backupPulses_per_cm = Fm2014.Dut_pulse_per_cm;
// Try to set the new calculated REF pulses limited by the property setter
Fm2014.Dut_pulse_per_cm = (UInt32)pulses_per_cm;
Fm2014.Dut_pulse_per_cm = pulses_per_cm;
// Output the Fm2014 setting to avoid wrong display of invalid ranges as this will be
// limited during the setup of the FM2014 property!
tbxDutPulsePerCm.Text = $@"{Fm2014.Dut_pulse_per_cm:D}";
@ -1280,7 +1280,7 @@ namespace Sensus.Ui.FM2014TestApp
}
else if (resp.IntValue != null)
{
LogText($"{resp.AnswerStr}: {resp.IntValue:D} {resp.SiUnit}");
LogText($"{resp.AnswerStr}: {resp.IntValue:D} {resp.Unit}");
switch (resp.CmdName)
{
case FM2014CmdDef.CmdName.CMD_REF_GET_PLS_CTR:
@ -1320,7 +1320,7 @@ namespace Sensus.Ui.FM2014TestApp
}
else if (resp.DoubleValue != null)
{
LogText($"{resp.AnswerStr}: {resp.DoubleValue:F2} {resp.SiUnit}");
LogText($"{resp.AnswerStr}: {resp.DoubleValue:F2} {resp.Unit}");
switch (resp.CmdName)
{
case FM2014CmdDef.CmdName.CMD_GET_UDTLC:
@ -1399,11 +1399,11 @@ namespace Sensus.Ui.FM2014TestApp
}
else if (resp.IntValue != null)
{
LogText($"{resp.AnswerStr}: {resp.IntValue:D} {resp.SiUnit}");
LogText($"{resp.AnswerStr}: {resp.IntValue:D} {resp.Unit}");
}
else if (resp.DoubleValue != null)
{
LogText($"{resp.AnswerStr}: {resp.DoubleValue:F2} {resp.SiUnit}");
LogText($"{resp.AnswerStr}: {resp.DoubleValue:F2} {resp.Unit}");
}
}));
}

View File

@ -72,10 +72,11 @@
</StatusBar>
<!--Background Grid Design-->
<Grid Background="AliceBlue" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="2,2,2,2">
<Grid Background="AliceBlue" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1,1,1,1">
<Grid.RowDefinitions>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="20"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
@ -89,6 +90,8 @@
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="10"/>
<RowDefinition Height="30"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
@ -98,12 +101,10 @@
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="*"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="38"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
@ -146,8 +147,8 @@
</Grid.ColumnDefinitions>
<!-- User controls placeholder for devices-->
<GroupBox Header="FM2014" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="2" Grid.RowSpan="15" Grid.Column="0" Grid.ColumnSpan="26" >
<Grid Background="AliceBlue" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="2,2,2,2">
<GroupBox Header="FM2014" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="2" Grid.RowSpan="15" Grid.Column="0" Grid.ColumnSpan="26" >
<Grid Background="AliceBlue" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1,1,1,1">
<Grid.RowDefinitions>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
@ -207,7 +208,7 @@
<Label x:Name="lblSerialNumberText" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="5" Content="Serial Number:" />
<Label x:Name="lblLifeTimeText" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="11" Grid.Column="0" Grid.ColumnSpan="5" Content="Lifetime:" />
<Button x:Name="btnFM2014Connect" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="13" Grid.Column="0" Grid.ColumnSpan="4" Content="Connect" Click="btnConnect_Click" />
<Button x:Name="btnFM2014Connect" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="13" Grid.Column="0" Grid.ColumnSpan="4" Content="Connect" Click="btnConnect_Click" />
<!-- Rectangle for common measurement labels for all devices -->
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="25" Stroke="LightGray"/>
@ -262,11 +263,11 @@
<!--Pictures-->
<Border Grid.Row="0" Grid.RowSpan="2" Grid.Column="35" Grid.ColumnSpan="5" Margin="2,2,2,2" BorderThickness="1" BorderBrush="Black" >
<Image Margin="0,0,-1,-1" Source="/Sensus_A.jpg" Stretch="Fill" />
<Image Margin="0,0,0,0" Source="/Sensus_A.jpg" Stretch="Fill" />
</Border>
<!--Process Information-->
<GroupBox Header="Info" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="2" Grid.RowSpan="23" Grid.Column="26" Grid.ColumnSpan="14" >
<GroupBox Header="Info" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,0" Grid.Row="2" Grid.RowSpan="23" Grid.Column="26" Grid.ColumnSpan="14" >
<RichTextBox x:Name="rtbLog" Margin="0,0,0,0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" AcceptsTab="True" Width="Auto" UseLayoutRounding="False" HorizontalAlignment="Left" >
<FlowDocument PageWidth="1000"/>
</RichTextBox>
@ -281,19 +282,19 @@
<Label x:Name="lblTotalProcessText" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,0,1,0" Grid.Row="26" Grid.Column="4" Grid.ColumnSpan="8" Content="" />
<Label x:Name="lblActualProgressPercent" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="1,0,1,0" Grid.Row="25" Grid.Column="37" Grid.ColumnSpan="3" Content="0,0 %" />
<Label x:Name="lblTotalProgressPercent" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="1,0,1,0" Grid.Row="26" Grid.Column="37" Grid.ColumnSpan="3" Content="0,0 %" />
<!-- Measurements -->
<TabControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,2,2,2" Grid.Row="17" Grid.RowSpan="8" Grid.Column="0" Grid.ColumnSpan="26" >
<TabControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="17" Grid.RowSpan="8" Grid.Column="0" Grid.ColumnSpan="26" >
<!-- REF to Scale Calibration -->
<TabItem x:Name="tabDutToRefRegulation" Header="DUT to REF Regulation">
<Grid Background="#FFE5E5E5" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="2,2,2,2">
<Grid Background="#FFE5E5E5" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1,1,1,1">
<Grid.RowDefinitions>
<RowDefinition Height="24"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="*"/>
<RowDefinition Height="26"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
@ -324,30 +325,32 @@
<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 [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 [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="lblAttenuation" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="4" Grid.Column="17" Grid.ColumnSpan="5" Content="Attenuation []:" />
<TextBox x:Name="tbxRefPulsesPerCm" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="1" Grid.Column="22" Grid.ColumnSpan="3" Text="100000" MaxLines="1" HorizontalContentAlignment="Right" KeyDown="tbxRefPulsesPerCm_KeyDown" LostFocus="tbxRefPulsesPerCm_LostFocus" TabIndex="102" TextChanged="tbxRefPulsesPerCm_TextChanged" />
<TextBox x:Name="tbxDutPulsesPerCm" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="2" Grid.Column="22" Grid.ColumnSpan="3" Text="1000" MaxLines="1" HorizontalContentAlignment="Right" KeyDown="tbxDutPulsesPerCm_KeyDown" LostFocus="tbxDutPulsesPerCm_LostFocus" TabIndex="103" TextChanged="tbxDutPulsesPerCm_TextChanged"/>
<TextBox x:Name="tbxScaleRefToDut" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="3" Grid.Column="22" Grid.ColumnSpan="3" Text="10.0" MaxLines="1" HorizontalContentAlignment="Right" IsTabStop="False"/>
<ComboBox x:Name="cbxAttenuation" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="4" Grid.Column="22" Grid.ColumnSpan="3" HorizontalContentAlignment="Right" SelectionChanged="cbxAttenuation_SelectedIndexChanged" TabIndex="104" KeyDown="cbxAttenuation_KeyDown" />
<CheckBox x:Name="chkUseDampedTolerance" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="0,0,0,0" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="6" Content="Damped Tolerance ON" Click="chkUseDampedTolerance_CheckedChanged" TabIndex="101"/>
<Button x:Name="btnDutToRefRegulation" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="4" Content="Start Regulation" Click="btnDutToRefRegulation_Click" TabIndex="100" />
<Button x:Name="btnSaveRegulationSetup" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="6" Grid.Column="21" Grid.ColumnSpan="4" Content="Save" Click="btnSaveRegulationSetup_Click" TabIndex="100" />
<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 [%]:" />
<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"/>
<ComboBox x:Name="cbxDisplayAttenuation" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="4" Grid.Column="23" Grid.ColumnSpan="2" TabIndex="104" HorizontalContentAlignment="Right" KeyDown="cbxAttenuation_KeyDown" SelectionChanged="cbxAttenuation_SelectedIndexChanged" />
<ComboBox x:Name="cbxToleranceCalc" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="5" Grid.Column="23" Grid.ColumnSpan="2" TabIndex="105" HorizontalContentAlignment="Right" KeyDown="cbxToleranceCalc_KeyDown" SelectionChanged="cbxToleranceCalc_SelectedIndexChanged"/>
<CheckBox x:Name="chkUseDampedTolerance" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="1,1,1,1" Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="6" Content="Damped Tolerance ON" TabIndex="101" Click="chkUseDampedTolerance_CheckedChanged" />
<Button x:Name="btnDutToRefRegulation" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="4" Content="Start Regulation" TabIndex="100" Click="btnDutToRefRegulation_Click" />
<Button x:Name="btnSaveRegulationSetup" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="6" Grid.Column="21" Grid.ColumnSpan="4" Content="Save" TabIndex="106" Click="btnSaveRegulationSetup_Click" />
</Grid>
</TabItem>
<!-- DUT to REF Calibration -->
<TabItem x:Name="tabDutToRefCalibration" Header="DUT to REF Calibration">
<Grid Background="#FFE5E5E5" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="2,2,2,2">
<TabItem x:Name="tabDutToRefCalibration" Header="DUT to REF Calibration" Height="20" VerticalAlignment="Top">
<Grid Background="#FFE5E5E5" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1,1,1,1">
<Grid.RowDefinitions>
<RowDefinition Height="24"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="*"/>
<RowDefinition Height="26"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
@ -378,23 +381,71 @@
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label x:Name="lblRefRequiredPulses" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="1" Grid.Column="17" Grid.ColumnSpan="6" Content="REF Required Pulses:" />
<Label x:Name="lblDutRequiredPulses" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="2" Grid.Column="17" Grid.ColumnSpan="6" Content="DUT Required Pulses:" />
<Label x:Name="lblDutToRefTolMin" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="3" Grid.Column="17" Grid.ColumnSpan="6" Content="Double Pulse Deadtime [ms]:" />
<Label x:Name="lblDutToRefTolMax" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="4" Grid.Column="17" Grid.ColumnSpan="6" Content="DUT/REF Tolerance max [%]:" />
<Label x:Name="lblDoublePulseDeadtime" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="5" Grid.Column="17" Grid.ColumnSpan="6" Content="DUT/REF Tolerance min [%]:" />
<TextBox x:Name="tbxRefRequiredPulse" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="1" Grid.Column="23" Grid.ColumnSpan="2" Text="500" MaxLines="1" HorizontalContentAlignment="Right" TabIndex="111" /><!--KeyDown="tbxRefRequiredPulses_KeyDown" LostFocus="tbxRefRequiredPulses_LostFocus" TextChanged="tbxRefRequiredPulses_TextChanged" /-->
<TextBox x:Name="tbxDutRequiredPulses" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="2" Grid.Column="23" Grid.ColumnSpan="2" Text="50" MaxLines="1" HorizontalContentAlignment="Right" TabIndex="112" /><!--KeyDown="tbxDutRequiredPulses_KeyDown" LostFocus="tbxDutRequiredPulses_LostFocus" TextChanged="tbxDutRequiredPulses_TextChanged"/-->
<TextBox x:Name="tbxDoublePulseDeadtime" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="3" Grid.Column="23" Grid.ColumnSpan="2" Text="0.005" MaxLines="1" HorizontalContentAlignment="Right" TabIndex="113" />
<TextBox x:Name="tbxDutToRefTolMin" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="4" Grid.Column="23" Grid.ColumnSpan="2" Text="1.0" MaxLines="1" HorizontalContentAlignment="Right" TabIndex="114" />
<TextBox x:Name="tbxDutToRefTolMax" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="5" Grid.Column="23" Grid.ColumnSpan="2" Text="-0.5" MaxLines="1" HorizontalContentAlignment="Right" TabIndex="115" />
<Button x:Name="btnDutToRefCalibration" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="4" Content="Start Calibration" Click="btnDutToRefCalibration_Click" TabIndex="110" />
<Label x:Name="lblRefPulsesRequired" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="1" Grid.Column="17" Grid.ColumnSpan="6" Content="REF Required Pulses:" />
<Label x:Name="lblDutPulsesRequired" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="2" Grid.Column="17" Grid.ColumnSpan="6" Content="DUT Required Pulses:" />
<Label x:Name="lblDoublePulseDeadtime" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="3" Grid.Column="17" Grid.ColumnSpan="6" Content="Double Pulse Deadtime [ms]:" />
<Label x:Name="lblDutToRefTolMax" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="4" Grid.Column="17" Grid.ColumnSpan="6" Content="DUT/REF Tolerance max [%]" />
<Label x:Name="lblDutToRefTolMin" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="5" Grid.Column="17" Grid.ColumnSpan="6" Content="DUT/REF Tolerance min [%]:" />
<TextBox x:Name="tbxRefPulsesRequired" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="1" Grid.Column="23" Grid.ColumnSpan="2" Text="500" MaxLines="1" HorizontalContentAlignment="Right" TabIndex="111" KeyDown="tbxRefPulsesRequired_KeyDown" LostFocus="tbxRefPulsesRequired_LostFocus" TextChanged="tbxRefPulsesRequired_TextChanged" />
<TextBox x:Name="tbxDutPulsesRequired" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="2" Grid.Column="23" Grid.ColumnSpan="2" Text="50" MaxLines="1" HorizontalContentAlignment="Right" TabIndex="112" KeyDown="tbxDutPulsesRequired_KeyDown" LostFocus="tbxDutPulsesRequired_LostFocus" TextChanged="tbxDutPulsesRequired_TextChanged"/>
<TextBox x:Name="tbxDoublePulseDeadtime" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="3" Grid.Column="23" Grid.ColumnSpan="2" Text="5" MaxLines="1" HorizontalContentAlignment="Right" TabIndex="113" KeyDown="tbxDoublePulseDeadtime_KeyDown" LostFocus="tbxDoublePulseDeadtime_LostFocus" TextChanged="tbxDoublePulseDeadtime_TextChanged"/>
<TextBox x:Name="tbxDutToRefTolMax" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="4" Grid.Column="23" Grid.ColumnSpan="2" Text="1.1" MaxLines="1" HorizontalContentAlignment="Right" TabIndex="114" KeyDown="tbxDutToRefToleranceMax_KeyDown" LostFocus="tbxDutToRefToleranceMax_LostFocus" TextChanged="tbxDutToRefToleranceMax_TextChanged"/>
<TextBox x:Name="tbxDutToRefTolMin" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="5" Grid.Column="23" Grid.ColumnSpan="2" Text="-0.5" MaxLines="1" HorizontalContentAlignment="Right" TabIndex="115" KeyDown="tbxDutToRefToleranceMin_KeyDown" LostFocus="tbxDutToRefToleranceMin_LostFocus" TextChanged="tbxDutToRefToleranceMin_TextChanged"/>
<Button x:Name="btnDutToRefCalibration" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="4" Content="Start Calibration" TabIndex="110" Click="btnDutToRefCalibration_Click" />
<Button x:Name="btnTakeCalibrationSetup" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="6" Grid.Column="21" Grid.ColumnSpan="4" Content="Take It" TabIndex="116" Click="btnTakeCalibrationSettings_Click" />
</Grid>
</TabItem>
<!-- REF to Scale Calibration -->
<TabItem x:Name="tabRefToScaleCalibration" Header="REF to Scale Calibration">
<Grid Background="#FFE5E5E5"/>
<TabItem x:Name="tabRefToScaleCalibration" Header="REF to Volume Calibration">
<Grid Background="#FFE5E5E5" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="1,1,1,1">
<Grid.RowDefinitions>
<RowDefinition Height="24"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label x:Name="lblRefPulsesMeasured" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="1" Grid.Column="17" Grid.ColumnSpan="6" Content="Measured REF Pulses:" />
<CheckBox x:Name="chkDutPulsesMeasured" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="4,1,1,1" Grid.Row="2" Grid.Column="17" Grid.ColumnSpan="6" Content="Measured DUT Pulses:" TabIndex="121" Click="chkUseDampedTolerance_CheckedChanged" />
<Label x:Name="lblVolumeMeasured" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="3" Grid.Column="17" Grid.ColumnSpan="6" Content="Measured Volume [liters]:" />
<Label x:Name="lblRefPulsesPerCmScale" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="4" Grid.Column="17" Grid.ColumnSpan="6" Content="REF Pulse Ratio [pulses/m³]:" />
<TextBox x:Name="tbxRefPulsesMeasured" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="1" Grid.Column="23" Grid.ColumnSpan="2" Text="500" MaxLines="1" HorizontalContentAlignment="Right" />
<TextBox x:Name="tbxDutPulsesMeasured" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="2" Grid.Column="23" Grid.ColumnSpan="2" Text="50" MaxLines="1" HorizontalContentAlignment="Right" />
<TextBox x:Name="tbxVolumeMeasured" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="3" Grid.Column="23" Grid.ColumnSpan="2" Text="500" MaxLines="1" HorizontalContentAlignment="Right" TabIndex="122" KeyDown="tbxVolumeMeasuredLiters_KeyDown" LostFocus="tbxVolumeMeasuredLiters_LostFocus" TextChanged="tbxVolumeMeasuredLiters_LostFocus"/>
<TextBox x:Name="tbxRefPulsesPerCmScale" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="4" Grid.Column="23" Grid.ColumnSpan="2" Text="1000" MaxLines="1" HorizontalContentAlignment="Right" />
<Button x:Name="btnRefToVolumeCalibration" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,1,1,1" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="4" Content="Start Calibration" TabIndex="120" Click="btnRefToVolumeCalibration_Click" />
</Grid>
</TabItem>
</TabControl>
</Grid>

File diff suppressed because it is too large Load Diff

View File

@ -114,6 +114,15 @@ namespace Sensus.Ui.Fm2014TestBench.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Use Settings.
/// </summary>
internal static string StrBtnTakeCalibrationSetup {
get {
return ResourceManager.GetString("StrBtnTakeCalibrationSetup", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Damped Tolerance On.
/// </summary>
@ -213,15 +222,6 @@ namespace Sensus.Ui.Fm2014TestBench.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Attenuation []:.
/// </summary>
internal static string StrLblAttenuation {
get {
return ResourceManager.GetString("StrLblAttenuation", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Serial Port:.
/// </summary>
@ -231,6 +231,15 @@ namespace Sensus.Ui.Fm2014TestBench.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Display Attenuation []:.
/// </summary>
internal static string StrLblDisplayAttenuation {
get {
return ResourceManager.GetString("StrLblDisplayAttenuation", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Double Pulses Deadtime:.
/// </summary>
@ -240,6 +249,51 @@ namespace Sensus.Ui.Fm2014TestBench.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to DUT Captured Pulses: .
/// </summary>
internal static string StrLblDutPulsesMeasured {
get {
return ResourceManager.GetString("StrLblDutPulsesMeasured", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DUT Pulse Ratio [pulses/m³]:.
/// </summary>
internal static string StrLblDutPulsesPerCmRatio {
get {
return ResourceManager.GetString("StrLblDutPulsesPerCmRatio", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DUT Remaining Pulses:.
/// </summary>
internal static string StrLblDutPulsesRemaining {
get {
return ResourceManager.GetString("StrLblDutPulsesRemaining", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DUT Required Pulses:.
/// </summary>
internal static string StrLblDutPulsesRequired {
get {
return ResourceManager.GetString("StrLblDutPulsesRequired", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DUT Measured Time [s]:.
/// </summary>
internal static string StrLblDutTimeMeasured {
get {
return ResourceManager.GetString("StrLblDutTimeMeasured", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DUT/REF Tolerance max [%]:.
/// </summary>
@ -249,6 +303,15 @@ namespace Sensus.Ui.Fm2014TestBench.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to DUT/REF Tolerance [%]:.
/// </summary>
internal static string StrLblDutToRefToleranceMeasured {
get {
return ResourceManager.GetString("StrLblDutToRefToleranceMeasured", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DUT/REF Tolerance min [%]:.
/// </summary>
@ -258,6 +321,15 @@ namespace Sensus.Ui.Fm2014TestBench.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Flow Rate [m³/h]:.
/// </summary>
internal static string StrLblFlowRateMeasured {
get {
return ResourceManager.GetString("StrLblFlowRateMeasured", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Address:.
/// </summary>
@ -330,69 +402,6 @@ namespace Sensus.Ui.Fm2014TestBench.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to DUT Captured Pulses: .
/// </summary>
internal static string StrLblMeasuredDutPulses {
get {
return ResourceManager.GetString("StrLblMeasuredDutPulses", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DUT Measured Time [s]:.
/// </summary>
internal static string StrLblMeasuredDutTime {
get {
return ResourceManager.GetString("StrLblMeasuredDutTime", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DUT/REF Tolerance [%]:.
/// </summary>
internal static string StrLblMeasuredDutToRefTolerance {
get {
return ResourceManager.GetString("StrLblMeasuredDutToRefTolerance", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Flow Rate [m³/h]:.
/// </summary>
internal static string StrLblMeasuredFlowRate {
get {
return ResourceManager.GetString("StrLblMeasuredFlowRate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to REF Captured Pulses:.
/// </summary>
internal static string StrLblMeasuredRefPulses {
get {
return ResourceManager.GetString("StrLblMeasuredRefPulses", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to REF Measured Time [s]:.
/// </summary>
internal static string StrLblMeasuredRefTime {
get {
return ResourceManager.GetString("StrLblMeasuredRefTime", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Volume [liters]:.
/// </summary>
internal static string StrLblMeasuredVolume {
get {
return ResourceManager.GetString("StrLblMeasuredVolume", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to _Options.
/// </summary>
@ -438,24 +447,6 @@ namespace Sensus.Ui.Fm2014TestBench.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to DUT Pulse Ratio [pulses/m³]:.
/// </summary>
internal static string StrLblRatioDutPulsesPerCm {
get {
return ResourceManager.GetString("StrLblRatioDutPulsesPerCm", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to REF Pulse Ratio [pulses/m³]:.
/// </summary>
internal static string StrLblRatioRefPulsesPerCm {
get {
return ResourceManager.GetString("StrLblRatioRefPulsesPerCm", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to REF Frequency [Hz]:.
/// </summary>
@ -466,47 +457,56 @@ namespace Sensus.Ui.Fm2014TestBench.Properties {
}
/// <summary>
/// Looks up a localized string similar to DUT Remaining Pulses:.
/// Looks up a localized string similar to REF Captured Pulses:.
/// </summary>
internal static string StrLblRemainingDutPulses {
internal static string StrLblRefPulsesMeasured {
get {
return ResourceManager.GetString("StrLblRemainingDutPulses", resourceCulture);
return ResourceManager.GetString("StrLblRefPulsesMeasured", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to REF Pulse Ratio [pulses/m³]:.
/// </summary>
internal static string StrLblRefPulsesPerCmRatio {
get {
return ResourceManager.GetString("StrLblRefPulsesPerCmRatio", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to REF Remaining Pulses:.
/// </summary>
internal static string StrLblRemainingRefPulses {
internal static string StrLblRefPulsesRemaining {
get {
return ResourceManager.GetString("StrLblRemainingRefPulses", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DUT Required Pulses:.
/// </summary>
internal static string StrLblRequiredDutPulses {
get {
return ResourceManager.GetString("StrLblRequiredDutPulses", resourceCulture);
return ResourceManager.GetString("StrLblRefPulsesRemaining", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to REF Required Pulses:.
/// </summary>
internal static string StrLblRequiredRefPulses {
internal static string StrLblRefPulsesRequired {
get {
return ResourceManager.GetString("StrLblRequiredRefPulses", resourceCulture);
return ResourceManager.GetString("StrLblRefPulsesRequired", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to REF Measured Time [s]:.
/// </summary>
internal static string StrLblRefTimeMeasured {
get {
return ResourceManager.GetString("StrLblRefTimeMeasured", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to REF/DUT Ratio [norm]:.
/// </summary>
internal static string StrLblScaleRefToDut {
internal static string StrLblRefToDutScale {
get {
return ResourceManager.GetString("StrLblScaleRefToDut", resourceCulture);
return ResourceManager.GetString("StrLblRefToDutScale", resourceCulture);
}
}
@ -528,6 +528,15 @@ namespace Sensus.Ui.Fm2014TestBench.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Volume [liters]:.
/// </summary>
internal static string StrLblVolumeMeasured {
get {
return ResourceManager.GetString("StrLblVolumeMeasured", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to WAITING FOR FM2014 RESPONSE.
/// </summary>

View File

@ -168,20 +168,20 @@
<data name="StrLblFM2014Connected" xml:space="preserve">
<value>ONLINE</value>
</data>
<data name="StrLblRatioRefPulsesPerCm" xml:space="preserve">
<data name="StrLblRefPulsesPerCmRatio" xml:space="preserve">
<value>REF Pulsratio [Pulse/m³]:</value>
</data>
<data name="StrGbxRegulationSetup" xml:space="preserve">
<value>Regulierungseinstellungen</value>
</data>
<data name="StrLblRatioDutPulsesPerCm" xml:space="preserve">
<data name="StrLblDutPulsesPerCmRatio" xml:space="preserve">
<value>DUT Pulsratio [Pulse/m³]:</value>
</data>
<data name="StrLblScaleRefToDut" xml:space="preserve">
<data name="StrLblRefToDutScale" xml:space="preserve">
<value>REF/DUT Ratio [norm]:</value>
</data>
<data name="StrLblAttenuation" xml:space="preserve">
<value>Dämpfung []:</value>
<data name="StrLblDisplayAttenuation" xml:space="preserve">
<value>Anzeigestabilisierung []:</value>
</data>
<data name="StrBtnSaveRegulationSetup" xml:space="preserve">
<value>Speichern</value>
@ -189,19 +189,19 @@
<data name="StrGbxManualRefCalibration" xml:space="preserve">
<value>REF zu Volumen Kalibrierung</value>
</data>
<data name="StrLblMeasuredRefPulses" xml:space="preserve">
<data name="StrLblRefPulsesMeasured" xml:space="preserve">
<value>REF Erfaßte Imulse:</value>
</data>
<data name="StrLblMeasuredVolume" xml:space="preserve">
<data name="StrLblVolumeMeasured" xml:space="preserve">
<value>Volumen [Liter]:</value>
</data>
<data name="StrGbxDutToRefRegulation" xml:space="preserve">
<value>DUT zu REF Regulierung</value>
</data>
<data name="StrLblMeasuredFlowRate" xml:space="preserve">
<data name="StrLblFlowRateMeasured" xml:space="preserve">
<value>Durchfluß [m³/h]:</value>
</data>
<data name="StrLblMeasuredDutToRefTolerance" xml:space="preserve">
<data name="StrLblDutToRefToleranceMeasured" xml:space="preserve">
<value>DUT/REF Toleranz [%]:</value>
</data>
<data name="StrChkUseDampedTolerance" xml:space="preserve">
@ -222,7 +222,7 @@
<data name="StrLblRefFrequencyHz" xml:space="preserve">
<value>REF Frequenz [Hz]:</value>
</data>
<data name="StrLblMeasuredDutPulses" xml:space="preserve">
<data name="StrLblDutPulsesMeasured" xml:space="preserve">
<value>DUT Erfaßte Impulse:</value>
</data>
<data name="StrGbxDutToRefCalibration" xml:space="preserve">
@ -252,26 +252,26 @@
<data name="StrLblSlotSelection" xml:space="preserve">
<value>Einbauplatz:</value>
</data>
<data name="StrLblRequiredRefPulses" xml:space="preserve">
<data name="StrLblRefPulsesRequired" xml:space="preserve">
<value>REF Erfordeliche Pulse:</value>
</data>
<data name="StrLblRemainingRefPulses" xml:space="preserve">
<data name="StrLblRefPulsesRemaining" xml:space="preserve">
<value>REF Verbleibende Pulse:</value>
</data>
<data name="StrLblRequiredDutPulses" xml:space="preserve">
<data name="StrLblDutPulsesRequired" xml:space="preserve">
<value>DUT Erforderliche Pulse:</value>
</data>
<data name="StrLblRemainingDutPulses" xml:space="preserve">
<data name="StrLblDutPulsesRemaining" xml:space="preserve">
<value>DUT Verbleibende Pulse:</value>
</data>
<data name="StrLblMeasuredRefTime" xml:space="preserve">
<data name="StrLblRefTimeMeasured" xml:space="preserve">
<value>REF Gemessene Zeit [s]:</value>
</data>
<data name="StrLblMeasuredDutTime" xml:space="preserve">
<data name="StrLblDutTimeMeasured" xml:space="preserve">
<value>DUT Gemessene Zeit [s]:</value>
</data>
<data name="StrLblDoublePulseDeadTime" xml:space="preserve">
<value>Doppelimpulstotozeit:</value>
<value>Doppelimpulstotzeit:</value>
</data>
<data name="StrLblDutToRefToleranceMin" xml:space="preserve">
<value>DUT/REF Toleranz min [%]:</value>
@ -282,4 +282,7 @@
<data name="StrLblOverallProcessStatus" xml:space="preserve">
<value>Gesamtprozeß:</value>
</data>
<data name="StrBtnTakeCalibrationSetup" xml:space="preserve">
<value>Anwenden</value>
</data>
</root>

View File

@ -168,20 +168,20 @@
<data name="StrLblFM2014Connected" xml:space="preserve">
<value>ONLINE</value>
</data>
<data name="StrLblRatioRefPulsesPerCm" xml:space="preserve">
<data name="StrLblRefPulsesPerCmRatio" xml:space="preserve">
<value>REF Pulse Ratio [pulses/m³]:</value>
</data>
<data name="StrGbxRegulationSetup" xml:space="preserve">
<value>Regulation Setup</value>
</data>
<data name="StrLblRatioDutPulsesPerCm" xml:space="preserve">
<data name="StrLblDutPulsesPerCmRatio" xml:space="preserve">
<value>DUT Pulse Ratio [pulses/m³]:</value>
</data>
<data name="StrLblScaleRefToDut" xml:space="preserve">
<data name="StrLblRefToDutScale" xml:space="preserve">
<value>REF/DUT Ratio [norm]:</value>
</data>
<data name="StrLblAttenuation" xml:space="preserve">
<value>Attenuation []:</value>
<data name="StrLblDisplayAttenuation" xml:space="preserve">
<value>Display Attenuation []:</value>
</data>
<data name="StrBtnSaveRegulationSetup" xml:space="preserve">
<value>Save</value>
@ -189,19 +189,19 @@
<data name="StrGbxManualRefCalibration" xml:space="preserve">
<value>REF to Volume Calibration</value>
</data>
<data name="StrLblMeasuredRefPulses" xml:space="preserve">
<data name="StrLblRefPulsesMeasured" xml:space="preserve">
<value>REF Captured Pulses:</value>
</data>
<data name="StrLblMeasuredVolume" xml:space="preserve">
<data name="StrLblVolumeMeasured" xml:space="preserve">
<value>Volume [liters]:</value>
</data>
<data name="StrGbxDutToRefRegulation" xml:space="preserve">
<value>DUT to REF Regulation</value>
</data>
<data name="StrLblMeasuredFlowRate" xml:space="preserve">
<data name="StrLblFlowRateMeasured" xml:space="preserve">
<value>Flow Rate [m³/h]:</value>
</data>
<data name="StrLblMeasuredDutToRefTolerance" xml:space="preserve">
<data name="StrLblDutToRefToleranceMeasured" xml:space="preserve">
<value>DUT/REF Tolerance [%]:</value>
</data>
<data name="StrChkUseDampedTolerance" xml:space="preserve">
@ -222,7 +222,7 @@
<data name="StrLblRefFrequencyHz" xml:space="preserve">
<value>REF Frequency [Hz]:</value>
</data>
<data name="StrLblMeasuredDutPulses" xml:space="preserve">
<data name="StrLblDutPulsesMeasured" xml:space="preserve">
<value>DUT Captured Pulses: </value>
</data>
<data name="StrGbxDutToRefCalibration" xml:space="preserve">
@ -252,22 +252,22 @@
<data name="StrLblSlotSelection" xml:space="preserve">
<value>Slot Selection:</value>
</data>
<data name="StrLblRequiredRefPulses" xml:space="preserve">
<data name="StrLblRefPulsesRequired" xml:space="preserve">
<value>REF Required Pulses:</value>
</data>
<data name="StrLblRemainingRefPulses" xml:space="preserve">
<data name="StrLblRefPulsesRemaining" xml:space="preserve">
<value>REF Remaining Pulses:</value>
</data>
<data name="StrLblRequiredDutPulses" xml:space="preserve">
<data name="StrLblDutPulsesRequired" xml:space="preserve">
<value>DUT Required Pulses:</value>
</data>
<data name="StrLblRemainingDutPulses" xml:space="preserve">
<data name="StrLblDutPulsesRemaining" xml:space="preserve">
<value>DUT Remaining Pulses:</value>
</data>
<data name="StrLblMeasuredRefTime" xml:space="preserve">
<data name="StrLblRefTimeMeasured" xml:space="preserve">
<value>REF Measured Time [s]:</value>
</data>
<data name="StrLblMeasuredDutTime" xml:space="preserve">
<data name="StrLblDutTimeMeasured" xml:space="preserve">
<value>DUT Measured Time [s]:</value>
</data>
<data name="StrLblDoublePulseDeadTime" xml:space="preserve">
@ -282,4 +282,7 @@
<data name="StrLblOverallProcessStatus" xml:space="preserve">
<value>Overall Process:</value>
</data>
<data name="StrBtnTakeCalibrationSetup" xml:space="preserve">
<value>Use Settings</value>
</data>
</root>

View File

@ -143,11 +143,17 @@ namespace Sensus.Ui.Fm2014TestBench.UserControls
/// </summary>
/// <param name="numberOfMeasurementFields">0 = Off, 1..7 measurements active</param>
/// <returns></returns>
/// <remarks date="2026-Feb-03..06" author="Thomas Wiedebusch">
/// <remarks date="2026-Feb-03..09" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public Boolean EnableMeasurements(UInt32 numberOfMeasurementFields)
{
// Rest all labels to 0
for (var idx = 0; idx < MeasurementLabels.Count; idx++)
{
SetValue(idx, "0", ColorStandardDisplayField);
}
// Check if device is connected and logged in
if (!lblStatus.Content.Equals(GetConnectionStatusInfo(ConnectionStatusName.ONLINE)))
return false;
@ -158,7 +164,6 @@ namespace Sensus.Ui.Fm2014TestBench.UserControls
return true;
}
//MeasurementIsActive = true;
// Hide the picture and the border of it
UiElmEnable(picFM2014, false, false);
UiElmEnable(brdFM2014Pic, false, false);