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:
+53
-30
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+15
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+19
@@ -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} %";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons
|
||||
{
|
||||
public enum FlipMode : byte
|
||||
{
|
||||
Disabled = 0x00,
|
||||
Enabled = 0x01
|
||||
}
|
||||
}
|
||||
+22
-2
@@ -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
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons
|
||||
{
|
||||
public enum ReadingUnits : byte
|
||||
{
|
||||
CubicMeters = 0x00,
|
||||
CubicFeet = 0x01,
|
||||
UsGallons = 0x02
|
||||
}
|
||||
}
|
||||
+59
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user