IperlASIC - Improved and added tests, hardware integtration tests works

Improve `VersionTypeResult` parsing and handling: Add compact/legacy payload support. Extend `ReadingUnits`. Update `IperlHatIntegrationTests` and `RadioService` with new tests and functionality integration.
This commit is contained in:
Michal Buzik 2026-08-04 11:53:47 +02:00
parent 865528e344
commit 7a8ce7b6ab
5 changed files with 289 additions and 57 deletions

View File

@ -4,6 +4,9 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommon
{
CubicMeters = 0x00,
CubicFeet = 0x01,
UsGallons = 0x02
UsGallons = 0x02,
UsGallons1 = 0x04,
UsGallons2 = 0x08,
UsGallons3 = 0x10,
}
}

View File

@ -2,58 +2,134 @@ using System.Text.RegularExpressions;
namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons
{
public class VersionTypeResult
using System;
using System.Text.RegularExpressions;
namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.IperlHatProtocol
{
public string TouchReadVersion { get; set; }
public string MeterDeviceType { get; set; }
public string MeterFirmwareVersion { get; set; }
public override string ToString()
public class VersionTypeResult
{
return $"TouchReadVersion={TouchReadVersion}, " +
$"MeterDeviceType={MeterDeviceType}, " +
$"MeterFirmwareVersion={MeterFirmwareVersion}";
}
public static VersionTypeResult ParseVersionTypePayload(string payload)
{
if (string.IsNullOrWhiteSpace(payload))
return null;
public string TouchReadVersion { get; set; }
// Example:
// vers: Harry T:B800, V:06.06.01, FW:190215,
// 7ECE, B1.6.01, HW:4, Serial:0
public string MeterDeviceType { get; set; }
string meterDeviceType = GetPayloadValue(payload, "T");
string touchReadVersion = GetPayloadValue(payload, "V");
string meterFirmwareVersion = GetPayloadValue(payload, "FW");
public string MeterFirmwareVersion { get; set; }
if (string.IsNullOrWhiteSpace(meterDeviceType) &&
string.IsNullOrWhiteSpace(touchReadVersion) &&
string.IsNullOrWhiteSpace(meterFirmwareVersion))
public static VersionTypeResult ParseVersionTypePayload(
string payload)
{
return null;
if (string.IsNullOrWhiteSpace(payload))
{
return null;
}
string normalizedPayload = payload
.Trim('\0', '\r', '\n', ' ');
VersionTypeResult result =
ParseCompactPayload(normalizedPayload);
if (result != null)
{
return result;
}
return ParseLegacyPayload(normalizedPayload);
}
return new VersionTypeResult
private static VersionTypeResult ParseCompactPayload(
string payload)
{
MeterDeviceType = meterDeviceType,
TouchReadVersion = touchReadVersion,
MeterFirmwareVersion = meterFirmwareVersion
};
}
// Expected format:
// B1.23,SWM004,1.00
private static string GetPayloadValue(string payload, string key)
{
Match match = Regex.Match(
payload,
@"(?:^|[\s,])" + Regex.Escape(key) + @":\s*([^,\s]+)",
RegexOptions.IgnoreCase);
string[] parts = payload.Split(',');
return match.Success
? match.Groups[1].Value.Trim()
: null;
if (parts.Length != 3)
{
return null;
}
string touchReadVersion = parts[0].Trim();
string meterDeviceType = parts[1].Trim();
string meterFirmwareVersion = parts[2].Trim();
if (string.IsNullOrWhiteSpace(touchReadVersion) ||
string.IsNullOrWhiteSpace(meterDeviceType) ||
string.IsNullOrWhiteSpace(meterFirmwareVersion))
{
return null;
}
return new VersionTypeResult
{
TouchReadVersion = touchReadVersion,
MeterDeviceType = meterDeviceType,
MeterFirmwareVersion = meterFirmwareVersion
};
}
private static VersionTypeResult ParseLegacyPayload(
string payload)
{
// Expected example:
//
// vers: Harry T:B800, V:06.06.01, FW:190215,
// 7ECE, B1.6.01, HW:4, Serial:0
Match deviceTypeMatch = Regex.Match(
payload,
@"(?:^|[\s,])T\s*:\s*([^,\s]+)",
RegexOptions.IgnoreCase);
Match versionMatch = Regex.Match(
payload,
@"(?:^|[\s,])V\s*:\s*([^,\s]+)",
RegexOptions.IgnoreCase);
Match firmwareMatch = Regex.Match(
payload,
@"(?:^|[\s,])FW\s*:\s*([^,\s]+)",
RegexOptions.IgnoreCase);
if (!deviceTypeMatch.Success ||
!versionMatch.Success ||
!firmwareMatch.Success)
{
return null;
}
string meterDeviceType =
deviceTypeMatch.Groups[1].Value.Trim();
string touchReadVersion =
versionMatch.Groups[1].Value.Trim();
string meterFirmwareVersion =
firmwareMatch.Groups[1].Value.Trim();
if (string.IsNullOrWhiteSpace(touchReadVersion) ||
string.IsNullOrWhiteSpace(meterDeviceType) ||
string.IsNullOrWhiteSpace(meterFirmwareVersion))
{
return null;
}
return new VersionTypeResult
{
TouchReadVersion = touchReadVersion,
MeterDeviceType = meterDeviceType,
MeterFirmwareVersion = meterFirmwareVersion
};
}
public override string ToString()
{
return
$"TouchReadVersion={TouchReadVersion}, " +
$"MeterDeviceType={MeterDeviceType}, " +
$"MeterFirmwareVersion={MeterFirmwareVersion}";
}
}
}
}

View File

@ -5,6 +5,7 @@ using log4net;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.IperlHatProtocol;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons.TBF.Rig.TestMethods.iPerlCommunication.communication.C4.IperlHatProtocol;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.wiredProtocol;
using TBF.Rig.TestMethods.iPerlCommunication.communication.Utils;
using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead;

View File

@ -9,6 +9,7 @@ using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed.pars
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.hexLogger;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.IperlHatProtocol;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons.TBF.Rig.TestMethods.iPerlCommunication.communication.C4.IperlHatProtocol;
namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol
@ -16,7 +17,8 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHat
[TestClass]
public class IperlHatIntegrationTests
{
private const string ComPort = "COM3"; // CHANGE THIS
private const string ComPort = "COM12"; // CHANGE THIS
private const string ComPortOptho = "COM13";
private const int BaudRate = 2400;
private const int BaudRateOpto = 38400;
private const int ReadTimeoutMs = 2000;
@ -461,7 +463,7 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHat
//method test connection to optho head
private void Serial_OptoRawSniff()
{
using (var port = new SerialPort("COM4", BaudRateOpto, Parity.None, 8, StopBits.One))
using (var port = new SerialPort(ComPortOptho, BaudRateOpto, Parity.None, 8, StopBits.One))
{
port.Handshake = Handshake.None;
port.ReadTimeout = 10000;
@ -506,5 +508,163 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHat
}
}
// ---- NEXT COMMUNICATION ----
[TestMethod]
[TestCategory("Hardware")]
[TestCategory("Serial")]
public void Serial_ViewConfigurationCommands_ReturnValidResponses()
{
using (var port = new SerialPort(
ComPort,
BaudRate,
Parity.None,
8,
StopBits.One))
{
port.Handshake = Handshake.None;
port.ReadTimeout = 5000;
port.Open();
TestViewVersionAndType(port);
TestViewReadingUnits(port);
TestViewFlipMode(port);
TestViewCalibration(port);
}
}
private static IperlHatResponse SendRequest(
SerialPort port,
byte[] request,
string commandName)
{
var parser = new IperlHatFrameParser();
port.DiscardInBuffer();
Console.WriteLine( $"{commandName} TX → " + HexFormatter.ToSerialHexWithAscii(request));
port.Write(request, 0, request.Length);
byte[] response;
try
{
response = ReadResponse(port);
}
catch (TimeoutException ex)
{
Assert.Fail( $"{commandName}: Timeout while waiting for response. " + ex.Message);
return null;
}
Assert.IsNotNull( response, $"{commandName}: Response is null.");
Assert.IsTrue( response.Length > 0, $"{commandName}: Response is empty.");
Console.WriteLine( $"{commandName} RX ← " + HexFormatter.ToSerialHexWithAscii(response));
try
{
IperlHatResponse decoded = parser.Parse(response);
Assert.IsNotNull( decoded, $"{commandName}: Parser returned null.");
Assert.IsTrue( decoded.IsOk, $"{commandName}: Device returned NOK. " + $"Payload={BitConverter.ToString(decoded.Payload ?? new byte[0])}");
return decoded;
}
catch (Exception ex)
{
Assert.Fail( $"{commandName}: Response frame could not be parsed. " + $"Frame={BitConverter.ToString(response)}. Error={ex}");
return null;
}
}
private static void TestViewVersionAndType(
SerialPort port)
{
byte[] request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.ViewVersionAndType)
.BuildBytes();
IperlHatResponse response = SendRequest(
port,
request,
nameof(ProtocolCommand.ViewVersionAndType));
Assert.IsNotNull( response.Payload, "ViewVersionAndType: Payload is null.");
Assert.IsTrue( response.Payload.Length > 0, "ViewVersionAndType: Payload is empty.");
string payloadText = Encoding.ASCII.GetString(response.Payload).Trim('\0', '\r', '\n', ' ');
Console.WriteLine( "ViewVersionAndType payload: " + payloadText);
VersionTypeResult result = VersionTypeResult.ParseVersionTypePayload(payloadText);
Assert.IsNotNull( result, "ViewVersionAndType: Payload cannot be parsed. " + $"Payload='{payloadText}'");
Assert.IsFalse( string.IsNullOrWhiteSpace(result.TouchReadVersion), "ViewVersionAndType: TouchReadVersion is empty.");
Assert.IsFalse( string.IsNullOrWhiteSpace(result.MeterDeviceType), "ViewVersionAndType: MeterDeviceType is empty.");
Assert.IsFalse( string.IsNullOrWhiteSpace(result.MeterFirmwareVersion), "ViewVersionAndType: MeterFirmwareVersion is empty.");
Console.WriteLine( "ViewVersionAndType result: " + result);
}
private static void TestViewReadingUnits(SerialPort port)
{
byte[] request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.ViewReadingUnits)
.BuildBytes();
IperlHatResponse response = SendRequest( port, request, nameof(ProtocolCommand.ViewReadingUnits));
ReadingUnits units = response.GetResponse<ReadingUnits>(out bool responseOk);
Assert.IsTrue( responseOk, $"ViewReadingUnits: Invalid payload. Payload={BitConverter.ToString(response.Payload ?? new byte[0])}");
Assert.IsTrue( Enum.IsDefined(typeof(ReadingUnits), units), $"ViewReadingUnits: Unknown value 0x{Convert.ToByte(units):X2}.");
Console.WriteLine( $"ViewReadingUnits result: {units} " + $"(0x{Convert.ToByte(units):X2})");
}
private static void TestViewFlipMode(SerialPort port)
{
byte[] request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddDeviceCommand(ProtocolDeviceSubCommand.ViewFlipMode)
.BuildBytes();
IperlHatResponse response = SendRequest(
port,
request,
nameof(ProtocolDeviceSubCommand.ViewFlipMode));
FlipMode flipMode = response.GetResponse<FlipMode>(out bool responseOk);
Assert.IsTrue( responseOk, $"ViewFlipMode: Invalid payload. Payload={BitConverter.ToString(response.Payload ?? new byte[0])}");
Assert.IsTrue( Enum.IsDefined(typeof(FlipMode), flipMode),$"ViewFlipMode: Unknown value 0x{Convert.ToByte(flipMode):X2}.");
Console.WriteLine( $"ViewFlipMode result: {flipMode} (0x{Convert.ToByte(flipMode):X2})");
}
private static void TestViewCalibration(SerialPort port)
{
byte[] request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddDeviceCommand(ProtocolDeviceSubCommand.ViewCalibration)
.BuildBytes();
IperlHatResponse response = SendRequest( port, request, nameof(ProtocolDeviceSubCommand.ViewCalibration));
ushort rawValue = response.GetUInt16LittleEndian(out bool responseOk);
Assert.IsTrue( responseOk, "ViewCalibration: Expected a two-byte little-endian payload. " +
$"Payload={BitConverter.ToString(response.Payload ?? new byte[0])}");
double percentage = rawValue * 100.0 / 4096.0;
double correctionPercentage = percentage - 100.0;
Console.WriteLine( $"ViewCalibration result: RawValue={rawValue}, Percentage={percentage:F4} %, " +
$"Correction={correctionPercentage:+0.0000;-0.0000;0.0000} %");
Assert.IsTrue( rawValue > 0, "ViewCalibration: Calibration factor must be greater than zero.");
Assert.IsTrue( percentage > 0.0, "ViewCalibration: Calculated percentage must be greater than zero.");
}
}
}

View File

@ -5,6 +5,7 @@ using TBF.Rig.TestMethods.iPerlCommunication.communication;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.IperlHatProtocol;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons.TBF.Rig.TestMethods.iPerlCommunication.communication.C4.IperlHatProtocol;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.wiredProtocol;
using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead;
@ -47,23 +48,14 @@ namespace TBFTests.Rig.TestMethods.iPerlCommunication.communication
IperlResponseFactory.CreateVersionResponse(responseText);
// Act
VersionTypeResult result =
service.SetViewVersionAndType(head);
VersionTypeResult result = service.SetViewVersionAndType(head);
// Assert
Assert.IsNotNull(result);
Assert.AreEqual(
"B800",
result.MeterDeviceType);
Assert.AreEqual(
"06.06.01",
result.TouchReadVersion);
Assert.AreEqual(
"190215",
result.MeterFirmwareVersion);
Assert.AreEqual( "B800", result.MeterDeviceType);
Assert.AreEqual( "06.06.01", result.TouchReadVersion);
Assert.AreEqual( "190215", result.MeterFirmwareVersion);
Assert.AreEqual(1, serialDriver.SendAndWaitCallCount);
Assert.AreEqual(5000, serialDriver.LastTimeout);