iPerl ASIC - implementation

This commit is contained in:
2026-03-12 14:50:47 +01:00
parent 3c964f2aab
commit a997c3d7be
378 changed files with 5684 additions and 295925 deletions
@@ -3,264 +3,203 @@
///
using System;
using System.IO;
using System.Text;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
public class ConfigStruct
{
public const int Length = 32;
//public const int Length = 32; // dynamic
public Byte Version; /// 0: 1 byte
public MeterState MeterState; /// 1: 1 byte
public UInt32 TargetTimeVeryLowBatt; /// 2: 4 bytes in seconds
public UInt32 TargetTimeLowBatt; /// 6: 4 bytes, in seconds
public UInt32 TestModeTime; /// 10: 4 bytes, Max. test mode time in seconds
public UInt16 EmptyPipeThreshold; /// 14: 2 bytes
public byte[] PCBNumber; /// 16: 5 bytes
public byte TestModeConfig; /// 21: 1 byte
public UInt32 RadioAddress; /// 22: 4 bytes
public UInt16 TempCalibration; /// 26: 2 bytes
public UInt16 AlarmMask; /// 28: 2 bytes, Default 0xA3F7
public UInt16 ConfigCheckSum; /// 30: 2 bytes
//public Byte Version; // 0: 1 byte
public string PCBNumberString; // dynamic
public ProtocolStatuses StatusMode; // byte
public DiagnosticLedState OpthoStatusMode;// byte
public string Unit; //dynamic
public string Version; // dynamic
public ConfigStruct()
{
PCBNumber = new byte[5];
}
public byte[] ToByteArray()
//Optho test status mode
public DiagnosticLedState TestModeConfig
{
byte[] result = new byte[Length];
result[0] = Version;
result[1] = (byte)MeterState;
result[2] = (byte)(TargetTimeVeryLowBatt & 0x000000FF);
result[3] = (byte)((TargetTimeVeryLowBatt >> 8) & 0x000000FF);
result[4] = (byte)((TargetTimeVeryLowBatt >> 16) & 0x000000FF);
result[5] = (byte)((TargetTimeVeryLowBatt >> 24) & 0x000000FF);
result[6] = (byte)(TargetTimeLowBatt & 0x000000FF);
result[7] = (byte)((TargetTimeLowBatt >> 8) & 0x000000FF);
result[8] = (byte)((TargetTimeLowBatt >> 16) & 0x000000FF);
result[9] = (byte)((TargetTimeLowBatt >> 24) & 0x000000FF);
result[10] = (byte)(TestModeTime & 0x000000FF);
result[11] = (byte)((TestModeTime >> 8) & 0x000000FF);
result[12] = (byte)((TestModeTime >> 16) & 0x000000FF);
result[13] = (byte)((TestModeTime >> 24) & 0x000000FF);
result[14] = (byte)(EmptyPipeThreshold & 0x00FF);
result[15] = (byte)((EmptyPipeThreshold >> 8) & 0x00FF);
result[16] = PCBNumber[0];
result[17] = PCBNumber[1];
result[18] = PCBNumber[2];
result[19] = PCBNumber[3];
result[20] = PCBNumber[4];
result[21] = TestModeConfig;
result[22] = (byte)(RadioAddress & 0x000000FF);
result[23] = (byte)((RadioAddress >> 8) & 0x000000FF);
result[24] = (byte)((RadioAddress >> 16) & 0x000000FF);
result[25] = (byte)((RadioAddress >> 24) & 0x000000FF);
result[26] = (byte)(TempCalibration & 0x00FF);
result[27] = (byte)((TempCalibration >> 8) & 0x00FF);
result[28] = (byte)(AlarmMask & 0x00FF);
result[29] = (byte)((AlarmMask >> 8) & 0x00FF);
result[30] = (byte)(ConfigCheckSum & 0x00FF);
result[31] = (byte)((ConfigCheckSum >> 8) & 0x00FF);
return result;
get { return OpthoStatusMode; }
}
/// <summary>
/// Create a configuration structure from a complete byte array
/// </summary>
/// <param name="data">A complete byte array data</param>
/// <returns>ConfigStruct or null when byte array was not complete</returns>
public static ConfigStruct FromByteArray(byte[] data)
//Activity test status mode
public ProtocolStatuses MeterState
{
if (data.Length != Length) return null;
ConfigStruct result = new ConfigStruct();
result.Version = data[0];
result.MeterState = (MeterState)data[1];
result.TargetTimeVeryLowBatt = (((UInt32)data[5] * 256 + data[4]) * 256 + data[3]) * 256 + data[2];
result.TargetTimeLowBatt = (((UInt32)data[9] * 256 + data[8]) * 256 + data[7]) * 256 + data[6];
result.TestModeTime = (((UInt32)data[13] * 256 + data[12]) * 256 + data[11]) * 256 + data[10];
result.EmptyPipeThreshold = (UInt16)(data[15] * 256 + data[14]);
result.PCBNumber[0] = data[16];
result.PCBNumber[1] = data[17];
result.PCBNumber[2] = data[18];
result.PCBNumber[3] = data[19];
result.PCBNumber[4] = data[20];
result.TestModeConfig = data[21];
result.RadioAddress = (((UInt32)data[25] * 256 + data[24]) * 256 + data[23]) * 256 + data[22];
result.TempCalibration = (UInt16)(data[27] * 256 + data[26]);
result.AlarmMask = (UInt16)(data[29] * 256 + data[28]);
result.ConfigCheckSum = (UInt16)(data[31] * 256 + data[30]);
return result;
get
{
return StatusMode;
}
}
/// <summary>
/// Update the configuration structure from an incomplete byte array
/// </summary>
/// <param name="offset">Offset of byte array data in ConfigStruct</param>
/// <param name="data">Byte array data</param>
/// <returns>true when successful, false when data are not appropriate</returns>
public bool Update(int offset, byte[] data)
{
if (offset == 0 && data.Length == 2)
{
/// iPerl mode of function
Version = data[0];
MeterState = (MeterState)data[1];
return true;
}
else if (offset == 0 && data.Length == 4)
{
/// iPerl mode of function and extra 2 bytes
Version = data[0];
MeterState = (MeterState)data[1];
return true;
}
else if (offset == 21 && data.Length == 1)
{
/// TestModeConfig value
TestModeConfig = data[21 - offset];
return true;
}
else if (offset == 0 && data.Length == Length)
{
/// Complete ConfigStruct
Version = data[0];
MeterState = (MeterState)data[1];
TargetTimeVeryLowBatt = (((UInt32)data[5] * 256 + data[4]) * 256 + data[3]) * 256 + data[2];
TargetTimeLowBatt = (((UInt32)data[9] * 256 + data[8]) * 256 + data[7]) * 256 + data[6];
TestModeTime = (((UInt32)data[13] * 256 + data[12]) * 256 + data[11]) * 256 + data[10];
EmptyPipeThreshold = (UInt16)(data[15] * 256 + data[14]);
PCBNumber[0] = data[16];
PCBNumber[1] = data[17];
PCBNumber[2] = data[18];
PCBNumber[3] = data[19];
PCBNumber[4] = data[20];
TestModeConfig = data[21];
RadioAddress = (((UInt32)data[25] * 256 + data[24]) * 256 + data[23]) * 256 + data[22];
TempCalibration = (UInt16)(data[27] * 256 + data[26]);
AlarmMask = (UInt16)(data[29] * 256 + data[28]);
ConfigCheckSum = (UInt16)(data[31] * 256 + data[30]);
return true;
}
else
return false;
}
/// <summary>
/// Returns PCB number string (12 characters, 12 decimal digits)
/// </summary>
/// <returns>PCB number STRING</returns>
public string GetPcbNrString()
{
return PCBNumber2String(this.PCBNumber);
}
public string GetPcbNrString(){
return PCBNumberString;
}
/// <summary>
/// Converts PCBNumber to string (12 characters, 12 decimal digits)
/// </summary>
/// <param name="pcbNumber"></param>
/// <returns>PCB number string</returns>
public static string PCBNumber2String(byte[] pcbNumber)
{
if (pcbNumber.Length != 5) return string.Empty;
Int64 number = 0;
for (int i = 4; i >= 0; i--)
{
number = 256 * number + (Int64)pcbNumber[i];
}
return number.ToString();
}
public string GetStatusModeString()
{
return string.Format(
"Config: StatusMode={0}",
StatusMode
);
}
public string GetActiveModeString()
{
return string.Format(
"Config: StatusMode={0}, OpthoStatusMode={1}",
StatusMode,
OpthoStatusMode
);
}
public override string ToString()
{
return string.Format("Config: V{0} State={1} VLoBattT={2}s LoBattT={3}s TestModeT={4}s EPThld={5} PCB#={6} TMCfg={7} RadioAddr={8} TempCalib={9} AlarmMask={10} CfgCheckSum={11}",
Version,
MeterState,
TargetTimeVeryLowBatt,
TargetTimeLowBatt,
TestModeTime,
EmptyPipeThreshold,
GetPcbNrString(),
TestModeConfig.ToString("X2"),
RadioAddress,
TempCalibration,
AlarmMask.ToString("X4"),
ConfigCheckSum.ToString("X4"));
return string.Format(
"Config: PCB#={0} StatusMode={1} Unit={2} V{3}",
GetPcbNrString(),
StatusMode,
Unit,
Version
);
}
public string ToString(int sel)
{
return string.Format("{1} PCB#={6} TMCfg={7}",
Version,
MeterState,
TargetTimeVeryLowBatt,
TargetTimeLowBatt,
TestModeTime,
EmptyPipeThreshold,
GetPcbNrString(),
TestModeConfig.ToString("X2"),
RadioAddress,
TempCalibration,
AlarmMask.ToString("X4"),
ConfigCheckSum.ToString("X4"));
return string.Format(
"Config: PCB#={0} StatusMode={1} Unit={2} V{3}",
GetPcbNrString(),
StatusMode,
Unit,
Version
);
}
public virtual void WriteBinary(BinaryWriter writer)
{
writer.Write(Version);
writer.Write((byte)MeterState);
writer.Write(TargetTimeVeryLowBatt);
writer.Write(TargetTimeLowBatt);
writer.Write(TestModeTime);
writer.Write(EmptyPipeThreshold);
writer.Write(PCBNumber[0]);
writer.Write(PCBNumber[1]);
writer.Write(PCBNumber[2]);
writer.Write(PCBNumber[3]);
writer.Write(PCBNumber[4]);
writer.Write(TestModeConfig);
writer.Write(RadioAddress);
writer.Write(TempCalibration);
writer.Write(AlarmMask);
writer.Write(ConfigCheckSum);
if (writer == null)
throw new ArgumentNullException(nameof(writer));
// ---- Marker ----
writer.Write((byte)0x11);
// ---- Version ----
if (!string.IsNullOrEmpty(Version))
{
// Convert string to bytes (UTF8 is standard)
byte[] versionBytes = Encoding.UTF8.GetBytes(Version);
// 1) write length
writer.Write(versionBytes.Length);
// 2) write string bytes
writer.Write(versionBytes);
//writer.Write(Version);
}
else
{
writer.Write(0);//Length
}
// ---- StatusMode ----
writer.Write((byte)StatusMode);
// ---- PCB Number ----
if (!string.IsNullOrEmpty(PCBNumberString))
{
byte[] PCBNumberStringBytes = Encoding.UTF8.GetBytes(PCBNumberString);
// 1) write length
writer.Write(PCBNumberStringBytes.Length);
// 2) write string bytes
writer.Write(PCBNumberStringBytes);
}
else
{
writer.Write(0); //Length
}
// ---- Unit ----
if (!string.IsNullOrEmpty(Unit))
{
// Convert string to bytes (UTF8 is standard)
byte[] unitBytes = Encoding.UTF8.GetBytes(Unit);
// 1) write length
writer.Write(unitBytes.Length);
// 2) write string bytes
writer.Write(unitBytes);
//writer.Write(Version);
}
else
{
writer.Write(0); //Length
}
}
public virtual void ReadBinary(BinaryReader reader)
{
Version = reader.ReadByte();
MeterState = (MeterState)reader.ReadByte();
TargetTimeVeryLowBatt = reader.ReadUInt32();
TargetTimeLowBatt = reader.ReadUInt32();
TestModeTime = reader.ReadUInt32();
EmptyPipeThreshold = reader.ReadUInt16();
PCBNumber[0] = reader.ReadByte();
PCBNumber[1] = reader.ReadByte();
PCBNumber[2] = reader.ReadByte();
PCBNumber[3] = reader.ReadByte();
PCBNumber[4] = reader.ReadByte();
TestModeConfig = reader.ReadByte();
RadioAddress = reader.ReadUInt32();
TempCalibration = reader.ReadUInt16();
AlarmMask = reader.ReadUInt16();
ConfigCheckSum = reader.ReadUInt16();
if (reader == null)
throw new ArgumentNullException(nameof(reader));
// ---- Marker ----
byte marker = reader.ReadByte();
if (marker != 0x11)
throw new InvalidDataException($"Invalid config marker: 0x{marker:X2}");
// ---- Version ----
int versionLength = reader.ReadInt32();
if (versionLength < 0)
throw new InvalidDataException("Invalid Version length.");
byte[] versionBytes = reader.ReadBytes(versionLength);
if (versionBytes.Length != versionLength)
throw new EndOfStreamException("Unexpected end of stream while reading Version.");
Version = versionLength > 0
? Encoding.UTF8.GetString(versionBytes)
: string.Empty;
// ---- StatusMode ----
StatusMode = (ProtocolStatuses)reader.ReadByte();
// ---- PCB Number ----
int pcbLength = reader.ReadInt32();
if (pcbLength < 0)
throw new InvalidDataException("Invalid PCB number length.");
byte[] pcbBytes = reader.ReadBytes(pcbLength);
if (pcbBytes.Length != pcbLength)
throw new EndOfStreamException("Unexpected end of stream while reading PCB number.");
PCBNumberString = pcbLength > 0
? Encoding.UTF8.GetString(pcbBytes)
: string.Empty;
// ---- Unit ----
int unitLength = reader.ReadInt32();
if (unitLength < 0)
throw new InvalidDataException("Invalid Unit length.");
byte[] unitBytes = reader.ReadBytes(unitLength);
if (unitBytes.Length != unitLength)
throw new EndOfStreamException("Unexpected end of stream while reading Unit.");
Unit = unitLength > 0
? Encoding.UTF8.GetString(unitBytes)
: string.Empty;
}
}
}
@@ -11,40 +11,35 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
private static readonly ILog log = LogManager.GetLogger(typeof(IperlHead));
const int FIFO_SIZE = 64; /// 8 sec. @ 8Hz
const double MAX_OPTO_DROPOUT = 4.5; /// sec.
const int FIFO_SIZE = 64; // 8 sec @ 8Hz
const double MAX_OPTO_DROPOUT = 4.5; // sec
Int64[] volumeRawFifo; /// Volume FIFO buffer
Int64[] timestampFifo; /// Timestamp FIFO buffer
private readonly double[] volumeRawFifo;
private readonly double[] timestampFifo; // centered timestamps
int fifoCount; /// Number of valid FIFO items
int fifoIx; /// Index of the next FIFO item
DateTime lastFifoWriteTime; /// Time of the last write to FIFO
private int fifoCount;
private int fifoIx;
private DateTime lastFifoWriteTime;
///
/// Sums for linear regression calculation
///
decimal sumXX;
decimal sumX;
decimal sumXY;
decimal sumY;
decimal N;
// regression sums (double is ideal here)
private double sumXX;
private double sumX;
private double sumXY;
private double sumY;
double minSlope; /// max. slope of the regressed line, always positive or 0
double maxSlope; /// min. slope of the regressed line, always negative or 0
private double minSlope;
private double maxSlope;
// timestamp centering for numerical stability
private double firstTimestamp = double.NaN;
public FlowDirectionDetection()
{
volumeRawFifo = new Int64[FIFO_SIZE];
timestampFifo = new Int64[FIFO_SIZE];
volumeRawFifo = new double[FIFO_SIZE];
timestampFifo = new double[FIFO_SIZE];
ClearFifo();
}
/// <summary>
/// Clear FIFO data
/// </summary>
public void ClearFifo()
{
fifoCount = 0;
@@ -55,94 +50,93 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
sumX = 0;
sumXY = 0;
sumY = 0;
N = 0;
minSlope = 0;
maxSlope = 0;
firstTimestamp = double.NaN;
}
/// <summary>
/// Write data to FIFO
/// Add sample to rolling FIFO and update regression sums
/// </summary>
/// <param name="volumeRaw">Volume</param>
/// <param name="timestamp">Time stamp</param>
public void WriteToFifo(Int64 volumeRaw, Int64 timestamp)
public void WriteToFifo(double volumeRaw, double timestamp)
{
///
/// Update sums for linear regression calculation
///
// establish time origin (CRITICAL for double precision)
if (double.IsNaN(firstTimestamp))
firstTimestamp = timestamp;
double x = timestamp - firstTimestamp; // centered time
double y = volumeRaw;
// remove oldest sample if buffer full
if (fifoCount == FIFO_SIZE)
{
/// Buffer is already full, the oldest item will be re-written
sumXX -= timestampFifo[fifoIx] * timestampFifo[fifoIx];
sumX -= timestampFifo[fifoIx];
sumXY -= timestampFifo[fifoIx] * volumeRawFifo[fifoIx];
sumY -= volumeRawFifo[fifoIx];
N--;
double oldX = timestampFifo[fifoIx];
double oldY = volumeRawFifo[fifoIx];
sumXX -= oldX * oldX;
sumX -= oldX;
sumXY -= oldX * oldY;
sumY -= oldY;
}
sumXX += timestamp * timestamp;
sumX += timestamp;
sumXY += timestamp * volumeRaw;
sumY += volumeRaw;
N++;
else
{
fifoCount++;
}
// add new sample
sumXX += x * x;
sumX += x;
sumXY += x * y;
sumY += y;
// store sample
timestampFifo[fifoIx] = x;
volumeRawFifo[fifoIx] = y;
///
/// Save new values to FIFO
///
volumeRawFifo[fifoIx] = volumeRaw;
timestampFifo[fifoIx] = timestamp;
fifoIx = (fifoIx + 1) % FIFO_SIZE;
fifoCount = Math.Min(fifoCount + 1, FIFO_SIZE);
lastFifoWriteTime = DateTime.Now;
}
/// <summary>
/// Determine whether there are enough recent FIFO data
/// </summary>
/// <returns>true when data valid</returns>
public bool AreFifoDataValid()
{
return (DateTime.Now.Subtract(lastFifoWriteTime).TotalSeconds <= MAX_OPTO_DROPOUT) && (fifoCount == FIFO_SIZE);
return (DateTime.Now.Subtract(lastFifoWriteTime).TotalSeconds <= MAX_OPTO_DROPOUT)
&& (fifoCount == FIFO_SIZE);
}
/// <summary>
/// Verify whether the flow direction is correct
/// </summary>
/// <returns>OptoHeadState.OptoAndDirOK, OptoHeadState.OptoNok or OptoHeadState.DirNok</returns>
public OptoHeadState CheckFlowDirection(Counting counting, string iPerlHeadName)
{
if (!AreFifoDataValid()) return OptoHeadState.OptoNok;
if (!AreFifoDataValid())
return OptoHeadState.OptoNok;
try
{
decimal numer = N * sumXY - sumX * sumY;
decimal denom = N * sumXX - sumX * sumX;
double N = fifoCount;
if (denom == 0) return OptoHeadState.DirNok;
double numer = N * sumXY - sumX * sumY;
double denom = N * sumXX - sumX * sumX;
if (Math.Abs(denom) < 1e-12)
return OptoHeadState.DirNok;
double slope = numer / denom;
/// Calculate the slope of the regressed line, determine min. and max.
double slope = (double)(numer / denom);
if (slope > maxSlope) maxSlope = slope;
if (slope < minSlope) minSlope = slope;
if ( counting == Counting.Arbitrary ||
if (counting == Counting.Arbitrary ||
(counting == Counting.Positive && maxSlope > Math.Abs(2 * minSlope)) ||
(counting == Counting.Negative && minSlope < -Math.Abs(2 * maxSlope)))
{
return OptoHeadState.OptoAndDirOK;
}
else
{
return OptoHeadState.DirNok;
}
return OptoHeadState.DirNok;
}
catch (Exception)
catch (Exception ex)
{
log.ErrorFormat("{0} : CheckFlowDirection() failed", iPerlHeadName);
return OptoHeadState.DirNok; /// ???
log.ErrorFormat("{0} : CheckFlowDirection() failed: {1}", iPerlHeadName, ex);
return OptoHeadState.DirNok;
}
}
}
@@ -6,28 +6,36 @@ using System.IO;
using System.IO.Ports;
using log4net;
using Common;
using Common.Iperl;
using Config.Entities;
using TBF.Rig.Generic;
using TBF.Rig.GenericDevices;
using Sensus.iPerl.NfcHandler;
using NHibernate;
using Renci.SshNet;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq; // This line is correct and does not need to be changed.
using System.Windows;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using TBF.Rig.TestMethods.iPerlCommunication.communication; // This line is correct and does not need to be changed.
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed.parserer;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed.utils;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.hexLogger;
using TBF.Rig.TestMethods.iPerlCommunication.communication.Utils;
using OptoTelegramFlags = TBF.Rig.TestMethods.iPerlCommunication.common.OptoTelegramFlags;
using OptoTelegramRaw = TBF.Rig.TestMethods.iPerlCommunication.common.OptoTelegramRaw;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
/// <summary>
/// This component = instance of this class is a placeholder for a combined main watermeter
/// </summary>
public class IperlHead : ComponentBase, IDevice,IRegReaderDatastream, ISessionDataMngmnt, IOperation, ISmartReader
public class IperlHead : ComponentBase, IDevice,IRegReaderDatastream, ISessionDataMngmnt, IOperation, IRegReaderSmart
{
private static readonly ILog log = LogManager.GetLogger(typeof(IperlHead));
private static readonly ILog logStream = LogManager.GetLogger("StreamData");
public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
#if TURA_SPECIAL
@@ -38,63 +46,49 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
public const string OptoDataDirectory = "C:\\TBF\\ProcessData";
public const int StartOptoDataCount = OptoDataBufferSize / 2;
public const int EndOptoDataCount = OptoDataBufferSize - StartOptoDataCount;
public const int StartEndFilterSamplesCount2 = 20; /// StartEndFilterSamplesCount = 2 * StartEndFilterSamplesCount2 + 1
public const int StartEndFilterSamplesCount2 = 2; //20 /// StartEndFilterSamplesCount = 2 * StartEndFilterSamplesCount2 + 1
public const int FeatureVectorSize = 9;
private OptoHeadTest _optoHeadTest;
public OptoHeadTest OptoHeadTest
{
get
{
if (_optoHeadTest == null)
_optoHeadTest = new OptoHeadTest(this);
return _optoHeadTest;
}
set { _optoHeadTest = value; }
}
readonly IperlHeadCfg iperlHeadCfg;
public int RfidComPortNr { get { return iperlHeadCfg.RfidComPortNr; } }
public bool CommFailed { get; set; }
public bool Disabled { get; set; }
public int OptoComPortNr { get { return iperlHeadCfg.OptoComPortNr; } }
public int MuxBoardNrOrGroup14 { get { return iperlHeadCfg.MuxBoardNr; } }
public int Group { get { return iperlHeadCfg.Group; } }
public iPerlHead.MeterType MeterType { get { return iperlHeadCfg.MeterType; } }
public string CommInterface { get { return iperlHeadCfg.CommunicationInterface.ToDescription(); } }
public CommunicationInterface CommInterface { get { return iperlHeadCfg.CommunicationInterface; } }
static int? ExtractPreferredNumber(string input)
{
if (string.IsNullOrEmpty(input))
return null;
// Match all sequences of digits
var matches = Regex.Matches(input, @"\d+");
if (matches.Count == 0)
return null;
// Prefer the last one if there are multiple
string selected = matches[matches.Count - 1].Value; // last element
return int.Parse(selected);
}
/// <summary>
/// this is a hack to get the position from the name
/// name must consist only with digit describing order of the meter
/// </summary>
public int Position
{
get
{
//BUMI this is a hack to get the position from the name
// but it is not working for all cases !!!!!
// - what stupid prediction (what index should be used?? see: 'S4iPerl1' or ...)
int? extractPreferredNumber = ExtractPreferredNumber(Name);
if (extractPreferredNumber.HasValue)
return extractPreferredNumber.Value;
return 0;
int firstDigitPos = Name.IndexOfAny(new char[] { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' });
int position;
return (firstDigitPos < 0) ? 0 : (int.TryParse(Name.Substring(firstDigitPos), out position) ? position : 0);
}
}
public RegisterReaderType RegisterReaderType { get { return RegisterReaderType.DataStream; } }
public double PulsesPerLtr {
get { return 1000.0; }
set {PulsesPerLtr = value; }
get { return 1000.0; }
set { }
}
public double LtrsPerPulse { get { return 1 / PulsesPerLtr; } }
public string QuantityUnits { get; set; }
public double CalibTarget { get { return iperlHeadCfg.ProcParams.CalibTarget; } }
public ushort FactorLimitLo { get { return (ushort)iperlHeadCfg.ProcParams.FactorLimitLo; } }
public ushort FactorLimitHi { get { return (ushort)iperlHeadCfg.ProcParams.FactorLimitHi; } }
@@ -118,7 +112,9 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
simulatedPcbNr = value;
}
}
public bool Disabled;
public bool CommFailed;
public int ResultCode;
@@ -132,8 +128,8 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
/// <summary>
/// Passed to OptoTelegramRaw.UpdateFromString(...)
/// </summary>
Int64 volumeRawExtLast;
Int64 timestampExtLast;
double volumeRawExtLast;
double timestampExtLast;
FlowDirectionDetection flowDirectionDetection;
@@ -190,13 +186,23 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
///
public int WMPulses { get { return wmPulses; } }
public int WMRefPulses { get { return wmRefPulses; } }
public double BeginWMState { get { return beginWMState; } set { beginWMState = value; }}
public double EndWMState { get { return endWMState; } set { endWMState = value;} }
public double WMVolume { get { return wmVolume; } }
public double WMTestTime { get { return wmTestTime; } }
public double BeginWMState { get { return ResolveNaNDouble(beginWMState); } }
public double EndWMState { get { return ResolveNaNDouble(endWMState); } }
public double WMVolume { get { return ResolveNaNDouble(wmVolume); } }
public double WMTestTime { get { return ResolveNaNDouble(wmTestTime); } }
string simulatedPcbNr = null;
double ResolveNaNDouble(double d)
{
if (Double.IsNaN(d))
{
return 0.0;
}
else
return d;
}
int wmPulses;
int wmRefPulses;
double beginWMState;
@@ -269,7 +275,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
const double C = B * 60.0; /// Raw units per hour, 480
double D = C / A; /// ml correction per hour
double F = D / (nominalTestFlowLph * 10.0); /// Error corrected with 8 Raw Units per minute [%]
double G = F / B; /// Error corrected with 1 Raw Unit per minute [%]
double G = F / B; /// Error corrected with 1 Raw Units per minute [%]
/// Do not change the factor for an invalid measurement (q2adjResult.VolumeMeter == 0)
double q2CorrectionFactor = (Math.Abs(currentQ2Result.VolumeMeter) <= float.Epsilon) ? Convert.ToDouble(currentFactor) :
@@ -389,7 +395,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
///
/// Timestamp from the opto telegram
///
private Int64 lastTimestamp;
private double lastTimestamp;
private double timestampSec;
private double timestampSec0;
@@ -412,14 +418,14 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
///
/// Volume of water from the opto telegram
///
private Int64 lastVolumeRaw; /// Last read raw volume
private double lastVolumeRaw; /// Last read raw volume
private double volumeLtr;
private double volumeLtr0;
/// Test start volume for metrology in liters
public double VolumeLtrStart
{
get { return NoSamples ? 0 : VolumeFromSamples(optoData, optoDataCount, TestStartTelegramIx, ScalingFactor(), StartEndFilterSamplesCount2); }
get { return NoSamples ? 0 : VolumeFromSamples(optoData, optoDataCount, TestStartTelegramIx, ScalingFactor(), 0); }
}
/// Test end volume for metrology in liters
public double VolumeLtrEnd
@@ -473,7 +479,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
/// Check whether head is connected, working
try
{
OpenOptoSerialPort($"COM{iperlHeadCfg.OptoComPortNr}", 9600, Parity.None, 8, StopBits.One, Handshake.None);
OpenOptoSerialPort($"COM{iperlHeadCfg.OptoComPortNr}", 38400, Parity.None, 8, StopBits.One, Handshake.None);
CloseOptoSerialPort();
log.FatalFormat($"{Name} initialized: {this}");
}
@@ -618,10 +624,10 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
ResultCode = 0;
volumeLtr = 0;
volumeLtr0 = 0;
timestampSec = 0;
timestampSec0 = 0;
volumeLtr = Double.NaN;
volumeLtr0 = Double.NaN;
timestampSec = Double.NaN;
timestampSec0 = Double.NaN;
extraDataPath = null;
@@ -887,34 +893,63 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
endWMState = volumeLtr;
wmVolume = Math.Abs(endWMState - beginWMState);
wmPulses = (int)(wmVolume * (double)PulsesPerLtr + 0.5);
log.Debug("wmPulses = " + wmPulses + "wmVolume = " + wmVolume + "PulsesPerLtr = " + PulsesPerLtr + "");
wmRefPulses = StateMachine.ControlBoardMain.RefPulses;
wmTestTime = timestampSec - timestampSec0;
}
private void OpenOptoSerialPort(string comPort, int baudRate, Parity parity, int dataBits, StopBits stopBit, Handshake handshake)
private void OpenOptoSerialPort(
string comPort,
int baudRate,
Parity parity,
int dataBits,
StopBits stopBit,
Handshake handshake,
int openTimeoutMs = 3000)
{
if (DebugLevel == DebugMode.FailureDuringOperation) DebugLevel = DebugMode.Normal;
if (DebugLevel == DebugMode.Normal)
{
/// Open serial port: 9600 Bd, 8 data bits, 1 stop bit, no parity
try
{
CloseOptoSerialPort();
optoSerialPort = new SerialPort(comPort, baudRate, parity, dataBits, stopBit);
optoSerialPort.Handshake = handshake;
optoSerialPort.Open();
log.FatalFormat($"{Name} OptoPort opened: {this}");
}
catch (Exception ex)
{
log.FatalFormat($"{Name} OptoPort - error opening port: {this}" + Environment.NewLine + ex.Message);
throw ex;
}
}
else
if (DebugLevel != DebugMode.Normal)
{
optoSerialPort = null;
log.FatalFormat($"{Name} OproPort simulated: {this}");
return;
}
try
{
CloseOptoSerialPort();
var port = new SerialPort(comPort, baudRate, parity, dataBits, stopBit)
{
Handshake = handshake,
NewLine = "\r\n",
Encoding = Encoding.ASCII
};
port.ReadTimeout = 5000;
port.WriteTimeout = 5000;
port.DtrEnable = true;
port.RtsEnable = true;
// Run Open() on separate task
var openTask = Task.Run(() => port.Open());
if (!openTask.Wait(openTimeoutMs))
{
port.Dispose();
throw new TimeoutException(
$"Opening serial port {comPort} timed out after {openTimeoutMs} ms.");
}
optoSerialPort = port;
log.FatalFormat($"{Name} OptoPort opened: {this}");
}
catch (Exception ex)
{
log.FatalFormat($"{Name} OptoPort - error opening port: {this}"
+ Environment.NewLine + ex.Message);
throw; // NEVER use "throw ex;" (destroys stack trace)
}
}
@@ -937,7 +972,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
try
{
OpenOptoSerialPort($"COM{iperlHeadCfg.OptoComPortNr}", 9600, Parity.None, 8, StopBits.One, Handshake.None);
OpenOptoSerialPort($"COM{iperlHeadCfg.OptoComPortNr}", 38400, Parity.None, 8, StopBits.One, Handshake.None);
}
catch (Exception)
{
@@ -961,18 +996,6 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
dataStreamState = DataStreamState.ProcessAndSave;
}
public void SetCommunicationInterface(string commInterface)
{
CommunicationInterface com = (CommunicationInterface)Enum.Parse(typeof(CommunicationInterface), commInterface);
SetCommunicationInterface(com);
}
public void SetCommunicationInterface(RegisterReaders.CommonRR.CommunicationInterface commInterface)
{
CommunicationInterface com = (CommunicationInterface)Enum.Parse(typeof(CommunicationInterface), commInterface.ToDescription());
SetCommunicationInterface(com);
}
/// <summary>
/// Returns true when processing and saving datastream data is in progress
/// </summary>
@@ -981,11 +1004,6 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
return dataStreamState == DataStreamState.ProcessAndSave;
}
void ISmartReader.SetRfidInterface()
{
SetRfidInterface();
}
/// <summary>
/// Stop processing and saving datastream data
/// </summary>
@@ -1001,121 +1019,95 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
bool synchronized;
bool synchronized2;
string partOfTelegram;
private RegisterReaders.CommonRR.CommunicationInterface _commInterface;
private string _commInterface1;
DiagnosticLedParser parser = new DiagnosticLedParser(DiagnosticLedState.State4);
/// <summary>
/// Reads opto-datastream via serial port. Invoked from RunDeviceBefore()
///
/// Telegram description:
/// AAAAAA[tab]BBBB[tab]CCCC[tab]DDDDDD[tab]EEEE[tab]FFFFFFFF[tab]GG[cr][lf] (42 bytes)
/// Example:
/// FFFFFE 51EA 0000 65324E 0087 F6319DFF 86
/// FFDD3A 51F9 0000 65324E 0088 F631A60B 45
/// ...
/// </summary>
/// <param name="optoState">OptoState.Read or OptoState.Flush</param>
void ReadOptoData(DataStreamState optoState)
{
/// Telegram description:
/// AAAAAA[tab]BBBB[tab]CCCC[tab]DDDDDD[tab]EEEE[tab]FFFFFFFF[tab]GG[cr][lf] (42 bytes)
/// Example:
/// FFFFFE 51EA 0000 65324E 0087 F6319DFF 86
/// FFDD3A 51F9 0000 65324E 0088 F631A60B 45
/// ...
/// </summary>
/// <param name="optoState">OptoState.Read or OptoState.Flush</param>
void ReadOptoData(DataStreamState optoState)
{
if (optoSerialPort is null) return;
lock (this)
{
int nrBytes = optoSerialPort.BytesToRead;
if (nrBytes > 0)
try
{
char[] buffer = new char[nrBytes];
optoSerialPort.Read(buffer, 0, nrBytes);
string received = new string(buffer);
string allRcvd = partOfTelegram + received;
while (true)
int nrBytes = optoSerialPort.BytesToRead;
if (nrBytes > 0)
{
int pos = allRcvd.IndexOf("\r\n");
// This will now wait max 3 seconds (ReadTimeout)
string line = optoSerialPort.ReadLine();
if (pos < 0)
byte[] bytes = optoSerialPort.Encoding.GetBytes(line);
log.Debug("ComPort: " + OptoComPortNr + " OPTHO RX ← " + HexFormatter.ToSerialHex(bytes));
if (optoState == DataStreamState.ProcessAndSave)
{
/// No CR+LF found, wait for more characters in the next invocation
partOfTelegram = allRcvd;
return;
DiagnosticLedState4Data data =
(DiagnosticLedState4Data)parser.ParseLine(line, false);
int bufferIx = BufferIdx(optoDataCount);
if (synchronized)
{
optoData[bufferIx].Counter = optoDataCount;
optoData[bufferIx].SetFlags(OptoTelegramFlags.SyncError);
}
if (data != null)
{
log.Debug($"OPTHO {OptoComPortNr} Parsed optho data:" + data);
logStream.Debug($"ID: {OptoComPortNr} " + data);
optoData[bufferIx].UpdateFromSmart(
data,
optoDataCount,
Convert.ToSingle(Sequences.ProcessData.RefFlow.Val),
ref volumeRawExtLast,
ref timestampExtLast);
flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast);
OptoTelegramReceived(
optoDataCount,
true,
volumeRawExtLast,
timestampExtLast);
}
optoDataCount++;
}
else
{
/// CR+LF found
if (optoState == DataStreamState.ProcessAndSave)
{
int bufferIx = BufferIdx(optoDataCount);
// Flush mode
DiagnosticLedState4Data data =
(DiagnosticLedState4Data)parser.ParseLine(line, false);
if (pos < OptoTelegramRaw.Length - 2)
{
/// CR+LF found too early, truncate the beginning incl CR+LF and keep scanning in this loop
allRcvd = allRcvd.Substring(pos + 2);
if (synchronized)
{
optoData[bufferIx].Counter = optoDataCount;
optoData[bufferIx].SetFlags(OptoTelegramFlags.SyncError);
}
synchronized = true;
}
else if (optoData[bufferIx].UpdateFromString(allRcvd.Substring(pos - OptoTelegramRaw.Length + 2),
optoDataCount,
Convert.ToSingle(Sequences.ProcessData.RefFlow.Val),
ref volumeRawExtLast, ref timestampExtLast))
{
/// CR+LF was found && (pos >= OptoTelegramRaw.Length - 2) && the telegram is OK
flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast);
OptoTelegramReceived(optoDataCount, synchronized2, volumeRawExtLast, timestampExtLast);
synchronized2 = synchronized;
allRcvd = allRcvd.Substring(pos + 2);
}
else
{
/// CR+LF was found && (pos >= OptoTelegramRaw.Length - 2) but the telgram was not OK
optoData[bufferIx].Counter = optoDataCount;
optoDataCount++;
allRcvd = allRcvd.Substring(pos + 2);
}
optoDataCount++;
}
else /// optoState == OptoState.Flush
{
if (pos < OptoTelegramRaw.Length - 2)
{
/// CR+LF found too early, truncate the beginning incl CR+LF and keep scanning in this loop
allRcvd = allRcvd.Substring(pos + 2);
synchronized = true;
}
// CR+LF found and (pos >= OptoTelegram.Length - 2)
else if (toBeFlushed.UpdateFromString(allRcvd.Substring(pos - OptoTelegramRaw.Length + 2),
0,
Convert.ToSingle(Sequences.ProcessData.RefFlow.Val),
ref volumeRawExtLast, ref timestampExtLast))
{
flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast);
synchronized2 = synchronized;
allRcvd = allRcvd.Substring(pos + 2);
}
else
{
allRcvd = allRcvd.Substring(pos + 2);
}
}
flowDirectionDetection.WriteToFifo(volumeRawExtLast, timestampExtLast);
}
}
//OnOptoReceived(this, new OptoReceivedEventArgs(s));
}
else
catch (TimeoutException)
{
//OnOptoReceived(this, new OptoReceivedEventArgs("."));
// ✅ No data received within 3 seconds
log.Debug($"OPTHO {OptoComPortNr} ReadLine timeout (3s) - continuing.");
// Just continue without parsing
}
catch (Exception ex)
{
log.Error($"OPTHO {OptoComPortNr} Read error: {ex.Message}");
}
}
}
void ISmartReader.ResetNfcInterface(bool? nfc_on)
{
ResetNfcInterface(nfc_on);
}
public string ReadOptoData()
@@ -1124,48 +1116,114 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
string received = ".";
lock (this)
{
int nrBytes = optoSerialPort.BytesToRead;
if (nrBytes > 0)
try
{
char[] buffer = new char[nrBytes];
optoSerialPort.Read(buffer, 0, nrBytes);
received = new string(buffer);
string line = optoSerialPort.ReadLine(); // string
byte[] bytes = optoSerialPort.Encoding.GetBytes(line);
received = HexFormatter.ToSerialHex(bytes);
log.Debug("RX ← " + received);
}
catch (TimeoutException)
{
// ✅ No data received within 3 seconds
log.Debug($"ReadOptoData() OPTHO {OptoComPortNr} ReadLine timeout (3s) - continuing.");
// Just continue without parsing
}
catch (Exception ex)
{
log.Error($"ReadOptoData() OPTHO {OptoComPortNr} Read error: {ex.Message}");
}
}
return received;
}
void ISmartReader.SetNfcInterface()
public async Task<string> ReadOptoDataWithTimeoutAsync(int timeoutMs = 5000)
{
SetNfcInterface();
if (optoSerialPort == null)
return string.Empty;
var readTask = Task.Run(() =>
{
lock (this)
{
if (optoSerialPort== null) return string.Empty;
try
{
string line = optoSerialPort.ReadLine();
byte[] bytes = optoSerialPort.Encoding.GetBytes(line);
string received = HexFormatter.ToSerialHex(bytes);
log.Debug("RX ← " + received);
return line;
}
catch (TimeoutException)
{
// ✅ No data received within 3 seconds
log.Debug($"ReadOptoData() OPTHO {OptoComPortNr} ReadLine timeout (3s) - continuing.");
// Just continue without parsing
}
catch (Exception ex)
{
log.Error($"ReadOptoData() OPTHO {OptoComPortNr} Read error: {ex.Message}");
}
return string.Empty;
}
});
var completedTask = await Task.WhenAny(readTask, Task.Delay(timeoutMs));
if (completedTask == readTask)
{
return await readTask; // completed successfully
}
log.Debug("ReadOptoData timeout after " + timeoutMs + " ms");
return string.Empty; // timeout case
}
public string ReadOptoDataWithTimeout(int timeoutMs = 5000)
{
try
{
return ReadOptoDataWithTimeoutAsync(timeoutMs)
.GetAwaiter()
.GetResult();
}
catch
{
return string.Empty;
}
}
void OptoTelegramReceived(int currentIx, bool async, Int64 volumeRawExt, Int64 timestampRawExt)
void OptoTelegramReceived(int currentIx, bool async, double volumeRawExt, double timestampRawExt)
{
currentTelegramIx = currentIx;
lastVolumeRaw = volumeRawExt;
lastTimestamp = timestampRawExt;
if (volumeLtr == 0 && volumeLtr0 == 0)
if (Double.IsNaN(volumeLtr) && Double.IsNaN(volumeLtr0))
{
volumeLtr = (double)lastVolumeRaw * ScalingFactor() / 16000.0;
volumeLtr = lastVolumeRaw;
volumeLtr0 = volumeLtr;
}
else
{
volumeLtr = (double)lastVolumeRaw * ScalingFactor() / 16000.0;
volumeLtr = lastVolumeRaw;
}
if (timestampSec == 0 && timestampSec0 == 0)
if (Double.IsNaN(timestampSec)&& Double.IsNaN(timestampSec0))
{
timestampSec = (double)lastTimestamp / 8192.0;
timestampSec = lastTimestamp;
timestampSec0 = timestampSec;
}
else
{
timestampSec = (double)lastTimestamp / 8192.0;
timestampSec = lastTimestamp;
}
}
@@ -1270,25 +1328,55 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
/// <returns>Filtered volume</returns>
double VolumeFromSamples(OptoTelegramRaw[] optoData, int optoDataCount, int unwrappedIx, double scalingFactor, int samplesCount2 = 0)
{
log.Debug("-- Get VolumeFromSamples() --");
if (samplesCount2 == 0)
{
if (unwrappedIx >= optoDataCount)
{
log.Debug(
$"-- FAILED VolumeFromSamples() - unwrappedIx {unwrappedIx} >= optoDataCount{optoDataCount}--");
return 0;
}
int wrappedIx = BufferIdx(unwrappedIx);
if (optoData[wrappedIx].Flags != OptoTelegramFlags.OK &&
optoData[wrappedIx].Flags != OptoTelegramFlags.OK_TestStart &&
optoData[wrappedIx].Flags != OptoTelegramFlags.OK_TestEnd)
{
log.Debug($"-- Get VolumeFromSamples() - Quit because:{optoData[wrappedIx].Flags}--");
return 0;
}
log.Debug($"Valid data VolumeRawExt: {optoData[wrappedIx].VolumeRawExt}");
return optoData[wrappedIx].VolumeRawExt;
}
//TODO BUMI - do result as average from data - usually 5 samples
if (samplesCount2 < 0) samplesCount2 = 0;
if ((unwrappedIx - samplesCount2) < 0 || (unwrappedIx + samplesCount2) >= optoDataCount) return 0;
Int64 sum = 0;
double sum = 0;
for (int i = unwrappedIx - samplesCount2; i <= unwrappedIx + samplesCount2; i++)
{
int wrappedIx = BufferIdx(i);
if (optoData[wrappedIx].Flags != OptoTelegramFlags.OK &&
optoData[wrappedIx].Flags != OptoTelegramFlags.OK_TestStart &&
optoData[wrappedIx].Flags != OptoTelegramFlags.OK_TestEnd)
{
return 0;
}
sum += optoData[wrappedIx].VolumeRawExt;
}
return 0.0000625 * scalingFactor * sum / (double)(2 * samplesCount2 + 1);
return sum / (double)(2 * samplesCount2 + 1);
//return 0.0000625 * scalingFactor * sum / (double)(2 * samplesCount2 + 1);
}
/// <summary>
@@ -1299,25 +1387,45 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
/// <returns>Filtered time</returns>
double TimeFromSamples(OptoTelegramRaw[] optoData, int optoDataCount, int unwrappedIx, int samplesCount2 = 0)
{
if (samplesCount2 < 0) samplesCount2 = 0;
if ((unwrappedIx - samplesCount2) < 0 || (unwrappedIx + samplesCount2) >= optoDataCount) return 0;
Int64 sum = 0;
for (int i = unwrappedIx - samplesCount2; i <= unwrappedIx + samplesCount2; i++)
log.Debug("-- Get TimeFromSamples() --");
if (unwrappedIx >= optoDataCount)
{
int wrappedIx = BufferIdx(i);
if (optoData[wrappedIx].Flags != OptoTelegramFlags.OK &&
optoData[wrappedIx].Flags != OptoTelegramFlags.OK_TestStart &&
optoData[wrappedIx].Flags != OptoTelegramFlags.OK_TestEnd)
{
return 0;
}
sum += optoData[wrappedIx].TimestampExt;
log.Debug(
$"-- FAILED TimeFromSamples() - unwrappedIx {unwrappedIx} >= optoDataCount{optoDataCount}--");
return 0;
}
int wrappedIx = BufferIdx(unwrappedIx);
return sum / (double)(8192 * (2 * samplesCount2 + 1));
if (optoData[wrappedIx].Flags != OptoTelegramFlags.OK &&
optoData[wrappedIx].Flags != OptoTelegramFlags.OK_TestStart &&
optoData[wrappedIx].Flags != OptoTelegramFlags.OK_TestEnd)
{
log.Debug($"-- Get TimeFromSamples() - Quit because:{optoData[wrappedIx].Flags}--");
return 0;
}
log.Debug($"Valid data TimestampExt: {optoData[wrappedIx].TimestampExt}");
return optoData[wrappedIx].TimestampExt;
// if (samplesCount2 < 0) samplesCount2 = 0;
// if ((unwrappedIx - samplesCount2) < 0 || (unwrappedIx + samplesCount2) >= optoDataCount) return 0;
//
// Int64 sum = 0;
// for (int i = unwrappedIx - samplesCount2; i <= unwrappedIx + samplesCount2; i++)
// {
// int wrappedIx = BufferIdx(i);
//
// if (optoData[wrappedIx].Flags != OptoTelegramFlags.OK &&
// optoData[wrappedIx].Flags != OptoTelegramFlags.OK_TestStart &&
// optoData[wrappedIx].Flags != OptoTelegramFlags.OK_TestEnd)
// {
// return 0;
// }
//
// sum += optoData[wrappedIx].TimestampExt;
// }
//
// return sum / (double)(8192 * (2 * samplesCount2 + 1));
}
/// <summary>
@@ -1536,5 +1644,204 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
return this;
}
public async Task<string> DataEntry_ReadSerialNumber()
{
log.Debug("called DataEntry_ReadSerialNumber()");
if (!string.IsNullOrEmpty(SerialNr)) return SerialNr;
//need to find serial number
SerialNr = await DataEntry_ReadSerialNumberAsync();
return SerialNr;
}
public Task<double> DataEntry_ReadBeginVolume()
{
log.Debug("called DataEntry_ReadBeginVolumer()");
Task<double> readedVolume = DataEntry_BeginVolumeAsync();
return readedVolume;
}
public Task<double> DataEntry_ReadEndVolume()
{
log.Debug("called DataEntry_ReadBeginVolumer()");
Task<double> readedVolume = DataEntry_EndVolumeAsync();
return readedVolume;
}
public async Task<double> DataEntry_EndVolumeAsync()
{
if (optoSerialPort == null || !optoSerialPort.IsOpen)
{
StartDataStreamProcessing();
if (optoSerialPort == null || !optoSerialPort.IsOpen)
{
log.Error($"optoSerialPort COM: {this.OptoComPortNr} is not open - DataEntry_EndVolumeAsync()");
return Double.NaN;
}
}
return await Task.Run(() =>
{
log.Debug($"Try get End Volume! COM: {this.OptoComPortNr}");
volumeLtr = Double.NaN;
int counter = 0;
while (Double.IsNaN(volumeLtr) && counter < 2)
{
counter++;
try
{
string readOptoDataWithTimeout = ReadOptoDataWithTimeout(2000);
if (!string.IsNullOrEmpty(readOptoDataWithTimeout))
{
try
{
DiagnosticLedState4Data data =
(DiagnosticLedState4Data)parser.ParseLine(readOptoDataWithTimeout, false);
volumeLtr = data.RawVolume;
break;
}
catch (Exception ex)
{
log.Warn($"Parsing DiagnosticLedState4Data: {ex.Message}");
}
}
}
catch (Exception ex)
{
break;
}
}
log.Debug($"Try get End Volume! COM: {this.OptoComPortNr}, Volume: {volumeLtr}");
if (optoSerialPort != null && optoSerialPort.IsOpen) CloseOptoSerialPort();
if (!Double.IsNaN(volumeLtr))
{
endWMState = volumeLtr;
if (!Double.IsNaN(beginWMState) && !Double.IsNaN(endWMState))
{
//Solve roll over
if (endWMState < beginWMState)
{
log.Debug($"Solve roll over! endWMState: {endWMState} < beginWMState: {beginWMState}");
const double VOL_RANGE_LITERS = 16777216.0 * 0.00025; // 4,194.304 l
endWMState += VOL_RANGE_LITERS;
volumeLtr = endWMState;
ReadPulses();
log.Debug($"Solve roll over! Upgraded endWMState: {endWMState}, beginWMState: {beginWMState}");
}
}
return endWMState;
}
//}
log.Warn("Default NaN value returned! Data Opto stream reading failed!");
return Double.NaN;
}).ConfigureAwait(false);
}
public async Task<double> DataEntry_BeginVolumeAsync()
{
if (ConfigStruct == null)
{
log.Debug("ConfigStruct is null - created new in ReadSerialNr()");
ConfigStruct = new ConfigStruct();
}
return await Task.Run(() =>
{
log.Debug($"Try get Start Volume! COM: {this.OptoComPortNr}");
Start();
volumeLtr0 = Double.NaN;
int counter = 0;
while (Double.IsNaN(volumeLtr0) && counter < 10)
{
counter++;
try
{
string readOptoDataWithTimeout = ReadOptoDataWithTimeout(5000);
if (!string.IsNullOrEmpty(readOptoDataWithTimeout))
{
try
{
DiagnosticLedState4Data data =
(DiagnosticLedState4Data)parser.ParseLine(readOptoDataWithTimeout, false);
volumeLtr0 = data.RawVolume;
break;
}
catch (Exception ex)
{
log.Warn($"Parsing DiagnosticLedState4Data: {ex.Message}");
}
}
}
catch (Exception ex)
{
break;
}
}
log.Debug($"Try get Start Volume! COM: {this.OptoComPortNr}, Volume: {volumeLtr0}");
if (optoSerialPort != null && optoSerialPort.IsOpen) CloseOptoSerialPort();
if (!Double.IsNaN(volumeLtr0))
{
beginWMState = volumeLtr0;
ReadPulses();
return beginWMState;
}
//}
log.Warn("Default NaN value returned! Data Opto stream reading failed!");
return Double.NaN;
}).ConfigureAwait(false);
}
public async Task<string> DataEntry_ReadSerialNumberAsync()
{
if (!string.IsNullOrEmpty(SerialNr))
return SerialNr;
if (ConfigStruct == null)
{
log.Debug("ConfigStruct is null - created new in ReadSerialNr()");
ConfigStruct = new ConfigStruct();
}
if (CommFailed || ConfigStruct == null)
return CommErr.CommFailed.ToString();
return await Task.Run(() =>
{
log.Debug($"Try get ReadSerialNr! COM: {this.RfidComPortNr}");
if (OptoHeadTest.ReadSerialNr())
{
SerialNr = this.ConfigStruct.PCBNumberString;
log.Debug("ReadSerialNr successful");
}
//optoHeadTest.CloseConnection();
return SerialNr;
});
}
}
}
@@ -3,7 +3,7 @@ using System.Threading;
using System.Web.UI.WebControls;
using System.Windows.Forms;
using TBF.Rig.Sequences;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication;
using TBF.Rig.TestMethods.iPerlCommunication.communication;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
@@ -22,7 +22,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
InitializeComponent();
if (config == null) return;
SmartCommunicationForm.TestMethodCfg = new TestMethodCfg_IPerl(null); // default values for iPerlCommunication
iPerlCommunicationForm.cfg = new TestMethodCfg(null); // default values for iPerlCommunication
foreach(var head in ProcessData.IperlHeads)
{
@@ -78,14 +78,16 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
using (Tools.LogChecker logChecker = new Tools.LogChecker("RfidData", log4net.Core.Level.Debug))
{
ListItem rfidListItem = new ListItem();
rfidListItem.Attributes.Add("style", "font-weight:bold");
rfidListItem.Attributes.Add("style", "font-volume:bold");
//rfidListItem.Attributes.Add("style", "font-weight:bold");
bool isTestModeSuccessful = false;
switch (rfidCommandComboBox.SelectedValue)
{
case "ReadPCB":
rfidListItem.Text = $"PCB: {OpticalHeadTest.ReadRequest_PCB(iPerlHead)}";
rfidListItem.Text = $"PCB: {iPerlHead.OptoHeadTest.ReadRequest_PCB()}";
break;
case "SetTestMode":
rfidListItem.Text = OpticalHeadTest.SetTestMode(iPerlHead);
rfidListItem.Text = iPerlHead.OptoHeadTest.SetTestMode(ref isTestModeSuccessful);
optoListBox.Items.Clear();
stopWorkerThread = false;
optoThread = new Thread(OptoWorker);
@@ -96,7 +98,8 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
}
break;
case "SetActiveMode":
rfidListItem.Text = OpticalHeadTest.SetActiveMode(iPerlHead);
rfidListItem.Text = iPerlHead.OptoHeadTest.SetActiveMode(ref isTestModeSuccessful);
stopWorkerThread = true;
iPerlHead.StopDataStreamProcessing(); // close opto port
break;
@@ -1,4 +1,4 @@
using log4net;
using log4net;
using Sensus.iPerl.NfcHandler;
using Sensus.iPerl.RfidCom.Exceptions;
using System;
@@ -12,9 +12,9 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
internal class NfcServices
{
protected static readonly ILog rfidDataLogger = LogManager.GetLogger("RfidData");
internal static int ReadRequest(TestMethodCfg_IPerl cfgIPerl, IperlHead iperlHead, MCI_Protocol.StructName structName, int offset, int length, out byte[] buffer)
internal static int ReadRequest(TestMethodCfg cfg, IperlHead iperlHead, MCI_Protocol.StructName structName, int offset, int length, out byte[] buffer)
{
for (int i = 0; i < cfgIPerl.MaxCommRetries; i++)
for (int i = 0; i < cfg.MaxCommRetries; i++)
{
NfcDataHandler _nfcDataHandler = new NfcDataHandler();
@@ -26,9 +26,9 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
try
{
rfidDataLogger.Info($"NFC COM{iperlHead.RfidComPortNr} ReadRequest : {structName}, {offset}, {length}");
OpenConnection(_nfcDataHandler, cfgIPerl, iperlHead);
OpenConnection(_nfcDataHandler, cfg, iperlHead);
buffer = MciRead(_nfcDataHandler, cfgIPerl, structName, (ushort)offset, length);
buffer = MciRead(_nfcDataHandler, cfg, structName, (ushort)offset, length);
_nfcDataHandler.RFProtocolOFF(); // turn off rf antenna due to possible interference
CloseComPort(_nfcDataHandler);
@@ -44,7 +44,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
}
catch (RfidValidationException)
{
Thread.Sleep(cfgIPerl.WaitTimeAfterFailure);
Thread.Sleep(cfg.WaitTimeAfterFailure);
}
catch (Exception ex)
{
@@ -57,7 +57,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
return 3;
}
internal static int WriteRequest(TestMethodCfg_IPerl cfgIPerl, IperlHead iperlHead, MCI_Protocol.StructName structName, int offset, int length, byte[] buffer)
internal static int WriteRequest(TestMethodCfg cfg, IperlHead iperlHead, MCI_Protocol.StructName structName, int offset, int length, byte[] buffer)
{
NfcDataHandler _nfcDataHandler = new NfcDataHandler();
MessageEventHandlers(_nfcDataHandler);
@@ -68,9 +68,9 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
try
{
rfidDataLogger.Info($"NFC COM{iperlHead.RfidComPortNr} WriteRequest : {structName}, {offset}, {length}, {ByteArrayToHexString(buffer)}");
OpenConnection(_nfcDataHandler, cfgIPerl, iperlHead);
OpenConnection(_nfcDataHandler, cfg, iperlHead);
MciWrite(_nfcDataHandler, cfgIPerl, structName, (ushort)offset, length, buffer);
MciWrite(_nfcDataHandler, cfg, structName, (ushort)offset, length, buffer);
_nfcDataHandler.RFProtocolOFF(); // turn off rf antenna due to possible interference
CloseComPort(_nfcDataHandler);
@@ -83,14 +83,14 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
}
}
private static void OpenConnection(NfcDataHandler nfcDataHandler, TestMethodCfg_IPerl cfgIPerl, IperlHead iperlHead)
private static void OpenConnection(NfcDataHandler nfcDataHandler, TestMethodCfg cfg, IperlHead iperlHead)
{
string comPort = $"COM{iperlHead.RfidComPortNr}";
int retryCount = 0 ;
Open:
nfcDataHandler.Close();
Thread.Sleep(100);
if (nfcDataHandler.OpenConnection(comPort, cfgIPerl.BaudRate, cfgIPerl.DataBits, cfgIPerl.ParityBit, cfgIPerl.StopBits))
if (nfcDataHandler.OpenConnection(comPort, cfg.BaudRate, cfg.DataBits, cfg.ParityBit, cfg.StopBits))
{
rfidDataLogger.Info($"NFC COM{iperlHead.RfidComPortNr} Port Open");
@@ -100,7 +100,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
if (!nfcDataHandler.ConnectDevice())
{
rfidDataLogger.Error($"NFC COM{iperlHead.RfidComPortNr} Error connect device.");
for (int i = 0; i < cfgIPerl.MaxCommRetries; i++)
for (int i = 0; i < cfg.MaxCommRetries; i++)
{
if (nfcDataHandler.Echo()) break;
}
@@ -110,7 +110,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
retryCount++;
rfidDataLogger.Error($"NFC COM{iperlHead.RfidComPortNr} Error connect reader. Reconnect comport {retryCount}");
if (retryCount < cfgIPerl.MaxCommRetries)
if (retryCount < cfg.MaxCommRetries)
{
nfcDataHandler.Close();
iperlHead.ResetNfcInterface(); // reset NFC head via optoport - switch to RFID and back to NFC interface
@@ -127,7 +127,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
nfcDataHandler.Close();
}
private static byte[] MciRead(NfcDataHandler nfcDataHandler, TestMethodCfg_IPerl cfgIPerl, StructName structName, ushort offset, int length)
private static byte[] MciRead(NfcDataHandler nfcDataHandler, TestMethodCfg cfg, StructName structName, ushort offset, int length)
{
bool isReadValues = false;
int retryCount = 0;
@@ -153,14 +153,14 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
rfidDataLogger.Info($"MCI Error: Unidentified");
retryCount++;
if (retryCount < cfgIPerl.MaxCommRetries)
if (retryCount < cfg.MaxCommRetries)
goto Read;
}
else
{
rfidDataLogger.Info("Last error message: " + nfcDataHandler.LastErrorMessage);
retryCount++;
if (retryCount < cfgIPerl.MaxCommRetries)
if (retryCount < cfg.MaxCommRetries)
goto Read;
}
}
@@ -168,13 +168,13 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
rfidDataLogger.Error("MciRead Last error message: " + ex.Message);
retryCount++;
if (retryCount < cfgIPerl.MaxCommRetries)
if (retryCount < cfg.MaxCommRetries)
goto Read;
}
return new byte[length];
}
private static void MciWrite(NfcDataHandler nfcDataHandler, TestMethodCfg_IPerl cfgIPerl, MCI_Protocol.StructName structName, ushort offset, int length, byte[] payload)
private static void MciWrite(NfcDataHandler nfcDataHandler, TestMethodCfg cfg, MCI_Protocol.StructName structName, ushort offset, int length, byte[] payload)
{
int retryCount = 0;
Write:
@@ -199,7 +199,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
rfidDataLogger.Error("MciWrite error message: " + ex.Message);
retryCount++;
if (retryCount < cfgIPerl.MaxCommRetries)
if (retryCount < cfg.MaxCommRetries)
goto Write;
}
}
@@ -149,16 +149,16 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
return pars;
}
public override bool UpdateFromDbEntity(ComponentProcedure dbEntity)
{
if (dbEntity == null) return false;
try
{
ProcParams tmp = Serializer.Deserialize(new StringReader(dbEntity.Parameters)) as ProcParams;
public override bool UpdateFromDbEntity(ComponentProcedure dbEntity)
{
if (dbEntity == null) return false;
try
{
ProcParams tmp = Serializer.Deserialize(new StringReader(dbEntity.Parameters)) as ProcParams;
procedureParamsEntity = dbEntity;
componentName = dbEntity.CmpntName;
procedure = dbEntity.Procedure;
procedureParamsEntity = dbEntity;
componentName = dbEntity.CmpntName;
procedure = dbEntity.Procedure;
if (tmp != null)
{
@@ -181,7 +181,8 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
}
}
public ProcParams()
public ProcParams()
{
}
@@ -1,29 +1,28 @@
using Sensus.iPerl.RfidCom.Exceptions;
using Sensus.iPerl.RfidCom.Exceptions;
using Sensus.iPerl.RfidCom.Helper;
using Sensus.iPerl.RfidCom;
using System;
using System.Text.RegularExpressions;
using System.Threading;
using log4net;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
internal class RfidServices
{
protected static readonly ILog rfidDataLogger = LogManager.GetLogger("RfidData");
private static readonly ILog log = LogManager.GetLogger(typeof(SmartCommunicationForm));
private static readonly ILog log = LogManager.GetLogger(typeof(iPerlCommunicationForm));
internal static int ReadRequest(TestMethodCfg_IPerl cfgIPerl, IperlHead iperlHead, MessageID messageID, int offset, int length, out byte[] buffer)
internal static int ReadRequest(TestMethodCfg cfg, IperlHead iperlHead, MessageID messageID, int offset, int length, out byte[] buffer)
{
for (int i = 0; i < cfgIPerl.MaxCommRetries; i++)
for (int i = 0; i < cfg.MaxCommRetries; i++)
{
rfidDataLogger.Error($"{iperlHead.CommInterface} Start {i} reading: COM{iperlHead.RfidComPortNr}: ReadRequestPort ({messageID},{offset}, {length}... timeout {cfgIPerl.CommTimeout})");
rfidDataLogger.Error($"{iperlHead.CommInterface} Start {i} reading: COM{iperlHead.RfidComPortNr}: ReadRequestPort ({messageID},{offset}, {length}... timeout {cfg.CommTimeout})");
using (RfidLogic writer = new RfidLogic($"COM{iperlHead.RfidComPortNr}"))
{
try
{
byte[] response = writer.ReadRequest((byte)messageID, offset, length, cfgIPerl.CommTimeout);
byte[] response = writer.ReadRequest((byte)messageID, offset, length, cfg.CommTimeout);
string hexString = RfidHelper.ConvertByteArrayToHexString(response);
string swapHexString = RfidHelper.SwapHexcode(hexString);
string decString = RfidHelper.HexLiteral2Unsigned(RfidHelper.SwapHexcode(hexString)).ToString();
@@ -57,42 +56,42 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
if (/*RfidHelper.IsPassThrough(messageID)*/ messageID == MessageID.ASICRegisterReadTest || messageID == MessageID.RadioPassthrough)
{
rfidDataLogger.Info($"COM{iperlHead.RfidComPortNr}: ReadRequestPort ({messageID},...) {(i > 0 ? "<- Error: Invalid Pass-Through data." : "<- Info: Wait for Pass-Through data.")}");
Thread.Sleep(cfgIPerl.PassThroughWaitTime);
Thread.Sleep(cfg.PassThroughWaitTime);
}
else
{
Thread.Sleep(cfgIPerl.WaitTimeAfterFailure);
Thread.Sleep(cfg.WaitTimeAfterFailure);
}
}
catch (RfidValidationException)
{
Thread.Sleep(cfgIPerl.WaitTimeAfterFailure);
Thread.Sleep(cfg.WaitTimeAfterFailure);
}
catch (Exception ex)
{
rfidDataLogger.Error($"COM{iperlHead.RfidComPortNr}: ReadRequestPort ({messageID},...) {ex.Message}");
Thread.Sleep(cfgIPerl.WaitTimeAfterFailure);
Thread.Sleep(cfg.WaitTimeAfterFailure);
}
}
}
rfidDataLogger.Error($"{iperlHead.CommInterface} reading failed after {cfgIPerl.MaxCommRetries} retries: COM{iperlHead.RfidComPortNr}: ReadRequestPort ({messageID},...)");
log.Error($"{iperlHead.CommInterface} reading failed after {cfgIPerl.MaxCommRetries} retries: COM{iperlHead.RfidComPortNr}: ReadRequestPort ({messageID},...)");
rfidDataLogger.Error($"{iperlHead.CommInterface} reading failed after {cfg.MaxCommRetries} retries: COM{iperlHead.RfidComPortNr}: ReadRequestPort ({messageID},...)");
log.Error($"{iperlHead.CommInterface} reading failed after {cfg.MaxCommRetries} retries: COM{iperlHead.RfidComPortNr}: ReadRequestPort ({messageID},...)");
buffer = new byte[length];
return 3;
}
internal static int WriteRequest(TestMethodCfg_IPerl cfgIPerl, IperlHead iperlHead, MessageID messageID, int offset, int length, byte[] buffer)
internal static int WriteRequest(TestMethodCfg cfg, IperlHead iperlHead, MessageID messageID, int offset, int length, byte[] buffer)
{
RfidLogic writer = new RfidLogic($"COM{iperlHead.RfidComPortNr}");
string payload = RfidHelper.ConvertByteArrayToHexString(buffer);
for (var i = 0; i < cfgIPerl.MaxCommRetries; i++)
for (var i = 0; i < cfg.MaxCommRetries; i++)
{
rfidDataLogger.Error($"{iperlHead.CommInterface} Start {i} writting: COM{iperlHead.RfidComPortNr}: WriteRequest ({messageID},{offset}, {length}, {payload}... timeout {cfgIPerl.CommTimeout})");
rfidDataLogger.Error($"{iperlHead.CommInterface} Start {i} writting: COM{iperlHead.RfidComPortNr}: WriteRequest ({messageID},{offset}, {length}, {payload}... timeout {cfg.CommTimeout})");
try
{
if (writer.ClosePort()) writer.OpenPort();
writer.WriteRequest((byte)messageID, offset, length, buffer, cfgIPerl.CommTimeout, false);
writer.WriteRequest((byte)messageID, offset, length, buffer, cfg.CommTimeout, false);
writer.ClosePort();
rfidDataLogger.InfoFormat($"{iperlHead.Name}({iperlHead.SerialNr},COM{iperlHead.RfidComPortNr}): WriteRequestPort({messageID}, {offset}, {length}, {payload})");
return 0;
@@ -106,7 +105,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
else
{
rfidDataLogger.Error($"COM{iperlHead.RfidComPortNr}: Error: {ex.Message}");
Thread.Sleep(cfgIPerl.WaitTimeAfterFailure);
Thread.Sleep(cfg.WaitTimeAfterFailure);
if (i > 1)
{
rfidDataLogger.Error($"COM{iperlHead.RfidComPortNr}: Reopen the com port.");
@@ -116,8 +115,8 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
}
}
writer.ClosePort();
rfidDataLogger.Error($"RFID writing failed after {cfgIPerl.MaxCommRetries} retries: COM{iperlHead.RfidComPortNr}: WriteRequestPort ({messageID},...) {System.Text.Encoding.UTF8.GetString(buffer)}");
log.Error($"RFID writing failed after {cfgIPerl.MaxCommRetries} retries: COM{iperlHead.RfidComPortNr}: WriteRequestPort ({messageID},...) {System.Text.Encoding.UTF8.GetString(buffer)}");
rfidDataLogger.Error($"RFID writing failed after {cfg.MaxCommRetries} retries: COM{iperlHead.RfidComPortNr}: WriteRequestPort ({messageID},...) {System.Text.Encoding.UTF8.GetString(buffer)}");
log.Error($"RFID writing failed after {cfg.MaxCommRetries} retries: COM{iperlHead.RfidComPortNr}: WriteRequestPort ({messageID},...) {System.Text.Encoding.UTF8.GetString(buffer)}");
return 2;
}
}
@@ -1,5 +1,8 @@
using System;
using System.IO;
using System.Linq;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
@@ -7,11 +10,34 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
const int Q2CorrFactorsAddr = 0x1878;
internal static int ReadRequest(TestMethodCfg_IPerl cfgIPerl, IperlHead iperlHead, MessageID messageID, int offset, int length, out byte[] buffer)
internal static int ReadRequest(TestMethodCfg cfg, IperlHead iperlHead, MessageID messageID, int offset, int length, out byte[] buffer)
{
byte[] configurationBuffer = new byte[ConfigStruct.Length] { 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
string pcbStr = iperlHead.RfidComPortNr.ToString().PadRight(10,'0') + iperlHead.Position.ToString("D2");
long decVal = Convert.ToInt64(pcbStr);
string nHexStr = decVal.ToString("X4");
ConfigStruct configStruct = new ConfigStruct();
configStruct.PCBNumberString = nHexStr;
configStruct.StatusMode = ProtocolStatuses.Active;
configStruct.OpthoStatusMode = DiagnosticLedState.State4;
configStruct.Version = "Good Version: 123456";
byte[] configurationBuffer;
using (var ms = new MemoryStream())
using (var writer = new BinaryWriter(ms))
{
configStruct.WriteBinary(writer);
writer.Flush();
configurationBuffer = ms.ToArray(); // ← this is the binary output
}
byte[] calibrationBuffer = new byte[CalibrationStructV4.Length] { 3, 0, 150, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 150, 10 };
buffer = new byte[length];
return 0;//switch off
if (messageID == MessageID.Configuration)
{
@@ -34,7 +60,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
return iperlHead.Name.Equals("iPerl13") ? 2 : 0; /// Simulates an error on position 13
}
internal static int WriteRequest(TestMethodCfg_IPerl cfgIPerl, IperlHead iperlHead, MessageID messageID, int offset, int length, byte[] buffer)
internal static int WriteRequest(TestMethodCfg cfg, IperlHead iperlHead, MessageID messageID, int offset, int length, byte[] buffer)
{
return 0;
}