tbf/TestBenchFramework/BenchControl/TestMethods/iPerlCommunication/iPerlHead/IperlHead.cs

849 lines
27 KiB
C#
Raw Normal View History

///
/// Copyright (c) 2015-2017 Sensus Metering Systems
///
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Windows.Forms;
using log4net;
using Config.Entities;
using TBF.BenchControl;
using TBF.BenchControl.Generic;
using TBF.Resources;
namespace TBF.BenchControl.TestMethods.iPerlCommunication.iPerlHead
{
/// <summary>
/// This component = instance of this class is a placeholder for a combined main watermeter
/// </summary>
public class IperlHead : ComponentBase, IDevice, GenericDevices.IRegisterReader, IOperation
{
private static readonly ILog log = LogManager.GetLogger(typeof(IperlHead));
public override string ToString() { return string.Format("iPerl({0})", Cfg.ToString(1)); }
readonly IperlHeadCfg iperlHeadCfg;
public int RfidComPortNr { get { return iperlHeadCfg.RfidComPortNr; } }
public int MuxBoardNr { get { return iperlHeadCfg.MuxBoardNr; } }
public int Group { get { return iperlHeadCfg.Group; } }
public double PulsesPerLtr { get { return 1000.0; } }
public double LtrsPerPulse { get { return 1 / PulsesPerLtr; } }
#if ORACLE_DB
public int WMType_ID { get { return iperlHeadCfg.ProcParams.WMType_ID; } } /// Required by Oracle DB
public int WMType_Rev { get { return iperlHeadCfg.ProcParams.WMType_Rev; } } /// Required by Oracle DB
#endif
public ushort FactorLimitLo { get { return (ushort)iperlHeadCfg.ProcParams.FactorLimitLo; } }
public ushort FactorLimitHi { get { return (ushort)iperlHeadCfg.ProcParams.FactorLimitHi; } }
/// Properties set by the Begin and the End form
public string SerialNr
{
get
{
if (ConfigStruct != null) return configStruct.PCBNumber2String();
else return string.Empty;
}
set { }
}
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;
public bool CommFailed
{
get { return commFailed; }
set { commFailed = value; }
}
bool commFailed;
/// <summary>
/// Passed to OptoTelegramRaw.UpdateFromString(...)
/// </summary>
Int64 volumeRawExtLast;
Int64 timestampExtLast;
public bool PositiveCounting;
/// <summary> ConfigStruct of the water meter obtained or updated by iPerlCommunication </summary>
public ConfigStruct ConfigStruct
{
get { return configStruct; }
set { configStruct = value; }
}
ConfigStruct configStruct;
/// <summary> CalibrationStruct of the water meter obtained or updated by iPerlCommunication </summary>
public CalibrationStruct CalibrationStruct
{
get { return calibrationStruct; }
set { calibrationStruct = value; }
}
CalibrationStruct calibrationStruct;
public ushort OriginalCalibFactor;
public ushort CalibrationFactor { get { return (CalibrationStruct != null) ? CalibrationStruct.Calibration : (ushort)0; } }
public double Q2ErrorWOCorrection;
public bool Q2CorrectionDone;
public double Q2Correction;
public int Q2CorrRFlow;
public int Q2CorrLFlow;
public double Diff2Hz8Hz;
public bool Hz2CorrectionDone;
public int Hz2CorrectionFactor;
public string FWVersion { get { return (CalibrationStruct != null) ? CalibrationStruct.FWVersionStr() : string.Empty; } }
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>
public Results.Entities.MeterTestRslt LastTestResult2;
public Results.Entities.MeterTestRslt LastTestResult;
public double NominalTestFlowLph; /// in liter per hour
2015-08-22 04:32:55 +00:00
///
/// Required for IRegisterReader interface
///
public int WMPulses { get { return wmPulses; } }
public int WMRefPulses { get { return wmRefPulses; } }
public double WMVolume { get { return wmVolume; } }
public double BeginWMState { get { return beginWMState; } }
public double EndWMState { get { return endWMState; } }
public double WMTestTime { get { return wmTestTime; } }
double beginWMState;
double endWMState;
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 or 0 (= Out of range)</returns>
public UInt16 CalculateNewCalibFactor(UInt16 originalCalibrationFactor, UInt16 FactorLimitLo, UInt16 FactorLimitHi)
2015-08-14 12:33:36 +00:00
{
OriginalCalibFactor = originalCalibrationFactor;
double volumeMeter = Math.Abs(VolumeLtrEnd - VolumeLtrStart);
if (volumeMeter > 1E-2)
2015-08-14 12:33:36 +00:00
{
PositiveCounting = VolumeLtrEnd > VolumeLtrStart;
UInt16 newFactor = (UInt16)((double)originalCalibrationFactor * VolumeLtrRef / volumeMeter + 0.5);
log.InfoFormat("Calibration factor: orig={0} new={1} V_1={2} V_2={3} Vdiff={4} Vref={5}",
originalCalibrationFactor, newFactor,
VolumeLtrStart.ToString("F3"), VolumeLtrEnd.ToString("F3"), volumeMeter.ToString("F3"),
VolumeLtrRef.ToString("F3"));
if (newFactor < FactorLimitLo || newFactor > FactorLimitHi) return 0;
return newFactor;
}
else
{
log.ErrorFormat("Calibration factor: orig={0} new={0} (unchanged!) Vdiff={1}",
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-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>
/// <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
{
q2CorrectionFactor = 0;
if (q2TestResult == null) return false; /// Q2 test result is missing ==> water meter failed
double errLimitLo = q2TestResult.ErrLimLo() + q2TestResult.Uncertainty();
double errLimitHi = q2TestResult.ErrLimHi() - q2TestResult.Uncertainty();
if ((q2TestResult.Error < errLimitLo) || (q2TestResult.Error > errLimitHi))
{
return false; /// Q2 error too large ==> water meter failed
}
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 / (NominalTestFlowLph * 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
q2CorrectionFactor = (-1) * (q2TestResult.Error / G) * (q2TestResult.VolumeRef / q2TestResult.VolumeMeter);
log.WarnFormat("Q2 correction: Pos={0}, PCB#={1}, corrFactor={2}, error={3}% [Lo={4}%, Hi={5}%]",
Name,
SerialNr,
q2CorrectionFactor.ToString("F1"),
q2TestResult.Error.ToString("F2"),
errLimitLo.ToString("F1"),
errLimitHi.ToString("F1"));
return true;
2015-08-22 04:32:55 +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>
/// <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
{
hz2CorrectionFactor = 0;
diff2Hz8Hz = 0;
2016-02-15 14:16:44 +00:00
if ((resultAt2Hz == null) || (resultAt8Hz == null))
2016-02-15 14:16:44 +00:00
{
return false; /// Test result @2Hz and/or @8Hz is missing ==> water meter failed
2016-02-15 14:16:44 +00:00
}
diff2Hz8Hz = resultAt2Hz.Error - resultAt8Hz.Error;
2016-02-15 14:16:44 +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
hz2CorrectionFactor = -1 * (int)Math.Round(10 * diff2Hz8Hz);
2016-02-15 14:16:44 +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,
SerialNr,
hz2CorrectionFactor,
resultAt2Hz.Error.ToString("F2"),
resultAt8Hz.Error.ToString("F2"));
2016-02-15 14:16:44 +00:00
return true;
2016-02-15 14:16:44 +00:00
}
/// <summary> Name set by the test, to be used as a part of the opto-data log file name </summary>
public string TestName;
/// <summary> Name set by the test, to be used as a part of the opto-data log file name </summary>
public string BenchName;
///
/// Volume of water from the opto telegram
///
private Int64 lastVolumeRaw; /// Last read raw volume
private double volumeLtr; ///
private double volumeLtr0;
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-22 18:37:43 +00:00
///
/// Timestamp from the opto telegram
///
bool lastTimestampValid;
private Int64 lastTimestamp;
private double timestampSec;
private double timestampSec0;
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;
double timestampSecEnd1;
double timestampSecEnd2;
2015-08-22 18:37:43 +00:00
double timestampSecEnd3;
private int telegramIx;
public int TestStartTelegramIx;
public int TestEndTelegramIx;
OptoTelegramRaw[] optoData;
const int MaxOptoDataCount = 40000;
int optoDataCount;
string optoDataLogFileName;
OptoTelegramRaw toBeFlushed;
int flushedDataCount;
public int FlushedDataCount
{
get { return flushedDataCount; }
set { flushedDataCount = lastFlushedDataCount = lastFlushedDataCount_1 = value; }
}
int lastFlushedDataCount_1;
int lastFlushedDataCount;
public int FlushedDataDelta
{
get
{
int retval = Math.Max(flushedDataCount - lastFlushedDataCount, lastFlushedDataCount - lastFlushedDataCount_1);
lastFlushedDataCount_1 = lastFlushedDataCount;
lastFlushedDataCount = flushedDataCount;
return retval;
}
}
///
/// Opto serial port and worker thread related private variables
///
private SerialPort optoSerialPort;
public IperlHead()
{
ClearData();
}
public IperlHead(Generic.IComponentCfg cfg)
: base(cfg)
{
ClearData();
iperlHeadCfg = cfg as IperlHeadCfg;
log.Debug(this.ToString());
}
/// <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;
NominalTestFlowLph = 0;
OriginalCalibFactor = 0;
Q2ErrorWOCorrection = 0;
Q2CorrectionDone = false;
Q2CorrRFlow = 0;
optoDataCount = 0;
}
public void Initialize()
{
ClearData();
if (DebugLevel == DebugMode.Normal)
{
optoSerialPortParsingEnabled = false;
/// Allocate memory for opto-data from iPerl
optoData = new OptoTelegramRaw[MaxOptoDataCount];
for (int i = 0; i < MaxOptoDataCount; i++) optoData[i] = new OptoTelegramRaw();
toBeFlushed = new OptoTelegramRaw();
flushedDataCount = 0;
synchronized = false;
synchronized2 = false;
partOfTelegram = string.Empty;
/// Prepare serial port
optoSerialPort = new SerialPort(string.Format("COM{0}", iperlHeadCfg.OptoComPortNr),
9600, Parity.None, 8, StopBits.One);
optoSerialPort.Handshake = Handshake.None;
optoSerialPort.Open();
}
}
public void RunDeviceBefore()
{
if (DebugLevel == DebugMode.Normal)
{
try
{
if (optoSerialPortParsingEnabled)
ReadOptoData(OptoState.Read);
else
ReadOptoData(OptoState.Flush);
}
catch (Exception e)
{
DebugLevel = DebugMode.FailureDuringOperation;
log.FatalFormat("Opto-data serial port failure : {0}", e.Message);
if (e.InnerException != null)
{
log.FatalFormat("InnerMessage : {0}", e.InnerException.Message);
}
}
}
else if (DebugLevel == DebugMode.FailureDuringOperation)
{
}
}
public void RunDeviceAfter()
{
}
public void StopDevice()
{
try
{
if (DebugLevel == DebugMode.Normal && optoSerialPort != null)
{
optoSerialPort.Close();
optoSerialPort = null;
}
}
catch
{
}
}
/// <summary>
/// Events: Event.ReadRegisterDone, Event.Error
/// </summary>
/// <returns>ReadWaterMeter instance reference casted to IOperaton</returns>
public IOperation ReadRegisterOp()
{
return this;
}
/// <summary>
/// Clear data/counters related to a specific tests
/// </summary>
public void Clear()
{
sampleNr = 0;
volumeLtr = 0;
volumeLtr0 = 0;
timestampSec = 0;
timestampSec0 = 0;
ReadPulses();
}
public void TestCompleted()
{
/// TODO: Implement
}
2015-08-22 04:32:55 +00:00
int sampleNr; /// This is to determine when the test start sample should be taken
/// <summary>Start this operation</summary>
public void Start()
{
Clear();
/// Reset opto data
optoDataCount = 0;
TestStartTelegramIx = 0;
TestEndTelegramIx = 0;
/// File name is: PCB_AA_BB_HH_MI_SS..txt
string wmPosition = Name.Substring(5);
if (wmPosition.Length == 1) wmPosition = "0" + wmPosition;
string flowNr;
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";
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"));
StartParsingOptoSerialPort();
}
/// <summary>Run this operation</summary>
/// <returns>eventDone</returns>
public Event Run()
{
sampleNr++;
ReadPulses();
2015-08-22 18:37:43 +00:00
if (sampleNr == 4)
{
2015-08-22 04:32:55 +00:00
/// Take the test start sample
VolumeLtrStart = volumeLtr;
timestampSecStart = timestampSec;
TestStartTelegramIx = telegramIx;
}
/// Shift data in pipelines
2015-08-22 18:37:43 +00:00
VolumeLtrEnd = volumeLtrEnd3;
volumeLtrEnd3 = volumeLtrEnd2;
volumeLtrEnd2 = volumeLtrEnd1;
volumeLtrEnd1 = volumeLtr;
timestampSecEnd = timestampSecEnd3;
2015-08-22 18:37:43 +00:00
timestampSecEnd3 = timestampSecEnd2;
timestampSecEnd2 = timestampSecEnd1;
timestampSecEnd1 = timestampSec;
TestEndTelegramIx = telegramIx - 3;
return Event.ReadRegisterDone;
}
/// <summary>Stop this operation</summary>
public void Stop()
{
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();
OptoTelegramRaw.FIRFilterFlow(optoData, optoDataCount);
SaveOptoData();
}
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"));
try
{
2016-03-02 19:02:30 +00:00
Directory.CreateDirectory(directory);
2016-03-02 19:02:30 +00:00
double scalFact = ScalingFactor();
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();
}
}
catch (Exception exc)
{
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);
}
}
void ReadPulses()
{
beginWMState = volumeLtr0;
endWMState = volumeLtr;
wmVolume = Math.Abs(endWMState - beginWMState);
wmPulses = (int)(wmVolume * (double)PulsesPerLtr + 0.5);
wmRefPulses = StateMachine.ControlBoard.EtPulses(0);
wmTestTime = timestampSec - timestampSec0;
}
bool optoSerialPortParsingEnabled;
/// <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;
}
///
/// Variables storing the context of serial port data parsing (ReadOptoSerialPort(...))
///
bool synchronized;
bool synchronized2;
string partOfTelegram;
/// <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>
/// <param name="optoState">OptoState.Read or OptoState.Flush</param>
void ReadOptoData(OptoState optoState)
{
int nrBytes = optoSerialPort.BytesToRead;
if (nrBytes > 0)
{
char[] buffer = new char[nrBytes];
optoSerialPort.Read(buffer, 0, nrBytes);
string received = new string(buffer);
string allRcvd = partOfTelegram + received;
while (true)
{
int pos = allRcvd.IndexOf("\r\n");
if (pos < 0)
{
/// No CR+LF found, wait for more characters in the next invocation
partOfTelegram = allRcvd;
return;
}
else
{
// CR+LF found
if (optoState == OptoState.Read)
{
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[optoDataCount].Counter = optoDataCount;
optoData[optoDataCount++].SetFlags(OptoTelegramFlags.SyncError);
}
synchronized = true;
}
// CR+LF found and (pos >= OptoTelegramRaw.Length - 2)
else if (optoData[optoDataCount].UpdateFromString(allRcvd.Substring(pos - OptoTelegramRaw.Length + 2), optoDataCount, ref volumeRawExtLast, ref timestampExtLast))
{
OptoTelegramRreceived(optoDataCount++, synchronized2);
synchronized2 = synchronized;
allRcvd = allRcvd.Substring(pos + 2);
}
else
{
optoData[optoDataCount].Counter = optoDataCount;
2016-03-02 19:02:30 +00:00
optoDataCount++;
allRcvd = allRcvd.Substring(pos + 2);
}
}
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.UpdateFromStringDummy(allRcvd.Substring(pos - OptoTelegramRaw.Length + 2)))
{
flushedDataCount++;
synchronized2 = synchronized;
allRcvd = allRcvd.Substring(pos + 2);
}
else
{
allRcvd = allRcvd.Substring(pos + 2);
}
}
}
}
//OnOptoReceived(this, new OptoReceivedEventArgs(s));
}
else
{
//OnOptoReceived(this, new OptoReceivedEventArgs("."));
}
}
void OptoTelegramRreceived(int currentIx, bool async)
{
OptoTelegramRaw optoTelegram = optoData[currentIx];
telegramIx = currentIx;
lastVolumeRaw = volumeRawExtLast;
lastTimestamp = timestampExtLast;
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;
}
//optoDataLogger.InfoFormat("{0} {1} {2} ltr {3} {4}", optoTelegram, lastTimestamp, lastVolumeRaw.ToString("X8"), timestampSec.ToString("F1"), volumeLtr.ToString("F3"));
}
/// <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;
/// <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()
{
if (iperlHeadCfg.MeterType == MeterType.AutoDetect || CalibrationStruct == null)
{
return true;
}
return iperlHeadCfg.MeterType == CalibrationStruct.MeterType;
}
public static double UnitVolume(VolumeUnits units)
{
switch (units)
{
default:
case VolumeUnits.m3: return Config.Units.ConvertFrom(Config.Unit.m3, 1.0); /// liter
case VolumeUnits.UK_gallon: return Config.Units.ConvertFrom(Config.Unit.gal_UK, 1.0); /// liter
case VolumeUnits.US_gallon: return Config.Units.ConvertFrom(Config.Unit.gal_US, 1.0); /// liter
}
}
public double ScalingFactor()
{
if ((iperlHeadCfg.MeterType == MeterType.AutoDetect) && (CalibrationStruct != null))
{
return IperlHead.ScalingFactor(CalibrationStruct.MeterType);
}
else if (iperlHeadCfg.MeterType != MeterType.AutoDetect)
{
return IperlHead.ScalingFactor(iperlHeadCfg.MeterType);
}
else
{
return IperlHead.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;
}
}
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;
}
}
}