FM2014: - prepared for multiple FM2014s - 4.Step

This commit is contained in:
Thomas Wiedebusch 2026-01-30 10:44:42 +01:00
parent 5c6302b2fe
commit ff3205e15f

View File

@ -172,6 +172,11 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// </summary>
public String SerialNumber { get; private set; }
/// <summary>
/// Lifetime of the FM2014 in hours
/// </summary>
public Int32 Lifetime_h { get; private set; }
/// <summary>
/// FM2014 identification string e.g. FM2014.112.1
/// </summary>
@ -411,6 +416,20 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
return false;
}
/// <summary>
/// Common handler to publish the response.
/// </summary>
/// <param name="fm2014"></param>
/// <param name="processExecEventArgs"></param>
/// <returns></returns>
/// <remarks date="2026-Jan-30" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private static void PublishResponse(FM2014 fm2014, ProcessExecEventArgs processExecEventArgs)
{
fm2014?.OnRawRecordReceived?.Invoke(fm2014, processExecEventArgs);
}
/// <summary>
/// Read FM2014:
/// - Accesses directly to the 'SerialPort.ReadTo',
@ -423,7 +442,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// <remarks date="2026-Jan-23" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2026-Jan-28" author="Thomas Wiedebusch">
/// <remarks date="2026-Jan-28..30" author="Thomas Wiedebusch">
/// - Support for multiple FM2014s.
/// </remarks>
private static Boolean Read(CmdName cmdName, FM2014 fm2014, out String responseStr)
@ -456,8 +475,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
var isErrorStr = StatusReturn.Failed == statusReturn ? Resources.StrError + ": " : "";
var response = new CmdResponse(cmdName, $"{isErrorStr}{info} {responseStr}");
fm2014?.OnRawRecordReceived?.Invoke(fm2014, new ProcessExecEventArgs("", specificInfoObj: response,
statusReturn: statusReturn));
PublishResponse(fm2014, new ProcessExecEventArgs("", specificInfoObj: response, statusReturn: statusReturn));
return StatusReturn.Failed != statusReturn;
}
@ -479,7 +497,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// <remarks date="2026-Jan-12..23" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2026-Jan-28" author="Thomas Wiedebusch">
/// <remarks date="2026-Jan-28..30" author="Thomas Wiedebusch">
/// - Support for multiple FM2014s.
/// </remarks>
private static Boolean Write(CmdName cmdName, FM2014 fm2014, Object dataObj = null)
@ -560,8 +578,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
}
var statusReturn = retVal ? StatusReturn.Okay : StatusReturn.Failed;
fm2014.OnRawRecordReceived?.Invoke(fm2014, new ProcessExecEventArgs("", specificInfoObj: response,
statusReturn: statusReturn));
PublishResponse(fm2014, new ProcessExecEventArgs("", specificInfoObj: response, statusReturn: statusReturn));
return retVal;
}
@ -592,7 +609,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
var response = new CmdResponse(CmdName.CMD_ADDR_PC,
$"{Resources.StrCmdRespErrorBroadcast}");
fm2014.OnRawRecordReceived?.Invoke(fm2014, new ProcessExecEventArgs(Resources.StrError,
PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response,
statusReturn: StatusReturn.Failed));
SharedCmdTypeReminderLastCmd = CmdType.NOT_INITIALIZED;
@ -614,7 +631,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
var cmdName = CmdName.CMD_CONNECT;
var info = GetCmdInfo(cmdName) + ": " + BROADCAST_ADDRESS;
var response = new CmdResponse(cmdName, info);
fm2014.OnRawRecordReceived?.Invoke(fm2014, new ProcessExecEventArgs("", specificInfoObj: response));
PublishResponse(fm2014, new ProcessExecEventArgs("", specificInfoObj: response));
SharedCmdTypeReminderLastCmd = CmdType.BROADCAST;
return true;
}
@ -622,7 +639,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
var response = new CmdResponse(CmdName.CMD_ADDR_PC,
$"{Resources.StrCmdRespErrorBroadcast} - {e.Message}");
fm2014.OnRawRecordReceived?.Invoke(fm2014, new ProcessExecEventArgs(Resources.StrError,
PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, statusReturn: StatusReturn.Failed));
SharedCmdTypeReminderLastCmd = CmdType.NOT_INITIALIZED;
return false;
@ -655,7 +672,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
if (!SharedSerialPort.IsOpen)
{
response = new CmdResponse(CmdName.CMD_ADDR_PC, $"{Resources.StrCmdRespErrorIndividual}");
fm2014.OnRawRecordReceived?.Invoke(fm2014, new ProcessExecEventArgs(Resources.StrError,
PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response,
statusReturn: StatusReturn.Failed));
SharedCmdTypeReminderLastCmd = CmdType.NOT_INITIALIZED;
@ -677,7 +694,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
var cmdName = CmdName.CMD_CONNECT;
var info = GetCmdInfo(cmdName) + ": " + fm2014.AddressStr;
response = new CmdResponse(cmdName, info);
fm2014.OnRawRecordReceived?.Invoke(fm2014, new ProcessExecEventArgs("", specificInfoObj: response));
PublishResponse(fm2014, new ProcessExecEventArgs("", specificInfoObj: response));
fm2014.ConnectResponse = fm2014.ConnectResponse.Replace(ADDR_RECORD_STR, "");
var fields = fm2014.ConnectResponse.Split('.');
fm2014.FwVersion = fields[1];
@ -689,7 +706,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
response = new CmdResponse(CmdName.CMD_CONNECT, $"{Resources.StrCmdRespErrorIndividual}" +
$": {fm2014.AddressStr}" + $" - {e.Message}");
fm2014.OnRawRecordReceived?.Invoke(fm2014, new ProcessExecEventArgs(Resources.StrError,
PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, statusReturn: StatusReturn.Failed));
SharedCmdTypeReminderLastCmd = CmdType.NOT_INITIALIZED;
fm2014.IsLoggedOn = false;
@ -697,7 +714,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
}
response = new CmdResponse(CmdName.CMD_CONNECT, $"{Resources.StrCmdRespErrorIndividual}");
fm2014.OnRawRecordReceived?.Invoke(fm2014, new ProcessExecEventArgs(Resources.StrError,
PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, statusReturn: StatusReturn.Failed));
SharedCmdTypeReminderLastCmd = CmdType.NOT_INITIALIZED;
return false;
@ -789,7 +806,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
catch (Exception e)
{
var response = new CmdResponse(cmdName, e.Message);
OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
PublishResponse(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
statusReturn: StatusReturn.Failed));
return false;
}
@ -841,7 +858,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
out var intValue))
{
var response = new CmdResponse(cmdNameRef, infoRef, intValue);
fm2014.OnRawRecordReceived?.Invoke(fm2014,
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr,
specificInfoObj: response));
}
@ -854,7 +871,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
out var intValue))
{
var response = new CmdResponse(cmdNameDut, infoDut, intValue);
fm2014.OnRawRecordReceived?.Invoke(fm2014,
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr,
specificInfoObj: response));
}
@ -864,7 +881,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
catch (Exception e)
{
var response = new CmdResponse(cmdName, e.Message);
fm2014?.OnRawRecordReceived?.Invoke(fm2014, new ProcessExecEventArgs(Resources.StrError,
PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, statusReturn: StatusReturn.Failed));
}
}
@ -957,7 +974,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
catch (Exception e)
{
var response = new CmdResponse(cmdName, e.Message);
OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
PublishResponse(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
statusReturn: StatusReturn.Failed));
return false;
}
@ -992,7 +1009,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
var response = new CmdResponse(cmdName, info,
doubleValue: measTolerance * _toleranceRawToPercentScale * signMultiplier,
siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.PERCENTAGE_NON_SI));
fm2014?.OnRawRecordReceived?.Invoke(fm2014,
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
}
@ -1012,7 +1029,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
var response = new CmdResponse(cmdName, info,
doubleValue: TMR_RESOLUTION_s * 1000.0 * period,
siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME));
fm2014?.OnRawRecordReceived?.Invoke(fm2014,
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
@ -1022,7 +1039,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
response = new CmdResponse(cmdName, info,
doubleValue: 1.0 / (TMR_RESOLUTION_s * period),
siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY));
fm2014?.OnRawRecordReceived?.Invoke(fm2014,
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
@ -1033,7 +1050,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
info = GetCmdInfo(cmdName);
response = new CmdResponse(cmdName, info, doubleValue: flowRate_cm_per_h,
siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FLOW_RATE_NON_SI));
fm2014?.OnRawRecordReceived?.Invoke(fm2014,
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
}
@ -1054,7 +1071,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
var response = new CmdResponse(cmdName, info,
doubleValue: TMR_RESOLUTION_s * 1000.0 * period,
siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME));
fm2014?.OnRawRecordReceived?.Invoke(fm2014,
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
@ -1064,7 +1081,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
response = new CmdResponse(cmdName, info,
doubleValue: 1.0 / (TMR_RESOLUTION_s * period),
siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY));
fm2014?.OnRawRecordReceived?.Invoke(fm2014,
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
@ -1075,7 +1092,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
info = GetCmdInfo(cmdName);
response = new CmdResponse(cmdName, info, doubleValue: flowRate_cm_per_h,
siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FLOW_RATE_NON_SI));
fm2014?.OnRawRecordReceived?.Invoke(fm2014,
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
}
@ -1094,7 +1111,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));
fm2014?.OnRawRecordReceived?.Invoke(fm2014, new ProcessExecEventArgs("",
PublishResponse(fm2014, new ProcessExecEventArgs("",
actualProcessMessage: measurementInfo,
specificInfoObj: response));
@ -1105,7 +1122,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
info = GetCmdInfo(cmdName);
response = new CmdResponse(cmdName, info, doubleValue: RefFlowRate_cm_per_h,
siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FLOW_RATE_NON_SI));
fm2014?.OnRawRecordReceived?.Invoke(fm2014,
PublishResponse(fm2014,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfo,
specificInfoObj: response));
}
@ -1114,7 +1131,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
catch (Exception e)
{
var response = new CmdResponse(cmdName, e.Message);
fm2014?.OnRawRecordReceived?.Invoke(fm2014, new ProcessExecEventArgs(Resources.StrError,
PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, statusReturn: StatusReturn.Failed));
}
}
@ -1182,7 +1199,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
catch (Exception e)
{
var response = new CmdResponse(cmdName, e.Message);
OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
PublishResponse(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
statusReturn: StatusReturn.Failed));
}
return false;
@ -1206,7 +1223,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// <remarks date="2026-Jan-28..29" author="Thomas Wiedebusch">
/// - Support for multiple FM2014s.
/// </remarks>
private Boolean ReadAloneMeasurementControl(CmdName cmdName)
private Boolean ReadStandAloneMeasurement(CmdName cmdName)
{
// If the cyclic task has already been started everything is fine
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE || !IsLoggedOn)
@ -1257,9 +1274,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
var info = GetCmdInfo(cmdName);
Attenuation = attenuation;
var response = new CmdResponse(cmdName, info, Attenuation);
OnRawRecordReceived?.Invoke(this,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr,
specificInfoObj: response));
PublishResponse(this, new ProcessExecEventArgs("",
actualProcessMessage: measurementInfoStr, specificInfoObj: response));
}
}
if (str.Contains(StandAlonePartSearchScaleStr))
@ -1272,7 +1288,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
var info = GetCmdInfo(cmdName);
RefToDutScale_norm = value;
var response = new CmdResponse(cmdName, info, doubleValue: RefToDutScale_norm);
OnRawRecordReceived?.Invoke(this,
PublishResponse(this,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr,
specificInfoObj: response));
}
@ -1287,7 +1303,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
var info = GetCmdInfo(cmdName);
Dut_pulse_per_cm = value;
var response = new CmdResponse(cmdName, info, (Int32)Dut_pulse_per_cm);
OnRawRecordReceived?.Invoke(this,
PublishResponse(this,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr,
specificInfoObj: response));
// Added 2026-Jan-01 to overcome limitation of max 9999 Impulses per volume
@ -1296,7 +1312,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
// Calculate REF pulses per cubic meter
Ref_pulse_per_cm = (UInt32)Math.Round(Dut_pulse_per_cm * RefToDutScale_norm, 1);
response = new CmdResponse(cmdName, info, (Int32)Ref_pulse_per_cm);
OnRawRecordReceived?.Invoke(this,
PublishResponse(this,
new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr,
specificInfoObj: response));
}
@ -1307,7 +1323,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
catch (Exception e)
{
var response = new CmdResponse(cmdName, e.Message);
OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
PublishResponse(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
statusReturn: StatusReturn.Failed));
return false;
}
@ -1333,34 +1349,46 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE)
return false;
var cmdCode = GetCmdStr(CmdName.CMD_SERIAL);
const CmdName cmdName = CmdName.CMD_SERIAL;
var cmdName = CmdName.CMD_SERIAL;
try
{
var cmdCode = GetCmdStr(cmdName);
// Read out the serial number of this FM2014
if (!Write(cmdName, this))
{
return false;
}
IsLoggedOn = true;
if (Read(cmdName, this, out var responseStr))
SerialNumber = responseStr;
if (!string.IsNullOrEmpty(SerialNumber))
{
SerialNumber = SerialNumber.Replace(cmdCode, "");
IsLoggedOn = true;
}
if (IsLoggedOn)
cmdName = CmdName.CMD_PRINT_STATISTICS;
// Read out the lifetime of this FM2014
if (!Write(cmdName, this))
{
IsLoggedOn = ReadAloneMeasurementControl(CmdName.CMD_READ_EEP_MEAS_SETUP);
return false;
}
if (Read(cmdName, this, out responseStr))
{
var cleanedResponse = Regex.Replace(responseStr, "[^0-9]", string.Empty);
if (int.TryParse(cleanedResponse, /*NumberStyles.HexNumber, new CultureInfo("en"),*/ out var intValue))
Lifetime_h = intValue;
}
IsLoggedOn = ReadStandAloneMeasurement(CmdName.CMD_READ_EEP_MEAS_SETUP);
}
catch (Exception e)
{
var response = new CmdResponse(cmdName, e.Message);
OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
PublishResponse(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
statusReturn: StatusReturn.Failed));
return false;
}
@ -1409,7 +1437,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));
OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(infoStr,
PublishResponse(this, new ProcessExecEventArgs(infoStr,
actualProcessMessage: infoStr,
actualProcessPercent: _actualProcessProgress_percent,
specificInfoObj: response));