/// /// 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() { } /// /// 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, 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]); } /// /// Get number of pulses corrected for windowing effect /// /// 1-based water meter number /// /// When true lastPulses[wmNr1] is disregarded and is overwritten with lowerWordOfMeterPulses value /// Corrected water meter pulses 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 ]"; } } /// /// 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.StStopVlvsCount, /// 294 ETX3 = ETX2 + 3, /// 297 } }