270 lines
10 KiB
C#
270 lines
10 KiB
C#
///
|
|
/// Copyright (c) 2015-2021 Sensus Metering Systems
|
|
///
|
|
using System;
|
|
using System.Globalization;
|
|
|
|
namespace Common.Iperl
|
|
{
|
|
public enum OptoTelegramFlags : byte
|
|
{
|
|
OK = 0,
|
|
OK_TestStart,
|
|
OK_TestEnd,
|
|
InvalidTelegram, /// Wrong telegram format of checksum error
|
|
SyncError,
|
|
}
|
|
|
|
public class OptoTelegramRaw
|
|
{
|
|
public static readonly int Length = 42;
|
|
private static CultureInfo culture;
|
|
|
|
|
|
///
|
|
/// Strobed value
|
|
///
|
|
public static decimal TestStartTimestampDec;
|
|
|
|
///
|
|
/// Stored values
|
|
///
|
|
public OptoTelegramFlags Flags;
|
|
|
|
public DateTime DateTime; /// From PC
|
|
public float RefFlow; /// [m3/h]
|
|
public int Counter;
|
|
|
|
public Int32 EmfRaw; /// Signed EMF from iPerl opto data
|
|
public Int16 MagneticFieldRaw;
|
|
public Int16 FlowRaw;
|
|
public UInt32 VolumeRaw;
|
|
public Int64 VolumeRawExt;
|
|
public Int16 Impedance;
|
|
public UInt32 Timestamp;
|
|
public Int64 TimestampExt;
|
|
public byte CheckSum;
|
|
|
|
///
|
|
/// Calculated values
|
|
///
|
|
public double EMF()
|
|
{
|
|
return 0.000000333 * (double)EmfRaw;
|
|
}
|
|
public double MagneticField() { return (double)MagneticFieldRaw; }
|
|
public double Flow(double scalingFactor) { return 0.225 * scalingFactor * (double)FlowRaw; }
|
|
public double Volume(double scalingFactor) { return 0.0000625 * scalingFactor * (double)VolumeRawExt; }
|
|
public Int32 FlipTime() { return Impedance; }
|
|
public decimal TimestampDec() { return (decimal)TimestampExt / (decimal)8192; }
|
|
public double VolumeDelta(double scalingFactor, OptoTelegramRaw previous) { return (previous == null) ? 0 : Volume(scalingFactor) - previous.Volume(scalingFactor); }
|
|
public decimal TimeDelta() { return TimestampDec() - TestStartTimestampDec; }
|
|
public string Label()
|
|
{
|
|
if (Flags == OptoTelegramFlags.OK_TestStart) return "#### start test ####";
|
|
else if (Flags == OptoTelegramFlags.OK_TestEnd) return "#### end of test ####";
|
|
else return string.Empty;
|
|
}
|
|
|
|
|
|
static OptoTelegramRaw()
|
|
{
|
|
culture = CultureInfo.CreateSpecificCulture("DE"); /// This is to use comma as decimal number separator
|
|
}
|
|
|
|
public OptoTelegramRaw()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses optical telegram and returns OptoTelegramRaw object
|
|
/// </summary>
|
|
/// <description>
|
|
/// Create a configuration structure from a complete byte array
|
|
///
|
|
/// Telegram description:
|
|
///
|
|
/// AAAAAA[tab]BBBB[tab]CCCC[tab]DDDDDD[tab]EEEE[tab]FFFFFFFF[tab]GG[cr][lf] (42 bytes)
|
|
///
|
|
/// Data Comment Type Calculate to decimal
|
|
/// ----------------------------------------------------------------
|
|
/// AAAAAA EMF Int24 Value * 0.000000333
|
|
/// BBBB Magnetic field Int16 Value
|
|
/// CCCC Flow Int16 Value * 0.225 * Scalig factor
|
|
/// DDDDDD Volume Int24 Value / 16000 * Scaling factor
|
|
/// EEEE Impedance Int16 Value
|
|
/// FFFFFFFF Timestamp Uint32 Value / 8192
|
|
/// GG Checksum Byte
|
|
/// ----------------------------------------------------------------
|
|
///
|
|
/// Example:
|
|
/// FFFFFE 51EA 0000 65324E 0087 F6319DFF 86
|
|
/// FFDD3A 51F9 0000 65324E 0088 F631A60B 45
|
|
/// ...
|
|
/// </description>
|
|
/// <param name="data">A complete byte array data</param>
|
|
/// <returns>true = telegram OK, false = telegram NOK</returns>
|
|
public bool UpdateFromString(string telegram, int counter, float refFlow, ref Int64 volumeRawExtLast, ref Int64 timestampExtLast, bool isLog = false)
|
|
{
|
|
DateTime = DateTime.Now;
|
|
Counter = counter;
|
|
RefFlow = refFlow;
|
|
|
|
if ((telegram == null) || (telegram.Length < Length) ||
|
|
(telegram[6] != '\t') || (telegram[11] != '\t') || (telegram[16] != '\t') ||
|
|
(telegram[23] != '\t') || (telegram[28] != '\t') || (telegram[37] != '\t') ||
|
|
(!isLog && (telegram[40] != '\r' || telegram[41] != '\n')))
|
|
{
|
|
Flags = OptoTelegramFlags.InvalidTelegram;
|
|
return false;
|
|
}
|
|
|
|
UInt32 uEmfRaw;
|
|
bool f1 = UInt32.TryParse(telegram.Substring(0, 6), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out uEmfRaw);
|
|
EmfRaw = (uEmfRaw > 0x7FFFFF) ? ((int)uEmfRaw - 0x1000000) : (int)uEmfRaw;
|
|
|
|
bool f2 = Int16.TryParse(telegram.Substring(7, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out MagneticFieldRaw);
|
|
bool f3 = Int16.TryParse(telegram.Substring(12, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out FlowRaw);
|
|
bool f4 = UInt32.TryParse(telegram.Substring(17, 6), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out VolumeRaw);
|
|
bool f5 = Int16.TryParse(telegram.Substring(24, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out Impedance);
|
|
bool f6 = UInt32.TryParse(telegram.Substring(29, 8), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out Timestamp);
|
|
bool f7 = byte.TryParse(telegram.Substring(38, 2), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out CheckSum);
|
|
|
|
byte calculatedCheckSum = 0;
|
|
for (int i = 0; i < Length - 4; i++)
|
|
{
|
|
calculatedCheckSum += (byte)telegram[i];
|
|
}
|
|
|
|
bool allOk = f1 && f2 && f3 && f4 && f5 && f6 && f7 && (calculatedCheckSum == CheckSum);
|
|
|
|
if (allOk)
|
|
{
|
|
///
|
|
/// Cope with 'VolumeRaw' overflow
|
|
///
|
|
Int64 uncorrected = (Int64)(((UInt64)volumeRawExtLast & 0xFFFFFFFFFF000000UL) | VolumeRaw);
|
|
if (Math.Abs(uncorrected - volumeRawExtLast) <= 0x800000L)
|
|
{
|
|
VolumeRawExt = volumeRawExtLast = uncorrected;
|
|
}
|
|
else if (Math.Abs(uncorrected + 0x1000000L - volumeRawExtLast) <= 0x800000L)
|
|
{
|
|
VolumeRawExt = volumeRawExtLast = uncorrected + 0x1000000L;
|
|
}
|
|
else if (Math.Abs(uncorrected - 0x1000000L - volumeRawExtLast) <= 0x800000L)
|
|
{
|
|
VolumeRawExt = volumeRawExtLast = uncorrected - 0x1000000L;
|
|
}
|
|
else
|
|
{
|
|
VolumeRawExt = volumeRawExtLast = uncorrected;
|
|
}
|
|
|
|
///
|
|
/// Cope with 'Timestamp' overflow
|
|
///
|
|
uncorrected = (Int64)(((UInt64)timestampExtLast & 0xFFFFFFFF00000000UL) | Timestamp);
|
|
if (Math.Abs(uncorrected - timestampExtLast) <= 0x80000000L)
|
|
{
|
|
TimestampExt = timestampExtLast = uncorrected;
|
|
}
|
|
else if (Math.Abs(uncorrected + 0x100000000L - timestampExtLast) <= 0x80000000L)
|
|
{
|
|
TimestampExt = timestampExtLast = uncorrected + 0x100000000L;
|
|
}
|
|
else if (Math.Abs(uncorrected - 0x100000000L - timestampExtLast) <= 0x80000000L)
|
|
{
|
|
TimestampExt = timestampExtLast = uncorrected - 0x100000000L;
|
|
}
|
|
else
|
|
{
|
|
TimestampExt = timestampExtLast = uncorrected;
|
|
}
|
|
}
|
|
|
|
Flags = allOk ? OptoTelegramFlags.OK : OptoTelegramFlags.InvalidTelegram;
|
|
|
|
return allOk;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Alternative to UpdateFromString(...) when data are flushed
|
|
/// </summary>
|
|
public bool UpdateFromStringDummy(string telegram)
|
|
{
|
|
DateTime = DateTime.Now;
|
|
RefFlow = 0;
|
|
|
|
if ((telegram == null) || (telegram.Length < Length) ||
|
|
(telegram[6] != '\t') || (telegram[11] != '\t') || (telegram[16] != '\t') ||
|
|
(telegram[23] != '\t') || (telegram[28] != '\t') || (telegram[37] != '\t') ||
|
|
(telegram[40] != '\r') || (telegram[41] != '\n'))
|
|
{
|
|
Flags = OptoTelegramFlags.InvalidTelegram;
|
|
return false;
|
|
}
|
|
|
|
bool f7 = byte.TryParse(telegram.Substring(38, 2), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out CheckSum);
|
|
|
|
byte calculatedCheckSum = 0;
|
|
for (int i = 0; i < Length - 4; i++)
|
|
{
|
|
calculatedCheckSum += (byte)telegram[i];
|
|
}
|
|
|
|
bool allOk = f7 && (calculatedCheckSum == CheckSum);
|
|
|
|
Flags = allOk ? OptoTelegramFlags.OK : OptoTelegramFlags.InvalidTelegram;
|
|
|
|
return allOk;
|
|
}
|
|
|
|
|
|
public void SetFlags(OptoTelegramFlags flags)
|
|
{
|
|
this.Flags = flags;
|
|
}
|
|
|
|
|
|
public string ToString(double scalingFactor, OptoTelegramRaw previous)
|
|
{
|
|
if (Flags == OptoTelegramFlags.SyncError)
|
|
{
|
|
return "Sychronization error";
|
|
}
|
|
else if (Flags == OptoTelegramFlags.InvalidTelegram)
|
|
{
|
|
return "Invalid telegram";
|
|
}
|
|
else /// if (flags == OptoTelegramFlags.OK / OptoTelegramFlags.OK_TestStart / OptoTelegramFlags.OK_TestEnd)
|
|
{
|
|
return string.Format("{0}:{1}:{2}.{3}\t{4} :\t{5}\t{6}\t{7}\t{8}\t{9}\t{10}\t{11}\t{12}\t{13}\t{14}\t{15}\t{16}\t{17}\t{18}\t{19}\t{20}\t{21}\t{22}",
|
|
DateTime.Hour.ToString("D2"),
|
|
DateTime.Minute.ToString("D2"),
|
|
DateTime.Second.ToString("D2"),
|
|
DateTime.Millisecond.ToString("D4"),
|
|
Counter,
|
|
(EmfRaw & 0x00FFFFFF).ToString("X6"),
|
|
MagneticFieldRaw.ToString("X4"),
|
|
FlowRaw.ToString("X4"),
|
|
VolumeRaw.ToString("X6"),
|
|
Impedance.ToString("X4"),
|
|
Timestamp.ToString("X8"),
|
|
CheckSum.ToString("X2"),
|
|
EMF().ToString("F4", culture),
|
|
MagneticField().ToString("F0", culture),
|
|
Flow(scalingFactor).ToString("F2", culture),
|
|
Volume(scalingFactor).ToString("F4", culture),
|
|
FlipTime().ToString("F0", culture),
|
|
TimestampDec().ToString("F4", culture),
|
|
(RefFlow * 1000).ToString("F2", culture),
|
|
VolumeDelta(scalingFactor, previous).ToString("F4", culture),
|
|
TimeDelta().ToString("F3", culture),
|
|
scalingFactor.ToString("F1", culture),
|
|
Label());
|
|
}
|
|
}
|
|
}
|
|
}
|