1020 lines
39 KiB
C#
1020 lines
39 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.IO.Ports;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed;
|
|
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed.parserer;
|
|
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.hexLogger;
|
|
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.IperlHatProtocol;
|
|
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons;
|
|
|
|
|
|
namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHatProtocol
|
|
{
|
|
[TestClass]
|
|
[TestCategory("HardwareIntegration")]
|
|
public class IperlHatIntegrationTests
|
|
{
|
|
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;
|
|
|
|
[TestInitialize]
|
|
public void RequireExplicitHardwareConfiguration()
|
|
{
|
|
if (!string.Equals(Environment.GetEnvironmentVariable("IPERL_ASIC_HW_TESTS"), "1",
|
|
StringComparison.Ordinal))
|
|
{
|
|
Assert.Inconclusive("Set IPERL_ASIC_HW_TESTS=1 to run iPerl ASIC serial hardware integration tests.");
|
|
}
|
|
|
|
string[] ports = SerialPort.GetPortNames();
|
|
if (!ports.Any(port => string.Equals(port, ComPort, StringComparison.OrdinalIgnoreCase)) ||
|
|
!ports.Any(port => string.Equals(port, ComPortOptho, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
Assert.Inconclusive($"iPerl ASIC hardware integration requires configured ports {ComPort} and {ComPortOptho}.");
|
|
}
|
|
}
|
|
|
|
[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 Integration_Serial_OptoRawSniff_Standalone()
|
|
{
|
|
Serial_OptoRawSniff();
|
|
}
|
|
|
|
//method test connection to optho head
|
|
private void Serial_OptoRawSniff()
|
|
{
|
|
using (var port = new SerialPort(ComPortOptho, 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);
|
|
|
|
// Original byte-level output. Keep it for diagnostics.
|
|
Console.WriteLine( "RX ASCII bytes ← " + HexFormatter.ToSerialHex(bytes));
|
|
// Customer-compatible optical HEX packet.
|
|
string opticalHex = FormatOpticalHexPacket(line);
|
|
Console.WriteLine( "OPTO HEX ← " + opticalHex);
|
|
|
|
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.Inconclusive("No optical packet was received within 10 seconds. Enable optical test mode before running this sniff integration test.");
|
|
}
|
|
}
|
|
|
|
Console.WriteLine("\nDone.");
|
|
}
|
|
}
|
|
|
|
|
|
// ---- 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);
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
[TestCategory("Hardware")]
|
|
[TestCategory("Serial")]
|
|
public void Serial_SetFlipModes_ActiveOptical_Idle_VerifyReadBack()
|
|
{
|
|
using (var port = new SerialPort(
|
|
ComPort,
|
|
BaudRate,
|
|
Parity.None,
|
|
8,
|
|
StopBits.One))
|
|
{
|
|
port.Handshake = Handshake.None;
|
|
port.ReadTimeout = ReadTimeoutMs;
|
|
port.WriteTimeout = ReadTimeoutMs;
|
|
|
|
Console.WriteLine(
|
|
"Flip-mode integration: command={0}/{1}, optical={2}/{3}",
|
|
ComPort,
|
|
BaudRate,
|
|
ComPortOptho,
|
|
BaudRateOpto);
|
|
|
|
port.Open();
|
|
bool opticalOutputEnabled = false;
|
|
bool mainTestCompleted = false;
|
|
Exception cleanupFailure = null;
|
|
|
|
try
|
|
{
|
|
SetAndVerifyMeterState(port, ProtocolStatuses.Idle);
|
|
SetAndVerifyFlipMode(port, FlipMode.Constant, "Constant");
|
|
|
|
SetAndVerifyMeterState(port, ProtocolStatuses.Active);
|
|
SetDiagnosticLed(port, DiagnosticLedState.State4);
|
|
opticalOutputEnabled = true;
|
|
|
|
Console.WriteLine(
|
|
"Reading optical packets from {0} while the meter is Active...",
|
|
ComPortOptho);
|
|
ReadAndVerifyOpticalPacket();
|
|
|
|
SetAndVerifyFlipMode(port, FlipMode.Randomized, "Randomized");
|
|
mainTestCompleted = true;
|
|
}
|
|
finally
|
|
{
|
|
if (opticalOutputEnabled && port.IsOpen)
|
|
{
|
|
try
|
|
{
|
|
SetDiagnosticLed(port, DiagnosticLedState.StateOFF);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
cleanupFailure = ex;
|
|
Console.WriteLine("CLEANUP FAIL: optical output could not be disabled: " + ex);
|
|
}
|
|
}
|
|
|
|
if (port.IsOpen)
|
|
{
|
|
try
|
|
{
|
|
SetAndVerifyMeterState(port, ProtocolStatuses.Idle);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (cleanupFailure == null)
|
|
cleanupFailure = ex;
|
|
Console.WriteLine("CLEANUP FAIL: Idle state could not be restored: " + ex);
|
|
}
|
|
}
|
|
|
|
if (mainTestCompleted && cleanupFailure != null)
|
|
{
|
|
Assert.Fail(
|
|
"Main flip-mode scenario passed, but cleanup failed: " +
|
|
cleanupFailure.Message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void SetAndVerifyFlipMode(
|
|
SerialPort port,
|
|
FlipMode expectedMode,
|
|
string modeName)
|
|
{
|
|
byte[] setRequest = new IperlHatFrameBuilder()
|
|
.RequestResponse(true)
|
|
.AddDeviceCommand(ProtocolDeviceSubCommand.SetFlipMode)
|
|
.AddPayload((byte)expectedMode)
|
|
.BuildBytes();
|
|
|
|
SendRequest(
|
|
port,
|
|
setRequest,
|
|
"SetFlipMode" + modeName,
|
|
string.Format(
|
|
"Sets flip mode to {0}; payload=0x{1:X2}.",
|
|
modeName,
|
|
(byte)expectedMode));
|
|
|
|
byte[] viewRequest = new IperlHatFrameBuilder()
|
|
.RequestResponse(true)
|
|
.AddDeviceCommand(ProtocolDeviceSubCommand.ViewFlipMode)
|
|
.BuildBytes();
|
|
|
|
IperlHatResponse viewResponse = SendRequest(
|
|
port,
|
|
viewRequest,
|
|
"ViewFlipMode after " + modeName,
|
|
"Reads flip mode back to verify the SetFlipMode command.");
|
|
|
|
FlipMode actualMode = viewResponse.GetResponse<FlipMode>(out bool responseOk);
|
|
Assert.IsTrue(
|
|
responseOk,
|
|
"ViewFlipMode returned an invalid payload after setting " + modeName + ".");
|
|
Assert.AreEqual(
|
|
expectedMode,
|
|
actualMode,
|
|
string.Format(
|
|
"Flip-mode verification failed. Expected {0} (0x{1:X2}), actual {2} (0x{3:X2}).",
|
|
modeName,
|
|
(byte)expectedMode,
|
|
actualMode,
|
|
(byte)actualMode));
|
|
|
|
Console.WriteLine(
|
|
"VERIFY PASS: FlipMode={0}, value=0x{1:X2}",
|
|
modeName,
|
|
(byte)actualMode);
|
|
}
|
|
|
|
private static void SetAndVerifyMeterState(
|
|
SerialPort port,
|
|
ProtocolStatuses expectedState)
|
|
{
|
|
byte[] setRequest = new IperlHatFrameBuilder()
|
|
.RequestResponse(true)
|
|
.AddCommand(ProtocolCommand.SetState)
|
|
.AddSubCommand(expectedState)
|
|
.BuildBytes();
|
|
|
|
SendRequest(
|
|
port,
|
|
setRequest,
|
|
"SetState " + expectedState,
|
|
"Sets the meter activity state to " + expectedState + ".");
|
|
|
|
byte[] viewRequest = new IperlHatFrameBuilder()
|
|
.RequestResponse(true)
|
|
.AddCommand(ProtocolCommand.ViewState)
|
|
.BuildBytes();
|
|
|
|
IperlHatResponse viewResponse = SendRequest(
|
|
port,
|
|
viewRequest,
|
|
"ViewState after " + expectedState,
|
|
"Reads the activity state back to verify SetState.");
|
|
|
|
ProtocolStatuses actualState =
|
|
viewResponse.GetResponse<ProtocolStatuses>(out bool responseOk);
|
|
Assert.IsTrue(
|
|
responseOk,
|
|
"ViewState returned an invalid payload after setting " + expectedState + ".");
|
|
Assert.AreEqual(
|
|
expectedState,
|
|
actualState,
|
|
"Meter-state verification failed.");
|
|
|
|
Console.WriteLine("VERIFY PASS: MeterState=" + actualState);
|
|
}
|
|
|
|
private static void SetDiagnosticLed(
|
|
SerialPort port,
|
|
DiagnosticLedState state)
|
|
{
|
|
byte[] request = new IperlHatFrameBuilder()
|
|
.RequestResponse(true)
|
|
.AddDeviceCommand(ProtocolDeviceSubCommand.SetDiagnosticLEDState)
|
|
.AddPayload(state)
|
|
.BuildBytes();
|
|
|
|
SendRequest(
|
|
port,
|
|
request,
|
|
"SetDiagnosticLEDState " + state,
|
|
"Controls optical output on " + ComPortOptho + ".");
|
|
}
|
|
|
|
private static void ReadAndVerifyOpticalPacket()
|
|
{
|
|
using (var opticalPort = new SerialPort(
|
|
ComPortOptho,
|
|
BaudRateOpto,
|
|
Parity.None,
|
|
8,
|
|
StopBits.One))
|
|
{
|
|
opticalPort.Handshake = Handshake.None;
|
|
opticalPort.ReadTimeout = 10000;
|
|
opticalPort.NewLine = "\r\n";
|
|
opticalPort.Encoding = Encoding.ASCII;
|
|
opticalPort.Open();
|
|
|
|
string line = opticalPort.ReadLine();
|
|
string opticalHex = FormatOpticalHexPacket(line);
|
|
Console.WriteLine("OPTO HEX COM13 <- " + opticalHex);
|
|
|
|
var parser = new DiagnosticLedParser(DiagnosticLedState.State4);
|
|
DiagnosticLedState4Data parsed =
|
|
(DiagnosticLedState4Data)parser.ParseLine(line, false);
|
|
|
|
Assert.IsNotNull(parsed, "COM13 optical parser returned null.");
|
|
Console.WriteLine("OPTO PARSE PASS COM13: " + parsed);
|
|
}
|
|
}
|
|
|
|
private static IperlHatResponse SendRequest(
|
|
SerialPort port,
|
|
byte[] request,
|
|
string commandName,
|
|
string commandDescription)
|
|
{
|
|
var parser = new IperlHatFrameParser();
|
|
|
|
Console.WriteLine();
|
|
Console.WriteLine("==================================================");
|
|
Console.WriteLine($"Command : {commandName}");
|
|
Console.WriteLine($"Description : {commandDescription}");
|
|
Console.WriteLine($"TX packet : {HexFormatter.ToSerialHexWithAscii(request)}");
|
|
|
|
port.DiscardInBuffer();
|
|
port.Write(request, 0, request.Length);
|
|
|
|
byte[] response;
|
|
|
|
try
|
|
{
|
|
response = ReadResponse(port);
|
|
}
|
|
catch (TimeoutException ex)
|
|
{
|
|
Assert.Fail($"{commandName}: Timeout while waiting for response." +
|
|
Environment.NewLine +
|
|
$"Description: {commandDescription}" +
|
|
Environment.NewLine +
|
|
$"TX packet: {HexFormatter.ToSerialHexWithAscii(request)}" +
|
|
Environment.NewLine +
|
|
$"Error: {ex.Message}");
|
|
|
|
return null;
|
|
}
|
|
|
|
Assert.IsNotNull(response,
|
|
$"{commandName}: Response is null. TX packet: {HexFormatter.ToSerialHexWithAscii(request)}");
|
|
|
|
Assert.IsTrue(response.Length > 0,
|
|
$"{commandName}: Response is empty. TX packet: {HexFormatter.ToSerialHexWithAscii(request)}");
|
|
|
|
Console.WriteLine($"RX packet : {HexFormatter.ToSerialHexWithAscii(response)}");
|
|
|
|
try
|
|
{
|
|
IperlHatResponse decoded = parser.Parse(response);
|
|
|
|
Assert.IsNotNull(
|
|
decoded,
|
|
$"{commandName}: Parser returned null." +
|
|
Environment.NewLine +
|
|
$"TX packet: {HexFormatter.ToSerialHexWithAscii(request)}" +
|
|
Environment.NewLine +
|
|
$"RX packet: {HexFormatter.ToSerialHexWithAscii(response)}");
|
|
|
|
Assert.IsTrue(
|
|
decoded.IsOk,
|
|
$"{commandName}: Device returned NOK." +
|
|
Environment.NewLine +
|
|
$"TX packet: {HexFormatter.ToSerialHexWithAscii(request)}" +
|
|
Environment.NewLine +
|
|
$"RX packet: {HexFormatter.ToSerialHexWithAscii(response)}" +
|
|
Environment.NewLine +
|
|
$"Payload: {BitConverter.ToString(decoded.Payload ?? new byte[0])}");
|
|
|
|
Console.WriteLine($"Result : OK");
|
|
Console.WriteLine($"Payload : {BitConverter.ToString(decoded.Payload ?? new byte[0])}");
|
|
|
|
return decoded;
|
|
}
|
|
catch (AssertFailedException)
|
|
{
|
|
// Zachová pôvodnú a podrobnejšiu Assert chybu.
|
|
throw;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Assert.Fail(
|
|
$"{commandName}: Response frame could not be parsed." +
|
|
Environment.NewLine +
|
|
$"Description: {commandDescription}" +
|
|
Environment.NewLine +
|
|
$"TX packet: {HexFormatter.ToSerialHexWithAscii(request)}" +
|
|
Environment.NewLine +
|
|
$"RX packet: {HexFormatter.ToSerialHexWithAscii(response)}" +
|
|
Environment.NewLine +
|
|
$"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),
|
|
"Reads the TouchRead version, meter device type and meter firmware version.");
|
|
|
|
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),
|
|
"Reads the measurement units currently configured in the meter.");
|
|
|
|
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),
|
|
"Reads the current display flip mode.");
|
|
|
|
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),
|
|
"Reads the raw two-byte calibration factor used to calculate the calibration percentage.");
|
|
|
|
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.");
|
|
}
|
|
|
|
private static string FormatOpticalHexPacket(string asciiPacket)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(asciiPacket))
|
|
throw new FormatException("Optical packet is empty.");
|
|
|
|
string[] fields = asciiPacket.Split(
|
|
new[] { ' ', '\t', '\r', '\n' },
|
|
StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
for (int i = 0; i < fields.Length; i++)
|
|
{
|
|
string field = fields[i].Trim();
|
|
|
|
if (!IsHexadecimal(field))
|
|
{
|
|
throw new FormatException(
|
|
$"Optical packet contains a non-HEX field at index {i}: '{field}'.");
|
|
}
|
|
|
|
fields[i] = field.ToUpperInvariant();
|
|
}
|
|
|
|
return string.Join(" ", fields);
|
|
}
|
|
|
|
private static bool IsHexadecimal(string value)
|
|
{
|
|
if (string.IsNullOrEmpty(value))
|
|
return false;
|
|
|
|
for (int i = 0; i < value.Length; i++)
|
|
{
|
|
char c = value[i];
|
|
|
|
bool isHexDigit =
|
|
(c >= '0' && c <= '9') ||
|
|
(c >= 'A' && c <= 'F') ||
|
|
(c >= 'a' && c <= 'f');
|
|
|
|
if (!isHexDigit)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|