Compare commits

...
59 changed files with 4119 additions and 374 deletions
@@ -3,8 +3,6 @@ using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using log4net;
using TBF.Rig.Output.Printers.Label;
using TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Utils;
using CliRunner = TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Utils.CliRunnerOld;
using OptoHeadStatus = TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols.OptoHeadStatus;
using SerialPortData = TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Utils.SerialPortData;
@@ -1,5 +1,5 @@
using System;
using NfcC7_DLL.NfcHandler.Protocols;
namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Protocols
{
@@ -7,7 +7,7 @@ using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NfcC7_DLL.NfcHandler.Utils;
namespace TBF.Rig.RegisterReaders.PoseidonReader.communication.C7.Utils
{
@@ -0,0 +1,15 @@
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol
{
public static class Constants
{
public const byte Start = 0x53; //'S'
public const byte Write = 0x57; // 'W'
public const byte Read = 0x52; // 'R'
public const byte End = 0x0D; //'.'
public const byte Question = (byte)0x3F; // '?'
public static readonly byte[] Version = {0x76, 0x65, 0x72, 0x73 }; // 'v' 'e' 'r' 's'
public const byte StatusOk = 0x01;
public const byte StatusNok = 0x00;
}
}
@@ -0,0 +1,25 @@
using System;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol
{
public sealed class IperlHatFrame
{
public byte Start { get; }
public byte Direction { get; }
public byte End { get; }
public byte Length { get; }
public byte[] CommandInformation { get; }
public byte[] Payload { get; }
public IperlHatFrame(byte start, byte direction, byte length, byte[] commandBytes, byte[] payload, byte end)
{
Start = start;
Direction = direction;
Length = length;
CommandInformation = commandBytes ?? Array.Empty<byte>();
Payload = payload ?? Array.Empty<byte>();
End = end;
}
}
}
@@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.protocolCommons;
using TBF.Rig.RegisterReaders.PoseidonReader.communication.C7;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol
{
public sealed class IperlHatFrameBuilder
{
private byte _direction;
private readonly List<byte> _commandBytes = new List<byte>();
private readonly List<byte> _payload = new List<byte>();
public IperlHatFrameBuilder RequestResponse(bool enabled)
{
_direction = enabled ? Constants.Write : Constants.Read;
return this;
}
public IperlHatFrameBuilder AddCommand(ProtocolCommand command)
{
_commandBytes.Add((byte)command);
return this;
}
public IperlHatFrameBuilder AddSubCommand(ProtocolCommand subCommand)
{
if (_commandBytes.Count == 0 ||
_commandBytes[0] != (byte)ProtocolCommand.DeviceSpecific)
throw new InvalidOperationException(
"Sub-command is only valid for DeviceSpecific (0xFD) commands.");
_commandBytes.Add((byte)subCommand);
return this;
}
public IperlHatFrameBuilder AddSubCommand(ProtocolStatuses subCommand)
{
if (_commandBytes.Count == 0 ||
_commandBytes[0] != (byte)ProtocolCommand.SetState)
throw new InvalidOperationException(
"Sub-command is only valid for SetState (0xA1) commands.");
_commandBytes.Add((byte)subCommand);
return this;
}
public IperlHatFrameBuilder AddDeviceCommand(
ProtocolDeviceSubCommand subCommand)
{
_commandBytes.Add((byte)ProtocolCommand.DeviceSpecific);
_commandBytes.Add((byte)subCommand);
return this;
}
public IperlHatFrameBuilder SetVersionCommand()
{
_commandBytes.Add((byte)ProtocolCommand.Question);
_payload.AddRange(Constants.Version);
return this;
}
public IperlHatFrameBuilder AddPayload(byte[] payload)
{
if (payload != null)
_payload.AddRange(payload);
return this;
}
public IperlHatFrameBuilder AddPayload(DiagnosticLedState state)
{
_payload.Add((byte)state);
return this;
}
public IperlHatFrameBuilder AddPayload(byte payload)
{
_payload.Add(payload);
return this;
}
public IperlHatFrameBuilder AddDiagnosticLedState(DiagnosticLedState state)
{
RequestResponse(true);
AddDeviceCommand(ProtocolDeviceSubCommand.SetDiagnosticLEDState);
AddPayload((byte)state);
return this;
}
public IperlHatFrameBuilder AddNullTerminatedAscii(string text)
{
if (!string.IsNullOrEmpty(text))
_commandBytes.AddRange(
System.Text.Encoding.ASCII.GetBytes(text));
_commandBytes.Add(0x00);
return this;
}
public IperlHatFrame BuildFrame()
{
if (_commandBytes.Count == 0)
throw new InvalidOperationException("No command specified.");
byte length = (byte)(4 + _commandBytes.Count + _payload.Count); // 4 = START + dirrection + LEN + END
return new IperlHatFrame(
Constants.Start,
_direction,
length,
_commandBytes.ToArray(),
_payload.ToArray(),
Constants.End);
}
public byte[] BuildBytes()
{
IperlHatFrame frame = BuildFrame();
if (frame.CommandInformation.Length > 0 && frame.CommandInformation[0] == Constants.Question)
{
var bytes = new List<byte>
{
frame.Start,
frame.Direction,
};
bytes.AddRange(frame.CommandInformation);
bytes.AddRange(frame.Payload);
bytes.Add(frame.End);
return bytes.ToArray();
}
else
{
var bytes = new List<byte>
{
frame.Start,
frame.Direction,
frame.Length,
};
bytes.AddRange(frame.CommandInformation);
bytes.AddRange(frame.Payload);
bytes.Add(frame.End);
return bytes.ToArray();
}
}
}
}
@@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol
{
public sealed class IperlHatFrameParser
{
public IperlHatResponse Parse(byte[] data)
{
if (data == null)
throw new ArgumentNullException(nameof(data));
if (data.Length < 5)
throw new FormatException("Frame too short.");
if (data[0] != Constants.Start)
{
//if version parse version
if (data[0] == Constants.Question)
{
//Define Question answer
var prefix = new List<byte>{ Constants.Question };
var end = new List<byte>{ Constants.End };
if (IsPrefixValid(data, prefix, end))
{
//whole payload may be like "vers: Harry T:B800, V:06.06.01, FW:190215, 7ECE, B1.6.01, HW:4, Serial:0"
prefix = new List<byte>{ Constants.Question };
byte[] payloadVersion = ExtractPayloadUsePrefix(data, prefix, end);
return new IperlHatResponse(Constants.Question, payloadVersion.Length > 0 ? Constants.StatusOk : Constants.StatusNok, payloadVersion);
}
}
throw new FormatException("Invalid START byte.");
}
if (data[1] != Constants.Read)
throw new FormatException("Frame is no Response.");
byte length = data[2];
if (length != data.Length)
throw new FormatException("Length mismatch.");
byte direction = data[1];
byte status = data[3];
var prefixCommand = new List<byte>{ Constants.Start,direction,length,status };
var endCommand = new List<byte>{ Constants.End };
byte[] payload = ExtractPayloadUsePrefix(data,prefixCommand,endCommand);
return new IperlHatResponse(0x00, status, payload);
}
private static byte[] ExtractPayloadUsePrefix(byte[] data, List<byte> prefix, List<byte> end)
{
// payload exists only if frame longer than:
// START + DIRECTION + LEN + CTRL + END = 5 bytes
// OR VERSION_START + VERSION = 5 bytes
if (data.Length <= 5)
return Array.Empty<byte>();
//check prefix is equal
int prefixLength = prefix.Count;
byte[] commandPrefix = new byte[prefixLength];
Buffer.BlockCopy(data, 0, commandPrefix, 0, prefixLength);
if (StartsWithPrefix(end, commandPrefix))
{
return Array.Empty<byte>();
}
int payloadLength = data.Length - (prefix.Count + end.Count);
byte[] payload = new byte[payloadLength];
Buffer.BlockCopy(data, prefix.Count, payload, 0, payloadLength);
return payload;
}
private static bool IsPrefixValid(byte[] data, List<byte> prefix, List<byte> end)
{
int prefixLength = prefix.Count;
// payload exists only if frame longer than:
// OR VERSION_START + VERSION = 5 bytes - "?VERS" version implemented
if (data.Length <= prefixLength) // need be and on END
return false;
//check prefix is equal
byte[] commandPrefix = new byte[prefixLength];
Buffer.BlockCopy(data, 0, commandPrefix, 0, prefixLength);
if (StartsWithPrefix(end, commandPrefix))
{
return false;
}
return true;
}
private static bool StartsWithPrefix(List<byte> data, byte[] prefix)
{
if (data.Count < prefix.Length)
return false;
for (int i = 0; i < prefix.Length; i++)
{
if (data[i] != prefix[i])
return false;
}
return true;
}
private static byte[] ExtractVersionPayload(byte[] data)
{
// payload exists only if frame longer than:
// START + LEN + CTRL + STATUS + CHK_HI + CHK_LO = 6 bytes
if (data.Length <= 5)
return Array.Empty<byte>();
int payloadLength = data.Length - 4;
byte[] payload = new byte[payloadLength];
Buffer.BlockCopy(data, 5, payload, 0, payloadLength);
return payload;
}
}
}
@@ -0,0 +1,10 @@
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol
{
public static class IperlHatProtocol
{
public const byte START = 0x0D;
// Control bits (CNTRL1)
public const byte RESPONSE_FLAG = 0x08; // RF
}
}
@@ -0,0 +1,35 @@
using System;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol
{
public sealed class IperlHatResponse
{
public byte Control { get; } //classic control byte - valid for question now
private byte Status { get; }
public byte[] Payload { get; }
public bool IsOk => Status == Constants.StatusOk;
public IperlHatResponse(byte control, byte status, byte[] payload)
{
Control = control;
Status = status;
Payload = payload ?? Array.Empty<byte>();
}
public string GetAsciiPayload()
{
if (Payload.Length == 0)
return null;
int length = Array.IndexOf(Payload, (byte)0x00);
if (length < 0)
length = Payload.Length;
return System.Text.Encoding.ASCII.GetString(Payload, 0, length);
}
}
}
@@ -1,105 +0,0 @@
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4
{
/// <summary>
/// Device-specific TouchRead sub-commands.
/// These commands are used with the DeviceSpecific (0xFD) command.
/// </summary>
public enum TouchReadDeviceSubCommand : byte
{
// ---- Alarm / Mask -----------------------------------------
/// <summary>
/// View Alarm Mask (lower 16 bits).
/// Returns a 2-byte little-endian alarm bit field.
/// </summary>
ViewAlarmMask = 0x31,
/// <summary>
/// Set Alarm Mask (lower 16 bits).
/// Payload: 2-byte little-endian alarm bit field.
/// </summary>
SetAlarmMask = 0x32,
/// <summary>
/// View alarm persistence period (days).
/// Range: 890 days.
/// </summary>
ViewPersistence = 0x33,
/// <summary>
/// Set alarm persistence period (days).
/// Range: 890 days.
/// </summary>
SetPersistence = 0x34,
/// <summary>
/// View leak duration (hours).
/// Range: 24180 hours.
/// </summary>
ViewLeakDuration = 0x35,
/// <summary>
/// Set leak duration (hours).
/// Range: 24180 hours.
/// </summary>
SetLeakDuration = 0x36,
ViewAlarms = 0x37,
SetAlarms = 0x38,
/// <summary>
/// View system time.
/// Returns uint32 seconds since 2000-01-01.
/// </summary>
ViewSystemTime = 0x10,
/// <summary>
/// Set system time.
/// Payload: uint32 seconds since 2000-01-01.
/// If zero, device will reset and erase data.
/// Protected by meter seal.
/// </summary>
SetSystemTime = 0x11,
// ---- Manufacture / Time -----------------------------------
ViewManufactureDate = 0x39,
SetManufactureDate = 0x3A,
ViewSecondsIdle = 0x3B,
ViewSecondsActive = 0x3D,
ViewSecondsUsed = 0x3F,
// ---- Snapshot / Logging -----------------------------------
ViewSnapshotData = 0x41,
ViewDatalogDuration = 0x43,
SetDatalogDuration = 0x44,
ReadDatalog = 0x45,
ClearDatalog = 0x46,
// ---- History ----------------------------------------------
ViewHistoryMask = 0x47,
SetHistoryMask = 0x48,
ReadHistory = 0x49,
ClearHistory = 0x4A,
// ---- Diagnostics ------------------------------------------
ViewDiagnostics = 0x4B,
ResetDiagnostics = 0x4C,
// ---- Calibration / Build ----------------------------------
ViewCalibration = 0x53,
SetCalibration = 0x54,
ViewIPerlBuild = 0x65,
SetIPerlBuild = 0x66,
// ---- Bootloader (dangerous!) ------------------------------
EnterBootloader = 0x81,
ReadFlash = 0x82,
EraseAll = 0x83,
EraseSegment = 0x84,
UpdateCode = 0x85,
ExitBootloader = 0x86
}
}
@@ -13,12 +13,12 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed
_state = state;
}
public DiagnosticLedData ParseLine(string line)
public DiagnosticLedData ParseLine(string line, bool checkLineTermination = true)
{
if (string.IsNullOrEmpty(line))
throw new ArgumentNullException(nameof(line));
if (!line.EndsWith("\r\n"))
if (checkLineTermination && !line.EndsWith("\r\n"))
throw new FormatException("Invalid diagnostic LED line termination");
string trimmed = line.TrimEnd('\r', '\n');
@@ -1,13 +1,96 @@
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.protocolCommons;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.wiredProtocol;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed
{
/// <summary>
/// Diagnostic LED output mode.
/// <para>
/// Determines the format and content of high-speed serial diagnostic data
/// emitted by the meter when the diagnostic LED is enabled.
/// </para>
/// <para>
/// Each state corresponds to a specific TAB-separated ASCII HEX frame layout
/// as defined in the iPERL TouchRead protocol documentation.
/// </para>
/// <para>
/// See <see cref="ProtocolDeviceSubCommand.SetDiagnosticLEDState"/>
/// diagnostic LED States.
/// </para>
/// </summary>
public enum DiagnosticLedState : byte
{
/// <summary>
/// Diagnostic LED OFF - State #0.
/// <para>
/// Basic diagnostic output containing raw ADC, field strength,
/// flow rate, volume accumulator, and capacitor voltage.
/// </para>
/// </summary>
StateOFF = 0x00,
/// <summary>
/// Diagnostic LED State #1.
/// <para>
/// Basic diagnostic output containing raw ADC, field strength,
/// flow rate, volume accumulator, and capacitor voltage.
/// </para>
/// </summary>
State1 = 0x01,
/// <summary>
/// Diagnostic LED State #2.
/// <para>
/// Extends State #1 with LCD volume, meter state,
/// and low-flow cutoff indication.
/// </para>
/// </summary>
State2 = 0x02,
/// <summary>
/// Diagnostic LED State #3.
/// <para>
/// Extends State #1 with field calibration value,
/// ASIC timestamp, and field drive time.
/// </para>
/// </summary>
State3 = 0x03,
/// <summary>
/// Diagnostic LED State #4.
/// <para>
/// Extended diagnostic output including mean flow rate,
/// field measurements, integrator calibration values,
/// and ASIC state.
/// </para>
/// </summary>
State4 = 0x04,
/// <summary>
/// Diagnostic LED State #5.
/// <para>
/// Extends State #4 with water impedance measurement.
/// </para>
/// </summary>
State5 = 0x05,
/// <summary>
/// Diagnostic LED State #6.
/// <para>
/// Extends State #5 with electrode delta, spike detection data,
/// pipe status, LCD volume, and additional ASIC state.
/// </para>
/// </summary>
State6 = 0x06,
/// <summary>
/// Diagnostic LED State #7.
/// <para>
/// Extends State #6 with raw ADC before offset correction,
/// detrended ADC value, imaginary water impedance,
/// electrode voltage noise, and ADC offset learning status.
/// </para>
/// </summary>
State7 = 0x07
}
}
}
@@ -39,6 +39,9 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed
/// </summary>
public abstract class DiagnosticLedData
{
public abstract int GetByteCount();
/// <summary>
/// Raw diagnostic LED line exactly as received from the meter,
/// including checksum and CRLF.
@@ -0,0 +1,37 @@
using System;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed.parserer
{
public static class DiagnosticLedFrameSpec
{
public static int GetExpectedAsciiLength(DiagnosticLedState state)
{
switch (state)
{
case DiagnosticLedState.State1: return 33;
case DiagnosticLedState.State2: return 48;
case DiagnosticLedState.State3: return 50;
case DiagnosticLedState.State4: return 84;
case DiagnosticLedState.State5: return 89;
case DiagnosticLedState.State6: return 112;
case DiagnosticLedState.State7: return 139;
default: throw new ArgumentOutOfRangeException(nameof(state));
}
}
public static int GetExpectedFieldCount(DiagnosticLedState state)
{
switch (state)
{
case DiagnosticLedState.State1: return 6;
case DiagnosticLedState.State2: return 9;
case DiagnosticLedState.State3: return 9;
case DiagnosticLedState.State4: return 15;
case DiagnosticLedState.State5: return 16;
case DiagnosticLedState.State6: return 21;
case DiagnosticLedState.State7: return 26;
default: throw new ArgumentOutOfRangeException(nameof(state));
}
}
}
}
@@ -36,5 +36,23 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed
RawVolume = DiagnosticHex.ParseUInt24(f[3]);
CapacitorMv = DiagnosticHex.ParseUInt16(f[4]);
}
public override string ToString()
{
return $"DiagnosticLedState1Data: Adc24={Adc24}, FieldStrength={FieldStrength}, RawFlow={RawFlow}, RawVolume={RawVolume}, CapacitorMv={CapacitorMv}";
}
/// <summary>
/// Format: xxxxxx aaaa yyyy vvvvvv cccc ss
/// Chars total = 26
/// Tabs = 5
/// CRLF = 2
/// Total bytes = 33
/// </summary>
/// <returns> Total bytes</returns>
public override int GetByteCount()
{
return 33;
}
}
}
@@ -67,6 +67,24 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed
MeterState = DiagnosticHex.ParseByte(fields[6]);
IsLowFlowCutoff = DiagnosticHex.ParseByte(fields[7]) != 0;
}
public override string ToString()
{
return $"DiagnosticLedState2Data: Adc24={Adc24}, FieldStrength={FieldStrength}, RawFlow={RawFlow}, RawVolume={RawVolume}, CapacitorMv={CapacitorMv}, LcdVolume={LcdVolume}, MeterState={MeterState}, IsLowFlowCutoff={IsLowFlowCutoff}";
}
/// <summary>
/// Format: xxxxxx aaaa yyyy vvvvvv cccc gggggggg mm ff ss
/// Chars total = 38
/// Tabs = 8
/// CRLF = 2
/// Total bytes = 48
/// </summary>
/// <returns> Total bytes</returns>
public override int GetByteCount()
{
return 48;
}
}
}
@@ -67,5 +67,23 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed
AsicTimestamp = DiagnosticHex.ParseUInt32(fields[6]);
FieldDriveTimeUs = DiagnosticHex.ParseByte(fields[7]);
}
public override string ToString()
{
return $"DiagnosticLedState3Data: Adc24={Adc24}, FieldStrength={FieldStrength}, RawFlow={RawFlow}, RawVolume={RawVolume}, CapacitorMv={CapacitorMv}, FieldCalibration={FieldCalibration}, AsicTimestamp={AsicTimestamp}, FieldDriveTimeUs={FieldDriveTimeUs}";
}
/// <summary>
/// Format: xxxxxx aaaa yyyy vvvvvv cccc tttt bbbbbbbb ff ss
/// Chars total = 40
/// Tabs = 8
/// CRLF = 2
/// Total bytes = 50
/// </summary>
/// <returns> Total bytes</returns>
public override int GetByteCount()
{
return 50;
}
}
}
@@ -68,6 +68,24 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed
AsicState = DiagnosticHex.ParseByte(fields[13]);
}
public override string ToString()
{
return $"DiagnosticLedState4Data: Adc24={Adc24}, FieldStrength={FieldStrength}, RawFlow={RawFlow}, RawVolume={RawVolume}, CapacitorMv={CapacitorMv}, FieldCalibration={FieldCalibration}, AsicTimestamp={AsicTimestamp}, FieldDriveTimeUs={FieldDriveTimeUs}, MeanFlowRate={MeanFlowRate}, Field1Measurement={Field1Measurement}, Field2Measurement={Field2Measurement}, IntegratorCalibrationPositive={IntegratorCalibrationPositive}, IntegratorCalibrationNegative={IntegratorCalibrationNegative}, AsicState={AsicState}";
}
/// <summary>
/// Format: xxxxxx aaaa yyyy vvvvvv cccc tttt bbbbbbbb mmmmmmmm ff gggg hhhh cccc nnnn qq ss
/// Chars total = 68
/// Tabs = 14
/// CRLF = 2
/// Total bytes = 84
/// </summary>
/// <returns> Total bytes</returns>
public override int GetByteCount()
{
return 84;
}
}
}
@@ -88,6 +88,24 @@ public sealed class DiagnosticLedState5Data : DiagnosticLedData
AsicState = DiagnosticHex.ParseByte(fields[13]);
WaterImpedance = DiagnosticHex.ParseInt16(fields[14]);
}
public override string ToString()
{
return $"DiagnosticLedState5Data: Adc24={Adc24}, FieldStrength={FieldStrength}, RawFlow={RawFlow}, RawVolume={RawVolume}, CapacitorMv={CapacitorMv}, FieldCalibration={FieldCalibration}, AsicTimestamp={AsicTimestamp}, FieldDriveTimeUs={FieldDriveTimeUs}, MeanFlowRate={MeanFlowRate}, Field1Measurement={Field1Measurement}, Field2Measurement={Field2Measurement}, IntegratorCalibrationPositive={IntegratorCalibrationPositive}, IntegratorCalibrationNegative={IntegratorCalibrationNegative}, AsicState={AsicState}, WaterImpedance={WaterImpedance}";
}
/// <summary>
/// Format: xxxxxx aaaa yyyy vvvvvv cccc tttt bbbbbbbb mmmmmmmm ff iiii ss
/// Chars total = 72
/// Tabs = 15
/// CRLF = 2
/// Total bytes = 89
/// </summary>
/// <returns> Total bytes</returns>
public override int GetByteCount()
{
return 89;
}
}
}
@@ -129,6 +129,24 @@ public sealed class DiagnosticLedState6Data : DiagnosticLedData
return (SpikeDetectionStatus)SpikeDetection;
}
}
public override string ToString()
{
return $"DiagnosticLedState6Data: Adc24={Adc24}, FieldStrength={FieldStrength}, RawFlow={RawFlow}, RawVolume={RawVolume}, CapacitorMv={CapacitorMv}, FieldCalibration={FieldCalibration}, AsicTimestamp={AsicTimestamp}, FieldDriveTimeUs={FieldDriveTimeUs}, MeanFlowRate={MeanFlowRate}, Field1Measurement={Field1Measurement}, Field2Measurement={Field2Measurement}, IntegratorCalibrationPositive={IntegratorCalibrationPositive}, IntegratorCalibrationNegative={IntegratorCalibrationNegative}, AsicState0={AsicState0}, WaterImpedance={WaterImpedance}, SpikeDetection={SpikeDetection}, PipeStatus={PipeStatus}, LcdVolume={LcdVolume}, AsicState1={AsicState1}";
}
/// <summary>
/// Format: xxxxxx aaaa yyyy vvvvvv cccc tttt bbbbbbbb mmmmmmmm ff iiii rrrr pp ll dddddddd oo ss
/// Chars total = 90
/// Tabs = 20
/// CRLF = 2
/// Total bytes = 112
/// </summary>
/// <returns> Total bytes</returns>
public override int GetByteCount()
{
return 112;
}
}
}
@@ -132,6 +132,24 @@ public sealed class DiagnosticLedState7Data : DiagnosticLedData
ElectrodeVoltageNoise = DiagnosticHex.ParseUInt16(fields[23]);
AdcOffsetLearningStatus = DiagnosticHex.ParseByte(fields[24]);
}
public override string ToString()
{
return $"DiagnosticLedState7Data: Adc24={Adc24}, FieldStrength={FieldStrength}, RawFlow={RawFlow}, RawVolume={RawVolume}, CapacitorMv={CapacitorMv}, FieldCalibration={FieldCalibration}, AsicTimestamp={AsicTimestamp}, FieldDriveTimeUs={FieldDriveTimeUs}, MeanFlowRate={MeanFlowRate}, Field1Measurement={Field1Measurement}, Field2Measurement={Field2Measurement}, IntegratorCalibrationPositive={IntegratorCalibrationPositive}, IntegratorCalibrationNegative={IntegratorCalibrationNegative}, AsicState0={AsicState0}, WaterImpedance={WaterImpedance}, ElectrodeDeltaMv={ElectrodeDeltaMv}, SpikeDetection={SpikeDetection}, PipeStatus={PipeStatus}, LcdVolume={LcdVolume}, AsicState1={AsicState1}, RawAdcBeforeOffset={RawAdcBeforeOffset}, DetrendedAdc={DetrendedAdc}, ImaginaryWaterImpedance={ImaginaryWaterImpedance}, ElectrodeVoltageNoise={ElectrodeVoltageNoise}, AdcOffsetLearningStatus={AdcOffsetLearningStatus}";
}
/// <summary>
/// Format:
/// Chars total = 112
/// Tabs = 25
/// CRLF = 2
/// Total bytes = 139
/// </summary>
/// <returns> Total bytes</returns>
public override int GetByteCount()
{
return 139;
}
}
}
@@ -1,3 +1,8 @@
using System;
using System.Globalization;
using System.Linq;
using System.Text;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger
{
public static class HexFormatter
@@ -10,6 +15,21 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger
{
return "0x" + value.ToString("X2");
}
/// <summary>
/// int to byte securely
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static byte ToHexByte(int value)
{
if (value < 0 || value > 255)
throw new ArgumentOutOfRangeException(nameof(value),
"Value must be between 0 and 255.");
return (byte)value;
}
/// <summary>
/// Formats a byte array as 0xNN 0xNN ...
@@ -54,6 +74,101 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger
return sb.ToString();
}
public static string ToHexWithAscii(byte value)
{
char c = (value >= 32 && value <= 126) ? (char)value : '.';
return $"0x{value:X2} ('{c}')";
}
public static string ToSerialHexWithAscii(byte[] data)
{
if (data == null || data.Length == 0)
return string.Empty;
var hex = new StringBuilder(data.Length * 3);
var ascii = new StringBuilder(data.Length);
foreach (byte b in data)
{
hex.Append(b.ToString("X2")).Append(' ');
// Printable ASCII range
if (b >= 32 && b <= 126)
{
ascii.Append((char)b);
}
// Binary numbers 09 -> show digit
else if (b <= 9)
{
ascii.Append((char)('0' + b));
}
else
{
ascii.Append('.');
}
}
// remove last trailing space in hex
if (hex.Length > 0)
hex.Length--;
return $"{hex} | {ascii}";
}
public static string ToHex(int value)
{
return $"0x{(byte)value:X2}";
}
public static byte[] IntToBytesBE(int value, int byteCount)
{
var result = new byte[byteCount];
for (int i = 0; i < byteCount; i++)
result[byteCount - 1 - i] = (byte)(value >> (8 * i));
return result;
}
public static byte[] IntToBytesLE(int value, int byteCount)
{
var result = new byte[byteCount];
for (int i = 0; i < byteCount; i++)
result[i] = (byte)(value >> (8 * i));
return result;
}
public static byte[] AsciiToBytes(string text)
{
return string.IsNullOrEmpty(text)
? Array.Empty<byte>()
: System.Text.Encoding.ASCII.GetBytes(text);
}
/// <summary>
/// Converts a hex string to a byte array.
/// Like: string hex = "3F 76 65 72 73 3A 20 48 61 72 72 79 20 54 3A 42 38 30 30 2C 20";
/// </summary>
/// <param name="hex"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static byte[] HexStringToByteArray(string hex)
{
if (hex == null)
throw new ArgumentNullException(nameof(hex));
return hex
.Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(b => byte.Parse(b, NumberStyles.HexNumber, CultureInfo.InvariantCulture))
.ToArray();
}
}
}
@@ -0,0 +1,21 @@
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger
{
public class IpelHatCommandDecoder
{
public static string DescribeCommand(byte command)
{
return "";
}
public static string DescribeDirection(byte direction)
{
if (direction == IperlHatProtocol.Constants.Write)
return "(WRITE - OUTGOING)";
if (direction == IperlHatProtocol.Constants.Read)
return "(READ - INCOMING)";
return "INVALID CONTROL BITS (unsupported pattern)";
}
}
}
@@ -0,0 +1,85 @@
using System;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.wiredProtocol;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger
{
public static class IperlHatLogger
{
public static string DescribeTx(byte[] frame)
{
if (frame == null || frame.Length < 5)
return "Invalid frame";
if (frame[2] == IperlHatProtocol.Constants.Question)
{
return
"TX Frame\n" +
$" START : {HexFormatter.ToHex(frame[0])}\n" +
$" DIRECTION : {HexFormatter.ToHex(frame[1])} ({IpelHatCommandDecoder.DescribeDirection(frame[1])})\n" +
$" COMMAND : {HexFormatter.ToHexWithAscii(frame[2])}\n" +
$" INFO : {HexFormatter.ToSerialHexWithAscii(GetInformatioQuestion(frame))}\n" +
$" END : {HexFormatter.ToHex(frame[frame.Length - 1])}\n" +
$" RAW : {HexFormatter.ToHex(frame)}";
}
else
{
return
"TX Frame\n" +
$" START : {HexFormatter.ToHex(frame[0])}\n" +
$" DIRECTION : {HexFormatter.ToHex(frame[1])} ({HexFormatter.ToHexWithAscii(frame[1])}) {IpelHatCommandDecoder.DescribeDirection(frame[1])}\n" +
$" LEN : {HexFormatter.ToHex(frame[2])} - {(int)frame[2]}\n" +
$" COMMAND : {HexFormatter.ToHexWithAscii(frame[3])}\n" +
$" INFO : {HexFormatter.ToSerialHexWithAscii(GetInformation(frame))}\n" +
$" END : {HexFormatter.ToHex(frame[frame.Length - 1])}\n" +
$" RAW : {HexFormatter.ToHex(frame)}";
}
}
//payload
private static byte[] GetInformation(byte[] frame)
{
int infoLength = frame.Length - 5; // START + DIRECTION + LEN + COMMAND + END
if (infoLength <= 0)
return Array.Empty<byte>();
var info = new byte[infoLength];
Buffer.BlockCopy(frame, 4, info, 0, infoLength);
return info;
}
//payload for question
private static byte[] GetInformatioQuestion(byte[] frame)
{
int infoLength = frame.Length - 4; // START + DIRECTION + COMMAND + END
if (infoLength <= 0)
return Array.Empty<byte>();
var info = new byte[infoLength];
Buffer.BlockCopy(frame, 3, info, 0, infoLength);
return info;
}
public static string DescribeRx(byte[] frame, TouchReadResponse response)
{
return
"RX Frame\n" +
$" START : {HexFormatter.ToHex(frame[0])}\n" +
$" LEN : {HexFormatter.ToHex(frame[1])} ({frame[1]})\n" +
$" CONTROL : {HexFormatter.ToHex(response.Control)}\n" +
$" STATUS : {HexFormatter.ToHex(response.Status)} ({DescribeStatus(response.Status)})\n" +
$" PAYLOAD : {HexFormatter.ToHex(response.Payload)}\n" +
$" RAW : {HexFormatter.ToHex(frame)}";
}
private static string DescribeStatus(byte status)
{
switch (status)
{
case 0x01: return "Command complete, no errors";
case 0x02: return "Unable to execute";
case 0x04: return "Unsupported control bits";
default: return "Unknown status";
}
}
}
}
@@ -1,4 +1,5 @@
using System;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.wiredProtocol;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger
{
@@ -1,11 +1,11 @@
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.protocolCommons
{
/// <summary>
/// Common iPERL TouchRead bidirectional commands.
/// These commands consist of a single-byte command code
/// placed in the Information field.
/// </summary>
public enum TouchReadCommand : byte
public enum ProtocolCommand : byte
{
/// <summary>
/// Simple (legacy) commands (e.g. View Factory ID = 0x01)
@@ -129,6 +129,12 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4
/// Device-specific command prefix.
/// Must be followed by a device sub-command byte.
/// </summary>
DeviceSpecific = 0xFD
DeviceSpecific = 0xFD,
/// <summary>
/// Question - specific switch to add additional payload request like "vers"
/// Mandatory add payload
/// </summary>
Question = 0x3F,
}
}
@@ -0,0 +1,201 @@
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.protocolCommons
{
/// <summary>
/// Device-specific TouchRead sub-commands.
/// These sub-commands are used together with the
/// <see cref="TouchReadCommand.DeviceSpecific"/> (0xFD) command.
/// </summary>
public enum ProtocolDeviceSubCommand : byte
{
// ==========================================================
// System / Time
// ==========================================================
/// <summary>
/// View system time.
/// Returns uint32 seconds since 2000-01-01 00:00:00.
/// </summary>
ViewSystemTime = 0x10,
/// <summary>
/// Set system time.
/// Payload: uint32 seconds since 2000-01-01.
/// If set to zero, the meter resets and erases data.
/// Protected by meter seal.
/// </summary>
SetSystemTime = 0x11,
// ==========================================================
// Alarm Mask / Alarm Configuration
// ==========================================================
/// <summary>View alarm mask (lower 16 bits).</summary>
ViewAlarmMask = 0x31,
/// <summary>Set alarm mask (lower 16 bits).</summary>
SetAlarmMask = 0x32,
/// <summary>View alarm persistence period (days).</summary>
ViewPersistence = 0x33,
/// <summary>Set alarm persistence period (days).</summary>
SetPersistence = 0x34,
/// <summary>View leak duration (hours).</summary>
ViewLeakDuration = 0x35,
/// <summary>Set leak duration (hours).</summary>
SetLeakDuration = 0x36,
/// <summary>View current alarm states.</summary>
ViewAlarms = 0x37,
/// <summary>Set alarm states (protected by meter seal).</summary>
SetAlarms = 0x38,
// ==========================================================
// Manufacture / Counters
// ==========================================================
/// <summary>View manufacture date.</summary>
ViewManufactureDate = 0x39,
/// <summary>Set manufacture date (protected by meter seal).</summary>
SetManufactureDate = 0x3A,
/// <summary>View seconds idle.</summary>
ViewSecondsIdle = 0x3B,
/// <summary>View seconds active.</summary>
ViewSecondsActive = 0x3D,
/// <summary>View seconds used.</summary>
ViewSecondsUsed = 0x3F,
// ==========================================================
// Snapshot / Datalog
// ==========================================================
/// <summary>View snapshot data.</summary>
ViewSnapshotData = 0x41,
/// <summary>View datalog duration.</summary>
ViewDatalogDuration = 0x43,
/// <summary>Set datalog duration.</summary>
SetDatalogDuration = 0x44,
/// <summary>Read datalog.</summary>
ReadDatalog = 0x45,
/// <summary>Clear datalog.</summary>
ClearDatalog = 0x46,
// ==========================================================
// History
// ==========================================================
/// <summary>View history mask.</summary>
ViewHistoryMask = 0x47,
/// <summary>Set history mask.</summary>
SetHistoryMask = 0x48,
/// <summary>Read history.</summary>
ReadHistory = 0x49,
/// <summary>Clear history.</summary>
ClearHistory = 0x4A,
// ==========================================================
// Diagnostics / Status
// ==========================================================
/// <summary>View diagnostics.</summary>
ViewDiagnostics = 0x4B,
/// <summary>Reset diagnostics.</summary>
ResetDiagnostics = 0x4C,
/// <summary>View status file.</summary>
ViewStatusFile = 0x4F,
/// <summary>Set status file (protected by meter seal).</summary>
SetStatusFile = 0x50,
// ==========================================================
// Calibration / Configuration
// ==========================================================
/// <summary>View calibration structure.</summary>
ViewCalibrationStructure = 0x51,
/// <summary>Set calibration structure (protected by meter seal).</summary>
SetCalibrationStructure = 0x52,
/// <summary>View calibration.</summary>
ViewCalibration = 0x53,
/// <summary>Set calibration (protected by meter seal).</summary>
SetCalibration = 0x54,
/// <summary>View reboot count.</summary>
ViewRebootCount = 0x55,
/// <summary>Set reboot count (protected by meter seal).</summary>
SetRebootCount = 0x56,
/// <summary>View temperature.</summary>
ViewTemperature = 0x57,
/// <summary>Set temperature (protected by meter seal).</summary>
SetTemperature = 0x58,
// ==========================================================
// Diagnostic LED / Hardware
// ==========================================================
/// <summary>
/// Set diagnostic LED state.
/// Enables or disables high-speed LED serial output.
/// <para>
/// See <see cref="TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed.DiagnosticLedState"/>
/// diagnostic LED output modes.
/// </para>
/// </summary>
SetDiagnosticLEDState = 0x60,
// ==========================================================
// Build / Firmware Info
// ==========================================================
/// <summary>View iPERL build information.</summary>
ViewIPerlBuild = 0x65,
/// <summary>Set iPERL build (protected by meter seal).</summary>
SetIPerlBuild = 0x66,
// ==========================================================
// Bootloader (DANGEROUS use with care)
// ==========================================================
/// <summary>Enter bootloader mode.</summary>
EnterBootloader = 0x81,
/// <summary>Read FLASH memory.</summary>
ReadFlash = 0x82,
/// <summary>Erase all FLASH memory.</summary>
EraseAll = 0x83,
/// <summary>Erase FLASH segment.</summary>
EraseSegment = 0x84,
/// <summary>Update firmware code.</summary>
UpdateCode = 0x85,
/// <summary>Exit bootloader mode.</summary>
ExitBootloader = 0x86
}
}
@@ -0,0 +1,10 @@
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.protocolCommons
{
public enum ProtocolStatuses : byte
{
Idle = 0x01,
Active = 0x02,
Inactive = 0x03,
}
}
@@ -1,6 +1,6 @@
using System;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.wiredProtocol
{
public sealed class TouchReadFrame
{
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.protocolCommons;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.wiredProtocol
{
public sealed class TouchReadFrameBuilder
{
@@ -15,16 +17,16 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4
return this;
}
public TouchReadFrameBuilder AddCommand(TouchReadCommand command)
public TouchReadFrameBuilder AddCommand(ProtocolCommand command)
{
_information.Add((byte)command);
return this;
}
public TouchReadFrameBuilder AddSubCommand(TouchReadDeviceSubCommand subCommand)
public TouchReadFrameBuilder AddSubCommand(ProtocolDeviceSubCommand subCommand)
{
if (_information.Count == 0 ||
_information[0] != (byte)TouchReadCommand.DeviceSpecific)
_information[0] != (byte)ProtocolCommand.DeviceSpecific)
throw new InvalidOperationException(
"Sub-command is only valid for DeviceSpecific (0xFD) commands.");
@@ -33,9 +35,9 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4
}
public TouchReadFrameBuilder AddDeviceCommand(
TouchReadDeviceSubCommand subCommand)
ProtocolDeviceSubCommand subCommand)
{
_information.Add((byte)TouchReadCommand.DeviceSpecific);
_information.Add((byte)ProtocolCommand.DeviceSpecific);
_information.Add((byte)subCommand);
return this;
}
@@ -47,6 +49,14 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4
return this;
}
public TouchReadFrameBuilder AddDiagnosticLedState(DiagnosticLedState state)
{
_information.Add((byte)ProtocolCommand.DeviceSpecific);
_information.Add((byte)ProtocolDeviceSubCommand.SetDiagnosticLEDState);
_information.Add((byte)state);
return this;
}
public TouchReadFrameBuilder AddNullTerminatedAscii(string text)
{
@@ -104,7 +114,7 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4
return bytes.ToArray();
}
private static ushort CalculateChecksum(IEnumerable<byte> data)
public static ushort CalculateChecksum(IEnumerable<byte> data)
{
ushort sum = 0;
foreach (var b in data)
@@ -1,6 +1,6 @@
using System;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.wiredProtocol
{
public sealed class TouchReadFrameParser
{
@@ -1,4 +1,4 @@
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.wiredProtocol
{
public static class TouchReadProtocol
{
@@ -1,6 +1,6 @@
using System;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.wiredProtocol
{
public sealed class TouchReadResponse
{
@@ -0,0 +1,10 @@
using log4net;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication
{
public class OpthoHeadService
{
protected static readonly ILog rfidDataLogger = LogManager.GetLogger("RfidData");
}
}
@@ -0,0 +1,123 @@
using System;
using System.IO.Ports;
using Common;
using log4net;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.Utils;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication
{
public class OptoHeadTest : IDisposable
{
protected static readonly ILog rfidDataLogger = LogManager.GetLogger("RfidData");
private static SerialDriver serialDriver;
public static SerialDriver BuildConnection(ISmartReader iHead)
{
return new SerialDriverBuilder()
.WithPort($"COM{iHead.RfidComPortNr}")
.WithBaudRate(2400)
.WithDataBits(8)
.WithParity(Parity.None)
.WithStopBits(StopBits.One)
.WithTimeouts(4000, 2000)
.BuildAndConnect();
}
public void CloseConnection()
{
if (serialDriver != null)
serialDriver.CloseConnection();
}
public static string ReadRequest_PCB(ISmartReader iHead)
{
if (iHead.DebugLevel == DebugMode.Simulate)
{
return "-OK Simulated response-";
}
try
{
if (iHead != null)
{
if (serialDriver == null)
serialDriver = BuildConnection(iHead);
RadioService headService = new RadioService(serialDriver);
string serialNo = headService.ReadRequest_PCB(iHead);
return serialNo;
}
}
catch (Exception ex)
{
return (ex.Message.ToString());
}
return "";
}
public static string SetTestMode(ISmartReader iHead)
{
if (iHead.DebugLevel == DebugMode.Simulate)
{
return "-OK Simulated response-";
}
try
{
if (iHead != null)
{
if (serialDriver == null)
serialDriver = BuildConnection(iHead);
RadioService headService = new RadioService(serialDriver);
string answer = headService.SetTestMode(iHead);
return answer;
}
}
catch (Exception ex)
{
return (ex.Message.ToString());
}
return "";
}
public static string SetActiveMode(ISmartReader iHead)
{
if (iHead.DebugLevel == DebugMode.Simulate)
{
return "-OK Simulated response-";
}
try
{
if (iHead != null)
{
if (serialDriver == null)
serialDriver = BuildConnection(iHead);
RadioService headService = new RadioService(serialDriver);
string answer = headService.SetActiveMode(iHead);
return answer;
}
}
catch (Exception ex)
{
return (ex.Message.ToString());
}
return "";
}
public void Dispose()
{
CloseConnection();
}
}
}
@@ -0,0 +1,122 @@
using Common;
using log4net;
using TBF.Rig.Modbus.Meret.AdjustableScale;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.protocolCommons;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.wiredProtocol;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.Utils;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication
{
public class RadioService
{
static string okResponse = "Command complete, no errors";
static string errorResponse = "Unable to execute";
private SerialDriver serialDriver;
public RadioService(SerialDriver serialDriver)
{
this.serialDriver = serialDriver;
}
public string ReadRequest_PCB(ISmartReader iHead)
{
if (!serialDriver.IsOpen())
{
serialDriver.Open();
}
var request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.ViewFactoryId)
.BuildBytes();
byte[] rawData = serialDriver.SendAndWait(request, 10000);
if (rawData == null)
return null;
// parse rawData
var parser = new IperlHatFrameParser();
IperlHatResponse decoded = parser.Parse(rawData);
if (decoded.IsOk)
{
return decoded.GetAsciiPayload();
}
return null;
}
public string SetTestMode(ISmartReader iHead)
{
if (!serialDriver.IsOpen())
{
serialDriver.Open();
}
//Set LED to state 4
byte[] request = new IperlHatFrameBuilder()
.AddDiagnosticLedState(DiagnosticLedState.State4)
.BuildBytes();
byte[] rawData = serialDriver.SendAndWait(request, 1000);
if (rawData == null)
return null;
// parse rawData
var parser = new IperlHatFrameParser();
IperlHatResponse decoded = parser.Parse(rawData);
if (decoded.IsOk)
{
//correct or incorrect response
//okResponse, errorResponse
return "Set Test Mode - OK";
}
return "Set Test Mode - FAILED";
}
/// <summary>
/// stop data streaming by LED
/// </summary>
/// <param name="iHead"></param>
/// <returns></returns>
public string SetActiveMode(ISmartReader iHead)
{
if (!serialDriver.IsOpen())
{
serialDriver.Open();
}
//Set LED to state 1
byte[] request = new IperlHatFrameBuilder()
.AddDiagnosticLedState(DiagnosticLedState.StateOFF)
.BuildBytes();
byte[] rawData = serialDriver.SendAndWait(request, 1000);
if (rawData == null)
return null;
// parse rawData
var parser = new IperlHatFrameParser();
IperlHatResponse decoded = parser.Parse(rawData);
if (decoded.IsOk)
{
return "Set Active Mode - OK";
}
return "Set Active Mode - FAILED";
}
}
}
@@ -0,0 +1,290 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO.Ports;
using System.Linq;
using System.Threading;
using FluentNHibernate.Conventions;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.Utils
{
public class SerialDriver : IDisposable
{
public string ErrorMessage { get; private set; }
private List<byte> SerialPortReadBuffer = new List<byte>();
private SerialPort _serialPort;
private readonly List<byte> _binMessages = new List<byte>();
private bool _isReading;
// Stored configuration (used by Builder)
private readonly string _portName;
private readonly int _baudRate;
private readonly int _dataBits;
private readonly Parity _parity;
private readonly StopBits _stopBits;
private readonly int _readTimeout;
private readonly int _writeTimeout;
private readonly ManualResetEvent _responseReceived = new ManualResetEvent(false);
#region Constructors
// Default constructor (legacy support)
public SerialDriver()
{
_serialPort = new SerialPort();
}
// Builder constructor
internal SerialDriver(
string portName,
int baudRate,
int dataBits,
Parity parity,
StopBits stopBits,
int readTimeout,
int writeTimeout)
{
_portName = portName;
_baudRate = baudRate;
_dataBits = dataBits;
_parity = parity;
_stopBits = stopBits;
_readTimeout = readTimeout;
_writeTimeout = writeTimeout;
}
#endregion
#region Open / Close
// Builder-based open
public bool Open()
{
return OpenConnection(
_portName,
_baudRate,
_dataBits,
_parity,
_stopBits,
_readTimeout,
_writeTimeout
);
}
// Legacy API (unchanged)
public bool OpenConnection(
string comPort,
int baudrate,
int dataBits,
Parity parity,
StopBits stopbits,
int readTimeout = 1000,
int writeTimeout = 1000)
{
lock (this)
{
CloseConnection();
try
{
ErrorMessage = string.Empty;
_serialPort = new SerialPort(comPort, baudrate, parity, dataBits, stopbits)
{
ReadTimeout = readTimeout,
WriteTimeout = writeTimeout
};
_serialPort.DataReceived += DataReceivedHandler;
_serialPort.Open();
}
catch (Exception ex)
{
ErrorMessage = $"COM error: Open failed {comPort}. {ex.Message}";
return false;
}
if (!_serialPort.IsOpen)
{
ErrorMessage = $"COM error: Can't open {comPort}.";
return false;
}
}
return true;
}
public void CloseConnection()
{
if (_serialPort != null)
{
_serialPort.DataReceived -= DataReceivedHandler;
if (_serialPort.IsOpen)
_serialPort.Close();
_serialPort.Dispose();
_serialPort = null;
}
}
public bool IsOpen() => _serialPort?.IsOpen == true;
#endregion
#region Send / Receive
public bool SendMessage(byte[] sendDataBytes, int length, int readTimeout = 1000, int writeTimeout = 1000)
{
if (!IsOpen()) return false;
if (sendDataBytes.Length == 0) return true;
try
{
PrepareReading();
_serialPort.WriteTimeout = writeTimeout;
_serialPort.ReadTimeout = readTimeout;
_serialPort.Write(sendDataBytes, 0, length);
_isReading = true;
var stopwatch = Stopwatch.StartNew();
while (_isReading)
{
if (stopwatch.ElapsedMilliseconds > readTimeout)
{
ErrorMessage = "COM error: Receive timeout";
return false;
}
}
}
catch (Exception ex)
{
ErrorMessage = $"COM error: Transmit failed {_serialPort.PortName}. {ex.Message}";
return false;
}
return true;
}
private void PrepareReading()
{
_serialPort.DiscardInBuffer();
_binMessages.Clear();
_responseReceived.Reset();
_isReading = true;
}
public byte[] GetRawData()
{
return _binMessages.ToArray();
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
lock (this)
{
if (_serialPort == null || !_serialPort.IsOpen) return;
try
{
Thread.Sleep(5);
if (!SerialPortReadBuffer.IsEmpty())
{
SerialPortReadBuffer.Clear();
}
int iWordCounter = 0;
bool isStart = false;
bool isQuestion = false;
int iLength = 0;
while (true)//_serialPort.BytesToRead > 0
{
byte readByte = (byte)_serialPort.ReadByte();
//I have START
if (readByte == C4.IperlHatProtocol.Constants.Start)
{
iWordCounter++;
isStart = true;
}
// I have QUESTION
if (readByte == C4.IperlHatProtocol.Constants.Question)
{
iWordCounter++;
isQuestion = true;
}
//I count length from start
if (iWordCounter > 0)
iWordCounter++;
if (iWordCounter > 0)
{
//Store byte to data
SerialPortReadBuffer.Add(readByte);
}
// we have length
if (iLength == 0 && isStart && SerialPortReadBuffer.Count > 2 )
{
iLength = (int)SerialPortReadBuffer[2];
}
//If we have enough bytes
if (isStart && iLength > 0 && SerialPortReadBuffer.Count >= iLength)
{
break;
}
//if we read END
if (isQuestion && readByte == C4.IperlHatProtocol.Constants.End)
{
break;
}
}
if (SerialPortReadBuffer.Count > 0)
{
_binMessages.AddRange(SerialPortReadBuffer.ToArray());
_responseReceived.Set();
}
}
catch (TimeoutException te)
{
// Ignore shutdown race conditions
}
finally
{
_isReading = false;
}
}
}
public byte[] SendAndWait(byte[] data, int timeoutMs)
{
if (!IsOpen())
throw new InvalidOperationException("Serial port not open");
PrepareReading();
_serialPort.Write(data, 0, data.Length);
if (!_responseReceived.WaitOne(timeoutMs))
{
ErrorMessage = "COM error: response timeout";
return null;
}
return GetRawData();
}
#endregion
public void Dispose()
{
CloseConnection();
}
}
}
@@ -0,0 +1,82 @@
using System;
using System.IO.Ports;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.communication.Utils
{
public class SerialDriverBuilder
{
private string _portName;
private int _baudRate = 9600;
private int _dataBits = 8;
private Parity _parity = Parity.None;
private StopBits _stopBits = StopBits.One;
private int _readTimeout = 1000;
private int _writeTimeout = 1000;
public SerialDriverBuilder WithPort(string portName)
{
_portName = portName;
return this;
}
public SerialDriverBuilder WithBaudRate(int baudRate)
{
_baudRate = baudRate;
return this;
}
public SerialDriverBuilder WithDataBits(int dataBits)
{
_dataBits = dataBits;
return this;
}
public SerialDriverBuilder WithParity(Parity parity)
{
_parity = parity;
return this;
}
public SerialDriverBuilder WithStopBits(StopBits stopBits)
{
_stopBits = stopBits;
return this;
}
public SerialDriverBuilder WithTimeouts(int readTimeout, int writeTimeout)
{
_readTimeout = readTimeout;
_writeTimeout = writeTimeout;
return this;
}
/// <summary>
/// Build driver WITHOUT opening connection
/// </summary>
public SerialDriver Build()
{
return new SerialDriver(
_portName,
_baudRate,
_dataBits,
_parity,
_stopBits,
_readTimeout,
_writeTimeout
);
}
/// <summary>
/// Build driver AND open connection
/// </summary>
public SerialDriver BuildAndConnect()
{
var driver = Build();
if (!driver.Open())
{
throw new InvalidOperationException(driver.ErrorMessage);
}
return driver;
}
}
}
@@ -7,6 +7,7 @@ using System.Windows.Forms;
using Common;
using TBF.Rig.Generic;
using TBF.Rig.RegisterReaders.CommonRR.IPerl.communication;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication;
using TBF.Rig.RegisterReaders.iPerlReaderUNI.common;
using TBF.Rig.RegisterReaders.iPerlReaderUNI.test;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
@@ -89,20 +90,21 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.implementations
{
a.RfidOutputListBox.Items.Clear();
using (Tools.LogChecker logChecker = new Tools.LogChecker("RfidData", log4net.Core.Level.Debug))
using (Tools.LogChecker logChecker = new Tools.LogChecker("ASIC_RfidData", log4net.Core.Level.Debug))
{
ListItem rfidListItem = new ListItem();
rfidListItem.Attributes.Add("style", "font-weight:bold");
Operations selectedOperation;
if(!ItemsForIperlOperations.TryGetValue((string)a.RfidCommandComboBox.SelectedValue, out selectedOperation))
selectedOperation = Operations.EmptyCmd;
switch (selectedOperation)
{
case Operations.ReadPcbCmd:
rfidListItem.Text = $"PCB: {OpticalHeadTest.ReadRequest_PCB(a.ISmartReader)}";
rfidListItem.Text = $"PCB: {OptoHeadTest.ReadRequest_PCB(a.ISmartReader)}";
break;
case Operations.SetTestModeCmd:
rfidListItem.Text = OpticalHeadTest.SetTestMode(a.ISmartReader);
rfidListItem.Text = OptoHeadTest.SetTestMode(a.ISmartReader);
a.OptoListBox.Items.Clear();
stopWorkerThread = false;
optoThread = new Thread(OptoWorker);
@@ -114,7 +116,7 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.implementations
break;
case Operations.SetActiveModeCmd:
rfidListItem.Text = OpticalHeadTest.SetActiveMode(a.ISmartReader);
rfidListItem.Text = OptoHeadTest.SetActiveMode(a.ISmartReader);
stopWorkerThread = true;
a.ISmartReader.StopDataStreamProcessing(); // close opto port
break;
@@ -0,0 +1,160 @@
using System;
using System.Globalization;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed.parserer;
namespace TBF.Rig.RegisterReaders.iPerlASICReader.implementations
{
public enum OptoTelegramFlags : byte
{
OK = 0,
OK_TestStart,
OK_TestEnd,
InvalidTelegram, /// Wrong telegram format of checksum error
SyncError,
}
public class OptoTelegramRaw
{
private DiagnosticLedState4Data data;
private static CultureInfo culture;
///
/// Strobed value
///
public static decimal TestStartTimestampDec;
///
/// Stored values
///
public OptoTelegramFlags Flags;
public DateTime DateTime; /// From PC
public float RefFlow; /// [m3/h]
public int Counter;
public Int16 FlowRaw;
public UInt32 VolumeRaw;
public Int64 VolumeRawExt;
public UInt32 Timestamp;
public Int64 TimestampExt;
///
/// Calculated values
///
public double Flow(double scalingFactor) { return 0.225 * scalingFactor * (double)FlowRaw; }
public double Volume(double scalingFactor) { return 0.0000625 * scalingFactor * (double)VolumeRawExt; }
public decimal TimestampDec() { return (decimal)TimestampExt / (decimal)8192; }
public double VolumeDelta(double scalingFactor,OptoTelegramRaw previous) { return (previous == null) ? 0 : Volume(scalingFactor) - previous.Volume(scalingFactor); }
public decimal TimeDelta() { return TimestampDec() - TestStartTimestampDec; }
public string Label()
{
if (Flags == OptoTelegramFlags.OK_TestStart) return "#### start test ####";
else if (Flags == OptoTelegramFlags.OK_TestEnd) return "#### end of test ####";
else return string.Empty;
}
static OptoTelegramRaw()
{
culture = CultureInfo.CreateSpecificCulture("DE"); /// This is to use comma as decimal number separator
}
public OptoTelegramRaw()
{
}
public void UpdateFromSmart(DiagnosticLedState4Data data,int counter, float refFlow, ref Int64 volumeRawExtLast, ref Int64 timestampExtLast)
{
DateTime = DateTime.Now;
Counter = counter;
RefFlow = refFlow;
FlowRaw = data.RawFlow;
VolumeRaw = data.RawVolume;
Timestamp = data.AsicTimestamp;
///
/// Cope with 'VolumeRaw' overflow
///
Int64 uncorrected = (Int64)(((UInt64)volumeRawExtLast & 0xFFFFFFFFFF000000UL) | VolumeRaw);
if (Math.Abs(uncorrected - volumeRawExtLast) <= 0x800000L)
{
VolumeRawExt = volumeRawExtLast = uncorrected;
}
else if (Math.Abs(uncorrected + 0x1000000L - volumeRawExtLast) <= 0x800000L)
{
VolumeRawExt = volumeRawExtLast = uncorrected + 0x1000000L;
}
else if (Math.Abs(uncorrected - 0x1000000L - volumeRawExtLast) <= 0x800000L)
{
VolumeRawExt = volumeRawExtLast = uncorrected - 0x1000000L;
}
else
{
VolumeRawExt = volumeRawExtLast = uncorrected;
}
///
/// Cope with 'Timestamp' overflow
///
uncorrected = (Int64)(((UInt64)timestampExtLast & 0xFFFFFFFF00000000UL) | Timestamp);
if (Math.Abs(uncorrected - timestampExtLast) <= 0x80000000L)
{
TimestampExt = timestampExtLast = uncorrected;
}
else if (Math.Abs(uncorrected + 0x100000000L - timestampExtLast) <= 0x80000000L)
{
TimestampExt = timestampExtLast = uncorrected + 0x100000000L;
}
else if (Math.Abs(uncorrected - 0x100000000L - timestampExtLast) <= 0x80000000L)
{
TimestampExt = timestampExtLast = uncorrected - 0x100000000L;
}
else
{
TimestampExt = timestampExtLast = uncorrected;
}
}
public void SetFlags(OptoTelegramFlags flags)
{
this.Flags = flags;
}
public string ToString(double scalingFactor, OptoTelegramRaw previous)
{
if (Flags == OptoTelegramFlags.SyncError)
{
return "Sychronization error";
}
else if (Flags == OptoTelegramFlags.InvalidTelegram)
{
return "Invalid telegram";
}
else /// if (flags == OptoTelegramFlags.OK / OptoTelegramFlags.OK_TestStart / OptoTelegramFlags.OK_TestEnd)
{
return string.Format("{0}:{1}:{2}.{3}\t{4} :\t{5}\t{6}\t{7}\t{8}\t{9}\t{10}\t{11}\t{12}\t{13}\t{14}\t{15}",
DateTime.Hour.ToString("D2"),
DateTime.Minute.ToString("D2"),
DateTime.Second.ToString("D2"),
DateTime.Millisecond.ToString("D4"),
Counter,
FlowRaw.ToString("X4"),
VolumeRaw.ToString("X6"),
Timestamp.ToString("X8"),
Flow(scalingFactor).ToString("F2", culture),
Volume(scalingFactor).ToString("F4", culture),
TimestampDec().ToString("F4", culture),
(RefFlow * 1000).ToString("F2", culture),
VolumeDelta(scalingFactor, previous).ToString("F4", culture),
TimeDelta().ToString("F3", culture),
scalingFactor.ToString("F1", culture),
Label());
}
}
}
}
@@ -2,6 +2,7 @@ using System;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Common;
using Common.Iperl;
@@ -11,6 +12,9 @@ using NHibernate;
using Sensus.iPerl.NfcHandler;
using TBF.Rig.Generic;
using TBF.Rig.GenericDevices;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed.parserer;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger;
using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
using CalibrationStruct = TBF.Rig.RegisterReaders.CommonRR.IPerl.communication.CalibrationStruct;
@@ -318,6 +322,7 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.implementations
OptoTelegramRaw[] optoData;
int optoDataCount;
DiagnosticLedParser parser = new DiagnosticLedParser(DiagnosticLedState.State4);
/// Real opto deta count, can be larger then optoData.Length
///
@@ -388,7 +393,7 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.implementations
/// Check whether head is connected, working
try
{
OpenOptoSerialPort($"COM{_iPerlCfg.OptoComPortNr}", 9600, Parity.None, 8, StopBits.One, Handshake.None);
OpenOptoSerialPort($"COM{_iPerlCfg.OptoComPortNr}", 38400, Parity.None, 8, StopBits.One, Handshake.None);
CloseOptoSerialPort();
log.FatalFormat($"{Name} initialized: {this}");
}
@@ -839,12 +844,6 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.implementations
/// <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)
@@ -852,91 +851,64 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.implementations
if (optoSerialPort is null) return;
lock (this)
{
int nrBytes = optoSerialPort.BytesToRead;
if (nrBytes > 0)
{
char[] buffer = new char[nrBytes];
optoSerialPort.Read(buffer, 0, nrBytes);
string received = new string(buffer);
string allRcvd = partOfTelegram + received;
string line = optoSerialPort.ReadLine(); // string
byte[] bytes = optoSerialPort.Encoding.GetBytes(line);
while (true)
Console.WriteLine("RX ← " + HexFormatter.ToSerialHex(bytes));
try
{
int pos = allRcvd.IndexOf("\r\n");
/// CR+LF found
if (optoState == DataStreamState.ProcessAndSave)
{
DiagnosticLedState4Data data = (DiagnosticLedState4Data)parser.ParseLine(line, false);
int bufferIx = BufferIdx(optoDataCount);
if (pos < 0)
{
/// No CR+LF found, wait for more characters in the next invocation
partOfTelegram = allRcvd;
return;
}
else
{
/// CR+LF found
if (optoState == DataStreamState.ProcessAndSave)
if (synchronized)
{
int bufferIx = BufferIdx(optoDataCount);
optoData[bufferIx].Counter = optoDataCount;
optoData[bufferIx].SetFlags(OptoTelegramFlags.SyncError);
}
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);
}
if (data != null)
{
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);
}
optoData[bufferIx].UpdateFromSmart(data, 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, true, volumeRawExtLast,
timestampExtLast);
}
else
{
/// CR+LF was found && (pos >= OptoTelegramRaw.Length - 2) but the telgram was not OK
optoData[bufferIx].Counter = optoDataCount;
optoDataCount++;
}
else /// optoState == OptoState.Flush
optoDataCount++;
}
else /// optoState == OptoState.Flush
{
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);
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);
}
}
}
catch (Exception e)
{
}
//OnOptoReceived(this, new OptoReceivedEventArgs(s));
}
@@ -947,19 +919,19 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.implementations
}
}
public string ReadOptoData()
{
if (optoSerialPort is null) return "";
string received = ".";
lock (this)
{
int nrBytes = optoSerialPort.BytesToRead;
if (nrBytes > 0)
{
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);
Console.WriteLine("RX ← " + received);
}
return received;
@@ -1020,7 +992,7 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.implementations
if ((startIx > 0) && (startIx < StartOptoDataCount) && (optoData[startIx].Flags == OptoTelegramFlags.OK))
{
optoData[startIx].Flags = OptoTelegramFlags.OK_TestStart;
OptoTelegramRaw.TestStartTimestampDec = optoData[startIx].TimestampDec();
Common.Iperl.OptoTelegramRaw.TestStartTimestampDec = optoData[startIx].TimestampDec();
}
else
{
@@ -1029,7 +1001,7 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.implementations
if (optoData[ix].Flags == OptoTelegramFlags.OK)
{
/// This is the first correct opto-telegram received
OptoTelegramRaw.TestStartTimestampDec = optoData[ix].TimestampDec();
Common.Iperl.OptoTelegramRaw.TestStartTimestampDec = optoData[ix].TimestampDec();
break;
}
}
@@ -1177,6 +1149,8 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.implementations
CloseOptoSerialPort();
optoSerialPort = new SerialPort(comPort, baudRate, parity, dataBits, stopBit);
optoSerialPort.Handshake = handshake;
optoSerialPort.NewLine = "\r\n";
optoSerialPort.Encoding = Encoding.ASCII; // or UTF8 if needed
optoSerialPort.Open();
log.FatalFormat($"{Name} OptoPort opened: {this}");
}
@@ -1212,7 +1186,7 @@ namespace TBF.Rig.RegisterReaders.iPerlASICReader.implementations
{
try
{
OpenOptoSerialPort($"COM{_iPerlCfg.OptoComPortNr}", 9600, Parity.None, 8, StopBits.One, Handshake.None);
OpenOptoSerialPort($"COM{_iPerlCfg.OptoComPortNr}", 38400, Parity.None, 8, StopBits.One, Handshake.None);
}
catch (Exception)
{
@@ -11,6 +11,7 @@ using TBF.Rig.RegisterReaders.CommonRR.IPerl;
using TBF.Rig.TestMethods.SmartTest;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication;
namespace TBF.Rig.RegisterReaders.iPerlReaderUNI
{
public class TestMethodCfg : ComponentCfgBase, ITestMethodCfg
+2
View File
@@ -9,6 +9,7 @@ using log4net;
using log4net.Repository.Hierarchy;
using TBF.Rig.Generic;
using TBF.Rig.Sequences;
using TBF.Rig.TestMethods.SmartTest;
namespace TBF.Rig
{
@@ -191,6 +192,7 @@ namespace TBF.Rig
new TestMethods.FlyingStartTankCollection.Compound.Factory(),
new TestMethods.GrabImage.Factory(),
new TestMethods.iPerlCommunication.TestMethodFactory(), /// iPerlCommunication
new TestMethods.SmartTest.TestMethodFactory(), /// Smart Meter Tests
new TestMethods.LeakTest.Factory(),
new TestMethods.LiveStream.Factory(),
new TestMethods.ManualEntry.Factory(),
@@ -0,0 +1,85 @@
///
/// Copyright (c) 2015-2021 Sensus Slovensko a.s.
///
using System.Collections.Generic;
using System.IO.Ports;
using System.Xml.Serialization;
using Common;
using Config.Entities;
using TBF.Rig.Generic;
using TBF.Rig.RegisterReaders.CommonRR.IPerl;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication;
namespace TBF.Rig.TestMethods.SmartTest
{
public class TestMethodCfg : ComponentCfgBase, ITestMethodCfg
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(TestMethodCfg) })[0];
public override XmlSerializer GetSerializer() { return Serializer; }
public IComponentCfgCtrl GetControl(IList<Config.Entities.Component> cmpntEntities) { return new TestMethodCfgCtrl(); }
public override IParamsProvider GetRuntimeTestParamsProvider() { return TestParams; }
public override IParamsProvider CreateTestParamsProvider() { return new iPerlCommunicationParams(true); }
public override IParamsProvider GetUITestParamsProvider(Test test)
{
return (test.Method == Name) ? base.GetUITestParamsProvider(test) : null;
}
/// Private parameterless constructor invoked by all other (public) constructors
TestMethodCfg()
{
Name = "iPerlCommunication";
ParentName = string.Empty;
CommTimeout = 1800; /// ms
MaxCommRetries = 4;
WaitTimeAfterFailure = 2200;
PassThroughWaitTime = 1500;
NrThreads = 2; /// 1, 2 or 4 threads
IperlCheckErrorsToStop = 10;
MciTimeoutMs = 4000; // ms, NFC interface
BaudRate = 57600; // NFC Interface
DataBits = 8; // NFC Interface
ParityBit = Parity.None; // NFC Interface
StopBits = StopBits.Two; // NFC Interface
TestParams = CreateTestParamsProvider() as iPerlCommunicationParams;
}
public TestMethodCfg(IComponentFactory factory)
: this()
{
this.Factory = factory;
}
public string ToString(int i)
{
return string.Format("Name={0}, CommTimeout={1}, MaxRetries={2}, NrThreads={3}", Name, CommTimeout, MaxCommRetries, NrThreads);
}
public int CommTimeout { get; set; }
public int DelayBetweenRetries { get; set; }
public int MaxCommRetries { get; set; }
public int WaitTimeAfterFailure { get; set; }
public int PassThroughWaitTime { get; set; }
public int NrThreads { get; set; }
public int IperlCheckErrorsToStop { get; set; }
public int MciTimeoutMs { get; set; }
public int BaudRate { get; set; }
public int DataBits { get; set; }
public Parity ParityBit { get; set; }
public StopBits StopBits { get; set; }
/// <summary> Test parameters </summary>
[XmlIgnore]
public iPerlCommunicationParams TestParams;
public bool UseWebService { get; set; }
public string BaseUrl { get; set; }
public string RelativeUrl { get; set; }
}
}
@@ -0,0 +1,975 @@
///
/// Copyright (c) 2015-2023 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using System.Drawing;
using Common;
using Config.Entities;
using log4net;
using RestClient;
using Results.Entities;
using TBF.Resources;
using TBF.Rig.Generic;
using TBF.Rig.Sequences;
using TBF.Rig.TestMethods.iPerlCommunication;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication;
using TBF.UiBridge;
using iPerlCommunicationParams = TBF.Rig.TestMethods.iPerlCommunication.iPerlCommunicationParams;
/// Point definition
namespace TBF.Rig.TestMethods.SmartTest
{
public class iPerlCommunicationSeq : SequenceBase
{
private static readonly ILog log = LogManager.GetLogger(typeof(iPerlCommunicationSeq));
public const string Q2correctedFromCmd = "Q2 corrected from ";
public const string StrictQ2ErrorCheckStr = "Strict Q2 error check ";
public const string Q2correctionCheckCmd = "Q2 correction check ";
public const string IperlCheckCmd = "iPERL_check ";
public const string SimulateCmd = "simulate ";
System.Windows.Forms.Form modelessDlg;
///
delegate void SmartCommunicationFormDlgt(iPerlCommunicationSeq myRef, TestMethod method, Test test, ITestParams testParams);
///
void OpenIPerlCommForm(iPerlCommunicationSeq myRef, TestMethod method, Test test, ITestParams testParams)
{
myRef.modelessDlg = new SmartCommunicationForm(method, test, testParams);
myRef.modelessDlg.Show();
}
void CloseIPerlCommForm()
{
UiBridge.Bridge.OnCloseModelessForm(this, null);
modelessDlg = null;
}
/// <summary>
/// Flying start mass collection method sequence
/// </summary>
/// <param name="test">Test entity</param>
/// <returns>
/// Event.Done . . . . . . . OK
/// Event.UiCmdStop . . . . Stopped by the user using the on-screen button STOP
/// Event.OpArgumentError . Target flow is out of range
/// Event.Error . . . . . . Unspecified error
/// </returns>
public IList<Event> Execute(Test test, int repetitionNr, TestMethod method, ITestParams testParams)
{
TestMethodCfg cfgIPerl = method.Cfg as TestMethodCfg;
IList<Event> e; /// Events from currently running operations
checkUiOp = new Operations.CheckUIOp(true); /// Runs in more then one state
modelessDlg = null;
processDataLoggingOp = new TBF.Rig.Operations.ProcessDataLoggingOp(processDataLogger, this, false);
string cmd;
// Normalize activity once (also prevents NullReferenceException on .ToLower()).
var activity = testParams?.Activity;
var activityLower = activity?.ToLowerInvariant();
if (!string.IsNullOrEmpty(activityLower) &&
activityLower.Equals(cmd = iPerlCommunicationConstants.GetDefaultQ2CorrectionsStr.ToLower()))
{
TestProgressEventArgs.SetEstimatedTimes(new int[] { 0, 0, 0, 30, 0, 30, 0, 0 });
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.JustStarted));
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.FlowSetting));
/// Get 'wmType' from IperlHead procedure parameters
int wmType = 0;
#if IPERL
/*foreach (var wm in ProcessData.BatchRslts.Batch.WaterMeters)
{
if (wm != null && !wm.Disabled && wm.WMTypeId() > 0)
{
wmType = wm.WMTypeId();
break;
}
}*/
#endif
//
// if (cfgIPerl.UseWebService)
// {
// IsQ2PreCorrectionCalculated = GetQ2PreCorrectionsOrBackups(cfgIPerl, wmType, out CalculatedQ2PreCorrectionLR, out CalculatedQ2PreCorrectionRL);
// }
/// Generate test results
Results.Entities.TestRslt tstRslt = BatchRslts.GetTestRslt(test.Name, 0);
if (tstRslt != null)
{
tstRslt.StartTime = DateTime.Now;
tstRslt.TestDone = true;
foreach (var wm in BatchRslts.Batch.WaterMeters)
{
if (!wm.Disabled)
{
foreach(var mtr in wm.MeterTestRslts)
{
if (mtr.TestRslt == tstRslt)
{
mtr.Passed = /*!cfgIPerl.UseWebService ||*/ IsQ2PreCorrectionCalculated;
mtr.TestDone = true;
break;
}
}
}
}
}
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.Completed));
Bridge.OnTestCompleted(this, new TestCompletedEventArgs(test.Name, ProcessData.BatchRslts.GetTestRslt(test.Name, 0)));
allResults.Info(TestResult2CsvLine(test.Name, 0)); /// Append the results to the CSV-file
return new List<Event> { Event.Done };
}
else if (!string.IsNullOrEmpty(activityLower) &&
activityLower.Contains(cmd = Q2correctedFromCmd.ToLowerInvariant()))
{
TestProgressEventArgs.SetEstimatedTimes(new int[] { 0, 0, 0, 30, 0, 30, 0, 0 });
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.JustStarted));
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.FlowSetting));
string[] args = testParams.Activity.Substring(cmd.Length).Split(new char[] { ' ' });
string fromTestName = (args.Length >= 1) ? args[0] : string.Empty;
bool isPlus = (args.Length >= 2) ? args[1].ToLower().Contains("plus") : false;
MakeQ2CorrectedFrom(test.Name, fromTestName, isPlus);
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.Completed));
Bridge.OnTestCompleted(this, new TestCompletedEventArgs(test.Name, ProcessData.BatchRslts.GetTestRslt(test.Name, 0)));
allResults.Info(TestResult2CsvLine(test.Name, 0)); /// Append the results to the CSV-file
return new List<Event> { Event.Done };
}
else if (!string.IsNullOrEmpty(activityLower) &&
activityLower.Contains(cmd = StrictQ2ErrorCheckStr.ToLowerInvariant()))
{
TestProgressEventArgs.SetEstimatedTimes(new int[] { 0, 0, 0, 30, 0, 30, 0, 0 });
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.JustStarted));
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.FlowSetting));
string fromTestName = testParams.Activity.Substring(cmd.Length);
StrictQ2ErrorCheck(test.Name, fromTestName);
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.Completed));
Bridge.OnTestCompleted(this, new TestCompletedEventArgs(test.Name, ProcessData.BatchRslts.GetTestRslt(test.Name, 0)));
allResults.Info(TestResult2CsvLine(test.Name, 0)); /// Append the results to the CSV-file
return new List<Event> { Event.Done };
}
else if (!string.IsNullOrEmpty(activityLower) &&
activityLower.Contains(cmd = Q2correctionCheckCmd.ToLowerInvariant()))
{
TestProgressEventArgs.SetEstimatedTimes(new int[] { 0, 0, 0, 30, 0, 30, 0, 0 });
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.JustStarted));
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.FlowSetting));
string[] testNames = testParams.Activity.Substring(cmd.Length).Split(new char[] { ' ' });
if (testNames.Length >= 2)
{
CheckQ2Correction(test.Name, testNames[0], testNames[1]);
}
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.Completed));
Bridge.OnTestCompleted(this, new TestCompletedEventArgs(test.Name, ProcessData.BatchRslts.GetTestRslt(test.Name, 0)));
allResults.Info(TestResult2CsvLine(test.Name, 0)); /// Append the results to the CSV-file
return new List<Event> { Event.Done };
}
else if (!string.IsNullOrEmpty(activityLower) &&
activityLower.Contains(cmd = IperlCheckCmd.ToLowerInvariant()))
{
TestProgressEventArgs.SetEstimatedTimes(new int[] { 0, 0, 0, 30, 0, 30, 0, 0 });
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.JustStarted));
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.FlowSetting));
string[] args = testParams.Activity.Substring(cmd.Length).Split(new char[] { ' ' });
//int maxTestIndex = (ProcessData.BenchInfo is TBF.Rig.DataContainer.BenchInfo.Component)
// ? (ProcessData.BenchInfo as TBF.Rig.DataContainer.BenchInfo.Component).MaxTestIndex
// : int.MaxValue;
Results.Entities.TestRslt tstRslt = ProcessData.BatchRslts.GetTestRslt(test.Name, 0);
if (tstRslt != null)
{
tstRslt.StartTime = DateTime.Now;
int wrongMetersCount = 0;
string message = string.Empty;
for (int i = 0; i < BatchRslts.WMPositionsCount; i++)
{
Results.Entities.WaterMeter wm = BatchRslts.Batch.WaterMeters[i];
Results.Entities.MeterTestRslt mtr = ProcessData.BatchRslts.GetMeterTestRslt(test.Name, i, CompoundMeterId.Single);
///// Reference to iPerl water meter or null:
//TestMethods.iPerlCommunication.iPerlHead.IperlHead iPerlHead = ((sensPath.RegisterReaders != null) && (i < sensPath.RegisterReaders.Length))
// ? (sensPath.RegisterReaders[i] as TestMethods.iPerlCommunication.iPerlHead.IperlHead)
// : null;
if ((wm != null) && (mtr != null))
{
int errorIndicators = 0;
bool anyErrorOfThisMeter = false;
foreach (var arg in args)
{
#if TURA_SPECIAL
if (arg.ToLower() == "q2factors")
{
if ((wm.ProdQ2CorrRL != wm.Q2CorrRL) || (wm.ProdQ2CorrLR != wm.Q2CorrLR))
{
anyErrorOfThisMeter = true;
message += string.Format("Q2 korekčné faktory vodomera {0} nesedia{1}", wm.WMPosition, Environment.NewLine);
errorIndicators |= (int)ErrorFlagMask.E26; /// Q2 correction factors not valid
}
}
#endif
if (arg.ToLower() == "direction")
{
//if (wm.Pruefindex > maxTestIndex)
//{
// anyErrorOfThisMeter = true;
// message += string.Format("Príliš veľa opakovaní testu vodomera {0}{1}", wm.WMPosition, Environment.NewLine);
// errorIndicators |= (int)ErrorFlagMask.E27; /// Wrong direction (positive/negative counting)
//}
}
if (arg.ToLower() == "prevworkstep")
{
if (wm.LastRecordIsNok)
{
wm.ErrorFlags |= (int)ErrorFlagMask.E28; /// Set E28
}
if ((wm.ErrorFlags & (int)ErrorFlagMask.E28) != 0)
{
anyErrorOfThisMeter = true;
message += string.Format("iPerl{0} : Predchádzajúci krok nebol zaznamenaný{1}", wm.WMPosition, Environment.NewLine);
errorIndicators |= (int)ErrorFlagMask.E28; /// Previous workstep missing or NOK (production tracing)
}
}
}
mtr.TestDone = true;
mtr.ErrorIndicators = errorIndicators;
///
if (anyErrorOfThisMeter)
{
/// This iPerl check did not pass
mtr.Passed = false;
wrongMetersCount++;
}
else
{
/// Check passed OK
mtr.Passed = true;
}
}
}
tstRslt.EndTime = DateTime.Now;
tstRslt.TestDone = true;
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.Completed));
Bridge.OnTestCompleted(this, new TestCompletedEventArgs(test.Name, tstRslt));
allResults.Info(TestResult2CsvLine(test.Name, 0)); /// Append the results to the CSV-file
if (wrongMetersCount >= cfgIPerl.IperlCheckErrorsToStop)
{
State.Create("iPerlCommunicationSeq : Show check result")
.AddOperation(new Operations.LargeMessageBoxOp(message))
.EnterState();
do
{
e = StateMachine.WaitRunDevsRunOps();
}
while (!e.Contains(Event.Continue) && !e.Contains(Event.Abort));
if (e.Contains(Event.Abort))
{
Bridge.OnError(this, string.Format("Niečo nie je v poriadku !"));
return new List<Event> { Event.UiCmdStop };
}
}
}
return new List<Event> { Event.Done };
}
else if (!string.IsNullOrEmpty(activity) &&
activity.Contains(cmd = SimulateCmd))
{
TestProgressEventArgs.SetEstimatedTimes(new int[] { 0, 0, 0, 30, 0, 30, 0, 0 });
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.JustStarted));
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.FlowSetting));
if (testParams.Activity.ToLower().Contains("q3")) MakeSimulated(test, 1, 0, -0.5f);
else if (testParams.Activity.Substring(cmd.Length).ToLower() == "q2") MakeSimulated(test, 1, 0, 0.5f);
else if (testParams.Activity.Substring(cmd.Length).ToLower() == "q1") MakeSimulated(test, 1, 0, -5.1f);
else if (testParams.Activity.Substring(cmd.Length).ToLower() == "compound ok") MakeSimulatedCompound(test, 1, 0, 0.7f, 1.0f);
else if (testParams.Activity.Substring(cmd.Length).ToLower() == "compound nok") MakeSimulatedCompound(test, 1, 0, 4.7f, 0.9f);
else if (testParams.Activity.Substring(cmd.Length).ToLower() == "compound rise") MakeSimulatedCompound(test, 1, 0, 0.7f, 0.0f);
else if (testParams.Activity.Substring(cmd.Length).ToLower() == "compound fall") MakeSimulatedCompound(test, 1, 0, 0.7f, 0.9f);
else if (testParams.Activity.Substring(cmd.Length).ToLower() == "iperls")
{
string[] pcbNrs = new string[] { "831232435539", "831232435562", "831232435587",
"831232432141", "831232432497", "831232763641" };
TestRslt tstRslt = BatchRslts.GetTestRslt(test.Name, test.Part);
if (tstRslt != null)
{
Results.Utils.GetCounterStates(tstRslt, Program.LocalSettings.Counters);
/// Auxiliary results ... not required
/// Main results
tstRslt.MethodClass = TbfComponents.FindComponent(test.Method).ClassName;
tstRslt.TestDone = true;
tstRslt.StartTime = tstRslt.Batch.StartTime;
tstRslt.EndTime = DateTime.Now;
tstRslt.FlowSetTime = 0;
tstRslt.MassOfEvapWater = 0;
tstRslt.TestTime = 1;
for (int i = 0; i < BatchRslts.Batch.WaterMeters.Count; i++)
{
MeterTestRslt meterRslt =
BatchRslts.GetMeterTestRslt(test.Name, i, CompoundMeterId.Single);
if (meterRslt != null)
{
meterRslt.WaterMeter.SerialNr = pcbNrs[i % pcbNrs.Length];
meterRslt.Passed = true;
meterRslt.TestDone = true;
}
//if (iperlHeads[i] != null)
//{
// iperlHeads[i].CommFailed = iperlHeads[i].Disabled = false;
// iperlHeads[i].SerialNr = pcbNrs[i % pcbNrs.Length];
//}
}
}
}
Bridge.OnTestProgress(this, new TestProgressEventArgs(test, Progress.Completed));
Bridge.OnTestCompleted(this, new TestCompletedEventArgs(test.Name, ProcessData.BatchRslts.GetTestRslt(Common.Utils.GetTestName(test.Name, 1, 1), 0)));
allResults.Info(TestResult2CsvLine(test.Name, 0)); /// Append the results to the CSV-file
//------------------------------------------------
Bridge.OnActivity(this, testParams.Activity);
//------------------------------------------------
State.Create(string.Format("iPerlCommunicationSeq : {0}", testParams.Activity))
.AddOperation(checkUiOp)
.EnterState();
e = StateMachine.WaitRunDevsRunOps();
if (TestAndLogUiCmdStop(test, e))
{
return new List<Event> { Event.UiCmdStop };
}
}
else
{
///
/// Show the modeless dialog with error indication
///
///
// IMPORTANT:
// Avoid Control.Invoke(Delegate, object[]) because it tries to convert each argument
// to the delegate parameter types at runtime (and currently it expects a different TestMethod type).
//Program.MainWnd.Invoke((Action)(() => OpenIPerlCommForm(this, method, test, testParams)));
Program.MainWnd.Invoke(new SmartCommunicationFormDlgt(OpenIPerlCommForm), new object[] { this, method, test, testParams });
//------------------------------------------------
Bridge.OnActivity(this, Strings.iPerl_Communication_in_progress);
//------------------------------------------------
bool stopPressed = false; /// true when STOP button pressed
bool completed = false;
State.Create("iPerlCommunicationSeq : Wait until the entry form is closed")
.AddOperation(checkUiOp)
.EnterState();
do {
e = StateMachine.WaitRunDevsRunOps();
stopPressed = TestAndLogUiCmdStop(test, e);
completed = (modelessDlg is GenericDevices.IHasCompleted)
&& (modelessDlg as GenericDevices.IHasCompleted).Completed;
}
while (!stopPressed && !completed);
if (stopPressed)
{
CloseIPerlCommForm();
return new List<Event> { Event.UiCmdStop };
}
else
{
TBF.UiBridge.Bridge.OnTestProgress(null, new TBF.UiBridge.TestProgressEventArgs(test.Name, Progress.Completed));
}
/// Test 'Quit'
modelessDlg = null; /// Modeless dialog is closed now
}
return new List<Event> { Event.Done };
}
/// <summary>
/// Read default Q2 correction factors from a REST service (= Web service).
/// </summary>
/// <param name="cfgIPerl">iPerlCommunication component configuration</param>
/// <param name="wmType">Water meter type (WZ Typ)</param>
/// <param name="q2PreCorrectionLR">Default Q2 correction LR</param>
/// <param name="q2PreCorrectionRL">Default Q2 correction RL</param>
/// <returns>true when successful</returns>
static bool ReadCorrectionsFromWebService(TestMethodCfg_IPerl cfgIPerl, int wmType, out int q2PreCorrectionLR, out int q2PreCorrectionRL)
{
if (wmType == 0)
{
/// No REST service call when wmType == 0, factors are 0
q2PreCorrectionLR = 0;
q2PreCorrectionRL = 0;
return true;
}
try
{
GetQ2PreCorrectionClient client = new GetQ2PreCorrectionClient(cfgIPerl.BaseUrl);
client.GetToken("ReadUser", "sensus", "https://deluh1web03.world.fluidtechnology.net/SensusCore/api/v1/Locations/1/Login2").Wait();
Q2PreCorrection response = client.GetQ2Correction(string.Format(cfgIPerl.RelativeUrl, wmType)).Result;
if (response != null && response.AreDataCalculated)
{
q2PreCorrectionLR = response.CorrLR;
q2PreCorrectionRL = response.CorrRL;
log.WarnFormat("Q2 corrections from a REST client for WM Type = {0} are: LR = {1}, RL = {2}", wmType, q2PreCorrectionLR, q2PreCorrectionRL);
return true;
}
else
{
log.ErrorFormat("Failed to obtain Q2 corrections from a REST client for WM Type = {0}", wmType);
q2PreCorrectionLR = 0;
q2PreCorrectionRL = 0;
return false;
}
}
catch (Exception exc)
{
log.ErrorFormat("Failed to obtain Q2 corrections from a REST client for WM Type = {0}: {1}", wmType, exc.Message);
q2PreCorrectionLR = 0;
q2PreCorrectionRL = 0;
return false;
}
}
/// <summary>
/// Obtain Q2 correction factors from a REST service or from local settings (stored backup values)
/// </summary>
/// <param name="cfgIPerl">iPerlCommunication component configuration</param>
/// <param name="wmType">Water meter type (WZ Typ)</param>
/// <param name="q2PreCorrectionLR">Default Q2 correction LR</param>
/// <param name="q2PreCorrectionRL">Default Q2 correction RL</param>
/// <returns>true when successful</returns>
public static bool GetQ2PreCorrectionsOrBackups(TestMethodCfg_IPerl cfgIPerl, int wmType, out int q2PreCorrectionLR, out int q2PreCorrectionRL)
{
/// Get Q2 pre-correction values from REST service
bool restOK = ReadCorrectionsFromWebService(cfgIPerl, wmType, out q2PreCorrectionLR, out q2PreCorrectionRL);
/// Store / load Q2 pre-correction values
Point storedValue;
if (restOK)
{
/// Q2 pre-correction values were successfully obtained from a REST service for the specified wmType
if (!Program.LocalSettings.Q2PreCorrections.TryGetValue(wmType, out storedValue))
{
/// No Q2 pre-correction values in the dictionary for the specified wmType => save them
Program.LocalSettings.Q2PreCorrections.Add(wmType, new Point(q2PreCorrectionLR, q2PreCorrectionRL));
log.WarnFormat("Q2 corrections added to dictionary for WM Type = {0}: LR = {1}, RL = {2}", wmType, q2PreCorrectionLR, q2PreCorrectionRL);
}
else if (storedValue.X != q2PreCorrectionLR || storedValue.Y != q2PreCorrectionRL)
{
/// Different Q2 pre-correction values in the dictionary for the specified wmType => overwrite them with ones from the REST service
Program.LocalSettings.Q2PreCorrections[wmType] = new Point(q2PreCorrectionLR, q2PreCorrectionRL);
log.WarnFormat("Q2 corrections modified in dictionary for WM Type = {0}: LR = {1}, RL = {2}", wmType, q2PreCorrectionLR, q2PreCorrectionRL);
}
else
{
/// Q2 pre-correction values in the dictionary are the same and were not changed
log.WarnFormat("Q2 corrections in dictionary for WM Type = {0} are the same and were not changed", wmType, q2PreCorrectionLR, q2PreCorrectionRL);
}
}
else
{
/// No Q2 pre-correction values from a REST service => read the dictionary
if (Program.LocalSettings.Q2PreCorrections.TryGetValue(wmType, out storedValue))
{
/// Q2 pre-correction values successfully read from the dictionary
q2PreCorrectionLR = storedValue.X;
q2PreCorrectionRL = storedValue.Y;
log.WarnFormat("Q2 corrections loaded from dictionary for WM Type = {0}: LR = {1}, RL = {2}", wmType, q2PreCorrectionLR, q2PreCorrectionRL);
}
else
{
/// Q2 pre-correction values not found in the dictionary => use zeros
q2PreCorrectionLR = 0;
q2PreCorrectionRL = 0;
log.ErrorFormat("Q2 corrections not found in the dictionary for WM Type = {0}, using zeros", wmType, q2PreCorrectionLR, q2PreCorrectionRL);
/// Everything failed => using zero values
return false;
}
}
/// Q2 pre-corections were obtained from REST service or stored backup values were used
return true;
}
/// <summary>
/// Virtually apply Q2 correction to a test used for the correction calculation.
/// </summary>
/// <param name="testName">This test name</param>
/// <param name="oriTestRslt">Name of Q2 test done before Q2 correction (Q2adj)</param>
/// <remarks>Assuming this test does not have multiple parts (part = 0)</remarks>
void MakeQ2CorrectedFrom(string testName, string oriTestName, bool isPlus = false)
{
Results.Entities.TestRslt oriTestRslt = ProcessData.BatchRslts.GetTestRslt(oriTestName, 0);
Results.Entities.TestRslt tstRslt = ProcessData.BatchRslts.GetTestRslt(testName, 0);
if (oriTestRslt == null || tstRslt == null) return;
tstRslt.Components = oriTestRslt.Components;
/// Auxiliary results, as in SequenceBase.UpdateTemoPressDensAmb()
tstRslt.AmbTempMean = oriTestRslt.AmbTempMean;
tstRslt.AmbTempStart = oriTestRslt.AmbTempStart;
tstRslt.AmbTempEnd = oriTestRslt.AmbTempEnd;
tstRslt.AmbTempMin = oriTestRslt.AmbTempMin;
tstRslt.AmbTempMax = oriTestRslt.AmbTempMax;
tstRslt.AmbPressMean = oriTestRslt.AmbPressMean;
tstRslt.AmbPressStart = oriTestRslt.AmbPressStart;
tstRslt.AmbPressEnd = oriTestRslt.AmbPressEnd;
tstRslt.AmbPressMin = oriTestRslt.AmbPressMin;
tstRslt.AmbPressMax = oriTestRslt.AmbPressMax;
tstRslt.AmbHumiMean = oriTestRslt.AmbHumiMean;
tstRslt.AmbHumiStart = oriTestRslt.AmbHumiStart;
tstRslt.AmbHumiEnd = oriTestRslt.AmbHumiEnd;
tstRslt.AmbHumiMin = oriTestRslt.AmbHumiMin;
tstRslt.AmbHumiMax = oriTestRslt.AmbHumiMax;
tstRslt.PressUpMean = oriTestRslt.PressUpMean;
tstRslt.PressUpStart = oriTestRslt.PressUpStart;
tstRslt.PressUpEnd = oriTestRslt.PressUpEnd;
tstRslt.PressUpMin = oriTestRslt.PressUpMin;
tstRslt.PressUpMax = oriTestRslt.PressUpMax;
tstRslt.PressDownMean = oriTestRslt.PressDownMean;
tstRslt.PressDownStart = oriTestRslt.PressDownStart;
tstRslt.PressDownEnd = oriTestRslt.PressDownEnd;
tstRslt.PressDownMin = oriTestRslt.PressDownMin;
tstRslt.PressDownMax = oriTestRslt.PressDownMax;
tstRslt.PressDeltaMean = oriTestRslt.PressDeltaMean;
tstRslt.PressDeltaStart = oriTestRslt.PressDeltaStart;
tstRslt.PressDeltaEnd = oriTestRslt.PressDeltaEnd;
tstRslt.PressDeltaMin = oriTestRslt.PressDeltaMin;
tstRslt.PressDeltaMax = oriTestRslt.PressDeltaMax;
tstRslt.ConductMean = oriTestRslt.ConductMean;
tstRslt.ConductStart = oriTestRslt.ConductStart;
tstRslt.ConductEnd = oriTestRslt.ConductEnd;
tstRslt.ConductMin = oriTestRslt.ConductMin;
tstRslt.ConductMax = oriTestRslt.ConductMax;
tstRslt.TempUpMean = oriTestRslt.TempUpMean;
tstRslt.TempUpStart = oriTestRslt.TempUpStart;
tstRslt.TempUpEnd = oriTestRslt.TempUpEnd;
tstRslt.TempUpMin = oriTestRslt.TempUpMin;
tstRslt.TempUpMax = oriTestRslt.TempUpMax;
tstRslt.TempDownMean = oriTestRslt.TempDownMean;
tstRslt.TempDownStart = oriTestRslt.TempDownStart;
tstRslt.TempDownEnd = oriTestRslt.TempDownEnd;
tstRslt.TempDownMin = oriTestRslt.TempDownMin;
tstRslt.TempDownMax = oriTestRslt.TempDownMax;
tstRslt.TempDivMean = oriTestRslt.TempDivMean;
tstRslt.TempDivStart = oriTestRslt.TempDivStart;
tstRslt.TempDivEnd = oriTestRslt.TempDivEnd;
tstRslt.TempDivMin = oriTestRslt.TempDivMin;
tstRslt.TempDivMax = oriTestRslt.TempDivMax;
tstRslt.DensityIn = oriTestRslt.DensityIn;
tstRslt.DensityLine = oriTestRslt.DensityLine;
tstRslt.DensityDiv = oriTestRslt.DensityDiv;
tstRslt.StartTime = oriTestRslt.StartTime;
tstRslt.EndTime = oriTestRslt.EndTime;
tstRslt.FlowSetTime = oriTestRslt.FlowSetTime;
tstRslt.TestTime = oriTestRslt.TestTime;
tstRslt.PulsesMaster = oriTestRslt.PulsesMaster;
tstRslt.ConstMasterRaw = oriTestRslt.ConstMasterRaw;
tstRslt.ConstMaster = oriTestRslt.ConstMaster;
tstRslt.MassStartRaw = oriTestRslt.MassStartRaw;
tstRslt.MassStart = oriTestRslt.MassStart;
tstRslt.MassEndRaw = oriTestRslt.MassEndRaw;
tstRslt.MassEnd = oriTestRslt.MassEnd;
tstRslt.MassOfEvapWater = oriTestRslt.MassOfEvapWater;
//tstRslt.FlowMass = oriTestRslt.FlowMass;
//tstRslt.FlowVolume = oriTestRslt.FlowVolume;
tstRslt.VolumeCTV = oriTestRslt.VolumeCTV;
tstRslt.VolumeMaster = oriTestRslt.VolumeMaster;
tstRslt.ErrorMaster = oriTestRslt.ErrorMaster;
tstRslt.FlowMean = oriTestRslt.FlowMean;
tstRslt.FlowMin = oriTestRslt.FlowMin;
tstRslt.FlowMax = oriTestRslt.FlowMax;
tstRslt.Custom1 = oriTestRslt.Custom1;
tstRslt.Custom2 = oriTestRslt.Custom2;
tstRslt.Custom3 = oriTestRslt.Custom3;
tstRslt.Custom4 = oriTestRslt.Custom4;
tstRslt.Custom5 = oriTestRslt.Custom5;
tstRslt.Custom6 = oriTestRslt.Custom6;
tstRslt.Custom7 = oriTestRslt.Custom7;
tstRslt.Custom8 = oriTestRslt.Custom8;
for (int i = 0; i < ProcessData.BatchRslts.WMPositionsCount; i++)
{
// Fix for CS7036: Added the missing 'meterId' argument to the GetMeterTestRslt method call.
var q3mtr = ProcessData.BatchRslts.GetMeterTestRslt("Q3", i, CompoundMeterId.SingleOrCompound);
double q3error = (q3mtr != null) ? q3mtr.Error : 0;
Results.Entities.MeterTestRslt oriMeterRslt = ProcessData.BatchRslts.GetMeterTestRslt(oriTestName, i, CompoundMeterId.SingleOrCompound);
Results.Entities.MeterTestRslt meterRslt = ProcessData.BatchRslts.GetMeterTestRslt(testName, i, CompoundMeterId.SingleOrCompound);
/// Reference to iPerl water meter or null:
TestMethods.iPerlCommunication.iPerlHead.IperlHead iPerl = ((sensPath != null) && (sensPath.RegisterReaders != null) && (i < sensPath.RegisterReaders.Length))
? (sensPath.RegisterReaders[i] as TestMethods.iPerlCommunication.iPerlHead.IperlHead)
: null;
if (iPerl != null && meterRslt != null && oriMeterRslt != null)
{
#if ORACLE_DB
meterRslt.ErrorBC = oriMeterRslt.Error;
#endif
meterRslt.PulsesMeter = oriMeterRslt.PulsesMeter;
meterRslt.PulsesMaster = oriMeterRslt.PulsesMaster;
meterRslt.PulsesPerLiter = oriMeterRslt.PulsesPerLiter;
meterRslt.VolumeRef = oriMeterRslt.VolumeRef;
meterRslt.TestTime = oriMeterRslt.TestTime;
if (q3error * oriMeterRslt.Error < 0)
{
/// iPerl with Q2 correction => generate an artificial error equal to +1/10 of the original one (relative to Q2 target error)
meterRslt.Error = 0.1 * oriMeterRslt.Error;
}
else
{
/// iPerl with Q2 correction => generate an artificial error equal to -1/10 of the original one (relative to Q2 target error)
meterRslt.Error = - 0.1 * oriMeterRslt.Error;
}
meterRslt.VolumeMeter = meterRslt.VolumeRef * (100.0 + meterRslt.Error) / 100.0;
double signature = (oriMeterRslt.VolumeEnd > oriMeterRslt.VolumeStart) ? (+1) : (-1);
meterRslt.VolumeStart = oriMeterRslt.VolumeStart;
meterRslt.VolumeEnd = meterRslt.VolumeStart + signature * meterRslt.VolumeMeter;
meterRslt.Passed = (meterRslt.Error >= tstRslt.ErrLimLo() + tstRslt.ErrLimMargin()
&& meterRslt.Error <= tstRslt.ErrLimHi() - tstRslt.ErrLimMargin());
meterRslt.TestDone = true;
tstRslt.TestDone = true;
}
}
}
/// <summary>
/// Evaluate a given Q2 test result agains stricter error limits when Oruefindex == 1.
/// </summary>
/// <param name="testName">This test name</param>
/// <param name="oriTestRslt">Name of Q2 test done before Q2 correction (Q2adj)</param>
/// <remarks>Assuming this test does not have multiple parts (part = 0)</remarks>
void StrictQ2ErrorCheck(string testName, string oriTestName)
{
Results.Entities.TestRslt oriTestRslt = ProcessData.BatchRslts.GetTestRslt(oriTestName, 0);
Results.Entities.TestRslt tstRslt = ProcessData.BatchRslts.GetTestRslt(testName, 0);
if (oriTestRslt == null || tstRslt == null) return;
tstRslt.Components = oriTestRslt.Components;
/// Auxiliary results, as in SequenceBase.UpdateTemoPressDensAmb()
tstRslt.AmbTempMean = oriTestRslt.AmbTempMean;
tstRslt.AmbTempStart = oriTestRslt.AmbTempStart;
tstRslt.AmbTempEnd = oriTestRslt.AmbTempEnd;
tstRslt.AmbTempMin = oriTestRslt.AmbTempMin;
tstRslt.AmbTempMax = oriTestRslt.AmbTempMax;
tstRslt.AmbPressMean = oriTestRslt.AmbPressMean;
tstRslt.AmbPressStart = oriTestRslt.AmbPressStart;
tstRslt.AmbPressEnd = oriTestRslt.AmbPressEnd;
tstRslt.AmbPressMin = oriTestRslt.AmbPressMin;
tstRslt.AmbPressMax = oriTestRslt.AmbPressMax;
tstRslt.AmbHumiMean = oriTestRslt.AmbHumiMean;
tstRslt.AmbHumiStart = oriTestRslt.AmbHumiStart;
tstRslt.AmbHumiEnd = oriTestRslt.AmbHumiEnd;
tstRslt.AmbHumiMin = oriTestRslt.AmbHumiMin;
tstRslt.AmbHumiMax = oriTestRslt.AmbHumiMax;
tstRslt.PressUpMean = oriTestRslt.PressUpMean;
tstRslt.PressUpStart = oriTestRslt.PressUpStart;
tstRslt.PressUpEnd = oriTestRslt.PressUpEnd;
tstRslt.PressUpMin = oriTestRslt.PressUpMin;
tstRslt.PressUpMax = oriTestRslt.PressUpMax;
tstRslt.PressDownMean = oriTestRslt.PressDownMean;
tstRslt.PressDownStart = oriTestRslt.PressDownStart;
tstRslt.PressDownEnd = oriTestRslt.PressDownEnd;
tstRslt.PressDownMin = oriTestRslt.PressDownMin;
tstRslt.PressDownMax = oriTestRslt.PressDownMax;
tstRslt.PressDeltaMean = oriTestRslt.PressDeltaMean;
tstRslt.PressDeltaStart = oriTestRslt.PressDeltaStart;
tstRslt.PressDeltaEnd = oriTestRslt.PressDeltaEnd;
tstRslt.PressDeltaMin = oriTestRslt.PressDeltaMin;
tstRslt.PressDeltaMax = oriTestRslt.PressDeltaMax;
tstRslt.ConductMean = oriTestRslt.ConductMean;
tstRslt.ConductStart = oriTestRslt.ConductStart;
tstRslt.ConductEnd = oriTestRslt.ConductEnd;
tstRslt.ConductMin = oriTestRslt.ConductMin;
tstRslt.ConductMax = oriTestRslt.ConductMax;
tstRslt.TempUpMean = oriTestRslt.TempUpMean;
tstRslt.TempUpStart = oriTestRslt.TempUpStart;
tstRslt.TempUpEnd = oriTestRslt.TempUpEnd;
tstRslt.TempUpMin = oriTestRslt.TempUpMin;
tstRslt.TempUpMax = oriTestRslt.TempUpMax;
tstRslt.TempDownMean = oriTestRslt.TempDownMean;
tstRslt.TempDownStart = oriTestRslt.TempDownStart;
tstRslt.TempDownEnd = oriTestRslt.TempDownEnd;
tstRslt.TempDownMin = oriTestRslt.TempDownMin;
tstRslt.TempDownMax = oriTestRslt.TempDownMax;
tstRslt.TempDivMean = oriTestRslt.TempDivMean;
tstRslt.TempDivStart = oriTestRslt.TempDivStart;
tstRslt.TempDivEnd = oriTestRslt.TempDivEnd;
tstRslt.TempDivMin = oriTestRslt.TempDivMin;
tstRslt.TempDivMax = oriTestRslt.TempDivMax;
tstRslt.DensityIn = oriTestRslt.DensityIn;
tstRslt.DensityLine = oriTestRslt.DensityLine;
tstRslt.DensityDiv = oriTestRslt.DensityDiv;
tstRslt.StartTime = oriTestRslt.StartTime;
tstRslt.EndTime = oriTestRslt.EndTime;
tstRslt.FlowSetTime = oriTestRslt.FlowSetTime;
tstRslt.TestTime = oriTestRslt.TestTime;
tstRslt.PulsesMaster = oriTestRslt.PulsesMaster;
tstRslt.ConstMasterRaw = oriTestRslt.ConstMasterRaw;
tstRslt.ConstMaster = oriTestRslt.ConstMaster;
tstRslt.MassStartRaw = oriTestRslt.MassStartRaw;
tstRslt.MassStart = oriTestRslt.MassStart;
tstRslt.MassEndRaw = oriTestRslt.MassEndRaw;
tstRslt.MassEnd = oriTestRslt.MassEnd;
tstRslt.MassOfEvapWater = oriTestRslt.MassOfEvapWater;
//tstRslt.FlowMass = oriTestRslt.FlowMass;
//tstRslt.FlowVolume = oriTestRslt.FlowVolume;
tstRslt.VolumeCTV = oriTestRslt.VolumeCTV;
tstRslt.VolumeMaster = oriTestRslt.VolumeMaster;
tstRslt.ErrorMaster = oriTestRslt.ErrorMaster;
tstRslt.FlowMean = oriTestRslt.FlowMean;
tstRslt.FlowMin = oriTestRslt.FlowMin;
tstRslt.FlowMax = oriTestRslt.FlowMax;
tstRslt.Custom1 = oriTestRslt.Custom1;
tstRslt.Custom2 = oriTestRslt.Custom2;
tstRslt.Custom3 = oriTestRslt.Custom3;
tstRslt.Custom4 = oriTestRslt.Custom4;
tstRslt.Custom5 = oriTestRslt.Custom5;
tstRslt.Custom6 = oriTestRslt.Custom6;
tstRslt.Custom7 = oriTestRslt.Custom7;
tstRslt.Custom8 = oriTestRslt.Custom8;
for (int i = 0; i < ProcessData.BatchRslts.WMPositionsCount; i++)
{
Results.Entities.MeterTestRslt oriMeterRslt = ProcessData.BatchRslts.GetMeterTestRslt(oriTestName, i, CompoundMeterId.Single);
Results.Entities.MeterTestRslt meterRslt = ProcessData.BatchRslts.GetMeterTestRslt(testName, i, CompoundMeterId.Single);
/// Reference to iPerl water meter or null:
TestMethods.iPerlCommunication.iPerlHead.IperlHead iPerl = ((sensPath != null) && (sensPath.RegisterReaders != null) && (i < sensPath.RegisterReaders.Length))
? (sensPath.RegisterReaders[i] as TestMethods.iPerlCommunication.iPerlHead.IperlHead)
: null;
if (meterRslt != null && oriMeterRslt != null)
{
#if ORACLE_DB
meterRslt.ErrorBC = oriMeterRslt.Error;
#endif
meterRslt.PulsesMeter = oriMeterRslt.PulsesMeter;
meterRslt.PulsesMaster = oriMeterRslt.PulsesMaster;
meterRslt.PulsesPerLiter = oriMeterRslt.PulsesPerLiter;
meterRslt.VolumeRef = oriMeterRslt.VolumeRef;
meterRslt.TestTime = oriMeterRslt.TestTime;
if (iPerl != null && ProcessData.BatchRslts.Batch.WaterMeters[i] != null &&
!ProcessData.BatchRslts.Batch.WaterMeters[i].Disabled)
{
/// Either no iPerl head or no Q2 correction
meterRslt.Error = oriMeterRslt.Error;
meterRslt.VolumeMeter = oriMeterRslt.VolumeMeter;
meterRslt.VolumeStart = oriMeterRslt.VolumeStart;
meterRslt.VolumeEnd = oriMeterRslt.VolumeEnd;
#if ORACLE_DB
if ((ProcessData.BatchRslts.Batch.WaterMeters[i].Pruefindex % 100) == 1)
{
meterRslt.Passed = (oriMeterRslt.Error >= tstRslt.ErrLimLo() + tstRslt.ErrLimMargin()
&& oriMeterRslt.Error <= tstRslt.ErrLimHi() - tstRslt.ErrLimMargin());
}
else
#endif
{
meterRslt.Passed = oriMeterRslt.Passed;
}
meterRslt.TestDone = true;
tstRslt.TestDone = true;
}
}
}
}
/// <summary>
/// Check results of 2 tests: before Q2 correction and after Q2 correction.
/// Evaluate whether Q2 correction works OK.
/// </summary>
/// <param name="testName">This test name</param>
/// <param name="testNameQ2bc">Name of Q2 test done before correction</param>
/// <param name="testNameQ2ac">Name of Q2 test done after correction</param>
/// <remarks>Assuming these tests do not have multiple parts (part = 0)</remarks>
void CheckQ2Correction(string testName, string testNameQ2bc, string testNameQ2ac)
{
Results.Entities.TestRslt tstRslt = ProcessData.BatchRslts.GetTestRslt(testName, 0);
Results.Entities.TestRslt testRsltQ2bc = ProcessData.BatchRslts.GetTestRslt(testNameQ2bc, 0);
Results.Entities.TestRslt testRsltQ2ac = ProcessData.BatchRslts.GetTestRslt(testNameQ2ac, 0);
if ((tstRslt == null) || (testRsltQ2bc == null) || (testRsltQ2ac == null)) return;
tstRslt.Components = testRsltQ2ac.Components;
/// Auxiliary results, as in SequenceBase.UpdateTemoPressDensAmb()
tstRslt.AmbTempMean = testRsltQ2ac.AmbTempMean;
tstRslt.AmbTempStart = testRsltQ2ac.AmbTempStart;
tstRslt.AmbTempEnd = testRsltQ2ac.AmbTempEnd;
tstRslt.AmbTempMin = testRsltQ2ac.AmbTempMin;
tstRslt.AmbTempMax = testRsltQ2ac.AmbTempMax;
tstRslt.AmbPressMean = testRsltQ2ac.AmbPressMean;
tstRslt.AmbPressStart = testRsltQ2ac.AmbPressStart;
tstRslt.AmbPressEnd = testRsltQ2ac.AmbPressEnd;
tstRslt.AmbPressMin = testRsltQ2ac.AmbPressMin;
tstRslt.AmbPressMax = testRsltQ2ac.AmbPressMax;
tstRslt.AmbHumiMean = testRsltQ2ac.AmbHumiMean;
tstRslt.AmbHumiStart = testRsltQ2ac.AmbHumiStart;
tstRslt.AmbHumiEnd = testRsltQ2ac.AmbHumiEnd;
tstRslt.AmbHumiMin = testRsltQ2ac.AmbHumiMin;
tstRslt.AmbHumiMax = testRsltQ2ac.AmbHumiMax;
tstRslt.PressUpMean = testRsltQ2ac.PressUpMean;
tstRslt.PressUpStart = testRsltQ2ac.PressUpStart;
tstRslt.PressUpEnd = testRsltQ2ac.PressUpEnd;
tstRslt.PressUpMin = testRsltQ2ac.PressUpMin;
tstRslt.PressUpMax = testRsltQ2ac.PressUpMax;
tstRslt.PressDownMean = testRsltQ2ac.PressDownMean;
tstRslt.PressDownStart = testRsltQ2ac.PressDownStart;
tstRslt.PressDownEnd = testRsltQ2ac.PressDownEnd;
tstRslt.PressDownMin = testRsltQ2ac.PressDownMin;
tstRslt.PressDownMax = testRsltQ2ac.PressDownMax;
tstRslt.PressDeltaMean = testRsltQ2ac.PressDeltaMean;
tstRslt.PressDeltaStart = testRsltQ2ac.PressDeltaStart;
tstRslt.PressDeltaEnd = testRsltQ2ac.PressDeltaEnd;
tstRslt.PressDeltaMin = testRsltQ2ac.PressDeltaMin;
tstRslt.PressDeltaMax = testRsltQ2ac.PressDeltaMax;
tstRslt.ConductMean = testRsltQ2ac.ConductMean;
tstRslt.ConductStart = testRsltQ2ac.ConductStart;
tstRslt.ConductEnd = testRsltQ2ac.ConductEnd;
tstRslt.ConductMin = testRsltQ2ac.ConductMin;
tstRslt.ConductMax = testRsltQ2ac.ConductMax;
tstRslt.TempUpMean = testRsltQ2ac.TempUpMean;
tstRslt.TempUpStart = testRsltQ2ac.TempUpStart;
tstRslt.TempUpEnd = testRsltQ2ac.TempUpEnd;
tstRslt.TempUpMin = testRsltQ2ac.TempUpMin;
tstRslt.TempUpMax = testRsltQ2ac.TempUpMax;
tstRslt.TempDownMean = testRsltQ2ac.TempDownMean;
tstRslt.TempDownStart = testRsltQ2ac.TempDownStart;
tstRslt.TempDownEnd = testRsltQ2ac.TempDownEnd;
tstRslt.TempDownMin = testRsltQ2ac.TempDownMin;
tstRslt.TempDownMax = testRsltQ2ac.TempDownMax;
tstRslt.TempDivMean = testRsltQ2ac.TempDivMean;
tstRslt.TempDivStart = testRsltQ2ac.TempDivStart;
tstRslt.TempDivEnd = testRsltQ2ac.TempDivEnd;
tstRslt.TempDivMin = testRsltQ2ac.TempDivMin;
tstRslt.TempDivMax = testRsltQ2ac.TempDivMax;
tstRslt.DensityIn = testRsltQ2ac.DensityIn;
tstRslt.DensityLine = testRsltQ2ac.DensityLine;
tstRslt.DensityDiv = testRsltQ2ac.DensityDiv;
tstRslt.StartTime = testRsltQ2ac.StartTime;
tstRslt.EndTime = testRsltQ2ac.EndTime;
tstRslt.FlowSetTime = testRsltQ2ac.FlowSetTime;
tstRslt.TestTime = testRsltQ2ac.TestTime;
tstRslt.PulsesMaster = testRsltQ2ac.PulsesMaster;
tstRslt.ConstMasterRaw = testRsltQ2ac.ConstMasterRaw;
tstRslt.ConstMaster = testRsltQ2ac.ConstMaster;
tstRslt.MassStartRaw = testRsltQ2ac.MassStartRaw;
tstRslt.MassStart = testRsltQ2ac.MassStart;
tstRslt.MassEndRaw = testRsltQ2ac.MassEndRaw;
tstRslt.MassEnd = testRsltQ2ac.MassEnd;
tstRslt.MassOfEvapWater = testRsltQ2ac.MassOfEvapWater;
//tstRslt.FlowMass = testRsltQ2ac.FlowMass;
//tstRslt.FlowVolume = testRsltQ2ac.FlowVolume;
tstRslt.VolumeCTV = testRsltQ2ac.VolumeCTV;
tstRslt.VolumeMaster = testRsltQ2ac.VolumeMaster;
tstRslt.ErrorMaster = testRsltQ2ac.ErrorMaster;
tstRslt.FlowMean = testRsltQ2ac.FlowMean;
tstRslt.FlowMin = testRsltQ2ac.FlowMin;
tstRslt.FlowMax = testRsltQ2ac.FlowMax;
tstRslt.Custom1 = testRsltQ2ac.Custom1;
tstRslt.Custom2 = testRsltQ2ac.Custom2;
tstRslt.Custom3 = testRsltQ2ac.Custom3;
tstRslt.Custom4 = testRsltQ2ac.Custom4;
tstRslt.Custom5 = testRsltQ2ac.Custom5;
tstRslt.Custom6 = testRsltQ2ac.Custom6;
tstRslt.Custom7 = testRsltQ2ac.Custom7;
tstRslt.Custom8 = testRsltQ2ac.Custom8;
for (int i = 0; i < ProcessData.BatchRslts.WMPositionsCount; i++)
{
Results.Entities.MeterTestRslt meterRslt = ProcessData.BatchRslts.GetMeterTestRslt(testName, i, CompoundMeterId.Single);
Results.Entities.MeterTestRslt meterRsltQ2bc = ProcessData.BatchRslts.GetMeterTestRslt(testNameQ2bc, i, CompoundMeterId.Single);
Results.Entities.MeterTestRslt meterRsltQ2ac = ProcessData.BatchRslts.GetMeterTestRslt(testNameQ2ac, i, CompoundMeterId.Single);
if ((meterRslt != null) && (meterRsltQ2bc != null) && (meterRsltQ2ac != null))
{
meterRslt.PulsesMeter = meterRsltQ2ac.PulsesMeter;
meterRslt.PulsesMaster = meterRsltQ2ac.PulsesMaster;
meterRslt.PulsesPerLiter = meterRsltQ2ac.PulsesPerLiter;
meterRslt.VolumeRef = meterRsltQ2ac.VolumeRef;
meterRslt.TestTime = meterRsltQ2ac.TestTime;
meterRslt.VolumeStart = meterRsltQ2ac.VolumeStart;
meterRslt.VolumeEnd = meterRsltQ2ac.VolumeEnd;
meterRslt.VolumeMeter = meterRsltQ2ac.VolumeMeter;
meterRslt.Error = meterRsltQ2ac.Error;
meterRslt.TestDone = meterRsltQ2ac.TestDone;
tstRslt.TestDone = true;
if (((meterRsltQ2bc.Error < -0.51) && (meterRsltQ2ac.Error < meterRsltQ2bc.Error)) ||
((meterRsltQ2bc.Error > +0.51) && (meterRsltQ2ac.Error > meterRsltQ2bc.Error)))
{
meterRslt.Passed = false; /// Q2 correction check failed
}
else
{
meterRslt.Passed = true; /// Q2 correction check passed
}
}
}
}
}
}
@@ -158,6 +158,22 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
continue;
}
if (smartHead is RegisterReaders.iPerlASICReader.implementations.SmartReader smartReaderASIC )
{
if (correctionsList.Any(x => x is IPerlASICCorrections))
continue;
//(SmartCommunicationForm parent, ISmartTestMethod testMethod, ITestMethodCfg cfg, IList<Config.Entities.Test> tests, IList<ITestParams> multiTestParams)
correctionsList.Add(new IPerlASICCorrections(this,/*log, rfidDataLogger,*/ componentBase, cfg, tests, multiTestParams));
continue;
}
if (smartHead is RegisterReaders.IPerlReader.implementations.SmartReader smartiPerlReader )
{
if (correctionsList.Any(x => x is IPerlCorrections))
continue;
correctionsList.Add(new IPerlCorrections( this,componentBase, cfg, tests, multiTestParams));
continue;
}
throw new Exception("Unknown smart head type");
}
catch (Exception e)
@@ -325,11 +341,11 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
{
checkBoxesEditMode = false;
ITestMethodCfg cfg = (componentBase as ITestMethodCfg);
ITestMethodCfg cfg = (componentBase.Cfg as ITestMethodCfg);
ISmartTestMethod smartTestMethod = componentBase as ISmartTestMethod;
//TODO get corrections based on defined meter
_corrections = GetNewCorrectionList(smartTestMethod, smartTestMethod.TestMethodCfg, tests, multiTestParams);
_corrections = GetNewCorrectionList(smartTestMethod, cfg, tests, multiTestParams);
InitializeMeterTypeItems();
UpdateHeads();
@@ -410,7 +426,10 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
if (waterMeterPositions0 == null) waterMeterPositions0 = new List<int>();
//iperlHeads = ProcessData.SmartHeadsUni;
string comparedTypeReader = SelectedTypeReader;
if (string.IsNullOrEmpty(SelectedTypeReader))
//TODO BUMI solve problem with init first
//ProcessData.SmartHeadsUni?.ForEach( head => iperlHeads.Add(head));
if (string.IsNullOrEmpty(SelectedTypeReader) )
{
comparedTypeReader = ProcessData.SmartHeadsUni?.First()?.GetType().Name;
}
@@ -418,12 +437,13 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
ICorrections corre = null;
foreach (ICorrections correction in _corrections)
{
if (correction.TypeIdentificatorName() == SelectedTypeReader)
if (correction.TypeIdentificatorName() == comparedTypeReader)
{
corre = correction;
break;
}
}
if (corre == null) corre = _corrections.First();
if (corre != null)
{
@@ -700,7 +720,11 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
//currentGroup++;
int wtId = 0;
foreach (var wt in Correction.GetAllThreads())
var threads = Correction?.GetAllThreads();
if (threads == null)
return;
foreach (var wt in threads)
{
wt.Start(new Boxes.IntBox(wtId++)); /// Start worker threads !!!
}
@@ -860,6 +884,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
checkBoxImage41, checkBoxImage42, checkBoxImage43, checkBoxImage44, checkBoxImage45,
checkBoxImage46, checkBoxImage47, checkBoxImage48,
};
if (ckbIndex == null) return;
for (int i = 0; i < 48; i++)
{
int wmNr0 = ckbIndex[i];
@@ -15,11 +15,15 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication
public class iPerlCommunicationParams : TestParamsBase, IParamsProvider, ITestParams
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(iPerlCommunicationParams) })[0];
public override XmlSerializer GetSerializer() { return Serializer; }
public string Activity; /// Communication activity
public bool SimultWithPrevious;
public bool SimultWithNext;
public override XmlSerializer GetSerializer()
{
return Serializer;
}
public override void InitializeAll()
{
@@ -384,7 +384,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
int[] ckbIndex,
bool[] ckbState, IList<ISmartReader> iperlHeads, int textBoxesCount, bool checkBoxesEditMode)
{
if (iperlHeads == null)
if (iperlHeads == null || iperlHeads.Count == 0)
{
for (int i = 0; i < textBoxesCount; i++)
{
@@ -401,7 +401,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
{
labels[i].Visible = counters[i].Visible = messages[i].Visible = checkBoxes[i].Visible = true;
IperlHead iperlHead = iperlHeads[i] as IperlHead;
if (!checkBoxesEditMode && (iperlHead == null || iperlHead.Disabled))
{
@@ -416,6 +416,7 @@ namespace TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.implementations
checkBoxes[i].Enabled = checkBoxes[i].Checked = ckbState[i] = true;
messages[i].Text = "---";
}
}
}
+25 -11
View File
@@ -1254,6 +1254,7 @@
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\diagnosticLed\DiagnosticLedParser.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\diagnosticLed\DiagnosticLedState.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\diagnosticLed\parserer\DiagnosticLedData.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\diagnosticLed\parserer\DiagnosticLedFrameSpec.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\diagnosticLed\parserer\DiagnosticLedState1Data.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\diagnosticLed\parserer\DiagnosticLedState2Data.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\diagnosticLed\parserer\DiagnosticLedState3Data.cs" />
@@ -1266,21 +1267,36 @@
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\diagnosticLed\utils\DiagnosticChecksum.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\diagnosticLed\utils\DiagnosticHex.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\hexLogger\HexFormatter.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\hexLogger\IpelHatCommandDecoder.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\hexLogger\IperlHatLogger.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\hexLogger\TouchReadControlDecoder.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\hexLogger\TouchReadLogger.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\IperlHatProtocol\Constants.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\IperlHatProtocol\IperlHatFrame.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\IperlHatProtocol\IperlHatFrameBuilder.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\IperlHatProtocol\IperlHatFrameParser.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\IperlHatProtocol\IperlHatProtocol.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\IperlHatProtocol\IperlHatResponse.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\led\ITouchReadLedParser.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\led\ShortVariableLedParser.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\led\TouchReadLedData.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\led\TouchReadLedMessage.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\TouchReadCommand.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\TouchReadDeviceSubCommand.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\TouchReadFrame.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\TouchReadFrameBuilder.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\TouchReadFrameParser.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\TouchReadProtocol.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\TouchReadResponse.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\protocolCommons\ProtocolCommand.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\protocolCommons\ProtocolDeviceSubCommand.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\protocolCommons\ProtocolStatuses.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadFrame.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadFrameBuilder.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadFrameParser.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadProtocol.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadResponse.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\OpthoHeadService.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\OptoHeadTest.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\RadioService.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\Utils\SerialDriverBuilder.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\Utils\SerialDriver.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\Factory.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\implementations\IPerlASICImplHeadTestCtrl.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\implementations\OptoTelegramRaw.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\implementations\SmartReader.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\IPerlCfg.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\IPerlUniCfgCtrl.cs">
@@ -1663,8 +1679,10 @@
</Compile>
<Compile Include="Rig\TestMethods\SmartMeterFlyingStartMassCollection\Factory.cs" />
<Compile Include="Rig\TestMethods\SmartMeterFlyingStartMassCollection\TestMethodCfg.cs" />
<Compile Include="Rig\TestMethods\SmartTest\iPerlCommunicationSeq.cs" />
<Compile Include="Rig\TestMethods\SmartTest\SequenceConditionOp.cs" />
<Compile Include="Rig\TestMethods\SmartTest\TestMethod.cs" />
<Compile Include="Rig\TestMethods\SmartTest\TestMethodCfg.cs" />
<Compile Include="Rig\TestMethods\SmartTest\TestMethodCfgCtrl.cs">
<SubType>UserControl</SubType>
</Compile>
@@ -4052,10 +4070,6 @@
<Project>{0C0A1F4D-1363-4544-A7C5-196C76D26CCA}</Project>
<Name>GraphLib</Name>
</ProjectReference>
<ProjectReference Include="..\NfcC7_DLL\NfcC7_DLL.csproj">
<Project>{53e75979-b530-4805-8fc9-b314f14a62be}</Project>
<Name>NfcC7_DLL</Name>
</ProjectReference>
<ProjectReference Include="..\RestClient\RestClient.csproj">
<Project>{46e3b0e1-209f-4550-b0dd-d7e2c039b3ce}</Project>
<Name>RestClient</Name>
@@ -0,0 +1,179 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.protocolCommons;
namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol
{
[TestClass]
public class IperlHatFrameBuilderTests
{
[TestMethod]
public void Encode_ViewFactoryId_Command()
{
byte[] frame = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.ViewFactoryId)
.BuildBytes();
byte[] expected =
{
Constants.Start, // START
Constants.Write, // DIRECTION
0x05, // LEN
(byte)ProtocolCommand.ViewFactoryId, // COMMAND
Constants.End // END
};
CollectionAssert.AreEqual(expected, frame);
string log = IperlHatLogger.DescribeTx(frame);
Console.WriteLine(log);
Console.WriteLine(@"Raw: < {0} >", HexFormatter.ToSerialHex(frame));
}
[TestMethod]
public void Encode_VERS_Command()
{
byte[] frame = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.Question)
.AddPayload(Constants.Version)
.BuildBytes();
byte[] expected = HexFormatter.HexStringToByteArray("53 57 3F 76 65 72 73 0D");
CollectionAssert.AreEqual(expected, frame);
string log = IperlHatLogger.DescribeTx(frame);
Console.WriteLine(log);
Console.WriteLine(@"Raw: < {0} >", HexFormatter.ToSerialHex(frame));
}
[TestMethod]
public void Encode_VERS2_Command()
{
byte[] frame = new IperlHatFrameBuilder()
.RequestResponse(true)
.SetVersionCommand()
.BuildBytes();
byte[] expected = HexFormatter.HexStringToByteArray("53 57 3F 76 65 72 73 0D");
CollectionAssert.AreEqual(expected, frame);
string log = IperlHatLogger.DescribeTx(frame);
Console.WriteLine(log);
Console.WriteLine(@"Raw: < {0} >", HexFormatter.ToSerialHex(frame));
}
[TestMethod]
public void Encode_ViewProgrammableId_Command()
{
byte[] frame = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.ViewProgrammableId)
.BuildBytes();
byte[] expected = HexFormatter.HexStringToByteArray("53 57 05 03 0D");
CollectionAssert.AreEqual(expected, frame);
string log = IperlHatLogger.DescribeTx(frame);
Console.WriteLine(log);
Console.WriteLine(@"Raw: < {0} >", HexFormatter.ToSerialHex(frame));
}
[TestMethod]
public void Encode_ViewState_Command()
{
byte[] frame = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.ViewState)
.BuildBytes();
byte[] expected = HexFormatter.HexStringToByteArray("53 57 05 19 0D");
CollectionAssert.AreEqual(expected, frame);
string log = IperlHatLogger.DescribeTx(frame);
Console.WriteLine(log);
Console.WriteLine(@"Raw: < {0} >", HexFormatter.ToSerialHex(frame));
}
[TestMethod]
public void Encode_SetState_Idle_Command()
{
byte[] frame = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.SetState)
.AddSubCommand(ProtocolStatuses.Idle) // Idle
.BuildBytes();
byte[] expected = HexFormatter.HexStringToByteArray("53 57 06 1A 01 0D");
CollectionAssert.AreEqual(expected, frame);
string log = IperlHatLogger.DescribeTx(frame);
Console.WriteLine(log);
Console.WriteLine(@"Raw: < {0} >", HexFormatter.ToSerialHex(frame));
}
[TestMethod]
public void Encode_SetState_Active_Command()
{
byte[] frame = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.SetState)
.AddSubCommand(ProtocolStatuses.Active) // Active
.BuildBytes();
byte[] expected = HexFormatter.HexStringToByteArray("53 57 06 1A 02 0D");
CollectionAssert.AreEqual(expected, frame);
string log = IperlHatLogger.DescribeTx(frame);
Console.WriteLine(log);
Console.WriteLine(@"Raw: < {0} >", HexFormatter.ToSerialHex(frame));
}
[TestMethod]
public void Encode_SetDiagnosticLEDState_Status4_Command()
{
// ----------- Arrange -----------
byte[] request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddDeviceCommand(ProtocolDeviceSubCommand.SetDiagnosticLEDState)
.AddPayload(DiagnosticLedState.State4)
.BuildBytes();
byte[] expected = HexFormatter.HexStringToByteArray("53 57 07 FD 60 04 0D");
CollectionAssert.AreEqual(expected, request);
string log = IperlHatLogger.DescribeTx(request);
Console.WriteLine(log);
Console.WriteLine(@"Raw: < {0} >", HexFormatter.ToSerialHex(request));
}
[TestMethod]
public void AddDiagnosticLedState()
{
// ----------- Arrange -----------
byte[] request = new IperlHatFrameBuilder()
.AddDiagnosticLedState(DiagnosticLedState.State4)
.BuildBytes();
byte[] expected = HexFormatter.HexStringToByteArray("53 57 07 FD 60 04 0D");
CollectionAssert.AreEqual(expected, request);
string log = IperlHatLogger.DescribeTx(request);
Console.WriteLine(log);
Console.WriteLine(@"Raw: < {0} >", HexFormatter.ToSerialHex(request));
}
}
}
@@ -0,0 +1,65 @@
using JetBrains.Annotations;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol;
namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol
{
[TestClass]
[TestSubject(typeof(IperlHatFrameParser))]
public class IperlHatFrameParserTest
{
[TestMethod]
public void ParseTest_FactoryID()
{
IperlHatFrameParser parser = new IperlHatFrameParser();
byte[] result1 = HexFormatter.HexStringToByteArray("53 52 0F 01 31 30 30 31 30 34 30 35 31 00 0D");
IperlHatResponse iperlHatResponse = parser.Parse(result1);
Assert.IsNotNull(iperlHatResponse);
Assert.IsTrue(iperlHatResponse.IsOk);
Assert.IsTrue(iperlHatResponse.Payload.Length > 0);
Assert.AreEqual("100104051", iperlHatResponse.GetAsciiPayload());
}
[TestMethod]
public void ParseTest_VERS()
{
IperlHatFrameParser parser = new IperlHatFrameParser();
byte[] versionResult = HexFormatter.HexStringToByteArray("3F 76 65 72 73 3A 20 48 61 72 72 79 20 54 3A 42 38 30 30 2C 20 56 3A 30 36 2E 30 36 2E 30 31 2C 20 46 57 3A 31 39 30 32 31 35 2C 20 37 45 43 45 2C 20 42 31 2E 36 2E 30 31 2C 20 48 57 3A 34 2C 20 53 65 72 69 61 6C 3A 30 0D");
IperlHatResponse iperlHatResponse = parser.Parse(versionResult);
Assert.IsNotNull(iperlHatResponse);
Assert.IsTrue(iperlHatResponse.IsOk);
Assert.IsTrue(iperlHatResponse.Payload.Length > 0);
Assert.AreEqual(Constants.Question, iperlHatResponse.Control);
Assert.AreEqual("vers: Harry T:B800, V:06.06.01, FW:190215, 7ECE, B1.6.01, HW:4, Serial:0", iperlHatResponse.GetAsciiPayload());
}
[TestMethod]
public void ParseTest_Response_OK ()
{
IperlHatFrameParser parser = new IperlHatFrameParser();
byte[] versionResult = HexFormatter.HexStringToByteArray("53 52 05 01 0D");
IperlHatResponse iperlHatResponse = parser.Parse(versionResult);
Assert.IsNotNull(iperlHatResponse);
Assert.IsTrue(iperlHatResponse.IsOk);
Assert.AreEqual(null, iperlHatResponse.GetAsciiPayload());
}
[TestMethod]
public void ParseTest_Response_NOK ()
{
IperlHatFrameParser parser = new IperlHatFrameParser();
byte[] versionResult = HexFormatter.HexStringToByteArray("53 52 05 FD 0D");
IperlHatResponse iperlHatResponse = parser.Parse(versionResult);
Assert.IsNotNull(iperlHatResponse);
Assert.IsFalse(iperlHatResponse.IsOk);
Assert.AreEqual(null, iperlHatResponse.GetAsciiPayload());
}
}
}
@@ -0,0 +1,510 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.diagnosticLed.parserer;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.protocolCommons;
namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol
{
[TestClass]
public class IperlHatIntegrationTests
{
private const string ComPort = "COM3"; // CHANGE THIS
private const int BaudRate = 2400;
private const int BaudRateOpto = 38400;
private const int ReadTimeoutMs = 2000;
[TestMethod]
[TestCategory("Hardware")]
[TestCategory("Serial")]
public void Serial_ViewFactoryId_ReadSerialNumber()
{
// -------- Arrange --------
byte[] request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.ViewFactoryId)
.BuildBytes();
var parser = new IperlHatFrameParser();
using (var port = new SerialPort(ComPort, BaudRate, Parity.None, 8, StopBits.One))
{
port.Handshake = Handshake.None;
port.ReadTimeout = 5000;
port.Open();
DateTime end = DateTime.Now.AddSeconds(10);
Console.WriteLine("TX → " + HexFormatter.ToSerialHexWithAscii(request));
port.Write(request, 0, request.Length);
Console.WriteLine("Listening for 10 seconds...");
byte[] response = null;
while (DateTime.Now < end)
{
try
{
response = ReadResponse(port);
Console.WriteLine("RX ← " + HexFormatter.ToSerialHexWithAscii(response));
break;
}
catch (TimeoutException e)
{
Assert.Fail("Timeout: ", e);
}
}
IperlHatResponse iperlHatResponse = parser.Parse(response);
Assert.IsTrue(iperlHatResponse.IsOk);
Assert.IsFalse(iperlHatResponse.Payload.Length < 8);
Console.WriteLine("\nDone.");
}
}
[TestMethod]
[TestCategory("Hardware")]
[TestCategory("Serial")]
public void Serial_SetStatus_Idle_Active()
{
byte[] requestStatus = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.ViewState)
.BuildBytes();
var parser = new IperlHatFrameParser();
using (var port = new SerialPort(ComPort, BaudRate, Parity.None, 8, StopBits.One))
{
port.Handshake = Handshake.None;
port.ReadTimeout = 5000;
port.Open();
DateTime end = DateTime.Now.AddSeconds(10);
Console.WriteLine("TX → " + HexFormatter.ToSerialHexWithAscii(requestStatus));
port.Write(requestStatus, 0, requestStatus.Length);
Console.WriteLine("Listening for 10 seconds...");
byte[] resStart = null;
while (DateTime.Now < end)
{
try
{
resStart = ReadResponse(port);
Console.WriteLine("RX ← " + HexFormatter.ToSerialHexWithAscii(resStart));
break;
}
catch (TimeoutException e)
{
Assert.Fail("Timeout: ", e);
}
}
IperlHatResponse iperlHatResponseOld = parser.Parse(resStart);
Assert.IsTrue(iperlHatResponseOld.IsOk, "Idle No set!");
Console.WriteLine("We start with status: " + HexFormatter.ToHex(iperlHatResponseOld.Payload[0]));
//----------------------------------------------------------------
// -------- Arrange --------
byte[] request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.SetState)
.AddSubCommand(ProtocolStatuses.Idle)
.BuildBytes();
// -- set idle
end = DateTime.Now.AddSeconds(10);
Console.WriteLine("TX → " + HexFormatter.ToSerialHexWithAscii(request));
port.Write(request, 0, request.Length);
Console.WriteLine("Listening for 10 seconds...");
byte[] response = null;
while (DateTime.Now < end)
{
try
{
response = ReadResponse(port);
Console.WriteLine("RX ← " + HexFormatter.ToSerialHexWithAscii(response));
break;
}
catch (TimeoutException e)
{
Assert.Fail("Timeout: ", e);
}
}
IperlHatResponse iperlHatResponse = parser.Parse(response);
Assert.IsTrue(iperlHatResponse.IsOk, "Idle No set!");
//----------------------------------------------------------------
request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.SetState)
.AddSubCommand(ProtocolStatuses.Active)
.BuildBytes();
end = DateTime.Now.AddSeconds(10);
Console.WriteLine("TX → " + HexFormatter.ToSerialHexWithAscii(request));
port.Write(request, 0, request.Length);
Console.WriteLine("Listening for 10 seconds...");
response = null;
while (DateTime.Now < end)
{
try
{
response = ReadResponse(port);
Console.WriteLine("RX ← " + HexFormatter.ToSerialHexWithAscii(response));
break;
}
catch (TimeoutException e)
{
Assert.Fail("Timeout: ", e);
}
}
iperlHatResponse = parser.Parse(response);
Assert.IsTrue(iperlHatResponse.IsOk, "Idle No set!");
//--------------------------------------------------------------------
Console.WriteLine("\nDone.");
}
}
[TestMethod]
[TestCategory("Hardware")]
public void Serial_RawSniff()
{
using (var port = new SerialPort(ComPort, BaudRate, Parity.None, 8, StopBits.One))
{
port.Handshake = Handshake.None;
port.ReadTimeout = 5000;
port.Open();
DateTime end = DateTime.Now.AddSeconds(10);
//welcome message
byte[] frame = HexFormatter.HexStringToByteArray("53 57 3F 76 65 72 73 0D");
Console.WriteLine("TX → " + HexFormatter.ToSerialHexWithAscii(frame));
port.Write(frame, 0, frame.Length);
Console.WriteLine("Listening for 10 seconds...");
while (DateTime.Now < end)
{
try
{
byte[] response = ReadResponse(port);
Console.WriteLine("RX ← " + HexFormatter.ToSerialHexWithAscii(response));
break;
}
catch (TimeoutException e)
{
Assert.Fail("Timeout: ",e);
}
}
end = DateTime.Now.AddSeconds(10);
byte[] frameID = HexFormatter.HexStringToByteArray("53 57 05 01 0D");
Console.WriteLine("TX → " + HexFormatter.ToSerialHexWithAscii(frameID));
port.Write(frameID, 0, frameID.Length);
Console.WriteLine("Listening for 10 seconds...");
while (DateTime.Now < end)
{
try
{
byte[] response = ReadResponse(port);
Console.WriteLine("RX ← " + HexFormatter.ToSerialHexWithAscii(response));
break;
}
catch (TimeoutException e)
{
Assert.Fail("Timeout: ",e);
}
}
Console.WriteLine("\nDone.");
}
}
public static byte[] ReadResponse(SerialPort port)
{
var result = new List<byte>();
try
{
while (true)
{
int value = port.ReadByte(); // blocks until byte or timeout
if (value < 0)
throw new IOException("Serial port returned end of stream.");
byte b = HexFormatter.ToHexByte(value);
result.Add(b);
// stop when CR received
if (b == 0x0D)
break;
}
return result.ToArray();
}
catch (TimeoutException ex)
{
if (result.Count > 0)
return result.ToArray();
throw new TimeoutException("Timeout reading response from serial port.", ex);
}
}
[TestMethod]
[TestCategory("Hardware")]
public void OptoCommunicationON_ReadOpto_CommunicatonOFF()
{
//OPEN COMMUNICATION to Iperl Hat
var parser = new IperlHatFrameParser();
using (var port = new SerialPort(ComPort, BaudRate, Parity.None, 8, StopBits.One))
{
port.Handshake = Handshake.None;
port.ReadTimeout = 5000;
port.Open();
//----------------------------------------------------------------
// ------ Set active mode ------
//----------------------------------------------------------------
byte[] requestStatus = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.SetState)
.AddSubCommand(ProtocolStatuses.Active)
.BuildBytes();
DateTime end = DateTime.Now.AddSeconds(10);
Console.WriteLine("TX → " + HexFormatter.ToSerialHexWithAscii(requestStatus));
port.Write(requestStatus, 0, requestStatus.Length);
Console.WriteLine("Set active mode - Listening for 10 seconds...");
byte[] resStart = null;
while (DateTime.Now < end)
{
try
{
resStart = ReadResponse(port);
Console.WriteLine("RX ← " + HexFormatter.ToSerialHexWithAscii(resStart));
break;
}
catch (TimeoutException e)
{
Assert.Fail("Timeout: ", e);
}
}
IperlHatResponse iperlHatResponseOld = parser.Parse(resStart);
Assert.IsTrue(iperlHatResponseOld.IsOk, "Active No set!");
//----------------------------------------------------------------
// ------ Enable Opto data ------
//----------------------------------------------------------------
// ----------- Arrange -----------
byte[] request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddDeviceCommand(ProtocolDeviceSubCommand.SetDiagnosticLEDState)
.AddPayload(DiagnosticLedState.State4)
.BuildBytes();
end = DateTime.Now.AddSeconds(10);
Console.WriteLine("TX → " + HexFormatter.ToSerialHexWithAscii(request));
port.Write(request, 0, request.Length);
Console.WriteLine("Enable Opto data - Listening for 10 seconds...");
byte[] response = null;
while (DateTime.Now < end)
{
try
{
response = ReadResponse(port);
Console.WriteLine("RX ← " + HexFormatter.ToSerialHexWithAscii(response));
break;
}
catch (TimeoutException e)
{
Assert.Fail("Timeout: ", e);
}
}
IperlHatResponse iperlHatResponse = parser.Parse(response);
Assert.IsTrue(iperlHatResponse.IsOk, "Opto data not set!");
//----------------------------------------------------------------
// ------ Test Opto data ------
//----------------------------------------------------------------
//Now try test opto data
Serial_OptoRawSniff();
//----------------------------------------------------------------
// ------ Disable Opto data ------
//----------------------------------------------------------------
// ----------- Arrange -----------
request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddDeviceCommand(ProtocolDeviceSubCommand.SetDiagnosticLEDState)
.AddPayload(DiagnosticLedState.StateOFF)
.BuildBytes();
end = DateTime.Now.AddSeconds(10);
Console.WriteLine("TX → " + HexFormatter.ToSerialHexWithAscii(request));
port.Write(request, 0, request.Length);
Console.WriteLine("Disable Opto data - Listening for 10 seconds...");
response = null;
while (DateTime.Now < end)
{
try
{
response = ReadResponse(port);
Console.WriteLine("RX ← " + HexFormatter.ToSerialHexWithAscii(response));
break;
}
catch (TimeoutException e)
{
Assert.Fail("Timeout: ", e);
}
}
iperlHatResponse = parser.Parse(response);
Assert.IsTrue(iperlHatResponse.IsOk, "Disabled opto LED not set!");
//----------------------------------------------------------------
// ------ Set Idle mode ------
//----------------------------------------------------------------
// ----------- Arrange -----------
request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.SetState)
.AddSubCommand(ProtocolStatuses.Idle)
.BuildBytes();
end = DateTime.Now.AddSeconds(10);
Console.WriteLine("TX → " + HexFormatter.ToSerialHexWithAscii(request));
port.Write(request, 0, request.Length);
Console.WriteLine("Set Idle mode - Listening for 10 seconds...");
response = null;
while (DateTime.Now < end)
{
try
{
response = ReadResponse(port);
Console.WriteLine("RX ← " + HexFormatter.ToSerialHexWithAscii(response));
break;
}
catch (TimeoutException e)
{
Assert.Fail("Timeout: ", e);
}
}
iperlHatResponse = parser.Parse(response);
Assert.IsTrue(iperlHatResponse.IsOk, "Idle status not set!");
//----------------------------------------------------------------
// ------ Test finished ------
//----------------------------------------------------------------
Console.WriteLine("\nDone.");
}
}
/// <summary>
/// This test work only if Opto data are active
/// Use OptoCommunicationON_ReadOpto_CommunicatonOFF() test method
/// </summary>
[TestMethod]
[TestCategory("Hardware")]
public void Serial_OptoRawSniff_Standalone()
{
Serial_OptoRawSniff();
}
//method test connection to optho head
private void Serial_OptoRawSniff()
{
using (var port = new SerialPort("COM4", BaudRateOpto, Parity.None, 8, StopBits.One))
{
port.Handshake = Handshake.None;
port.ReadTimeout = 10000;
// 🔑 CRLF handling
port.NewLine = "\r\n";
port.Encoding = Encoding.ASCII; // or UTF8 if needed
port.Open();
Console.WriteLine("Listening for 10 seconds...");
DateTime end = DateTime.Now.AddSeconds(10);
var parser = new DiagnosticLedParser(DiagnosticLedState.State4);
while (DateTime.Now < end)
{
try
{
string line = port.ReadLine(); // string
byte[] bytes = port.Encoding.GetBytes(line);
Console.WriteLine("RX ← " + HexFormatter.ToSerialHex(bytes));
try
{
var data = (DiagnosticLedState4Data)parser.ParseLine(line,false);
Console.WriteLine("Parsed: " + data);
}catch(Exception e)
{
Console.WriteLine("Failed to parse: " + e.Message);
}
}
catch (TimeoutException)
{
Assert.Fail("Serial read timeout");
}
}
Console.WriteLine("\nDone.");
}
}
}
}
@@ -1,123 +0,0 @@
using System;
using System.IO.Ports;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger;
namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4
{
[TestClass]
public class TouchReadSerialIntegrationTests
{
private const string ComPort = "COM3"; // CHANGE THIS
private const int BaudRate = 9600;//38400;//115200;//9600; // VERIFY FROM METER DOC
private const int ReadTimeoutMs = 2000;
[TestMethod]
[TestCategory("Hardware")]
[TestCategory("Serial")]
public void Serial_ViewFactoryId_ReadSerialNumber()
{
// -------- Arrange --------
byte[] request = new TouchReadFrameBuilder()
.RequestResponse(true)
.AddCommand(TouchReadCommand.ViewFactoryId)
.BuildBytes();
var parser = new TouchReadFrameParser();
using (var port = new SerialPort(ComPort, BaudRate, Parity.None, 8, StopBits.One))
{
port.ReadTimeout = ReadTimeoutMs;
port.WriteTimeout = 500;
port.Open();
// Flush buffers
port.DiscardInBuffer();
port.DiscardOutBuffer();
// -------- Act --------
port.Write(request, 0, request.Length);
Console.WriteLine("TX → " + HexFormatter.ToSerialHex(request));
byte[] response = ReadFullFrame(port);
Console.WriteLine("RX ← " + HexFormatter.ToSerialHex(response));
TouchReadResponse decoded = parser.Parse(response);
// -------- Assert --------
Assert.AreEqual(0x01, decoded.Status, "Meter returned error status");
string serialNumber = decoded.GetAsciiPayload();
Assert.IsFalse(string.IsNullOrEmpty(serialNumber),
"Factory ID (serial number) is empty");
Console.WriteLine("Meter Factory ID: " + serialNumber);
}
}
/// <summary>
/// Reads a full TouchRead frame from the serial port.
/// Blocks until complete frame or timeout.
/// </summary>
private static byte[] ReadFullFrame(SerialPort port)
{
// Read START + LEN first
byte start = (byte)port.ReadByte();
if (start != 0x0D)
throw new InvalidOperationException("Invalid START byte from meter");
byte length = (byte)port.ReadByte();
// LEN counts from CONTROL to CHECKSUM
int remaining = length;
byte[] buffer = new byte[2 + remaining];
buffer[0] = start;
buffer[1] = length;
int offset = 2;
while (remaining > 0)
{
int read = port.Read(buffer, offset, remaining);
offset += read;
remaining -= read;
}
return buffer;
}
[TestMethod]
[TestCategory("Hardware")]
public void Serial_RawSniff()
{
using (var port = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One))
{
port.ReadTimeout = 500;
port.Open();
Console.WriteLine("Listening for 5 seconds...");
DateTime end = DateTime.Now.AddSeconds(5);
while (DateTime.Now < end)
{
try
{
int b = port.ReadByte();
Console.Write($"{b:X2} ");
}
catch (TimeoutException)
{
}
}
Console.WriteLine("\nDone.");
}
}
}
}
@@ -0,0 +1,162 @@
using System;
using System.Text;
using JetBrains.Annotations;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger;
namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger
{
[TestClass]
[TestSubject(typeof(HexFormatter))]
public class HexFormatterTest
{
// -----------------------------
// ToHex(byte)
// -----------------------------
[TestMethod]
public void ToHex_FormatsSingleByte()
{
var result = HexFormatter.ToHex(0x0D);
Assert.AreEqual("0x0D", result);
}
// -----------------------------
// ToHex(byte[])
// -----------------------------
[TestMethod]
public void ToHex_FormatsByteArray()
{
byte[] data = { 0x01, 0x0D, 0xFF };
var result = HexFormatter.ToHex(data);
Assert.AreEqual("0x01 0x0D 0xFF", result);
}
[TestMethod]
public void ToHex_EmptyArray_ReturnsEmptyMarker()
{
var result = HexFormatter.ToHex(Array.Empty<byte>());
Assert.AreEqual("<empty>", result);
}
[TestMethod]
public void ToHex_Null_ReturnsEmptyMarker()
{
var result = HexFormatter.ToHex(null);
Assert.AreEqual("<empty>", result);
}
// -----------------------------
// ToSerialHex(byte[])
// -----------------------------
[TestMethod]
public void ToSerialHex_FormatsCorrectly()
{
byte[] data = { 0x0D, 0x04, 0x08, 0x01, 0x00, 0x1A };
var result = HexFormatter.ToSerialHex(data);
Assert.AreEqual("0D 04 08 01 00 1A", result);
}
[TestMethod]
public void ToSerialHex_Empty_ReturnsEmptyString()
{
var result = HexFormatter.ToSerialHex(Array.Empty<byte>());
Assert.AreEqual(string.Empty, result);
}
// -----------------------------
// ToHexWithAscii(byte)
// -----------------------------
[TestMethod]
public void ToHexWithAscii_PrintableAscii()
{
var result = HexFormatter.ToHexWithAscii(0x41); // 'A'
Assert.AreEqual("0x41 ('A')", result);
}
[TestMethod]
public void ToHexWithAscii_ControlChar()
{
var result = HexFormatter.ToHexWithAscii(0x0D); // CR
Assert.AreEqual("0x0D ('.')", result);
}
// -----------------------------
// ToSerialHexWithAscii(byte[])
// -----------------------------
[TestMethod]
public void ToSerialHexWithAscii_MixedData()
{
byte[] data = Encoding.ASCII.GetBytes("OK\r\n");
var result = HexFormatter.ToSerialHexWithAscii(data);
Assert.AreEqual("4F 4B 0D 0A | OK..", result);
}
// -----------------------------
// IntToBytesBE
// -----------------------------
[TestMethod]
public void IntToBytesBE_TwoBytes()
{
var result = HexFormatter.IntToBytesBE(0x1234, 2);
CollectionAssert.AreEqual(
new byte[] { 0x12, 0x34 },
result
);
}
// -----------------------------
// IntToBytesLE
// -----------------------------
[TestMethod]
public void IntToBytesLE_TwoBytes()
{
var result = HexFormatter.IntToBytesLE(0x1234, 2);
CollectionAssert.AreEqual(
new byte[] { 0x34, 0x12 },
result
);
}
// -----------------------------
// AsciiToBytes
// -----------------------------
[TestMethod]
public void AsciiToBytes_ConvertsString()
{
var result = HexFormatter.AsciiToBytes("AB");
CollectionAssert.AreEqual(
new byte[] { 0x41, 0x42 },
result
);
}
[TestMethod]
public void AsciiToBytes_EmptyString_ReturnsEmpty()
{
var result = HexFormatter.AsciiToBytes(string.Empty);
Assert.AreEqual(0, result.Length);
}
[TestMethod]
public void AsciiToBytes_Null_ReturnsEmpty()
{
var result = HexFormatter.AsciiToBytes(null);
Assert.AreEqual(0, result.Length);
}
}
}
@@ -1,10 +1,11 @@
using System;
using System.IO.Ports;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.protocolCommons;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.wiredProtocol;
namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4
namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.wiredProtocol
{
[TestClass]
public class TouchReadBaudRateDetectionTests
@@ -14,9 +15,9 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4
private static readonly int[] StandardBaudRates =
{
300, 600, 7812, 1200, 18432, 2400, 4800,
9600, 10400, 15625, 19200, 31250, 36864,
38400, 50000, 57600, 62500, 76800, 115200
115200, 9600//, 10400, 15625, 18432, 19200, 31250, 36864,
//38400, 50000, 57600, 62500, 76800,
//1200, 2400, 4800, 7812
};
[TestMethod]
@@ -26,7 +27,7 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4
{
byte[] request = new TouchReadFrameBuilder()
.RequestResponse(true)
.AddCommand(TouchReadCommand.ViewFactoryId)
.AddCommand(ProtocolCommand.Simple)
.BuildBytes();
var parser = new TouchReadFrameParser();
@@ -109,5 +110,14 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4
return buffer;
}
[TestMethod]
public void ConvertTest()
{
byte[] input = { 0x53, 0x57, 0x05, 0x01, 0x0D };
Console.WriteLine(HexFormatter.ToSerialHex(input));
Console.WriteLine(HexFormatter.ToHex(input));
}
}
}
@@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.hexLogger;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.protocolCommons;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication.C4.wiredProtocol;
namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4
namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.wiredProtocol
{
[TestClass]
[TestSubject(typeof(TouchReadFrameBuilder))]
@@ -16,7 +19,7 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4
{
byte[] frame = new TouchReadFrameBuilder()
.RequestResponse(true)
.AddCommand(TouchReadCommand.ViewFactoryId)
.AddCommand(ProtocolCommand.ViewFactoryId)
.BuildBytes();
byte[] expected =
@@ -41,7 +44,7 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4
{
byte[] frame = new TouchReadFrameBuilder()
.RequestResponse(true)
.AddCommand(TouchReadCommand.ViewProgrammableId)
.AddCommand(ProtocolCommand.ViewProgrammableId)
.BuildBytes();
byte[] expected =
@@ -67,7 +70,7 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4
// Arrange
byte[] frame = new TouchReadFrameBuilder()
.RequestResponse(true)
.AddCommand(TouchReadCommand.SetState)
.AddCommand(ProtocolCommand.SetState)
.AddPayload(new byte[] { 0x01 }) // Idle
.BuildBytes();
@@ -105,5 +108,59 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4
parser.Parse(response);
}
[TestMethod]
public void BuildBytes()
{
// Reserve space for checksum at [0] and [1]
var frame = new List<byte>
{
0x00, // checksum high (placeholder)
0x00, // checksum low (placeholder)
0x05, // length
0x01, // command
0x0D // start
};
// Calculate checksum over payload only (skip checksum bytes)
ushort checksum = TouchReadFrameBuilder.CalculateChecksum(
frame.Skip(2)
);
// Write checksum into first two positions
frame[0] = (byte)(checksum >> 8); // high byte
frame[1] = (byte)(checksum & 0xFF); // low byte
Console.WriteLine(HexFormatter.ToHex(frame.ToArray()));
}
[TestMethod]
public void ByteConversion()
{
byte[] frame = { 0x53, 0x57,0x05, 0x01, 0x0D};
Console.WriteLine(@"1: " + HexFormatter.ToHex(frame.ToArray()));
byte[] payload = { 0x31, 0x30, 0x30, 0x31, 0x30, 0x34, 0x30, 0x35, 0x31, 0x00 };
Console.WriteLine(@"Payload: " + HexFormatter.ToSerialHexWithAscii(payload.ToArray()));
//Connect
//2026-02-04 14:06:22.515 TX (8) 53 57 3F 76 65 72 73 0D
//2026-02-04 14:06:22.906 RX (74) 3F 76 65 72 73 3A 20 48 61 72 72 79 20 54 3A 42 38 30 30 2C 20 56 3A 30 36 2E 30 36 2E 30 31 2C 20 46 57 3A 31 39 30 32 31 35 2C 20 37 45 43 45 2C 20 42 31 2E 36 2E 30 31 2C 20 48 57 3A 34 2C 20 53 65 72 69 61 6C 3A 30 0D
string msg = "53 57 3F 76 65 72 73 0D";
byte[] activationMsg = HexFormatter.HexStringToByteArray(msg);
Console.WriteLine(@"ActivationMsg MSG: " + HexFormatter.ToSerialHexWithAscii(activationMsg.ToArray()));
string str =
"3F 76 65 72 73 3A 20 48 61 72 72 79 20 54 3A 42 38 30 30 2C 20 56 3A 30 36 2E 30 36 2E 30 31 2C 20 46 57 3A 31 39 30 32 31 35 2C 20 37 45 43 45 2C 20 42 31 2E 36 2E 30 31 2C 20 48 57 3A 34 2C 20 53 65 72 69 61 6C 3A 30 0D";
byte[] activationMsgRes = HexFormatter.HexStringToByteArray(str);
Console.WriteLine(@"ActivationMsg RES: " + HexFormatter.ToSerialHexWithAscii(activationMsgRes.ToArray()));
}
}
}
@@ -0,0 +1,25 @@
using JetBrains.Annotations;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.iPerlASICReader.communication;
using TBF.Rig.RegisterReaders.iPerlASICReader.implementations;
namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication
{
[TestClass]
[TestSubject(typeof(OptoHeadTest))]
public class OptoHeadTestTest
{
[TestMethod]
public void ReadRequest_PCB_Test()
{
OptoHeadTest optoHeadTest = new OptoHeadTest();
//TBF.Rig.Generic.IComponentCfg cfg = optoHeadTest.;
SmartReader iPerlAsicReader = new SmartReader();
OptoHeadTest.ReadRequest_PCB(iPerlAsicReader);
}
}
}
@@ -0,0 +1,18 @@
using JetBrains.Annotations;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.iPerlASICReader.implementations;
namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.implementations
{
[TestClass]
[TestSubject(typeof(IPerlASICImplHeadTestCtrl))]
public class IPerlASICImplHeadTestCtrlTest
{
[TestMethod]
public void CommandTestButtonClick_Operations_ReadPcbCmd()
{
}
}
}
+8 -3
View File
@@ -104,11 +104,16 @@
<Compile Include="Rig\Network\Camera\RoiForFixedStartKeyence\RoiTest.cs" />
<Compile Include="Rig\Output\FileWriters\Enhanced\WriterTest.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\diagnosticLed\DiagnosticLedParserTest.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\hexLogger\HexFormatterTest.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\IperlHatProtocol\IperlHatFrameBuilderTests.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\IperlHatProtocol\IperlHatFrameParserTest.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\IperlHatProtocol\IperlHatIntegrationTests.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\led\ShortVariableLedParserTest.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\led\TouchReadLedMessageTest.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\TouchReadBaudRateDetectionTests.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\TouchReadFrameBuilderTest.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\TouchReadSerialIntegrationTests.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadBaudRateDetectionTests.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\wiredProtocol\TouchReadFrameBuilderTest.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\OptoHeadTestTest.cs" />
<Compile Include="Rig\RegisterReaders\iPerlASICReader\implementations\IPerlASICImplHeadTestCtrlTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\CliRunnerTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonCmdStartStop\PoseidonReaderTest.cs" />
<Compile Include="Rig\RegisterReaders\PoseidonReader\UniHeadTestCtrlTest.cs" />