Files
tbf/TBF/Rig/TestMethods/iPerlCommunication/communication/RadioService.cs
T
michal 8738edd0c5 Added FLIP MODE Constant, Randomised
Add support for Flip Mode configuration in iPerl: Implement `SetFlipMode` functionality in `OptoHeadTest` and `RadioService`, integrate constants, enhance UI, and add unit and integration tests for constant/randomized flip mode settings.
2026-08-18 13:59:59 +02:00

633 lines
21 KiB
C#

using System;
using System.Text;
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;
namespace TBF.Rig.TestMethods.iPerlCommunication.communication
{
public class RadioService
{
private static readonly ILog log = LogManager.GetLogger(typeof(RadioService));
static string okResponse = "Command complete, no errors";
static string errorResponse = "Unable to execute";
private ISerialDriver serialDriver;
public RadioService(SerialDriver serialDriver)
{
this.serialDriver = serialDriver;
log.Debug("RadioService created with serialDriver= " + serialDriver + "");
}
public RadioService(ISerialDriver serialDriver)
{
this.serialDriver = serialDriver;
log.Debug("RadioService created with serialDriver= " + serialDriver + "");
}
public string ReadRequest_PCB(ref IperlHead 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)
{
string asciiPayload = decoded.GetAsciiPayload();
if (iHead.ConfigStruct != null) // store mechanism
{
iHead.ConfigStruct.PCBNumberString = asciiPayload;
}
return asciiPayload;
}
return null;
}
public ProtocolStatuses GetActivityStatusMode(IperlHead iHead)
{
if (!serialDriver.IsOpen())
serialDriver.Open();
var request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.ViewState)
.BuildBytes();
byte[] rawData = serialDriver.SendAndWait(request, 10000);
if (rawData == null)
return ProtocolStatuses.Unknown;
var parser = new IperlHatFrameParser();
IperlHatResponse decoded = parser.Parse(rawData);
if (!decoded.IsOk)
return ProtocolStatuses.Unknown;
ProtocolStatuses statusMode = decoded.GetResponse<ProtocolStatuses>(out bool isOK);
if (!isOK)
return ProtocolStatuses.Unknown; // wrong payload
return statusMode;
}
public DiagnosticLedState SetOptoStatusMode(IperlHead iHead, DiagnosticLedState opthoStatusMode)
{
if (!serialDriver.IsOpen())
serialDriver.Open();
var request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddDeviceCommand(ProtocolDeviceSubCommand.SetDiagnosticLEDState)
.AddPayload(opthoStatusMode)
.BuildBytes();
byte[] rawData = serialDriver.SendAndWait(request, 10000);
if (rawData == null)
return DiagnosticLedState.StatusUnknown;
var parser = new IperlHatFrameParser();
IperlHatResponse decoded = parser.Parse(rawData);
log.Debug("SetOptoStatusMode isOK: " + decoded.IsOk);
// if is response ok - it set it correctly
if (!decoded.IsOk)
return DiagnosticLedState.StatusUnknown;
return opthoStatusMode;
}
private static ushort SafeIntToUShort(int value)
{
if (value < ushort.MinValue || value > ushort.MaxValue)
return 0xFD; // your error code
return (ushort)value;
}
public string GetVersion(IperlHead iHead)
{
if (!serialDriver.IsOpen())
{
serialDriver.Open();
}
byte[] request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.Question)
.AddPayload(IperlHatProtocolConstants.Version)
.BuildBytes();
byte[] rawData = serialDriver.SendAndWait(request, 10000);
if (rawData == null)
return "";
// parse rawData
var parser = new IperlHatFrameParser();
IperlHatResponse decoded = parser.Parse(rawData);
log.Debug("GetVersion isOK: " + decoded.IsOk);
if (decoded.IsOk)
{
return decoded.GetAsciiPayload();
}
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 IperlHatFrameBuilder()
.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;
}
if (decoded.Payload == null || decoded.Payload.Length != 1)
{
log.Warn( "ViewFlipMode: Invalid payload. Expected exactly one byte. Payload="
+ BitConverter.ToString( decoded.Payload ?? new byte[0]));
return null;
}
byte rawValue = decoded.Payload[0];
if (!Enum.IsDefined( typeof(FlipMode), rawValue))
{
log.Warn( $"ViewFlipMode: Unknown value. Value=0x{rawValue:X2}");
return null;
}
FlipMode flipMode = (FlipMode)rawValue;
log.Debug( $"ViewFlipMode: {flipMode} " + $"(0x{rawValue: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 bool SetFlipMode(IperlHead iHead, FlipMode flipMode)
{
if (!Enum.IsDefined(typeof(FlipMode), flipMode))
throw new ArgumentOutOfRangeException(nameof(flipMode));
if (!serialDriver.IsOpen())
serialDriver.Open();
byte[] request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddDeviceCommand(ProtocolDeviceSubCommand.SetFlipMode)
.AddPayload((byte)flipMode)
.BuildBytes();
byte[] rawData = serialDriver.SendAndWait(request, 5000);
if (rawData == null || rawData.Length == 0)
{
log.Warn("SetFlipMode: No response received.");
return false;
}
var parser = new IperlHatFrameParser();
IperlHatResponse decoded = parser.Parse(rawData);
log.Debug(
string.Format(
"SetFlipMode({0}, 0x{1:X2}) isOK: {2}",
flipMode,
(byte)flipMode,
decoded.IsOk));
if (!decoded.IsOk)
return false;
if (iHead != null && iHead.ConfigStruct != null)
iHead.ConfigStruct.FlipMode = flipMode;
return true;
}
public CalibrationFactorResult ViewCalibration( IperlHead iHead)
{
if (!serialDriver.IsOpen())
{
serialDriver.Open();
}
byte[] request = new IperlHatFrameBuilder()
.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;
}
if (decoded.Payload == null || decoded.Payload.Length != 2)
{
log.Warn( "ViewCalibration: Invalid payload. Expected two-byte little-endian value. Payload=" +
BitConverter.ToString( decoded.Payload ?? new byte[0]));
return null;
}
ushort calibrationFactor = (ushort)( decoded.Payload[0] | (decoded.Payload[1] << 8));
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())
{
serialDriver.Open();
}
//Set State to Active 02
byte[] request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.SetState)
.AddSubCommand(ProtocolStatuses.Active) // Active
.BuildBytes();
byte[] rawData = serialDriver.SendAndWait(request, 5000);
if (rawData == null)
return false;
// parse rawData
var parser = new IperlHatFrameParser();
IperlHatResponse decoded = parser.Parse(rawData);
log.Debug("SetActivityMode_Active isOK: " + decoded.IsOk);
if (decoded.IsOk && iHead.ConfigStruct != null)
{
iHead.ConfigStruct.StatusMode = ProtocolStatuses.Active;
}
return decoded.IsOk;
}
public bool SetActivityMode_Idle(IperlHead iHead)
{
if (!serialDriver.IsOpen())
{
serialDriver.Open();
}
//Set Activity State Idle 01
byte[] request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddCommand(ProtocolCommand.SetState)
.AddSubCommand(ProtocolStatuses.Idle)
.BuildBytes();
byte[] rawData = serialDriver.SendAndWait(request, 5000);
if (rawData == null)
return false;
// parse rawData
var parser = new IperlHatFrameParser();
IperlHatResponse decoded = parser.Parse(rawData);
log.Debug("SetActivityMode_Idle isOK: " + decoded.IsOk);
if (decoded.IsOk && iHead.ConfigStruct != null)
{
iHead.ConfigStruct.StatusMode = ProtocolStatuses.Idle;
}
return decoded.IsOk;
}
public bool SetOptTestMode(IperlHead iHead)
{
if (!serialDriver.IsOpen())
{
serialDriver.Open();
}
//Set LED to state 7
var request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddDeviceCommand(ProtocolDeviceSubCommand.SetDiagnosticLEDState)
.AddPayload(DiagnosticLedState.State7)
.BuildBytes();
byte[] rawData = serialDriver.SendAndWait(request, 5000);
if (rawData == null)
return false;
// parse rawData
var parser = new IperlHatFrameParser();
IperlHatResponse decoded = parser.Parse(rawData);
bool isOk = decoded.IsOk;
log.Debug("SetOptTestMode isOK: " + isOk);
if (decoded.IsOk && iHead.ConfigStruct != null)
{
iHead.ConfigStruct.OpthoStatusMode = DiagnosticLedState.State7;
}
return isOk;
}
/// <summary>
/// stop data streaming by LED
/// </summary>
/// <param name="iHead"></param>
/// <returns></returns>
public bool SetOptActiveMode(IperlHead iHead)
{
if (!serialDriver.IsOpen())
{
serialDriver.Open();
}
//Set LED to state 1
var request = new IperlHatFrameBuilder()
.RequestResponse(true)
.AddDeviceCommand(ProtocolDeviceSubCommand.SetDiagnosticLEDState)
.AddPayload(DiagnosticLedState.StateOFF)
.BuildBytes();
byte[] rawData = serialDriver.SendAndWait(request, 5000);
if (rawData == null)
return false;
// parse rawData
var parser = new IperlHatFrameParser();
IperlHatResponse decoded = parser.Parse(rawData);
log.Debug("SetOptActiveMode isOK: " + decoded.IsOk);
if (decoded.IsOk && iHead.ConfigStruct != null)
{
iHead.ConfigStruct.OpthoStatusMode = DiagnosticLedState.StateOFF;
}
return decoded.IsOk;
}
public string GetUnit(IperlHead iperlHead)
{
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)
{
string asciiPayload = decoded.GetAsciiPayload();
if (iperlHead.ConfigStruct != null) // store mechanism
{
iperlHead.ConfigStruct.Unit = asciiPayload;
}
return asciiPayload;
}
return null;
}
}
}