2015-07-31 08:45:50 +00:00
|
|
|
|
///
|
2022-05-27 07:30:45 +00:00
|
|
|
|
/// Copyright (c) 2015-2022 Sensus Slovensko a.s.
|
2015-07-31 08:45:50 +00:00
|
|
|
|
///
|
|
|
|
|
|
using System;
|
2022-05-27 07:30:45 +00:00
|
|
|
|
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;
|
2022-05-27 07:30:45 +00:00
|
|
|
|
using System.Linq;
|
2015-07-31 08:45:50 +00:00
|
|
|
|
using log4net;
|
2021-09-23 09:46:40 +00:00
|
|
|
|
using Common;
|
2021-07-06 11:11:42 +00:00
|
|
|
|
using Common.Iperl;
|
2015-12-04 16:17:20 +00:00
|
|
|
|
using Config.Entities;
|
2021-10-26 09:23:57 +00:00
|
|
|
|
using TBF.Rig.Generic;
|
|
|
|
|
|
using TBF.Rig.GenericDevices;
|
|
|
|
|
|
using TBF.Rig.Output;
|
|
|
|
|
|
using TBF.Rig.Sequences;
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
2021-10-26 09:23:57 +00:00
|
|
|
|
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
|
2015-07-31 08:45:50 +00:00
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This component = instance of this class is a placeholder for a combined main watermeter
|
|
|
|
|
|
/// </summary>
|
2021-10-08 09:36:38 +00:00
|
|
|
|
public class IperlHead : ComponentBase, IDevice, IRegReaderDatastream, ISessionDataMngmnt, IOperation
|
2015-07-31 08:45:50 +00:00
|
|
|
|
{
|
2018-12-17 13:12:55 +00:00
|
|
|
|
private static readonly ILog log = LogManager.GetLogger(typeof(IperlHead));
|
2021-04-07 07:57:42 +00:00
|
|
|
|
public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
2020-09-23 08:58:49 +00:00
|
|
|
|
#if TURA_SPECIAL
|
2021-10-08 09:36:38 +00:00
|
|
|
|
public const int OptoDataBufferSize = 250000;
|
2020-09-23 08:58:49 +00:00
|
|
|
|
#else
|
2021-10-08 09:36:38 +00:00
|
|
|
|
public const int OptoDataBufferSize = 40000; /// Opto data count is not limitted by the buffer size
|
2020-09-23 08:58:49 +00:00
|
|
|
|
#endif
|
2021-06-24 10:19:15 +00:00
|
|
|
|
public const string OptoDataDirectory = "C:\\TBF\\ProcessData";
|
2021-10-08 09:36:38 +00:00
|
|
|
|
public const int StartOptoDataCount = OptoDataBufferSize / 2;
|
|
|
|
|
|
public const int EndOptoDataCount = OptoDataBufferSize - StartOptoDataCount;
|
2021-05-19 08:36:54 +00:00
|
|
|
|
public const int StartEndFilterSamplesCount2 = 20; /// StartEndFilterSamplesCount = 2 * StartEndFilterSamplesCount2 + 1
|
2021-07-01 07:32:58 +00:00
|
|
|
|
public const int FeatureVectorSize = 9;
|
2018-12-17 13:12:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
2017-05-31 12:51:26 +00:00
|
|
|
|
readonly IperlHeadCfg iperlHeadCfg;
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
2017-05-31 12:51:26 +00:00
|
|
|
|
public int RfidComPortNr { get { return iperlHeadCfg.RfidComPortNr; } }
|
2017-08-04 15:45:40 +00:00
|
|
|
|
public int MuxBoardNrOrGroup14 { get { return iperlHeadCfg.MuxBoardNr; } }
|
2017-05-31 12:51:26 +00:00
|
|
|
|
public int Group { get { return iperlHeadCfg.Group; } }
|
2021-07-29 10:02:49 +00:00
|
|
|
|
public iPerlHead.MeterType MeterType { get { return iperlHeadCfg.MeterType; } }
|
2015-07-31 17:45:10 +00:00
|
|
|
|
|
2019-11-20 08:40:54 +00:00
|
|
|
|
public int Position
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
int firstDigitPos = Name.IndexOfAny(new char[] { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' });
|
|
|
|
|
|
int position;
|
|
|
|
|
|
return (firstDigitPos < 0) ? 0 : (int.TryParse(Name.Substring(firstDigitPos), out position) ? position : 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-07-13 11:36:33 +00:00
|
|
|
|
public RegisterReaderType RegisterReaderType { get { return RegisterReaderType.DataStream; } }
|
2018-04-10 14:10:44 +00:00
|
|
|
|
public double PulsesPerLtr { get { return 1000.0; } }
|
2017-05-31 12:51:26 +00:00
|
|
|
|
public double LtrsPerPulse { get { return 1 / PulsesPerLtr; } }
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
2019-04-08 07:44:13 +00:00
|
|
|
|
public double CalibTarget { get { return iperlHeadCfg.ProcParams.CalibTarget; } }
|
2019-04-25 11:53:08 +00:00
|
|
|
|
public ushort FactorLimitLo { get { return (ushort)iperlHeadCfg.ProcParams.FactorLimitLo; } }
|
2017-05-31 12:51:26 +00:00
|
|
|
|
public ushort FactorLimitHi { get { return (ushort)iperlHeadCfg.ProcParams.FactorLimitHi; } }
|
2019-02-06 09:34:43 +00:00
|
|
|
|
public Counting InitFlowDir { get { return (iperlHeadCfg != null && iperlHeadCfg.ProcParams != null) ? iperlHeadCfg.ProcParams.Counting : Counting.Arbitrary; } }
|
2019-03-28 08:33:17 +00:00
|
|
|
|
|
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
|
|
|
|
|
|
{
|
2020-12-10 10:13:23 +00:00
|
|
|
|
if (ConfigStruct != null) return ConfigStruct.GetPcbNrString();
|
2015-08-20 03:06:23 +00:00
|
|
|
|
else return string.Empty;
|
|
|
|
|
|
}
|
2015-12-04 11:24:55 +00:00
|
|
|
|
set { }
|
2015-07-31 08:45:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-10 10:13:23 +00:00
|
|
|
|
public bool Disabled;
|
2021-06-24 11:57:31 +00:00
|
|
|
|
public bool CommFailed;
|
2020-12-10 10:13:23 +00:00
|
|
|
|
public int ResultCode;
|
2017-03-23 14:14:53 +00:00
|
|
|
|
|
2018-12-03 07:14:15 +00:00
|
|
|
|
|
2021-06-24 10:19:15 +00:00
|
|
|
|
string extraDataPath;
|
|
|
|
|
|
public string ExtraDataPath { get { return extraDataPath; } }
|
|
|
|
|
|
|
2021-07-01 07:32:58 +00:00
|
|
|
|
float[] x;
|
|
|
|
|
|
public float[] X { get { return x; } }
|
|
|
|
|
|
|
2021-06-24 10:19:15 +00:00
|
|
|
|
|
2017-03-23 14:14:53 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Passed to OptoTelegramRaw.UpdateFromString(...)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
Int64 volumeRawExtLast;
|
|
|
|
|
|
Int64 timestampExtLast;
|
|
|
|
|
|
|
2018-12-03 07:14:15 +00:00
|
|
|
|
FlowDirectionDetection flowDirectionDetection;
|
2017-03-23 14:14:53 +00:00
|
|
|
|
|
2015-11-13 00:11:36 +00:00
|
|
|
|
public bool PositiveCounting;
|
|
|
|
|
|
|
2018-12-03 07:14:15 +00:00
|
|
|
|
|
2020-12-10 10:13:23 +00:00
|
|
|
|
public ConfigStruct ConfigStruct; /// ConfigStruct of WM obtained or updated by iPerlCommunication
|
|
|
|
|
|
public CalibrationStruct CalibrationStruct; /// CalibrationStruct of WM obtained or updated by iPerlCommunication
|
|
|
|
|
|
public CalibrationStructV4 CalibrationStructV4; /// CalibrationStruct of WM obtained or updated by iPerlCommunication
|
2018-09-20 07:05:32 +00:00
|
|
|
|
|
2022-09-06 17:55:54 +00:00
|
|
|
|
public Byte OrigTestModeConfig; /// Written to by StartTestingSealedMeter(), read from by EndTestingSealedMeter()
|
|
|
|
|
|
|
2017-07-18 06:33:20 +00:00
|
|
|
|
public ushort OrigCalibFactor;
|
2018-09-20 07:05:32 +00:00
|
|
|
|
public ushort CalibFactor
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return (CalibrationStruct != null) ? CalibrationStruct.Calibration
|
|
|
|
|
|
: ((CalibrationStructV4 != null) ? CalibrationStructV4.Calibration
|
|
|
|
|
|
: (ushort)0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public ushort OrigCalibFactorLNA;
|
|
|
|
|
|
public ushort CalibFactorLNA { get { return (CalibrationStructV4 != null) ? CalibrationStructV4.CalibrationLNA : (ushort)0; } }
|
|
|
|
|
|
|
2015-08-14 17:20:47 +00:00
|
|
|
|
|
2017-07-18 06:33:20 +00:00
|
|
|
|
public double Q2ErrWOCorrection;
|
2018-10-18 07:28:17 +00:00
|
|
|
|
public int Q2CorrRL;
|
|
|
|
|
|
public int Q2CorrLR;
|
2015-08-26 16:59:38 +00:00
|
|
|
|
|
2016-02-16 13:44:10 +00:00
|
|
|
|
public double Diff2Hz8Hz;
|
|
|
|
|
|
public bool Hz2CorrectionDone;
|
2017-07-18 06:33:20 +00:00
|
|
|
|
public int Hz2Correction;
|
2017-06-15 11:08:31 +00:00
|
|
|
|
|
2018-09-20 07:05:32 +00:00
|
|
|
|
public string FWVersion
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return (CalibrationStruct != null) ? CalibrationStruct.FWVersionStr()
|
|
|
|
|
|
: ((CalibrationStructV4 != null) ? CalibrationStructV4.FWVersionStr()
|
|
|
|
|
|
: string.Empty);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-06-15 11:08:31 +00:00
|
|
|
|
|
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-01-03 01:05:35 +00:00
|
|
|
|
public Results.Entities.MeterTestRslt LastTestResult;
|
2020-02-04 07:10:19 +00:00
|
|
|
|
public Results.Entities.MeterTestRslt LastTestResult2;
|
2015-08-22 04:32:55 +00:00
|
|
|
|
|
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; } }
|
2017-03-10 13:31:08 +00:00
|
|
|
|
public double BeginWMState { get { return beginWMState; } }
|
|
|
|
|
|
public double EndWMState { get { return endWMState; } }
|
2020-12-10 10:13:23 +00:00
|
|
|
|
public double WMVolume { get { return wmVolume; } }
|
|
|
|
|
|
public double WMTestTime { get { return wmTestTime; } }
|
2016-02-13 07:18:49 +00:00
|
|
|
|
|
2020-12-10 10:13:23 +00:00
|
|
|
|
int wmPulses;
|
|
|
|
|
|
int wmRefPulses;
|
|
|
|
|
|
double beginWMState;
|
2017-03-10 13:31:08 +00:00
|
|
|
|
double endWMState;
|
2016-02-13 07:18:49 +00:00
|
|
|
|
double wmVolume;
|
|
|
|
|
|
double wmTestTime;
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-08-22 04:32:55 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// New calibration factor calculated from the original factor (argument)
|
2017-07-18 06:33:20 +00:00
|
|
|
|
/// and results of any test(s).
|
2017-12-11 09:17:37 +00:00
|
|
|
|
/// Uses also: this.CalibTarget, this.VolumeLtrStart, this.VolumeLtrEnd
|
|
|
|
|
|
/// Side effects: this.OrigCalibFactor, this.PositiveCounting
|
2015-08-22 04:32:55 +00:00
|
|
|
|
/// </summary>
|
2017-07-18 06:33:20 +00:00
|
|
|
|
/// <param name="adjustTestResult">Test result for calculations</param>
|
2015-08-22 04:32:55 +00:00
|
|
|
|
/// <param name="originalCalibrationFactor">Original calibration factor</param>
|
2017-12-11 09:17:37 +00:00
|
|
|
|
/// <param name="factorLimitLo">Lower limit for the calibration factor</param>
|
|
|
|
|
|
/// <param name="factorLimitHi">Upper limit for the calibration factor</param>
|
2017-06-07 14:22:18 +00:00
|
|
|
|
/// <returns>New calibration factor or 0 (= Out of range)</returns>
|
2017-12-11 09:17:37 +00:00
|
|
|
|
public UInt16 CalculateNewCalibFactor(Results.Entities.MeterTestRslt adjustTestResult, UInt16 originalCalibrationFactor, UInt16 factorLimitLo, UInt16 factorLimitHi)
|
2017-07-18 06:33:20 +00:00
|
|
|
|
{
|
|
|
|
|
|
double meterVolume = adjustTestResult.VolumeMeter;
|
2017-09-18 10:32:39 +00:00
|
|
|
|
double targetVolume = adjustTestResult.VolumeRef * (1.0f + CalibTarget / 100.0f);
|
2015-11-10 22:29:14 +00:00
|
|
|
|
|
2017-07-18 06:33:20 +00:00
|
|
|
|
OrigCalibFactor = originalCalibrationFactor;
|
2015-11-13 00:11:36 +00:00
|
|
|
|
|
2017-07-18 06:33:20 +00:00
|
|
|
|
if (meterVolume > 1E-2)
|
|
|
|
|
|
{
|
2021-05-19 08:36:54 +00:00
|
|
|
|
PositiveCounting = VolumeLtrEnd > VolumeLtrStart;
|
2015-11-13 00:11:36 +00:00
|
|
|
|
|
2017-09-18 10:32:39 +00:00
|
|
|
|
UInt16 newFactor = (UInt16)((double)originalCalibrationFactor * targetVolume / meterVolume + 0.5);
|
|
|
|
|
|
log.InfoFormat("Calibration factor: orig={0} new={1} V_iperl={2} V_ref={3} V_target={4}",
|
2017-07-18 06:33:20 +00:00
|
|
|
|
originalCalibrationFactor,
|
|
|
|
|
|
newFactor,
|
|
|
|
|
|
meterVolume.ToString("F3"),
|
2017-09-18 10:32:39 +00:00
|
|
|
|
adjustTestResult.VolumeRef.ToString("F3"),
|
|
|
|
|
|
targetVolume.ToString("F3"));
|
|
|
|
|
|
|
2015-08-26 15:57:49 +00:00
|
|
|
|
|
2017-12-11 09:17:37 +00:00
|
|
|
|
if (newFactor < factorLimitLo || newFactor > factorLimitHi) return 0;
|
2015-08-26 15:57:49 +00:00
|
|
|
|
|
|
|
|
|
|
return newFactor;
|
2017-07-18 06:33:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
log.ErrorFormat("Calibration factor: orig={0} new={0} (unchanged!) V_iperl={1}",
|
|
|
|
|
|
originalCalibrationFactor,
|
|
|
|
|
|
meterVolume.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).
|
2019-03-28 08:33:17 +00:00
|
|
|
|
/// This factor should be used only for R800 meters.
|
2015-08-22 04:32:55 +00:00
|
|
|
|
/// </summary>
|
2019-03-28 08:33:17 +00:00
|
|
|
|
/// <param name="q2TestResult">A test result from which to calculate the factor</param>
|
|
|
|
|
|
/// <param name="nominalFlow">Nominal flow in m3/h</param>
|
|
|
|
|
|
/// <param name="currentFactor">0 or the current Q2 correction factor when updating the factor</param>
|
|
|
|
|
|
/// <returns>Calculated Q2 correction factor</returns>
|
2023-08-04 10:33:08 +00:00
|
|
|
|
public double CalculateQ2CorrectionFactor(Results.Entities.MeterTestRslt q2adjResult, double nominalFlow, int currentFactor = 0)
|
2015-08-22 04:32:55 +00:00
|
|
|
|
{
|
2023-08-04 10:33:08 +00:00
|
|
|
|
double nominalTestFlowLph = Units.ConvertTo(Unit.lph, nominalFlow);
|
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
|
2018-10-12 08:54:33 +00:00
|
|
|
|
double F = D / (nominalTestFlowLph * 10.0); /// Error corrected with 8 Raw Units per minute [%]
|
2016-02-16 13:44:10 +00:00
|
|
|
|
double G = F / B; /// Error corrected with 1 Raw Unit per minute [%]
|
2015-08-22 04:32:55 +00:00
|
|
|
|
|
2023-08-08 10:08:23 +00:00
|
|
|
|
/// Do not change the factor for an invalid measurement (q2adjResult.VolumeMeter == 0)
|
|
|
|
|
|
double q2CorrectionFactor = (Math.Abs(q2adjResult.VolumeMeter) <= float.Epsilon) ? Convert.ToDouble(currentFactor) :
|
|
|
|
|
|
Convert.ToDouble(currentFactor) - (q2adjResult.Error / G) * (q2adjResult.VolumeRef / q2adjResult.VolumeMeter);
|
2015-08-26 16:18:23 +00:00
|
|
|
|
|
2023-08-04 10:33:08 +00:00
|
|
|
|
log.WarnFormat("CalculateQ2CorrectionFactor() : Pos={0}, PCB#={1}, Error={2}%, Q2CorrFactor={3}",
|
2019-03-28 08:33:17 +00:00
|
|
|
|
Name,
|
|
|
|
|
|
SerialNr,
|
|
|
|
|
|
q2adjResult.Error.ToString("F2"),
|
|
|
|
|
|
q2CorrectionFactor.ToString("F1"));
|
|
|
|
|
|
|
|
|
|
|
|
return q2CorrectionFactor;
|
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
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Store/update values to be used as a part of the opto-data log file name.
|
|
|
|
|
|
/// Stop data stream processing and saving, if it is enabled.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="test">Currently executed test</param>
|
|
|
|
|
|
/// <param name="repetitionNr">Currently executed repetition number</param>
|
2022-12-01 08:54:04 +00:00
|
|
|
|
public void TestIsGoingToStartSoon(Test _test, int _repetitionNr)
|
2021-10-08 09:36:38 +00:00
|
|
|
|
{
|
|
|
|
|
|
/// Store/update values to be used as a part of the opto-data log file name
|
2022-12-01 08:54:04 +00:00
|
|
|
|
this.test = _test;
|
|
|
|
|
|
this.repetitionNr = _repetitionNr;
|
2021-10-08 09:36:38 +00:00
|
|
|
|
|
|
|
|
|
|
if (IsDataStreamProcessing())
|
|
|
|
|
|
{
|
|
|
|
|
|
StopDataStreamProcessing();
|
|
|
|
|
|
|
2022-01-24 11:55:39 +00:00
|
|
|
|
/// Dummy '#### start test ####' and '#### end of test ####' marks are added
|
|
|
|
|
|
/// to the raw data file on request of Joern Goege
|
|
|
|
|
|
if (optoDataCount >= 100 && optoDataCount <= optoData.Length &&
|
|
|
|
|
|
optoData[40].Flags == OptoTelegramFlags.OK &&
|
|
|
|
|
|
optoData[optoDataCount - 40].Flags == OptoTelegramFlags.OK)
|
|
|
|
|
|
{
|
|
|
|
|
|
optoData[40].Flags = OptoTelegramFlags.OK_TestStart;
|
|
|
|
|
|
optoData[optoDataCount - 40].Flags = OptoTelegramFlags.OK_TestEnd;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DataStreamPostProcessing();
|
|
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
string pcbNr = (ConfigStruct != null) ? ConfigStruct.GetPcbNrString() : "UnknownPcbNr";
|
|
|
|
|
|
string wmPosition = Name.Substring(5); /// WMPosition is extracted from a component name in form 'iPerl#'
|
|
|
|
|
|
if (wmPosition.Length == 1) wmPosition = "0" + wmPosition;
|
|
|
|
|
|
string cycleStartTime = StateMachine.CycleStartTimeStamp.ToString("HH_mm_ss");
|
|
|
|
|
|
///
|
|
|
|
|
|
string relativeDirectory = Path.Combine(StateMachine.CycleStartTimeStamp.ToString("yy"),
|
|
|
|
|
|
StateMachine.CycleStartTimeStamp.ToString("MM"),
|
|
|
|
|
|
StateMachine.CycleStartTimeStamp.ToString("dd"));
|
|
|
|
|
|
string directory = Path.Combine(OptoDataDirectory, relativeDirectory);
|
|
|
|
|
|
string fileName = string.Format("{0}_{1}_{2}_{3}.txt", pcbNr, wmPosition, "WM", cycleStartTime);
|
|
|
|
|
|
|
|
|
|
|
|
if (SaveOptoDataToFile(directory, fileName))
|
|
|
|
|
|
{
|
|
|
|
|
|
extraDataPath = Path.Combine(relativeDirectory, fileName);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2018-06-01 09:55:36 +00:00
|
|
|
|
///
|
2022-12-01 08:54:04 +00:00
|
|
|
|
Test test;
|
2018-06-01 09:55:36 +00:00
|
|
|
|
int repetitionNr;
|
2015-08-22 11:34:10 +00:00
|
|
|
|
|
2021-05-21 09:19:56 +00:00
|
|
|
|
|
2015-08-19 11:04:47 +00:00
|
|
|
|
///
|
2021-05-21 09:19:56 +00:00
|
|
|
|
/// Indices to determine centers of start / end samples
|
2015-08-19 11:04:47 +00:00
|
|
|
|
///
|
2021-05-21 09:19:56 +00:00
|
|
|
|
public int TestStartTelegramIx;
|
|
|
|
|
|
public int TestEndTelegramIx;
|
|
|
|
|
|
int endTelegramIdx1;
|
|
|
|
|
|
int endTelegramIdx2;
|
|
|
|
|
|
int endTelegramIdx3;
|
2015-08-19 11:04:47 +00:00
|
|
|
|
|
2021-05-21 09:19:56 +00:00
|
|
|
|
int currentTelegramIx;
|
|
|
|
|
|
bool startSampleAcquired;
|
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
|
|
|
|
|
|
///
|
|
|
|
|
|
private Int64 lastTimestamp;
|
|
|
|
|
|
private double timestampSec;
|
|
|
|
|
|
private double timestampSec0;
|
|
|
|
|
|
|
2021-05-19 08:36:54 +00:00
|
|
|
|
/// Test start volume for metrology in seconds
|
2021-05-21 09:19:56 +00:00
|
|
|
|
public double TimestampSecStart
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return TimeFromSamples(optoData, optoDataCount, TestStartTelegramIx, StartEndFilterSamplesCount2); }
|
|
|
|
|
|
}
|
2021-05-19 08:36:54 +00:00
|
|
|
|
/// Test end time for metrology in seconds
|
2021-05-21 09:19:56 +00:00
|
|
|
|
public double TimestampSecEnd
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return TimeFromSamples(optoData, optoDataCount, TestEndTelegramIx, StartEndFilterSamplesCount2); }
|
|
|
|
|
|
}
|
|
|
|
|
|
///
|
|
|
|
|
|
public bool NoSamples
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return TimestampSecStart == 0 || TimestampSecEnd == 0 || (TimestampSecEnd - TimestampSecStart) < float.Epsilon; }
|
|
|
|
|
|
}
|
2021-05-19 08:36:54 +00:00
|
|
|
|
|
|
|
|
|
|
///
|
2021-05-21 09:19:56 +00:00
|
|
|
|
/// Volume of water from the opto telegram
|
2021-05-19 08:36:54 +00:00
|
|
|
|
///
|
2021-05-21 09:19:56 +00:00
|
|
|
|
private Int64 lastVolumeRaw; /// Last read raw volume
|
|
|
|
|
|
private double volumeLtr;
|
|
|
|
|
|
private double volumeLtr0;
|
2019-05-22 12:29:20 +00:00
|
|
|
|
|
2021-05-21 09:19:56 +00:00
|
|
|
|
/// Test start volume for metrology in liters
|
|
|
|
|
|
public double VolumeLtrStart
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return NoSamples ? 0 : VolumeFromSamples(optoData, optoDataCount, TestStartTelegramIx, ScalingFactor(), StartEndFilterSamplesCount2); }
|
|
|
|
|
|
}
|
|
|
|
|
|
/// Test end volume for metrology in liters
|
|
|
|
|
|
public double VolumeLtrEnd
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return NoSamples ? 0 : VolumeFromSamples(optoData, optoDataCount, TestEndTelegramIx, ScalingFactor(), StartEndFilterSamplesCount2); }
|
|
|
|
|
|
}
|
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;
|
2021-10-08 09:36:38 +00:00
|
|
|
|
int optoDataCount; /// Real opto deta count, can be larger then optoData.Length
|
2018-12-04 06:03:04 +00:00
|
|
|
|
///
|
|
|
|
|
|
OptoTelegramRaw toBeFlushed;
|
2016-06-27 13:24:10 +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
|
|
|
|
|
|
|
|
|
|
|
2021-04-07 07:57:42 +00:00
|
|
|
|
public IperlHead() { }
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
2017-05-31 12:51:26 +00:00
|
|
|
|
public IperlHead(Generic.IComponentCfg cfg)
|
2015-07-31 08:45:50 +00:00
|
|
|
|
: base(cfg)
|
2018-12-03 07:14:15 +00:00
|
|
|
|
{
|
2017-05-31 12:51:26 +00:00
|
|
|
|
iperlHeadCfg = cfg as IperlHeadCfg;
|
2015-07-31 08:45:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-07 07:57:42 +00:00
|
|
|
|
public override void Initialize()
|
2018-12-04 06:03:04 +00:00
|
|
|
|
{
|
2022-05-27 07:30:45 +00:00
|
|
|
|
x = new float[FeatureVectorSize];
|
2021-04-07 07:57:42 +00:00
|
|
|
|
flowDirectionDetection = new FlowDirectionDetection();
|
2018-12-04 06:03:04 +00:00
|
|
|
|
|
2020-02-20 12:23:07 +00:00
|
|
|
|
/// Allocate memory for opto-data from iPerl
|
2021-10-08 09:36:38 +00:00
|
|
|
|
optoData = new OptoTelegramRaw[OptoDataBufferSize];
|
|
|
|
|
|
for (int i = 0; i < OptoDataBufferSize; i++)
|
2021-04-07 07:57:42 +00:00
|
|
|
|
{
|
|
|
|
|
|
optoData[i] = new OptoTelegramRaw();
|
|
|
|
|
|
}
|
2018-12-04 06:03:04 +00:00
|
|
|
|
|
2020-02-20 12:23:07 +00:00
|
|
|
|
toBeFlushed = new OptoTelegramRaw();
|
2018-12-04 06:03:04 +00:00
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
dataStreamState = DataStreamState.Flush;
|
2020-02-20 12:23:07 +00:00
|
|
|
|
synchronized = false;
|
|
|
|
|
|
synchronized2 = false;
|
|
|
|
|
|
partOfTelegram = string.Empty;
|
2018-12-04 06:03:04 +00:00
|
|
|
|
|
2020-02-20 12:23:07 +00:00
|
|
|
|
if (DebugLevel == DebugMode.Normal)
|
|
|
|
|
|
{
|
2022-01-24 11:55:39 +00:00
|
|
|
|
/// Open serial port: 9600 Bd, 8 data bits, 1 stop bit, no parity
|
2021-06-24 11:57:31 +00:00
|
|
|
|
string portName = string.Format("COM{0}", iperlHeadCfg.OptoComPortNr);
|
|
|
|
|
|
optoSerialPort = new SerialPort(portName, 9600, Parity.None, 8, StopBits.One);
|
2018-12-04 06:03:04 +00:00
|
|
|
|
optoSerialPort.Handshake = Handshake.None;
|
|
|
|
|
|
optoSerialPort.Open();
|
2021-04-07 07:57:42 +00:00
|
|
|
|
log.FatalFormat("{0} initialized: {1}", Name, this);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
optoSerialPort = null;
|
|
|
|
|
|
log.FatalFormat("{0} simulated: {1}", Name, this);
|
2018-12-04 06:03:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2015-12-04 11:24:55 +00:00
|
|
|
|
/// Clear data related to a specific water meter
|
|
|
|
|
|
/// </summary>
|
2021-10-08 09:36:38 +00:00
|
|
|
|
public void StartSession()
|
2015-12-04 11:24:55 +00:00
|
|
|
|
{
|
2020-12-10 10:13:23 +00:00
|
|
|
|
ResultCode = 0;
|
2017-08-21 13:43:23 +00:00
|
|
|
|
|
2020-12-10 10:13:23 +00:00
|
|
|
|
Disabled = false;
|
|
|
|
|
|
CommFailed = false;
|
2015-12-04 11:24:55 +00:00
|
|
|
|
|
2020-12-10 10:13:23 +00:00
|
|
|
|
ConfigStruct = null;
|
|
|
|
|
|
CalibrationStruct = null;
|
|
|
|
|
|
CalibrationStructV4 = null;
|
2015-12-04 11:24:55 +00:00
|
|
|
|
|
2022-09-06 17:55:54 +00:00
|
|
|
|
OrigTestModeConfig = 0;
|
|
|
|
|
|
|
2015-12-04 11:24:55 +00:00
|
|
|
|
LastTestResult = null;
|
2018-10-12 08:54:33 +00:00
|
|
|
|
LastTestResult2 = null;
|
2015-12-04 11:24:55 +00:00
|
|
|
|
|
2017-07-18 06:33:20 +00:00
|
|
|
|
OrigCalibFactor = 0;
|
2018-09-20 07:05:32 +00:00
|
|
|
|
OrigCalibFactorLNA = 0;
|
2017-07-18 06:33:20 +00:00
|
|
|
|
Q2ErrWOCorrection = 0;
|
2018-10-18 07:28:17 +00:00
|
|
|
|
Q2CorrRL = 0;
|
2019-04-04 12:39:16 +00:00
|
|
|
|
Q2CorrLR = 0;
|
2022-01-23 12:18:36 +00:00
|
|
|
|
|
2022-01-24 11:55:39 +00:00
|
|
|
|
dataStreamState = DataStreamState.Flush;
|
2018-12-03 07:14:15 +00:00
|
|
|
|
|
2018-12-04 06:03:04 +00:00
|
|
|
|
currentFlowDir = InitFlowDir;
|
2018-12-03 07:14:15 +00:00
|
|
|
|
}
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
public void SaveMark(object mark)
|
|
|
|
|
|
{
|
|
|
|
|
|
/// TODO
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void EndSession()
|
|
|
|
|
|
{
|
|
|
|
|
|
StopDataStreamProcessing();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-09-21 14:29:12 +00:00
|
|
|
|
|
2019-02-06 09:34:43 +00:00
|
|
|
|
Counting currentFlowDir;
|
2018-12-04 06:03:04 +00:00
|
|
|
|
///
|
|
|
|
|
|
public OptoHeadState CheckFlowDirection()
|
|
|
|
|
|
{
|
2019-03-28 08:33:17 +00:00
|
|
|
|
return (flowDirectionDetection != null) ? flowDirectionDetection.CheckFlowDirection(currentFlowDir, Name) : OptoHeadState.DirNok;
|
2015-08-01 19:20:22 +00:00
|
|
|
|
}
|
2018-12-04 06:03:04 +00:00
|
|
|
|
///
|
|
|
|
|
|
public void ChangeFlowDirection()
|
|
|
|
|
|
{
|
2019-02-06 09:34:43 +00:00
|
|
|
|
switch (InitFlowDir)
|
|
|
|
|
|
{
|
|
|
|
|
|
case Counting.Positive:
|
|
|
|
|
|
currentFlowDir = Counting.Negative;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case Counting.Negative:
|
|
|
|
|
|
currentFlowDir = Counting.Positive;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case Counting.Arbitrary:
|
|
|
|
|
|
default:
|
|
|
|
|
|
currentFlowDir = Counting.Arbitrary;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-12-04 06:03:04 +00:00
|
|
|
|
if (flowDirectionDetection != null) flowDirectionDetection.ClearFifo(); /// Clear FIFO for flow direction detection
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2021-10-08 09:36:38 +00:00
|
|
|
|
ReadOptoData(dataStreamState);
|
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
|
|
|
|
|
2017-12-05 18:26:07 +00:00
|
|
|
|
public void RunDeviceAfter() { }
|
2015-08-01 19:20:22 +00:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2017-12-05 18:26:07 +00:00
|
|
|
|
public void StopDevice2() { }
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2020-12-10 10:13:23 +00:00
|
|
|
|
ResultCode = 0;
|
2016-02-13 07:18:49 +00:00
|
|
|
|
|
|
|
|
|
|
volumeLtr = 0;
|
|
|
|
|
|
volumeLtr0 = 0;
|
|
|
|
|
|
timestampSec = 0;
|
|
|
|
|
|
timestampSec0 = 0;
|
|
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
extraDataPath = null;
|
2021-07-01 07:32:58 +00:00
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
/// Clear the feature vector
|
|
|
|
|
|
for (int i = 0; i < FeatureVectorSize; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
x[i] = 0;
|
|
|
|
|
|
}
|
2016-02-13 07:18:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void TestCompleted()
|
|
|
|
|
|
{
|
|
|
|
|
|
/// TODO: Implement
|
2015-07-31 08:45:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-05-22 12:29:20 +00:00
|
|
|
|
int timeFromStart; /// [s] Time from test start to determine when the test start sample should be taken
|
2015-08-22 04:32:55 +00:00
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Start this operation
|
|
|
|
|
|
/// </summary>
|
2015-07-31 08:45:50 +00:00
|
|
|
|
public void Start()
|
|
|
|
|
|
{
|
2022-01-24 11:55:39 +00:00
|
|
|
|
lock (this)
|
|
|
|
|
|
{
|
|
|
|
|
|
Clear();
|
|
|
|
|
|
ReadPulses();
|
|
|
|
|
|
StartDataStreamProcessing();
|
|
|
|
|
|
}
|
2015-08-19 11:04:47 +00:00
|
|
|
|
}
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Run this operation
|
|
|
|
|
|
/// </summary>
|
2015-07-31 08:45:50 +00:00
|
|
|
|
/// <returns>eventDone</returns>
|
|
|
|
|
|
public Event Run()
|
|
|
|
|
|
{
|
2022-01-24 11:55:39 +00:00
|
|
|
|
lock (this)
|
2019-05-21 12:40:22 +00:00
|
|
|
|
{
|
2022-01-24 11:55:39 +00:00
|
|
|
|
timeFromStart += StateMachine.Period;
|
|
|
|
|
|
ReadPulses();
|
|
|
|
|
|
|
|
|
|
|
|
if (!startSampleAcquired && (timeFromStart >= 8) && (currentTelegramIx >= 0))
|
|
|
|
|
|
{
|
|
|
|
|
|
/// Take the test start sample
|
|
|
|
|
|
startSampleAcquired = true;
|
|
|
|
|
|
TestStartTelegramIx = currentTelegramIx;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (startSampleAcquired)
|
|
|
|
|
|
{
|
|
|
|
|
|
/// Shift data in pipelines
|
|
|
|
|
|
TestEndTelegramIx = endTelegramIdx3;
|
|
|
|
|
|
endTelegramIdx3 = endTelegramIdx2;
|
|
|
|
|
|
endTelegramIdx2 = endTelegramIdx1;
|
|
|
|
|
|
endTelegramIdx1 = currentTelegramIx;
|
|
|
|
|
|
}
|
2019-05-21 12:40:22 +00:00
|
|
|
|
}
|
2015-12-17 11:29:39 +00:00
|
|
|
|
|
2015-07-31 08:45:50 +00:00
|
|
|
|
return Event.ReadRegisterDone;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Stop this operation
|
|
|
|
|
|
/// </summary>
|
2015-07-31 08:45:50 +00:00
|
|
|
|
public void Stop()
|
|
|
|
|
|
{
|
2021-07-01 07:32:58 +00:00
|
|
|
|
log.DebugFormat("Flow filtering end, feature vector calculation start: {0:HH:mm:ss.fff}", DateTime.Now);
|
|
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
int startIx;
|
|
|
|
|
|
int endIx;
|
2022-01-24 11:55:39 +00:00
|
|
|
|
|
|
|
|
|
|
lock (this)
|
|
|
|
|
|
{
|
|
|
|
|
|
StopDataStreamProcessing();
|
|
|
|
|
|
AddTestStartEndMarksToData(out startIx, out endIx);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
DataStreamPostProcessing();
|
2021-07-01 07:32:58 +00:00
|
|
|
|
|
2021-10-10 12:39:51 +00:00
|
|
|
|
// TODO: Enable when calculations completed
|
|
|
|
|
|
//
|
|
|
|
|
|
// float[] offsetV, kOhmsR, kOhmsC, dutFlow, refFlow, flowRatio, magField, emfV;
|
|
|
|
|
|
// x = Common.StatisticalMetrics.Calculate(optoData, optoDataCount, startIx, endIx, true,
|
|
|
|
|
|
// out offsetV, out kOhmsR, out kOhmsC, out dutFlow,
|
|
|
|
|
|
// out refFlow, out flowRatio, out magField, out emfV);
|
|
|
|
|
|
//
|
|
|
|
|
|
// log.DebugFormat("Feature vector calculation end, save opto-file start: {0:HH:mm:ss.fff}", DateTime.Now);
|
2021-07-01 07:32:58 +00:00
|
|
|
|
|
2023-01-04 08:02:28 +00:00
|
|
|
|
#if ORACLE_DB
|
2023-01-13 08:23:45 +00:00
|
|
|
|
if (test.RawDataId + (test.Repeats - repetitionNr) * test.RawDataIdRepetMulti != 0)
|
2021-10-08 09:36:38 +00:00
|
|
|
|
{
|
2022-12-01 08:54:04 +00:00
|
|
|
|
string relativeDirectory = Path.Combine(StateMachine.CycleStartTimeStamp.ToString("yy"),
|
|
|
|
|
|
StateMachine.CycleStartTimeStamp.ToString("MM"),
|
|
|
|
|
|
StateMachine.CycleStartTimeStamp.ToString("dd"));
|
|
|
|
|
|
string fileName = DetermineExtraDataFileName();
|
|
|
|
|
|
if (SaveOptoDataToFile(Path.Combine(OptoDataDirectory, relativeDirectory), fileName))
|
2021-10-08 09:36:38 +00:00
|
|
|
|
{
|
|
|
|
|
|
extraDataPath = Path.Combine(relativeDirectory, fileName);
|
|
|
|
|
|
}
|
2017-08-21 13:43:23 +00:00
|
|
|
|
|
2022-12-01 08:54:04 +00:00
|
|
|
|
log.WarnFormat("IperlHead.Stop() startIx={0} endIx={1} len={2} raw data file = {3}",
|
|
|
|
|
|
startIx, endIx, optoData.Length, fileName);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2023-01-04 08:02:28 +00:00
|
|
|
|
#endif
|
2022-12-01 08:54:04 +00:00
|
|
|
|
{
|
|
|
|
|
|
log.WarnFormat("IperlHead.Stop() startIx={0} endIx={1} len={2} no raw data file", startIx, endIx, optoData.Length);
|
|
|
|
|
|
}
|
2021-07-01 07:32:58 +00:00
|
|
|
|
|
2019-02-06 09:34:43 +00:00
|
|
|
|
if (TestStartTelegramIx == 0 || optoDataCount < 100)
|
|
|
|
|
|
{
|
2020-12-10 10:13:23 +00:00
|
|
|
|
ResultCode |= (int)Results.Entities.ResultCode.MissingOptoData;
|
2019-02-06 09:34:43 +00:00
|
|
|
|
}
|
2021-05-19 08:36:54 +00:00
|
|
|
|
else if (VolumeLtrEnd == VolumeLtrStart)
|
2019-02-06 09:34:43 +00:00
|
|
|
|
{
|
2020-12-10 10:13:23 +00:00
|
|
|
|
ResultCode |= (int)Results.Entities.ResultCode.OptoDataWithZeroFlow;
|
2019-02-06 09:34:43 +00:00
|
|
|
|
}
|
2015-08-19 11:04:47 +00:00
|
|
|
|
}
|
2015-07-31 08:45:50 +00:00
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
void AddTestStartEndMarksToData(out int startIx, out int endIx)
|
|
|
|
|
|
{
|
|
|
|
|
|
startIx = BufferIdx(TestStartTelegramIx);
|
|
|
|
|
|
if ((startIx > 0) && (startIx < StartOptoDataCount) && (optoData[startIx].Flags == OptoTelegramFlags.OK))
|
|
|
|
|
|
{
|
|
|
|
|
|
optoData[startIx].Flags = OptoTelegramFlags.OK_TestStart;
|
|
|
|
|
|
OptoTelegramRaw.TestStartTimestampDec = optoData[startIx].TimestampDec();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int ix = 0; ix < StartOptoDataCount; ix++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (optoData[ix].Flags == OptoTelegramFlags.OK)
|
|
|
|
|
|
{
|
|
|
|
|
|
/// This is the first correct opto-telegram received
|
|
|
|
|
|
OptoTelegramRaw.TestStartTimestampDec = optoData[ix].TimestampDec();
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
endIx = BufferIdx(TestEndTelegramIx);
|
|
|
|
|
|
if ((endIx > 0) && (optoData[endIx].Flags == OptoTelegramFlags.OK))
|
|
|
|
|
|
{
|
|
|
|
|
|
optoData[endIx].Flags = OptoTelegramFlags.OK_TestEnd;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Data stream post processing:
|
|
|
|
|
|
/// Flow from a reference flowmeter is FIR filtered
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
void DataStreamPostProcessing()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (optoDataCount <= OptoDataBufferSize)
|
|
|
|
|
|
{
|
|
|
|
|
|
FIRFilterFlow(optoData, 0, optoDataCount - 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
FIRFilterFlow(optoData, 0, StartOptoDataCount - 1);
|
|
|
|
|
|
FIRFilterFlow(optoData, (optoDataCount - EndOptoDataCount), optoDataCount - 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Determine opto data file name: PCB_AA_BB_HH_MI_SS..txt
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="testName">Test name</param>
|
|
|
|
|
|
/// <param name="testRepeats">Test repeats count (>= 1)</param>
|
|
|
|
|
|
/// <param name="repetitionNr">Repetition number (1 .. testRepeats)</param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
string DetermineExtraDataFileName()
|
|
|
|
|
|
{
|
2021-10-20 07:17:10 +00:00
|
|
|
|
///
|
2022-12-01 08:54:04 +00:00
|
|
|
|
/// Get required pieces of information
|
2021-10-20 07:17:10 +00:00
|
|
|
|
///
|
2021-10-08 09:36:38 +00:00
|
|
|
|
string pcbNr = (ConfigStruct != null) ? ConfigStruct.GetPcbNrString() : "UnknownPcbNr";
|
|
|
|
|
|
string wmPosition = Name.Substring(5); /// WMPosition is extracted from a component name in form 'iPerl#'
|
|
|
|
|
|
if (wmPosition.Length == 1) wmPosition = "0" + wmPosition;
|
|
|
|
|
|
string cycleStartTime = StateMachine.CycleStartTimeStamp.ToString("HH_mm_ss");
|
2023-01-04 08:02:28 +00:00
|
|
|
|
#if ORACLE_DB
|
2022-12-01 08:54:04 +00:00
|
|
|
|
string[] designations = string.IsNullOrEmpty(test.RawDataDesignation) ? new string[0] : test.RawDataDesignation.Split(new char[] { '~' });
|
2023-01-13 08:23:45 +00:00
|
|
|
|
int testId = test.RawDataId + (test.Repeats - repetitionNr) * test.RawDataIdRepetMulti;
|
2022-12-01 08:54:04 +00:00
|
|
|
|
string designation = string.IsNullOrEmpty(test.RawDataDesignation)
|
2022-12-13 15:32:46 +00:00
|
|
|
|
? testId.ToString(testId > 0 ? "D2" : "D1") /// Name is generated from Id
|
|
|
|
|
|
: (designations.Length > repetitionNr - 1) ? designations[repetitionNr - 1] /// Name is from 'RawDataDesignation' parameter
|
2023-01-04 08:02:28 +00:00
|
|
|
|
: string.Format("{0}-{1}", designations[0], repetitionNr); /// Name is form test name and repetition nr.
|
|
|
|
|
|
#else
|
|
|
|
|
|
int testId = 0;
|
|
|
|
|
|
string designation = (test.Repeats == 1) ? test.Name : string.Format("{0}-{1}", test.Name, repetitionNr);
|
|
|
|
|
|
#endif
|
2021-10-08 09:36:38 +00:00
|
|
|
|
///
|
2021-10-20 07:17:10 +00:00
|
|
|
|
/// Return the file name
|
2022-12-01 08:54:04 +00:00
|
|
|
|
///
|
|
|
|
|
|
return string.Format("{0}_{1}_{2}_{3}.txt", pcbNr, wmPosition, designation, cycleStartTime);
|
2021-10-08 09:36:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-12-17 13:12:55 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Save opto data to a file.
|
|
|
|
|
|
/// </summary>
|
2021-10-08 09:36:38 +00:00
|
|
|
|
bool SaveOptoDataToFile(string directory, string fileName)
|
2015-08-21 14:06:38 +00:00
|
|
|
|
{
|
2021-10-08 09:36:38 +00:00
|
|
|
|
string fullFileName = Path.Combine(directory, fileName);
|
2022-12-01 08:54:04 +00:00
|
|
|
|
log.WarnFormat("Saving {0} raw data to {1}", Name, fullFileName);
|
2021-10-08 09:36:38 +00:00
|
|
|
|
|
|
|
|
|
|
try
|
2016-01-08 15:57:37 +00:00
|
|
|
|
{
|
2021-10-08 09:36:38 +00:00
|
|
|
|
Directory.CreateDirectory(directory);
|
2018-12-18 11:37:49 +00:00
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
double scalFact = ScalingFactor();
|
2018-12-18 11:37:49 +00:00
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
using (TextWriter optoLogFile = new StreamWriter(fullFileName))
|
2018-12-18 11:37:49 +00:00
|
|
|
|
{
|
2021-10-08 09:36:38 +00:00
|
|
|
|
if (optoDataCount <= OptoDataBufferSize)
|
2018-12-17 13:12:55 +00:00
|
|
|
|
{
|
2021-10-08 09:36:38 +00:00
|
|
|
|
/// Telegrams are stored continuously, save them.
|
|
|
|
|
|
optoLogFile.WriteLine(optoData[0].ToString(scalFact, null));
|
|
|
|
|
|
for (int i = 1; i < optoDataCount; i++)
|
2018-12-17 13:12:55 +00:00
|
|
|
|
{
|
2021-10-08 09:36:38 +00:00
|
|
|
|
optoLogFile.WriteLine(optoData[i].ToString(scalFact, optoData[i - 1]));
|
2018-12-17 13:12:55 +00:00
|
|
|
|
}
|
2021-10-08 09:36:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
else /// if (optoDataCount > MaxOptoDataCount)
|
|
|
|
|
|
{
|
|
|
|
|
|
/// Buffer overflow
|
|
|
|
|
|
/// First part of the buffer is saved as is
|
|
|
|
|
|
optoLogFile.WriteLine(optoData[0].ToString(scalFact, null));
|
|
|
|
|
|
for (int i = 1; i < StartOptoDataCount; i++)
|
2018-12-17 13:12:55 +00:00
|
|
|
|
{
|
2021-10-08 09:36:38 +00:00
|
|
|
|
optoLogFile.WriteLine(optoData[i].ToString(scalFact, optoData[i - 1]));
|
2018-12-17 13:12:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
optoLogFile.WriteLine(" ...");
|
2018-12-17 13:12:55 +00:00
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
/// Second part of the buffer is an overflowed circular buffer
|
|
|
|
|
|
optoLogFile.WriteLine(optoData[BufferIdx(optoDataCount)].ToString(scalFact, null));
|
|
|
|
|
|
for (int i = optoDataCount - EndOptoDataCount + 1; i < optoDataCount; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
optoLogFile.WriteLine(optoData[BufferIdx(i)].ToString(scalFact, optoData[BufferIdx(i - 1)]));
|
|
|
|
|
|
}
|
2018-12-17 13:12:55 +00:00
|
|
|
|
}
|
2021-06-24 10:19:15 +00:00
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
log.WarnFormat("{0} opto data successfully saved: {1} lines", Name, optoDataCount);
|
|
|
|
|
|
|
|
|
|
|
|
optoLogFile.Close();
|
2018-12-18 11:37:49 +00:00
|
|
|
|
}
|
2021-06-24 10:19:15 +00:00
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception exc)
|
|
|
|
|
|
{
|
|
|
|
|
|
File.Delete(fullFileName);
|
|
|
|
|
|
log.ErrorFormat(string.Format("Error writing into file {0}", fullFileName));
|
|
|
|
|
|
log.ErrorFormat(string.Format("Exception message: {0}", exc.Message));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2021-06-24 10:19:15 +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;
|
2017-03-20 20:29:48 +00:00
|
|
|
|
wmVolume = Math.Abs(endWMState - beginWMState);
|
2016-02-13 07:18:49 +00:00
|
|
|
|
wmPulses = (int)(wmVolume * (double)PulsesPerLtr + 0.5);
|
2021-10-01 12:54:19 +00:00
|
|
|
|
wmRefPulses = StateMachine.ControlBoard.RefPulses;
|
2016-02-13 07:18:49 +00:00
|
|
|
|
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
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
DataStreamState dataStreamState;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Reset counters / indices / time and start processing and saving datastream data
|
|
|
|
|
|
/// </summary>
|
2022-01-24 11:55:39 +00:00
|
|
|
|
public void StartDataStreamProcessing()
|
2021-10-08 09:36:38 +00:00
|
|
|
|
{
|
|
|
|
|
|
/// Reset opto-data, etc.
|
|
|
|
|
|
optoDataCount = 0;
|
|
|
|
|
|
timeFromStart = 0;
|
|
|
|
|
|
currentTelegramIx = -1;
|
|
|
|
|
|
startSampleAcquired = false;
|
|
|
|
|
|
TestStartTelegramIx = 0;
|
|
|
|
|
|
endTelegramIdx1 = 0;
|
|
|
|
|
|
endTelegramIdx2 = 0;
|
|
|
|
|
|
endTelegramIdx3 = 0;
|
|
|
|
|
|
TestEndTelegramIx = 0;
|
|
|
|
|
|
|
2022-01-24 11:55:39 +00:00
|
|
|
|
if (optoSerialPort != null && optoSerialPort.IsOpen) optoSerialPort.DiscardInBuffer();
|
|
|
|
|
|
|
|
|
|
|
|
if (flowDirectionDetection != null) flowDirectionDetection.ClearFifo(); /// Clear FIFO for flow direction detection
|
|
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
/// Enable opto-data parsing and saving
|
|
|
|
|
|
dataStreamState = DataStreamState.ProcessAndSave;
|
|
|
|
|
|
}
|
2015-08-15 21:01:26 +00:00
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Returns true when processing and saving datastream data is in progress
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
bool IsDataStreamProcessing()
|
2015-08-19 11:04:47 +00:00
|
|
|
|
{
|
2021-10-08 09:36:38 +00:00
|
|
|
|
return dataStreamState == DataStreamState.ProcessAndSave;
|
2015-08-19 11:04:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-08 09:36:38 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Stop processing and saving datastream data
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
void StopDataStreamProcessing()
|
2015-08-19 11:04:47 +00:00
|
|
|
|
{
|
2021-10-08 09:36:38 +00:00
|
|
|
|
dataStreamState = DataStreamState.Flush;
|
2015-08-19 11:04:47 +00:00
|
|
|
|
}
|
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>
|
2022-01-24 11:55:39 +00:00
|
|
|
|
/// Reads opto-datastream via serual port. Invoked from RunDeviceBefore()
|
|
|
|
|
|
///
|
2015-08-15 21:01:26 +00:00
|
|
|
|
/// Telegram description:
|
2022-01-24 11:55:39 +00:00
|
|
|
|
/// AAAAAA[tab]BBBB[tab]CCCC[tab]DDDDDD[tab]EEEE[tab]FFFFFFFF[tab]GG[cr][lf] (42 bytes)
|
2015-08-15 21:01:26 +00:00
|
|
|
|
/// Example:
|
2022-01-24 11:55:39 +00:00
|
|
|
|
/// FFFFFE 51EA 0000 65324E 0087 F6319DFF 86
|
|
|
|
|
|
/// FFDD3A 51F9 0000 65324E 0088 F631A60B 45
|
2015-08-15 21:01:26 +00:00
|
|
|
|
/// ...
|
|
|
|
|
|
/// </summary>
|
2015-09-21 14:29:12 +00:00
|
|
|
|
/// <param name="optoState">OptoState.Read or OptoState.Flush</param>
|
2021-10-08 09:36:38 +00:00
|
|
|
|
void ReadOptoData(DataStreamState optoState)
|
2015-08-15 21:01:26 +00:00
|
|
|
|
{
|
2022-01-24 11:55:39 +00:00
|
|
|
|
lock (this)
|
|
|
|
|
|
{
|
|
|
|
|
|
int nrBytes = optoSerialPort.BytesToRead;
|
|
|
|
|
|
if (nrBytes > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
char[] buffer = new char[nrBytes];
|
|
|
|
|
|
optoSerialPort.Read(buffer, 0, nrBytes);
|
|
|
|
|
|
string received = new string(buffer);
|
2015-08-15 21:01:26 +00:00
|
|
|
|
|
2022-01-24 11:55:39 +00:00
|
|
|
|
string allRcvd = partOfTelegram + received;
|
2015-08-15 21:01:26 +00:00
|
|
|
|
|
2022-01-24 11:55:39 +00:00
|
|
|
|
while (true)
|
|
|
|
|
|
{
|
|
|
|
|
|
int pos = allRcvd.IndexOf("\r\n");
|
2015-08-15 21:01:26 +00:00
|
|
|
|
|
2022-01-24 11:55:39 +00:00
|
|
|
|
if (pos < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
/// No CR+LF found, wait for more characters in the next invocation
|
|
|
|
|
|
partOfTelegram = allRcvd;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
/// CR+LF found
|
|
|
|
|
|
if (optoState == DataStreamState.ProcessAndSave)
|
|
|
|
|
|
{
|
|
|
|
|
|
int bufferIx = BufferIdx(optoDataCount);
|
|
|
|
|
|
|
|
|
|
|
|
if (pos < OptoTelegramRaw.Length - 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
/// CR+LF found too early, truncate the beginning incl CR+LF and keep scanning in this loop
|
|
|
|
|
|
allRcvd = allRcvd.Substring(pos + 2);
|
|
|
|
|
|
if (synchronized)
|
|
|
|
|
|
{
|
|
|
|
|
|
optoData[bufferIx].Counter = optoDataCount;
|
|
|
|
|
|
optoData[bufferIx].SetFlags(OptoTelegramFlags.SyncError);
|
|
|
|
|
|
}
|
|
|
|
|
|
synchronized = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (optoData[bufferIx].UpdateFromString(allRcvd.Substring(pos - OptoTelegramRaw.Length + 2),
|
|
|
|
|
|
optoDataCount,
|
|
|
|
|
|
Convert.ToSingle(Sequences.ProcessData.RefFlow.Val),
|
|
|
|
|
|
ref volumeRawExtLast, ref timestampExtLast))
|
|
|
|
|
|
{
|
|
|
|
|
|
/// CR+LF was found && (pos >= OptoTelegramRaw.Length - 2) && the telegram is OK
|
|
|
|
|
|
flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast);
|
2022-12-01 08:54:04 +00:00
|
|
|
|
OptoTelegramReceived(optoDataCount, synchronized2, volumeRawExtLast, timestampExtLast);
|
2022-01-24 11:55:39 +00:00
|
|
|
|
synchronized2 = synchronized;
|
|
|
|
|
|
allRcvd = allRcvd.Substring(pos + 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
/// CR+LF was found && (pos >= OptoTelegramRaw.Length - 2) but the telgram was not OK
|
|
|
|
|
|
optoData[bufferIx].Counter = optoDataCount;
|
|
|
|
|
|
optoDataCount++;
|
|
|
|
|
|
allRcvd = allRcvd.Substring(pos + 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
optoDataCount++;
|
|
|
|
|
|
}
|
|
|
|
|
|
else /// optoState == OptoState.Flush
|
|
|
|
|
|
{
|
|
|
|
|
|
if (pos < OptoTelegramRaw.Length - 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
/// 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)
|
|
|
|
|
|
else if (toBeFlushed.UpdateFromString(allRcvd.Substring(pos - OptoTelegramRaw.Length + 2),
|
|
|
|
|
|
0,
|
|
|
|
|
|
Convert.ToSingle(Sequences.ProcessData.RefFlow.Val),
|
|
|
|
|
|
ref volumeRawExtLast, ref timestampExtLast))
|
|
|
|
|
|
{
|
|
|
|
|
|
flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast);
|
|
|
|
|
|
synchronized2 = synchronized;
|
|
|
|
|
|
allRcvd = allRcvd.Substring(pos + 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
allRcvd = allRcvd.Substring(pos + 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2015-08-21 18:52:10 +00:00
|
|
|
|
|
2022-01-24 11:55:39 +00:00
|
|
|
|
//OnOptoReceived(this, new OptoReceivedEventArgs(s));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
//OnOptoReceived(this, new OptoReceivedEventArgs("."));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2015-08-15 21:01:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-08-19 11:04:47 +00:00
|
|
|
|
|
2022-12-01 08:54:04 +00:00
|
|
|
|
void OptoTelegramReceived(int currentIx, bool async, Int64 volumeRawExt, Int64 timestampRawExt)
|
2015-08-15 21:01:26 +00:00
|
|
|
|
{
|
2019-05-21 12:40:22 +00:00
|
|
|
|
currentTelegramIx = currentIx;
|
2015-12-17 11:29:39 +00:00
|
|
|
|
|
2018-12-03 07:14:15 +00:00
|
|
|
|
lastVolumeRaw = volumeRawExt;
|
|
|
|
|
|
lastTimestamp = timestampRawExt;
|
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-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;
|
|
|
|
|
|
|
2017-04-28 08:14:49 +00:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Compares CalibrationStruct.MeterType with iPerlCfg.MeterType.
|
|
|
|
|
|
/// iPerlCfg.MeterType == MeterType.AutoDetect disables type checking
|
|
|
|
|
|
/// CalibrationStruct == null disables type checking ...
|
|
|
|
|
|
/// ... so that failed RFID communication does not cause type verification failure)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>true when type is OK</returns>
|
|
|
|
|
|
public bool VerifyIPerlType()
|
|
|
|
|
|
{
|
2017-05-31 12:51:26 +00:00
|
|
|
|
if (iperlHeadCfg.MeterType == MeterType.AutoDetect || CalibrationStruct == null)
|
2017-04-28 08:14:49 +00:00
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-05-31 12:51:26 +00:00
|
|
|
|
return iperlHeadCfg.MeterType == CalibrationStruct.MeterType;
|
2017-04-28 08:14:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-08-19 11:04:47 +00:00
|
|
|
|
public static double UnitVolume(VolumeUnits units)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (units)
|
|
|
|
|
|
{
|
|
|
|
|
|
default:
|
2022-01-23 18:56:15 +00:00
|
|
|
|
case VolumeUnits.m3: return Common.Units.ConvertFrom(Common.Unit.m3, 1.0); /// 1 liter
|
|
|
|
|
|
case VolumeUnits.UK_gallon: return Common.Units.ConvertFrom(Common.Unit.UKgal, 1.0); /// 1 imperial gallon
|
|
|
|
|
|
case VolumeUnits.US_gallon: return Common.Units.ConvertFrom(Common.Unit.USgal, 1.0); /// 1 US gallon
|
2015-08-19 11:04:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public double ScalingFactor()
|
|
|
|
|
|
{
|
2017-05-31 12:51:26 +00:00
|
|
|
|
if ((iperlHeadCfg.MeterType == MeterType.AutoDetect) && (CalibrationStruct != null))
|
2015-08-19 11:04:47 +00:00
|
|
|
|
{
|
2017-05-31 12:51:26 +00:00
|
|
|
|
return IperlHead.ScalingFactor(CalibrationStruct.MeterType);
|
2015-08-19 11:04:47 +00:00
|
|
|
|
}
|
2017-05-31 12:51:26 +00:00
|
|
|
|
else if (iperlHeadCfg.MeterType != MeterType.AutoDetect)
|
2015-08-19 11:04:47 +00:00
|
|
|
|
{
|
2017-05-31 12:51:26 +00:00
|
|
|
|
return IperlHead.ScalingFactor(iperlHeadCfg.MeterType);
|
2015-08-19 11:04:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2017-05-31 12:51:26 +00:00
|
|
|
|
return IperlHead.ScalingFactor(MeterType.DN20);
|
2015-08-19 11:04:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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:
|
2021-07-28 12:47:05 +00:00
|
|
|
|
case MeterType.CoaxManifold:
|
|
|
|
|
|
return 1.0;
|
|
|
|
|
|
|
|
|
|
|
|
case MeterType.DN20:
|
|
|
|
|
|
return 2.0;
|
|
|
|
|
|
|
|
|
|
|
|
case MeterType.DN25:
|
|
|
|
|
|
return 4.0;
|
|
|
|
|
|
|
|
|
|
|
|
case MeterType.DN25_Q3_10:
|
|
|
|
|
|
case MeterType.DN32:
|
|
|
|
|
|
return 8.0;
|
|
|
|
|
|
|
|
|
|
|
|
case MeterType.DN40:
|
|
|
|
|
|
return 16.0;
|
2015-08-19 11:04:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-02-04 07:10:19 +00:00
|
|
|
|
|
2021-05-19 08:36:54 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Calculate a filtered volume from data stream samples
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="unwrappedIx">Samples used in calculation are centered around unwrappedIx</param>
|
|
|
|
|
|
/// <param name="samplesCount2">Count of samples used in calculation is 2 * smaplesCount2 + 1</param>
|
|
|
|
|
|
/// <returns>Filtered volume</returns>
|
|
|
|
|
|
double VolumeFromSamples(OptoTelegramRaw[] optoData, int optoDataCount, int unwrappedIx, double scalingFactor, int samplesCount2 = 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (samplesCount2 < 0) samplesCount2 = 0;
|
|
|
|
|
|
if ((unwrappedIx - samplesCount2) < 0 || (unwrappedIx + samplesCount2) >= optoDataCount) return 0;
|
|
|
|
|
|
|
|
|
|
|
|
Int64 sum = 0;
|
|
|
|
|
|
for (int i = unwrappedIx - samplesCount2; i <= unwrappedIx + samplesCount2; i++)
|
|
|
|
|
|
{
|
2021-07-06 11:11:42 +00:00
|
|
|
|
int wrappedIx = BufferIdx(i);
|
2021-05-19 08:36:54 +00:00
|
|
|
|
|
|
|
|
|
|
if (optoData[wrappedIx].Flags != OptoTelegramFlags.OK &&
|
|
|
|
|
|
optoData[wrappedIx].Flags != OptoTelegramFlags.OK_TestStart &&
|
|
|
|
|
|
optoData[wrappedIx].Flags != OptoTelegramFlags.OK_TestEnd)
|
|
|
|
|
|
{
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sum += optoData[wrappedIx].VolumeRawExt;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 0.0000625 * scalingFactor * sum / (double)(2 * samplesCount2 + 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Calculate a filtered time from data stream samples
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="unwrappedIx">Samples used in calculation are centered around unwrappedIx</param>
|
|
|
|
|
|
/// <param name="samplesCount2">Count of samples used in calculation is 2 * smaplesCount2 + 1</param>
|
|
|
|
|
|
/// <returns>Filtered time</returns>
|
|
|
|
|
|
double TimeFromSamples(OptoTelegramRaw[] optoData, int optoDataCount, int unwrappedIx, int samplesCount2 = 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (samplesCount2 < 0) samplesCount2 = 0;
|
|
|
|
|
|
if ((unwrappedIx - samplesCount2) < 0 || (unwrappedIx + samplesCount2) >= optoDataCount) return 0;
|
|
|
|
|
|
|
|
|
|
|
|
Int64 sum = 0;
|
|
|
|
|
|
for (int i = unwrappedIx - samplesCount2; i <= unwrappedIx + samplesCount2; i++)
|
|
|
|
|
|
{
|
2021-07-06 11:11:42 +00:00
|
|
|
|
int wrappedIx = BufferIdx(i);
|
2021-05-19 08:36:54 +00:00
|
|
|
|
|
|
|
|
|
|
if (optoData[wrappedIx].Flags != OptoTelegramFlags.OK &&
|
|
|
|
|
|
optoData[wrappedIx].Flags != OptoTelegramFlags.OK_TestStart &&
|
|
|
|
|
|
optoData[wrappedIx].Flags != OptoTelegramFlags.OK_TestEnd)
|
|
|
|
|
|
{
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sum += optoData[wrappedIx].TimestampExt;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return sum / (double)(8192 * (2 * samplesCount2 + 1));
|
|
|
|
|
|
}
|
2020-02-04 07:10:19 +00:00
|
|
|
|
|
2021-07-06 11:11:42 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Filter RefFlow data in an array of OptoTelegramRaw objects by a FIR filter:
|
|
|
|
|
|
///
|
|
|
|
|
|
/// kSize = 5, kSize2 = 2
|
|
|
|
|
|
///
|
|
|
|
|
|
/// i k
|
|
|
|
|
|
/// ---------------------------------------------------------------------------
|
|
|
|
|
|
/// 0 -5 filtered[0] = data[0]
|
|
|
|
|
|
/// 1 -4 filtered[1] = data[1]
|
|
|
|
|
|
/// 2 -3 filtered[2] = data[0]*k[0] + ... + data[4]*k[4]
|
|
|
|
|
|
/// 3 -2 filtered[3] = data[1]*k[0] + ... + data[5]*k[4]
|
|
|
|
|
|
/// 4 -1 filtered[4] = data[2]*k[0] + ... + data[6]*k[4]
|
|
|
|
|
|
/// 5 0 data[0] = filtered[0], filtered[0] = data[3]*k[0] + ... + data[7]*k[4]
|
|
|
|
|
|
/// 6 1 data[1] = filtered[1], filtered[1] = data[4]*k[0] + ... + data[8]*k[4]
|
|
|
|
|
|
/// 7 ...
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="optoData">array of OptoTelegramRaw objects</param>
|
|
|
|
|
|
/// <param name="from">Index of the first optoData item to process</param>
|
|
|
|
|
|
/// <param name="to">Index of the last optoData item to process</param>
|
|
|
|
|
|
public static void FIRFilterFlow(OptoTelegramRaw[] optoData, int from, int to)
|
|
|
|
|
|
{
|
|
|
|
|
|
float[] kernel = new float[] { 0.1f, 0.2f, 0.4f, 0.2f, 0.1f };
|
|
|
|
|
|
int kSize = kernel.Length;
|
|
|
|
|
|
int kSize2 = kernel.Length / 2;
|
|
|
|
|
|
|
|
|
|
|
|
float[] filtered = new float[kSize];
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = from; i <= to; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ((i < from + kSize2) || (i > to - kSize2))
|
|
|
|
|
|
{
|
|
|
|
|
|
/// Beginning or end of optoData buffer => Just copy data (=do not filter)
|
|
|
|
|
|
filtered[i % kSize] = optoData[BufferIdx(i)].RefFlow;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
/// Make a convolution of optoData and the kernel
|
|
|
|
|
|
float weightedSum = 0;
|
|
|
|
|
|
for (int j = -kSize2; j <= kSize2; j++)
|
|
|
|
|
|
weightedSum += optoData[BufferIdx(i + j)].RefFlow * kernel[j + kSize2];
|
|
|
|
|
|
|
|
|
|
|
|
filtered[i % kSize] = weightedSum;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (i >= from + kSize)
|
|
|
|
|
|
{
|
|
|
|
|
|
/// filtered[] buffer full => copy filtered data to optoData
|
|
|
|
|
|
optoData[BufferIdx(i - kSize)].RefFlow = filtered[i % kSize];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = to - kSize + 1; i <= to; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i >= 0) optoData[BufferIdx(i)].RefFlow = filtered[i % kSize];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Get index to optoData buffer
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="index">Original unwrapped index</param>
|
|
|
|
|
|
/// <returns>Index to the buffer</returns>
|
|
|
|
|
|
public static int BufferIdx(int index)
|
|
|
|
|
|
{
|
2021-10-08 09:36:38 +00:00
|
|
|
|
if (index < IperlHead.OptoDataBufferSize)
|
2021-07-06 11:11:42 +00:00
|
|
|
|
{
|
|
|
|
|
|
return index;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2021-10-08 09:36:38 +00:00
|
|
|
|
return IperlHead.StartOptoDataCount + (index - IperlHead.OptoDataBufferSize) % IperlHead.EndOptoDataCount;
|
2021-07-06 11:11:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-04 07:10:19 +00:00
|
|
|
|
public void WriteBinary(BinaryWriter writer)
|
|
|
|
|
|
{
|
2020-12-10 10:13:23 +00:00
|
|
|
|
writer.Write(Disabled);
|
|
|
|
|
|
writer.Write(CommFailed);
|
|
|
|
|
|
writer.Write(ResultCode);
|
2020-02-04 07:10:19 +00:00
|
|
|
|
writer.Write(PositiveCounting);
|
|
|
|
|
|
|
2020-12-10 10:13:23 +00:00
|
|
|
|
if (ConfigStruct != null)
|
2020-02-04 07:10:19 +00:00
|
|
|
|
{
|
|
|
|
|
|
writer.Write(true);
|
2020-12-10 10:13:23 +00:00
|
|
|
|
ConfigStruct.WriteBinary(writer);
|
2020-02-04 07:10:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
else writer.Write(false);
|
|
|
|
|
|
|
2020-12-10 10:13:23 +00:00
|
|
|
|
if (CalibrationStruct != null)
|
2020-02-04 07:10:19 +00:00
|
|
|
|
{
|
|
|
|
|
|
writer.Write(true);
|
2020-12-10 10:13:23 +00:00
|
|
|
|
CalibrationStruct.WriteBinary(writer);
|
2020-02-04 07:10:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
else writer.Write(false);
|
|
|
|
|
|
|
2020-12-10 10:13:23 +00:00
|
|
|
|
if (CalibrationStructV4 != null)
|
2020-02-04 07:10:19 +00:00
|
|
|
|
{
|
|
|
|
|
|
writer.Write(true);
|
2020-12-10 10:13:23 +00:00
|
|
|
|
CalibrationStructV4.WriteBinary(writer);
|
2020-02-04 07:10:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
else writer.Write(false);
|
|
|
|
|
|
|
|
|
|
|
|
writer.Write(OrigCalibFactor);
|
|
|
|
|
|
writer.Write(OrigCalibFactorLNA);
|
|
|
|
|
|
writer.Write(Q2ErrWOCorrection);
|
|
|
|
|
|
writer.Write(Q2CorrRL);
|
|
|
|
|
|
writer.Write(Q2CorrLR);
|
|
|
|
|
|
writer.Write(Diff2Hz8Hz);
|
|
|
|
|
|
writer.Write(Hz2CorrectionDone);
|
|
|
|
|
|
writer.Write(Hz2Correction);
|
|
|
|
|
|
|
|
|
|
|
|
if (LastTestResult != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
writer.Write(true);
|
|
|
|
|
|
LastTestResult.WriteBinary(writer);
|
|
|
|
|
|
}
|
|
|
|
|
|
else writer.Write(false);
|
|
|
|
|
|
|
|
|
|
|
|
if (LastTestResult2 != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
writer.Write(true);
|
|
|
|
|
|
LastTestResult2.WriteBinary(writer);
|
|
|
|
|
|
}
|
|
|
|
|
|
else writer.Write(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void ReadBinary(BinaryReader reader)
|
|
|
|
|
|
{
|
2020-12-10 10:13:23 +00:00
|
|
|
|
Disabled = reader.ReadBoolean();
|
|
|
|
|
|
CommFailed = reader.ReadBoolean();
|
|
|
|
|
|
ResultCode = reader.ReadInt32();
|
2020-02-04 07:10:19 +00:00
|
|
|
|
PositiveCounting = reader.ReadBoolean();
|
|
|
|
|
|
|
2020-12-10 10:13:23 +00:00
|
|
|
|
if (reader.ReadBoolean()) (ConfigStruct = new ConfigStruct()).ReadBinary(reader);
|
|
|
|
|
|
if (reader.ReadBoolean()) (CalibrationStruct = new CalibrationStruct()).ReadBinary(reader);
|
|
|
|
|
|
if (reader.ReadBoolean()) (CalibrationStructV4 = new CalibrationStructV4()).ReadBinary(reader);
|
2020-02-04 07:10:19 +00:00
|
|
|
|
|
|
|
|
|
|
OrigCalibFactor = reader.ReadUInt16();
|
|
|
|
|
|
OrigCalibFactorLNA = reader.ReadUInt16();
|
|
|
|
|
|
Q2ErrWOCorrection = reader.ReadDouble();
|
|
|
|
|
|
Q2CorrRL = reader.ReadInt32();
|
|
|
|
|
|
Q2CorrLR = reader.ReadInt32();
|
|
|
|
|
|
Diff2Hz8Hz = reader.ReadDouble();
|
|
|
|
|
|
Hz2CorrectionDone = reader.ReadBoolean();
|
|
|
|
|
|
Hz2Correction = reader.ReadInt32();
|
|
|
|
|
|
|
|
|
|
|
|
if (reader.ReadBoolean()) (LastTestResult = new Results.Entities.MeterTestRslt()).ReadBinary(reader, null);
|
|
|
|
|
|
if (reader.ReadBoolean()) (LastTestResult2 = new Results.Entities.MeterTestRslt()).ReadBinary(reader, null);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2015-07-31 08:45:50 +00:00
|
|
|
|
}
|