iPerlASIC - additional parameters.

Add protocolCommons classes and enhance RadioService with support for CalibrationFactor, VersionType, ReadingUnits, and FlipMode operations. Add corresponding unit tests for validation. Update project files.
This commit is contained in:
Michal Buzik 2026-08-03 15:42:16 +02:00
parent 02bb9ee9fa
commit 865528e344
13 changed files with 1030 additions and 34 deletions

View File

@ -56,47 +56,70 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.IperlHatProtoc
}
private static byte[] ExtractPayloadUsePrefix(byte[] data, List<byte> prefix, List<byte> end)
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)
if (!IsPrefixValid(data, prefix, end))
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);
if (payloadLength <= 0)
return Array.Empty<byte>();
byte[] payload = new byte[payloadLength];
Buffer.BlockCopy(data, prefix.Count, payload, 0, payloadLength);
Buffer.BlockCopy(
data,
prefix.Count,
payload,
0,
payloadLength);
return payload;
}
private static bool IsPrefixValid(byte[] data, List<byte> prefix, List<byte> end)
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))
if (data == null ||
prefix == null ||
end == null ||
data.Length < prefix.Count + end.Count)
{
return false;
}
// Check frame prefix.
byte[] commandPrefix = new byte[prefix.Count];
Buffer.BlockCopy(
data,
0,
commandPrefix,
0,
prefix.Count);
if (!StartsWithPrefix(prefix, commandPrefix))
return false;
// Check frame ending.
byte[] commandEnd = new byte[end.Count];
Buffer.BlockCopy(
data,
data.Length - end.Count,
commandEnd,
0,
end.Count);
if (!StartsWithPrefix(end, commandEnd))
return false;
return true;
}

View File

@ -38,7 +38,6 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.IperlHatProtoc
return default;
byte raw = Payload[0];
Type t = typeof(T);
// ----- BYTE -----
@ -88,6 +87,21 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.IperlHatProtoc
return System.Text.Encoding.ASCII.GetString(Payload, 0, length);
}
public ushort GetUInt16LittleEndian(out bool ok)
{
ok = false;
if (!IsOk || Payload.Length != 2)
return 0;
ushort value = (ushort)(
Payload[0] |
(Payload[1] << 8));
ok = true;
return value;
}
}

View File

@ -0,0 +1,19 @@
namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons
{
public class CalibrationFactorResult
{
public ushort RawValue { get; set; }
public double Percentage { get; set; }
public double CorrectionPercentage { get; set; }
public override string ToString()
{
return
$"RawValue={RawValue}, " +
$"Percentage={Percentage:F4} %, " +
$"Correction={CorrectionPercentage:+0.0000;-0.0000;0.0000} %";
}
}
}

View File

@ -0,0 +1,8 @@
namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons
{
public enum FlipMode : byte
{
Disabled = 0x00,
Enabled = 0x01
}
}

View File

@ -135,10 +135,18 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommon
/// <summary>Set calibration structure (protected by meter seal).</summary>
SetCalibrationStructure = 0x52,
/// <summary>View calibration.</summary>
/// <summary>
/// View calibration factor.
/// Response payload: uint16 little-endian calibration factor.
/// Nominal value 4096 represents 100.0 %.
/// </summary>
ViewCalibration = 0x53,
/// <summary>Set calibration (protected by meter seal).</summary>
/// <summary>
/// Set calibration factor.
/// Payload: uint16 little-endian calibration factor.
/// Protected by meter seal.
/// </summary>
SetCalibration = 0x54,
/// <summary>View reboot count.</summary>
@ -152,6 +160,18 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommon
/// <summary>Set temperature (protected by meter seal).</summary>
SetTemperature = 0x58,
// ==========================================================
// Flip mode
// ==========================================================
/// <summary>
/// View randomized field flip-rate mode.
/// Response payload:
/// 0x00 = randomized flip rate disabled,
/// 0x01 = randomized flip rate enabled.
/// </summary>
ViewFlipMode = 0xB9,
// ==========================================================
// Diagnostic LED / Hardware

View File

@ -0,0 +1,9 @@
namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons
{
public enum ReadingUnits : byte
{
CubicMeters = 0x00,
CubicFeet = 0x01,
UsGallons = 0x02
}
}

View File

@ -0,0 +1,59 @@
using System.Text.RegularExpressions;
namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons
{
public class VersionTypeResult
{
public string TouchReadVersion { get; set; }
public string MeterDeviceType { get; set; }
public string MeterFirmwareVersion { get; set; }
public override string ToString()
{
return $"TouchReadVersion={TouchReadVersion}, " +
$"MeterDeviceType={MeterDeviceType}, " +
$"MeterFirmwareVersion={MeterFirmwareVersion}";
}
public static VersionTypeResult ParseVersionTypePayload(string payload)
{
if (string.IsNullOrWhiteSpace(payload))
return null;
// Example:
// vers: Harry T:B800, V:06.06.01, FW:190215,
// 7ECE, B1.6.01, HW:4, Serial:0
string meterDeviceType = GetPayloadValue(payload, "T");
string touchReadVersion = GetPayloadValue(payload, "V");
string meterFirmwareVersion = GetPayloadValue(payload, "FW");
if (string.IsNullOrWhiteSpace(meterDeviceType) &&
string.IsNullOrWhiteSpace(touchReadVersion) &&
string.IsNullOrWhiteSpace(meterFirmwareVersion))
{
return null;
}
return new VersionTypeResult
{
MeterDeviceType = meterDeviceType,
TouchReadVersion = touchReadVersion,
MeterFirmwareVersion = meterFirmwareVersion
};
}
private static string GetPayloadValue(string payload, string key)
{
Match match = Regex.Match(
payload,
@"(?:^|[\s,])" + Regex.Escape(key) + @":\s*([^,\s]+)",
RegexOptions.IgnoreCase);
return match.Success
? match.Groups[1].Value.Trim()
: null;
}
}
}

View File

@ -1,7 +1,11 @@
using System;
using System.Text;
using System.Text.RegularExpressions;
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.wiredProtocol;
using TBF.Rig.TestMethods.iPerlCommunication.communication.Utils;
using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead;
@ -153,8 +157,278 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication
return "";
}
public VersionTypeResult SetViewVersionAndType(IperlHead iHead)
{
if (!serialDriver.IsOpen())
{
serialDriver.Open();
}
byte[] request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.ViewVersionAndType)
.BuildBytes();
byte[] rawData = serialDriver.SendAndWait(request, 5000);
if (rawData == null || rawData.Length == 0)
{
log.Warn("SetViewVersionAndType: No response received.");
return null;
}
try
{
var parser = new IperlHatFrameParser();
IperlHatResponse decoded = parser.Parse(rawData);
log.Debug("SetViewVersionAndType isOK: " + decoded.IsOk);
if (!decoded.IsOk)
{
log.Warn("SetViewVersionAndType: Device returned NOK response.");
return null;
}
if (decoded.Payload == null || decoded.Payload.Length == 0)
{
log.Warn("SetViewVersionAndType: Response payload is empty.");
return null;
}
string payloadText = Encoding.ASCII
.GetString(decoded.Payload)
.Trim('\0', '\r', '\n', ' ');
log.Debug("SetViewVersionAndType payload: " + payloadText);
VersionTypeResult result = VersionTypeResult.ParseVersionTypePayload(payloadText);
if (result == null)
{
log.Warn(
"SetViewVersionAndType: Unable to parse response payload: " +
payloadText);
return null;
}
log.Debug("SetViewVersionAndType result: " + result);
return result;
}
catch (FormatException ex)
{
log.Error("SetViewVersionAndType: Invalid response frame.", ex);
return null;
}
catch (Exception ex)
{
log.Error("SetViewVersionAndType failed.", ex);
return null;
}
}
public ReadingUnits? ViewReadingUnits(IperlHead iHead)
{
if (!serialDriver.IsOpen())
{
serialDriver.Open();
}
byte[] request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.ViewReadingUnits)
.BuildBytes();
byte[] rawData = serialDriver.SendAndWait(request, 5000);
if (rawData == null || rawData.Length == 0)
{
log.Warn("ViewReadingUnits: No response received.");
return null;
}
try
{
var parser = new IperlHatFrameParser();
IperlHatResponse decoded = parser.Parse(rawData);
log.Debug("ViewReadingUnits isOK: " + decoded.IsOk);
if (!decoded.IsOk)
{
log.Warn("ViewReadingUnits: Device returned NOK response.");
return null;
}
ReadingUnits readingUnits =
decoded.GetResponse<ReadingUnits>(out bool responseOk);
if (!responseOk)
{
string payload = BitConverter.ToString(decoded.Payload);
log.Warn(
"ViewReadingUnits: Invalid response payload. Payload=" +
payload);
return null;
}
log.Debug(
$"ViewReadingUnits: {readingUnits} " +
$"(0x{Convert.ToByte(readingUnits):X2})");
return readingUnits;
}
catch (FormatException ex)
{
log.Error("ViewReadingUnits: Invalid response frame.", ex);
return null;
}
catch (Exception ex)
{
log.Error("ViewReadingUnits failed.", ex);
return null;
}
}
public FlipMode? ViewFlipMode(IperlHead iHead)
{
if (!serialDriver.IsOpen())
{
serialDriver.Open();
}
byte[] request = new TouchReadFrameBuilder()
.RequestResponse(true)
.AddDeviceCommand(ProtocolDeviceSubCommand.ViewFlipMode)
.BuildBytes();
byte[] rawData = serialDriver.SendAndWait(request, 5000);
if (rawData == null || rawData.Length == 0)
{
log.Warn("ViewFlipMode: No response received.");
return null;
}
try
{
var parser = new IperlHatFrameParser();
IperlHatResponse decoded = parser.Parse(rawData);
log.Debug("ViewFlipMode isOK: " + decoded.IsOk);
if (!decoded.IsOk)
{
log.Warn("ViewFlipMode: Device returned NOK response.");
return null;
}
FlipMode flipMode =
decoded.GetResponse<FlipMode>(out bool responseOk);
if (!responseOk)
{
log.Warn(
"ViewFlipMode: Invalid payload. Expected one byte " +
"with value 0x00 or 0x01. Payload=" +
BitConverter.ToString(decoded.Payload));
return null;
}
log.Debug(
$"ViewFlipMode: {flipMode} " +
$"(0x{(byte)flipMode:X2})");
return flipMode;
}
catch (FormatException ex)
{
log.Error("ViewFlipMode: Invalid response frame.", ex);
return null;
}
catch (Exception ex)
{
log.Error("ViewFlipMode failed.", ex);
return null;
}
}
public CalibrationFactorResult ViewCalibration(IperlHead iHead)
{
if (!serialDriver.IsOpen())
{
serialDriver.Open();
}
byte[] request = new TouchReadFrameBuilder()
.RequestResponse(true)
.AddDeviceCommand(ProtocolDeviceSubCommand.ViewCalibration)
.BuildBytes();
byte[] rawData = serialDriver.SendAndWait(request, 5000);
if (rawData == null || rawData.Length == 0)
{
log.Warn("ViewCalibration: No response received.");
return null;
}
try
{
var parser = new IperlHatFrameParser();
IperlHatResponse decoded = parser.Parse(rawData);
log.Debug("ViewCalibration isOK: " + decoded.IsOk);
if (!decoded.IsOk)
{
log.Warn("ViewCalibration: Device returned NOK response.");
return null;
}
ushort calibrationFactor = decoded.GetUInt16LittleEndian(out bool responseOk);
if (!responseOk)
{
log.Warn( "ViewCalibration: Invalid payload. Expected uint16 little-endian value. Payload=" + BitConverter.ToString(decoded.Payload));
return null;
}
double percentage = calibrationFactor * 100.0 / 4096.0;
var result = new CalibrationFactorResult
{
RawValue = calibrationFactor,
Percentage = percentage,
CorrectionPercentage = percentage - 100.0
};
log.Debug("ViewCalibration result: " + result);
return result;
}
catch (FormatException ex)
{
log.Error("ViewCalibration: Invalid response frame.", ex);
return null;
}
catch (Exception ex)
{
log.Error("ViewCalibration failed.", ex);
return null;
}
}
// ----------------------
public bool SetActivityMode_Active(IperlHead iHead)
{
if (!serialDriver.IsOpen())

View File

@ -1964,9 +1964,13 @@
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\C4\led\ShortVariableLedParser.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\C4\led\TouchReadLedData.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\C4\led\TouchReadLedMessage.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\C4\protocolCommons\CalibrationFactorResult.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\C4\protocolCommons\FlipMode.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\C4\protocolCommons\ProtocolCommand.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\C4\protocolCommons\ProtocolDeviceSubCommand.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\C4\protocolCommons\ProtocolStatuses.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\C4\protocolCommons\ReadingUnits.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\C4\protocolCommons\VersionTypeResult.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\C4\wiredProtocol\TouchReadFrame.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\C4\wiredProtocol\TouchReadFrameBuilder.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\C4\wiredProtocol\TouchReadFrameParser.cs" />

View File

@ -0,0 +1,40 @@
using TBF.Rig.TestMethods.iPerlCommunication.communication.Utils;
namespace TBFTests.Rig.TestMethods.iPerlCommunication.communication
{
internal sealed class FakeSerialDriver : ISerialDriver
{
public bool IsPortOpen { get; set; }
public int OpenCallCount { get; private set; }
public int SendAndWaitCallCount { get; private set; }
public byte[] Response { get; set; }
public byte[] LastRequest { get; private set; }
public int LastTimeout { get; private set; }
public bool IsOpen()
{
return IsPortOpen;
}
public bool Open()
{
OpenCallCount++;
IsPortOpen = true;
return true;
}
public byte[] SendAndWait(byte[] request, int timeout)
{
SendAndWaitCallCount++;
LastRequest = request;
LastTimeout = timeout;
return Response;
}
}
}

View File

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.IperlHatProtocol;
namespace TBFTests.Rig.TestMethods.iPerlCommunication.communication
{
internal static class IperlResponseFactory
{
public static byte[] CreateVersionResponse(string responseText)
{
if (responseText == null)
throw new ArgumentNullException(nameof(responseText));
byte[] payload =
System.Text.Encoding.ASCII.GetBytes(responseText);
var response = new List<byte>
{
IperlHatProtocolConstants.Question
};
response.AddRange(payload);
response.Add(IperlHatProtocolConstants.End);
return response.ToArray();
}
public static byte[] CreateOkResponse(params byte[] payload)
{
return CreateResponse(
IperlHatProtocolConstants.StatusOk,
payload);
}
public static byte[] CreateNokResponse(params byte[] payload)
{
return CreateResponse(
IperlHatProtocolConstants.StatusNok,
payload);
}
private static byte[] CreateResponse(
byte status,
params byte[] payload)
{
payload = payload ?? Array.Empty<byte>();
var response = new List<byte>
{
IperlHatProtocolConstants.Start,
IperlHatProtocolConstants.Read,
0x00,
status
};
response.AddRange(payload);
response.Add(IperlHatProtocolConstants.End);
response[2] = checked((byte)response.Count);
return response.ToArray();
}
}
}

View File

@ -0,0 +1,459 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using JetBrains.Annotations;
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.wiredProtocol;
using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead;
namespace TBFTests.Rig.TestMethods.iPerlCommunication.communication
{
[TestClass]
[TestSubject(typeof(RadioService))]
public class RadioServiceTest
{
private FakeSerialDriver serialDriver;
private RadioService service;
private IperlHead head;
[TestInitialize]
public void TestInitialize()
{
serialDriver = new FakeSerialDriver
{
IsPortOpen = true
};
service = new RadioService(serialDriver);
head = new IperlHead();
}
// ==========================================================
// SetViewVersionAndType
// ==========================================================
[TestMethod]
public void SetViewVersionAndType_ValidResponse_ReturnsParsedResult()
{
// Arrange
const string responseText =
"vers: Harry T:B800, V:06.06.01, FW:190215, " +
"7ECE, B1.6.01, HW:4, Serial:0";
serialDriver.Response =
IperlResponseFactory.CreateVersionResponse(responseText);
// Act
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(1, serialDriver.SendAndWaitCallCount);
Assert.AreEqual(5000, serialDriver.LastTimeout);
}
[TestMethod]
public void SetViewVersionAndType_NullResponse_ReturnsNull()
{
// Arrange
serialDriver.Response = null;
// Act
VersionTypeResult result =
service.SetViewVersionAndType(head);
// Assert
Assert.IsNull(result);
}
[TestMethod]
public void SetViewVersionAndType_NokResponse_ReturnsNull()
{
// Arrange
serialDriver.Response =
IperlResponseFactory.CreateNokResponse();
// Act
VersionTypeResult result =
service.SetViewVersionAndType(head);
// Assert
Assert.IsNull(result);
}
[TestMethod]
public void SetViewVersionAndType_InvalidPayload_ReturnsNull()
{
// Arrange
byte[] payload =
System.Text.Encoding.ASCII.GetBytes(
"Unsupported response");
serialDriver.Response =
IperlResponseFactory.CreateOkResponse(payload);
// Act
VersionTypeResult result =
service.SetViewVersionAndType(head);
// Assert
Assert.IsNull(result);
}
[TestMethod]
public void SetViewVersionAndType_ClosedPort_OpensPort()
{
// Arrange
serialDriver.IsPortOpen = false;
byte[] payload =
System.Text.Encoding.ASCII.GetBytes(
"vers: Harry T:B800, V:06.06.01, FW:190215");
serialDriver.Response =
IperlResponseFactory.CreateOkResponse(payload);
// Act
service.SetViewVersionAndType(head);
// Assert
Assert.AreEqual(1, serialDriver.OpenCallCount);
Assert.IsTrue(serialDriver.IsPortOpen);
}
// ==========================================================
// ViewReadingUnits
// ==========================================================
[TestMethod]
public void ViewReadingUnits_ValidResponse_ReturnsReadingUnits()
{
// Arrange
serialDriver.Response =
IperlResponseFactory.CreateOkResponse(
(byte)ReadingUnits.CubicMeters);
// Act
ReadingUnits? result =
service.ViewReadingUnits(head);
// Assert
Assert.IsTrue(result.HasValue);
Assert.AreEqual(
ReadingUnits.CubicMeters,
result.Value);
byte[] expected = CreateSimpleCommandRequest(
ProtocolCommand.ViewReadingUnits);
Console.WriteLine(
"Expected: " + BitConverter.ToString(expected));
Console.WriteLine(
"Actual: " + BitConverter.ToString(serialDriver.LastRequest));
Console.WriteLine(
$"Expected length: {expected.Length}, " +
$"actual length: {serialDriver.LastRequest.Length}");
CollectionAssert.AreEqual(
expected,
serialDriver.LastRequest);
}
[TestMethod]
public void ViewReadingUnits_UnknownEnumValue_ReturnsNull()
{
// Arrange
serialDriver.Response =
IperlResponseFactory.CreateOkResponse(0x7E);
// Act
ReadingUnits? result =
service.ViewReadingUnits(head);
// Assert
Assert.IsFalse(result.HasValue);
}
[TestMethod]
public void ViewReadingUnits_InvalidPayloadLength_ReturnsNull()
{
// Arrange
serialDriver.Response =
IperlResponseFactory.CreateOkResponse(
0x00,
0x01);
// Act
ReadingUnits? result =
service.ViewReadingUnits(head);
// Assert
Assert.IsFalse(result.HasValue);
}
[TestMethod]
public void ViewReadingUnits_NokResponse_ReturnsNull()
{
// Arrange
serialDriver.Response =
IperlResponseFactory.CreateNokResponse(0x00);
// Act
ReadingUnits? result =
service.ViewReadingUnits(head);
// Assert
Assert.IsFalse(result.HasValue);
}
// ==========================================================
// ViewFlipMode
// ==========================================================
[TestMethod]
public void ViewFlipMode_DisabledResponse_ReturnsDisabled()
{
// Arrange
serialDriver.Response =
IperlResponseFactory.CreateOkResponse(
(byte)FlipMode.Disabled);
// Act
FlipMode? result =
service.ViewFlipMode(head);
// Assert
Assert.IsTrue(result.HasValue);
Assert.AreEqual(
FlipMode.Disabled,
result.Value);
CollectionAssert.AreEqual(
CreateDeviceCommandRequest(
ProtocolDeviceSubCommand.ViewFlipMode),
serialDriver.LastRequest);
}
[TestMethod]
public void ViewFlipMode_EnabledResponse_ReturnsEnabled()
{
// Arrange
serialDriver.Response =
IperlResponseFactory.CreateOkResponse(
(byte)FlipMode.Enabled);
// Act
FlipMode? result =
service.ViewFlipMode(head);
// Assert
Assert.IsTrue(result.HasValue);
Assert.AreEqual(
FlipMode.Enabled,
result.Value);
}
[TestMethod]
public void ViewFlipMode_UnsupportedValue_ReturnsNull()
{
// Arrange
serialDriver.Response =
IperlResponseFactory.CreateOkResponse(0x02);
// Act
FlipMode? result =
service.ViewFlipMode(head);
// Assert
Assert.IsFalse(result.HasValue);
}
[TestMethod]
public void ViewFlipMode_EmptyPayload_ReturnsNull()
{
// Arrange
serialDriver.Response =
IperlResponseFactory.CreateOkResponse();
// Act
FlipMode? result =
service.ViewFlipMode(head);
// Assert
Assert.IsFalse(result.HasValue);
}
// ==========================================================
// ViewCalibration
// ==========================================================
[TestMethod]
public void ViewCalibration_NominalValue_ReturnsOneHundredPercent()
{
// Arrange
// 4096 = 0x1000
// Little endian payload = 00 10
serialDriver.Response =
IperlResponseFactory.CreateOkResponse(
0x00,
0x10);
// Act
CalibrationFactorResult result =
service.ViewCalibration(head);
// Assert
Assert.IsNotNull(result);
Assert.AreEqual((ushort)4096, result.RawValue);
Assert.AreEqual(
100.0,
result.Percentage,
0.000001);
Assert.AreEqual(
0.0,
result.CorrectionPercentage,
0.000001);
byte[] deviceCommandRequest = CreateDeviceCommandRequest(ProtocolDeviceSubCommand.ViewCalibration);
Console.WriteLine( "Expected: " + BitConverter.ToString(deviceCommandRequest));
Console.WriteLine( "Actual: " + BitConverter.ToString(serialDriver.LastRequest));
CollectionAssert.AreEqual(
deviceCommandRequest,
serialDriver.LastRequest);
}
[TestMethod]
public void ViewCalibration_ValueAboveNominal_ReturnsPositiveCorrection()
{
// Arrange
// 4097 = 0x1001
// Little endian = 01 10
serialDriver.Response =
IperlResponseFactory.CreateOkResponse(
0x01,
0x10);
// Act
CalibrationFactorResult result =
service.ViewCalibration(head);
// Assert
Assert.IsNotNull(result);
Assert.AreEqual((ushort)4097, result.RawValue);
double expectedPercentage =
4097 * 100.0 / 4096.0;
Assert.AreEqual(
expectedPercentage,
result.Percentage,
0.000001);
Assert.AreEqual(
expectedPercentage - 100.0,
result.CorrectionPercentage,
0.000001);
}
[TestMethod]
public void ViewCalibration_ValueBelowNominal_ReturnsNegativeCorrection()
{
// Arrange
// 4095 = 0x0FFF
// Little endian = FF 0F
serialDriver.Response =
IperlResponseFactory.CreateOkResponse(
0xFF,
0x0F);
// Act
CalibrationFactorResult result =
service.ViewCalibration(head);
// Assert
Assert.IsNotNull(result);
Assert.AreEqual((ushort)4095, result.RawValue);
Assert.IsTrue(result.CorrectionPercentage < 0);
}
[TestMethod]
public void ViewCalibration_InvalidPayloadLength_ReturnsNull()
{
// Arrange
serialDriver.Response =
IperlResponseFactory.CreateOkResponse(0x10);
// Act
CalibrationFactorResult result =
service.ViewCalibration(head);
// Assert
Assert.IsNull(result);
}
[TestMethod]
public void ViewCalibration_NokResponse_ReturnsNull()
{
// Arrange
serialDriver.Response =
IperlResponseFactory.CreateNokResponse(
0x00,
0x10);
// Act
CalibrationFactorResult result =
service.ViewCalibration(head);
// Assert
Assert.IsNull(result);
}
// ==========================================================
// Request helpers
// ==========================================================
private static byte[] CreateSimpleCommandRequest(
ProtocolCommand command)
{
return new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(command)
.BuildBytes();
}
private static byte[] CreateDeviceCommandRequest(
ProtocolDeviceSubCommand subCommand)
{
return new TouchReadFrameBuilder()
.RequestResponse(true)
.AddDeviceCommand(subCommand)
.BuildBytes();
}
}
}

View File

@ -137,6 +137,9 @@
<Compile Include="Rig\Scales\MettlerToledo\ReadStableMassOpTest.cs" />
<Compile Include="Rig\TestMethods\GenesisCommunication\GenesisHead\GenesisHeadBatchIntegrationTest.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\common\OptoTelegramRawTest.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\FakeSerialDriver.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\IperlResponseFactory.cs" />
<Compile Include="Rig\TestMethods\iPerlCommunication\communication\RadioServiceTest.cs" />
<Compile Include="Rig\Uni\SharedDialogs\SmartMetersCommunication\implementations\PoseidonCorrectionsTest.cs" />
</ItemGroup>
<ItemGroup>