FM2014Core: - refactored, - start device stati

This commit is contained in:
Thomas Wiedebusch 2026-02-24 11:50:00 +01:00
parent 25d296d3b7
commit 0ab75f2235
20 changed files with 452 additions and 310 deletions

View File

@ -730,6 +730,10 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
/// <remarks date="2025-Feb-11" author="Thomas Wiedebusch"> /// <remarks date="2025-Feb-11" author="Thomas Wiedebusch">
/// - For special requirements the "not for production" version can be loaded. /// - For special requirements the "not for production" version can be loaded.
/// </remarks> /// </remarks>
/// <remarks date="2026-Feb-23" author="Thomas Wiedebusch">
/// - All FW versions as stated in the fwVersion string will be accepted as valid as those are defined in the
/// 'Standard-' or 'Special-Requirements'.
/// </remarks>
public static Boolean SearchProdFwInDb(String fwVersion, out Int32? fwPackageFileId, out String errorMsg, public static Boolean SearchProdFwInDb(String fwVersion, out Int32? fwPackageFileId, out String errorMsg,
Boolean isForProduction = true) Boolean isForProduction = true)
{ {
@ -746,7 +750,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
if (fwPackInfo != null) if (fwPackInfo != null)
{ {
foreach (var fw in fwPackInfo.Where(fw => (fw.CurrentForProd || !isForProduction) && foreach (var fw in fwPackInfo.Where(fw => //(fw.CurrentForProd || !isForProduction) &&
fw.Name.Contains(fwVersion))) fw.Name.Contains(fwVersion)))
{ {
fwPackageFileId = fw.FileClusterStoreId; fwPackageFileId = fw.FileClusterStoreId;

View File

@ -1,46 +0,0 @@
/*********************************************************************************************************************/
/*! @file ConnectionStatus.cs
* @brief <i></i>
*
* @author Thomas Wiedebusch
* @date 2026-Feb-17
*
* @details <i>.</i>
*
* @copyright © SENSUS GmbH 2026. All rights reserved.
*********************************************************************************************************************/
using System;
using Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Consts;
namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{
/// <summary>
/// Assembly of device status and multilingual information
/// </summary>
public struct ConnectionStatus
{
/// <summary>
/// Enumerator of command name acronym
/// </summary>
public readonly ConnectionStatusName Name;
/// <summary>
/// Multilingual information
/// </summary>
public readonly String InfoStr;
/// <summary>
/// Single command table entry to combine the name with the code and the communication type
/// </summary>
/// <param name="deviceConnectionStatus"></param>
/// <param name="statusInfoStr">Multilingual command information string</param>
public ConnectionStatus(ConnectionStatusName deviceConnectionStatus, String statusInfoStr)
{
Name = deviceConnectionStatus;
InfoStr = statusInfoStr;
}
}
}
/*********************************************** END OF FILE *********************************************************/

View File

@ -1,67 +0,0 @@
/*********************************************************************************************************************/
/*! @file Cmd.cs
* @brief <i></i>
*
* @author Thomas Wiedebusch
* @date 2026-Feb-17
*
* @details <i>.</i>
*
* @copyright © SENSUS GmbH 2026. All rights reserved.
*********************************************************************************************************************/
using System;
namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Consts
{
/// <summary>
/// Assembly of command information and functional code
/// </summary>
public struct Cmd
{
/// <summary>
/// Enumerator of command name acronym
/// </summary>
public readonly CmdName CmdName;
/// <summary>
/// Code being sent to FM2014 as string
/// </summary>
public readonly String CmdCodeStr;
/// <summary>
/// Command type regarding the address (broadcast or individual) and answer
/// </summary>
public readonly CmdType CmdType;
/// <summary>
/// Command parameter style for raw data conversion from response string or for transmission
/// </summary>
public readonly ParaFormat ParamFormat;
/// <summary>
/// Multilingual command information string
/// </summary>
public readonly String CmdInfoStr;
/// <summary>
/// Single command table entry to combine the name with the code and the communication type
/// </summary>
/// <param name="cmdName">Enumerator of command name acronym</param>
/// <param name="cmdCodeStr">Code being sent to FM2014 as string</param>
/// <param name="cmdType">Command type regarding the address (broadcast or individual) and answer</param>
/// <param name="paraFormat">parameter format for conversion</param>
/// <param name="cmdInfoStr">Multilingual command information string</param>
public Cmd(CmdName cmdName, String cmdCodeStr, CmdType cmdType, ParaFormat paraFormat,
String cmdInfoStr)
{
CmdName = cmdName;
CmdCodeStr = cmdCodeStr;
CmdType = cmdType;
ParamFormat = paraFormat;
CmdInfoStr = cmdInfoStr;
}
}
}
/*********************************************** END OF FILE *********************************************************/

View File

@ -1,45 +0,0 @@
/*********************************************************************************************************************/
/*! @file CmdErrorMsg.cs
* @brief <i></i>
*
* @author Thomas Wiedebusch
* @date 2026-Feb-17
*
* @details <i>.</i>
*
* @copyright © SENSUS GmbH 2026. All rights reserved.
*********************************************************************************************************************/
using System;
namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Consts
{
/// <summary>
/// Combine constant string returns from FM2014 firmware to multilingual information
/// </summary>
public struct CmdErrorMsg
{
/// <summary>
/// Hard coded return string from FM2014
/// </summary>
public String FM2014ErrorReturnStr;
/// <summary>
/// Multilingual forwarded string to GUI
/// </summary>
public String MultilingualForwardStr;
/// <summary>
/// Ctor
/// </summary>
/// <param name="fixedFwErrorStr"></param>
/// <param name="multilingualFeedback"></param>
public CmdErrorMsg(String fixedFwErrorStr, String multilingualFeedback)
{
FM2014ErrorReturnStr = fixedFwErrorStr;
MultilingualForwardStr = multilingualFeedback;
}
}
}
/*********************************************** END OF FILE *********************************************************/

View File

@ -15,7 +15,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co
/// <summary> /// <summary>
/// Parameter format to convert the response or the request /// Parameter format to convert the response or the request
/// </summary> /// </summary>
public enum ParaFormat public enum CmdParaFormat
{ {
/// <summary> /// <summary>
/// String taken directly to send or as received /// String taken directly to send or as received

View File

@ -37,6 +37,5 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co
/// </summary> /// </summary>
uninitialized uninitialized
} }
} }
/*********************************************** END OF FILE *********************************************************/ /*********************************************** END OF FILE *********************************************************/

View File

@ -15,7 +15,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co
/// <summary> /// <summary>
/// Device status of FM2014 /// Device status of FM2014
/// </summary> /// </summary>
public enum ConnectionStatusName public enum ConnectStatusName
{ {
/// <summary> /// <summary>
/// Unknown status /// Unknown status

View File

@ -0,0 +1,46 @@
/*********************************************************************************************************************/
/*! @file ErrorStatus.cs
* @brief <i>FM2014 device error status</i>
*
* @author Thomas Wiedebusch
* @date 2026-Feb-24
*
* @details <i>.</i>
*
* @copyright © SENSUS GmbH 2026. All rights reserved.
*********************************************************************************************************************/
namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Consts
{
/// <summary>
/// Error status returned from FM2014 with command '0x41 ' or 'UNICODE_ACK' as MSB - 1
/// </summary>
public enum ErrorStatus
{
/// <summary>
/// REF signal timeout during the adjustment measurement for two consecutive pulses of 21.889 s
/// </summary>
AdjustMeasurementRefTimeout = 0x80,
/// <summary>
/// DUT signal timeout during the adjustment measurement for two consecutive pulses of 21.889 s
/// </summary>
AdjustMeasurementDutTimeout = 0x40,
/// <summary>
/// DUT double pulse detected during the time measurement
/// </summary>
TimeMeasurementDoublePulseDutDetected = 0x04,
/// <summary>
/// REF signal timeout during the time measurement for two consecutive pulses of 21.889 s
/// </summary>
TimeMeasurementRefTimeout = 0x02,
/// <summary>
/// REF signal timeout during the time measurement for two consecutive pulses of 21.889 s
/// </summary>
TimeMeasurementDutTimeout = 0x01
}
}
/*********************************************** END OF FILE *********************************************************/

View File

@ -0,0 +1,47 @@
/*********************************************************************************************************************/
/*! @file Fault1Status.cs
* @brief <i>FM2014 device fault1 status</i>
*
* @author Thomas Wiedebusch
* @date 2026-Feb-24
*
* @details <i>.</i>
*
* @copyright © SENSUS GmbH 2026. All rights reserved.
*********************************************************************************************************************/
namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Consts
{
/// <summary>
/// Fault1 status returned from FM2014 with command '0x43 ' or 'UNICODE_ACK' as LSB
/// </summary>
public enum Fault1Status
{
/// <summary>
/// Error for U/I converter for the current output of the tolerance DUT to REF display
/// </summary>
ErrorUiConverterCurrentOutput = 0x40,
/// <summary>
/// Setup of REF pulses is out of range command 'M'
/// </summary>
RefPulseSetupOor = 0x08,
/// <summary>
/// Setup of DUT pulses is out of range command 'H'
/// </summary>
DutPulseSetupOor = 0x04,
/// <summary>
/// Missing or out of range of the setup of the scale REF to DUT command 'K'
/// </summary>
MissingOrOorSetupScaleRefToDut = 0x02,
/// <summary>
/// Missing or out of range of double pulse ignorance deadtime command 'G' or 's' and 'S'
/// </summary>
MissingOrOorSetupDoublePulesDeadtime = 0x01
}
}
/*********************************************** END OF FILE *********************************************************/

View File

@ -0,0 +1,31 @@
/*********************************************************************************************************************/
/*! @file Fault2Status.cs
* @brief <i>FM2014 device fault2 status</i>
*
* @author Thomas Wiedebusch
* @date 2026-Feb-24
*
* @details <i>.</i>
*
* @copyright © SENSUS GmbH 2026. All rights reserved.
*********************************************************************************************************************/
namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Consts
{
/// <summary>
/// Fault2 status returned from FM2014 with command '0x43 ' or 'UNICODE_ACK' as LSB
/// </summary>
public enum Fault2Status
{
/// <summary>
/// FLASH access error causing parameters set to default
/// </summary>
FlashAccessError = 0x10,
/// <summary>
/// Missing the setup for current output attenuation of the DUT to REF tolerance command 'T'
/// </summary>
MissingCurrentOutputAttenuation = 0x08,
}
}
/*********************************************** END OF FILE *********************************************************/

View File

@ -0,0 +1,56 @@
/*********************************************************************************************************************/
/*! @file MeasureStatus.cs
* @brief <i>FM2014 device measurement status</i>
*
* @author Thomas Wiedebusch
* @date 2026-Feb-24
*
* @details <i>.</i>
*
* @copyright © SENSUS GmbH 2026. All rights reserved.
*********************************************************************************************************************/
namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Consts
{
/// <summary>
/// Measurement status returned from FM2014 with command '0x40 ' or 'UNICODE_ACK' as MSB
/// </summary>
public enum MeasureStatus
{
/// <summary>
/// Double pulse deadtime set to 0, means double pulse ignorance time is switched off
/// </summary>
DoublePulseDeadtimeInactive = 0x80,
/// <summary>
/// Time measurement of REF has finished
/// </summary>
TimeMeasureRefRdy = 0x40,
/// <summary>
/// Time measurement of DUT has finished
/// </summary>
TimeMeasureDutRdy = 0x20,
/// <summary>
/// Time measurement of REF is ongoing
/// </summary>
TimeMeasureRefActive = 0x10,
/// <summary>
/// Time measurement of DUT is ongoing
/// </summary>
TimeMeasureDutActive = 0x08,
/// <summary>
/// Adjustment measurement of REF is finished
/// </summary>
AdjustMeasureRefRdy = 0x02,
/// <summary>
/// Adjustment measurement of DUT is finished
/// </summary>
AdjustMeasureDutRdy = 0x01
}
}
/*********************************************** END OF FILE *********************************************************/

View File

@ -737,7 +737,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
do do
{ {
// Change the SI unit to ms instead of seconds // Change the SI unit to ms instead of seconds
var response = new CmdResponse(CmdName.CmdResetMeasurement, infoStr, (UInt32)resetDelayCtr_ms, var response = new FM2014CmdResponse(CmdName.CmdResetMeasurement, infoStr, (UInt32)resetDelayCtr_ms,
unit: "m" + Units.GetInfo(Units.Name.TIME_s)); unit: "m" + Units.GetInfo(Units.Name.TIME_s));
PublishResponse(BroadcastFm2014, new ProcessExecEventArgs("", PublishResponse(BroadcastFm2014, new ProcessExecEventArgs("",
actualProcessMessage: infoStr, actualProcessMessage: infoStr,
@ -836,7 +836,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// <remarks date="2026-Feb-13..Feb-21" author="Thomas Wiedebusch"> /// <remarks date="2026-Feb-13..Feb-21" author="Thomas Wiedebusch">
/// - Initial. /// - Initial.
/// </remarks> /// </remarks>
private static void ResponseEvent(FM2014 fm2014, CmdResponse cmdResponse, private static void ResponseEvent(FM2014 fm2014, FM2014CmdResponse cmdResponse,
StatusReturn statusReturn = StatusReturn.Unknown) StatusReturn statusReturn = StatusReturn.Unknown)
{ {
var processExecEventArgs = new ProcessExecEventArgs("", var processExecEventArgs = new ProcessExecEventArgs("",
@ -900,7 +900,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
} }
// Dispatch human-readable result // Dispatch human-readable result
var info = Resources.StrInputMsgDoublePulseDeadTime; var info = Resources.StrInputMsgDoublePulseDeadTime;
var response = new CmdResponse(cmdName, info, var response = new FM2014CmdResponse(cmdName, info,
doubleValue: fm2014.DoublePulseDeadtime_ms * 0.001, doubleValue: fm2014.DoublePulseDeadtime_ms * 0.001,
unit: Units.GetInfo(Units.Name.TIME_s)); unit: Units.GetInfo(Units.Name.TIME_s));
ResponseEvent(actualFm2014, response); ResponseEvent(actualFm2014, response);
@ -996,7 +996,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
} }
catch (Exception e) catch (Exception e)
{ {
var response = new CmdResponse(cmdName, e.Message); var response = new FM2014CmdResponse(cmdName, e.Message);
PublishResponse(firstActiveFm2014, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response, PublishResponse(firstActiveFm2014, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
statusReturn: StatusReturn.Failed)); statusReturn: StatusReturn.Failed));
return false; return false;
@ -1052,7 +1052,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
if (Read(cmdName, fm2014, out var responseStr, out var status, out _)) if (Read(cmdName, fm2014, out var responseStr, out var status, out _))
{ {
info += $": {responseStr}"; info += $": {responseStr}";
var response = new CmdResponse(cmdName, info, status); var response = new FM2014CmdResponse(cmdName, info, status);
ResponseEvent(fm2014, response); ResponseEvent(fm2014, response);
} }
} }
@ -1061,7 +1061,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{ {
if (Read(cmdNameRef, fm2014, out _, out var intValue, out _)) if (Read(cmdNameRef, fm2014, out _, out var intValue, out _))
{ {
var response = new CmdResponse(cmdNameRef, infoRef, intValue); var response = new FM2014CmdResponse(cmdNameRef, infoRef, intValue);
ResponseEvent(fm2014, response); ResponseEvent(fm2014, response);
} }
} }
@ -1070,14 +1070,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{ {
if (Read(cmdNameDut, fm2014, out _, out var intValue, out _)) if (Read(cmdNameDut, fm2014, out _, out var intValue, out _))
{ {
var response = new CmdResponse(cmdNameDut, infoDut, intValue); var response = new FM2014CmdResponse(cmdNameDut, infoDut, intValue);
ResponseEvent(fm2014, response); ResponseEvent(fm2014, response);
} }
} }
} }
catch (Exception e) catch (Exception e)
{ {
var response = new CmdResponse(cmdName, e.Message); var response = new FM2014CmdResponse(cmdName, e.Message);
PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError, PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, statusReturn: StatusReturn.Failed)); specificInfoObj: response, statusReturn: StatusReturn.Failed));
} }
@ -1194,7 +1194,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
} }
catch (Exception e) catch (Exception e)
{ {
var response = new CmdResponse(cmdName, e.Message); var response = new FM2014CmdResponse(cmdName, e.Message);
PublishResponse(firstActiveFm2014, new ProcessExecEventArgs(Resources.StrError, PublishResponse(firstActiveFm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, statusReturn: StatusReturn.Failed)); specificInfoObj: response, statusReturn: StatusReturn.Failed));
return false; return false;
@ -1221,7 +1221,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
if (Read(cmdName, fm2014, out var responseStr, out var status, out _)) if (Read(cmdName, fm2014, out var responseStr, out var status, out _))
{ {
info += $": {responseStr}"; info += $": {responseStr}";
var response = new CmdResponse(cmdName, info, status); var response = new FM2014CmdResponse(cmdName, info, status);
ResponseEvent(fm2014, response); ResponseEvent(fm2014, response);
} }
} }
@ -1236,7 +1236,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{ {
fm2014.DutToRefToleranceMeasured_percent = doubleValue * fm2014.DutToRefToleranceMeasured_percent = doubleValue *
fm2014.CurrentOutputDisplayRangeNominalToRealScale; fm2014.CurrentOutputDisplayRangeNominalToRealScale;
var response = new CmdResponse(cmdName, info, var response = new FM2014CmdResponse(cmdName, info,
doubleValue: fm2014.DutToRefToleranceMeasured_percent, doubleValue: fm2014.DutToRefToleranceMeasured_percent,
unit: Units.GetInfo(Units.Name.PERCENT)); unit: Units.GetInfo(Units.Name.PERCENT));
@ -1259,7 +1259,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
if (Read(cmdName, fm2014, out _, out var period, out _)) if (Read(cmdName, fm2014, out _, out var period, out _))
{ {
// Publish the period [ms] // Publish the period [ms]
var response = new CmdResponse(cmdName, info, var response = new FM2014CmdResponse(cmdName, info,
doubleValue: TMR_RESOLUTION_s * 1000.0 * period, doubleValue: TMR_RESOLUTION_s * 1000.0 * period,
unit: "m" + Units.GetInfo(Units.Name.TIME_s)); unit: "m" + Units.GetInfo(Units.Name.TIME_s));
ResponseEvent(fm2014, response); ResponseEvent(fm2014, response);
@ -1267,7 +1267,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
// Publish the frequency in [Hz] // Publish the frequency in [Hz]
cmdName = CmdName.CmdCalculatedFrequRefPeriod; cmdName = CmdName.CmdCalculatedFrequRefPeriod;
info = GetCmdInfo(cmdName); info = GetCmdInfo(cmdName);
response = new CmdResponse(cmdName, info, response = new FM2014CmdResponse(cmdName, info,
doubleValue: 1.0 / (TMR_RESOLUTION_s * period), doubleValue: 1.0 / (TMR_RESOLUTION_s * period),
unit: Units.GetInfo(Units.Name.FREQUENCY_Hz)); unit: Units.GetInfo(Units.Name.FREQUENCY_Hz));
ResponseEvent(fm2014, response); ResponseEvent(fm2014, response);
@ -1277,7 +1277,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
// Publish the calculated flow rate [m³/h] // Publish the calculated flow rate [m³/h]
cmdName = CmdName.CmdCalculatedFlowRefPeriod; cmdName = CmdName.CmdCalculatedFlowRefPeriod;
info = GetCmdInfo(cmdName); info = GetCmdInfo(cmdName);
response = new CmdResponse(cmdName, info, doubleValue: fm2014.RefFlowRate_cm_per_h, response = new FM2014CmdResponse(cmdName, info, doubleValue: fm2014.RefFlowRate_cm_per_h,
unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h)); unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h));
ResponseEvent(fm2014, response); ResponseEvent(fm2014, response);
} }
@ -1294,7 +1294,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
if (Read(cmdName, fm2014, out _, out var period, out _)) if (Read(cmdName, fm2014, out _, out var period, out _))
{ {
// Publish the period [ms] // Publish the period [ms]
var response = new CmdResponse(cmdName, info, var response = new FM2014CmdResponse(cmdName, info,
doubleValue: TMR_RESOLUTION_s * 1000.0 * period, doubleValue: TMR_RESOLUTION_s * 1000.0 * period,
unit: "m" + Units.GetInfo(Units.Name.TIME_s)); unit: "m" + Units.GetInfo(Units.Name.TIME_s));
ResponseEvent(fm2014, response); ResponseEvent(fm2014, response);
@ -1302,7 +1302,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
// Publish the frequency in [Hz] // Publish the frequency in [Hz]
cmdName = CmdName.CmdCalculatedFrequencyDutPeriod; cmdName = CmdName.CmdCalculatedFrequencyDutPeriod;
info = GetCmdInfo(cmdName); info = GetCmdInfo(cmdName);
response = new CmdResponse(cmdName, info, response = new FM2014CmdResponse(cmdName, info,
doubleValue: 1.0 / (TMR_RESOLUTION_s * period), doubleValue: 1.0 / (TMR_RESOLUTION_s * period),
unit: Units.GetInfo(Units.Name.FREQUENCY_Hz)); unit: Units.GetInfo(Units.Name.FREQUENCY_Hz));
ResponseEvent(fm2014, response); ResponseEvent(fm2014, response);
@ -1312,7 +1312,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
// Publish the calculated flow rate [m³/h] // Publish the calculated flow rate [m³/h]
cmdName = CmdName.CmdCalculatedFlowDutPeriod; cmdName = CmdName.CmdCalculatedFlowDutPeriod;
info = GetCmdInfo(cmdName); info = GetCmdInfo(cmdName);
response = new CmdResponse(cmdName, info, doubleValue: fm2014.DutFlowRate_cm_per_h, response = new FM2014CmdResponse(cmdName, info, doubleValue: fm2014.DutFlowRate_cm_per_h,
unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h)); unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h));
ResponseEvent(fm2014, response); ResponseEvent(fm2014, response);
} }
@ -1331,7 +1331,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
else statusReturn = StatusReturn.Okay; else statusReturn = StatusReturn.Okay;
// Publish the frequency in [Hz] // Publish the frequency in [Hz]
var response = new CmdResponse(cmdName, info, (UInt32)refFrequency, var response = new FM2014CmdResponse(cmdName, info, (UInt32)refFrequency,
unit: Units.GetInfo(Units.Name.FREQUENCY_Hz)); unit: Units.GetInfo(Units.Name.FREQUENCY_Hz));
ResponseEvent(fm2014, response, statusReturn); ResponseEvent(fm2014, response, statusReturn);
@ -1340,7 +1340,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
// Publish the calculated flow rate [m³/h] // Publish the calculated flow rate [m³/h]
cmdName = CmdName.CmdCalculatedFlowRefFrequ; cmdName = CmdName.CmdCalculatedFlowRefFrequ;
info = GetCmdInfo(cmdName); info = GetCmdInfo(cmdName);
response = new CmdResponse(cmdName, info, doubleValue: fm2014.RefFlowRate_cm_per_h, response = new FM2014CmdResponse(cmdName, info, doubleValue: fm2014.RefFlowRate_cm_per_h,
unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h)); unit: Units.GetInfo(Units.Name.FLOW_RATE_cm_per_h));
ResponseEvent(fm2014, response, statusReturn); ResponseEvent(fm2014, response, statusReturn);
} }
@ -1348,7 +1348,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
} }
catch (Exception e) catch (Exception e)
{ {
var response = new CmdResponse(cmdName, e.Message); var response = new FM2014CmdResponse(cmdName, e.Message);
PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError, PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, statusReturn: StatusReturn.Failed)); specificInfoObj: response, statusReturn: StatusReturn.Failed));
} }
@ -1395,7 +1395,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// <param name="doublePulseDeadtime_ms">0 if double impulse deadtime detection is off</param> /// <param name="doublePulseDeadtime_ms">0 if double impulse deadtime detection is off</param>
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
/// <remarks date="2026-Jan-14..Feb-21" author="Thomas Wiedebusch"> /// <remarks date="2026-Jan-14..Feb-24" author="Thomas Wiedebusch">
/// - Initial. /// - Initial.
/// </remarks> /// </remarks>
public static Boolean PulseTimeMeasurement(UInt16? refPulsesRequired = null, UInt16? dutPulsesRequired = null, public static Boolean PulseTimeMeasurement(UInt16? refPulsesRequired = null, UInt16? dutPulsesRequired = null,
@ -1455,7 +1455,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
} }
// Dispatch human-readable result // Dispatch human-readable result
var info = Resources.StrCmdMsgRefSetPls; var info = Resources.StrCmdMsgRefSetPls;
var response = new CmdResponse(cmdName, info, var response = new FM2014CmdResponse(cmdName, info,
intValue: firstActiveFm2014.RefPulsesRequired); intValue: firstActiveFm2014.RefPulsesRequired);
ResponseEvent(BroadcastFm2014, response); ResponseEvent(BroadcastFm2014, response);
} }
@ -1469,14 +1469,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
} }
// Dispatch human-readable result // Dispatch human-readable result
var info = Resources.StrCmdMsgDutSetPls; var info = Resources.StrCmdMsgDutSetPls;
var response = new CmdResponse(cmdName, info, var response = new FM2014CmdResponse(cmdName, info,
intValue: firstActiveFm2014.DutPulsesRequired); intValue: firstActiveFm2014.DutPulsesRequired);
ResponseEvent(BroadcastFm2014, response); ResponseEvent(BroadcastFm2014, response);
} }
} }
catch (Exception e) catch (Exception e)
{ {
var response = new CmdResponse(cmdName, e.Message); var response = new FM2014CmdResponse(cmdName, e.Message);
PublishResponse(firstActiveFm2014, new ProcessExecEventArgs(Resources.StrError, PublishResponse(firstActiveFm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, statusReturn: StatusReturn.Failed)); specificInfoObj: response, statusReturn: StatusReturn.Failed));
return false; return false;
@ -1507,7 +1507,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
if (Read(cmdName, fm2014, out var responseStr, out var status, out _)) if (Read(cmdName, fm2014, out var responseStr, out var status, out _))
{ {
info += $": {responseStr}"; info += $": {responseStr}";
var response = new CmdResponse(cmdName, info, status); var response = new FM2014CmdResponse(cmdName, info, status);
ResponseEvent(fm2014, response); ResponseEvent(fm2014, response);
} }
} }
@ -1535,7 +1535,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
statusReturn = StatusReturn.Okay; statusReturn = StatusReturn.Okay;
} }
var response = new CmdResponse(cmdName, info, pulses); var response = new FM2014CmdResponse(cmdName, info, pulses);
ResponseEvent(fm2014, response, statusReturn); ResponseEvent(fm2014, response, statusReturn);
// Feed the progress counter // Feed the progress counter
@ -1568,7 +1568,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
statusReturn = StatusReturn.Okay; statusReturn = StatusReturn.Okay;
} }
var response = new CmdResponse(cmdName, info, pulses); var response = new FM2014CmdResponse(cmdName, info, pulses);
ResponseEvent(fm2014, response, statusReturn); ResponseEvent(fm2014, response, statusReturn);
// Feed the progress counter but only if not done for REF pulses // Feed the progress counter but only if not done for REF pulses
@ -1596,7 +1596,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{ {
// Is converting to a real time in seconds // Is converting to a real time in seconds
fm2014.RefTimerTicksMeasured = (UInt32)timerTicks; fm2014.RefTimerTicksMeasured = (UInt32)timerTicks;
var response = new CmdResponse(cmdName, info, doubleValue: fm2014.RefTimeMeasured_s, var response = new FM2014CmdResponse(cmdName, info, doubleValue: fm2014.RefTimeMeasured_s,
unit: Units.GetInfo(Units.Name.TIME_s)); unit: Units.GetInfo(Units.Name.TIME_s));
ResponseEvent(fm2014, response); ResponseEvent(fm2014, response);
} }
@ -1618,7 +1618,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{ {
// Is converting to a real time in seconds // Is converting to a real time in seconds
fm2014.DutTimerTicksMeasured = (UInt32)timerTicks; fm2014.DutTimerTicksMeasured = (UInt32)timerTicks;
var response = new CmdResponse(cmdName, info, doubleValue: fm2014.DutTimeMeasured_s, var response = new FM2014CmdResponse(cmdName, info, doubleValue: fm2014.DutTimeMeasured_s,
unit: Units.GetInfo(Units.Name.TIME_s)); unit: Units.GetInfo(Units.Name.TIME_s));
ResponseEvent(fm2014, response); ResponseEvent(fm2014, response);
} }
@ -1648,7 +1648,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
statusReturn = StatusReturn.MeasurementOutOfRange; statusReturn = StatusReturn.MeasurementOutOfRange;
else statusReturn = StatusReturn.MeasurementInRange; else statusReturn = StatusReturn.MeasurementInRange;
var response = new CmdResponse(cmdName, info, var response = new FM2014CmdResponse(cmdName, info,
doubleValue: fm2014.DutToRefToleranceMeasured_percent, doubleValue: fm2014.DutToRefToleranceMeasured_percent,
unit: Units.GetInfo(Units.Name.PERCENT)); unit: Units.GetInfo(Units.Name.PERCENT));
ResponseEvent(fm2014, response, statusReturn); ResponseEvent(fm2014, response, statusReturn);
@ -1661,7 +1661,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
} }
catch (Exception e) catch (Exception e)
{ {
var response = new CmdResponse(cmdName, e.Message); var response = new FM2014CmdResponse(cmdName, e.Message);
PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError, PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, statusReturn: StatusReturn.Failed)); specificInfoObj: response, statusReturn: StatusReturn.Failed));
} }
@ -1705,14 +1705,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
var statusReturn = StatusReturn.Unknown; var statusReturn = StatusReturn.Unknown;
var numberStyle = NumberStyles.None; var numberStyle = NumberStyles.None;
var paraFormat = GetParaFormat(cmdName); var paraFormat = GetParaFormat(cmdName);
if (paraFormat == ParaFormat.uintHexValue || paraFormat == ParaFormat.byteHexValuePlusSign) if (paraFormat == CmdParaFormat.uintHexValue || paraFormat == CmdParaFormat.byteHexValuePlusSign)
{ {
numberStyle = NumberStyles.HexNumber; numberStyle = NumberStyles.HexNumber;
} }
switch (paraFormat) switch (paraFormat)
{ {
case ParaFormat.byteHexValuePlusSign: case CmdParaFormat.byteHexValuePlusSign:
// The response contains a sign! // The response contains a sign!
var signMultiplier = responseStr.Contains("+") ? 1.0 : -1.0; var signMultiplier = responseStr.Contains("+") ? 1.0 : -1.0;
var cleanedStr = responseStr.Replace("+", "").Replace("-", ""); var cleanedStr = responseStr.Replace("+", "").Replace("-", "");
@ -1722,8 +1722,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
} }
break; break;
case ParaFormat.uintHexValue: case CmdParaFormat.uintHexValue:
case ParaFormat.uintDecimalValue: case CmdParaFormat.uintDecimalValue:
if (uint.TryParse(responseStr, numberStyle, new CultureInfo("en"), out intValueParsed)) if (uint.TryParse(responseStr, numberStyle, new CultureInfo("en"), out intValueParsed))
{ {
intValue = intValueParsed; intValue = intValueParsed;
@ -1783,7 +1783,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
} }
var isErrorStr = StatusReturn.Failed == statusReturn ? Resources.StrError + ": " : ""; var isErrorStr = StatusReturn.Failed == statusReturn ? Resources.StrError + ": " : "";
var response = new CmdResponse(cmdName, $"{isErrorStr}{info} {responseStr}"); var response = new FM2014CmdResponse(cmdName, $"{isErrorStr}{info} {responseStr}");
// Publish the raw response for logging // Publish the raw response for logging
ResponseEvent(fm2014, response, statusReturn); ResponseEvent(fm2014, response, statusReturn);
@ -1851,8 +1851,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
var cmdCodeStr = GetCmdStr(cmdName); var cmdCodeStr = GetCmdStr(cmdName);
var info = GetCmdInfo(cmdName) + $": {cmdCodeStr}"; var info = GetCmdInfo(cmdName) + $": {cmdCodeStr}";
var isHexFormat = GetParaFormat(cmdName) == ParaFormat.uintHexValue; var isHexFormat = GetParaFormat(cmdName) == CmdParaFormat.uintHexValue;
CmdResponse response; FM2014CmdResponse response;
// Without data the command alone needs to be sent // Without data the command alone needs to be sent
if (dataObj == null) if (dataObj == null)
{ {
@ -1860,7 +1860,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
var additionalInfo = GetCmdType(cmdName) == CmdType.broadcast var additionalInfo = GetCmdType(cmdName) == CmdType.broadcast
? Resources.StrCommDirectionSetup ? Resources.StrCommDirectionSetup
: Resources.StrCommDirectionRequest; : Resources.StrCommDirectionRequest;
response = new CmdResponse(cmdName, $"{additionalInfo}: {info}"); response = new FM2014CmdResponse(cmdName, $"{additionalInfo}: {info}");
} }
else else
{ {
@ -1868,7 +1868,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
if (dataObj is String) if (dataObj is String)
{ {
SharedSerialPort.Write($"{dataObj}{END_OF_STR}"); SharedSerialPort.Write($"{dataObj}{END_OF_STR}");
response = new CmdResponse(cmdName, $"{Resources.StrCommDirectionSetup}: {info}: {dataObj}"); response = new FM2014CmdResponse(cmdName, $"{Resources.StrCommDirectionSetup}: {info}: {dataObj}");
} }
// The data contains the preceding setup, the command code and string delimiter will be added here // The data contains the preceding setup, the command code and string delimiter will be added here
@ -1876,13 +1876,13 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{ {
var valueStr = isHexFormat ? $"{dataObj:X}{cmdCodeStr}" : $"{dataObj}{cmdCodeStr}"; var valueStr = isHexFormat ? $"{dataObj:X}{cmdCodeStr}" : $"{dataObj}{cmdCodeStr}";
SharedSerialPort.Write($"{valueStr}{END_OF_STR}"); SharedSerialPort.Write($"{valueStr}{END_OF_STR}");
response = new CmdResponse(cmdName, response = new FM2014CmdResponse(cmdName,
$"{Resources.StrCommDirectionSetup}: {info}: {valueStr}"); $"{Resources.StrCommDirectionSetup}: {info}: {valueStr}");
} }
else else
{ {
response = new CmdResponse(cmdName, response = new FM2014CmdResponse(cmdName,
$"{Resources.StrError}: {Resources.StrCommDirectionSetup}: {info} - " + $"{Resources.StrError}: {Resources.StrCommDirectionSetup}: {info} - " +
$"{Resources.StrCmdRespErrorPramTypeNotSupported} {dataObj}"); $"{Resources.StrCmdRespErrorPramTypeNotSupported} {dataObj}");
retVal = false; retVal = false;
@ -1913,7 +1913,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
if (fm2014 == null) if (fm2014 == null)
return false; return false;
CmdResponse response; FM2014CmdResponse response;
try try
{ {
if (!SharedSerialPort.IsOpen) if (!SharedSerialPort.IsOpen)
@ -1921,7 +1921,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
SharedSerialPort.Open(); SharedSerialPort.Open();
if (!SharedSerialPort.IsOpen) if (!SharedSerialPort.IsOpen)
{ {
response = new CmdResponse(CmdName.CmdAddress, response = new FM2014CmdResponse(CmdName.CmdAddress,
$"{Resources.StrCmdRespErrorBroadcast}"); $"{Resources.StrCmdRespErrorBroadcast}");
PublishResponse(BroadcastFm2014, new ProcessExecEventArgs(Resources.StrError, PublishResponse(BroadcastFm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, specificInfoObj: response,
@ -1941,14 +1941,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
SharedSerialPort.Write(BROADCAST_ADDRESS + END_OF_STR); SharedSerialPort.Write(BROADCAST_ADDRESS + END_OF_STR);
var cmdName = CmdName.CmdConnect; var cmdName = CmdName.CmdConnect;
var info = GetCmdInfo(cmdName) + ": " + BROADCAST_ADDRESS; var info = GetCmdInfo(cmdName) + ": " + BROADCAST_ADDRESS;
response = new CmdResponse(cmdName, info); response = new FM2014CmdResponse(cmdName, info);
PublishResponse(BroadcastFm2014, new ProcessExecEventArgs("", specificInfoObj: response)); PublishResponse(BroadcastFm2014, new ProcessExecEventArgs("", specificInfoObj: response));
SharedCmdTypeReminderLastCmd = CmdType.broadcast; SharedCmdTypeReminderLastCmd = CmdType.broadcast;
return true; return true;
} }
catch (Exception e) catch (Exception e)
{ {
response = new CmdResponse(CmdName.CmdAddress, response = new FM2014CmdResponse(CmdName.CmdAddress,
$"{Resources.StrCmdRespErrorBroadcast} - {e.Message}"); $"{Resources.StrCmdRespErrorBroadcast} - {e.Message}");
PublishResponse(BroadcastFm2014, new ProcessExecEventArgs(Resources.StrError, PublishResponse(BroadcastFm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, statusReturn: StatusReturn.Failed)); specificInfoObj: response, statusReturn: StatusReturn.Failed));
@ -1973,7 +1973,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
if (fm2014 == null) if (fm2014 == null)
return false; return false;
CmdResponse response; FM2014CmdResponse response;
try try
{ {
if (!SharedSerialPort.IsOpen) if (!SharedSerialPort.IsOpen)
@ -1981,7 +1981,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
SharedSerialPort.Open(); SharedSerialPort.Open();
if (!SharedSerialPort.IsOpen) if (!SharedSerialPort.IsOpen)
{ {
response = new CmdResponse(CmdName.CmdAddress, $"{Resources.StrCmdRespErrorIndividual}"); response = new FM2014CmdResponse(CmdName.CmdAddress, $"{Resources.StrCmdRespErrorIndividual}");
PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError, PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, specificInfoObj: response,
statusReturn: StatusReturn.Failed)); statusReturn: StatusReturn.Failed));
@ -2000,7 +2000,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{ {
var cmdName = CmdName.CmdConnect; var cmdName = CmdName.CmdConnect;
var info = GetCmdInfo(cmdName) + ": " + fm2014.AddressStr; var info = GetCmdInfo(cmdName) + ": " + fm2014.AddressStr;
response = new CmdResponse(cmdName, info); response = new FM2014CmdResponse(cmdName, info);
PublishResponse(fm2014, new ProcessExecEventArgs("", specificInfoObj: response)); PublishResponse(fm2014, new ProcessExecEventArgs("", specificInfoObj: response));
fm2014.ConnectResponse = fm2014.ConnectResponse.Replace(ADDR_RECORD_STR, ""); fm2014.ConnectResponse = fm2014.ConnectResponse.Replace(ADDR_RECORD_STR, "");
var fields = fm2014.ConnectResponse.Split('.'); var fields = fm2014.ConnectResponse.Split('.');
@ -2011,7 +2011,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
} }
catch (Exception e) catch (Exception e)
{ {
response = new CmdResponse(CmdName.CmdConnect, $"{Resources.StrCmdRespErrorIndividual}" + response = new FM2014CmdResponse(CmdName.CmdConnect, $"{Resources.StrCmdRespErrorIndividual}" +
$": {fm2014.AddressStr}" + $" - {e.Message}"); $": {fm2014.AddressStr}" + $" - {e.Message}");
PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError, PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, statusReturn: StatusReturn.Failed)); specificInfoObj: response, statusReturn: StatusReturn.Failed));
@ -2020,7 +2020,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
return false; return false;
} }
response = new CmdResponse(CmdName.CmdConnect, $"{Resources.StrCmdRespErrorIndividual}"); response = new FM2014CmdResponse(CmdName.CmdConnect, $"{Resources.StrCmdRespErrorIndividual}");
PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError, PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, statusReturn: StatusReturn.Failed)); specificInfoObj: response, statusReturn: StatusReturn.Failed));
SharedCmdTypeReminderLastCmd = CmdType.uninitialized; SharedCmdTypeReminderLastCmd = CmdType.uninitialized;
@ -2048,6 +2048,18 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
} }
} }
/// <summary>
/// Calculation of pulse ratio
/// </summary>
/// <param name="pulsesMeasured">counted pulses</param>
/// <param name="volumeMeasured_liters">measured volume in liters</param>
/// <returns></returns>
public static UInt32 CalculatePulsesPerCm(Int32 pulsesMeasured, Int32 volumeMeasured_liters)
{
// Convert measured volume from liters to cubic-meters
return (UInt32)(pulsesMeasured / (volumeMeasured_liters / 1000.0) + 0.5);
}
/// <summary> /// <summary>
/// Dispose FM2014 /// Dispose FM2014
/// </summary> /// </summary>
@ -2155,7 +2167,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
} }
catch (Exception e) catch (Exception e)
{ {
var response = new CmdResponse(cmdName, e.Message); var response = new FM2014CmdResponse(cmdName, e.Message);
PublishResponse(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response, PublishResponse(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
statusReturn: StatusReturn.Failed)); statusReturn: StatusReturn.Failed));
} }
@ -2226,7 +2238,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
cmdName = CmdName.CmdSetCurrentOutputAttenuation; cmdName = CmdName.CmdSetCurrentOutputAttenuation;
var info = GetCmdInfo(cmdName); var info = GetCmdInfo(cmdName);
CurrentOutputAttenuation = attenuation; CurrentOutputAttenuation = attenuation;
var response = new CmdResponse(cmdName, info, CurrentOutputAttenuation); var response = new FM2014CmdResponse(cmdName, info, CurrentOutputAttenuation);
ResponseEvent(this, response); ResponseEvent(this, response);
} }
} }
@ -2239,7 +2251,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
cmdName = CmdName.CmdRefToDutScale; cmdName = CmdName.CmdRefToDutScale;
var info = GetCmdInfo(cmdName); var info = GetCmdInfo(cmdName);
RefToDutScale_norm = value; RefToDutScale_norm = value;
var response = new CmdResponse(cmdName, info, doubleValue: RefToDutScale_norm); var response = new FM2014CmdResponse(cmdName, info, doubleValue: RefToDutScale_norm);
ResponseEvent(this, response); ResponseEvent(this, response);
} }
} }
@ -2252,14 +2264,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
cmdName = CmdName.CmdSetDutPulsesPerCm; cmdName = CmdName.CmdSetDutPulsesPerCm;
var info = GetCmdInfo(cmdName); var info = GetCmdInfo(cmdName);
DutPulses_per_cm = value; DutPulses_per_cm = value;
var response = new CmdResponse(cmdName, info, DutPulses_per_cm); var response = new FM2014CmdResponse(cmdName, info, DutPulses_per_cm);
ResponseEvent(this, response); ResponseEvent(this, response);
// Added 2026-Jan-01 to overcome limitation of max 9999 Impulses per volume // Added 2026-Jan-01 to overcome limitation of max 9999 Impulses per volume
cmdName = CmdName.CmdSetRefPulsesPerCm; cmdName = CmdName.CmdSetRefPulsesPerCm;
info = GetCmdInfo(cmdName); info = GetCmdInfo(cmdName);
// Calculate REF pulses per cubic meter // Calculate REF pulses per cubic meter
RefPulses_per_cm = (UInt32)Math.Round(DutPulses_per_cm * RefToDutScale_norm, 1); RefPulses_per_cm = (UInt32)Math.Round(DutPulses_per_cm * RefToDutScale_norm, 1);
response = new CmdResponse(cmdName, info, RefPulses_per_cm); response = new FM2014CmdResponse(cmdName, info, RefPulses_per_cm);
ResponseEvent(this, response); ResponseEvent(this, response);
} }
} }
@ -2268,7 +2280,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
} }
catch (Exception e) catch (Exception e)
{ {
var response = new CmdResponse(cmdName, e.Message); var response = new FM2014CmdResponse(cmdName, e.Message);
PublishResponse(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response, PublishResponse(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
statusReturn: StatusReturn.Failed)); statusReturn: StatusReturn.Failed));
return false; return false;
@ -2339,7 +2351,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
SharedSerialPort.Open(); SharedSerialPort.Open();
if (!SharedSerialPort.IsOpen) if (!SharedSerialPort.IsOpen)
{ {
var response = new CmdResponse(CmdName.CmdAddress, var response = new FM2014CmdResponse(CmdName.CmdAddress,
$"{Resources.StrCmdRespErrorBroadcast}"); $"{Resources.StrCmdRespErrorBroadcast}");
PublishResponse(this, new ProcessExecEventArgs(Resources.StrError, PublishResponse(this, new ProcessExecEventArgs(Resources.StrError,
specificInfoObj: response, specificInfoObj: response,
@ -2385,7 +2397,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
} }
catch (Exception e) catch (Exception e)
{ {
var response = new CmdResponse(cmdName, e.Message); var response = new FM2014CmdResponse(cmdName, e.Message);
PublishResponse(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response, PublishResponse(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response,
statusReturn: StatusReturn.Failed)); statusReturn: StatusReturn.Failed));
return false; return false;

View File

@ -73,77 +73,180 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
private const String UNICODE_DC1 = "\u0011"; private const String UNICODE_DC1 = "\u0011";
private const String UNICODE_DC2 = "\u0013"; private const String UNICODE_DC2 = "\u0013";
/// <summary>
/// Assembly of command information and functional code
/// </summary>
internal struct Cmd
{
/// <summary>
/// Enumerator of command name acronym
/// </summary>
internal readonly CmdName CmdName;
/// <summary>
/// Code being sent to FM2014 as string
/// </summary>
internal readonly String CmdCodeStr;
/// <summary>
/// Command type regarding the address (broadcast or individual) and answer
/// </summary>
internal readonly CmdType CmdType;
/// <summary>
/// Command parameter style for raw data conversion from response string or for transmission
/// </summary>
internal readonly CmdParaFormat ParamFormat;
/// <summary>
/// Multilingual command information string
/// </summary>
internal readonly String CmdInfoStr;
/// <summary>
/// Single command table entry to combine the name with the code and the communication type
/// </summary>
/// <param name="cmdName">Enumerator of command name acronym</param>
/// <param name="cmdCodeStr">Code being sent to FM2014 as string</param>
/// <param name="cmdType">Command type regarding the address (broadcast or individual) and answer</param>
/// <param name="paraFormat">parameter format for conversion</param>
/// <param name="cmdInfoStr">Multilingual command information string</param>
internal Cmd(CmdName cmdName, String cmdCodeStr, CmdType cmdType, CmdParaFormat paraFormat,
String cmdInfoStr)
{
CmdName = cmdName;
CmdCodeStr = cmdCodeStr;
CmdType = cmdType;
ParamFormat = paraFormat;
CmdInfoStr = cmdInfoStr;
}
}
/// <summary>
/// Combine constant string returns from FM2014 firmware to multilingual information
/// </summary>
internal struct CmdErrorMsg
{
/// <summary>
/// Hard coded return string from FM2014
/// </summary>
internal String DeviceErrorReturnStr;
/// <summary>
/// Multilingual forwarded string to GUI
/// </summary>
internal String MultilingualForwardStr;
/// <summary>
/// Ctor
/// </summary>
/// <param name="fixedFwErrorStr"></param>
/// <param name="multilingualFeedback"></param>
internal CmdErrorMsg(String fixedFwErrorStr, String multilingualFeedback)
{
DeviceErrorReturnStr = fixedFwErrorStr;
MultilingualForwardStr = multilingualFeedback;
}
}
/// <summary>
/// Assembly of device status and multilingual information
/// </summary>
internal struct ConnectStatus
{
/// <summary>
/// Enumerator of command name acronym
/// </summary>
internal readonly ConnectStatusName Name;
/// <summary>
/// Multilingual information
/// </summary>
internal readonly String InfoStr;
/// <summary>
/// Single command table entry to combine the name with the code and the communication type
/// </summary>
/// <param name="deviceConnectStatusName"></param>
/// <param name="statusInfoStr">Multilingual command information string</param>
internal ConnectStatus(ConnectStatusName deviceConnectStatusName, String statusInfoStr)
{
Name = deviceConnectStatusName;
InfoStr = statusInfoStr;
}
}
/// <summary> /// <summary>
/// Command table for communication with FM2014 combines the name with the command code and additional info. /// Command table for communication with FM2014 combines the name with the command code and additional info.
/// </summary> /// </summary>
public static readonly List<Cmd> CmdTbl = new List<Cmd> internal static readonly List<Cmd> CmdTbl = new List<Cmd>
{ {
// FM2014 control and information // FM2014 control and information
new Cmd(CmdName.CmdConnect, "", CmdType.individual, ParaFormat.parameterless, Resources.StrCmdMsgConnect), new Cmd(CmdName.CmdConnect, "", CmdType.individual, CmdParaFormat.parameterless, Resources.StrCmdMsgConnect),
new Cmd(CmdName.CmdUnlockErase, "&U@", CmdType.individual, ParaFormat.parameterless, Resources.StrCmdMsgUnlock), new Cmd(CmdName.CmdUnlockErase, "&U@", CmdType.individual, CmdParaFormat.parameterless, Resources.StrCmdMsgUnlock),
new Cmd(CmdName.CmdEraseSerialNumber, "&E@", CmdType.individual, ParaFormat.parameterless, Resources.StrCmdMsgErase), new Cmd(CmdName.CmdEraseSerialNumber, "&E@", CmdType.individual, CmdParaFormat.parameterless, Resources.StrCmdMsgErase),
new Cmd(CmdName.CmdReadEepMeasSetup, "%D@", CmdType.individual, ParaFormat.parameterless, Resources.StrCmdMsgReadDefMeasSetup), new Cmd(CmdName.CmdReadEepMeasSetup, "%D@", CmdType.individual, CmdParaFormat.parameterless, Resources.StrCmdMsgReadDefMeasSetup),
new Cmd(CmdName.CmdReadRamMeasSetup, "%A@", CmdType.individual, ParaFormat.parameterless, Resources.StrCmdMsgActualDefMeasSetup), new Cmd(CmdName.CmdReadRamMeasSetup, "%A@", CmdType.individual, CmdParaFormat.parameterless, Resources.StrCmdMsgActualDefMeasSetup),
new Cmd(CmdName.CmdSaveRamToEepMeasSetup, "%S@", CmdType.individual, ParaFormat.parameterless, Resources.StrCmdMsgStoreDefMeasSetup), new Cmd(CmdName.CmdSaveRamToEepMeasSetup, "%S@", CmdType.individual, CmdParaFormat.parameterless, Resources.StrCmdMsgStoreDefMeasSetup),
new Cmd(CmdName.CmdSwitchToBootMode, "$B@", CmdType.individual, ParaFormat.parameterless, Resources.StrCmdMsgBootMode), new Cmd(CmdName.CmdSwitchToBootMode, "$B@", CmdType.individual, CmdParaFormat.parameterless, Resources.StrCmdMsgBootMode),
new Cmd(CmdName.CmdPrintAppInfo, "?A", CmdType.individual, ParaFormat.parameterless, Resources.StrCmdMsgPrintAppInfo), new Cmd(CmdName.CmdPrintAppInfo, "?A", CmdType.individual, CmdParaFormat.parameterless, Resources.StrCmdMsgPrintAppInfo),
new Cmd(CmdName.CmdPrintBootInfo, "?B", CmdType.individual, ParaFormat.parameterless, Resources.StrCmdMsgPrintBootInfo), new Cmd(CmdName.CmdPrintBootInfo, "?B", CmdType.individual, CmdParaFormat.parameterless, Resources.StrCmdMsgPrintBootInfo),
new Cmd(CmdName.CmdBaudratePc, "=BP", CmdType.individual, ParaFormat.uintDecimalValue, Resources.StrCmdMsgBaudrateAll), new Cmd(CmdName.CmdBaudratePc, "=BP", CmdType.individual, CmdParaFormat.uintDecimalValue, Resources.StrCmdMsgBaudrateAll),
new Cmd(CmdName.CmdGetVirtualAddress, " ", CmdType.individual, ParaFormat.uintHexValue, Resources.StrCmdMsgGetVirtualAddress), new Cmd(CmdName.CmdGetVirtualAddress, " ", CmdType.individual, CmdParaFormat.uintHexValue, Resources.StrCmdMsgGetVirtualAddress),
new Cmd(CmdName.CmdSerialNumber, "#", CmdType.individual, ParaFormat.uintDecimalValue, Resources.StrCmdMsgSerial), new Cmd(CmdName.CmdSerialNumber, "#", CmdType.individual, CmdParaFormat.uintDecimalValue, Resources.StrCmdMsgSerial),
new Cmd(CmdName.CmdGetStatus, UNICODE_ACK, CmdType.individual, ParaFormat.uintHexValue, Resources.StrCmdMsgGetStatus), new Cmd(CmdName.CmdGetStatus, UNICODE_ACK, CmdType.individual, CmdParaFormat.uintHexValue, Resources.StrCmdMsgGetStatus),
new Cmd(CmdName.CmdSetVirtualAddress, "N", CmdType.individual, ParaFormat.uintHexValue, Resources.StrCmdMsgSetVirtualAddress), new Cmd(CmdName.CmdSetVirtualAddress, "N", CmdType.individual, CmdParaFormat.uintHexValue, Resources.StrCmdMsgSetVirtualAddress),
new Cmd(CmdName.CmdGetLifetime, "!", CmdType.individual, ParaFormat.stringValue, Resources.StrCmdMsgPrintStatistics), new Cmd(CmdName.CmdGetLifetime, "!", CmdType.individual, CmdParaFormat.stringValue, Resources.StrCmdMsgPrintStatistics),
//measurement reset //measurement reset
new Cmd(CmdName.CmdResetMeasurement, "R", CmdType.broadcast, ParaFormat.parameterless, Resources.StrCmdMsgRstMeas), new Cmd(CmdName.CmdResetMeasurement, "R", CmdType.broadcast, CmdParaFormat.parameterless, Resources.StrCmdMsgRstMeas),
// Double impulse detection settings // Double impulse detection settings
new Cmd(CmdName.CmdSetDoublePulseDeadtimeBase, "S", CmdType.broadcast, ParaFormat.uintDecimalValue, Resources.StrCmdMsgDplsLockTmr), new Cmd(CmdName.CmdSetDoublePulseDeadtimeBase, "S", CmdType.broadcast, CmdParaFormat.uintDecimalValue, Resources.StrCmdMsgDplsLockTmr),
new Cmd(CmdName.CmdSetDoublePulseDeadtimeMultiplier, "s", CmdType.broadcast, ParaFormat.uintDecimalValue, Resources.StrCmdMsgDplsLockMult), new Cmd(CmdName.CmdSetDoublePulseDeadtimeMultiplier, "s", CmdType.broadcast, CmdParaFormat.uintDecimalValue, Resources.StrCmdMsgDplsLockMult),
new Cmd(CmdName.CmdSwitchDoublePulseDeadtimeOff, "G", CmdType.broadcast, ParaFormat.parameterless, Resources.StrCmdMsgSetDbplUnlock), new Cmd(CmdName.CmdSwitchDoublePulseDeadtimeOff, "G", CmdType.broadcast, CmdParaFormat.parameterless, Resources.StrCmdMsgSetDbplUnlock),
// Main measurement pulse settings and readout // Main measurement pulse settings and readout
new Cmd(CmdName.CmdSetDutPulsesRequired, "H", CmdType.broadcast, ParaFormat.uintHexValue, Resources.StrCmdMsgDutSetPls), new Cmd(CmdName.CmdSetDutPulsesRequired, "H", CmdType.broadcast, CmdParaFormat.uintHexValue, Resources.StrCmdMsgDutSetPls),
new Cmd(CmdName.CmdSetRefPulsesRequired, "M", CmdType.broadcast, ParaFormat.uintHexValue, Resources.StrCmdMsgRefSetPls), new Cmd(CmdName.CmdSetRefPulsesRequired, "M", CmdType.broadcast, CmdParaFormat.uintHexValue, Resources.StrCmdMsgRefSetPls),
new Cmd(CmdName.CmdGetDutPulsesRemaining, "I", CmdType.individual, ParaFormat.uintHexValue, Resources.StrCmdMsgDutGetPlsRmn), new Cmd(CmdName.CmdGetDutPulsesRemaining, "I", CmdType.individual, CmdParaFormat.uintHexValue, Resources.StrCmdMsgDutGetPlsRmn),
new Cmd(CmdName.CmdGetRefPulsesRemaining, "J", CmdType.individual, ParaFormat.uintHexValue, Resources.StrCmdMsgRefGetPlsRmn), new Cmd(CmdName.CmdGetRefPulsesRemaining, "J", CmdType.individual, CmdParaFormat.uintHexValue, Resources.StrCmdMsgRefGetPlsRmn),
// Pulse counters start, readout and backup // Pulse counters start, readout and backup
new Cmd(CmdName.CmdStartRefPulseCounter, "O", CmdType.broadcast, ParaFormat.parameterless, Resources.StrCmdMsgRefStrPlsCtr), new Cmd(CmdName.CmdStartRefPulseCounter, "O", CmdType.broadcast, CmdParaFormat.parameterless, Resources.StrCmdMsgRefStrPlsCtr),
new Cmd(CmdName.CmdStartDutPulseCounter, "Q", CmdType.broadcast, ParaFormat.parameterless, Resources.StrCmdMsgDutStrPlsCtr), new Cmd(CmdName.CmdStartDutPulseCounter, "Q", CmdType.broadcast, CmdParaFormat.parameterless, Resources.StrCmdMsgDutStrPlsCtr),
new Cmd(CmdName.CmdGetRefPulsesCounted, "L", CmdType.individual, ParaFormat.uintHexValue, Resources.StrCmdMsgRefGetPlsCtr), new Cmd(CmdName.CmdGetRefPulsesCounted, "L", CmdType.individual, CmdParaFormat.uintHexValue, Resources.StrCmdMsgRefGetPlsCtr),
new Cmd(CmdName.CmdGetDutPulsesCounted, "U", CmdType.individual, ParaFormat.uintHexValue, Resources.StrCmdMsgDutGetPlsCtr), new Cmd(CmdName.CmdGetDutPulsesCounted, "U", CmdType.individual, CmdParaFormat.uintHexValue, Resources.StrCmdMsgDutGetPlsCtr),
new Cmd(CmdName.CmdGetRefPulsesCountedBackup, "l", CmdType.individual, ParaFormat.uintHexValue, Resources.StrCmdMsgRefGetPlsCtrBu), new Cmd(CmdName.CmdGetRefPulsesCountedBackup, "l", CmdType.individual, CmdParaFormat.uintHexValue, Resources.StrCmdMsgRefGetPlsCtrBu),
new Cmd(CmdName.CmdGetDutPulsesCountedBackup, "u", CmdType.individual, ParaFormat.uintHexValue, Resources.StrCmdMsgDutGetPlsCtrBu), new Cmd(CmdName.CmdGetDutPulsesCountedBackup, "u", CmdType.individual, CmdParaFormat.uintHexValue, Resources.StrCmdMsgDutGetPlsCtrBu),
new Cmd(CmdName.CmdStartRefAndDutPulseCounter, "o", CmdType.broadcast, ParaFormat.parameterless, Resources.StrCmdMsgRefDutStrPlsCtr), new Cmd(CmdName.CmdStartRefAndDutPulseCounter, "o", CmdType.broadcast, CmdParaFormat.parameterless, Resources.StrCmdMsgRefDutStrPlsCtr),
new Cmd(CmdName.CmdBackupRefAndDutPulses, "q", CmdType.broadcast, ParaFormat.parameterless, Resources.StrCmdMsgRefDutBuPlsCtr), new Cmd(CmdName.CmdBackupRefAndDutPulses, "q", CmdType.broadcast, CmdParaFormat.parameterless, Resources.StrCmdMsgRefDutBuPlsCtr),
// General measurement setup // General measurement setup
new Cmd(CmdName.CmdRefToDutScale, "K", CmdType.broadcast, ParaFormat.individual, Resources.StrCmdMsgRefSetScale), new Cmd(CmdName.CmdRefToDutScale, "K", CmdType.broadcast, CmdParaFormat.individual, Resources.StrCmdMsgRefSetScale),
new Cmd(CmdName.CmdSetDutPulsesPerCm, "V", CmdType.individualUnresponsive, ParaFormat.uintDecimalValue, Resources.StrCmdMsgDutLppScale), new Cmd(CmdName.CmdSetDutPulsesPerCm, "V", CmdType.individualUnresponsive, CmdParaFormat.uintDecimalValue, Resources.StrCmdMsgDutLppScale),
new Cmd(CmdName.CmdSetRefPulsesPerCm, "W", CmdType.individualUnresponsive, ParaFormat.uintDecimalValue, Resources.StrCmdMsgRefLppScale), new Cmd(CmdName.CmdSetRefPulsesPerCm, "W", CmdType.individualUnresponsive, CmdParaFormat.uintDecimalValue, Resources.StrCmdMsgRefLppScale),
new Cmd(CmdName.CmdSetCurrentOutputAttenuation, "T", CmdType.broadcast, ParaFormat.uintDecimalValue, Resources.StrCmdMsgMeasSetAttn), new Cmd(CmdName.CmdSetCurrentOutputAttenuation, "T", CmdType.broadcast, CmdParaFormat.uintDecimalValue, Resources.StrCmdMsgMeasSetAttn),
// Measurement result request // Measurement result request
new Cmd(CmdName.CmdGetDutTimerTicks, "X", CmdType.individual, ParaFormat.uintHexValue, Resources.StrCmdMsgDutGetTmr), new Cmd(CmdName.CmdGetDutTimerTicks, "X", CmdType.individual, CmdParaFormat.uintHexValue, Resources.StrCmdMsgDutGetTmr),
new Cmd(CmdName.CmdGetRefTimerTicks, "Y", CmdType.individual, ParaFormat.uintHexValue, Resources.StrCmdMsgRefGetTmr), new Cmd(CmdName.CmdGetRefTimerTicks, "Y", CmdType.individual, CmdParaFormat.uintHexValue, Resources.StrCmdMsgRefGetTmr),
new Cmd(CmdName.CmdCalculatedDutToRefTolerance, "", CmdType.individual, ParaFormat.doubleValue, Resources.StrCalDutToRefTol), new Cmd(CmdName.CmdCalculatedDutToRefTolerance, "", CmdType.individual, CmdParaFormat.doubleValue, Resources.StrCalDutToRefTol),
new Cmd(CmdName.CmdGetDutToRefToleranceUndamped, "Z", CmdType.individual, ParaFormat.byteHexValuePlusSign, Resources.StrCmdMsgGetUdtlc), new Cmd(CmdName.CmdGetDutToRefToleranceUndamped, "Z", CmdType.individual, CmdParaFormat.byteHexValuePlusSign, Resources.StrCmdMsgGetUdtlc),
new Cmd(CmdName.CmdGetDutToRefToleranceDamped, "z", CmdType.individual, ParaFormat.byteHexValuePlusSign, Resources.StrCmdMsgGetDtlc), new Cmd(CmdName.CmdGetDutToRefToleranceDamped, "z", CmdType.individual, CmdParaFormat.byteHexValuePlusSign, Resources.StrCmdMsgGetDtlc),
new Cmd(CmdName.CmdGetRefPeriod, UNICODE_DLE, CmdType.individual, ParaFormat.uintHexValue, Resources.StrCmdMsgGetRefPeriod), new Cmd(CmdName.CmdGetRefPeriod, UNICODE_DLE, CmdType.individual, CmdParaFormat.uintHexValue, Resources.StrCmdMsgGetRefPeriod),
new Cmd(CmdName.CmdGetDutPeriod, UNICODE_DC1, CmdType.individual,ParaFormat.uintHexValue, Resources.StrCmdMsgGetDutPeriod), new Cmd(CmdName.CmdGetDutPeriod, UNICODE_DC1, CmdType.individual,CmdParaFormat.uintHexValue, Resources.StrCmdMsgGetDutPeriod),
new Cmd(CmdName.CmdGetRefFrequency, UNICODE_DC2, CmdType.individual, ParaFormat.uintHexValue, Resources.StrCmdMsgGetRefFrequ), new Cmd(CmdName.CmdGetRefFrequency, UNICODE_DC2, CmdType.individual, CmdParaFormat.uintHexValue, Resources.StrCmdMsgGetRefFrequ),
new Cmd(CmdName.CmdCalculatedFrequRefPeriod, "", CmdType.individual, ParaFormat.doubleValue, Resources.StrCalRefFrequFromRefPeriod), new Cmd(CmdName.CmdCalculatedFrequRefPeriod, "", CmdType.individual, CmdParaFormat.doubleValue, Resources.StrCalRefFrequFromRefPeriod),
new Cmd(CmdName.CmdCalculatedFrequencyDutPeriod, "", CmdType.individual, ParaFormat.doubleValue, Resources.StrCalDutFrequFromDutPeriod), new Cmd(CmdName.CmdCalculatedFrequencyDutPeriod, "", CmdType.individual, CmdParaFormat.doubleValue, Resources.StrCalDutFrequFromDutPeriod),
new Cmd(CmdName.CmdCalculatedFlowRefFrequ, "", CmdType.individual, ParaFormat.doubleValue, Resources.StrCalRefFlowRateFromRefFrequ), new Cmd(CmdName.CmdCalculatedFlowRefFrequ, "", CmdType.individual, CmdParaFormat.doubleValue, Resources.StrCalRefFlowRateFromRefFrequ),
new Cmd(CmdName.CmdCalculatedFlowRefPeriod, "", CmdType.individual, ParaFormat.doubleValue, Resources.StrCalRefFlowRateFromRefPeriod), new Cmd(CmdName.CmdCalculatedFlowRefPeriod, "", CmdType.individual, CmdParaFormat.doubleValue, Resources.StrCalRefFlowRateFromRefPeriod),
new Cmd(CmdName.CmdCalculatedFlowDutPeriod, "", CmdType.individual, ParaFormat.doubleValue, Resources.StrCalDutFlowRateFromDutPeriod), new Cmd(CmdName.CmdCalculatedFlowDutPeriod, "", CmdType.individual, CmdParaFormat.doubleValue, Resources.StrCalDutFlowRateFromDutPeriod),
// Adjustment, place here Ids which may represent digital numbers from A to F // Adjustment, place here Ids which may represent digital numbers from A to F
new Cmd(CmdName.CmdStartCurrentOffsetCorrection, "C", CmdType.broadcast, ParaFormat.parameterless, Resources.StrCmdMsgCorCurrent), new Cmd(CmdName.CmdStartCurrentOffsetCorrection, "C", CmdType.broadcast, CmdParaFormat.parameterless, Resources.StrCmdMsgCorCurrent),
new Cmd(CmdName.CmdCurrentOutputMax, "+", CmdType.broadcast, ParaFormat.parameterless, Resources.StrCmdMsgSetXtrMax), new Cmd(CmdName.CmdCurrentOutputMax, "+", CmdType.broadcast, CmdParaFormat.parameterless, Resources.StrCmdMsgSetXtrMax),
new Cmd(CmdName.CmdCurrentOutputMin, "-", CmdType.broadcast, ParaFormat.parameterless, Resources.StrCmdMsgSetXtrMin), new Cmd(CmdName.CmdCurrentOutputMin, "-", CmdType.broadcast, CmdParaFormat.parameterless, Resources.StrCmdMsgSetXtrMin),
new Cmd(CmdName.CmdAddress, "A", CmdType.individual, ParaFormat.uintDecimalValue, Resources.StrCmdMsgAddrPc) new Cmd(CmdName.CmdAddress, "A", CmdType.individual, CmdParaFormat.uintDecimalValue, Resources.StrCmdMsgAddrPc)
}; };
/// <summary> /// <summary>
/// Table of error codes received from FM2014 /// Table of error codes received from FM2014
/// </summary> /// </summary>
public static readonly CmdErrorMsg[] CmdErrorCodes = internal static readonly CmdErrorMsg[] CmdErrorCodes =
{ {
new CmdErrorMsg ("Parameter is out of range!", Resources.StrCmdRespErrorParamOutOfRange), new CmdErrorMsg ("Parameter is out of range!", Resources.StrCmdRespErrorParamOutOfRange),
new CmdErrorMsg ("Command not supported!", Resources.StrCmdRespErrorCmdNotSupported), new CmdErrorMsg ("Command not supported!", Resources.StrCmdRespErrorCmdNotSupported),
@ -156,13 +259,13 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// <summary> /// <summary>
/// Translate the device connection status to multilingual answer /// Translate the device connection status to multilingual answer
/// </summary> /// </summary>
public static readonly ConnectionStatus[] DeviceConnectionStati = internal static readonly ConnectStatus[] DeviceConnectionStati =
{ {
new ConnectionStatus(ConnectionStatusName.unknown, "?"), new ConnectStatus(ConnectStatusName.unknown, "?"),
new ConnectionStatus(ConnectionStatusName.offline, "OFFLINE"), new ConnectStatus(ConnectStatusName.offline, "OFFLINE"),
new ConnectionStatus(ConnectionStatusName.standby, "STANDBY"), new ConnectStatus(ConnectStatusName.standby, "STANDBY"),
new ConnectionStatus(ConnectionStatusName.online, "ONLINE"), new ConnectStatus(ConnectStatusName.online, "ONLINE"),
new ConnectionStatus(ConnectionStatusName.error, Resources.StrError) new ConnectStatus(ConnectStatusName.error, Resources.StrError)
}; };
/// <summary> /// <summary>
@ -188,7 +291,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// <remarks date="2026-Feb-03" author="Thomas Wiedebusch"> /// <remarks date="2026-Feb-03" author="Thomas Wiedebusch">
/// - Initial. /// - Initial.
/// </remarks> /// </remarks>
public static String GetConnectionStatusInfo(ConnectionStatusName name) public static String GetConnectionStatusInfo(ConnectStatusName name)
{ {
return (from ct in DeviceConnectionStati return (from ct in DeviceConnectionStati
where ct.Name == name where ct.Name == name
@ -233,7 +336,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
/// <remarks date="2026-Feb-13" author="Thomas Wiedebusch"> /// <remarks date="2026-Feb-13" author="Thomas Wiedebusch">
/// - Initial. /// - Initial.
/// </remarks> /// </remarks>
public static ParaFormat GetParaFormat(CmdName cmdName) public static CmdParaFormat GetParaFormat(CmdName cmdName)
{ {
return (from ct in CmdTbl return (from ct in CmdTbl
where ct.CmdName == cmdName where ct.CmdName == cmdName

View File

@ -11,13 +11,14 @@
*********************************************************************************************************************/ *********************************************************************************************************************/
using System; using System;
using Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Consts;
namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Consts namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core
{ {
/// <summary> /// <summary>
/// Command response from FM2014 /// Command response from FM2014
/// </summary> /// </summary>
public struct CmdResponse public struct FM2014CmdResponse
{ {
/// <summary> /// <summary>
/// UTC timestamp /// UTC timestamp
@ -57,7 +58,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co
/// <param name="intValue"></param> /// <param name="intValue"></param>
/// <param name="doubleValue"></param> /// <param name="doubleValue"></param>
/// <param name="unit"></param> /// <param name="unit"></param>
public CmdResponse(CmdName cmdName, String answerStr, UInt32? intValue = null, Double? doubleValue = null, public FM2014CmdResponse(CmdName cmdName, String answerStr, UInt32? intValue = null, Double? doubleValue = null,
String unit = "") String unit = "")
{ {
TimestampUtc = DateTime.UtcNow; TimestampUtc = DateTime.UtcNow;

View File

@ -282,15 +282,16 @@
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ConnectionStatus.cs" />
<Compile Include="Consts\Cmd.cs" />
<Compile Include="Consts\CmdErrorMsg.cs" />
<Compile Include="Consts\CmdName.cs" /> <Compile Include="Consts\CmdName.cs" />
<Compile Include="Consts\CmdResponse.cs" /> <Compile Include="FM2014CmdResponse.cs" />
<Compile Include="Consts\CmdType.cs" /> <Compile Include="Consts\CmdType.cs" />
<Compile Include="Consts\ConnectionStatusName.cs" /> <Compile Include="Consts\ConnectStatusName.cs" />
<Compile Include="Consts\CyclicMeasSequ.cs" /> <Compile Include="Consts\CyclicMeasSequ.cs" />
<Compile Include="Consts\ParaFormat.cs" /> <Compile Include="Consts\CmdParaFormat.cs" />
<Compile Include="Consts\ErrorStatus.cs" />
<Compile Include="Consts\Fault1Status.cs" />
<Compile Include="Consts\Fault2Status.cs" />
<Compile Include="Consts\MeasureStatus.cs" />
<Compile Include="FM2014CmdDef.cs" /> <Compile Include="FM2014CmdDef.cs" />
<Compile Include="FM2014.cs" /> <Compile Include="FM2014.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />

@ -1 +1 @@
Subproject commit 1cd877ff8628cdcb94a7ad2751d61dc77e1211da Subproject commit 653498d43dcca1234a8fba95959ca02bb2c3f480

View File

@ -34,7 +34,7 @@
<appSettings> <appSettings>
<add key="Mode" value="1" /> <!--1 = EOL ; 2 = Picking--> <add key="Mode" value="2" /> <!--1 = EOL ; 2 = Picking-->
</appSettings> </appSettings>

View File

@ -748,10 +748,10 @@ namespace Sensus.Ui.FM2014TestBench
if (!slotShallBeUsed || !fm2014.Connect(comPort)) if (!slotShallBeUsed || !fm2014.Connect(comPort))
{ {
ucFm2014Device.SetConnectionStatus(ConnectionStatusName.offline); ucFm2014Device.SetConnectionStatus(ConnectStatusName.offline);
continue; continue;
} }
ucFm2014Device.SetConnectionStatus(ConnectionStatusName.online); ucFm2014Device.SetConnectionStatus(ConnectStatusName.online);
if (!fm2014.IsLoggedOn) if (!fm2014.IsLoggedOn)
{ {
@ -1192,7 +1192,7 @@ namespace Sensus.Ui.FM2014TestBench
SetTimeDisplay(); SetTimeDisplay();
// Data dispatcher // Data dispatcher
var resp = (CmdResponse)e.SpecificInfoObj; var resp = (FM2014CmdResponse)e.SpecificInfoObj;
String valueStr; String valueStr;
// Used as marker for error, null means measurement is in range or status or health is good // Used as marker for error, null means measurement is in range or status or health is good
@ -2119,7 +2119,7 @@ namespace Sensus.Ui.FM2014TestBench
/// </summary> /// </summary>
/// <param name="sender"></param> /// <param name="sender"></param>
/// <param name="e"></param> /// <param name="e"></param>
/// <remarks date="2026-Feb-09..Feb-18" author="Thomas Wiedebusch"> /// <remarks date="2026-Feb-09..Feb-24" author="Thomas Wiedebusch">
/// - Initial. /// - Initial.
/// </remarks> /// </remarks>
private void tbxDutToRefToleranceMax_LostFocus(Object sender, EventArgs e) private void tbxDutToRefToleranceMax_LostFocus(Object sender, EventArgs e)
@ -2139,7 +2139,7 @@ namespace Sensus.Ui.FM2014TestBench
var englishNumberFormat = tbxDutToRefTolMax.Text.Replace(',', '.'); var englishNumberFormat = tbxDutToRefTolMax.Text.Replace(',', '.');
if (double.TryParse(englishNumberFormat, NumberStyles.Any, new CultureInfo("en"), out var tolerance)) if (double.TryParse(englishNumberFormat, NumberStyles.Any, new CultureInfo("en"), out var tolerance))
{ {
if (Math.Abs(backupDutToRefTolerance - tolerance) > 0.05) if (Math.Abs(backupDutToRefTolerance - tolerance) > 0.01)
{ {
_programConfigurationSetupHasChanged = true; _programConfigurationSetupHasChanged = true;
// Preset to fill the configuration file storage ability // Preset to fill the configuration file storage ability
@ -2205,7 +2205,7 @@ namespace Sensus.Ui.FM2014TestBench
/// </summary> /// </summary>
/// <param name="sender"></param> /// <param name="sender"></param>
/// <param name="e"></param> /// <param name="e"></param>
/// <remarks date="2026-Feb-09..Feb-18" author="Thomas Wiedebusch"> /// <remarks date="2026-Feb-09..Feb-24" author="Thomas Wiedebusch">
/// - Initial. /// - Initial.
/// </remarks> /// </remarks>
private void tbxDutToRefToleranceMin_LostFocus(Object sender, EventArgs e) private void tbxDutToRefToleranceMin_LostFocus(Object sender, EventArgs e)
@ -2225,7 +2225,7 @@ namespace Sensus.Ui.FM2014TestBench
var englishNumberFormat = tbxDutToRefTolMin.Text.Replace(',', '.'); var englishNumberFormat = tbxDutToRefTolMin.Text.Replace(',', '.');
if (double.TryParse(englishNumberFormat, NumberStyles.Any, new CultureInfo("en"), out var tolerance)) if (double.TryParse(englishNumberFormat, NumberStyles.Any, new CultureInfo("en"), out var tolerance))
{ {
if (Math.Abs(backupDutToRefTolerance - tolerance) > 0.05) if (Math.Abs(backupDutToRefTolerance - tolerance) > 0.01)
{ {
_programConfigurationSetupHasChanged = true; _programConfigurationSetupHasChanged = true;
// Preset to fill the configuration file storage ability // Preset to fill the configuration file storage ability
@ -2469,7 +2469,7 @@ namespace Sensus.Ui.FM2014TestBench
/// </summary> /// </summary>
/// <param name="sender"></param> /// <param name="sender"></param>
/// <param name="e"></param> /// <param name="e"></param>
/// <remarks date="2026-Feb-09..18" author="Thomas Wiedebusch"> /// <remarks date="2026-Feb-09..24" author="Thomas Wiedebusch">
/// - Initial. /// - Initial.
/// </remarks> /// </remarks>
private void tbxVolumeMeasuredLiters_LostFocus(Object sender, EventArgs e) private void tbxVolumeMeasuredLiters_LostFocus(Object sender, EventArgs e)
@ -2490,8 +2490,8 @@ namespace Sensus.Ui.FM2014TestBench
int.TryParse(tbxVolumeMeasuredLiters.Text, out var liters) && liters > 0) int.TryParse(tbxVolumeMeasuredLiters.Text, out var liters) && liters > 0)
{ {
// Calculate the new pulse ratio based on the manual calibration // Calculate the new pulse ratio based on the manual calibration
var pulses_per_cm = (UInt32)(pulses / (liters / 1000.0) + 0.5); var pulses_per_cm = FM2014.CalculatePulsesPerCm(pulses, liters);
// Try to set the new calculated REF pulses limited by the property setter // Try to set the new calculated REF pulses limited by the property setter
_fm2014Common.RefPulses_per_cm = pulses_per_cm; _fm2014Common.RefPulses_per_cm = pulses_per_cm;

View File

@ -81,7 +81,7 @@ namespace Sensus.Ui.Fm2014TestBench.UserControls
///<inheritdoc/> ///<inheritdoc/>
public void Clear() public void Clear()
{ {
SetConnectionStatus(ConnectionStatusName.offline); SetConnectionStatus(ConnectStatusName.offline);
HideMeasurements(); HideMeasurements();
} }
@ -125,7 +125,7 @@ namespace Sensus.Ui.Fm2014TestBench.UserControls
private void HideMeasurements() private void HideMeasurements()
{ {
UiElmEnable(brdFM2014Pic, true); UiElmEnable(brdFM2014Pic, true);
UiElmEnable(picFM2014, lblStatus.Content.Equals(ConnectionStatusName.online)); UiElmEnable(picFM2014, lblStatus.Content.Equals(ConnectStatusName.online));
// Hide all measurement borders // Hide all measurement borders
foreach (var borders in MeasurementBorders) foreach (var borders in MeasurementBorders)
@ -156,7 +156,7 @@ namespace Sensus.Ui.Fm2014TestBench.UserControls
} }
// Check if device is connected and logged in // Check if device is connected and logged in
if (!lblStatus.Content.Equals(GetConnectionStatusInfo(ConnectionStatusName.online))) if (!lblStatus.Content.Equals(GetConnectionStatusInfo(ConnectStatusName.online)))
return false; return false;
if (numberOfMeasurementFields == 0) if (numberOfMeasurementFields == 0)
@ -187,16 +187,16 @@ namespace Sensus.Ui.Fm2014TestBench.UserControls
/// <remarks date="2026-Feb-03" author="Thomas Wiedebusch"> /// <remarks date="2026-Feb-03" author="Thomas Wiedebusch">
/// - Initial. /// - Initial.
/// </remarks> /// </remarks>
public void SetConnectionStatus(ConnectionStatusName statusName) public void SetConnectionStatus(ConnectStatusName statusName)
{ {
var connectionStatusStr = GetConnectionStatusInfo(statusName); var connectionStatusStr = GetConnectionStatusInfo(statusName);
Dispatcher.Invoke(DispatcherPriority.Send, Dispatcher.Invoke(DispatcherPriority.Send,
new Action(() => new Action(() =>
{ {
lblStatus.Content = connectionStatusStr; lblStatus.Content = connectionStatusStr;
var isEnabled = statusName.Equals(ConnectionStatusName.online); var isEnabled = statusName.Equals(ConnectStatusName.online);
UiElmEnable(picFM2014, isEnabled); UiElmEnable(picFM2014, isEnabled);
if (!statusName.Equals(ConnectionStatusName.online)) if (!statusName.Equals(ConnectStatusName.online))
{ {
HideMeasurements(); HideMeasurements();
lblStatus.Background = ColorStandardDisplayField; lblStatus.Background = ColorStandardDisplayField;