diff --git a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs index 46b1f90e..f49a4985 100644 --- a/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs +++ b/Common/Hardware/WaterMeter/MechanicalMeter/FM2014/FM2014Core/FM2014.cs @@ -144,12 +144,42 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// private static CmdType SharedCmdTypeReminderLastCmd { get; set; } - /// /// Reminder of last individual address of FM2014 to initiate a new communication to another device. /// private static Int32 ReminderLastIndividualCommFm2014Address { get; set; } + /// + /// Number of processing steps for this actual process progress + /// + private static Int32 MaxActualProcessProgress { get; set; } + + /// + /// Progress of actual process converted to percent + /// + private static Double _actualProcessProgress_percent; + + /// + /// Progress of actual process + /// + private static Int32 _actualProcessProgress; + + /// + /// Actual process progress of subroutine + /// + private static Int32 ActualProcessProgress + { + get => _actualProcessProgress; + set + { + _actualProcessProgress = value; + if (MaxActualProcessProgress == 0) + MaxActualProcessProgress = 1; + if (_actualProcessProgress > MaxActualProcessProgress) + _actualProcessProgress = MaxActualProcessProgress; + _actualProcessProgress_percent = 100.0 * _actualProcessProgress / MaxActualProcessProgress; + } + } #endregion ---------------------------------------- static properties ----------------------------------------- #region ------------------------------------------- object properties ----------------------------------------- @@ -210,11 +240,11 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// Private tolerance percentage /// private Int32 _tolerance_percent = DEFAULT_TOLERANCE_percent; - + /// /// Actual tolerance scale to convert raw value from FM2014 to percent /// - private Single _toleranceRawToPercentScale = 3.3f / 255; + private Single ToleranceRawToPercentScale { set; get; } = 3.3f / 255; /// /// Tolerance output of current loop in percent: @@ -235,7 +265,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core else percentage = 3.3f; _tolerance_percent = value; - _toleranceRawToPercentScale = percentage / 255; + ToleranceRawToPercentScale = percentage / 255; } } @@ -254,44 +284,92 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// public const UInt32 TimeMeasurementMax_pulses = 0xFFFF; - private UInt32 _refTimeMeasurement_pulses; + private UInt32 _refRequiredTimeMeasurement_pulses; /// - /// REF pulses for time measurement. + /// Required REF pulses for time measurement. /// - public UInt32 RefTimeMeasurement_pulses + public UInt32 RefRequiredTimeMeasurement_pulses { - get => _refTimeMeasurement_pulses; + get => _refRequiredTimeMeasurement_pulses; private set { // Check limits and equality if (value < TimeMeasurementMin_pulses || value > TimeMeasurementMax_pulses || - value == _refTimeMeasurement_pulses) + value == _refRequiredTimeMeasurement_pulses) return; - _refTimeMeasurement_pulses = value; + _refRequiredTimeMeasurement_pulses = value; } } - private UInt32 _dutTimeMeasurement_pulses; + private UInt32 _dutRequiredTimeMeasurement_pulses; /// - /// DUT pulses for time measurement. + /// Required DUT pulses for time measurement. /// - public UInt32 DutTimeMeasurement_pulses + public UInt32 DutRequiredTimeMeasurement_pulses { - get => _dutTimeMeasurement_pulses; + get => _dutRequiredTimeMeasurement_pulses; private set { // Check limits and equality if (value < TimeMeasurementMin_pulses || value > TimeMeasurementMax_pulses || - value == _dutTimeMeasurement_pulses) + value == _dutRequiredTimeMeasurement_pulses) return; - _dutTimeMeasurement_pulses = value; + _dutRequiredTimeMeasurement_pulses = value; } } + private Int32 _refMeasuredTimer_ticks; + /// + /// REF timer ticks of measured pulses + /// + public Int32 RefMeasuredTimer_ticks + { + get => _refMeasuredTimer_ticks; + private set + { + _refMeasuredTimer_ticks = value; + RefMeasuredTime_s = value * TMR_RESOLUTION_s; + } + } + + private Int32 _dutMeasuredTimer_ticks; + /// + /// DUT timer ticks of measured pulses + /// + public Int32 DutMeasuredTimer_ticks + { + get => _dutMeasuredTimer_ticks; + private set + { + _dutMeasuredTimer_ticks = value; + DutMeasuredTime_s = value * TMR_RESOLUTION_s; + } + } + + /// + /// Remaining REF pulses for time measurement. + /// + public UInt32 RefRemainingTimeMeasurement_pulses { get; private set; } + + /// + /// Remaining DT pulses for time measurement. + /// + public UInt32 DutRemainingTimeMeasurement_pulses { get; private set; } + + /// + /// Measured REF time of required pulses. + /// + public Double RefMeasuredTime_s { get; private set; } + + /// + /// Measured DUT time of required pulses. + /// + public Double DutMeasuredTime_s { get; private set; } + private UInt32 _ref_pulse_per_cm; /// /// Reference pulses per cubic-meter @@ -350,7 +428,11 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// "1000K3" = 100.0 /// Max value is "9999K3" = 999.9 /// - private String _refToDutScaleStr = "1000K+2"; + private String RefToDutScaleStr { get; set; } = "1000K+2"; + + /// + /// REF to DUT scale converted to meaningful human interpretable value + /// private Double _refToDutScale_norm = 10.0f; /// @@ -379,7 +461,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core } while (mantissa < MANTISSA_SCALE_MIN); // The pre-generated string will be used to send it to the FM2014 directly - _refToDutScaleStr = $"{mantissa}{GetCmdStr(CmdName.CMD_REF_SET_SCALE)}{exponent}"; + RefToDutScaleStr = $"{mantissa}{GetCmdStr(CmdName.CMD_REF_SET_SCALE)}{exponent}"; } } @@ -425,40 +507,73 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// public Boolean RequestDebugInformation { get; set; } - /// - /// Number of processing steps for this actual process progress - /// - private static Int32 MaxActualProcessProgress { get; set; } - - /// - /// Progress of actual process converted to percent - /// - private static Double _actualProcessProgress_percent; - - /// - /// Progress of actual process - /// - private static Int32 _actualProcessProgress; - - /// - /// Actual process progress of subroutine - /// - private static Int32 ActualProcessProgress - { - get => _actualProcessProgress; - set - { - _actualProcessProgress = value; - if (MaxActualProcessProgress == 0) - MaxActualProcessProgress = 1; - if (_actualProcessProgress > MaxActualProcessProgress) - _actualProcessProgress = MaxActualProcessProgress; - _actualProcessProgress_percent = 100.0 * _actualProcessProgress / MaxActualProcessProgress; - } - } #endregion ---------------------------------------- object properties ----------------------------------------- #region ------------------------------------------- static methods -------------------------------------------- + /// + /// Reset the measurement but only if receive task is active: + /// - This should prepare the hardware for a new measurement which takes about 4 s. + /// - It is a broadcast command which will reset all connected devices! + /// + /// + /// + /// - Initial. + /// + /// + /// - Delayed reset to allow other tasks to finish. + /// + /// + /// - Support for multiple FM2014s. + /// + /// + /// - Statics. + /// + public static Boolean ResetHardwareAllDevices() + { + if (SharedCyclicMeasSequ == CyclicMeasSequ.IDLE) + return true; + + var firstActiveFm2014 = GetFirstConnectedAndLoggedInFm2014(); + if (firstActiveFm2014 == null) + return false; + + InitActualProcessProgress(); + // Exit tasks + SharedCyclicMeasSequ = CyclicMeasSequ.IDLE; + + // Schedule to other tasks to avoid side effects through the reset command + Task.Factory.StartNew(() => + { + Thread.Sleep(1000); + }).ContinueWith(delegate + { + // Reset measurement + var infoStr = GetCmdInfo(CmdName.CMD_RST_MEAS); + if (!Write(CmdName.CMD_RST_MEAS, firstActiveFm2014)) + return; + + // Wait until FM2014 calibration finished + var resetDelayCtr_ms = 4000; + const Int32 loopTime_ms = 500; + MaxActualProcessProgress = resetDelayCtr_ms / loopTime_ms; + do + { + // Change the SI unit to ms instead of seconds + var response = new CmdResponse(CmdName.CMD_RST_MEAS, infoStr, + resetDelayCtr_ms, siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME)); + PublishResponse(firstActiveFm2014, new ProcessExecEventArgs(infoStr, + actualProcessMessage: infoStr, + actualProcessPercent: _actualProcessProgress_percent, + specificInfoObj: response)); + Thread.Sleep(loopTime_ms); + resetDelayCtr_ms -= loopTime_ms; + ActualProcessProgress++; + } while (resetDelayCtr_ms >= 0); + }, SharedCancellationToken); + + return true; + } + /// /// Executes all StoreConfiguration and StoreCalibration for each FM2014 which is logged on. /// @@ -483,6 +598,20 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core return retVal; } + /// + /// Get the first FM2014 which is connected and logged in for common service routines + /// using a broadcast command + /// + /// + private static FM2014 GetFirstConnectedAndLoggedInFm2014() + { + if (RegisteredFm2014s == null || RegisteredFm2014s.Count == 0 || + RegisteredFm2014s.All(fm2014 => !fm2014.IsLoggedOn)) + return null; + + return RegisteredFm2014s.FirstOrDefault(fm2014 => fm2014.IsLoggedOn); + } + /// /// Common routine to set up the counter for max process progress based on connected /// and logged in devices and set the actual process progress to zero. @@ -501,7 +630,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core return; } - foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn)) + foreach (var unused in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn)) { MaxActualProcessProgress++; } @@ -521,6 +650,256 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core fm2014?.OnRawRecordReceived?.Invoke(fm2014, processExecEventArgs); } + /// + /// The regulation measurement compares the DUT to REF tolerance: + /// Preconditions: + /// - The has to be executed in advance, + /// - The REF pulses per cubic meter has to be preset, + /// - The DUT pulses per cubic meter has to be preset, + /// - The Scale for REF to DUT pulses has to be preset. + /// Initial setup: + /// - Setup double impulse, + /// - Setup damping to attenuate the result, + /// - Setup scale between REF to DUT pulses, + /// - Setup ref pulse counting mode to get REF pulses for flow rate. + /// Cyclic: + /// - Request damped or undamped tolerance, + /// - Request REF pulse counter value and uses to calculate + /// the 'Flow rate'. + /// Exit: + /// - Set to false. + /// + /// + /// + /// - Initial. + /// + /// + /// - Support for multiple FM2014s. + /// + /// + /// - Static. + /// + public static Boolean RegulationMeasurement() + { + // If the cyclic task has already been started everything is fine + if (SharedCyclicMeasSequ == CyclicMeasSequ.REGULATION) + return true; + + // Marked measurement isn't pulse counter measurement, other FM2014s cannot activate this + if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE) + return false; + + var firstActiveFm2014 = GetFirstConnectedAndLoggedInFm2014(); + if (firstActiveFm2014 == null) + return false; + + // Check if regulation can be done + if (firstActiveFm2014.Ref_pulse_per_cm == 0 || + firstActiveFm2014.Dut_pulse_per_cm == 0 || + firstActiveFm2014.RefToDutScale_norm < REF_TO_DUT_SCALE_MIN || + firstActiveFm2014.RefToDutScale_norm > REF_TO_DUT_SCALE_MAX) + { + return false; + } + + + var cmdName = CmdName.CMD_NA; + var measurementInfo = Resources.StrMeasMsgRegulation; + InitActualProcessProgress(3); + // Prepare regulation measurement + try + { + cmdName = CmdName.CMD_SET_DBPL_UNLOCK; + if (!Write(cmdName, firstActiveFm2014)) + { + return false; + } + ActualProcessProgress++; + cmdName = CmdName.CMD_MEAS_SET_ATTN; + if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.Attenuation)) + { + return false; + } + ActualProcessProgress++; + cmdName = CmdName.CMD_REF_SET_SCALE; + if (!Write(cmdName, firstActiveFm2014, firstActiveFm2014.RefToDutScaleStr)) + { + return false; + } + ActualProcessProgress++; + } + catch (Exception e) + { + var response = new CmdResponse(cmdName, e.Message); + PublishResponse(firstActiveFm2014, new ProcessExecEventArgs(Resources.StrError, + specificInfoObj: response, statusReturn: StatusReturn.Failed)); + return false; + } + + // Measurement Loop + Task.Run(() => + { + // Mark regulation measurement as active + SharedCyclicMeasSequ = CyclicMeasSequ.REGULATION; + + do + { + InitActualProcessProgress(); + foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn)) + { + try + { + // Read out the damped/undamped tolerance and convert the raw value (0, +/-1..255 digits) to the + // tolerance in percent depending on the setup to 3 % (3.3) of 5 % (5.5) + String responseStr; + cmdName = fm2014.UseDampedTolerance ? CmdName.CMD_GET_DTLC : CmdName.CMD_GET_UDTLC; + var info = GetCmdInfo(cmdName); + if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) + { + if (Read(cmdName, fm2014, out responseStr)) + { + // The response contains a sign! + var signMultiplier = responseStr.Contains("+") ? 1.0f : -1.0f; + var cleanedStr = responseStr.Replace("+", "").Replace("-", ""); + if (int.TryParse(cleanedStr, NumberStyles.HexNumber, new CultureInfo("en"), + out var measTolerance)) + { + var response = new CmdResponse(cmdName, info, + doubleValue: measTolerance * fm2014.ToleranceRawToPercentScale * signMultiplier, + siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.PERCENTAGE_NON_SI)); + PublishResponse(fm2014, + new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + specificInfoObj: response)); + } + } + } + + // Read the REF period and calculate the frequency [Hz] and flow rate [m³/h] based on this period + cmdName = CmdName.CMD_GET_REF_PERIOD; + info = GetCmdInfo(cmdName); + if (fm2014.RequestDebugInformation && SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && + Write(cmdName, fm2014)) + { + if (Read(cmdName, fm2014, out responseStr) && + int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var period)) + { + // Publish the period [ms] + var response = new CmdResponse(cmdName, info, + doubleValue: TMR_RESOLUTION_s * 1000.0 * period, + siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME)); + PublishResponse(fm2014, + new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + specificInfoObj: response)); + + // Publish the frequency in [Hz] + cmdName = CmdName.CMD_CAL_FREQU_REF_PERIOD; + info = GetCmdInfo(cmdName); + response = new CmdResponse(cmdName, info, + doubleValue: 1.0 / (TMR_RESOLUTION_s * period), + siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY)); + PublishResponse(fm2014, + new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + specificInfoObj: response)); + + // Convert to m³/h (3600 s/h) based on REF pulse rate + fm2014.RefFlowRate_cm_per_h = 3600.0 / (period * TMR_RESOLUTION_s) / fm2014.Ref_pulse_per_cm; + // Publish the calculated flow rate [m³/h] + cmdName = CmdName.CMD_CAL_FLOW_REF_PERIOD; + info = GetCmdInfo(cmdName); + response = new CmdResponse(cmdName, info, doubleValue: fm2014.RefFlowRate_cm_per_h, + siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FLOW_RATE_NON_SI)); + PublishResponse(fm2014, + new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + specificInfoObj: response)); + } + } + + + // Read the DUT period and calculate the frequency [Hz] and flow rate [m³/h] based on this period + cmdName = CmdName.CMD_GET_DUT_PERIOD; + info = GetCmdInfo(cmdName); + if (fm2014.RequestDebugInformation && + SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && + Write(cmdName, fm2014)) + { + if (Read(cmdName, fm2014, out responseStr) && + int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var period)) + { + // Publish the period [ms] + var response = new CmdResponse(cmdName, info, + doubleValue: TMR_RESOLUTION_s * 1000.0 * period, + siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME)); + PublishResponse(fm2014, + new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + specificInfoObj: response)); + + // Publish the frequency in [Hz] + cmdName = CmdName.CMD_CAL_FREQU_DUT_PERIOD; + info = GetCmdInfo(cmdName); + response = new CmdResponse(cmdName, info, + doubleValue: 1.0 / (TMR_RESOLUTION_s * period), + siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY)); + PublishResponse(fm2014, + new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + specificInfoObj: response)); + + // Convert to m³/h (3600 s/h) based on DUT pulse rate + fm2014.DutFlowRate_cm_per_h = 3600.0 / (period * TMR_RESOLUTION_s) / fm2014.Dut_pulse_per_cm; + // Publish the calculated flow rate [m³/h] + cmdName = CmdName.CMD_CAL_FLOW_DUT_PERIOD; + info = GetCmdInfo(cmdName); + response = new CmdResponse(cmdName, info, doubleValue: fm2014.DutFlowRate_cm_per_h, + siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FLOW_RATE_NON_SI)); + PublishResponse(fm2014, + new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + specificInfoObj: response)); + } + } + + + // Read the REF frequency [Hz] directly and calculate the flow rate [m³/h] based on this frequency + cmdName = CmdName.CMD_GET_REF_FREQU; + info = GetCmdInfo(cmdName); + if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) + { + if (Read(cmdName, fm2014, out responseStr) && + int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), + out var refFrequency)) + { + // Publish the frequency in [Hz] + var response = new CmdResponse(cmdName, info, refFrequency, + siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY)); + PublishResponse(fm2014, new ProcessExecEventArgs("", + actualProcessMessage: measurementInfo, + specificInfoObj: response)); + + // Convert to m³/h (3600 s/h) and save the value + fm2014.RefFlowRate_cm_per_h = 3600.0 * refFrequency / fm2014.Ref_pulse_per_cm; + // Publish the calculated flow rate [m³/h] + cmdName = CmdName.CMD_CAL_FLOW_REF_FREQU; + info = GetCmdInfo(cmdName); + response = new CmdResponse(cmdName, info, doubleValue: fm2014.RefFlowRate_cm_per_h, + siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FLOW_RATE_NON_SI)); + PublishResponse(fm2014, + new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, + specificInfoObj: response)); + } + } + } + catch (Exception e) + { + var response = new CmdResponse(cmdName, e.Message); + PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError, + specificInfoObj: response, statusReturn: StatusReturn.Failed)); + } + } + } while (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE); + + }, SharedCancellationToken); + + return true; + } + /// /// Read FM2014: /// - Accesses directly to the 'SerialPort.ReadTo', @@ -998,249 +1377,6 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core return true; } - /// - /// The regulation measurement compares the DUT to REF tolerance: - /// Preconditions: - /// - The has to be executed in advance, - /// - The REF pulses per cubic meter has to be preset, - /// - The DUT pulses per cubic meter has to be preset, - /// - The Scale for REF to DUT pulses has to be preset. - /// Initial setup: - /// - Setup double impulse, - /// - Setup damping to attenuate the result, - /// - Setup scale between REF to DUT pulses, - /// - Setup ref pulse counting mode to get REF pulses for flow rate. - /// Cyclic: - /// - Request damped or undamped tolerance, - /// - Request REF pulse counter value and uses to calculate - /// the 'Flow rate'. - /// Exit: - /// - Set to false. - /// - /// - /// - /// - Initial. - /// - /// - /// - Support for multiple FM2014s. - /// - public Boolean RegulationMeasurement() - { - if (Ref_pulse_per_cm == 0 || - Dut_pulse_per_cm == 0 || - RefToDutScale_norm < REF_TO_DUT_SCALE_MIN || - RefToDutScale_norm > REF_TO_DUT_SCALE_MAX || - !IsLoggedOn) - { - return false; - } - - // If the cyclic task has already been started everything is fine - if (SharedCyclicMeasSequ == CyclicMeasSequ.REGULATION) - return true; - - // Marked measurement isn't pulse counter measurement, other FM2014s cannot activate this - if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE) - return false; - - - var cmdName = CmdName.CMD_NA; - var measurementInfo = Resources.StrMeasMsgRegulation; - InitActualProcessProgress(3); - // Prepare regulation measurement - try - { - cmdName = CmdName.CMD_SET_DBPL_UNLOCK; - if (!Write(cmdName, this)) - { - return false; - } - ActualProcessProgress++; - cmdName = CmdName.CMD_MEAS_SET_ATTN; - if (!Write(cmdName, this, Attenuation)) - { - return false; - } - ActualProcessProgress++; - cmdName = CmdName.CMD_REF_SET_SCALE; - if (!Write(cmdName, this, _refToDutScaleStr)) - { - return false; - } - ActualProcessProgress++; - } - catch (Exception e) - { - var response = new CmdResponse(cmdName, e.Message); - PublishResponse(this, new ProcessExecEventArgs(Resources.StrError, specificInfoObj: response, - statusReturn: StatusReturn.Failed)); - return false; - } - - // Measurement Loop - Task.Run(() => - { - // Mark regulation measurement as active - SharedCyclicMeasSequ = CyclicMeasSequ.REGULATION; - - do - { - InitActualProcessProgress(); - foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn)) - { - try - { - // Read out the damped/undamped tolerance and convert the raw value (0, +/-1..255 digits) to the - // tolerance in percent depending on the setup to 3 % (3.3) of 5 % (5.5) - String responseStr; - cmdName = UseDampedTolerance ? CmdName.CMD_GET_DTLC : CmdName.CMD_GET_UDTLC; - var info = GetCmdInfo(cmdName); - if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) - { - if (Read(cmdName, fm2014, out responseStr)) - { - // The response contains a sign! - var signMultiplier = responseStr.Contains("+") ? 1.0f : -1.0f; - var cleanedStr = responseStr.Replace("+", "").Replace("-", ""); - if (int.TryParse(cleanedStr, NumberStyles.HexNumber, new CultureInfo("en"), - out var measTolerance)) - { - var response = new CmdResponse(cmdName, info, - doubleValue: measTolerance * _toleranceRawToPercentScale * signMultiplier, - siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.PERCENTAGE_NON_SI)); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, - specificInfoObj: response)); - } - } - } - - // Read the REF period and calculate the frequency [Hz] and flow rate [m³/h] based on this period - cmdName = CmdName.CMD_GET_REF_PERIOD; - info = GetCmdInfo(cmdName); - if (RequestDebugInformation && SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && - Write(cmdName, fm2014)) - { - if (Read(cmdName, fm2014, out responseStr) && - int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var period)) - { - // Publish the period [ms] - var response = new CmdResponse(cmdName, info, - doubleValue: TMR_RESOLUTION_s * 1000.0 * period, - siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME)); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, - specificInfoObj: response)); - - // Publish the frequency in [Hz] - cmdName = CmdName.CMD_CAL_FREQU_REF_PERIOD; - info = GetCmdInfo(cmdName); - response = new CmdResponse(cmdName, info, - doubleValue: 1.0 / (TMR_RESOLUTION_s * period), - siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY)); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, - specificInfoObj: response)); - - // Convert to m³/h (3600 s/h) based on REF pulse rate - RefFlowRate_cm_per_h = 3600.0 / (period * TMR_RESOLUTION_s) / Ref_pulse_per_cm; - // Publish the calculated flow rate [m³/h] - cmdName = CmdName.CMD_CAL_FLOW_REF_PERIOD; - info = GetCmdInfo(cmdName); - response = new CmdResponse(cmdName, info, doubleValue: RefFlowRate_cm_per_h, - siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FLOW_RATE_NON_SI)); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, - specificInfoObj: response)); - } - } - - - // Read the DUT period and calculate the frequency [Hz] and flow rate [m³/h] based on this period - cmdName = CmdName.CMD_GET_DUT_PERIOD; - info = GetCmdInfo(cmdName); - if (RequestDebugInformation && - SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && - Write(cmdName, fm2014)) - { - if (Read(cmdName, fm2014, out responseStr) && - int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), out var period)) - { - // Publish the period [ms] - var response = new CmdResponse(cmdName, info, - doubleValue: TMR_RESOLUTION_s * 1000.0 * period, - siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME)); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, - specificInfoObj: response)); - - // Publish the frequency in [Hz] - cmdName = CmdName.CMD_CAL_FREQU_DUT_PERIOD; - info = GetCmdInfo(cmdName); - response = new CmdResponse(cmdName, info, - doubleValue: 1.0 / (TMR_RESOLUTION_s * period), - siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY)); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, - specificInfoObj: response)); - - // Convert to m³/h (3600 s/h) based on DUT pulse rate - DutFlowRate_cm_per_h = 3600.0 / (period * TMR_RESOLUTION_s) / Dut_pulse_per_cm; - // Publish the calculated flow rate [m³/h] - cmdName = CmdName.CMD_CAL_FLOW_DUT_PERIOD; - info = GetCmdInfo(cmdName); - response = new CmdResponse(cmdName, info, doubleValue: DutFlowRate_cm_per_h, - siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FLOW_RATE_NON_SI)); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, - specificInfoObj: response)); - } - } - - - // Read the REF frequency [Hz] directly and calculate the flow rate [m³/h] based on this frequency - cmdName = CmdName.CMD_GET_REF_FREQU; - info = GetCmdInfo(cmdName); - if (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE && Write(cmdName, fm2014)) - { - if (Read(cmdName, fm2014, out responseStr) && - int.TryParse(responseStr, NumberStyles.HexNumber, new CultureInfo("en"), - out var refFrequency)) - { - // Publish the frequency in [Hz] - var response = new CmdResponse(cmdName, info, refFrequency, - siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FREQUENCY)); - PublishResponse(fm2014, new ProcessExecEventArgs("", - actualProcessMessage: measurementInfo, - specificInfoObj: response)); - - // Convert to m³/h (3600 s/h) and save the value - RefFlowRate_cm_per_h = 3600.0 * refFrequency / Ref_pulse_per_cm; - // Publish the calculated flow rate [m³/h] - cmdName = CmdName.CMD_CAL_FLOW_REF_FREQU; - info = GetCmdInfo(cmdName); - response = new CmdResponse(cmdName, info, doubleValue: RefFlowRate_cm_per_h, - siUnit: SiUnits.GetInfo(SiUnits.SiUnitName.FLOW_RATE_NON_SI)); - PublishResponse(fm2014, - new ProcessExecEventArgs("", actualProcessMessage: measurementInfo, - specificInfoObj: response)); - } - } - } - catch (Exception e) - { - var response = new CmdResponse(cmdName, e.Message); - PublishResponse(fm2014, new ProcessExecEventArgs(Resources.StrError, - specificInfoObj: response, statusReturn: StatusReturn.Failed)); - } - } - } while (SharedCyclicMeasSequ != CyclicMeasSequ.IDLE); - - }, SharedCancellationToken); - - return true; - } - /// /// Transfer all standalone settings to FM2014 RAM and safe those to nonvolatile EEPROM /// @@ -1279,7 +1415,7 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core { cmdName = CmdName.CMD_REF_SET_SCALE; // During setup of the 'Ref_pulse_per_cm' the '_refToDutScaleStr' will be generated - retVal = Write(cmdName, this, _refToDutScaleStr); + retVal = Write(cmdName, this, RefToDutScaleStr); } if (retVal) @@ -1454,63 +1590,6 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core return InitiateIndividualComm(this); } - /// - /// Reset the measurement but only if receive task is active: - /// - This should prepare the hardware for a new measurement which takes about 4 s. - /// - It is a broadcast command which will reset all connected devices! - /// - /// - /// - /// - Initial. - /// - /// - /// - Delayed reset to allow other tasks to finish. - /// - /// - /// - Support for multiple FM2014s. - /// - public Boolean ResetHardwareAllDevices() - { - if (SharedCyclicMeasSequ == CyclicMeasSequ.IDLE) - return true; - - InitActualProcessProgress(); - // Exit tasks - SharedCyclicMeasSequ = CyclicMeasSequ.IDLE; - - // Schedule to other tasks to avoid side effects through the reset command - Task.Factory.StartNew(() => - { - Thread.Sleep(1000); - }).ContinueWith(delegate - { - // Reset measurement - var infoStr = GetCmdInfo(CmdName.CMD_RST_MEAS); - if (!Write(CmdName.CMD_RST_MEAS, this)) - return; - - // Wait until FM2014 calibration finished - var resetDelayCtr_ms = 4000; - const Int32 loopTime_ms = 500; - MaxActualProcessProgress = resetDelayCtr_ms / loopTime_ms; - do - { - // Change the SI unit to ms instead of seconds - var response = new CmdResponse(CmdName.CMD_RST_MEAS, infoStr, - resetDelayCtr_ms, siUnit: "m" + SiUnits.GetInfo(SiUnits.SiUnitName.TIME)); - PublishResponse(this, new ProcessExecEventArgs(infoStr, - actualProcessMessage: infoStr, - actualProcessPercent: _actualProcessProgress_percent, - specificInfoObj: response)); - Thread.Sleep(loopTime_ms); - resetDelayCtr_ms -= loopTime_ms; - ActualProcessProgress++; - } while (resetDelayCtr_ms >= 0); - }, SharedCancellationToken); - - return true; - } - /// /// Common routine for reboot being able to override this routine which will be called in /// to simulate a reboot. @@ -1530,9 +1609,18 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core /// as this should be used to prepare the hardware for /// the new measurement after it being able to start the new measurement immediately! /// + /// + /// - Initial. + /// private static void ClearMeasurementResultsForAllDevices() { - + foreach (var fm2014 in RegisteredFm2014s.Where(fm2014 => fm2014.IsLoggedOn)) + { + fm2014.RefMeasuredTimer_ticks = 0; + fm2014.DutMeasuredTimer_ticks = 0; + fm2014.RefFlowRate_cm_per_h = 0; + fm2014.DutFlowRate_cm_per_h = 0; + } } /// @@ -1562,7 +1650,6 @@ namespace Sensus.Common.Hardware.WaterMeter.MechanicalMeter.FM2014.FM2014Core SharedSerialPort.ReadTimeout = 1000; SharedSerialPort.WriteTimeout = 1000; } - var cmdName = CmdName.CMD_SERIAL; try { diff --git a/FM2014TestApp/Ui/FM2014TestBenchWinFrms/FrmFm2014TestApp.cs b/FM2014TestApp/Ui/FM2014TestBenchWinFrms/FrmFm2014TestApp.cs index 41cedd3f..32b15770 100644 --- a/FM2014TestApp/Ui/FM2014TestBenchWinFrms/FrmFm2014TestApp.cs +++ b/FM2014TestApp/Ui/FM2014TestBenchWinFrms/FrmFm2014TestApp.cs @@ -777,7 +777,7 @@ namespace Sensus.Ui.FM2014TestApp { ActionControl(true); _autoProgressBar = true; - if (Fm2014.RegulationMeasurement()) + if (FM2014.RegulationMeasurement()) { btnDutToRefRegulation.Text = Resources.StrBtnStopRegulation; btnDutToRefRegulation.Enabled = true; @@ -788,7 +788,7 @@ namespace Sensus.Ui.FM2014TestApp _autoProgressBar = false; // Deactivate the button temporary to avoid repeated execution as the reset takes a certain time btnDutToRefRegulation.Enabled = false; - Fm2014.ResetHardwareAllDevices(); + FM2014.ResetHardwareAllDevices(); ActionControl(false); btnDutToRefRegulation.Text = Resources.StrBtnStartRegulation; } @@ -839,7 +839,7 @@ namespace Sensus.Ui.FM2014TestApp _autoProgressBar = false; // Deactivate the button temporary to avoid repeated execution as the reset takes a certain time btnManualRefCalibration.Enabled = false; - Fm2014.ResetHardwareAllDevices(); + FM2014.ResetHardwareAllDevices(); ActionControl(false); btnManualRefCalibration.Text = Resources.StrBtnStartCalibration; } diff --git a/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml b/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml index 91384c38..24f19b15 100644 --- a/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml +++ b/FM2014TestApp/Ui/Fm2014sTest/FM2014TestBenchWindow.xaml @@ -15,15 +15,15 @@ - - + + - + - + -