tbf/TBF/Rig/RegisterReaders/S640Stream/DatastreamFrame.cs
2021-10-26 19:38:09 +02:00

243 lines
8.7 KiB
C#

///
/// Copyright (c) 2021 Sensus Slovensko a.s.
///
using System;
using System.Globalization;
using System.Text;
namespace TBF.Rig.RegisterReaders.S640Stream
{
public class DatastreamFrame
{
public const int FrameLength = 49; /// Including CR,LF
public static double VolumeScaleFactor = 1000.0; /// Raw volume is in ml
public static double TimeScaleFactor = 1000.0; /// Raw time is in ms
///
/// Stored values
///
public FrameFlags Flags;
public DateTime DateTime; /// From PC
public int Counter;
public float RefFlow; /// [m3/h]
char[] data = new char[FrameLength - 2];
public Int32 VolumeRaw;
public Int64 VolumeRawExt;
public Int64 Timestamp;
public Int64 TimestampExt;
///
/// Calculated values
///
public double Volume() { return (double)VolumeRawExt / VolumeScaleFactor; }
public double TimestampDbl() { return (double)TimestampExt / TimeScaleFactor; }
public string Label()
{
if (Flags == FrameFlags.OK_FullRound) return " #### end of round ####";
else if (Flags == FrameFlags.OK_TestStart) return " #### start test ####";
else if (Flags == FrameFlags.OK_TestEnd) return " #### end of test ####";
else return string.Empty;
}
public DatastreamFrame() { }
/// <summary>
/// Parses optical telegram and returns OptoTelegramRaw object
/// </summary>
/// <description>
/// Create a configuration structure from a complete byte array
///
/// Telegram description:
///
/// AAAAAAAAAAAA[tab]BBBBBBBBBB[tab]CCCCCCCCC[tab]DDDDDDDDDD[tab]EE[cr][lf] (49 bytes)
///
/// Data Comment Type Calculate to decimal
/// ----------------------------------------------------------------
/// AAAAAAAAAAAA PCB number Decimal
/// BBBBBBBBBB Radio address Decimal
/// CCCCCCCCC Meter reading Decimal Volume in ml
/// DDDDDDDDDD Timestamp Decimal Time in ms
/// EE Checksum Hexadecimal
/// ----------------------------------------------------------------
///
/// Example:
/// 540210730770 4291524429 000250215 0433411626 4F
/// 540210730770 4291524429 000250215 0433411876 56
/// ...
/// </description>
/// <param name="data">A complete byte array data</param>
/// <returns>true = telegram OK, false = telegram NOK</returns>
public bool UpdateFromString(string frame, ref Int64 volumeRawExtLast, ref Int64 timestampExtLast, int counter)
{
DateTime = DateTime.Now;
Counter = counter;
RefFlow = (float)Sequences.ProcessData.RefFlow.Val;
if (frame == null || frame.Length < FrameLength ||
frame[FrameLength - 2] != '\r' || frame[FrameLength - 1] != '\n' ||
frame[12] != '\t' || frame[23] != '\t' || frame[33] != '\t' || frame[44] != '\t')
{
Flags = FrameFlags.InvalidFrame;
return false;
}
/// Copy data into the fixed size buffer
int sum = 0;
for (int i = 0; i < FrameLength - 4; i++)
{
data[i] = frame[i];
sum += (int)frame[i];
}
data[FrameLength - 4] = frame[FrameLength - 4];
data[FrameLength - 3] = frame[FrameLength - 3];
byte checkSum;
bool chksumOk = byte.TryParse(frame.Substring(FrameLength - 4, 2), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out checkSum)
&& checkSum == (sum & 0xFF);
bool volumeOk = Int32.TryParse(frame.Substring(24, 9), out VolumeRaw);
volumeRawExtLast = VolumeRawExt = VolumeRaw;
bool timeOk = Int64.TryParse(frame.Substring(34, 10), out Timestamp);
timestampExtLast = TimestampExt = Timestamp;
bool allOk = volumeOk && timeOk && chksumOk;
Flags = allOk ? FrameFlags.OK : FrameFlags.InvalidFrame;
return allOk;
}
/// <summary>
/// Alternative to UpdateFromString(...) when data are flushed
/// </summary>
public bool UpdateFromStringDummy(string frame, ref Int64 volumeRawExtLast, ref Int64 timestampExtLast,
out Int64 pcbNumber, out Int64 radioAddress)
{
pcbNumber = 0;
radioAddress = 0;
if (frame == null || frame.Length < FrameLength ||
frame[FrameLength - 2] != '\r' || frame[FrameLength - 1] != '\n' ||
frame[12] != '\t' || frame[23] != '\t' || frame[33] != '\t' || frame[44] != '\t')
{
Flags = FrameFlags.InvalidFrame;
return false;
}
/// Copy data into the fixed size buffer
int sum = 0;
for (int i = 0; i < FrameLength - 4; i++) sum += (int)frame[i];
byte checkSum;
bool chksumOk = byte.TryParse(frame.Substring(FrameLength - 4, 2), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out checkSum)
&& checkSum == (sum & 0xFF);
bool pcbNumberOk = Int64.TryParse(frame.Substring(0, 12), out pcbNumber);
bool radioAddressOk = Int64.TryParse(frame.Substring(13, 10), out radioAddress);
bool volumeOk = Int32.TryParse(frame.Substring(24, 9), out VolumeRaw);
volumeRawExtLast = VolumeRawExt = VolumeRaw;
bool timeOk = Int64.TryParse(frame.Substring(34, 10), out Timestamp);
timestampExtLast = TimestampExt = Timestamp;
return chksumOk && pcbNumberOk && radioAddressOk && volumeOk && timeOk;
}
public void SetFlags(FrameFlags flags)
{
this.Flags = flags;
}
public override string ToString()
{
if (Flags == FrameFlags.SyncError)
{
return "Sychronization error";
}
else if (Flags == FrameFlags.InvalidFrame)
{
return "Invalid telegram";
}
else /// if (flags == OptoTelegramFlags.OK || OptoTelegramFlags.OK_TestStart || OptoTelegramFlags.OK_TestEnd)
{
string cntStr = Counter.ToString();
string indent = (cntStr.Length == 1) ? " " : (cntStr.Length == 2) ? " " : (cntStr.Length == 3) ? " " : "";
StringBuilder sb = new StringBuilder(FrameLength - 2);
for (int i = 0; i < FrameLength - 2; i++) sb.Append(data[i]);
return string.Format("{0}:{1}:{2}.{3}\t{4}{5} :\t{6}\tTime={7} Volume={8}{9}",
DateTime.Hour.ToString("D2"),
DateTime.Minute.ToString("D2"),
DateTime.Second.ToString("D2"),
DateTime.Millisecond.ToString("D3"),
indent,
cntStr,
sb.ToString(),
TimestampDbl(),
Volume(),
Label());
}
}
/// <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="optoDataCount">number of objects to process</param>
public static void FIRFilterFlow(DatastreamFrame[] optoData, int optoDataCount)
{
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 = 0; i < optoDataCount; i++)
{
int k = i - kSize;
if (k >= 0) optoData[k].RefFlow = filtered[i % kSize];
if (i < kSize2 || i >= optoDataCount - kSize2)
{
filtered[i % kSize] = optoData[i].RefFlow;
}
else
{
float weoightedSum = 0;
for (int j = -kSize2; j <= kSize2; j++)
weoightedSum += optoData[i + j].RefFlow * kernel[j + kSize2];
filtered[i % kSize] = weoightedSum;
}
}
for (int k = optoDataCount - kSize; k < optoDataCount; k++)
{
if (k >= 0) optoData[k].RefFlow = filtered[k % kSize];
}
}
}
}