/// /// 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)' '; /// General data public ulong State; /// bytes 5,6,7,8,9,80,81 = 7 bytes = 56 bits public uint Chyby485; public ulong Vystupy; public uint[] StavDA = new uint[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[] RegVMoveTime = new float[Const.RVsCount]; 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]; public GeneralData() { } /// /// Returns tru when receive data fit the message frame /// /// Received data /// Cumulative sums of received data bytes /// Received bytes count /// Offset of the message /// true when frame fits data 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); } /// /// Auxiliary functions /// /// Received data /// Offset to the files /// 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); } /// /// Proces received data /// /// Received data /// Offset of the message public void UpdateData(byte[] data, int offs) { 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] = 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] = Convert.ToInt32(pulses); 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; } } StavDA[0] = 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 = Const.DivTimeResolution * 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.RVsCount; i++) /// Byte 278 .. 293 { RegVMoveTime[i] = 10 * GetUInt16(data, offs + (int)Poz.CasKoh + 2*i); } 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} Freq={2}", (State & 7), (State & 0xFFFFFFFFFFFFFFF8UL), ReferenceFreq[0]); } } /// /// Byte positions in rcvdData buffer /// 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.RVsCount, /// 294 ETX3 = ETX2 + 3, /// 297 } }