tbf/TBF/Rig/ControlBoard/Uni/GeneralData.cs
Marek Frniak 93d7456ccd <Fix>: RefPulses carry-over by restoring testInProgress behavior in StandingStartMassCollection, increase revision to 3.9.3134.112
Cause:
1. Background: there was a suspicion that the Bukoven board left RefPulses active across operations; this was investigated and ruled out. The pulse counter is reset by testInProgress when an operation is reused. During the third DataEntry window testInProgress had been temporarily disabled (SLM vs Karlsruhe difference), which allowed RefPulses to appear to persist.
2. Increase revision to 3.9.3134.112

Solution:
1. Restore/ensure testInProgress behavior during the DataEntry sequence and explicitly reset the pulse counter when starting a new operation.
2. Increased revision to 3.9.3134.112
2026-08-04 16:21:33 +02:00

306 lines
17 KiB
C#

using log4net;
using SharedComponents;
///
/// Copyright (c) 2021 Sensus Slovensko a.s.
///
using System;
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 !!!!!!!!!!!!!!!
//LiveLogCache.Instance.AddLog(string.Format("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]));
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
}
}