2015-07-31 08:45:50 +00:00
|
|
|
|
///
|
2017-03-09 12:12:32 +00:00
|
|
|
|
/// Copyright (c) 2015-2017 Sensus Metering Systems
|
2015-08-14 10:00:18 +00:00
|
|
|
|
/// Author: Milan Hanajík
|
2015-07-31 08:45:50 +00:00
|
|
|
|
///
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
2015-08-21 14:06:38 +00:00
|
|
|
|
using System.IO;
|
2015-07-31 17:45:10 +00:00
|
|
|
|
using System.IO.Ports;
|
|
|
|
|
|
using System.Threading;
|
2016-01-08 15:57:37 +00:00
|
|
|
|
using System.Windows.Forms;
|
2015-07-31 08:45:50 +00:00
|
|
|
|
using log4net;
|
2015-12-04 16:17:20 +00:00
|
|
|
|
using Config.Entities;
|
2015-07-31 08:45:50 +00:00
|
|
|
|
using TBF.BenchControl;
|
2015-07-31 17:45:10 +00:00
|
|
|
|
using TBF.BenchControl.Generic;
|
2016-05-28 11:40:58 +00:00
|
|
|
|
using TBF.Resources;
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
|
|
|
|
|
namespace TBF.BenchControl.WaterMeters.iPerl
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This component = instance of this class is a placeholder for a combined main watermeter
|
|
|
|
|
|
/// </summary>
|
2015-07-31 17:45:10 +00:00
|
|
|
|
public class WaterMeter : ComponentBase, IDevice, GenericDevices.IWaterMeter, GenericDevices.IRegisterReader, IOperation
|
2015-07-31 08:45:50 +00:00
|
|
|
|
{
|
|
|
|
|
|
private static readonly ILog log = LogManager.GetLogger(typeof(WaterMeter));
|
2015-08-15 21:01:26 +00:00
|
|
|
|
public override string ToString() { return string.Format("iPerl({0})", Cfg.ToString(1)); }
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
2015-08-19 11:04:47 +00:00
|
|
|
|
|
2015-07-31 08:45:50 +00:00
|
|
|
|
readonly WaterMeterCfg iPerlCfg;
|
|
|
|
|
|
|
2016-04-18 11:38:13 +00:00
|
|
|
|
public int RfidComPortNr { get { return iPerlCfg.RfidComPortNr; } }
|
2015-08-21 18:52:10 +00:00
|
|
|
|
public int MuxBoardNr { get { return iPerlCfg.MuxBoardNr; } }
|
2015-08-01 19:20:22 +00:00
|
|
|
|
public int Group { get { return iPerlCfg.Group; } }
|
2015-07-31 17:45:10 +00:00
|
|
|
|
|
2016-02-13 07:18:49 +00:00
|
|
|
|
public double PulsesPerLtr { get { return (double)iPerlCfg.ProcParams.PulsesPerLtr; } }
|
|
|
|
|
|
public double LtrsPerPulse { get { return ltrsPerPulse; } }
|
|
|
|
|
|
readonly double ltrsPerPulse;
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
2016-05-02 10:55:17 +00:00
|
|
|
|
/// <summary>WaterMeter type (product name) and producer</summary>
|
|
|
|
|
|
public string ProductName { get { return iPerlCfg.ProcParams.ProductName; } }
|
2015-07-31 08:45:50 +00:00
|
|
|
|
public string Producer { get { return iPerlCfg.ProcParams.Producer; } }
|
|
|
|
|
|
|
2016-05-02 10:55:17 +00:00
|
|
|
|
/// <summary>Pipe diameter [mm]</summary>
|
|
|
|
|
|
public float DN { get { return iPerlCfg.ProcParams.DN; } }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>Build lenght [mm]</summary>
|
|
|
|
|
|
public float L { get { return iPerlCfg.ProcParams.L; } }
|
|
|
|
|
|
|
|
|
|
|
|
public Mounting Mounting { get { return iPerlCfg.ProcParams.Mounting; } }
|
|
|
|
|
|
|
2015-07-31 08:45:50 +00:00
|
|
|
|
/// <summary>Nominal water flow in [m3/h]</summary>
|
2016-05-02 10:55:17 +00:00
|
|
|
|
public float Q4_Qmax { get { return iPerlCfg.ProcParams.Q4; } }
|
|
|
|
|
|
public float Q3_Qn { get { return iPerlCfg.ProcParams.Qn; } }
|
2016-05-01 08:04:05 +00:00
|
|
|
|
public float Q2_Qt { get { return iPerlCfg.ProcParams.Q2; } }
|
|
|
|
|
|
public float Q1_Qmin { get { return iPerlCfg.ProcParams.Q1; } }
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
2016-05-02 10:55:17 +00:00
|
|
|
|
/// <summary>Metrological class, etc</summary>
|
|
|
|
|
|
public string MetrologicalClass { get { return iPerlCfg.ProcParams.MetrologicalClass; } }
|
|
|
|
|
|
public TemperatureClass TemperatureClass { get { return iPerlCfg.ProcParams.TemperatureClass; } }
|
|
|
|
|
|
public PressureLossClass PressureLossClass { get { return iPerlCfg.ProcParams.PressureLossClass; } }
|
|
|
|
|
|
public MaxAdmissiblePressure MaxAdmissiblePressure { get { return iPerlCfg.ProcParams.MaxAdmissiblePressure; } }
|
|
|
|
|
|
public FlowProfileSensitivityClass FlowProfileSensitivityClass { get { return iPerlCfg.ProcParams.FlowProfileSensitivityClass; } }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>WaterMeter approval information or signature</summary>
|
|
|
|
|
|
public string ApprovalInfo { get { return iPerlCfg.ProcParams.ApprovalInfo; } }
|
|
|
|
|
|
public string Certificate { get { return iPerlCfg.ProcParams.Certificate; } }
|
|
|
|
|
|
|
|
|
|
|
|
public string Text1 { get { return iPerlCfg.ProcParams.Text1; } }
|
|
|
|
|
|
public string Text2 { get { return iPerlCfg.ProcParams.Text2; } }
|
|
|
|
|
|
public string Text3 { get { return iPerlCfg.ProcParams.Text3; } }
|
|
|
|
|
|
public string Text4 { get { return iPerlCfg.ProcParams.Text4; } }
|
|
|
|
|
|
public string Text5 { get { return iPerlCfg.ProcParams.Text5; } }
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
2017-03-09 12:12:32 +00:00
|
|
|
|
#if ORACLE_DB
|
2015-11-10 22:29:14 +00:00
|
|
|
|
/// <summary>WM Type ID for Oracle DB</summary>
|
|
|
|
|
|
public int WMType_ID { get { return iPerlCfg.ProcParams.WMType_ID; } }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>WM Type Revision for Oracle DB</summary>
|
|
|
|
|
|
public int WMType_Rev { get { return iPerlCfg.ProcParams.WMType_Rev; } }
|
2017-03-09 12:12:32 +00:00
|
|
|
|
#endif
|
2015-11-10 22:29:14 +00:00
|
|
|
|
|
2017-03-09 12:12:32 +00:00
|
|
|
|
/// Properties set by the Begin and the End form
|
2015-07-31 08:45:50 +00:00
|
|
|
|
public string SerialNr
|
|
|
|
|
|
{
|
2015-08-20 03:06:23 +00:00
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ConfigStruct != null) return configStruct.PCBNumber2String();
|
|
|
|
|
|
else return string.Empty;
|
|
|
|
|
|
}
|
2015-12-04 11:24:55 +00:00
|
|
|
|
set { }
|
2015-07-31 08:45:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public string EndState
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return endState; }
|
|
|
|
|
|
set { endState = value; }
|
|
|
|
|
|
}
|
|
|
|
|
|
string endState;
|
|
|
|
|
|
|
|
|
|
|
|
public string BeginState
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return beginState; }
|
|
|
|
|
|
set { beginState = value; }
|
|
|
|
|
|
}
|
|
|
|
|
|
string beginState;
|
|
|
|
|
|
|
|
|
|
|
|
public bool Disabled
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return disabled; }
|
|
|
|
|
|
set { disabled = value; }
|
|
|
|
|
|
}
|
|
|
|
|
|
bool disabled;
|
|
|
|
|
|
|
2015-09-11 12:15:32 +00:00
|
|
|
|
public bool CommFailed
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return commFailed; }
|
|
|
|
|
|
set { commFailed = value; }
|
|
|
|
|
|
}
|
|
|
|
|
|
bool commFailed;
|
|
|
|
|
|
|
2015-11-13 00:11:36 +00:00
|
|
|
|
public bool PositiveCounting;
|
|
|
|
|
|
|
2015-08-14 17:20:47 +00:00
|
|
|
|
/// <summary> ConfigStruct of the water meter obtained or updated by iPerlCommunication </summary>
|
2015-08-15 21:01:26 +00:00
|
|
|
|
public ConfigStruct ConfigStruct
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return configStruct; }
|
|
|
|
|
|
set { configStruct = value; }
|
|
|
|
|
|
}
|
|
|
|
|
|
ConfigStruct configStruct;
|
2015-08-14 09:38:53 +00:00
|
|
|
|
|
2015-08-14 17:20:47 +00:00
|
|
|
|
/// <summary> CalibrationStruct of the water meter obtained or updated by iPerlCommunication </summary>
|
2015-08-15 21:01:26 +00:00
|
|
|
|
public CalibrationStruct CalibrationStruct
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return calibrationStruct; }
|
2015-08-16 20:00:02 +00:00
|
|
|
|
set { calibrationStruct = value; }
|
2015-08-15 21:01:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
CalibrationStruct calibrationStruct;
|
2015-08-14 09:38:53 +00:00
|
|
|
|
|
2015-11-10 22:29:14 +00:00
|
|
|
|
public ushort OriginalCalibFactor;
|
2015-12-04 11:24:55 +00:00
|
|
|
|
public ushort CalibrationFactor { get { return (CalibrationStruct != null) ? CalibrationStruct.Calibration : (ushort)0; } }
|
2015-08-14 17:20:47 +00:00
|
|
|
|
|
2015-11-10 22:29:14 +00:00
|
|
|
|
public double Q2ErrorWOCorrection;
|
|
|
|
|
|
public bool Q2CorrectionDone;
|
2016-02-16 13:44:10 +00:00
|
|
|
|
public double Q2CorrectionFactor;
|
2015-08-26 16:59:38 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
public double Diff2Hz8Hz;
|
|
|
|
|
|
public bool Hz2CorrectionDone;
|
|
|
|
|
|
public int Hz2CorrectionFactor;
|
2015-08-14 12:33:36 +00:00
|
|
|
|
|
2015-08-22 04:32:55 +00:00
|
|
|
|
/// <summary> Result of the last test used to calculate Q2 correction factors, etc </summary>
|
2016-02-16 13:44:10 +00:00
|
|
|
|
public Results.Entities.MeterTestRslt LastTestResult2;
|
2016-01-03 01:05:35 +00:00
|
|
|
|
public Results.Entities.MeterTestRslt LastTestResult;
|
2015-08-22 04:32:55 +00:00
|
|
|
|
public double NominalTestFlow; /// liter per hour
|
|
|
|
|
|
|
2015-08-24 11:52:06 +00:00
|
|
|
|
|
2016-02-13 07:18:49 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// Required for IRegisterReader interface
|
|
|
|
|
|
///
|
|
|
|
|
|
public int WMPulses { get { return wmPulses; } }
|
|
|
|
|
|
public int WMRefPulses { get { return wmRefPulses; } }
|
|
|
|
|
|
public double WMVolume { get { return wmVolume; } }
|
2017-03-10 13:31:08 +00:00
|
|
|
|
public double BeginWMState { get { return beginWMState; } }
|
|
|
|
|
|
public double EndWMState { get { return endWMState; } }
|
2016-02-13 07:18:49 +00:00
|
|
|
|
public double WMTestTime { get { return wmTestTime; } }
|
|
|
|
|
|
|
2017-03-10 13:31:08 +00:00
|
|
|
|
|
|
|
|
|
|
double beginWMState;
|
|
|
|
|
|
double endWMState;
|
2016-02-13 07:18:49 +00:00
|
|
|
|
double wmVolume;
|
|
|
|
|
|
int wmPulses;
|
|
|
|
|
|
int wmRefPulses;
|
|
|
|
|
|
double wmTestTime;
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-08-22 04:32:55 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// New calibration factor calculated from the original factor (argument)
|
|
|
|
|
|
/// and results of the last test.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="originalCalibrationFactor">Original calibration factor</param>
|
|
|
|
|
|
/// <returns>New calibration factor</returns>
|
2015-10-16 09:24:03 +00:00
|
|
|
|
public UInt16 CalculateNewCalibFactor(UInt16 originalCalibrationFactor, UInt16 FactorLimitLo, UInt16 FactorLimitHi)
|
2015-08-14 12:33:36 +00:00
|
|
|
|
{
|
2015-11-10 22:29:14 +00:00
|
|
|
|
OriginalCalibFactor = originalCalibrationFactor;
|
|
|
|
|
|
|
2015-08-20 14:23:22 +00:00
|
|
|
|
double volumeMeter = Math.Abs(VolumeLtrEnd - VolumeLtrStart);
|
2015-11-13 00:11:36 +00:00
|
|
|
|
|
2015-08-19 19:12:10 +00:00
|
|
|
|
if (volumeMeter > 1E-2)
|
2015-08-14 12:33:36 +00:00
|
|
|
|
{
|
2015-11-13 00:11:36 +00:00
|
|
|
|
PositiveCounting = VolumeLtrEnd > VolumeLtrStart;
|
|
|
|
|
|
|
2015-08-19 19:12:10 +00:00
|
|
|
|
UInt16 newFactor = (UInt16)((double)originalCalibrationFactor * VolumeLtrRef / volumeMeter + 0.5);
|
2015-08-26 16:18:23 +00:00
|
|
|
|
log.InfoFormat("Calibration factor: orig={0} new={1} V_1={2} V_2={3} Vdiff={4} Vref={5}",
|
2015-08-19 19:12:10 +00:00
|
|
|
|
originalCalibrationFactor, newFactor,
|
|
|
|
|
|
VolumeLtrStart.ToString("F3"), VolumeLtrEnd.ToString("F3"), volumeMeter.ToString("F3"),
|
|
|
|
|
|
VolumeLtrRef.ToString("F3"));
|
2015-08-26 15:57:49 +00:00
|
|
|
|
|
2015-10-16 09:24:03 +00:00
|
|
|
|
if (newFactor < FactorLimitLo) newFactor = FactorLimitLo;
|
|
|
|
|
|
if (newFactor > FactorLimitHi) newFactor = FactorLimitHi;
|
2015-08-26 15:57:49 +00:00
|
|
|
|
|
|
|
|
|
|
return newFactor;
|
2015-08-19 19:12:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2015-08-26 16:18:23 +00:00
|
|
|
|
log.ErrorFormat("Calibration factor: orig={0} new={0} (unchanged!) Vdiff={1}",
|
2015-08-19 19:12:10 +00:00
|
|
|
|
originalCalibrationFactor, volumeMeter.ToString("F3"));
|
|
|
|
|
|
return originalCalibrationFactor; /// Too small volume in the denominator -> no correction at all
|
2015-08-14 12:33:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-08-24 11:52:06 +00:00
|
|
|
|
|
2015-08-22 04:32:55 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Q2 correction factor calculated from the last test (Q2).
|
|
|
|
|
|
/// This factors should be used only for R800 meters.
|
|
|
|
|
|
/// </summary>
|
2016-02-16 13:44:10 +00:00
|
|
|
|
/// <param name="q2TestResult">Test result from which to calculate the factor</param>
|
|
|
|
|
|
/// <param name="q2CorrectionFactor">The calculated Q2 correction factor</param>
|
|
|
|
|
|
/// <returns>true = OK, false = failed</returns>
|
|
|
|
|
|
public bool CalculateQ2CorrectionFactor(Results.Entities.MeterTestRslt q2TestResult, out double q2CorrectionFactor)
|
2015-08-22 04:32:55 +00:00
|
|
|
|
{
|
2016-02-16 13:44:10 +00:00
|
|
|
|
q2CorrectionFactor = 0;
|
2015-11-10 22:29:14 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
if (q2TestResult == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false; /// Q2 test result is missing ==> water meter failed
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (Math.Abs(q2TestResult.Error) <= 0.5)
|
2015-11-10 22:29:14 +00:00
|
|
|
|
{
|
2016-02-16 13:44:10 +00:00
|
|
|
|
return true; /// error < +/-0.5 % ==> no Q2 correction
|
2015-11-10 22:29:14 +00:00
|
|
|
|
}
|
2015-08-22 11:34:10 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
double A = 16.0 / ScalingFactor(); /// Raw units per ml: DN15=16, DN20=8, DN25=4, DN32=2, DN40=1
|
|
|
|
|
|
const double B = 8.0; /// Raw units per minute, 8
|
|
|
|
|
|
const double C = B * 60.0; /// Raw units per hour, 480
|
|
|
|
|
|
double D = C / A; /// ml correction per hour
|
|
|
|
|
|
double F = D / (NominalTestFlow * 10.0); /// Error corrected with 8 Raw Units per minute [%]
|
|
|
|
|
|
double G = F / B; /// Error corrected with 1 Raw Unit per minute [%]
|
2015-08-22 04:32:55 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
double errLimitLo = q2TestResult.ErrLimLo() + q2TestResult.Uncertainty();
|
|
|
|
|
|
double errLimitHi = q2TestResult.ErrLimHi() - q2TestResult.Uncertainty();
|
2015-08-26 16:18:23 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
if ((q2TestResult.Error < errLimitLo) || (q2TestResult.Error > errLimitHi))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false; /// Q2 error too large ==> water meter failed
|
|
|
|
|
|
}
|
2015-10-23 14:10:28 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
double volumeMeterErrLimLo = q2TestResult.VolumeRef * (100.0 + errLimitLo) / 100.0;
|
|
|
|
|
|
double volumeMeterErrLimHi = q2TestResult.VolumeRef * (100.0 + errLimitHi) / 100.0;
|
2015-08-26 16:18:23 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
double corrFactorHi = (-1) * (errLimitLo / G) * (q2TestResult.VolumeRef / volumeMeterErrLimLo); /// > 0
|
|
|
|
|
|
double corrFactorLo = (-1) * (errLimitHi / G) * (q2TestResult.VolumeRef / volumeMeterErrLimHi); /// < 0
|
2015-08-26 16:18:23 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
q2CorrectionFactor = (-1) * (q2TestResult.Error / G) * (q2TestResult.VolumeRef / q2TestResult.VolumeMeter);
|
2015-08-26 16:18:23 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
log.WarnFormat("Q2 correction: Pos={0}, PCB#={1}, corrFactor={2}, error={3}% [Lo={4}%, Hi={5}%]",
|
2015-08-27 05:03:42 +00:00
|
|
|
|
Name,
|
2016-02-16 13:44:10 +00:00
|
|
|
|
SerialNr,
|
|
|
|
|
|
q2CorrectionFactor.ToString("F1"),
|
|
|
|
|
|
q2TestResult.Error.ToString("F2"),
|
2015-08-27 05:03:42 +00:00
|
|
|
|
errLimitLo.ToString("F1"),
|
2016-02-16 13:44:10 +00:00
|
|
|
|
errLimitHi.ToString("F1"));
|
2015-08-27 05:03:42 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
return true;
|
2015-08-22 04:32:55 +00:00
|
|
|
|
}
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
2016-02-15 14:16:44 +00:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 2 Hz correction factor calculated from two Q3 tests - done at 2Hz and at 8Hz.
|
|
|
|
|
|
/// This factors should be used only for DN32 and DN40 meters.
|
|
|
|
|
|
/// </summary>
|
2016-02-16 13:44:10 +00:00
|
|
|
|
/// <param name="resultAt2Hz">Test result @2Hz from which to calculate the factor</param>
|
|
|
|
|
|
/// <param name="resultAt8Hz">Test result @8Hz from which to calculate the factor</param>
|
|
|
|
|
|
/// <param name="hz2CorrectionFactor">The calculated Q2 correction factor</param>
|
|
|
|
|
|
/// <returns>true = OK, false = failed</returns>
|
|
|
|
|
|
public bool Calculate2HzCorrectionFactor(Results.Entities.MeterTestRslt resultAt2Hz,
|
|
|
|
|
|
Results.Entities.MeterTestRslt resultAt8Hz,
|
|
|
|
|
|
out double diff2Hz8Hz, out int hz2CorrectionFactor)
|
2016-02-15 14:16:44 +00:00
|
|
|
|
{
|
2016-02-16 13:44:10 +00:00
|
|
|
|
hz2CorrectionFactor = 0;
|
|
|
|
|
|
diff2Hz8Hz = 0;
|
2016-02-15 14:16:44 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
if ((resultAt2Hz == null) || (resultAt8Hz == null))
|
2016-02-15 14:16:44 +00:00
|
|
|
|
{
|
2016-02-16 13:44:10 +00:00
|
|
|
|
return false; /// Test result @2Hz and/or @8Hz is missing ==> water meter failed
|
2016-02-15 14:16:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
diff2Hz8Hz = resultAt2Hz.Error - resultAt8Hz.Error;
|
2016-02-15 14:16:44 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
if (Math.Abs(diff2Hz8Hz) > 2.5) return false; /// Difference of errors > 2.5 % ==> water meter failed
|
2016-02-15 14:16:44 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
hz2CorrectionFactor = -1 * (int)Math.Round(10 * diff2Hz8Hz);
|
2016-02-15 14:16:44 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
log.WarnFormat("2Hz correction: Pos={0}, PCB#={1}, corrFactor={2}, erro@2Hz={3}%, erro@8Hz={4}%",
|
2016-02-15 14:16:44 +00:00
|
|
|
|
Name,
|
2016-02-16 13:44:10 +00:00
|
|
|
|
SerialNr,
|
|
|
|
|
|
hz2CorrectionFactor,
|
|
|
|
|
|
resultAt2Hz.Error.ToString("F2"),
|
|
|
|
|
|
resultAt8Hz.Error.ToString("F2"));
|
2016-02-15 14:16:44 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
return true;
|
2016-02-15 14:16:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
|
2015-12-08 09:48:06 +00:00
|
|
|
|
/// <summary> Name set by the test, to be used as a part of the opto-data log file name </summary>
|
2015-08-22 11:34:10 +00:00
|
|
|
|
public string TestName;
|
|
|
|
|
|
|
2015-12-08 09:48:06 +00:00
|
|
|
|
/// <summary> Name set by the test, to be used as a part of the opto-data log file name </summary>
|
2015-08-22 11:34:10 +00:00
|
|
|
|
public string BenchName;
|
2015-08-19 11:04:47 +00:00
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Volume of water from the opto telegram
|
|
|
|
|
|
///
|
|
|
|
|
|
bool lastVolumeRawValid; /// true = valid
|
|
|
|
|
|
private Int32 lastVolumeRaw; /// Last read raw volume
|
|
|
|
|
|
private double volumeLtr; ///
|
|
|
|
|
|
private double volumeLtr0;
|
|
|
|
|
|
|
2015-08-19 19:12:10 +00:00
|
|
|
|
public double VolumeLtrStart; /// Test start volume for metrology
|
|
|
|
|
|
public double VolumeLtrEnd; /// Test end volume for metrology
|
|
|
|
|
|
public double VolumeLtrRef; /// Reference volume or metrology
|
|
|
|
|
|
double volumeLtrEnd1; /// auxiliary buffer1 to keep the end volume before test stops
|
2015-08-22 18:37:43 +00:00
|
|
|
|
double volumeLtrEnd2; /// auxiliary buffer2 to keep the end volume before test stops
|
|
|
|
|
|
double volumeLtrEnd3; /// auxiliary buffer3 to keep the end volume before test stops
|
2015-08-19 11:04:47 +00:00
|
|
|
|
|
2015-08-22 18:37:43 +00:00
|
|
|
|
///
|
2015-08-19 11:04:47 +00:00
|
|
|
|
/// Timestamp from the opto telegram
|
|
|
|
|
|
///
|
|
|
|
|
|
bool lastTimestampValid;
|
|
|
|
|
|
private Int64 lastTimestamp;
|
|
|
|
|
|
private double timestampSec;
|
|
|
|
|
|
private double timestampSec0;
|
|
|
|
|
|
|
2016-01-08 18:18:45 +00:00
|
|
|
|
public bool NoSamples { get { return (timestampSecEnd - timestampSecStart) < float.Epsilon; } }
|
|
|
|
|
|
public double TimestampSecStart { get { return timestampSecStart; } }
|
|
|
|
|
|
public double TimestampSecEnd { get { return timestampSecEnd; } }
|
|
|
|
|
|
double timestampSecStart;
|
|
|
|
|
|
double timestampSecEnd;
|
2015-08-19 11:04:47 +00:00
|
|
|
|
double timestampSecEnd1;
|
|
|
|
|
|
double timestampSecEnd2;
|
2015-08-22 18:37:43 +00:00
|
|
|
|
double timestampSecEnd3;
|
2015-08-24 11:52:06 +00:00
|
|
|
|
|
2015-12-17 11:29:39 +00:00
|
|
|
|
private int telegramIx;
|
|
|
|
|
|
public int TestStartTelegramIx;
|
|
|
|
|
|
public int TestEndTelegramIx;
|
2015-08-15 21:01:26 +00:00
|
|
|
|
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
2015-08-21 14:06:38 +00:00
|
|
|
|
OptoTelegramRaw[] optoData;
|
|
|
|
|
|
const int MaxOptoDataCount = 40000;
|
|
|
|
|
|
int optoDataCount;
|
|
|
|
|
|
string optoDataLogFileName;
|
|
|
|
|
|
|
2015-09-21 14:29:12 +00:00
|
|
|
|
|
|
|
|
|
|
OptoTelegramRaw toBeFlushed;
|
|
|
|
|
|
int flushedDataCount;
|
|
|
|
|
|
public int FlushedDataCount
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return flushedDataCount; }
|
2016-10-27 12:27:37 +00:00
|
|
|
|
set { flushedDataCount = lastFlushedDataCount = lastFlushedDataCount_1 = value; }
|
2015-09-21 14:29:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-10-27 12:27:37 +00:00
|
|
|
|
int lastFlushedDataCount_1;
|
2016-06-27 13:24:10 +00:00
|
|
|
|
int lastFlushedDataCount;
|
|
|
|
|
|
public int FlushedDataDelta
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
2016-10-27 12:27:37 +00:00
|
|
|
|
int retval = Math.Max(flushedDataCount - lastFlushedDataCount, lastFlushedDataCount - lastFlushedDataCount_1);
|
|
|
|
|
|
lastFlushedDataCount_1 = lastFlushedDataCount;
|
2016-06-27 13:24:10 +00:00
|
|
|
|
lastFlushedDataCount = flushedDataCount;
|
|
|
|
|
|
return retval;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-09-21 14:29:12 +00:00
|
|
|
|
|
2015-08-01 19:20:22 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// Opto serial port and worker thread related private variables
|
|
|
|
|
|
///
|
|
|
|
|
|
private SerialPort optoSerialPort;
|
2015-08-15 21:01:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public WaterMeter()
|
2015-07-31 08:45:50 +00:00
|
|
|
|
{
|
2015-12-04 11:24:55 +00:00
|
|
|
|
ClearData();
|
2015-07-31 08:45:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-08-05 12:32:36 +00:00
|
|
|
|
public WaterMeter(Generic.IComponentCfg cfg)
|
2015-07-31 08:45:50 +00:00
|
|
|
|
: base(cfg)
|
|
|
|
|
|
{
|
2015-12-04 11:24:55 +00:00
|
|
|
|
ClearData();
|
2015-09-21 14:29:12 +00:00
|
|
|
|
|
2016-08-05 12:32:36 +00:00
|
|
|
|
iPerlCfg = cfg as WaterMeterCfg;
|
|
|
|
|
|
ltrsPerPulse = (iPerlCfg.ProcParams.PulsesPerLtr <= float.Epsilon) ? 0 : (1 / iPerlCfg.ProcParams.PulsesPerLtr);
|
2016-02-13 07:18:49 +00:00
|
|
|
|
|
2015-07-31 08:45:50 +00:00
|
|
|
|
log.Debug(this.ToString());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-12-04 11:24:55 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Clear data related to a specific water meter
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void ClearData()
|
|
|
|
|
|
{
|
|
|
|
|
|
disabled = false;
|
|
|
|
|
|
commFailed = false;
|
|
|
|
|
|
endState = string.Empty;
|
|
|
|
|
|
beginState = string.Empty;
|
|
|
|
|
|
|
|
|
|
|
|
configStruct = null;
|
|
|
|
|
|
calibrationStruct = null;
|
|
|
|
|
|
|
|
|
|
|
|
LastTestResult = null;
|
|
|
|
|
|
NominalTestFlow = 0;
|
|
|
|
|
|
|
|
|
|
|
|
OriginalCalibFactor = 0;
|
|
|
|
|
|
Q2ErrorWOCorrection = 0;
|
|
|
|
|
|
Q2CorrectionDone = false;
|
2016-02-16 13:44:10 +00:00
|
|
|
|
Q2CorrectionFactor = 0;
|
2015-12-04 11:24:55 +00:00
|
|
|
|
|
|
|
|
|
|
optoDataCount = 0;
|
|
|
|
|
|
}
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
2015-07-31 17:45:10 +00:00
|
|
|
|
public void Initialize()
|
|
|
|
|
|
{
|
2015-12-04 11:24:55 +00:00
|
|
|
|
ClearData();
|
|
|
|
|
|
|
2015-12-04 16:17:20 +00:00
|
|
|
|
if (DebugLevel == DebugMode.Normal)
|
2015-08-01 19:20:22 +00:00
|
|
|
|
{
|
2015-09-21 14:29:12 +00:00
|
|
|
|
optoSerialPortParsingEnabled = false;
|
2015-08-21 14:06:38 +00:00
|
|
|
|
|
2015-08-21 18:52:10 +00:00
|
|
|
|
/// Allocate memory for opto-data from iPerl
|
2015-08-21 14:06:38 +00:00
|
|
|
|
optoData = new OptoTelegramRaw[MaxOptoDataCount];
|
2015-08-21 18:52:10 +00:00
|
|
|
|
for (int i = 0; i < MaxOptoDataCount; i++) optoData[i] = new OptoTelegramRaw();
|
2015-08-15 21:01:26 +00:00
|
|
|
|
|
2015-09-21 14:29:12 +00:00
|
|
|
|
toBeFlushed = new OptoTelegramRaw();
|
|
|
|
|
|
flushedDataCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
synchronized = false;
|
|
|
|
|
|
synchronized2 = false;
|
|
|
|
|
|
partOfTelegram = string.Empty;
|
|
|
|
|
|
|
2015-08-19 11:04:47 +00:00
|
|
|
|
/// Prepare serial port
|
2015-08-01 19:20:22 +00:00
|
|
|
|
optoSerialPort = new SerialPort(string.Format("COM{0}", iPerlCfg.OptoComPortNr),
|
2015-08-19 11:04:47 +00:00
|
|
|
|
9600, Parity.None, 8, StopBits.One);
|
2015-08-01 19:20:22 +00:00
|
|
|
|
optoSerialPort.Handshake = Handshake.None;
|
|
|
|
|
|
|
2015-09-21 14:29:12 +00:00
|
|
|
|
optoSerialPort.Open();
|
2015-08-01 19:20:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void RunDeviceBefore()
|
|
|
|
|
|
{
|
2015-12-04 16:17:20 +00:00
|
|
|
|
if (DebugLevel == DebugMode.Normal)
|
2015-08-22 14:25:35 +00:00
|
|
|
|
{
|
2015-08-24 11:52:06 +00:00
|
|
|
|
try
|
2015-08-22 14:25:35 +00:00
|
|
|
|
{
|
2015-08-24 11:52:06 +00:00
|
|
|
|
if (optoSerialPortParsingEnabled)
|
2017-02-06 17:34:32 +00:00
|
|
|
|
ReadOptoData(OptoState.Read);
|
2015-08-24 11:52:06 +00:00
|
|
|
|
else
|
2017-02-06 17:34:32 +00:00
|
|
|
|
ReadOptoData(OptoState.Flush);
|
2015-08-22 14:25:35 +00:00
|
|
|
|
}
|
2015-08-24 11:52:06 +00:00
|
|
|
|
catch (Exception e)
|
2015-08-22 14:25:35 +00:00
|
|
|
|
{
|
2015-12-04 16:17:20 +00:00
|
|
|
|
DebugLevel = DebugMode.FailureDuringOperation;
|
2015-08-24 11:52:06 +00:00
|
|
|
|
|
2015-09-22 09:28:48 +00:00
|
|
|
|
log.FatalFormat("Opto-data serial port failure : {0}", e.Message);
|
2015-08-24 11:52:06 +00:00
|
|
|
|
if (e.InnerException != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
log.FatalFormat("InnerMessage : {0}", e.InnerException.Message);
|
|
|
|
|
|
}
|
2015-08-22 14:25:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2015-12-04 16:17:20 +00:00
|
|
|
|
else if (DebugLevel == DebugMode.FailureDuringOperation)
|
2015-08-24 11:52:06 +00:00
|
|
|
|
{
|
|
|
|
|
|
}
|
2015-08-15 21:01:26 +00:00
|
|
|
|
}
|
2015-08-01 19:20:22 +00:00
|
|
|
|
|
|
|
|
|
|
public void RunDeviceAfter()
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void StopDevice()
|
|
|
|
|
|
{
|
2017-03-10 12:42:33 +00:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (DebugLevel == DebugMode.Normal && optoSerialPort != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
optoSerialPort.Close();
|
|
|
|
|
|
optoSerialPort = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
2015-08-15 21:01:26 +00:00
|
|
|
|
}
|
2015-07-31 17:45:10 +00:00
|
|
|
|
|
2015-07-31 08:45:50 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Events: Event.ReadRegisterDone, Event.Error
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>ReadWaterMeter instance reference casted to IOperaton</returns>
|
2016-02-13 15:47:15 +00:00
|
|
|
|
public IOperation ReadRegisterOp()
|
2015-07-31 08:45:50 +00:00
|
|
|
|
{
|
|
|
|
|
|
return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2016-02-13 07:18:49 +00:00
|
|
|
|
/// Clear data/counters related to a specific tests
|
2015-07-31 08:45:50 +00:00
|
|
|
|
/// </summary>
|
2016-02-13 07:18:49 +00:00
|
|
|
|
public void Clear()
|
2015-07-31 08:45:50 +00:00
|
|
|
|
{
|
2016-02-13 07:18:49 +00:00
|
|
|
|
sampleNr = 0;
|
|
|
|
|
|
|
|
|
|
|
|
volumeLtr = 0;
|
|
|
|
|
|
volumeLtr0 = 0;
|
|
|
|
|
|
timestampSec = 0;
|
|
|
|
|
|
timestampSec0 = 0;
|
|
|
|
|
|
|
|
|
|
|
|
ReadPulses();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void TestCompleted()
|
|
|
|
|
|
{
|
|
|
|
|
|
/// TODO: Implement
|
2015-07-31 08:45:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-08-22 04:32:55 +00:00
|
|
|
|
int sampleNr; /// This is to determine when the test start sample should be taken
|
|
|
|
|
|
|
2015-07-31 08:45:50 +00:00
|
|
|
|
/// <summary>Start this operation</summary>
|
|
|
|
|
|
public void Start()
|
|
|
|
|
|
{
|
2016-02-13 07:18:49 +00:00
|
|
|
|
Clear();
|
|
|
|
|
|
|
2015-08-21 14:06:38 +00:00
|
|
|
|
/// Reset opto data
|
|
|
|
|
|
optoDataCount = 0;
|
2015-12-17 11:29:39 +00:00
|
|
|
|
TestStartTelegramIx = 0;
|
|
|
|
|
|
TestEndTelegramIx = 0;
|
2015-12-02 17:03:37 +00:00
|
|
|
|
|
|
|
|
|
|
/// File name is: PCB_AA_BB_HH_MI_SS..txt
|
|
|
|
|
|
string wmPosition = Name.Substring(5);
|
|
|
|
|
|
if (wmPosition.Length == 1) wmPosition = "0" + wmPosition;
|
|
|
|
|
|
|
|
|
|
|
|
string flowNr;
|
2015-12-08 09:48:06 +00:00
|
|
|
|
if (TestName != null && TestName.ToLower().Contains("q1")) flowNr = "01";
|
|
|
|
|
|
else if (TestName != null && TestName.ToLower().Contains("q2")) flowNr = "02";
|
|
|
|
|
|
else if (TestName != null && TestName.ToLower().Contains("q3")) flowNr = "03";
|
2016-03-02 19:02:30 +00:00
|
|
|
|
else if (TestName != null && TestName.ToLower().Contains("adj")) flowNr = "0-1";
|
2015-12-02 17:03:37 +00:00
|
|
|
|
else flowNr = "99";
|
|
|
|
|
|
|
|
|
|
|
|
optoDataLogFileName = string.Format("{0}_{1}_{2}_{3}.txt",
|
|
|
|
|
|
(ConfigStruct != null) ? ConfigStruct.PCBNumber2String() : "UnknownPcbNr",
|
|
|
|
|
|
wmPosition,
|
|
|
|
|
|
flowNr,
|
2016-03-02 19:02:30 +00:00
|
|
|
|
StateMachine.CycleStartTimeStamp.ToString("HH_mm_ss"));
|
2015-08-21 14:06:38 +00:00
|
|
|
|
|
2015-09-21 14:29:12 +00:00
|
|
|
|
StartParsingOptoSerialPort();
|
2015-08-19 11:04:47 +00:00
|
|
|
|
}
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
|
|
|
|
|
/// <summary>Run this operation</summary>
|
|
|
|
|
|
/// <returns>eventDone</returns>
|
|
|
|
|
|
public Event Run()
|
|
|
|
|
|
{
|
2015-08-19 11:04:47 +00:00
|
|
|
|
sampleNr++;
|
2015-07-31 08:45:50 +00:00
|
|
|
|
ReadPulses();
|
2015-08-22 18:37:43 +00:00
|
|
|
|
if (sampleNr == 4)
|
2015-08-19 11:04:47 +00:00
|
|
|
|
{
|
2015-08-22 04:32:55 +00:00
|
|
|
|
/// Take the test start sample
|
2015-08-19 11:04:47 +00:00
|
|
|
|
VolumeLtrStart = volumeLtr;
|
2016-01-08 18:18:45 +00:00
|
|
|
|
timestampSecStart = timestampSec;
|
2015-12-17 11:29:39 +00:00
|
|
|
|
TestStartTelegramIx = telegramIx;
|
2015-08-19 11:04:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Shift data in pipelines
|
2015-08-22 18:37:43 +00:00
|
|
|
|
VolumeLtrEnd = volumeLtrEnd3;
|
|
|
|
|
|
volumeLtrEnd3 = volumeLtrEnd2;
|
|
|
|
|
|
volumeLtrEnd2 = volumeLtrEnd1;
|
2015-08-19 11:04:47 +00:00
|
|
|
|
volumeLtrEnd1 = volumeLtr;
|
|
|
|
|
|
|
2016-01-08 18:18:45 +00:00
|
|
|
|
timestampSecEnd = timestampSecEnd3;
|
2015-08-22 18:37:43 +00:00
|
|
|
|
timestampSecEnd3 = timestampSecEnd2;
|
|
|
|
|
|
timestampSecEnd2 = timestampSecEnd1;
|
|
|
|
|
|
timestampSecEnd1 = timestampSec;
|
2015-08-19 11:04:47 +00:00
|
|
|
|
|
2015-12-17 11:29:39 +00:00
|
|
|
|
TestEndTelegramIx = telegramIx - 3;
|
|
|
|
|
|
|
2015-07-31 08:45:50 +00:00
|
|
|
|
return Event.ReadRegisterDone;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>Stop this operation</summary>
|
|
|
|
|
|
public void Stop()
|
|
|
|
|
|
{
|
2015-12-17 11:29:39 +00:00
|
|
|
|
if (TestStartTelegramIx > 0 && TestStartTelegramIx < optoData.Length && optoData[TestStartTelegramIx].Flags == OptoTelegramFlags.OK)
|
|
|
|
|
|
{
|
|
|
|
|
|
optoData[TestStartTelegramIx].Flags = OptoTelegramFlags.OK_TestStart;
|
|
|
|
|
|
OptoTelegramRaw.TestStartTimestampDec = optoData[TestStartTelegramIx].TimestampDec();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (TestEndTelegramIx > 0 && TestEndTelegramIx < optoData.Length && optoData[TestEndTelegramIx].Flags == OptoTelegramFlags.OK)
|
|
|
|
|
|
{
|
|
|
|
|
|
optoData[TestEndTelegramIx].Flags = OptoTelegramFlags.OK_TestEnd;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
StopParsingOptoSerialPort();
|
2015-12-20 19:50:46 +00:00
|
|
|
|
OptoTelegramRaw.FIRFilterFlow(optoData, optoDataCount);
|
2015-08-21 14:06:38 +00:00
|
|
|
|
SaveOptoData();
|
2015-08-19 11:04:47 +00:00
|
|
|
|
}
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
2015-08-21 14:06:38 +00:00
|
|
|
|
void SaveOptoData()
|
|
|
|
|
|
{
|
2016-03-02 19:02:30 +00:00
|
|
|
|
string directory = string.Format("C:\\TBF\\ProcessData\\{0}\\{1}\\{2}\\",
|
|
|
|
|
|
StateMachine.CycleStartTimeStamp.ToString("yy"),
|
|
|
|
|
|
StateMachine.CycleStartTimeStamp.ToString("MM"),
|
|
|
|
|
|
StateMachine.CycleStartTimeStamp.ToString("dd"));
|
2016-01-08 15:57:37 +00:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2016-03-02 19:02:30 +00:00
|
|
|
|
Directory.CreateDirectory(directory);
|
2015-12-02 17:03:37 +00:00
|
|
|
|
|
2016-03-02 19:02:30 +00:00
|
|
|
|
double scalFact = ScalingFactor();
|
2015-12-04 12:52:00 +00:00
|
|
|
|
|
2016-03-02 19:02:30 +00:00
|
|
|
|
using (TextWriter optoLogFile = new StreamWriter(directory + optoDataLogFileName))
|
|
|
|
|
|
{
|
|
|
|
|
|
optoLogFile.WriteLine(optoData[0].ToString(scalFact, null));
|
|
|
|
|
|
for (int i = 1; i < optoDataCount; i++) optoLogFile.WriteLine(optoData[i].ToString(scalFact, optoData[i - 1]));
|
|
|
|
|
|
optoLogFile.Close();
|
|
|
|
|
|
}
|
2016-01-08 15:57:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception exc)
|
|
|
|
|
|
{
|
2016-05-28 11:40:58 +00:00
|
|
|
|
MessageBox.Show(Strings.Error_writing_into_file_ + Environment.NewLine +
|
|
|
|
|
|
string.Format("{0}\\{1}\r\n{2}", directory, optoDataLogFileName, exc.Message),
|
|
|
|
|
|
Strings.Error,
|
|
|
|
|
|
MessageBoxButtons.OK,
|
|
|
|
|
|
MessageBoxIcon.Exclamation);
|
2016-01-08 15:57:37 +00:00
|
|
|
|
}
|
2015-08-21 14:06:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-07-31 08:45:50 +00:00
|
|
|
|
void ReadPulses()
|
|
|
|
|
|
{
|
2017-03-10 13:31:08 +00:00
|
|
|
|
beginWMState = volumeLtr0;
|
|
|
|
|
|
endWMState = volumeLtr;
|
|
|
|
|
|
wmVolume = endWMState - beginWMState;
|
2016-02-13 07:18:49 +00:00
|
|
|
|
wmPulses = (int)(wmVolume * (double)PulsesPerLtr + 0.5);
|
|
|
|
|
|
wmRefPulses = StateMachine.ControlBoard.EtPulses(0);
|
|
|
|
|
|
wmTestTime = timestampSec - timestampSec0;
|
2015-07-31 08:45:50 +00:00
|
|
|
|
}
|
2015-07-31 17:45:10 +00:00
|
|
|
|
|
2015-08-19 11:04:47 +00:00
|
|
|
|
|
|
|
|
|
|
bool optoSerialPortParsingEnabled;
|
2015-08-15 21:01:26 +00:00
|
|
|
|
|
2015-08-19 11:04:47 +00:00
|
|
|
|
/// <summary> Flush internal buffers and start parsing the opto serial port data </summary>
|
|
|
|
|
|
void StartParsingOptoSerialPort()
|
|
|
|
|
|
{
|
|
|
|
|
|
optoSerialPortParsingEnabled = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary> Stop parsig the opto serial port data </summary>
|
|
|
|
|
|
void StopParsingOptoSerialPort()
|
|
|
|
|
|
{
|
|
|
|
|
|
optoSerialPortParsingEnabled = false;
|
|
|
|
|
|
}
|
2015-07-31 17:45:10 +00:00
|
|
|
|
|
2015-09-21 14:29:12 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// Variables storing the context of serial port data parsing (ReadOptoSerialPort(...))
|
|
|
|
|
|
///
|
|
|
|
|
|
bool synchronized;
|
|
|
|
|
|
bool synchronized2;
|
|
|
|
|
|
string partOfTelegram;
|
2015-08-21 18:52:10 +00:00
|
|
|
|
|
2015-08-15 21:01:26 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 9600 Bd, 8 data bits, 1 stop bit, no parity
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Telegram description:
|
|
|
|
|
|
///
|
|
|
|
|
|
/// AAAAAA[tab]BBBB[tab]CCCC[tab]DDDDDD[tab]EEEE[tab]FFFFFFFF[tab]GG[cr][lf] (42 bytes)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Example:
|
|
|
|
|
|
/// FFFFFE 51EA 0000 65324E 0087 F6319DFF 86
|
|
|
|
|
|
/// FFDD3A 51F9 0000 65324E 0088 F631A60B 45
|
|
|
|
|
|
/// ...
|
|
|
|
|
|
/// </summary>
|
2015-09-21 14:29:12 +00:00
|
|
|
|
/// <param name="optoState">OptoState.Read or OptoState.Flush</param>
|
2017-02-06 17:34:32 +00:00
|
|
|
|
void ReadOptoData(OptoState optoState)
|
2015-08-15 21:01:26 +00:00
|
|
|
|
{
|
2015-08-21 18:52:10 +00:00
|
|
|
|
int nrBytes = optoSerialPort.BytesToRead;
|
|
|
|
|
|
if (nrBytes > 0)
|
2015-08-15 21:01:26 +00:00
|
|
|
|
{
|
2015-08-21 18:52:10 +00:00
|
|
|
|
char[] buffer = new char[nrBytes];
|
|
|
|
|
|
optoSerialPort.Read(buffer, 0, nrBytes);
|
|
|
|
|
|
string received = new string(buffer);
|
2015-08-15 21:01:26 +00:00
|
|
|
|
|
2015-08-21 18:52:10 +00:00
|
|
|
|
string allRcvd = partOfTelegram + received;
|
2015-08-15 21:01:26 +00:00
|
|
|
|
|
2015-08-21 18:52:10 +00:00
|
|
|
|
while (true)
|
|
|
|
|
|
{
|
|
|
|
|
|
int pos = allRcvd.IndexOf("\r\n");
|
2015-08-15 21:01:26 +00:00
|
|
|
|
|
2015-08-21 18:52:10 +00:00
|
|
|
|
if (pos < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
/// No CR+LF found, wait for more characters in the next invocation
|
|
|
|
|
|
partOfTelegram = allRcvd;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2015-09-21 14:29:12 +00:00
|
|
|
|
else
|
2015-08-21 18:52:10 +00:00
|
|
|
|
{
|
2015-09-21 14:29:12 +00:00
|
|
|
|
// CR+LF found
|
|
|
|
|
|
if (optoState == OptoState.Read)
|
2015-08-15 21:01:26 +00:00
|
|
|
|
{
|
2015-12-02 17:03:37 +00:00
|
|
|
|
if (pos < OptoTelegramRaw.Length - 2)
|
2015-09-21 14:29:12 +00:00
|
|
|
|
{
|
|
|
|
|
|
/// CR+LF found too early, truncate the beginning incl CR+LF and keep scanning in this loop
|
|
|
|
|
|
allRcvd = allRcvd.Substring(pos + 2);
|
|
|
|
|
|
if (synchronized)
|
|
|
|
|
|
{
|
2015-12-03 19:58:54 +00:00
|
|
|
|
optoData[optoDataCount].Counter = optoDataCount;
|
2015-09-21 14:29:12 +00:00
|
|
|
|
optoData[optoDataCount++].SetFlags(OptoTelegramFlags.SyncError);
|
|
|
|
|
|
}
|
|
|
|
|
|
synchronized = true;
|
|
|
|
|
|
}
|
2015-12-02 17:03:37 +00:00
|
|
|
|
// CR+LF found and (pos >= OptoTelegramRaw.Length - 2)
|
2015-12-03 19:58:54 +00:00
|
|
|
|
else if (optoData[optoDataCount].UpdateFromString(allRcvd.Substring(pos - OptoTelegramRaw.Length + 2), optoDataCount))
|
2015-09-21 14:29:12 +00:00
|
|
|
|
{
|
2015-12-17 11:29:39 +00:00
|
|
|
|
OptoTelegramRreceived(optoDataCount++, synchronized2);
|
|
|
|
|
|
synchronized2 = synchronized;
|
2015-09-21 14:29:12 +00:00
|
|
|
|
allRcvd = allRcvd.Substring(pos + 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2015-12-03 19:58:54 +00:00
|
|
|
|
optoData[optoDataCount].Counter = optoDataCount;
|
2016-03-02 19:02:30 +00:00
|
|
|
|
optoDataCount++;
|
2015-09-21 14:29:12 +00:00
|
|
|
|
allRcvd = allRcvd.Substring(pos + 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else /// optoState == OptoState.Flush
|
|
|
|
|
|
{
|
2015-12-02 17:03:37 +00:00
|
|
|
|
if (pos < OptoTelegramRaw.Length - 2)
|
2015-09-21 14:29:12 +00:00
|
|
|
|
{
|
|
|
|
|
|
/// CR+LF found too early, truncate the beginning incl CR+LF and keep scanning in this loop
|
|
|
|
|
|
allRcvd = allRcvd.Substring(pos + 2);
|
|
|
|
|
|
synchronized = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
// CR+LF found and (pos >= OptoTelegram.Length - 2)
|
2015-12-03 19:58:54 +00:00
|
|
|
|
else if (toBeFlushed.UpdateFromString(allRcvd.Substring(pos - OptoTelegramRaw.Length + 2), 0))
|
2015-09-21 14:29:12 +00:00
|
|
|
|
{
|
|
|
|
|
|
flushedDataCount++;
|
|
|
|
|
|
synchronized2 = synchronized;
|
|
|
|
|
|
allRcvd = allRcvd.Substring(pos + 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
allRcvd = allRcvd.Substring(pos + 2);
|
|
|
|
|
|
}
|
2015-08-15 21:01:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2015-08-21 18:52:10 +00:00
|
|
|
|
|
|
|
|
|
|
//OnOptoReceived(this, new OptoReceivedEventArgs(s));
|
2015-08-15 21:01:26 +00:00
|
|
|
|
}
|
2015-08-21 18:52:10 +00:00
|
|
|
|
else
|
2015-08-15 21:01:26 +00:00
|
|
|
|
{
|
2015-08-21 18:52:10 +00:00
|
|
|
|
//OnOptoReceived(this, new OptoReceivedEventArgs("."));
|
2015-08-15 21:01:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-08-19 11:04:47 +00:00
|
|
|
|
|
2015-12-17 11:29:39 +00:00
|
|
|
|
void OptoTelegramRreceived(int currentIx, bool async)
|
2015-08-15 21:01:26 +00:00
|
|
|
|
{
|
2015-12-17 11:29:39 +00:00
|
|
|
|
OptoTelegramRaw optoTelegram = optoData[currentIx];
|
|
|
|
|
|
telegramIx = currentIx;
|
|
|
|
|
|
|
2015-08-19 11:04:47 +00:00
|
|
|
|
Int32 uncorrectedRawVolume = 0;
|
|
|
|
|
|
if (!lastVolumeRawValid)
|
|
|
|
|
|
{
|
|
|
|
|
|
lastVolumeRaw = optoTelegram.VolumeRaw;
|
|
|
|
|
|
lastVolumeRawValid = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
uncorrectedRawVolume = (Int32)((lastVolumeRaw & 0x7F000000) | (optoTelegram.VolumeRaw & 0x00FFFFFF));
|
|
|
|
|
|
if (Math.Abs(uncorrectedRawVolume - lastVolumeRaw) <= 0x007FFFFF)
|
|
|
|
|
|
{
|
|
|
|
|
|
lastVolumeRaw = uncorrectedRawVolume;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Math.Abs(uncorrectedRawVolume + 0x01000000 - lastVolumeRaw) <= 0x007FFFFF)
|
|
|
|
|
|
{
|
|
|
|
|
|
lastVolumeRaw = uncorrectedRawVolume + 0x01000000;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Math.Abs(uncorrectedRawVolume - 0x01000000 - lastVolumeRaw) <= 0x007FFFFF)
|
|
|
|
|
|
{
|
|
|
|
|
|
lastVolumeRaw = uncorrectedRawVolume - 0x01000000;
|
|
|
|
|
|
}
|
2015-08-21 14:06:38 +00:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
lastVolumeRaw = uncorrectedRawVolume; /// This should never happen
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2015-08-19 11:04:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Int64 uncorrectedTimestamp = 0;
|
|
|
|
|
|
if (!lastTimestampValid)
|
|
|
|
|
|
{
|
|
|
|
|
|
lastTimestamp = optoTelegram.Timestamp;
|
|
|
|
|
|
lastTimestampValid = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
uncorrectedTimestamp = (Int64)((lastTimestamp & 0x7FFFFFFF00000000) | (optoTelegram.Timestamp & 0xFFFFFFFF));
|
|
|
|
|
|
if (Math.Abs(uncorrectedTimestamp - lastTimestamp) <= 0x7FFFFFFF)
|
|
|
|
|
|
{
|
|
|
|
|
|
lastTimestamp = uncorrectedTimestamp;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Math.Abs(uncorrectedTimestamp + 0x100000000 - lastTimestamp) <= 0x7FFFFFFF)
|
|
|
|
|
|
{
|
|
|
|
|
|
lastTimestamp = uncorrectedTimestamp + 0x100000000;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Math.Abs(uncorrectedTimestamp - 0x100000000 - lastTimestamp) <= 0x7FFFFFFF)
|
|
|
|
|
|
{
|
|
|
|
|
|
lastTimestamp = uncorrectedTimestamp - 0x100000000;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2015-08-21 14:06:38 +00:00
|
|
|
|
lastTimestamp = uncorrectedTimestamp; /// This should never happen
|
2015-08-19 11:04:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (volumeLtr == 0 && volumeLtr0 == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
volumeLtr = (double)lastVolumeRaw * ScalingFactor() / 16000.0;
|
|
|
|
|
|
volumeLtr0 = volumeLtr;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
volumeLtr = (double)lastVolumeRaw * ScalingFactor() / 16000.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (timestampSec == 0 && timestampSec0 == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
timestampSec = (double)lastTimestamp / 8192.0;
|
|
|
|
|
|
timestampSec0 = timestampSec;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
timestampSec = (double)lastTimestamp / 8192.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-08-21 14:06:38 +00:00
|
|
|
|
//optoDataLogger.InfoFormat("{0} {1} {2} ltr {3} {4}", optoTelegram, lastTimestamp, lastVolumeRaw.ToString("X8"), timestampSec.ToString("F1"), volumeLtr.ToString("F3"));
|
2015-08-19 11:04:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-07-31 17:45:10 +00:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Called from the state machine when a test is selected and UI needs to be updated.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void OnOptoReceived(object sender, OptoReceivedEventArgs args)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (OptoReceivedHandler == null) return;
|
|
|
|
|
|
try { OptoReceivedHandler(sender, args); }
|
|
|
|
|
|
catch (Exception) { }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public event EventHandler<OptoReceivedEventArgs> OptoReceivedHandler;
|
|
|
|
|
|
|
2015-08-19 11:04:47 +00:00
|
|
|
|
public static double UnitVolume(VolumeUnits units)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (units)
|
|
|
|
|
|
{
|
|
|
|
|
|
default:
|
|
|
|
|
|
case VolumeUnits.m3: return 1000.0; /// liter
|
|
|
|
|
|
case VolumeUnits.UK_gallon: return 4.546092; /// liter
|
|
|
|
|
|
case VolumeUnits.US_gallon: return 3.785412; // liter
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public double ScalingFactor()
|
|
|
|
|
|
{
|
|
|
|
|
|
if ((iPerlCfg.MeterType == MeterType.AutoDetect) && (CalibrationStruct != null))
|
|
|
|
|
|
{
|
|
|
|
|
|
return WaterMeter.ScalingFactor(CalibrationStruct.MeterType);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (iPerlCfg.MeterType != MeterType.AutoDetect)
|
|
|
|
|
|
{
|
|
|
|
|
|
return WaterMeter.ScalingFactor(iPerlCfg.MeterType);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return WaterMeter.ScalingFactor(MeterType.DN20);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Scaling factor:
|
|
|
|
|
|
/// 0, 1 (DN15, Coax) . . . . 1
|
|
|
|
|
|
/// 2 (DN20) . . . . . . . . 2
|
|
|
|
|
|
/// 3 (DN25) . . . . . . . . 4
|
|
|
|
|
|
/// 4, 5 (DN26, DN32) . . . . 8
|
|
|
|
|
|
/// 6 (DN40) . . . . . . . . 16
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="meterType">MeterType (0..6)</param>
|
|
|
|
|
|
/// <returns>Scaling factor</returns>
|
|
|
|
|
|
public static double ScalingFactor(MeterType meterType)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (meterType)
|
|
|
|
|
|
{
|
|
|
|
|
|
default:
|
|
|
|
|
|
case MeterType.DN15:
|
|
|
|
|
|
case MeterType.CoaxManifold: return 1.0;
|
|
|
|
|
|
case MeterType.DN20: return 2.0;
|
|
|
|
|
|
case MeterType.DN25: return 4.0;
|
|
|
|
|
|
case MeterType.DN26:
|
|
|
|
|
|
case MeterType.DN32: return 8.0;
|
|
|
|
|
|
case MeterType.DN40: return 16.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2015-12-04 11:24:55 +00:00
|
|
|
|
|
|
|
|
|
|
int PositionNrFormWMName(string name)
|
|
|
|
|
|
{
|
|
|
|
|
|
int len = name.Length;
|
|
|
|
|
|
if (len < 2) return 0;
|
|
|
|
|
|
|
|
|
|
|
|
int loNr = (int)name[len - 1] - (int)'0';
|
|
|
|
|
|
int hiNr = (int)name[len - 2] - (int)'0';
|
|
|
|
|
|
if (hiNr < 1 || hiNr > 4) hiNr = 0;
|
|
|
|
|
|
|
|
|
|
|
|
return 10 * hiNr + loNr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2015-07-31 08:45:50 +00:00
|
|
|
|
}
|