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

This commit is contained in:
Thomas Wiedebusch 2026-02-05 17:31:15 +01:00
parent 82cc3be1e8
commit 59be4e7903
10 changed files with 810 additions and 538 deletions

View File

@ -144,12 +144,42 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// </summary>
private static CmdType SharedCmdTypeReminderLastCmd { get; set; }
/// <summary>
/// Reminder of last individual address of FM2014 to initiate a new communication to another device.
/// </summary>
private static Int32 ReminderLastIndividualCommFm2014Address { get; set; }
/// <summary>
/// Number of processing steps for this actual process progress
/// </summary>
private static Int32 MaxActualProcessProgress { get; set; }
/// <summary>
/// Progress of actual process converted to percent
/// </summary>
private static Double _actualProcessProgress_percent;
/// <summary>
/// Progress of actual process
/// </summary>
private static Int32 _actualProcessProgress;
/// <summary>
/// Actual process progress of subroutine
/// </summary>
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
/// </summary>
private Int32 _tolerance_percent = DEFAULT_TOLERANCE_percent;
/// <summary>
/// Actual tolerance scale to convert raw value from FM2014 to percent
/// </summary>
private Single _toleranceRawToPercentScale = 3.3f / 255;
private Single ToleranceRawToPercentScale { set; get; } = 3.3f / 255;
/// <summary>
/// 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
/// </summary>
public const UInt32 TimeMeasurementMax_pulses = 0xFFFF;
private UInt32 _refTimeMeasurement_pulses;
private UInt32 _refRequiredTimeMeasurement_pulses;
/// <summary>
/// REF pulses for time measurement.
/// Required REF pulses for time measurement.
/// </summary>
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;
/// <summary>
/// DUT pulses for time measurement.
/// Required DUT pulses for time measurement.
/// </summary>
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;
/// <summary>
/// REF timer ticks of measured pulses
/// </summary>
public Int32 RefMeasuredTimer_ticks
{
get => _refMeasuredTimer_ticks;
private set
{
_refMeasuredTimer_ticks = value;
RefMeasuredTime_s = value * TMR_RESOLUTION_s;
}
}
private Int32 _dutMeasuredTimer_ticks;
/// <summary>
/// DUT timer ticks of measured pulses
/// </summary>
public Int32 DutMeasuredTimer_ticks
{
get => _dutMeasuredTimer_ticks;
private set
{
_dutMeasuredTimer_ticks = value;
DutMeasuredTime_s = value * TMR_RESOLUTION_s;
}
}
/// <summary>
/// Remaining REF pulses for time measurement.
/// </summary>
public UInt32 RefRemainingTimeMeasurement_pulses { get; private set; }
/// <summary>
/// Remaining DT pulses for time measurement.
/// </summary>
public UInt32 DutRemainingTimeMeasurement_pulses { get; private set; }
/// <summary>
/// Measured REF time of required pulses.
/// </summary>
public Double RefMeasuredTime_s { get; private set; }
/// <summary>
/// Measured DUT time of required pulses.
/// </summary>
public Double DutMeasuredTime_s { get; private set; }
private UInt32 _ref_pulse_per_cm;
/// <summary>
/// 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
/// </summary>
private String _refToDutScaleStr = "1000K+2";
private String RefToDutScaleStr { get; set; } = "1000K+2";
/// <summary>
/// REF to DUT scale converted to meaningful human interpretable value
/// </summary>
private Double _refToDutScale_norm = 10.0f;
/// <summary>
@ -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
/// </summary>
public Boolean RequestDebugInformation { get; set; }
/// <summary>
/// Number of processing steps for this actual process progress
/// </summary>
private static Int32 MaxActualProcessProgress { get; set; }
/// <summary>
/// Progress of actual process converted to percent
/// </summary>
private static Double _actualProcessProgress_percent;
/// <summary>
/// Progress of actual process
/// </summary>
private static Int32 _actualProcessProgress;
/// <summary>
/// Actual process progress of subroutine
/// </summary>
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 --------------------------------------------
/// <summary>
/// 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!
/// </summary>
/// <returns></returns>
/// <remarks date="2026-Jan-12..14" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2026-Jan-27" author="Thomas Wiedebusch">
/// - Delayed reset to allow other tasks to finish.
/// </remarks>
/// <remarks date="2026-Jan-28..30" author="Thomas Wiedebusch">
/// - Support for multiple FM2014s.
/// </remarks>
/// <remarks date="2026-Feb-05" author="Thomas Wiedebusch">
/// - Statics.
/// </remarks>
public static Boolean ResetHardwareAllDevices()
{
if (SharedCyclicMeasSequ == CyclicMeasSequ.IDLE)
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;
}
/// <summary>
/// Executes all StoreConfiguration and StoreCalibration for each FM2014 which is logged on.
/// </summary>
@ -483,6 +598,20 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
return retVal;
}
/// <summary>
/// Get the first FM2014 which is connected and logged in for common service routines
/// using a broadcast command
/// </summary>
/// <returns></returns>
private static FM2014 GetFirstConnectedAndLoggedInFm2014()
{
if (RegisteredFm2014s == null || RegisteredFm2014s.Count == 0 ||
RegisteredFm2014s.All(fm2014 => !fm2014.IsLoggedOn))
return null;
return RegisteredFm2014s.FirstOrDefault(fm2014 => fm2014.IsLoggedOn);
}
/// <summary>
/// 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);
}
/// <summary>
/// The regulation measurement compares the DUT to REF tolerance:
/// Preconditions:
/// - The <see cref="ResetHardwareAllDevices"/> has to be executed in advance,
/// - The <see cref="Ref_pulse_per_cm"/>REF pulses per cubic meter has to be preset,
/// - The <see cref="Dut_pulse_per_cm"/>DUT pulses per cubic meter has to be preset,
/// - The <see cref="RefToDutScale_norm"/>Scale for REF to DUT pulses has to be preset.
/// Initial setup:
/// - Setup double impulse,
/// - 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 <see cref="Ref_pulse_per_cm"/> to calculate
/// <see cref="RefFlowRate_cm_per_h"/>the 'Flow rate'.
/// Exit:
/// - Set <see cref="SharedCyclicMeasSequ"/> to false.
/// </summary>
/// <returns></returns>
/// <remarks date="2026-Jan-14..21" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2026-Jan-28..29" author="Thomas Wiedebusch">
/// - Support for multiple FM2014s.
/// </remarks>
/// <remarks date="2026-Feb-05" author="Thomas Wiedebusch">
/// - Static.
/// </remarks>
public static Boolean RegulationMeasurement()
{
// 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;
}
/// <summary>
/// Read FM2014:
/// - Accesses directly to the 'SerialPort.ReadTo',
@ -998,249 +1377,6 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
return true;
}
/// <summary>
/// The regulation measurement compares the DUT to REF tolerance:
/// Preconditions:
/// - The <see cref="ResetHardwareAllDevices"/> has to be executed in advance,
/// - The <see cref="Ref_pulse_per_cm"/>REF pulses per cubic meter has to be preset,
/// - The <see cref="Dut_pulse_per_cm"/>DUT pulses per cubic meter has to be preset,
/// - The <see cref="RefToDutScale_norm"/>Scale for REF to DUT pulses has to be preset.
/// Initial setup:
/// - Setup double impulse,
/// - 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 <see cref="Ref_pulse_per_cm"/> to calculate
/// <see cref="RefFlowRate_cm_per_h"/>the 'Flow rate'.
/// Exit:
/// - Set <see cref="SharedCyclicMeasSequ"/> to false.
/// </summary>
/// <returns></returns>
/// <remarks date="2026-Jan-14..21" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2026-Jan-28..29" author="Thomas Wiedebusch">
/// - Support for multiple FM2014s.
/// </remarks>
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;
}
/// <summary>
/// Transfer all standalone settings to FM2014 RAM and safe those to nonvolatile EEPROM
/// </summary>
@ -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);
}
/// <summary>
/// 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!
/// </summary>
/// <returns></returns>
/// <remarks date="2026-Jan-12..14" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2026-Jan-27" author="Thomas Wiedebusch">
/// - Delayed reset to allow other tasks to finish.
/// </remarks>
/// <remarks date="2026-Jan-28..30" author="Thomas Wiedebusch">
/// - Support for multiple FM2014s.
/// </remarks>
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;
}
/// <summary>
/// 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
/// <see cref="ResetHardwareAllDevices"/> as this should be used to prepare the hardware for
/// the new measurement after it being able to start the new measurement immediately!
/// </summary>
/// <remarks date="2026-Feb-05" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
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;
}
}
/// <summary>
@ -1562,7 +1650,6 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
SharedSerialPort.ReadTimeout = 1000;
SharedSerialPort.WriteTimeout = 1000;
}
var cmdName = CmdName.CMD_SERIAL;
try
{

View File

@ -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;
}

View File

@ -15,15 +15,15 @@
<Menu DockPanel.Dock="Top" Visibility="Visible" Height="30" Background="#FFF0F0F0" HorizontalAlignment="Stretch">
<!-- Menu -->
<MenuItem x:Name="optionsMenu" HorizontalAlignment="Right" Height="26" Width="60" Header="_Options" >
<MenuItem x:Name="optionsMenuLogin" Header="_Login" HorizontalAlignment="Left"/>
<MenuItem x:Name="optMenu" HorizontalAlignment="Right" Height="26" Width="80" Header="_Options" >
<MenuItem x:Name="optMenuLogin" Header="_Login" HorizontalAlignment="Left"/>
<!--Click="OnLogin_Click"/-->
<MenuItem x:Name="optionsMenuLogout" Header="L_ogout" HorizontalAlignment="Left"/>
<MenuItem x:Name="optMenuLogout" Header="L_ogout" HorizontalAlignment="Left"/>
<!-- Click="OnLogout_Click"/-->
<MenuItem x:Name="optionsMenuChangePassword" Header="Change Password" HorizontalAlignment="Left"/>
<MenuItem x:Name="optMenuChangePassword" Header="Change Password" HorizontalAlignment="Left"/>
<!--Click="OnChangePassword_Click"/-->
</MenuItem>
<Label Content="Serial Port:" Margin="2,2,2,2"/>
<Label x:Name="portMenu" Content="Serial Port:" Margin="2,2,2,2"/>
<ComboBox x:Name="cbxFM2014ComPort" Margin="2,2,2,2" SelectionChanged="cbxFM2014BaseSettings_SelectedValueChanged"/>
<MenuItem x:Name="hlpMenu" HorizontalAlignment="Right" Height="26" Width="40" FontSize="12" Header="Help" Click="HlpMenu_Click" />
<!--Special approval / requirement-->
@ -207,21 +207,21 @@
<Label x:Name="lblSerialNumberText" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="4" Content="Serial Number:" />
<Label x:Name="lblLifeTimeText" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="11" Grid.Column="0" Grid.ColumnSpan="4" Content="Lifetime:" />
<Button 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="0,0,0,0" 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="24" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,-1,0" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="4" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,-1,0" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,-1,0" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="4" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,-1,0" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="4" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,-1,0" Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="4" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,-1,0" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="4" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,-1,0" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="4" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,-1,0" Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="4" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,-1,0" Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="4" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,-1,0" Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="4" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,-1,0" Grid.Row="11" Grid.Column="0" Grid.ColumnSpan="4" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="24" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="24" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="24" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="24" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="24" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="24" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="24" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="24" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="24" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="24" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="24" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="11" Grid.Column="0" Grid.ColumnSpan="24" Stroke="LightGray"/>
<!-- Stack panels for each device -->
<StackPanel x:Name="spDevice1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="1" Grid.RowSpan="11" Grid.Column="4" Grid.ColumnSpan="2" />
@ -273,13 +273,70 @@
</GroupBox>
<!--Progress Bars and Info-->
<ProgressBar x:Name="pbSubProgress" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,2,2,2" Grid.Row="25" Grid.Column="0" Grid.ColumnSpan="40" />
<ProgressBar x:Name="pbTotalProgress" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,2,2,2" Grid.Row="26" Grid.Column="0" Grid.ColumnSpan="40" />
<Label x:Name="lblSingleProgressText" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,0,1,0" Grid.Row="25" Grid.Column="0" Grid.ColumnSpan="8" Content="Single Progress" />
<Label x:Name="lblTotalProgressText" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,0,1,0" Grid.Row="26" Grid.Column="0" Grid.ColumnSpan="8" Content="Total Progress" />
<Label x:Name="lblSingleProgressValue" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="1,0,1,0" Grid.Row="25" Grid.Column="37" Grid.ColumnSpan="3" Content="0,0 %" />
<Label x:Name="lblTotalProgressValue" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="1,0,1,0" Grid.Row="26" Grid.Column="37" Grid.ColumnSpan="3" Content="0,0 %" />
<ProgressBar x:Name="pbActualProgress" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,2,2,2" Grid.Row="25" Grid.Column="0" Grid.ColumnSpan="40" />
<ProgressBar x:Name="pbTotalProgress" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,2,2,2" Grid.Row="26" Grid.Column="0" Grid.ColumnSpan="40" />
<Label x:Name="lblActualProcessLabel" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,0,1,0" Grid.Row="25" Grid.Column="0" Grid.ColumnSpan="4" Content="Single Progress" />
<Label x:Name="lblTotalProcessLabel" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,0,1,0" Grid.Row="26" Grid.Column="0" Grid.ColumnSpan="4" Content="Total Progress" />
<Label x:Name="lblActualProcessText" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,0,1,0" Grid.Row="25" Grid.Column="4" Grid.ColumnSpan="8" Content="Single Progress" />
<Label x:Name="lblTotalProcessText" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="1,0,1,0" Grid.Row="26" Grid.Column="4" Grid.ColumnSpan="8" Content="Total Progress" />
<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="25" >
<!-- 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.RowDefinitions>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="26"/>
<RowDefinition Height="*"/>
<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="*"/>
</Grid.ColumnDefinitions>
<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" />
</Grid>
</TabItem>
<!-- DUT to REF Calibration -->
<TabItem x:Name="tabDutToRefCalibration" Header="DUT to REF Calibration">
<Grid Background="#FFE5E5E5"/>
</TabItem>
<!-- REF to Scale Calibration -->
<TabItem x:Name="tabRefToScaleCalibration" Header="REF to Scale Calibration">
<Grid Background="#FFE5E5E5"/>
</TabItem>
</TabControl>
</Grid>
</DockPanel>

View File

@ -35,7 +35,6 @@ using System;
using System.Collections.Generic;
using Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Config;
using Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core;
using Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Consts;
using System.IO;
using System.IO.Ports;
using System.Linq;
@ -45,7 +44,6 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Media;
using System.Windows.Threading;
using Sensus.Ui.Fm2014TestBench.UserControls;
@ -55,7 +53,6 @@ using Xylem.Common.Utils.ProcessExec.EventArguments;
using static Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Consts.FM2014CmdDef;
using CheckBox = System.Windows.Controls.CheckBox;
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
using MessageBox = System.Windows.MessageBox;
using UserControl = System.Windows.Controls.UserControl;
namespace Sensus.Ui.FM2014TestBench
@ -63,7 +60,7 @@ namespace Sensus.Ui.FM2014TestBench
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class FM2014TestBenchWindow : Window
public partial class FM2014TestBenchWindow
{
#region ------------------------------------------- Properties ------------------------------------------------
// cancellation token
@ -93,11 +90,22 @@ namespace Sensus.Ui.FM2014TestBench
private readonly List<UserControl> UcFM2014s = new List<UserControl>();
/// <summary>
/// The start time is going to be used for time measurements of processes. It has to be set to
/// the actual time if the measurement should be (re)started.
/// Variable measurement labels depending on ongoing measurement shared for all
/// FM2014 devices.
/// </summary>
private readonly List<Label> MeasurementLabels = new List<Label>();
/// <summary>
/// The start time is going to be used for time measurements of processes.
/// It has to be set to the actual time if the measurement should be (re)started.
/// </summary>
private DateTimeOffset _startTime;
/// <summary>
/// Program internal timer
/// </summary>
private DispatcherTimer _tmrProgressUpdate = new DispatcherTimer();
/// <summary>
/// The auto progress bar enabled is for infinite processes or if the process doesn't feed
/// the progress bar with information (e.g. Connect()).
@ -164,6 +172,9 @@ namespace Sensus.Ui.FM2014TestBench
_cancellationTokenSource = new CancellationTokenSource();
_cancellationToken = _cancellationTokenSource.Token;
// Set timer to 500 ms interval
_tmrProgressUpdate.Interval = new TimeSpan(0, 0, 0, 0, 500);
_tmrProgressUpdate.Tick += tmrProgressUpdate_Tick;
}
private void Window_Loaded(Object sender, RoutedEventArgs e)
@ -210,6 +221,15 @@ namespace Sensus.Ui.FM2014TestBench
SlotSelections.Add(cbxSlot9);
SlotSelections.Add(cbxSlot10);
// ---- SORTED! ---- list of measurement labels for all devices
MeasurementLabels.Add(lblMeasurement1Text);
MeasurementLabels.Add(lblMeasurement2Text);
MeasurementLabels.Add(lblMeasurement3Text);
MeasurementLabels.Add(lblMeasurement4Text);
MeasurementLabels.Add(lblMeasurement5Text);
MeasurementLabels.Add(lblMeasurement6Text);
MeasurementLabels.Add(lblMeasurement7Text);
Init();
}
@ -348,9 +368,9 @@ namespace Sensus.Ui.FM2014TestBench
#region ------------------------------------------- TimerControls ---------------------------------------------
private void tmrProgressUpdate_Tick(Object sender, EventArgs e)
{
if (_autoProgressBar || pbSubProgress.IsVisible)
if (_autoProgressBar || pbActualProgress.IsVisible)
{
pbSubProgress.Value = pbSubProgress.Value + 4 > 100 ? 0 : pbSubProgress.Value + 4;
pbActualProgress.Value = pbActualProgress.Value + 4 > 100 ? 0 : pbActualProgress.Value + 4;
}
SetTimeDisplay();
@ -387,7 +407,7 @@ namespace Sensus.Ui.FM2014TestBench
}
_autoProgressBar = false;
// Load the configuration from the local file stored in AppData\FM2014
_fm2014Config.ReadFM2014Config();
@ -421,19 +441,21 @@ namespace Sensus.Ui.FM2014TestBench
var userControl = new UcFM2014Device(fm2014, StoreFM2014Settings);
UcFM2014s.Add(userControl);
SetNewUserControl(PanelsForUcFM2014s[ctr], UcFM2014s[ctr]);
// Restore previous slot selection
if (_fm2014Config?.SlotIsSelected?[ctr] != null &&
_fm2014Config.SlotIsSelected.Count == MaxFm2014s &&
SlotSelections.Count == MaxFm2014s)
SlotSelections[ctr].IsChecked = _fm2014Config?.SlotIsSelected[ctr] ?? false;
// Restore individual tolerance selection
if (_fm2014Config?.IndividualTolerancePercent?[ctr] != null &&
_fm2014Config.IndividualTolerancePercent.Count == MaxFm2014s)
fm2014.Tolerance_percent = _fm2014Config?.IndividualTolerancePercent[ctr] ??
fm2014.Tolerance_percent = _fm2014Config?.IndividualTolerancePercent[ctr] ??
FM2014.DEFAULT_TOLERANCE_percent;
}
//grpBoxDebug.Visible = false;
//gbxDutToRefRegulation.Visible = true;
//gbxRegulationSetup.Visible = true;
// Disable variable measurement labels
foreach (var measLbl in MeasurementLabels)
UiElmEnable(measLbl, false, false);
// Display actual com-port, if not assigned use the '?'. This will be placed on Items[0]
if (_fm2014Config?.SerialPort != null && !cbxFM2014ComPort.Items.Contains(_fm2014Config.SerialPort))
@ -454,56 +476,65 @@ namespace Sensus.Ui.FM2014TestBench
cbxFM2014ComPort.Text = cbxFM2014ComPort.Items[_comPortIdx]?.ToString();
//// FM2014 group box
//lblConnectionStatus.Text = Resources.StrLblFM2014NotConnected;
//lblConnectionStatus.Text = Properties.Resources.StrLblFM2014NotConnected;
//lblConnectionStatus.ForeColor = ColorProcessFailed;
//tbxFM2014ApplicationFwVersion.Text = "";
//lblFM2014SerialNumber.Text = Resources.StrLblFM2014SerialNumber;
//lblFM2014SerialNumber.Text = Properties.Resources.StrLblFM2014SerialNumber;
//tbxFM2014SerialNumber.Text = "";
//lblFM2014SerialPort.Text = Resources.StrLblFM2014SerialPort;
//lblFM2014Address.Text = Resources.StrLblFM2014Address;
//lblFM2014Tolerance.Text = Resources.StrLblFM2014Tolerance;
//btnFM2014Connect.Text = Resources.StrBtnConnect;
//lblFM2014SerialPort.Text = Properties.Resources.StrLblFM2014SerialPort;
//lblFM2014Address.Text = Properties.Resources.StrLblFM2014Address;
//lblFM2014Tolerance.Text = Properties.Resources.StrLblFM2014Tolerance;
btnFM2014Connect.Content = Properties.Resources.StrBtnConnect;
//// Regulation Setup group box
//gbxRegulationSetup.Text = Resources.StrGbxRegulationSetup;
//lblRefPulsePerVolume.Text = Resources.StrLblRefPulsesPerCm;
// Measurement selection tab setup
tabDutToRefRegulation.Header = Properties.Resources.StrGbxDutToRefRegulation;
tabDutToRefCalibration.Header = Properties.Resources.StrGbxDutToRefCalibration;
tabRefToScaleCalibration.Header = Properties.Resources.StrGbxManualRefCalibration;
hlpMenu.Header = Properties.Resources.StrLblHelp;
optMenu.Header = Properties.Resources.StrLblOptions;
optMenuLogin.Header = Properties.Resources.StrLblOptionsLogin;
optMenuLogout.Header = Properties.Resources.StrLblOptionsLogout;
optMenuChangePassword.Header = Properties.Resources.StrLblOptionsChangePassword;
portMenu.Content = Properties.Resources.StrLblComPort;
// Regulation Setup group box
//lblRefPulsePerVolume.Text = Properties.Resources.StrLblRefPulsesPerCm;
//tbxRefPulsePerCm.Text = "";
//lblDutPulsePerVolume.Text = Resources.StrLblDutPulsesPerCm;
//lblDutPulsePerVolume.Text = Properties.Resources.StrLblDutPulsesPerCm;
//tbxDutPulsePerCm.Text = "";
//lblScaleRefToDut.Text = Resources.StrLblScaleRefToDut;
//lblScaleRefToDut.Text = Properties.Resources.StrLblScaleRefToDut;
//tbxScaleRefToDut.Text = "";
//lblAttenuation.Text = Resources.StrLblAttenuation;
//btnSaveRegulationSetup.Text = Resources.StrBtnSaveRegulationSetup;
//lblAttenuation.Text = Properties.Resources.StrLblAttenuation;
//btnSaveRegulationSetup.Text = Properties.Resources.StrBtnSaveRegulationSetup;
//// DUT to REF Regulation group box
//tabDutToRefRegulation.Content = Properties.Resources.StrGbxDutToRefRegulation;
//lblActualFlowRateCmPerHour.Text = Properties.Resources.StrLblActualMeasuredFlowRate;
//tbxActualFlowRateCmPerHour.Text = "";
//lblActualMeasuredTolerance.Text = Properties.Resources.StrLblActualMeasuredToleranceDutToRef;
//tbxActualMeasuredToleranceDutToRef.Text = "";
//chkUseDampedTolerance.Text = Properties.Resources.StrChkUseDampedTolerance;
btnDutToRefRegulation.Content = Properties.Resources.StrBtnStartRegulation;
//tbxRefFrequencyHz.Text = "";
//// Manual REF Calibration group box
//gbxManualRefCalibration.Text = Resources.StrGbxManualRefCalibration;
//lblMeasuredRefPulses.Text = Resources.StrLblMeasuredRefPulses;
//gbxManualRefCalibration.Text = Properties.Resources.StrGbxManualRefCalibration;
//lblMeasuredRefPulses.Text = Properties.Resources.StrLblMeasuredRefPulses;
//tbxMeasuredRefPulses.Text = "";
//cbxMeasuredDutPulses.Text = Resources.StrLblMeasuredDutPulses;
//tbxMeasuredDutPulses.Text = "";
//lblWeightScaleVolume.Text = Resources.StrLblMeasuredWeightScaleVolume;
//lblWeightScaleVolume.Text = Properties.Resources.StrLblMeasuredWeightScaleVolume;
//tbxManualInputVolumeLiters.Text = "";
//lblPulseVolumeCmRelation.Text = Resources.StrLblRefPulsesPerCm;
//lblPulseVolumeCmRelation.Text = Properties.Resources.StrLblRefPulsesPerCm;
//tbxRefCalibrationResultPulsePerCm.Text = "";
//btnManualRefCalibration.Text = Resources.StrBtnStartCalibration;
//// DUT to REF Regulation group box
//gbxDutToRefRegulation.Text = Resources.StrGbxDutToRefRegulation;
//lblActualFlowRateCmPerHour.Text = Resources.StrLblActualMeasuredFlowRate;
//tbxActualFlowRateCmPerHour.Text = "";
//lblActualMeasuredTolerance.Text = Resources.StrLblActualMeasuredToleranceDutToRef;
//tbxActualMeasuredToleranceDutToRef.Text = "";
//chkUseDampedTolerance.Text = Resources.StrChkUseDampedTolerance;
//btnDutToRefRegulation.Text = Resources.StrBtnStartRegulation;
//tbxRefFrequencyHz.Text = "";
//btnManualRefCalibration.Text = Properties.Resources.StrBtnStartCalibration;
//// Process status
//lblActualProcess.Text = Resources.StrLblProcessStatus;
//lblTimeText.Text = Resources.StrLblTime;
//lblWaitingForMeterResponse.Text = Resources.StrLblWaitingForMeterResponse;
//lblWaitingForMeterResponse.Visible = false;
lblActualProcessLabel.Content = Properties.Resources.StrLblProcessStatus;
lblTimeText.Text = Properties.Resources.StrLblTime;
CheckUpdateEnabled();
ActionControl(false);
}
/// <summary>
@ -572,7 +603,7 @@ namespace Sensus.Ui.FM2014TestBench
ResetCancellationToken();
FM2014.SharedCancellationToken = _cancellationToken;
UpdateContentControl(lblTotalProgressText, Properties.Resources.StrMsgConnecting);
UpdateContentControl(lblTotalProcessLabel, Properties.Resources.StrMsgConnecting);
var comPort = cbxFM2014ComPort.SelectedItem.ToString();
@ -619,8 +650,6 @@ namespace Sensus.Ui.FM2014TestBench
var msg = $"{dt:yyyy-MM-dd HH:mm:ss} UTC";
LogText($"{Properties.Resources.StrMsgPcDateTime} {msg}");
LogText(StrSeparator);
CheckUpdateEnabled();
}
}
}, _cancellationToken).ContinueWith(delegate
@ -637,7 +666,6 @@ namespace Sensus.Ui.FM2014TestBench
#endregion ---------------------------------------- BoardControls ---------------------------------------------
#region ------------------------------------------- ProcessControls -------------------------------------------
/// <summary>
/// Common method to (de-)activate controls and timer.
/// </summary>
@ -646,30 +674,50 @@ namespace Sensus.Ui.FM2014TestBench
/// </remarks>
private void ActionControl(Boolean isActive)
{
//Invoke(new Action(() =>
//{
// if (isActive)
// {
// lblActualProcess.Visible = true;
// barSingleProgressUpdate.Visible = true;
// SetFM2014AccessLocked();
// tmrProgressUpdate.Enabled = true;
// }
// else
// {
// lblActualProcess.Visible = false;
// barSingleProgressUpdate.Visible = false;
// tmrProgressUpdate.Enabled = false;
// lblWaitingForMeterResponse.Visible = false;
// lblActualProcess.Text = "";
// CheckUpdateEnabled();
// }
// //common actions and settings
// lblActualProcess.Update();
// barSingleProgressUpdate.Value = 0;
// barSingleProgressUpdate.Update();
// Update();
//}));
if (isActive)
{
UiElmEnable(lblActualProcessText, true);
UiElmEnable(lblTotalProcessText, true);
UiElmEnable(lblActualProcessLabel, true);
UiElmEnable(lblTotalProcessLabel, true);
UiElmEnable(lblActualProgressPercent, true);
UiElmEnable(lblTotalProgressPercent, true);
UiElmEnable(pbActualProgress, true);
UiElmEnable(pbTotalProgress, true);
SetFM2014AccessLocked();
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() =>
{
_tmrProgressUpdate.IsEnabled = true;
}));
}
else
{
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() =>
{
_tmrProgressUpdate.IsEnabled = false;
}));
UiElmEnable(lblActualProcessText, false, false);
UiElmEnable(lblTotalProcessText, false, false);
UiElmEnable(lblActualProcessLabel, false, false);
UiElmEnable(lblTotalProcessLabel, false, false);
UiElmEnable(lblActualProgressPercent, false, false);
UiElmEnable(lblTotalProgressPercent, false, false);
UiElmEnable(pbActualProgress, false, false);
UiElmEnable(pbTotalProgress, false, false);
// Check if any FM2014 is connected and logged in to enable access buttons
if (Fm2014s == null || Fm2014s.Count == 0 ||
Fm2014s.All(fm2014 => !fm2014.IsLoggedOn))
{
SetFM2014AccessLocked();
UiElmEnable(btnFM2014Connect, true);
UiElmEnable(cbxFM2014ComPort, true);
return;
}
SetFM2014AccessEnabled();
}
}
/// <summary>
@ -839,7 +887,7 @@ namespace Sensus.Ui.FM2014TestBench
// Disable all buttons
//btnSaveRegulationSetup.Enabled = false;
//btnManualRefCalibration.Enabled = false;
//btnDutToRefRegulation.Enabled = false;
UiElmEnable(btnDutToRefRegulation, false);
//btnFM2014Connect.Enabled = false;
//// Disable manual input
@ -865,10 +913,10 @@ namespace Sensus.Ui.FM2014TestBench
//// Enable buttons and display correct information
//btnManualRefCalibration.Enabled = true;
//btnDutToRefRegulation.Enabled = true;
UiElmEnable(btnDutToRefRegulation, true);
//btnFM2014Connect.Enabled = true;
//btnManualRefCalibration.Text = Resources.StrBtnStartCalibration;
//btnDutToRefRegulation.Text = Resources.StrBtnStartRegulation;
UpdateContentControl(btnDutToRefRegulation, Properties.Resources.StrBtnStartRegulation);
//// Enable manual input
//tbxManualInputVolumeLiters.ReadOnly = false;
@ -883,23 +931,8 @@ namespace Sensus.Ui.FM2014TestBench
//cbxMeasuredDutPulses.Enabled = true;
}
/// <summary>
/// Restore setting of buttons and timeout after operation with device
/// </summary>
private void CheckUpdateEnabled()
{
//if (Fm2014 == null || !Fm2014.IsLoggedOn)
//{
// SetFM2014AccessLocked();
// btnFM2014Connect.Enabled = true;
// cbxFM2014Address.Enabled = true;
// cbxFM2014ComPort.Enabled = true;
// return;
//}
SetFM2014AccessEnabled();
}
#endregion ---------------------------------------- ActivationControls ----------------------------------------
#region ------------------------------------------- Event handler ---------------------------------------------
#region ------------------------------------------- Event handler ---------------------------------------------
/// <summary>
/// Feedback from FM2014being parsed to GUI
/// </summary>
@ -916,22 +949,22 @@ namespace Sensus.Ui.FM2014TestBench
// Progress info section
if (e.ActualProcessMessage != null)
{
UpdateContentControl(lblSingleProgressText, e.ActualProcessMessage);
UpdateContentControl(lblActualProcessText, e.ActualProcessMessage);
}
if (e.ActualProcessPercent != null && !_autoProgressBar)
{
pbSubProgress.Dispatcher.Invoke(DispatcherPriority.Normal,
pbActualProgress.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(() =>
{
pbSubProgress.Value = (Int32)e.ActualProcessPercent;
UpdateContentControl(lblTotalProgressValue, $@"{e.ActualProcessPercent:##0.0} %");
pbActualProgress.Value = (Int32)e.ActualProcessPercent;
UpdateContentControl(lblTotalProgressPercent, $@"{e.ActualProcessPercent:##0.0} %");
}
));
}
if (e.OverallProcessMessage != null)
{
UpdateContentControl(lblSingleProgressText, e.OverallProcessMessage);
UpdateContentControl(lblActualProcessText, e.OverallProcessMessage);
}
if (e.OverallProcessPercent != null && !_autoProgressBar)
@ -940,7 +973,7 @@ namespace Sensus.Ui.FM2014TestBench
new Action(() =>
{
pbTotalProgress.Value = (Int32)e.OverallProcessPercent;
UpdateContentControl(lblTotalProgressValue, $@"{e.OverallProcessPercent:##0.0} %");
UpdateContentControl(lblTotalProgressPercent, $@"{e.OverallProcessPercent:##0.0} %");
}
));
}
@ -1072,28 +1105,28 @@ namespace Sensus.Ui.FM2014TestBench
/// </remarks>
private void btnDutToRefRegulation_Click(Object sender, EventArgs e)
{
//_startTime = DateTimeOffset.UtcNow;
//if (FM2014.SharedCyclicMeasSequ == FM2014CmdDef.CyclicMeasSequ.IDLE)
//{
// ActionControl(true);
// _autoProgressBar = true;
// if (Fm2014.RegulationMeasurement())
// {
// btnDutToRefRegulation.Text = Resources.StrBtnStopRegulation;
// btnDutToRefRegulation.Enabled = true;
// }
//}
//else
//{
// _autoProgressBar = false;
// // Deactivate the button temporary to avoid repeated execution as the reset takes a certain time
// btnDutToRefRegulation.Enabled = false;
// Fm2014.ResetMeasurement();
// ActionControl(false);
// btnDutToRefRegulation.Text = Resources.StrBtnStartRegulation;
//}
_startTime = DateTimeOffset.UtcNow;
if (FM2014.SharedCyclicMeasSequ == CyclicMeasSequ.IDLE)
{
ActionControl(true);
if (FM2014.RegulationMeasurement())
{
_autoProgressBar = true;
UpdateContentControl(btnDutToRefRegulation, Properties.Resources.StrBtnStopRegulation);
UiElmEnable(btnDutToRefRegulation, true);
}
}
else
{
_autoProgressBar = false;
// Deactivate the button temporary to avoid repeated execution as the reset takes a certain time
UiElmEnable(btnDutToRefRegulation, false);
FM2014.ResetHardwareAllDevices();
ActionControl(false);
UpdateContentControl(btnDutToRefRegulation, Properties.Resources.StrBtnStartRegulation);
}
}
/// <summary>
/// Start and stop the manual REF calibration measurement.
/// </summary>

View File

@ -90,6 +90,7 @@
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>

View File

@ -168,6 +168,15 @@ namespace Sensus.Ui.Fm2014TestBench.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to DUT to REF Calibration.
/// </summary>
internal static string StrGbxDutToRefCalibration {
get {
return ResourceManager.GetString("StrGbxDutToRefCalibration", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DUT to REF Regulation.
/// </summary>
@ -222,6 +231,15 @@ namespace Sensus.Ui.Fm2014TestBench.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Serial Port.
/// </summary>
internal static string StrLblComPort {
get {
return ResourceManager.GetString("StrLblComPort", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DUT [pulses/m³]:.
/// </summary>
@ -285,6 +303,15 @@ namespace Sensus.Ui.Fm2014TestBench.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to _Help.
/// </summary>
internal static string StrLblHelp {
get {
return ResourceManager.GetString("StrLblHelp", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Measured DUT [pulses]:.
/// </summary>
@ -312,6 +339,42 @@ namespace Sensus.Ui.Fm2014TestBench.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to _Options.
/// </summary>
internal static string StrLblOptions {
get {
return ResourceManager.GetString("StrLblOptions", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to _Change Password.
/// </summary>
internal static string StrLblOptionsChangePassword {
get {
return ResourceManager.GetString("StrLblOptionsChangePassword", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to _Login.
/// </summary>
internal static string StrLblOptionsLogin {
get {
return ResourceManager.GetString("StrLblOptionsLogin", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to L_ogout.
/// </summary>
internal static string StrLblOptionsLogout {
get {
return ResourceManager.GetString("StrLblOptionsLogout", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Process status.
/// </summary>

View File

@ -225,4 +225,25 @@
<data name="StrLblMeasuredDutPulses" xml:space="preserve">
<value>Gemessene DUT [Pulse]:</value>
</data>
<data name="StrGbxDutToRefCalibration" xml:space="preserve">
<value>DUT zu REF Kalibrierung</value>
</data>
<data name="StrLblOptions" xml:space="preserve">
<value>_Optionen</value>
</data>
<data name="StrLblComPort" xml:space="preserve">
<value>Kommunikationsanschluß</value>
</data>
<data name="StrLblOptionsLogin" xml:space="preserve">
<value>_Anmelden</value>
</data>
<data name="StrLblOptionsLogout" xml:space="preserve">
<value>A_bmelden</value>
</data>
<data name="StrLblHelp" xml:space="preserve">
<value>_Hilfe</value>
</data>
<data name="StrLblOptionsChangePassword" xml:space="preserve">
<value>_Paßwort Ändern</value>
</data>
</root>

View File

@ -225,4 +225,25 @@
<data name="StrLblMeasuredDutPulses" xml:space="preserve">
<value>Measured DUT [pulses]:</value>
</data>
<data name="StrGbxDutToRefCalibration" xml:space="preserve">
<value>DUT to REF Calibration</value>
</data>
<data name="StrLblOptions" xml:space="preserve">
<value>_Options</value>
</data>
<data name="StrLblComPort" xml:space="preserve">
<value>Serial Port</value>
</data>
<data name="StrLblOptionsLogin" xml:space="preserve">
<value>_Login</value>
</data>
<data name="StrLblOptionsLogout" xml:space="preserve">
<value>L_ogout</value>
</data>
<data name="StrLblHelp" xml:space="preserve">
<value>_Help</value>
</data>
<data name="StrLblOptionsChangePassword" xml:space="preserve">
<value>_Change Password</value>
</data>
</root>

View File

@ -7,46 +7,36 @@
d:DesignHeight="286" d:DesignWidth="60">
<DockPanel Background="AliceBlue" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="0,0,0,0">
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="0,0,0,0">
<!-- Rectangle used as border for common measurement labels -->
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Stroke="LightGray"/>
<Rectangle Name="brdMeasurement1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Stroke="LightGray"/>
<Rectangle Name="brdMeasurement2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Stroke="LightGray"/>
<Rectangle Name="brdMeasurement3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Stroke="LightGray"/>
<Rectangle Name="brdMeasurement4" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Stroke="LightGray"/>
<Rectangle Name="brdMeasurement5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Stroke="LightGray"/>
<Rectangle Name="brdMeasurement6" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2" Stroke="LightGray"/>
<Rectangle Name="brdMeasurement7" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="2" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="2" Stroke="LightGray"/>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,-1,0,0" Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="2" Stroke="LightGray"/>
<!-- Bordered Static Labels -->
<Border Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,0" BorderThickness="1" BorderBrush="LightGray">
<Label Name="lblStatus" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,0,0,0" Content="OFFLINE" />
</Border>
<Border Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,-1,0,0" BorderThickness="1" BorderBrush="LightGray">
<Label Name="lblFirmwareVersion" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,0,0,0" Content="Firmware" />
</Border>
<Border Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,-1,0,0" BorderThickness="1" BorderBrush="LightGray">
<Label Name="lblSerialNumber" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,0,0,0" Content="Serial No" />
</Border>
<Border Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,-1,0,0" BorderThickness="1" BorderBrush="LightGray">
<Label Name="lblLifetime_h" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,0,0,0" Content="Lifetime" />
</Border>
<!-- Bordered Dynamic Labels -->
<Border x:Name="brdMeasurement1" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,0" BorderThickness="1" BorderBrush="LightGray" >
<Label Name="lblMeasurement1" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,-1,0,0" Content="0" />
</Border>
<Border x:Name="brdMeasurement2" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,0" BorderThickness="1" BorderBrush="LightGray" >
<Label Name="lblMeasurement2" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,-1,0,0" Content="0" />
</Border>
<Border x:Name="brdMeasurement3" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,0" BorderThickness="1" BorderBrush="LightGray" >
<Label Name="lblMeasurement3" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,-1,0,0" Content="0" />
</Border>
<Border x:Name="brdMeasurement4" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,0" BorderThickness="1" BorderBrush="LightGray" >
<Label Name="lblMeasurement4" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,-1,0,0" Content="0" />
</Border>
<Border x:Name="brdMeasurement5" Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,0" BorderThickness="1" BorderBrush="LightGray" >
<Label Name="lblMeasurement5" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,-1,0,0" Content="0" />
</Border>
<Border x:Name="brdMeasurement6" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,0" BorderThickness="1" BorderBrush="LightGray" >
<Label Name="lblMeasurement6" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,-1,0,0" Content="0" />
</Border>
<Border x:Name="brdMeasurement7" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,0" BorderThickness="1" BorderBrush="LightGray" >
<Label Name="lblMeasurement7" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,-1,0,0" Content="0" />
</Border>
<Label Name="lblStatus" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Content="OFFLINE" />
<Label Name="lblMeasurement1" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Content="0" />
<Label Name="lblMeasurement2" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Content="0" />
<Label Name="lblMeasurement3" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Content="0" />
<Label Name="lblMeasurement4" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Content="0" />
<Label Name="lblMeasurement5" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Content="0" />
<Label Name="lblMeasurement6" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2" Content="0" />
<Label Name="lblMeasurement7" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2" Content="0" />
<Label Name="lblFirmwareVersion" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="2" Content="Firmware" />
<Label Name="lblSerialNumber" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="2" Content="Serial No" />
<Label Name="lblLifetime_h" HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="0,0,0,0" Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="2" Content="Lifetime" />
<!-- Bordered Image -->
<Border x:Name="brdFM2014Pic" Grid.Row="1" Grid.RowSpan="7" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,0" BorderThickness="1" BorderBrush="LightGray" >
<Image Name="picFM2014" HorizontalAlignment="Center" VerticalAlignment="Stretch" Margin="0,0,-1,-1" Source="/UserControls/FM2014 58x172.jpg" Stretch="Fill" Opacity="0.5" />
<Border x:Name="brdFM2014Pic" Grid.Row="1" Grid.RowSpan="7" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,0" BorderThickness="1" BorderBrush="LightGray">
<Image Name="picFM2014" HorizontalAlignment="Center" VerticalAlignment="Stretch" Margin="0,0,-1,-1" Source="/UserControls/FM2014 58x172.jpg" Stretch="Fill" Opacity="0.3"/>
</Border>
<!--Background Grid Design-->

View File

@ -217,7 +217,6 @@ namespace Sensus.Ui.Fm2014TestBench.UserControls
}
else
{
lblLifetime_h.Content = _fM2014.Lifetime_h;
lblFirmwareVersion.Content = _fM2014.FwVersion;
lblSerialNumber.Content = _fM2014.SerialNumber;