303 lines
16 KiB
C#
303 lines
16 KiB
C#
///
|
|
/// Copyright (c) 2021 Sensus Slovensko a.s.
|
|
///
|
|
using System;
|
|
using log4net;
|
|
|
|
namespace TBF.Rig.ControlBoard.Uni
|
|
{
|
|
public class GeneralData
|
|
{
|
|
private static readonly ILog log = LogManager.GetLogger(typeof(GeneralData));
|
|
|
|
public const int MessageLen = 303;
|
|
public const byte MessageCode = (byte)' ';
|
|
|
|
public bool RestartReadingOfWMPulses = true;
|
|
|
|
/// General data
|
|
public ulong State; /// bytes 5,6,7,8,9,80,81 = 7 bytes = 56 bits
|
|
public uint Chyby485;
|
|
public ulong Vystupy;
|
|
public int[] StavDA = new int[2];
|
|
public double[] ReferenceFreq = new double[4];
|
|
public int[] EtPulses = new int[Const.WMsCount + 1]; /// EtPulses[0]=total pulses, EtPulses[1..WMsCount] gated et.pulses for respective WM
|
|
public int EtPulsesK;
|
|
public int EtPulses2;
|
|
public int EtPulses3;
|
|
public int[] WMeterPuls = new int[Const.WMsCount + 1]; /// WMsCount = 20
|
|
public double[] ImpulseTime = new double[Const.WMsCount + 1]; /// WMsCount = 20
|
|
public uint[] RegVStatus = new uint[Const.RVsCount + 1 + 1]; /// RVsCount = 8, posledny rValveStatus[9] je prudovy
|
|
public float[] ValveSwitchTime = new float[Const.StStopVlvsCount];
|
|
/// time in [s], resolution 10 ms
|
|
public int DivTime;
|
|
public double Ttime; /// [s]
|
|
public double TimeRef; /// [s]
|
|
public ulong Vstupy;
|
|
public Int16[] AnalogInput = new Int16[Const.ADCsCount];
|
|
public int[] TemperatureRaw = new int[8];
|
|
|
|
/// Private
|
|
int[] lastPulses = new int[Const.WMsCount + 1]; /// WMsCount = 20
|
|
|
|
|
|
public GeneralData()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns tru when receive data fit the message frame
|
|
/// </summary>
|
|
/// <param name="data">Received data</param>
|
|
/// <param name="cumulativeSums">Cumulative sums of received data bytes</param>
|
|
/// <param name="rcvdBytesCount">Received bytes count</param>
|
|
/// <param name="offset">Offset of the message</param>
|
|
/// <returns>true when frame fits data</returns>
|
|
public static bool FrameFitsData(byte[] data, int[] cumulativeSums, int rcvdBytesCount, int offset)
|
|
{
|
|
if (offset + MessageLen > rcvdBytesCount) return false;
|
|
|
|
int checksum = cumulativeSums[offset + MessageLen - 3] - cumulativeSums[offset];
|
|
|
|
return data[offset] == Const.STX &&
|
|
data[offset + 1] == 0x01 &&
|
|
data[offset + 2] == ((MessageLen - 5) & 0xFF) &&
|
|
data[offset + 3] == MessageCode &&
|
|
data[offset + MessageLen - 2] == (checksum & 0xFF) &&
|
|
data[offset + MessageLen - 1] == ((checksum >> 8) & 0xFF);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Auxiliary functions
|
|
/// </summary>
|
|
/// <param name="data">Received data</param>
|
|
/// <param name="offs">Offset to the files</param>
|
|
/// <returns></returns>
|
|
Int16 GetInt16(byte[] data, int offs) { return BitConverter.ToInt16(data, offs); }
|
|
UInt16 GetUInt16(byte[] data, int offs) { return BitConverter.ToUInt16(data, offs); }
|
|
UInt32 GetUInt24(byte[] data, int offs) { return BitConverter.ToUInt32(data, offs) & 0x00FFFFFF; }
|
|
UInt32 GetUInt32(byte[] data, int offs) { return BitConverter.ToUInt32(data, offs); }
|
|
UInt64 GetUInt40(byte[] data, int offs) { return BitConverter.ToUInt64(data, offs) & 0x000000FFFFFFFFFF; }
|
|
UInt64 GetUInt64(byte[] data, int offs) { return BitConverter.ToUInt64(data, offs); }
|
|
|
|
|
|
/// <summary>
|
|
/// Proces received data
|
|
/// </summary>
|
|
/// <param name="data">Received data</param>
|
|
/// <param name="offs">Offset of the message</param>
|
|
public void UpdateData(byte[] data, int offs, int divResolutionMs)
|
|
{
|
|
State = GetUInt40(data, offs + (int)Poz.State) | /// Byte 5,6,7,8,9,80,81
|
|
((ulong)data[offs + (int)Poz.Stat5] << 40) |
|
|
((ulong)data[offs + (int)Poz.Stat6] << 48);
|
|
|
|
Vystupy = GetUInt64(data, offs + (int)Poz.Vyst); /// Byte 10 .. 17
|
|
StavDA[1] = Convert.ToInt32(GetUInt16(data, offs + (int)Poz.SDA2)); /// Byte 18,19
|
|
Vstupy = GetUInt40(data, offs + (int)Poz.Vst); /// Byte 20,21,22,23,24
|
|
|
|
/// A/D converters: Feedback from RV-s and diverters
|
|
for (int i = 0; i < Const.ADCsCount; i++) /// Byte 25 .. 56
|
|
{
|
|
#if DEBUG
|
|
AnalogInput[Const.ADCsCount - 1 - i] = (short)(11 * (15 - i));
|
|
#else
|
|
AnalogInput[Const.ADCsCount - 1 - i] = GetInt16(data, offs + (int)Poz.Analogy + 2 * i);
|
|
#endif
|
|
}
|
|
|
|
/// Groch temperature meters
|
|
for (int i = 0; i < 8; i++) /// byte 57 .. 72
|
|
{
|
|
TemperatureRaw[i] = GetInt16(data, offs + (int)Poz.Groch + 2 * i);
|
|
}
|
|
|
|
EtPulses[0] = Convert.ToInt32(GetUInt24(data, offs + (int)Poz.Et1));/// Byte 73,74,75
|
|
EtPulsesK = Convert.ToInt32(GetUInt24(data, offs + (int)Poz.EtK)); /// Byte 76,77,78
|
|
byte spare1 = data[offs + (int)Poz.spare1]; /// byte 79
|
|
byte spare2 = data[offs + (int)Poz.spare2]; /// byte 82
|
|
|
|
for (int i = 0; i < Const.RVsCount; i++) /// Byte 83 .. 90
|
|
{
|
|
RegVStatus[i + 1] = ((uint)data[offs + (int)Poz.RVs + i] >> 3) & 7;
|
|
}
|
|
/// 4-20 mA reg. valve
|
|
uint stavRegQ = (uint)((State >> 16) & 0xFF);
|
|
RegVStatus[Const.RVsCount + 1] = (stavRegQ >> 4) & 2;
|
|
|
|
/// This must preceed processing bytes 91 .. 250 as Ttime is used there
|
|
uint ttimeRaw = GetUInt32(data, offs + (int)Poz.Ttime); /// Byte 251,252,253,254
|
|
Ttime = Convert.ToDouble(ttimeRaw) * Const.TimeResolution; /// convert to [s]
|
|
ImpulseTime[0] = Ttime;
|
|
|
|
/// Water meters (pulses count, gated test time, gated etalon pulses count)
|
|
int ix1 = offs + (int)Poz.CT1; /// Byte 91 .. 250
|
|
int ix2 = offs + (int)Poz.CTR1;
|
|
for (int wmNr = 1; wmNr <= Const.WMsCount; wmNr++, ix1 += 6, ix2 += 2)
|
|
{
|
|
if ((State & (ulong)StatusP.SynchroMethod) != 0)
|
|
{
|
|
ushort pulses = GetUInt16(data, ix2);
|
|
WMeterPuls[wmNr] = GetCorrectedPulses(wmNr, pulses, RestartReadingOfWMPulses);
|
|
EtPulses[wmNr] = (pulses == 0) ? 0 : Convert.ToInt32(GetUInt24(data, ix1));
|
|
ImpulseTime[wmNr] = (pulses == 0) ? 0 : Const.TimeResolution * Convert.ToDouble(GetUInt24(data, ix1 + 3));
|
|
}
|
|
else
|
|
{
|
|
WMeterPuls[wmNr] = Convert.ToInt32(GetUInt24(data, ix1));
|
|
EtPulses[wmNr] = EtPulses[0];
|
|
ImpulseTime[wmNr] = Ttime;
|
|
}
|
|
}
|
|
RestartReadingOfWMPulses = false;
|
|
|
|
StavDA[0] = Convert.ToInt32(GetUInt16(data, offs + (int)Poz.SDA)); /// Byte 255,256
|
|
byte spare3 = data[offs + (int)Poz.spare3]; /// Byte 257
|
|
|
|
/// Impulzy perioda (???)
|
|
uint[] par1 = new uint[3];
|
|
uint[] par2 = new uint[3];
|
|
par2[0] = GetUInt16(data, offs + (int)Poz.Per1); /// Byte 258,259
|
|
par1[0] = GetUInt16(data, offs + (int)Poz.Per1 + 2); /// Byte 260,261
|
|
par2[1] = GetUInt16(data, offs + (int)Poz.Per2); /// Byte 262.263
|
|
par1[1] = GetUInt16(data, offs + (int)Poz.Per2 + 2); /// Byte 264,265
|
|
par2[2] = GetUInt16(data, offs + (int)Poz.Per3); /// Byte 266,267
|
|
par1[2] = GetUInt16(data, offs + (int)Poz.Per3 + 2); /// Byte 268,269
|
|
///
|
|
ReferenceFreq[0] = (par2[0] != 0) ? (5000.0 * Convert.ToDouble(par1[0]) / Convert.ToDouble(par2[0])) : 0.0;
|
|
ReferenceFreq[1] = ReferenceFreq[2] = ReferenceFreq[3] = 0;
|
|
///
|
|
if ((State & (ulong)StatusP.TestWithRefFlowmtr) != 0 && (State & (ulong)StatusP.FlowmtrNrMask) == 0)
|
|
{
|
|
/// Measurement with parallel flowmeters
|
|
if ((State & (ulong)StatusP.Flowmtr1Active) != 0)
|
|
{
|
|
ReferenceFreq[1] = ReferenceFreq[0];
|
|
}
|
|
|
|
if ((State & (ulong)StatusP.Flowmtr2Active) != 0)
|
|
{
|
|
ReferenceFreq[2] = (par2[1] != 0) ? (5000.0 * Convert.ToDouble(par1[1]) / Convert.ToDouble(par2[1])) : 0.0;
|
|
}
|
|
|
|
if ((State & (ulong)StatusP.Flowmtr3Active) != 0)
|
|
{
|
|
ReferenceFreq[3] = (par2[2] != 0) ? (5000.0 * Convert.ToDouble(par1[2]) / Convert.ToDouble(par2[2])) : 0.0;
|
|
}
|
|
|
|
ReferenceFreq[0] = ReferenceFreq[1] + ReferenceFreq[2] + ReferenceFreq[3];
|
|
}
|
|
|
|
DivTime = divResolutionMs * data[offs + (int)Poz.CasKl]; /// Byte 271
|
|
byte spare4 = data[offs + (int)Poz.spare4]; /// Byte 272
|
|
Chyby485 = GetUInt16(data, offs + (int)Poz.Chyb485); /// Byte 273, 274 !!!!!!!!!!!!!!!
|
|
uint timeRefRaw = GetUInt24(data, offs + (int)Poz.Tim2); /// Byte 274,275,276 !!!!!!!!!!!!!!!
|
|
TimeRef = Convert.ToDouble(timeRefRaw) / 1000.0; /// convert to [s]
|
|
|
|
for (int i = 0; i < Const.StStopVlvsCount; i++) /// Byte 278 .. 293
|
|
{
|
|
ValveSwitchTime[i] =
|
|
0.01f * GetUInt16(data, offs + (int)Poz.CasKoh + 2*i); /// float in [s], resolution 10 ms
|
|
}
|
|
|
|
EtPulses2 = Convert.ToInt32(GetUInt24(data, offs + (int)Poz.ETX2)); /// Byte 294,295,296
|
|
EtPulses3 = Convert.ToInt32(GetUInt24(data, offs + (int)Poz.ETX3)); /// Byte 297,298.299 !!!!!!!!!!!!!!!
|
|
|
|
log.InfoFormat("Et={0} State={1:X14} Route={2} Freq={3} EtPulses={4} TTime={5} WMPulses[3]={6} WMRefPulses[3]={7}, Time[3]={8}",
|
|
State & 7, State, Utils.ToBin40(Vystupy >> 8), ReferenceFreq[0], EtPulses[0], Ttime, WMeterPuls[3], EtPulses[3], ImpulseTime[3]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get number of pulses corrected for windowing effect
|
|
/// </summary>
|
|
/// <param name="wmNr1">1-based water meter number</param>
|
|
/// <param name="lowerWordOfMeterPulses"></param>
|
|
/// <param name="firstReading">When true lastPulses[wmNr1] is disregarded and is overwritten with lowerWordOfMeterPulses value</param>
|
|
/// <returns>Corrected water meter pulses</returns>
|
|
private int GetCorrectedPulses(int wmNr1, UInt16 lowerWordOfMeterPulses, bool firstReading)
|
|
{
|
|
if (firstReading)
|
|
{
|
|
lastPulses[wmNr1] = (Int16)lowerWordOfMeterPulses;
|
|
}
|
|
else
|
|
{
|
|
int uncorrected = (int)(((uint)lastPulses[wmNr1] & 0xFFFF0000) | lowerWordOfMeterPulses);
|
|
|
|
if (Math.Abs(uncorrected - lastPulses[wmNr1]) <= Int16.MaxValue)
|
|
{
|
|
lastPulses[wmNr1] = uncorrected;
|
|
}
|
|
else if (Math.Abs(uncorrected + 65536 - lastPulses[wmNr1]) <= Int16.MaxValue)
|
|
{
|
|
lastPulses[wmNr1] = uncorrected + 65536;
|
|
}
|
|
else if (Math.Abs(uncorrected - 65536 - lastPulses[wmNr1]) <= Int16.MaxValue)
|
|
{
|
|
lastPulses[wmNr1] = uncorrected - 65536;
|
|
}
|
|
else
|
|
{
|
|
lastPulses[wmNr1] = uncorrected; /// Default
|
|
}
|
|
}
|
|
|
|
return lastPulses[wmNr1];
|
|
}
|
|
|
|
public static string Caption()
|
|
{
|
|
// 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
|
return " [. .. .. .. .. (-- state ---) (------ vystupy ------) (DA2) (-- vstupy --) (---------------------------------------------- analogy ------------------" +
|
|
// 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
|
|
"--------------------) (------------- meracia ustredna --------------) (--Et1-) (--EtK-) xx (s56) xx (---- reg.ventily ----) (--- citace --------------" +
|
|
// 100 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
|
"---------------------------------------------------------------------------------------- citace ------------------------------------------------------" +
|
|
// 150 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
|
|
"---------------------------------------------------------------------------------------- citace ------------------------------------------------------" +
|
|
// 200 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
|
"--------------------- citace ---) (------------------------------------------------------------- citace 2 --------------------------------------------" +
|
|
// 250 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
|
|
"--) (test-time) (SDA) xx (-period1-) (-period2-) (-period3-) xx KL xx CH (---Tim2--) (---------------- cas koh. -------------------) (-ETX2-) (-ETX3-)" +
|
|
// 300 01 02
|
|
" xx Sum ]";
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Byte positions in rcvdData buffer
|
|
/// </summary>
|
|
public enum Poz : int
|
|
{
|
|
State = 5, /// 5 .. 9
|
|
Vyst = State + 5, /// 10 .. 17
|
|
SDA2 = Vyst + 8, /// 18,19
|
|
Vst = SDA2 + 2, /// 20 .. 24
|
|
Analogy = Vst + 5, /// 25 .. 56
|
|
Groch = Analogy + 2 * Const.ADCsCount, /// 57 (16 analog inputs)
|
|
Et1 = Groch + 16, /// 73,74,75
|
|
EtK = Et1 + 3, /// 76,77,78
|
|
spare1 = EtK + 3, /// 79
|
|
Stat5 = spare1 + 1, /// 80
|
|
Stat6 = Stat5 + 1, /// 81
|
|
spare2 = Stat6 + 1, /// 82
|
|
RVs = spare2 + 1, /// 83
|
|
CT1 = RVs + 8, /// 91
|
|
CTR1 = CT1 + 6 * Const.WMsCount, /// 211
|
|
Ttime = CTR1 + 2 * Const.WMsCount, /// 251
|
|
SDA = Ttime + 4, /// 255
|
|
spare3 = SDA + 2, /// 257
|
|
Per1 = spare3 + 1, /// 258
|
|
Per2 = Per1 + 4, /// 262
|
|
Per3 = Per2 + 4, /// 266
|
|
CasKl = Per3 + 5, /// 271
|
|
spare4 = CasKl + 1, /// 272
|
|
Chyb485 = spare4 + 1, /// 273
|
|
Tim2 = Chyb485 + 1, /// 274
|
|
CasKoh = Tim2 + 4, /// 278
|
|
ETX2 = CasKoh + 2 * Const.StStopVlvsCount, /// 294
|
|
ETX3 = ETX2 + 3, /// 297
|
|
}
|
|
}
|