- Replace `Xylem.Common` namespace references with `TBF.Rig.RegisterReaders`. - Introduce `GenesisSmartReaderTest` and `FakeSerialDriver` for unit testing. - Revamp `FlowDirectionDetection` to support multiple channels. - Make constants in `StreamingDecoder` public for enhanced accessibility. - Update `AssemblyVersion` and `AssemblyFileVersion` to `3.9.3010.1`.
547 lines
24 KiB
C#
547 lines
24 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords;
|
|
|
|
|
|
// ReSharper disable UnusedMember.Local
|
|
|
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.StreamingProtocol
|
|
{
|
|
/// <summary>
|
|
/// Data fields and definitions for GENESIS streaming protocol
|
|
/// </summary>
|
|
public class StreamingDecoder
|
|
{
|
|
private const Double MilliLitersToCmFactor = 1.0E-6;
|
|
private const Double CpuTimeToSecondsFactor = 1.0 / 0x10000;
|
|
public const Double CpuTimeOverflowS = 0x100000000 * CpuTimeToSecondsFactor;
|
|
private const Double LitersPerSecondToCmPerHourFactor = 3600.0 / 1000.0;
|
|
|
|
private const Double DefaultVolumeScaleRawPerMl = 1024.0;
|
|
private const Double DefaultVolumeFactorRawToCm = MilliLitersToCmFactor / DefaultVolumeScaleRawPerMl;
|
|
private const Double MaxGenesisAccuVolumeRaw = UInt32.MaxValue; //0x100000000; //2^32
|
|
public const Double DefaultAccuDutOverflowVolumeCm = MaxGenesisAccuVolumeRaw * DefaultVolumeFactorRawToCm;
|
|
|
|
private const Double DisplayMlSetupDutOverflowVolumeCm = 1000.0; //overflow of LCD if set to ml
|
|
private const Double TofToSecondsFactor38Bit = 1.0 / 0x4000000000; // 2^38
|
|
private const Double AmplitudeToVoltFactor = 1.0 / 0x400000 / 1000.0; // 2^22 100 0000 0000 0000 0000 0000b
|
|
private const Double PulseWidthToRelFactor = 1.0 / 0x100; // 2^8
|
|
|
|
/// <summary>
|
|
/// Default data for bend detection tests of Genesis
|
|
/// </summary>
|
|
private readonly BendDetectionRecord _bendDetectionDefault = new BendDetectionRecord
|
|
{
|
|
StatusBendU0 = BendDetectionRecord.StatusBendU0Enum.OKAY,
|
|
InstallationType = BendDetectionRecord.InstallationTypeEnum.INSTALLATION_UNDISTURBED,
|
|
CorrectionFactor_percent = 0.0,
|
|
PreCorrectionVolumeRaw = 0.0,
|
|
PostCorrectionVolumeRaw = 0.0,
|
|
TimeS = 0.0,
|
|
OverflowTimeS = CpuTimeOverflowS,
|
|
Crc = 0xFFFF,
|
|
IsValid = false
|
|
};
|
|
|
|
/// <summary>
|
|
/// Default data for flow tests of Genesis
|
|
/// </summary>
|
|
private readonly FlowTestRecord _dataDefault = new FlowTestRecord
|
|
{
|
|
VolumeCm = 0.0,
|
|
OverflowVolumeCm = DisplayMlSetupDutOverflowVolumeCm,
|
|
TimeS = 0.0,
|
|
OverflowTimeS = CpuTimeOverflowS,
|
|
Crc = 0xFFFF,
|
|
IsValid = false
|
|
};
|
|
|
|
/// <summary>
|
|
/// Default data for calibration of Genesis
|
|
/// </summary>
|
|
private readonly CalibrationRecord _rawDataDefault = new CalibrationRecord
|
|
{
|
|
Channel = 0,
|
|
Validation = 0xFFFF,
|
|
TotalTimeOfFlightS = 0.0,
|
|
DeltaTimeOfFlightS = 0.0,
|
|
|
|
VolumeScaleRawPerMl = DefaultVolumeScaleRawPerMl,
|
|
VolumeFactorRawToQm = DefaultVolumeFactorRawToCm,
|
|
DeltaVolumeRaw = 0.0,
|
|
DeltaVolumeQm = 0.0,
|
|
AccuVolumeRaw = 0.0,
|
|
VolumeCm = 0.0,
|
|
OverflowVolumeCm = DefaultAccuDutOverflowVolumeCm,
|
|
|
|
SampleIntervalS = 0.0,
|
|
AmplitudeUpV = 0.0,
|
|
AmplitudeDownV = 0.0,
|
|
PulseWidthRatioUp = 0.0,
|
|
PulseWidthRatioDown = 0.0,
|
|
TemperatureRaw = 20.0,
|
|
TemperaturePowFactor = 1.0,
|
|
TemperatureDegC = 20.0,
|
|
TimeS = 0.0,
|
|
OverflowTimeS = CpuTimeOverflowS,
|
|
Crc = 0xFFFF,
|
|
IsValid = false
|
|
};
|
|
|
|
private CalibrationRecord _dataCalibRec;
|
|
private FlowTestRecord _dataFlowTestRec;
|
|
private BendDetectionRecord _dataBendDetectRec;
|
|
private readonly Boolean _ignoreCorruptedData;
|
|
|
|
/// <summary>
|
|
/// Constructor initializes all decoded members with default values
|
|
/// </summary>
|
|
public StreamingDecoder(Boolean ignoreCorruptedData = true)
|
|
{
|
|
_dataFlowTestRec = _dataDefault;
|
|
_dataCalibRec = _rawDataDefault;
|
|
_dataBendDetectRec = _bendDetectionDefault;
|
|
_ignoreCorruptedData = ignoreCorruptedData;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calibration data
|
|
/// </summary>
|
|
public CalibrationRecord DataCalib
|
|
{
|
|
get; private set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Flow test data
|
|
/// </summary>
|
|
public FlowTestRecord DataFlowTest
|
|
{
|
|
get; private set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bend detection test data
|
|
/// </summary>
|
|
public BendDetectionRecord DataBendDetectTest
|
|
{
|
|
get; private set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decoding the raw message
|
|
/// </summary>
|
|
/// <param name="rawMsg">message received as one line delimited with LF</param>
|
|
/// <returns>true if decoding was successful and data has been validated</returns>
|
|
/// <remarks date="2023-Mar-09" author="T.Wiedebusch">
|
|
/// - Modified using common CRC check before branching to the protocol specific decoder.
|
|
/// </remarks>
|
|
/// <remarks date="2023-Dec-06" author="T.Wiedebusch">
|
|
/// - Introduced protocol 'm' for bending detection.
|
|
/// </remarks>
|
|
public Boolean DecodeMsg(String rawMsg)
|
|
{
|
|
var rawRecordIsValid = false;
|
|
try
|
|
{
|
|
// save the raw message for CRC calculation before separation to fields
|
|
//_rawMsgForCrc = rawMsg;
|
|
// extract message and split it to fields
|
|
|
|
//DN50
|
|
//2022-07-21 07:22:06.9871 | @f 8497D 062E4216 9B2A
|
|
//2022-07-21 07:22:06.9871 | @h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331
|
|
//2022-07-21 07:22:07.0171 | @h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD
|
|
//rawMsg = "@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E";
|
|
|
|
//DN80
|
|
//2022-04-28 15:19:54.9167 | @f AA754B 4D0CEE78 5D89
|
|
//2022-04-28 15:19:54.9337 | @h 1 0 0EF4A130 0002AAB8 000DAC8C 02B98FBD 00000200 00000FFC 643BCF30 63C9CFF6 00015096 0C 4D0CF3CC 3E87
|
|
//2022-04-28 15:19:54.9497 | @h 2 0 0EFA3000 000283E7 000CE580 E3D5EFFA 00000200 00001000 6DC0A84E 6CD462C2 00015096 0C 4D0CF922 1646
|
|
//2022-04-28 15:19:54.9627 | @h 3 0 0EFF7F91 0002AC39 000DB43D FE1C0254 00000200 00000FFE 6C932DE6 6D20005D 00015096 0C 4D0CFE76 868A
|
|
//2022-04-28 15:19:54.9787 | @f AA7C01 4D0CFE76 B08F
|
|
// rawMsg = "@h 3 0 0EFF7F91 0002AC39 000DB43D FE1C0254 00000200 00000FFE 6C932DE6 6D20005D 00015096 0C 4D0CFE76 868A ";
|
|
|
|
// The received message is a string with a line delimiter.
|
|
rawMsg = rawMsg.Replace('\n', ' ');
|
|
|
|
// The raw message fields are the separated values from the received string with a blank as field separator
|
|
var rawMsgFields = rawMsg.Split(' ');
|
|
|
|
// The last element is the CRC, the CRC can be separated, calculated and validated before trying to decode the content
|
|
var rawRecordForCrc = "";
|
|
// get all fields excluding the CRC (length - 1)
|
|
for (var x = 0; x < rawMsgFields.Length - 1; x++)
|
|
{
|
|
rawRecordForCrc += rawMsgFields[x];
|
|
// add the field delimiter from raw data
|
|
rawRecordForCrc += " ";
|
|
}
|
|
|
|
// extract bytes of raw message for CRC calculation each character, CRC field is already removed
|
|
var byteArraySize = rawRecordForCrc.Length;
|
|
var byteArray = new Byte[byteArraySize];
|
|
for (var i = 0; i < byteArraySize; i++)
|
|
{
|
|
byteArray[i] = (Byte)rawRecordForCrc[i];
|
|
}
|
|
|
|
// calculate the CRC from the received data
|
|
var calculatedCrc = Crc16Ccitt.CalculateMsb1021(byteArray);
|
|
|
|
// extract received CRC
|
|
var receivedCrc = UInt16.Parse(rawMsgFields[rawMsgFields.Length - 1], NumberStyles.HexNumber);
|
|
|
|
// compare received with calculated CRC and remind valid decoding
|
|
rawRecordIsValid = calculatedCrc == receivedCrc;
|
|
|
|
switch (rawMsgFields[0])
|
|
{
|
|
case "@m":
|
|
_dataBendDetectRec.IsValid = rawRecordIsValid;
|
|
if (rawRecordIsValid || !_ignoreCorruptedData)
|
|
{
|
|
DecodeProtocolM(ref _dataBendDetectRec, rawMsgFields);
|
|
DataBendDetectTest = _dataBendDetectRec;
|
|
}
|
|
break;
|
|
|
|
case "@f":
|
|
_dataFlowTestRec.IsValid = rawRecordIsValid;
|
|
if (rawRecordIsValid || !_ignoreCorruptedData)
|
|
{
|
|
DecodeProtocolF(ref _dataFlowTestRec, rawMsgFields);
|
|
DataFlowTest = _dataFlowTestRec;
|
|
}
|
|
break;
|
|
|
|
case "@g":
|
|
_dataCalibRec.IsValid = rawRecordIsValid;
|
|
if (rawRecordIsValid || !_ignoreCorruptedData)
|
|
{
|
|
DecodeProtocolG(ref _dataCalibRec, rawMsgFields);
|
|
DataCalib = _dataCalibRec;
|
|
}
|
|
break;
|
|
|
|
case "@h":
|
|
_dataCalibRec.IsValid = rawRecordIsValid;
|
|
if (rawRecordIsValid || !_ignoreCorruptedData)
|
|
{
|
|
DecodeProtocolH(ref _dataCalibRec, rawMsgFields);
|
|
DataCalib = _dataCalibRec;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// ignored
|
|
}
|
|
return rawRecordIsValid;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extracting message from string fields for protocol 'm'
|
|
/// </summary>
|
|
/// <param name="dataBendTestRec">reference to bend detection test record</param>
|
|
/// <param name="fields">Separated fields containing the measurement as string</param>
|
|
/// <returns></returns>
|
|
/// <remarks date="2023-Mar-09" author="T.Wiedebusch">
|
|
/// - Modified using common CRC check in advance.
|
|
/// </remarks>
|
|
private static void DecodeProtocolM(ref BendDetectionRecord dataBendTestRec, IList<String> fields)
|
|
{
|
|
// time stamp attachment
|
|
dataBendTestRec.DecodedTime = DateTimeOffset.UtcNow;
|
|
|
|
// extract received CRC
|
|
dataBendTestRec.Crc = ushort.Parse(fields[(Int32)ProtMsubString.Crc],
|
|
NumberStyles.HexNumber);
|
|
// extract the status
|
|
dataBendTestRec.StatusBendU0 =
|
|
(BendDetectionRecord.StatusBendU0Enum)UInt32.Parse(fields[(Int32)ProtMsubString.Status],
|
|
NumberStyles.AllowHexSpecifier);
|
|
// extract the installation type
|
|
dataBendTestRec.InstallationType =
|
|
(BendDetectionRecord.InstallationTypeEnum)UInt32.Parse(fields[(Int32)ProtMsubString.Installation],
|
|
NumberStyles.AllowHexSpecifier);
|
|
// extract the correction factor in percent
|
|
dataBendTestRec.CorrectionFactor_percent =
|
|
UInt32.Parse(fields[(Int32)ProtMsubString.Factor],
|
|
NumberStyles.AllowHexSpecifier) * BendDetectionRecord.CorrectionFactorScale;
|
|
|
|
// build result values, the volume before and after correction can be positive or negative!
|
|
dataBendTestRec.PreCorrectionVolumeRaw = Int32.Parse(fields[(Int32)ProtMsubString.PreVolume],
|
|
NumberStyles.AllowHexSpecifier);
|
|
// build result values, the volume before and after correction can be positive or negative!
|
|
dataBendTestRec.PostCorrectionVolumeRaw = Int32.Parse(fields[(Int32)ProtMsubString.PostVolume],
|
|
NumberStyles.AllowHexSpecifier);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extracting message from string fields for protocol 'f'
|
|
/// </summary>
|
|
/// <param name="dataFlowTestRec">reference to flow test record</param>
|
|
/// <param name="fields">Separated fields containing the measurement as string</param>
|
|
/// <returns></returns>
|
|
/// <remarks date="2023-Mar-09" author="T.Wiedebusch">
|
|
/// - Modified using common CRC check in advance.
|
|
/// </remarks>
|
|
private static void DecodeProtocolF(ref FlowTestRecord dataFlowTestRec, IList<String> fields)
|
|
{
|
|
// time stamp attachment
|
|
dataFlowTestRec.DecodedTime = DateTimeOffset.UtcNow;
|
|
|
|
// extract received CRC
|
|
dataFlowTestRec.Crc = ushort.Parse(fields[(Int32)ProtFsubString.Crc],
|
|
NumberStyles.HexNumber);
|
|
// build result values, the display volume can be positive or negative!
|
|
dataFlowTestRec.VolumeCm = int.Parse(fields[(Int32)ProtFsubString.DisplayVolume],
|
|
NumberStyles.AllowHexSpecifier) * MilliLitersToCmFactor;
|
|
dataFlowTestRec.TimeS = uint.Parse(fields[(Int32)ProtFsubString.CpuTime],
|
|
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extracting message from string fields to individual raw channel for protocol 'g'
|
|
/// </summary>
|
|
/// <param name="dataProtGRec">Reference to result structure for raw data for one channel</param>
|
|
/// <param name="fields">Separated fields containing the measurement as string</param>
|
|
/// <returns>true if protocol is valid</returns>
|
|
/// <remarks date="2018-Mar-22" author="T.Wiedebusch">
|
|
/// - Usage of VolumeFactorRawToQm and calculation of AccuDutOverflowVolumeCm
|
|
/// </remarks>
|
|
/// <remarks date="2023-Mar-09" author="T.Wiedebusch">
|
|
/// - Modified using common CRC check in advance.
|
|
/// </remarks>
|
|
private static void DecodeProtocolG(ref CalibrationRecord dataProtGRec, IList<String> fields)
|
|
{
|
|
// time stamp attachment
|
|
dataProtGRec.DecodedTime = DateTimeOffset.UtcNow;
|
|
|
|
// extract received CRC
|
|
dataProtGRec.Crc = ushort.Parse(fields[(Int32)ProtGsubString.Crc],
|
|
NumberStyles.HexNumber);
|
|
|
|
dataProtGRec.Channel = ushort.Parse(fields[(Int32)ProtGsubString.ChanNo],
|
|
NumberStyles.HexNumber);
|
|
dataProtGRec.Validation = ushort.Parse(fields[(Int32)ProtGsubString.Validation],
|
|
NumberStyles.HexNumber);
|
|
|
|
// Delta time of flight
|
|
dataProtGRec.DeltaTimeOfFlightS = int.Parse(fields[(Int32)ProtGsubString.Dtof],
|
|
NumberStyles.AllowHexSpecifier) * TofToSecondsFactor38Bit;
|
|
|
|
// Delta raw volume between last sample
|
|
dataProtGRec.DeltaVolumeRaw = uint.Parse(fields[(Int32)ProtGsubString.RawDVolume],
|
|
NumberStyles.AllowHexSpecifier);
|
|
|
|
// volume scaling
|
|
var volumeRawScale = uint.Parse(fields[(Int32)ProtGsubString.VolumeScale],
|
|
NumberStyles.AllowHexSpecifier);
|
|
|
|
dataProtGRec.VolumeScaleRawPerMl = volumeRawScale != 0 ? volumeRawScale : DefaultVolumeScaleRawPerMl;
|
|
dataProtGRec.VolumeFactorRawToQm = MilliLitersToCmFactor / dataProtGRec.VolumeScaleRawPerMl;
|
|
dataProtGRec.OverflowVolumeCm = MaxGenesisAccuVolumeRaw * dataProtGRec.VolumeFactorRawToQm;
|
|
|
|
// Calculate volume in cubic meters out of the raw volume
|
|
dataProtGRec.DeltaVolumeQm = dataProtGRec.DeltaVolumeRaw * dataProtGRec.VolumeFactorRawToQm;
|
|
|
|
// accumulated volume for each channel received from water meter scaled with volumeScale
|
|
// the volume can just be positive
|
|
dataProtGRec.AccuVolumeRaw = uint.Parse(fields[(Int32)ProtGsubString.AccuVolume],
|
|
NumberStyles.AllowHexSpecifier);
|
|
dataProtGRec.VolumeCm = dataProtGRec.AccuVolumeRaw * dataProtGRec.VolumeFactorRawToQm;
|
|
|
|
// Sample interval
|
|
dataProtGRec.SampleIntervalS = uint.Parse(fields[(Int32)ProtGsubString.SampleInterval],
|
|
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
|
|
|
|
// amplitude for high threshold in V
|
|
dataProtGRec.AmplitudeUpV = uint.Parse(fields[(Int32)ProtGsubString.AmplitudeUp],
|
|
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
|
|
|
|
// amplitude for low threshold in V
|
|
dataProtGRec.AmplitudeDownV = uint.Parse(fields[(Int32)ProtGsubString.AmplitudeDown],
|
|
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
|
|
|
|
// pulse width ratio high threshold
|
|
dataProtGRec.PulseWidthRatioUp = uint.Parse(fields[(Int32)ProtGsubString.PulseWidthRatioUp],
|
|
NumberStyles.AllowHexSpecifier) * PulseWidthToRelFactor;
|
|
|
|
// pulse width ratio low threshold
|
|
dataProtGRec.PulseWidthRatioDown = uint.Parse(fields[(Int32)ProtGsubString.PulseWidthRatioDown],
|
|
NumberStyles.AllowHexSpecifier) * PulseWidthToRelFactor;
|
|
|
|
// raw temperature
|
|
dataProtGRec.TemperatureRaw = int.Parse(fields[(Int32)ProtGsubString.RawTemperature],
|
|
NumberStyles.AllowHexSpecifier);
|
|
|
|
// temperature scaling
|
|
dataProtGRec.TemperaturePowFactor = uint.Parse(fields[(Int32)ProtGsubString.TemperatureScale],
|
|
NumberStyles.AllowHexSpecifier);
|
|
|
|
// Calculate temperature
|
|
dataProtGRec.TemperatureDegC = dataProtGRec.TemperatureRaw /
|
|
Math.Pow(2.0, dataProtGRec.TemperaturePowFactor);
|
|
|
|
// absolute CPU time, started at LED mode 3 activation
|
|
dataProtGRec.TimeS = uint.Parse(fields[(Int32)ProtGsubString.CpuTime],
|
|
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extracting message from string fields to individual raw channel for protocol 'g'
|
|
/// </summary>
|
|
/// <param name="dataProtHRec">Reference to result structure for raw data for one channel</param>
|
|
/// <param name="fields">Separated fields containing the measurement as string</param>
|
|
/// <returns>true if protocol is valid</returns>
|
|
/// <remarks date="2018-Mar-22" author="T.Wiedebusch">
|
|
/// - Usage of VolumeFactorRawToQm and calculation of AccuDutOverflowVolumeCm
|
|
/// </remarks>
|
|
/// <remarks date="2023-Mar-09" author="T.Wiedebusch">
|
|
/// - Modified using common CRC check in advance.
|
|
/// </remarks>
|
|
private static void DecodeProtocolH(ref CalibrationRecord dataProtHRec, IList<String> fields)
|
|
{
|
|
// time stamp attachment
|
|
dataProtHRec.DecodedTime = DateTimeOffset.UtcNow;
|
|
|
|
// extract received CRC
|
|
dataProtHRec.Crc = ushort.Parse(fields[(Int32)ProtHsubString.Crc],
|
|
NumberStyles.HexNumber);
|
|
|
|
dataProtHRec.Channel = ushort.Parse(fields[(Int32)ProtHsubString.ChanNo],
|
|
NumberStyles.HexNumber);
|
|
dataProtHRec.Validation = ushort.Parse(fields[(Int32)ProtHsubString.Validation],
|
|
NumberStyles.HexNumber);
|
|
|
|
// Total time of flight
|
|
dataProtHRec.TotalTimeOfFlightS = int.Parse(fields[(Int32)ProtHsubString.Ttof],
|
|
NumberStyles.AllowHexSpecifier) * TofToSecondsFactor38Bit;
|
|
// Delta time of flight
|
|
dataProtHRec.DeltaTimeOfFlightS = int.Parse(fields[(Int32)ProtHsubString.Dtof],
|
|
NumberStyles.AllowHexSpecifier) * TofToSecondsFactor38Bit;
|
|
|
|
dataProtHRec.RawTotalTimeOfFlight =
|
|
int.Parse(fields[(Int32)ProtHsubString.Ttof], NumberStyles.AllowHexSpecifier);
|
|
dataProtHRec.RawDeltaTimeOfFlight = int.Parse(fields[(Int32)ProtHsubString.Dtof], NumberStyles.AllowHexSpecifier);
|
|
|
|
// Delta raw volume between two samples
|
|
dataProtHRec.DeltaVolumeRaw = uint.Parse(fields[(Int32)ProtHsubString.RawDVolume],
|
|
NumberStyles.AllowHexSpecifier);
|
|
|
|
// volume scaling
|
|
var volumeRawScale = uint.Parse(fields[(Int32)ProtHsubString.VolumeScale],
|
|
NumberStyles.AllowHexSpecifier);
|
|
|
|
dataProtHRec.VolumeScaleRawPerMl = volumeRawScale != 0 ? volumeRawScale : DefaultVolumeScaleRawPerMl;
|
|
dataProtHRec.VolumeFactorRawToQm = MilliLitersToCmFactor / dataProtHRec.VolumeScaleRawPerMl;
|
|
dataProtHRec.OverflowVolumeCm = MaxGenesisAccuVolumeRaw * dataProtHRec.VolumeFactorRawToQm;
|
|
|
|
// Calculate volume in cubic meters out of the raw volume
|
|
dataProtHRec.DeltaVolumeQm = dataProtHRec.DeltaVolumeRaw * dataProtHRec.VolumeFactorRawToQm;
|
|
|
|
// accumulated volume for each channel received from water meter scaled with volumeScale
|
|
// the volume can just be positive
|
|
dataProtHRec.AccuVolumeRaw = uint.Parse(fields[(Int32)ProtHsubString.AccuVolume],
|
|
NumberStyles.AllowHexSpecifier);
|
|
dataProtHRec.VolumeCm = dataProtHRec.AccuVolumeRaw * dataProtHRec.VolumeFactorRawToQm;
|
|
|
|
// Sample interval
|
|
dataProtHRec.SampleIntervalS = uint.Parse(fields[(Int32)ProtHsubString.SampleInterval],
|
|
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
|
|
|
|
// amplitude for high threshold in V
|
|
dataProtHRec.AmplitudeUpV = uint.Parse(fields[(Int32)ProtHsubString.AmplitudeUp],
|
|
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
|
|
|
|
// amplitude for low threshold in V
|
|
dataProtHRec.AmplitudeDownV = uint.Parse(fields[(Int32)ProtHsubString.AmplitudeDown],
|
|
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
|
|
|
|
// raw temperature
|
|
dataProtHRec.TemperatureRaw = int.Parse(fields[(Int32)ProtHsubString.RawTemperature],
|
|
NumberStyles.AllowHexSpecifier);
|
|
|
|
// temperature scaling
|
|
dataProtHRec.TemperaturePowFactor = uint.Parse(fields[(Int32)ProtHsubString.TemperatureScale],
|
|
NumberStyles.AllowHexSpecifier);
|
|
|
|
// Calculate temperature
|
|
dataProtHRec.TemperatureDegC = dataProtHRec.TemperatureRaw / (
|
|
Math.Pow(2.0, dataProtHRec.TemperaturePowFactor));
|
|
|
|
// absolute CPU time, started at LED mode 3 activation
|
|
dataProtHRec.TimeS = uint.Parse(fields[(Int32)ProtHsubString.CpuTime],
|
|
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
|
|
}
|
|
|
|
/// field position in protocol 'f'
|
|
private enum ProtFsubString
|
|
{
|
|
//do not remove this needed for position in record
|
|
ProtType,
|
|
DisplayVolume,
|
|
CpuTime,
|
|
Crc
|
|
}
|
|
/// field position in protocol 'm'
|
|
private enum ProtMsubString
|
|
{
|
|
//do not remove this needed for position in record
|
|
ProtType,
|
|
Status,
|
|
Installation,
|
|
Factor,
|
|
PreVolume,
|
|
PostVolume,
|
|
Crc
|
|
}
|
|
|
|
/// field position in protocol 'g'
|
|
private enum ProtGsubString
|
|
{
|
|
//do not remove this needed for position in record
|
|
ProtType,
|
|
ChanNo,
|
|
Validation,
|
|
Dtof,
|
|
RawDVolume,
|
|
AccuVolume,
|
|
VolumeScale,
|
|
SampleInterval,
|
|
AmplitudeUp,
|
|
AmplitudeDown,
|
|
PulseWidthRatioUp,
|
|
PulseWidthRatioDown,
|
|
RawTemperature,
|
|
TemperatureScale,
|
|
CpuTime,
|
|
Crc
|
|
}
|
|
|
|
/// field position in protocol 'h'
|
|
private enum ProtHsubString
|
|
{
|
|
//do not remove this needed for position in record
|
|
ProtType,
|
|
ChanNo,
|
|
Validation,
|
|
Ttof,
|
|
Dtof,
|
|
RawDVolume,
|
|
AccuVolume,
|
|
VolumeScale,
|
|
SampleInterval,
|
|
AmplitudeUp,
|
|
AmplitudeDown,
|
|
RawTemperature,
|
|
TemperatureScale,
|
|
CpuTime,
|
|
Crc
|
|
}
|
|
}
|
|
} |