This commit is contained in:
Stoyan Zlatev 2026-02-16 09:33:30 +01:00
commit d7bdaa1a18
9 changed files with 534 additions and 386 deletions

View File

@ -13,4 +13,4 @@ using System.Reflection;
//
//[assembly: AssemblyVersion("1.2.*.0")]
[assembly: AssemblyVersion("2.8.17.*")]
[assembly: AssemblyVersion("2.8.18.*")]

View File

@ -28,6 +28,11 @@
/// <summary>
/// inspection needed as warning returns
/// </summary>
Warning
Warning,
/// <summary>
/// the measurement is out of range
/// </summary>
MeasurementOutOfRange
}
}

View File

@ -77,6 +77,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
public GenesisMeter()
{
SetLogger();
CheckRebootCtr = true;
}
//initially do not signal event
@ -100,6 +101,8 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
{
Logger = NLogHelper.CreateOrGetMultiLogger($"Slot:{slot}", "Slot", "Meter", "MeterBase", "MeterBase");
SetupGenesisMeter(slot, requestPort, streamingPort, ignoreCorruptedData, password);
CheckRebootCtr = true;
}
#endregion
@ -195,6 +198,13 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
get; protected set;
}
/// <inheritdoc />
public Boolean CheckRebootCtr
{
get;
set;
}
/// <inheritdoc />
public List<MeterApplications> MeterAppListVersion
{

View File

@ -25,6 +25,17 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
get;
}
/// <summary>
/// A new meter has to be checked for the reboot counter. On retries the reboot counter
/// may have increased on unsuccessfully after-reboot procedure. This would force failing
/// the 'ConnectCordonel'.
/// </summary>
Boolean CheckRebootCtr
{
get;
set;
}
/// <summary>
/// Interface information
/// </summary>

View File

@ -415,6 +415,47 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co
}
/// <summary>
/// Parameter format to convert the response or the request
/// </summary>
public enum ParaFormat
{
/// <summary>
/// String taken directly to send or as received
/// </summary>
STRING,
/// <summary>
/// Double format
/// </summary>
DOUBLE,
/// <summary>
/// Hexadecimal integer
/// </summary>
UINT_HEX,
/// <summary>
/// Hexadecimal byte plus additional sign
/// </summary>
BYTE_HEX_PLUS_SIGN,
/// <summary>
/// Decimal integer
/// </summary>
UINT_DEZ,
/// <summary>
/// Individual formatting required
/// </summary>
INDIVIDUAL,
/// <summary>
/// Parameterless means the command will be sent without any parameter
/// </summary>
PARAMETERLESS
}
/// <summary>
/// Assembly of command information and functional code
/// </summary>
@ -435,6 +476,11 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co
/// </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>
@ -446,12 +492,15 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co
/// <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, String cmdInfoStr)
public Cmd(CmdName cmdName, String cmdCodeStr, CmdType cmdType, ParaFormat paraFormat,
String cmdInfoStr)
{
CmdName = cmdName;
CmdCodeStr = cmdCodeStr;
CmdType = cmdType;
ParamFormat = paraFormat;
CmdInfoStr = cmdInfoStr;
}
}
@ -507,70 +556,70 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co
private const String UNICODE_DC2 = "\u0013";
/// <summary>
/// Complete command table
/// Command table for communication with FM2014 combines the name with the command code and additional info.
/// </summary>
public static List<Cmd> CmdTbl = new List<Cmd>
public static readonly List<Cmd> CmdTbl = new List<Cmd>
{
// FM2014 control and information
new Cmd(CmdName.CMD_CONNECT, "", CmdType.INDIVIDUAL, Resources.StrCmdMsgConnect),
new Cmd(CmdName.CMD_UNLOCK_SETUP, "&U@", CmdType.INDIVIDUAL, Resources.StrCmdMsgUnlock),
new Cmd(CmdName.CMD_ERASE_SETUP, "&E@", CmdType.INDIVIDUAL, Resources.StrCmdMsgErase),
new Cmd(CmdName.CMD_READ_EEP_MEAS_SETUP, "%D@", CmdType.INDIVIDUAL, Resources.StrCmdMsgReadDefMeasSetup),
new Cmd(CmdName.CMD_READ_RAM_MEAS_SETUP, "%A@", CmdType.INDIVIDUAL, Resources.StrCmdMsgActualDefMeasSetup),
new Cmd(CmdName.CMD_SAVE_RAM_TO_EEP_MEAS_SETUP, "%S@", CmdType.INDIVIDUAL, Resources.StrCmdMsgStoreDefMeasSetup),
new Cmd(CmdName.CMD_BOOT_MODE, "$B@", CmdType.INDIVIDUAL, Resources.StrCmdMsgBootMode),
new Cmd(CmdName.CMD_PRINT_APP_INFO, "?A", CmdType.INDIVIDUAL, Resources.StrCmdMsgPrintAppInfo),
new Cmd(CmdName.CMD_PRINT_BOOT_INFO, "?B", CmdType.INDIVIDUAL, Resources.StrCmdMsgPrintBootInfo),
new Cmd(CmdName.CMD_BAUDRATE_PC, "=BP", CmdType.INDIVIDUAL, Resources.StrCmdMsgBaudrateAll),
new Cmd(CmdName.CMD_GET_VIRTUAL_ADDRESS, " ", CmdType.INDIVIDUAL, Resources.StrCmdMsgGetVirtualAddress),
new Cmd(CmdName.CMD_SERIAL, "#", CmdType.INDIVIDUAL, Resources.StrCmdMsgSerial),
new Cmd(CmdName.CMD_GET_STATUS, UNICODE_ACK, CmdType.INDIVIDUAL, Resources.StrCmdMsgGetStatus),
new Cmd(CmdName.CMD_SET_VIRTUAL_ADDRESS, "N", CmdType.INDIVIDUAL, Resources.StrCmdMsgSetVirtualAddress),
new Cmd(CmdName.CMD_PRINT_STATISTICS, "!", CmdType.INDIVIDUAL, Resources.StrCmdMsgPrintStatistics),
new Cmd(CmdName.CMD_CONNECT, "", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgConnect),
new Cmd(CmdName.CMD_UNLOCK_SETUP, "&U@", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgUnlock),
new Cmd(CmdName.CMD_ERASE_SETUP, "&E@", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgErase),
new Cmd(CmdName.CMD_READ_EEP_MEAS_SETUP, "%D@", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgReadDefMeasSetup),
new Cmd(CmdName.CMD_READ_RAM_MEAS_SETUP, "%A@", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgActualDefMeasSetup),
new Cmd(CmdName.CMD_SAVE_RAM_TO_EEP_MEAS_SETUP, "%S@", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgStoreDefMeasSetup),
new Cmd(CmdName.CMD_BOOT_MODE, "$B@", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgBootMode),
new Cmd(CmdName.CMD_PRINT_APP_INFO, "?A", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgPrintAppInfo),
new Cmd(CmdName.CMD_PRINT_BOOT_INFO, "?B", CmdType.INDIVIDUAL, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgPrintBootInfo),
new Cmd(CmdName.CMD_BAUDRATE_PC, "=BP", CmdType.INDIVIDUAL, ParaFormat.UINT_DEZ, Resources.StrCmdMsgBaudrateAll),
new Cmd(CmdName.CMD_GET_VIRTUAL_ADDRESS, " ", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgGetVirtualAddress),
new Cmd(CmdName.CMD_SERIAL, "#", CmdType.INDIVIDUAL, ParaFormat.UINT_DEZ, Resources.StrCmdMsgSerial),
new Cmd(CmdName.CMD_GET_STATUS, UNICODE_ACK, CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgGetStatus),
new Cmd(CmdName.CMD_SET_VIRTUAL_ADDRESS, "N", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgSetVirtualAddress),
new Cmd(CmdName.CMD_PRINT_STATISTICS, "!", CmdType.INDIVIDUAL, ParaFormat.STRING, Resources.StrCmdMsgPrintStatistics),
//measurement reset
new Cmd(CmdName.CMD_RST_MEAS, "R", CmdType.BROADCAST, Resources.StrCmdMsgRstMeas),
new Cmd(CmdName.CMD_RST_MEAS, "R", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgRstMeas),
// Double impulse detection settings
new Cmd(CmdName.CMD_SET_DPLS_LOCK_TMR, "S", CmdType.BROADCAST, Resources.StrCmdMsgDplsLockTmr),
new Cmd(CmdName.CMD_SET_DPLS_LOCK_MULT, "s", CmdType.BROADCAST, Resources.StrCmdMsgDplsLockMult),
new Cmd(CmdName.CMD_SET_DBPL_UNLOCK, "G", CmdType.BROADCAST, Resources.StrCmdMsgSetDbplUnlock),
new Cmd(CmdName.CMD_SET_DPLS_LOCK_TMR, "S", CmdType.BROADCAST, ParaFormat.UINT_DEZ, Resources.StrCmdMsgDplsLockTmr),
new Cmd(CmdName.CMD_SET_DPLS_LOCK_MULT, "s", CmdType.BROADCAST, ParaFormat.UINT_DEZ, Resources.StrCmdMsgDplsLockMult),
new Cmd(CmdName.CMD_SET_DBPL_UNLOCK, "G", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgSetDbplUnlock),
// Main measurement pulse settings and readout
new Cmd(CmdName.CMD_DUT_SET_PLS, "H", CmdType.BROADCAST, Resources.StrCmdMsgDutSetPls),
new Cmd(CmdName.CMD_REF_SET_PLS, "M", CmdType.BROADCAST, Resources.StrCmdMsgRefSetPls),
new Cmd(CmdName.CMD_DUT_GET_PLS_RMN, "I", CmdType.INDIVIDUAL, Resources.StrCmdMsgDutGetPlsRmn),
new Cmd(CmdName.CMD_REF_GET_PLS_RMN, "J", CmdType.INDIVIDUAL, Resources.StrCmdMsgRefGetPlsRmn),
new Cmd(CmdName.CMD_DUT_SET_PLS, "H", CmdType.BROADCAST, ParaFormat.UINT_HEX, Resources.StrCmdMsgDutSetPls),
new Cmd(CmdName.CMD_REF_SET_PLS, "M", CmdType.BROADCAST, ParaFormat.UINT_HEX, Resources.StrCmdMsgRefSetPls),
new Cmd(CmdName.CMD_DUT_GET_PLS_RMN, "I", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgDutGetPlsRmn),
new Cmd(CmdName.CMD_REF_GET_PLS_RMN, "J", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgRefGetPlsRmn),
// Pulse counters start, readout and backup
new Cmd(CmdName.CMD_REF_STR_PLS_CTR, "O", CmdType.BROADCAST, Resources.StrCmdMsgRefStrPlsCtr),
new Cmd(CmdName.CMD_DUT_STR_PLS_CTR, "Q", CmdType.BROADCAST, Resources.StrCmdMsgDutStrPlsCtr),
new Cmd(CmdName.CMD_REF_GET_PLS_CTR, "L", CmdType.INDIVIDUAL, Resources.StrCmdMsgRefGetPlsCtr),
new Cmd(CmdName.CMD_DUT_GET_PLS_CTR, "U", CmdType.INDIVIDUAL, Resources.StrCmdMsgDutGetPlsCtr),
new Cmd(CmdName.CMD_REF_GET_PLS_CTR_BU, "l", CmdType.INDIVIDUAL, Resources.StrCmdMsgRefGetPlsCtrBu),
new Cmd(CmdName.CMD_DUT_GET_PLS_CTR_BU, "u", CmdType.INDIVIDUAL, Resources.StrCmdMsgDutGetPlsCtrBu),
new Cmd(CmdName.CMD_REF_DUT_STR_PLS_CTR, "o", CmdType.BROADCAST, Resources.StrCmdMsgRefDutStrPlsCtr),
new Cmd(CmdName.CMD_REF_DUT_BU_PLS_CTR, "q", CmdType.BROADCAST, Resources.StrCmdMsgRefDutBuPlsCtr),
new Cmd(CmdName.CMD_REF_STR_PLS_CTR, "O", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgRefStrPlsCtr),
new Cmd(CmdName.CMD_DUT_STR_PLS_CTR, "Q", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgDutStrPlsCtr),
new Cmd(CmdName.CMD_REF_GET_PLS_CTR, "L", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgRefGetPlsCtr),
new Cmd(CmdName.CMD_DUT_GET_PLS_CTR, "U", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgDutGetPlsCtr),
new Cmd(CmdName.CMD_REF_GET_PLS_CTR_BU, "l", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgRefGetPlsCtrBu),
new Cmd(CmdName.CMD_DUT_GET_PLS_CTR_BU, "u", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgDutGetPlsCtrBu),
new Cmd(CmdName.CMD_REF_DUT_STR_PLS_CTR, "o", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgRefDutStrPlsCtr),
new Cmd(CmdName.CMD_REF_DUT_BU_PLS_CTR, "q", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgRefDutBuPlsCtr),
// General measurement setup
new Cmd(CmdName.CMD_REF_SET_SCALE, "K", CmdType.BROADCAST, Resources.StrCmdMsgRefSetScale),
new Cmd(CmdName.CMD_DUT_LPP_SCALE, "V", CmdType.INDIVIDUAL_UNRESPONSIVE, Resources.StrCmdMsgDutLppScale),
new Cmd(CmdName.CMD_REF_LPP_SCALE, "W", CmdType.INDIVIDUAL_UNRESPONSIVE, Resources.StrCmdMsgRefLppScale),
new Cmd(CmdName.CMD_MEAS_SET_ATTN, "T", CmdType.BROADCAST, Resources.StrCmdMsgMeasSetAttn),
new Cmd(CmdName.CMD_REF_SET_SCALE, "K", CmdType.BROADCAST, ParaFormat.INDIVIDUAL, Resources.StrCmdMsgRefSetScale),
new Cmd(CmdName.CMD_DUT_LPP_SCALE, "V", CmdType.INDIVIDUAL_UNRESPONSIVE, ParaFormat.UINT_DEZ, Resources.StrCmdMsgDutLppScale),
new Cmd(CmdName.CMD_REF_LPP_SCALE, "W", CmdType.INDIVIDUAL_UNRESPONSIVE, ParaFormat.UINT_DEZ, Resources.StrCmdMsgRefLppScale),
new Cmd(CmdName.CMD_MEAS_SET_ATTN, "T", CmdType.BROADCAST, ParaFormat.UINT_DEZ, Resources.StrCmdMsgMeasSetAttn),
// Measurement result request
new Cmd(CmdName.CMD_DUT_GET_TMR, "X", CmdType.INDIVIDUAL, Resources.StrCmdMsgDutGetTmr),
new Cmd(CmdName.CMD_REF_GET_TMR, "Y", CmdType.INDIVIDUAL, Resources.StrCmdMsgRefGetTmr),
new Cmd(CmdName.CMD_CAL_DUT_TO_REF_TOL, "", CmdType.INDIVIDUAL, Resources.StrCalDutToRefTol),
new Cmd(CmdName.CMD_GET_UDTLC, "Z", CmdType.INDIVIDUAL, Resources.StrCmdMsgGetUdtlc),
new Cmd(CmdName.CMD_GET_DTLC, "z", CmdType.INDIVIDUAL, Resources.StrCmdMsgGetDtlc),
new Cmd(CmdName.CMD_GET_REF_PERIOD, UNICODE_DLE, CmdType.INDIVIDUAL, Resources.StrCmdMsgGetRefPeriod),
new Cmd(CmdName.CMD_GET_DUT_PERIOD, UNICODE_DC1, CmdType.INDIVIDUAL, Resources.StrCmdMsgGetDutPeriod),
new Cmd(CmdName.CMD_GET_REF_FREQU, UNICODE_DC2, CmdType.INDIVIDUAL, Resources.StrCmdMsgGetRefFrequ),
new Cmd(CmdName.CMD_CAL_FREQU_REF_PERIOD, "", CmdType.INDIVIDUAL, Resources.StrCalRefFrequFromRefPeriod),
new Cmd(CmdName.CMD_CAL_FREQU_DUT_PERIOD, "", CmdType.INDIVIDUAL, Resources.StrCalDutFrequFromDutPeriod),
new Cmd(CmdName.CMD_CAL_FLOW_REF_FREQU, "", CmdType.INDIVIDUAL, Resources.StrCalRefFlowRateFromRefFrequ),
new Cmd(CmdName.CMD_CAL_FLOW_REF_PERIOD, "", CmdType.INDIVIDUAL, Resources.StrCalRefFlowRateFromRefPeriod),
new Cmd(CmdName.CMD_CAL_FLOW_DUT_PERIOD, "", CmdType.INDIVIDUAL, Resources.StrCalDutFlowRateFromDutPeriod),
new Cmd(CmdName.CMD_DUT_GET_TMR, "X", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgDutGetTmr),
new Cmd(CmdName.CMD_REF_GET_TMR, "Y", CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgRefGetTmr),
new Cmd(CmdName.CMD_CAL_DUT_TO_REF_TOL, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalDutToRefTol),
new Cmd(CmdName.CMD_GET_UDTLC, "Z", CmdType.INDIVIDUAL, ParaFormat.BYTE_HEX_PLUS_SIGN, Resources.StrCmdMsgGetUdtlc),
new Cmd(CmdName.CMD_GET_DTLC, "z", CmdType.INDIVIDUAL, ParaFormat.BYTE_HEX_PLUS_SIGN, Resources.StrCmdMsgGetDtlc),
new Cmd(CmdName.CMD_GET_REF_PERIOD, UNICODE_DLE, CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgGetRefPeriod),
new Cmd(CmdName.CMD_GET_DUT_PERIOD, UNICODE_DC1, CmdType.INDIVIDUAL,ParaFormat.UINT_HEX, Resources.StrCmdMsgGetDutPeriod),
new Cmd(CmdName.CMD_GET_REF_FREQU, UNICODE_DC2, CmdType.INDIVIDUAL, ParaFormat.UINT_HEX, Resources.StrCmdMsgGetRefFrequ),
new Cmd(CmdName.CMD_CAL_FREQU_REF_PERIOD, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalRefFrequFromRefPeriod),
new Cmd(CmdName.CMD_CAL_FREQU_DUT_PERIOD, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalDutFrequFromDutPeriod),
new Cmd(CmdName.CMD_CAL_FLOW_REF_FREQU, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalRefFlowRateFromRefFrequ),
new Cmd(CmdName.CMD_CAL_FLOW_REF_PERIOD, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalRefFlowRateFromRefPeriod),
new Cmd(CmdName.CMD_CAL_FLOW_DUT_PERIOD, "", CmdType.INDIVIDUAL, ParaFormat.DOUBLE, Resources.StrCalDutFlowRateFromDutPeriod),
// Adjustment, place here Ids which may represent digital numbers from A to F
new Cmd(CmdName.CMD_COR_CURRENT, "C", CmdType.BROADCAST, Resources.StrCmdMsgCorCurrent),
new Cmd(CmdName.CMD_SET_XTR_MAX, "+", CmdType.BROADCAST, Resources.StrCmdMsgSetXtrMax),
new Cmd(CmdName.CMD_SET_XTR_MIN, "-", CmdType.BROADCAST, Resources.StrCmdMsgSetXtrMin),
new Cmd(CmdName.CMD_ADDR_PC, "A", CmdType.INDIVIDUAL, Resources.StrCmdMsgAddrPc)
new Cmd(CmdName.CMD_COR_CURRENT, "C", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgCorCurrent),
new Cmd(CmdName.CMD_SET_XTR_MAX, "+", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgSetXtrMax),
new Cmd(CmdName.CMD_SET_XTR_MIN, "-", CmdType.BROADCAST, ParaFormat.PARAMETERLESS, Resources.StrCmdMsgSetXtrMin),
new Cmd(CmdName.CMD_ADDR_PC, "A", CmdType.INDIVIDUAL, ParaFormat.UINT_DEZ, Resources.StrCmdMsgAddrPc)
};
/// <summary>
@ -631,7 +680,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co
/// <summary>
/// Optional integer value
/// </summary>
public Int32? IntValue { get; private set; }
public UInt32? IntValue { get; private set; }
/// <summary>
/// Optional double value
@ -651,7 +700,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co
/// <param name="intValue"></param>
/// <param name="doubleValue"></param>
/// <param name="unit"></param>
public CmdResponse(CmdName cmdName, String answerStr, Int32? intValue = null, Double? doubleValue = null,
public CmdResponse(CmdName cmdName, String answerStr, UInt32? intValue = null, Double? doubleValue = null,
String unit = "")
{
CmdName = cmdName;
@ -721,5 +770,20 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co
where ct.CmdName == cmdName
select ct.CmdType).FirstOrDefault();
}
/// <summary>
/// Get the parameter format
/// </summary>
/// <param name="cmdName"></param>
/// <returns>Command type</returns>
/// <remarks date="2026-Feb-13" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static ParaFormat GetParaFormat(CmdName cmdName)
{
return (from ct in CmdTbl
where ct.CmdName == cmdName
select ct.ParamFormat).FirstOrDefault();
}
}
}

View File

@ -73,18 +73,27 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Gener
// Read reboot count and compare with hardcoded maximum value
var rebootCount =
RegisterConverter.ByteArrayToValue<UInt32>(Meter.ReadRegister("CUSTOMER_RebootCount"));
if (rebootCount > MeterResetPsu.MAX_REBOOTS_FOR_HEALTHY_HW)
if (Meter.CheckRebootCtr)
{
ErrorMsgDispatcher(Resources.StrErrorMsgTooManyReboots +
$" - {Resources.StrProcessStateAllowed}: {MeterResetPsu.MAX_REBOOTS_FOR_HEALTHY_HW}" +
$" - {Resources.StrProcessStateDetected}: {rebootCount}");
return StatusReturn.Failed;
if (rebootCount > MeterResetPsu.MAX_REBOOTS_FOR_HEALTHY_HW)
{
ErrorMsgDispatcher(Resources.StrErrorMsgTooManyReboots +
$" - {Resources.StrProcessStateAllowed}: {MeterResetPsu.MAX_REBOOTS_FOR_HEALTHY_HW}" +
$" - {Resources.StrProcessStateDetected}: {rebootCount}");
return StatusReturn.Failed;
}
// Skip reboot counter check on reties as it passed here the initial phase as a retry may follow an
// unsuccessfully hardware-reboot-procedure
Meter.CheckRebootCtr = false;
SuccessMsgDispatcher(Resources.StrSuccessMsgRebootCounts +
$" - {Resources.StrProcessStateAllowed}: {MeterResetPsu.MAX_REBOOTS_FOR_HEALTHY_HW}" +
$" - {Resources.StrProcessStateDetected}: {rebootCount}");
}
SuccessMsgDispatcher(Resources.StrSuccessMsgRebootCounts +
$" - {Resources.StrProcessStateAllowed}: {MeterResetPsu.MAX_REBOOTS_FOR_HEALTHY_HW}" +
$" - {Resources.StrProcessStateDetected}: {rebootCount}");
// If interface is outdated
if (!Meter.InterfaceSupportsFwVersion)
{

View File

@ -38,6 +38,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
private ProcessState _lockedStateMachineState;
// sender of state change event to evaluate in e.g. error process
private IProductionProcess _stateChangeRequestProcess;
// List of production processes
public List<IProductionProcess> Processes
{
@ -147,7 +148,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
{
// Timer for cyclic call of state machine
_processCtrlTimer = new Timer(TmrProcessCtrlCycle_Elapsed, null, ProcessTimerCycleMs, ProcessTimerCycleMs);
// Delay timer between final-param-exec finished and start of 'StoreConfiguration'
_paramExeFinishedToStoreConfigDelayTimer = new DispatcherTimer();
_paramExeFinishedToStoreConfigDelayTimer.Tick += TmrParamExeFinishedToStoreConfigCycle_Tick;
@ -281,7 +282,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
break;
}
// Delay timer init and start if parametrization exec finished but only for 'EMEA' region
if (pp != null &&
if (pp != null &&
pp.ProcessName != null &&
pp.Meter != null &&
pp.Meter.Region.Equals("EMEA") &&
@ -834,7 +835,7 @@ namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
_stateMachineState = (ProcessState)processStateStruct.NextStateOnSuccess;
return;
}
// Process is waiting for execution after init, this overrides the eventually abort state
processStateStruct.ProductionProcess.InitProcess();

View File

@ -503,8 +503,8 @@ namespace Sensus.Ui.FM2014TestBench
if (_fm2014Config?.IndividualTolerancePercent?[ctr] != null &&
_fm2014Config.IndividualTolerancePercent.Count == MaxFm2014s)
{
fm2014.Tolerance_percent = _fm2014Config?.IndividualTolerancePercent[ctr] ??
FM2014.STANDARD_NOMINAL_TOLERANCE_INPUT_percent;
fm2014.CurrentOutputToleranceNominalInt_percent = _fm2014Config?.IndividualTolerancePercent[ctr] ??
FM2014.CURRENT_OUTPUT_TOLERANCE_STANDARD_NOMINAL_INT_percent;
}
}
if (_fm2014Config != null)
@ -512,7 +512,7 @@ namespace Sensus.Ui.FM2014TestBench
// Restore baudrate
FM2014.Baudrate = _fm2014Config.Baudrate;
// Restore Calibration settings for user convenience
// Restore calibration settings for user convenience
tbxRefPulsesRequired.Text = $"{_fm2014Config.RefPulsesRequired:D}";
tbxDutPulsesRequired.Text = $"{_fm2014Config.DutPulsesRequired:D}";
tbxDoublePulseDeadtime.Text = $"{_fm2014Config.DoublePulseDeadtime:D}";
@ -646,7 +646,7 @@ namespace Sensus.Ui.FM2014TestBench
var fm2014 = Fm2014s[ctr];
if (fm2014 == null)
return;
_fm2014Config.IndividualTolerancePercent.Add(fm2014.Tolerance_percent);
_fm2014Config.IndividualTolerancePercent.Add(fm2014.CurrentOutputToleranceNominalInt_percent);
_fm2014Config.SlotIsSelected.Add(SlotSelections[ctr].IsChecked);
}
@ -655,8 +655,8 @@ namespace Sensus.Ui.FM2014TestBench
if (null == firstFm2014)
return;
_fm2014Config.RefPulsesRequired = firstFm2014.RefPulsesRequired;
_fm2014Config.DutPulsesRequired = firstFm2014.DutPulsesRequired;
_fm2014Config.RefPulsesRequired = (UInt16)firstFm2014.RefPulsesRequired;
_fm2014Config.DutPulsesRequired = (UInt16)firstFm2014.DutPulsesRequired;
_fm2014Config.DoublePulseDeadtime = firstFm2014.DoublePulseDeadtime_ms;
_fm2014Config.DutToRefToleranceMax = firstFm2014.DutToRefCalibrationToleranceMax_percent;
_fm2014Config.DutToRefToleranceMin = firstFm2014.DutToRefCalibrationToleranceMin_percent;
@ -759,7 +759,7 @@ namespace Sensus.Ui.FM2014TestBench
/// <summary>
/// Common method to (de-)activate controls and timer.
/// </summary>
/// <remarks date="2026-Jan-14..20" author="Thomas Wiedebusch">
/// <remarks date="2026-Jan-14..Feb-14" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void ActionControl(Boolean isActive)
@ -771,7 +771,7 @@ namespace Sensus.Ui.FM2014TestBench
//UiElmEnable(lblTotalProcessText, true);
//UiElmEnable(lblActualProcessLabel, true);
//UiElmEnable(lblTotalProcessLabel, true);
UiElmEnable(lblActualProgressPercent, true);
UiElmEnable(lblActualProgressPercent, !_autoProgressBar, !_autoProgressBar);
//UiElmEnable(lblTotalProgressPercent, true);
UiElmEnable(pbActualProgress, true);
//UiElmEnable(pbTotalProgress, true);
@ -1076,7 +1076,7 @@ namespace Sensus.Ui.FM2014TestBench
/// <summary>
/// Feedback from FM2014being parsed to GUI
/// </summary>
/// <remarks date="2026-Feb-02..10" author="Thomas Wiedebusch">
/// <remarks date="2026-Feb-02..14" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
private void DataReceived_Handler(Object sender, ProcessExecEventArgs e)
@ -1100,18 +1100,23 @@ namespace Sensus.Ui.FM2014TestBench
UpdateContentControl(lblActualProcessText, e.ActualProcessMessage);
}
if (e.ActualProcessPercent != null)
if (e.ActualProcessPercent != null && !_autoProgressBar)
{
UpdateProgressBar(pbActualProgress, (Double)e.ActualProcessPercent);
}
Dispatcher.Invoke(() => lblActualProgressPercent.Content = $@"{pbActualProgress.Value:##0.0} %");
// A circulating process bar will be used to observe an activity, but as it restarts at the beginning
// after reaching the end, a value isn't of importance
if (!_autoProgressBar)
Dispatcher.Invoke(() => lblActualProgressPercent.Content = $@"{pbActualProgress.Value:##0.0} %");
SetTimeDisplay();
// Data dispatcher
var resp = (CmdResponse)e.SpecificInfoObj;
String valueStr;
// Used as marker for error, null means measurement is in range or status or health is good
Brush statusColor = null;
if (e.StatusReturn == StatusReturn.Failed)
{
@ -1124,46 +1129,46 @@ namespace Sensus.Ui.FM2014TestBench
}
else if (resp.IntValue != null)
{
if (e.StatusReturn == StatusReturn.MeasurementOutOfRange)
statusColor = ColorProcessFailed;
LogText($"{resp.AnswerStr}: {resp.IntValue:D} {resp.Unit}");
switch (resp.CmdName)
{
case CmdName.CMD_REF_GET_PLS_RMN:
ucFm2014.SetValue(CAL_LBL_IDX_REF_RMN_PLS, $"{resp.IntValue:D}");
ucFm2014.SetValue(CAL_LBL_IDX_REF_RMN_PLS, $"{resp.IntValue:D}", statusColor);
break;
case CmdName.CMD_DUT_GET_PLS_RMN:
ucFm2014.SetValue(CAL_LBL_IDX_DUT_RMN_PLS, $"{resp.IntValue:D}");
ucFm2014.SetValue(CAL_LBL_IDX_DUT_RMN_PLS, $"{resp.IntValue:D}", statusColor);
break;
case CmdName.CMD_REF_GET_PLS_CTR:
case CmdName.CMD_REF_GET_PLS_CTR_BU:
if (firstFm2014 != null && firstFm2014.Address == fm2014.Address)
UpdateTextBox(tbxRefPulsesMeasured, $"{resp.IntValue:D}");
ucFm2014.SetValue(CTR_LBL_IDX_REF_PLS, $"{resp.IntValue:D}");
UpdateTextBox(tbxRefPulsesMeasured, $"{resp.IntValue:D}", statusColor);
ucFm2014.SetValue(CTR_LBL_IDX_REF_PLS, $"{resp.IntValue:D}", statusColor);
break;
case CmdName.CMD_DUT_GET_PLS_CTR:
case CmdName.CMD_DUT_GET_PLS_CTR_BU:
if (firstFm2014 != null && firstFm2014.Address == fm2014.Address)
UpdateTextBox(tbxDutPulsesMeasured, $"{resp.IntValue:D}");
ucFm2014.SetValue(CTR_LBL_IDX_DUT_PLS, $"{resp.IntValue:D}");
UpdateTextBox(tbxDutPulsesMeasured, $"{resp.IntValue:D}", statusColor);
ucFm2014.SetValue(CTR_LBL_IDX_DUT_PLS, $"{resp.IntValue:D}", statusColor);
break;
case CmdName.CMD_REF_LPP_SCALE:
UpdateTextBox(tbxRefPulsesPerCm, $"{resp.IntValue:D}");
UpdateTextBox(tbxRefPulsesPerCm, $"{resp.IntValue:D}", statusColor);
break;
case CmdName.CMD_DUT_LPP_SCALE:
UpdateTextBox(tbxDutPulsesPerCm, $"{resp.IntValue:D}");
UpdateTextBox(tbxDutPulsesPerCm, $"{resp.IntValue:D}", statusColor);
break;
case CmdName.CMD_MEAS_SET_ATTN:
Dispatcher.Invoke(() => cbxDisplayAttenuation.SelectedItem = $"{resp.IntValue}");
break;
case CmdName.CMD_GET_REF_FREQU:
valueStr = $"{resp.IntValue:D}";
if (resp.IntValue < FM2014.REF_FREQU_MIN || resp.IntValue > FM2014.REF_FREQU_MAX)
ucFm2014.SetValue(REG_LBL_IDX_REF_FREQU, valueStr, ColorProcessFailed);
else
ucFm2014.SetValue(REG_LBL_IDX_REF_FREQU, valueStr);
ucFm2014.SetValue(REG_LBL_IDX_REF_FREQU, valueStr, statusColor);
break;
case CmdName.CMD_RST_MEAS:
// Immediately lock all buttons and input fields until reset is finished
SetFM2014AccessLocked();
UiElmEnable(lblActualProgressPercent, true);
// Check countdown of reset method
if (resp.IntValue == 0)
ActionControl(false);
@ -1172,35 +1177,30 @@ namespace Sensus.Ui.FM2014TestBench
}
else if (resp.DoubleValue != null)
{
if (e.StatusReturn == StatusReturn.MeasurementOutOfRange)
statusColor = ColorProcessFailed;
LogText($"{resp.AnswerStr}: {resp.DoubleValue:F2} {resp.Unit}");
switch (resp.CmdName)
{
case CmdName.CMD_REF_GET_TMR:
valueStr = $"{resp.DoubleValue:F3}";
ucFm2014.SetValue(CAL_LBL_IDX_REF_MEAS_TMR, valueStr);
ucFm2014.SetValue(CAL_LBL_IDX_REF_MEAS_TMR, valueStr, statusColor);
break;
case CmdName.CMD_DUT_GET_TMR:
valueStr = $"{resp.DoubleValue:F3}";
ucFm2014.SetValue(CAL_LBL_IDX_DUT_MEAS_TMR, valueStr);
ucFm2014.SetValue(CAL_LBL_IDX_DUT_MEAS_TMR, valueStr, statusColor);
break;
case CmdName.CMD_CAL_DUT_TO_REF_TOL:
valueStr = $"{resp.DoubleValue:F3}";
if ((Double)resp.DoubleValue <= fm2014.DutToRefCalibrationToleranceMax_percent &&
(Double)resp.DoubleValue >= fm2014.DutToRefCalibrationToleranceMin_percent)
ucFm2014.SetValue(CAL_LBL_IDX_DUT_TO_REF_TOL, valueStr, ColorSuccess);
else
ucFm2014.SetValue(CAL_LBL_IDX_DUT_TO_REF_TOL, valueStr, ColorProcessFailed);
ucFm2014.SetValue(CAL_LBL_IDX_DUT_TO_REF_TOL, valueStr, statusColor);
break;
case CmdName.CMD_GET_UDTLC:
case CmdName.CMD_GET_DTLC:
valueStr = $"{resp.DoubleValue:F2}";
if (Math.Abs((Double)resp.DoubleValue) > fm2014.Tolerance_percent)
ucFm2014.SetValue(REG_LBL_IDX_DUT_TO_REF_TOL, valueStr, ColorProcessFailed);
else
ucFm2014.SetValue(REG_LBL_IDX_DUT_TO_REF_TOL, valueStr);
ucFm2014.SetValue(REG_LBL_IDX_DUT_TO_REF_TOL, valueStr, statusColor);
break;
case CmdName.CMD_REF_SET_SCALE:
UpdateTextBox(tbxScaleRefToDut, $"{resp.DoubleValue:F4}");
UpdateTextBox(tbxScaleRefToDut, $"{resp.DoubleValue:F4}", statusColor);
break;
// // DEBUG
// case FM2014CmdDef.CmdName.CMD_GET_REF_PERIOD:
@ -1217,7 +1217,7 @@ namespace Sensus.Ui.FM2014TestBench
// break;
case CmdName.CMD_CAL_FLOW_REF_FREQU:
valueStr = $"{resp.DoubleValue:F3}";
ucFm2014.SetValue(REG_LBL_IDX_FLOW_RATE, valueStr);
ucFm2014.SetValue(REG_LBL_IDX_FLOW_RATE, valueStr, statusColor);
//UpdateTextBox(tbxRefFlowRateFromRefFrequencyCmPerH, valueStr);
// TODO THW Check if the actual flow shall be taken based on 'REF Frequency'
//UpdateTextBox(tbxActualFlowRateCmPerHour, valueStr);
@ -1295,14 +1295,14 @@ namespace Sensus.Ui.FM2014TestBench
Dispatcher.Invoke(() =>
{
if (int.TryParse(tbxRefPulsesRequired.Text, out var pulses) &&
FM2014.TIME_MEASUREMENT_INPUT_MIN_pulses <= pulses &&
FM2014.TIME_MEASUREMENT_INPUT_MAX_pulses >= pulses)
FM2014.PULSES_TIME_MEASUREMENT_INPUT_MIN <= pulses &&
FM2014.PULSES_TIME_MEASUREMENT_INPUT_MAX >= pulses)
{
refPulsesRequired = (UInt16)pulses;
}
if (int.TryParse(tbxDutPulsesRequired.Text, out pulses) &&
FM2014.TIME_MEASUREMENT_INPUT_MIN_pulses <= pulses &&
FM2014.TIME_MEASUREMENT_INPUT_MAX_pulses >= pulses)
FM2014.PULSES_TIME_MEASUREMENT_INPUT_MIN <= pulses &&
FM2014.PULSES_TIME_MEASUREMENT_INPUT_MAX >= pulses)
{
dutPulsesRequired = (UInt16)pulses;
}
@ -1346,7 +1346,7 @@ namespace Sensus.Ui.FM2014TestBench
// Backup if meanwhile anything has changed
if (_configSetupHasChanged || _calibrationSetupHasChanged)
StoreFM2014SettingsToConfigFile();
_autoProgressBar = false;
ActionControl(true);
UpdateContentControl(btnDutToRefCalibration, Properties.Resources.StrBtnStopCalibration);
@ -1424,8 +1424,8 @@ namespace Sensus.Ui.FM2014TestBench
}
if (int.TryParse(cbxToleranceCalc.SelectedItem.ToString(), out var tolerance) &&
(tolerance == FM2014.STANDARD_NOMINAL_TOLERANCE_INPUT_percent ||
tolerance == FM2014.EXTENDED_NOMINAL_TOLERANCE_INPUT_percent))
(tolerance == FM2014.CURRENT_OUTPUT_TOLERANCE_STANDARD_NOMINAL_INT_percent ||
tolerance == FM2014.CURRENT_OUTPUT_TOLERANCE_EXTENDED_VALUE_INT_percent))
{
toleranceSetup = tolerance;
}
@ -1437,7 +1437,7 @@ namespace Sensus.Ui.FM2014TestBench
var idx = fm2014.Address - 1;
var ucFM2014 = (UcFM2014Device)UcFM2014s[idx];
ucFM2014.EnableMeasurements(REG_MEASURE_COUNT);
fm2014.Tolerance_percent = toleranceSetup;
fm2014.CurrentOutputToleranceNominalInt_percent = toleranceSetup;
}
if (FM2014.RegulationMeasurement(refPulses_per_cm, dutPulses_per_cm, doublePulseDeadtime_ms,
@ -1496,8 +1496,6 @@ namespace Sensus.Ui.FM2014TestBench
var idx = fm2014.Address - 1;
var ucFM2014 = (UcFM2014Device)UcFM2014s[idx];
ucFM2014.EnableMeasurements(CTR_MEASURE_COUNT);
//fm2014.RefPulseCtrOn = true;
//fm2014.DutPulseCtrOn = measureDutPulses;
}
if (FM2014.PulseCounterMeasurement(true, measureDutPulses))
@ -1551,9 +1549,9 @@ namespace Sensus.Ui.FM2014TestBench
Dispatcher.Invoke(() =>
{
if (byte.TryParse(cbxToleranceCalc.SelectedItem.ToString(), out var tolerance) &&
(tolerance == FM2014.STANDARD_NOMINAL_TOLERANCE_INPUT_percent ||
tolerance == FM2014.EXTENDED_NOMINAL_TOLERANCE_INPUT_percent) &&
firstFm2014.Tolerance_percent != tolerance)
(tolerance == FM2014.CURRENT_OUTPUT_TOLERANCE_STANDARD_NOMINAL_INT_percent ||
tolerance == FM2014.CURRENT_OUTPUT_TOLERANCE_EXTENDED_VALUE_INT_percent) &&
firstFm2014.CurrentOutputToleranceNominalInt_percent != tolerance)
{
_regulationSetupHasChanged = true;
btnSaveRegulationSetup.IsEnabled = true;
@ -1793,8 +1791,8 @@ namespace Sensus.Ui.FM2014TestBench
// Convert the input and check limits
if (int.TryParse(tbxRefPulsesRequired.Text, out var pulses) &&
FM2014.TIME_MEASUREMENT_INPUT_MIN_pulses <= pulses &&
FM2014.TIME_MEASUREMENT_INPUT_MAX_pulses >= pulses)
FM2014.PULSES_TIME_MEASUREMENT_INPUT_MIN <= pulses &&
FM2014.PULSES_TIME_MEASUREMENT_INPUT_MAX >= pulses)
{
if (backupPulsesRequired != pulses)
_calibrationSetupHasChanged = true;
@ -1888,8 +1886,8 @@ namespace Sensus.Ui.FM2014TestBench
// Convert the input and check limits
if (int.TryParse(tbxDutPulsesRequired.Text, out var pulses) &&
FM2014.TIME_MEASUREMENT_INPUT_MIN_pulses <= pulses &&
FM2014.TIME_MEASUREMENT_INPUT_MAX_pulses >= pulses)
FM2014.PULSES_TIME_MEASUREMENT_INPUT_MIN <= pulses &&
FM2014.PULSES_TIME_MEASUREMENT_INPUT_MAX >= pulses)
{
if (backupPulsesRequired != pulses)
_calibrationSetupHasChanged = true;
@ -2357,7 +2355,7 @@ namespace Sensus.Ui.FM2014TestBench
{
fm2014.DutPulses_per_cm = (UInt16)pulses_per_cm;
}
if (!UpdateRefToDutScale())
{
tbxDutPulsesPerCm.Background = ColorProcessFailed;