diff --git a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs index 5efacd9b..2443c152 100644 --- a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs +++ b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/Consts/FM2014CmdDef.cs @@ -277,7 +277,13 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co /// /// Broadcast command without answer to address 0 or individual without answer /// - BROADCAST + BROADCAST, + + /// + /// Uninitialized addressing command type + /// + NOT_INITIALIZED, + } /// @@ -363,7 +369,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core.Co /// /// Broadcast address definition /// - public static readonly String BROADCAST_ADDRESS = $"{ADDR_RECORD_STR}{ADDR_RECORD_STR}0{ADDR_VALIDATION_STR}"; + public static readonly String BROADCAST_ADDRESS = $"{ADDR_RECORD_STR}0{ADDR_VALIDATION_STR}"; // Unicode Strings for command table private const String UNICODE_ACK = "\u0006"; diff --git a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs index c2b7cb67..a99f2d95 100644 --- a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs +++ b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs @@ -61,9 +61,9 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core #region properties /// - /// Resolution of FM2014 timer for measurement of pulse width in seconds + /// Resolution of FM2014 timer for measurement of pulse width in seconds based on 2994 Hz /// - public const Single TMR_RESOLUTION_s = 334e-6f; + private const Double TMR_RESOLUTION_s = 334.001336e-6; /// public Boolean CyclicReceiveTaskLoopIsActive @@ -109,14 +109,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core set { _address = value; - AddressStr = $"{ADDR_RECORD_STR}{ADDR_RECORD_STR}{_address}{ADDR_VALIDATION_STR}"; + AddressStr = $"{ADDR_RECORD_STR}{_address}{ADDR_VALIDATION_STR}"; } } /// /// Marker if broadcast connection was last communication /// - private Boolean BroadcastCommIsActive + private CmdType CmdType { get; set; @@ -168,68 +168,85 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// Publish the FM2014 minimum pulses for REF and DUT time measurement. /// - public const UInt16 TimeMeasurementPulsesSetupMin = 1; + public const UInt32 TimeMeasurementPulsesSetupMin = 1; /// /// Publish the FM2014 maximum pulses for REF and DUT time measurement. /// - public const UInt16 TimeMeasurementPulsesSetupMax = 0xFFFF; + public const UInt32 TimeMeasurementPulsesSetupMax = 0xFFFF; /// - /// Publish the FM2014 minimum pulses for REF and DUT regulation measurement. + /// This is the invalid marker which can be stored to the FM2014 with command + /// 'W' - 'REF pulses per volume'! The REF pulses/cm can then be restored using + /// 'Ref_pulses_per_cm = Dut_pulses_per_cm * RefToDutScale_norm' /// - public const UInt16 PulsesPerCmRegulationSetupMin = 1; - /// - /// Publish the FM2014 maximum pulses for REF and DUT regulation measurement. - /// - public const UInt16 PulsesPerCmRegulationSetupMax = 9999; + private const UInt32 RefPulsesPerVolumeRegulationSetupInvalidMarker = 1; - private UInt16 _ref_pulse_per_cm = 1000; + /// + /// This is the minimum value which can be stored to the FM2014 with commands + /// 'V' - 'DUT pulses per volume' and 'W' - 'REF pulses per volume'! + /// + private const UInt32 PulsesPerVolumeRegulationSetupMin = 1; + + /// + /// This is the maximum value which can be stored to the FM2014 with command + /// 'V' - 'DUT pulses per volume'! + /// + private const UInt32 PulsesPerVolumeRegulationSetupMax = 9999; + + /// + /// This is the maximum input value for the REF which CANNOT be stored to FM2014 + /// but will be used to calculate the 'RefToDutScale_norm'. + /// + private const UInt32 RefPulsesPerCmRegulationInputLimitMax = 100000000; + + private UInt32 _ref_pulse_per_cm; /// - public UInt16 Ref_pulse_per_cm + public UInt32 Ref_pulse_per_cm { get => _ref_pulse_per_cm; set { // Check limits and equality - if (value < PulsesPerCmRegulationSetupMin || - value > PulsesPerCmRegulationSetupMax || + if (value < PulsesPerVolumeRegulationSetupMin || + value > RefPulsesPerCmRegulationInputLimitMax || value == _ref_pulse_per_cm) return; _ref_pulse_per_cm = value; - if (_dut_pulse_per_cm != 0) - RefToDutScale_norm = (Single)_ref_pulse_per_cm / _dut_pulse_per_cm; + if (_dut_pulse_per_cm != 0 && _ref_pulse_per_cm != 0) + RefToDutScale_norm = Math.Round((Double)_ref_pulse_per_cm / _dut_pulse_per_cm, 2); } } - private UInt16 _dut_pulse_per_cm = 100; + private UInt32 _dut_pulse_per_cm; /// - public UInt16 Dut_pulse_per_cm + public UInt32 Dut_pulse_per_cm { get => _dut_pulse_per_cm; set { - // Check limits and equality - if (value < PulsesPerCmRegulationSetupMin || - value > PulsesPerCmRegulationSetupMax || + // Check limits and equality, the DUT max input is 9999 as this will be + // directly stored to FM2014'V' - 'DUT pulses per volume' + if (value < PulsesPerVolumeRegulationSetupMin || + value > PulsesPerVolumeRegulationSetupMax || value == _dut_pulse_per_cm) return; _dut_pulse_per_cm = value; - if (_dut_pulse_per_cm != 0) - RefToDutScale_norm = (Single)_ref_pulse_per_cm / _dut_pulse_per_cm; + if (_dut_pulse_per_cm != 0 && _ref_pulse_per_cm != 0) + RefToDutScale_norm = Math.Round((Double)_ref_pulse_per_cm / _dut_pulse_per_cm, 2); } } /// /// Publish the FM2014 minimum REF to DUT scale for error display. /// - public const Single RefToDutScaleMin = 0.0001f; + public const Double RefToDutScaleMin = 0.0001; /// /// Publish the FM2014 maximum REF to DUT scale for error display. /// - public const Single RefToDutScaleMax = 999.9f; + public const Double RefToDutScaleMax = 999.9; /// /// As the scale needed to be sent to the FM2014 doesn't follow the standardized nomenclature @@ -245,11 +262,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// "1000K3" = 100.0 /// Max value is "9999K3" = 999.9 /// - public String RefToDutScaleStr = "1000K+2"; - private Single _refToDutScale_norm = 10.0f; + private String _refToDutScaleStr = "1000K+2"; + private Double _refToDutScale_norm = 10.0f; /// - public Single RefToDutScale_norm + /// + /// - Initial. + /// + public Double RefToDutScale_norm { get => _refToDutScale_norm; private set @@ -267,7 +287,9 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } var mantissa = (UInt16)number; - RefToDutScaleStr = $"{mantissa}{GetCmdStr(CmdName.CMD_REF_SET_SCALE)}{exponent}"; + + // The pre-generated string will be used to send it to the FM2014 directly + _refToDutScaleStr = $"{mantissa}{GetCmdStr(CmdName.CMD_REF_SET_SCALE)}{exponent}"; } } @@ -288,7 +310,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// The measured flow rate in cubic meters per hour using the REF /// - public Single RefFlowRate_cm_per_h + public Double RefFlowRate_cm_per_h { internal set; get; @@ -375,6 +397,10 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core SerialPort.PortName = comPort; SerialPort.ReadTimeout = 1000; SerialPort.WriteTimeout = 1000; + // Reset pulse ratios to force a preset with 'standalone' settings from + // FM2014 wit '%D@' - Read 'Default Measurement Setup' + _ref_pulse_per_cm = 0; + _dut_pulse_per_cm = 0; return true; } @@ -516,12 +542,13 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// - Set to false. /// /// - /// + /// /// - Initial. /// public Boolean RegulationMeasurement() { - if (Ref_pulse_per_cm == 0 || Dut_pulse_per_cm == 0 || RefToDutScale_norm == 0.0f) + if (Ref_pulse_per_cm == 0 || Dut_pulse_per_cm == 0 || + RefToDutScale_norm < RefToDutScaleMin || RefToDutScale_norm > RefToDutScaleMax) { return false; } @@ -543,7 +570,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core return false; } cmdName = CmdName.CMD_REF_SET_SCALE; - if (!Write(cmdName, RefToDutScaleStr)) + if (!Write(cmdName, _refToDutScaleStr)) { return false; } @@ -598,7 +625,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { // Publish the period [ms] var response = new CmdResponse(cmdName, info, - doubleValue: TMR_RESOLUTION_s * 1000.0f * period, + doubleValue: TMR_RESOLUTION_s * 1000.0 * period, siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME)); OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, @@ -608,14 +635,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core cmdName = CmdName.CMD_CAL_FREQU_REF_PERIOD; info = GetCmdInfo(cmdName); response = new CmdResponse(cmdName, info, - doubleValue: 1.0f / (TMR_RESOLUTION_s * period), + doubleValue: 1.0 / (TMR_RESOLUTION_s * period), siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY)); OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, specificInfoObj: response)); // Convert to m³/h (3600 s/h) based on REF pulse rate - var flowRate_cm_per_h = 3600.0f / (period * TMR_RESOLUTION_s) / Ref_pulse_per_cm; + var flowRate_cm_per_h = 3600.0 / (period * TMR_RESOLUTION_s) / Ref_pulse_per_cm; // Publish the calculated flow rate [m³/h] cmdName = CmdName.CMD_CAL_FLOW_REF_PERIOD; info = GetCmdInfo(cmdName); @@ -638,7 +665,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { // Publish the period [ms] var response = new CmdResponse(cmdName, info, - doubleValue: TMR_RESOLUTION_s * 1000.0f * period, + doubleValue: TMR_RESOLUTION_s * 1000.0 * period, siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME)); OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, @@ -648,14 +675,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core cmdName = CmdName.CMD_CAL_FREQU_DUT_PERIOD; info = GetCmdInfo(cmdName); response = new CmdResponse(cmdName, info, - doubleValue: 1.0f / (TMR_RESOLUTION_s * period), + doubleValue: 1.0 / (TMR_RESOLUTION_s * period), siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY)); OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, specificInfoObj: response)); // Convert to m³/h (3600 s/h) based on DUT pulse rate - var flowRate_cm_per_h = 3600.0f / (period * TMR_RESOLUTION_s) / Dut_pulse_per_cm; + var flowRate_cm_per_h = 3600.0 / (period * TMR_RESOLUTION_s) / Dut_pulse_per_cm; // Publish the calculated flow rate [m³/h] cmdName = CmdName.CMD_CAL_FLOW_DUT_PERIOD; info = GetCmdInfo(cmdName); @@ -683,7 +710,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core specificInfoObj: response)); // Convert to m³/h (3600 s/h) and save the value - RefFlowRate_cm_per_h = 3600.0f * refFrequency / Ref_pulse_per_cm; + RefFlowRate_cm_per_h = 3600.0 * refFrequency / Ref_pulse_per_cm; // Publish the calculated flow rate [m³/h] cmdName = CmdName.CMD_CAL_FLOW_REF_FREQU; info = GetCmdInfo(cmdName); @@ -716,24 +743,34 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// Transfer all standalone settings to FM2014 RAM and safe those to nonvolatile EEPROM /// /// + /// + /// - Initial. + /// + /// + /// - REF pulse rate can be 0 as 'invalid marker'. + /// public Boolean SaveStandAloneMeasurement() { var cmdName = CmdName.CMD_REF_LPP_SCALE; try { - var retVal = Write(cmdName, Ref_pulse_per_cm); + // Set to zero if it doesn't fit to the limit which can be stored as indicator for invalid value + // which should be recovered by 'Ref_pulses_per_cm = Dut_pulses_per_cm * RefToDutScale_norm' + var limitedRefPulsesPerCm = Ref_pulse_per_cm <= PulsesPerVolumeRegulationSetupMax ? + Ref_pulse_per_cm : RefPulsesPerVolumeRegulationSetupInvalidMarker; + var retVal = Write(cmdName, (UInt16)limitedRefPulsesPerCm); if (retVal) { cmdName = CmdName.CMD_DUT_LPP_SCALE; - retVal = Write(cmdName, Dut_pulse_per_cm); + retVal = Write(cmdName, (UInt16)Dut_pulse_per_cm); } if (retVal) { cmdName = CmdName.CMD_REF_SET_SCALE; - // During setup of the 'Ref_pulse_per_cm' the 'RefToDutScaleStr' will be generated - retVal = Write(cmdName, RefToDutScaleStr); + // During setup of the 'Ref_pulse_per_cm' the '_refToDutScaleStr' will be generated + retVal = Write(cmdName, _refToDutScaleStr); } if (retVal) @@ -768,6 +805,11 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// - Initial. /// + /// + /// - 'Ref_pulse_per_cm' will be calculated by 'Ref_pulses_per_cm = Dut_pulses_per_cm * RefToDutScale_norm' + /// to avoid an overflow as the REF pulses per volume can store max 9999 pulses. As the Dut_pulse_per_cm + /// is for the smallest meter 1000 Impulses/m³ + /// private Boolean ReadAloneMeasurementControl(CmdName cmdName) { // Allow explicit standalone measurement commands @@ -834,36 +876,46 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { cmdName = CmdName.CMD_REF_SET_SCALE; var info = GetCmdInfo(cmdName); - RefToDutScale_norm = (Single)value; + RefToDutScale_norm = value; var response = new CmdResponse(cmdName, info, doubleValue: RefToDutScale_norm); OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr, specificInfoObj: response)); } } - if (str.Contains(StandAlonePartSearchRefPpvStr)) - { - var strCleaned = Regex.Replace(str, "[^0-9]", string.Empty); - if (ushort.TryParse(strCleaned, out var value)) - { - cmdName = CmdName.CMD_REF_LPP_SCALE; - var info = GetCmdInfo(cmdName); - Ref_pulse_per_cm = value; - var response = new CmdResponse(cmdName, info, Ref_pulse_per_cm); - OnRawRecordReceived?.Invoke(this, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr, - specificInfoObj: response)); - } - } + // REMOVED 2026-Jan-21 + //if (str.Contains(StandAlonePartSearchRefPpvStr)) + //{ + // var strCleaned = Regex.Replace(str, "[^0-9]", string.Empty); + // if (ushort.TryParse(strCleaned, out var value)) + // { + // cmdName = CmdName.CMD_REF_LPP_SCALE; + // var info = GetCmdInfo(cmdName); + // Ref_pulse_per_cm = value; + // var response = new CmdResponse(cmdName, info, Ref_pulse_per_cm); + // OnRawRecordReceived?.Invoke(this, + // new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr, + // specificInfoObj: response)); + // } + //} if (str.Contains(StandAlonePartSearchDutPpvStr)) { var strCleaned = Regex.Replace(str, "[^0-9]", string.Empty); - if (ushort.TryParse(strCleaned, out var value)) + if (uint.TryParse(strCleaned, out var value)) { cmdName = CmdName.CMD_DUT_LPP_SCALE; var info = GetCmdInfo(cmdName); Dut_pulse_per_cm = value; - var response = new CmdResponse(cmdName, info, Dut_pulse_per_cm); + var response = new CmdResponse(cmdName, info, (Int32)Dut_pulse_per_cm); + OnRawRecordReceived?.Invoke(this, + new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr, + specificInfoObj: response)); + // Added 2026-Jan-01 to overcome limitation of max 9999 Impulses per volume + cmdName = CmdName.CMD_REF_LPP_SCALE; + info = GetCmdInfo(cmdName); + // Calculate REF pulses per cubic meter + Ref_pulse_per_cm = (UInt32)Math.Round(Dut_pulse_per_cm * RefToDutScale_norm, 2); + response = new CmdResponse(cmdName, info, (Int32)Ref_pulse_per_cm); OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs("", actualProcessMessage: measurementInfoStr, specificInfoObj: response)); @@ -881,8 +933,8 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } return true; - } + /// /// Login to individual address. /// @@ -947,7 +999,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response, statusReturn: StatusReturn.Failed)); - BroadcastCommIsActive = false; + CmdType = CmdType.NOT_INITIALIZED; return false; } @@ -961,13 +1013,13 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response, statusReturn: StatusReturn.Failed)); - BroadcastCommIsActive = false; + CmdType = CmdType.NOT_INITIALIZED; return false; } } // If broadcast communication is already active - if (BroadcastCommIsActive) + if (CmdType == CmdType.BROADCAST) { return true; } @@ -981,7 +1033,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var info = GetCmdInfo(cmdName) + ": " + BROADCAST_ADDRESS; var response = new CmdResponse(cmdName, info); OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs("", specificInfoObj: response)); - BroadcastCommIsActive = true; + CmdType = CmdType.BROADCAST; return true; } catch (Exception e) @@ -990,7 +1042,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core $"{Resources.StrCmdRespErrorBroadcast} - {e.Message}"); OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response, statusReturn: StatusReturn.Failed)); - BroadcastCommIsActive = false; + CmdType = CmdType.NOT_INITIALIZED; return false; } } @@ -999,7 +1051,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// Initiate communication to individual node /// /// - /// + /// /// - Initial. /// private Boolean InitiateIndividualComm() @@ -1011,6 +1063,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response, statusReturn: StatusReturn.Failed)); + CmdType = CmdType.NOT_INITIALIZED; return false; } @@ -1023,6 +1076,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response, statusReturn: StatusReturn.Failed)); + CmdType = CmdType.NOT_INITIALIZED; return false; } } @@ -1044,7 +1098,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core FullId = FullId.Replace(ADDR_RECORD_STR, ""); var fields = FullId.Split('.'); FwVersion = fields[1]; - BroadcastCommIsActive = false; + CmdType = GetCmdCmdType(cmdName); return true; } } @@ -1053,12 +1107,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core response = new CmdResponse(CmdName.CMD_CONNECT, $"{Resources.StrCmdRespErrorIndividual} - {e.Message}"); OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response, statusReturn: StatusReturn.Failed)); + CmdType = CmdType.NOT_INITIALIZED; return false; } response = new CmdResponse(CmdName.CMD_CONNECT, $"{Resources.StrCmdRespErrorIndividual}"); OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response, statusReturn: StatusReturn.Failed)); + CmdType = CmdType.NOT_INITIALIZED; return false; } @@ -1080,7 +1136,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { if (SerialPort == null || !SerialPort.IsOpen) { - BroadcastCommIsActive = false; + CmdType = CmdType.NOT_INITIALIZED; IsLoggedOn = false; CyclicReceiveTaskLoopIsActive = false; return false; @@ -1090,14 +1146,14 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var retVal = true; if (CmdType.BROADCAST == GetCmdCmdType(cmdName)) { - if (!BroadcastCommIsActive) + if (CmdType != CmdType.BROADCAST) { retVal = InitiateBroadcastComm(); } } else { - if (BroadcastCommIsActive) + if (CmdType != CmdType.INDIVIDUAL_WA || CmdType != CmdType.INDIVIDUAL_WA) { retVal = InitiateIndividualComm(); } @@ -1110,7 +1166,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core var cmdCodeStr = GetCmdStr(cmdName); var info = GetCmdInfo(cmdName); - var response = new CmdResponse(); + CmdResponse response; // Without data the command alone needs to be sent if (dataObj == null) { @@ -1130,12 +1186,16 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } // The data contains the preceding setup, the command code and string delimiter will be added here - if (dataObj is UInt16 || dataObj is Byte) + else if (dataObj is UInt16 || dataObj is Byte) { SerialPort.Write($"{dataObj}{cmdCodeStr}{END_OF_STR}"); response = new CmdResponse(cmdName, Resources.StrCommDirectionSetup + ": " + info + $": {dataObj}{cmdCodeStr}"); } + else + { + return false; + } } OnRawRecordReceived?.Invoke(this, new ProcessExecEventArgs("", specificInfoObj: response)); @@ -1189,7 +1249,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { ResetMeasurement(); IsLoggedOn = false; - BroadcastCommIsActive = false; + CmdType = CmdType.NOT_INITIALIZED; SerialPort?.Close(); return SerialPort != null && !SerialPort.IsOpen; } diff --git a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/IFM2014.cs b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/IFM2014.cs index d85ed81d..26494014 100644 --- a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/IFM2014.cs +++ b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/IFM2014.cs @@ -123,7 +123,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// Reference pulses per cubic-meter /// - UInt16 Ref_pulse_per_cm + UInt32 Ref_pulse_per_cm { get; } @@ -131,12 +131,12 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// /// REF to DUT scale normalized /// - Single RefToDutScale_norm { get; } + Double RefToDutScale_norm { get; } /// /// Device under test pulses per cubic-meter /// - UInt16 Dut_pulse_per_cm + UInt32 Dut_pulse_per_cm { get; } diff --git a/MiniPrf/Ui/FrmMainMiniPrf.cs b/MiniPrf/Ui/FrmMainMiniPrf.cs index 89537f9f..cb204144 100644 --- a/MiniPrf/Ui/FrmMainMiniPrf.cs +++ b/MiniPrf/Ui/FrmMainMiniPrf.cs @@ -90,6 +90,9 @@ namespace Sensus.MiniPrf.Ui private static readonly Color ColorProcessFailed = Color.Red; //private static readonly Color ColorOngoingProcess = Color.Blue; //private static readonly Color ColorUnknownStatus = Color.Gray; + private static Color ColorStandardInputField; + private static Color ColorStandardDisplayField; + //private const String SuccessSign = @"✔"; //private const String FailedSign = @"✘"; @@ -130,6 +133,9 @@ namespace Sensus.MiniPrf.Ui //Thread.CurrentThread.CurrentUICulture = cultureInfo; //Thread.CurrentThread.CurrentCulture = cultureInfo; _version = Assembly.GetExecutingAssembly().GetName().Version; + + ColorStandardDisplayField = lblFM2014SerialPort.BackColor; + ColorStandardInputField = cbxFM2014ComPort.BackColor; } /// @@ -140,7 +146,7 @@ namespace Sensus.MiniPrf.Ui /// private void Init() { - ViewProcessControl(false); + ActionControl(false); _autoProgressBar = false; _regulationSetupHasChanged = false; grpBoxDebug.Visible = false; @@ -165,7 +171,7 @@ namespace Sensus.MiniPrf.Ui { cbxFM2014ComPort.Items.Add(comPort); } - + cbxFM2014ComPort.Text = cbxFM2014ComPort.Items[_comPortIdx].ToString(); var itemContent = _fm2014Config?.Address ?? 1; for (var idx = 0; idx < cbxFM2014Address.MaxDropDownItems; idx++) @@ -189,7 +195,7 @@ namespace Sensus.MiniPrf.Ui // FM2014 group box lblConnectFM2014.Text = Resources.StrLblFM2014NotConnected; - lblConnectFM2014.ForeColor = Color.Red; + lblConnectFM2014.ForeColor = ColorProcessFailed; tbxFM2014FullId.Text = ""; tbxFM2014ApplicationFwVersion.Text = ""; lblFM2014SerialNumber.Text = Resources.StrLblFM2014SerialNumber; @@ -300,7 +306,6 @@ namespace Sensus.MiniPrf.Ui #endregion FormControls #region TimerControls - private void tmrProgressUpdate_Tick(Object sender, EventArgs e) { if (_autoProgressBar || barSingleProgressUpdate.Visible) @@ -370,7 +375,6 @@ namespace Sensus.MiniPrf.Ui cbxFM2014Address.Enabled = true; cbxFM2014ComPort.Enabled = true; cbxFM2014TolerancePercent.Enabled = true; - } /// @@ -388,11 +392,9 @@ namespace Sensus.MiniPrf.Ui } SetFM2014AccessEnabled(); } - #endregion ActivationControls #region BoardControls - /// /// Establish connection /// @@ -508,94 +510,84 @@ namespace Sensus.MiniPrf.Ui #region ProcessControls + /// + /// Common method to (de-)activate controls and timer. + /// + /// + /// - Initial. + /// private void ActionControl(Boolean isActive) { - if (isActive) + Invoke(new Action(() => { - Invoke(new Action(() => + if (isActive) { - ViewProcessControl(true); + lblActualProcess.Visible = true; + barSingleProgressUpdate.Visible = true; + SetFM2014AccessLocked(); tmrProgressUpdate.Enabled = true; - })); - } - else - { - Invoke(new Action(() => + } + else { - ViewProcessControl(false); + lblActualProcess.Visible = false; + barSingleProgressUpdate.Visible = false; + tmrProgressUpdate.Enabled = false; + lblWaitingForMeterResponse.Visible = false; + lblActualProcess.Text = ""; CheckUpdateEnabled(); - })); - } - } - - /// - /// View all process bars and labels - /// - private void ViewProcessControl(Boolean view) - { - if (view) - { - lblActualProcess.Visible = true; - barSingleProgressUpdate.Visible = true; - tmrProgressUpdate.Enabled = true; - SetFM2014AccessLocked(); - } - else - { - lblActualProcess.Visible = false; - barSingleProgressUpdate.Visible = false; - tmrProgressUpdate.Enabled = false; - lblWaitingForMeterResponse.Visible = false; - lblActualProcess.Text = ""; - SetFM2014AccessEnabled(); - } - - //common actions and settings - lblActualProcess.Update(); - barSingleProgressUpdate.Value = 0; - barSingleProgressUpdate.Update(); - Update(); + } + //common actions and settings + lblActualProcess.Update(); + barSingleProgressUpdate.Value = 0; + barSingleProgressUpdate.Update(); + Update(); + })); } /// /// Common method to prepare for changed setup. /// + /// + /// - Initial. + /// private void SetupHasChanged() { _regulationSetupHasChanged = true; btnSaveRegulationSetup.Enabled = true; - + tbxRefFrequencyHz.Text = ""; - tbxRefFrequencyHz.BackColor = gbxDutToRefRegulation.BackColor; + tbxRefFrequencyHz.BackColor = ColorStandardDisplayField; tbxActualFlowRateCmPerHour.Text = ""; - tbxActualFlowRateCmPerHour.BackColor = gbxDutToRefRegulation.BackColor; + tbxActualFlowRateCmPerHour.BackColor = ColorStandardDisplayField; tbxActualMeasuredToleranceDutToRef.Text = ""; - tbxActualMeasuredToleranceDutToRef.BackColor = gbxDutToRefRegulation.BackColor; - - tbxRefPulsePerCm.BackColor = Color.White; - tbxDutPulsePerCm.BackColor = Color.White; - tbxScaleRefToDut.BackColor = lblRefPulsePerVolume.BackColor; + tbxActualMeasuredToleranceDutToRef.BackColor = ColorStandardDisplayField; - tbxManualInputVolumeLiters.BackColor = Color.White; - tbxRefCalibrationResultPulsePerCm.BackColor = lblRefPulsePerVolume.BackColor; + tbxRefPulsePerCm.BackColor = ColorStandardInputField; + tbxDutPulsePerCm.BackColor = ColorStandardInputField; + tbxScaleRefToDut.BackColor = ColorStandardDisplayField; + tbxManualInputVolumeLiters.BackColor = ColorStandardInputField; + tbxRefCalibrationResultPulsePerCm.BackColor = ColorStandardDisplayField; } /// /// Common routine for REF to DUT scale check and update /// /// + /// + /// - Initial. + /// private Boolean UpdateRefToDutScale() { tbxScaleRefToDut.Text = $@"{Fm2014.RefToDutScale_norm:F4}"; - + // Display error if scale doesn't fit - if (Math.Abs(Fm2014.RefToDutScale_norm - + if (Math.Abs(Fm2014.RefToDutScale_norm - (Single)Fm2014.Ref_pulse_per_cm / Fm2014.Dut_pulse_per_cm) > FM2014.RefToDutScaleMin || - Fm2014.RefToDutScale_norm < FM2014.RefToDutScaleMin || - Fm2014.RefToDutScale_norm > FM2014.RefToDutScaleMax ) + Fm2014.RefToDutScale_norm < FM2014.RefToDutScaleMin || + Fm2014.RefToDutScale_norm > FM2014.RefToDutScaleMax) { - tbxScaleRefToDut.BackColor = Color.Red; + tbxScaleRefToDut.BackColor = ColorProcessFailed; tbxScaleRefToDut.Text = Resources.StrError; btnSaveRegulationSetup.Enabled = false; return false; @@ -888,14 +880,14 @@ namespace Sensus.MiniPrf.Ui //Backup the actual setting to detect changes var backupPulses_per_cm = Fm2014.Ref_pulse_per_cm; - Fm2014.Ref_pulse_per_cm = (UInt16)pulses_per_cm; + Fm2014.Ref_pulse_per_cm = (UInt32)pulses_per_cm; // Output the Fm2014 setting to avoid wrong display of invalid ranges as this will be // limited during the setup of the FM2014 property! tbxRefPulsePerCm.Text = $@"{Fm2014.Ref_pulse_per_cm:D}"; // During setup of the 'Ref_pulse_per_cm' the 'RefToDutScaleStr' will be generated if (!UpdateRefToDutScale()) { - tbxRefPulsePerCm.BackColor = Color.Red; + tbxRefPulsePerCm.BackColor = ColorProcessFailed; return; } // Check if values have changed and need to be updated in the standalone setup @@ -958,14 +950,14 @@ namespace Sensus.MiniPrf.Ui //Backup the actual setting to detect changes var backupPulses_per_cm = Fm2014.Dut_pulse_per_cm; // Try to set the new calculated REF pulses limited by the property setter - Fm2014.Dut_pulse_per_cm = (UInt16)pulses_per_cm; + Fm2014.Dut_pulse_per_cm = (UInt32)pulses_per_cm; // Output the Fm2014 setting to avoid wrong display of invalid ranges as this will be // limited during the setup of the FM2014 property! tbxDutPulsePerCm.Text = $@"{Fm2014.Dut_pulse_per_cm:D}"; // During setup of the 'Dut_pulse_per_cm' the 'RefToDutScaleStr' will be generated if (!UpdateRefToDutScale()) { - tbxDutPulsePerCm.BackColor = Color.Red; + tbxDutPulsePerCm.BackColor = ColorProcessFailed; return; } @@ -1022,12 +1014,12 @@ namespace Sensus.MiniPrf.Ui { tbxRefCalibrationResultPulsePerCm.Text = $@"{Fm2014.Ref_pulse_per_cm:D}"; tbxRefPulsePerCm.Text = $@"{Fm2014.Ref_pulse_per_cm:D}"; - tbxManualInputVolumeLiters.BackColor = Color.White; - tbxRefCalibrationResultPulsePerCm.BackColor = lblRefPulsePerVolume.BackColor; + tbxManualInputVolumeLiters.BackColor = ColorStandardInputField; + tbxRefCalibrationResultPulsePerCm.BackColor = ColorStandardDisplayField; if (!UpdateRefToDutScale()) { - tbxManualInputVolumeLiters.BackColor = Color.Red; + tbxManualInputVolumeLiters.BackColor = ColorProcessFailed; return; } @@ -1039,8 +1031,8 @@ namespace Sensus.MiniPrf.Ui } else { - tbxManualInputVolumeLiters.BackColor = Color.Red; - tbxRefCalibrationResultPulsePerCm.BackColor = Color.Red; + tbxManualInputVolumeLiters.BackColor = ColorProcessFailed; + tbxRefCalibrationResultPulsePerCm.BackColor = ColorProcessFailed; tbxRefCalibrationResultPulsePerCm.Text = Resources.StrError; } } @@ -1069,7 +1061,7 @@ namespace Sensus.MiniPrf.Ui /// /// Feedback from FM2014being parsed to GUI /// - /// + /// /// - Initial. /// private void DataReceived_Handler(Object sender, ProcessExecEventArgs e) @@ -1121,13 +1113,13 @@ namespace Sensus.MiniPrf.Ui tbxRefFrequencyDirectHz.Text = $@"{resp.IntValue:D}"; if (resp.IntValue < 1 || resp.IntValue > 254) { - tbxRefFrequencyHz.BackColor = Color.Red; - tbxActualFlowRateCmPerHour.BackColor = Color.Red; + tbxRefFrequencyHz.BackColor = ColorProcessFailed; + tbxActualFlowRateCmPerHour.BackColor = ColorProcessFailed; } else { - tbxRefFrequencyHz.BackColor = gbxDutToRefRegulation.BackColor; - tbxActualFlowRateCmPerHour.BackColor = gbxDutToRefRegulation.BackColor; + tbxRefFrequencyHz.BackColor = ColorStandardDisplayField; + tbxActualFlowRateCmPerHour.BackColor = ColorStandardDisplayField; } break; } @@ -1143,15 +1135,15 @@ namespace Sensus.MiniPrf.Ui if (resp.DoubleValue < -Fm2014.Tolerance_percent || resp.DoubleValue > Fm2014.Tolerance_percent) { - tbxActualMeasuredToleranceDutToRef.BackColor = Color.Red; + tbxActualMeasuredToleranceDutToRef.BackColor = ColorProcessFailed; } else { - tbxActualMeasuredToleranceDutToRef.BackColor = gbxDutToRefRegulation.BackColor; + tbxActualMeasuredToleranceDutToRef.BackColor = ColorStandardDisplayField; } break; case FM2014CmdDef.CmdName.CMD_REF_SET_SCALE: - tbxScaleRefToDut.Text = $@"{resp.DoubleValue:F4}" ; + tbxScaleRefToDut.Text = $@"{resp.DoubleValue:F4}"; break; // DEBUG case FM2014CmdDef.CmdName.CMD_GET_REF_PERIOD: