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.
This commit is contained in:
2026-08-18 13:59:59 +02:00
parent c71fe75446
commit 8738edd0c5
11 changed files with 508 additions and 20 deletions
@@ -2,7 +2,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommon
{
public enum FlipMode : byte
{
Disabled = 0x00,
Enabled = 0x01
DisabledConstant = 0x00, //Constant
EnabledRandomized = 0x01 //Randomized
}
}
@@ -173,6 +173,14 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommon
/// </summary>
ViewFlipMode = 0xB9,
/// <summary>
/// Set randomized field flip-rate mode.
/// Payload:
/// 0x00 = constant flip rate,
/// 0x01 = randomized flip rate.
/// </summary>
SetFlipMode = 0xBA,
// ==========================================================
// Diagnostic LED / Hardware
// ==========================================================
@@ -567,6 +567,59 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication
return null;
}
}
/// <summary>
/// Sets constant (0x00) or randomized (0x01) field flip-rate mode.
/// </summary>
public bool SetFlipMode(FlipMode flipMode)
{
if (iperlHead == null)
{
log.Warn("SetFlipMode: IperlHead is null.");
return false;
}
if (!Enum.IsDefined(typeof(FlipMode), flipMode))
{
log.WarnFormat("SetFlipMode: Unsupported value 0x{0:X2}.", (byte)flipMode);
return false;
}
if (iperlHead.DebugLevel == DebugMode.Simulate)
{
if (iperlHead.ConfigStruct == null)
iperlHead.ConfigStruct = new ConfigStruct();
iperlHead.ConfigStruct.FlipMode = flipMode;
return true;
}
try
{
if (serialDriver == null)
serialDriver = BuildConnection(iperlHead);
log.DebugFormat(
"SetFlipMode started: Head={0}, Mode={1} (0x{2:X2})",
iperlHead,
flipMode,
(byte)flipMode);
var headService = new RadioService(serialDriver);
bool successful = headService.SetFlipMode(iperlHead, flipMode);
log.InfoFormat(
"SetFlipMode finished: Head={0}, Mode={1}, successful={2}",
iperlHead,
flipMode,
successful);
return successful;
}
catch (Exception ex)
{
log.Error("SetFlipMode failed.", ex);
return false;
}
}
/// <summary>
/// Reads the calibration factor.
@@ -361,6 +361,46 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication
}
}
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())
@@ -239,6 +239,8 @@ namespace TBF.Rig.TestMethods.iPerlCommunication
ContextMenu cm = new ContextMenu();
cm.MenuItems.Add(NewMenuItem("Read PCB Number" , "ReadPCB"));
cm.MenuItems.Add(NewMenuItem( iPerlCommunicationConstants.ReadAdditionalCommonParametersStr, "ReadAdditionalCommonParameters"));
cm.MenuItems.Add(NewMenuItem( iPerlCommunicationConstants.SetFlipModeConstantStr, "SetFlipModeConstant"));
cm.MenuItems.Add(NewMenuItem( iPerlCommunicationConstants.SetFlipModeRandomizedStr, "SetFlipModeRandomized"));
cm.MenuItems.Add(NewMenuItem("Enter Test Mode" , "StartTestMode"));
cm.MenuItems.Add(NewMenuItem("Turn Off Test Mode (Enter Active Mode)", "TurnOffTestMode"));
cm.MenuItems.Add(NewMenuItem("Set Production Mode (Radio not start with flow)" , "SetProductionMode"));
@@ -506,7 +508,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication
}
///
/// Set checkbox states accroding to iPerlHeads[i].Disabled states
/// Set checkbox states accroding to iPerlHeads[i].Disabled_Constant states
///
for (int i = 0; i < textBoxesCount; i++)
{
@@ -718,6 +720,8 @@ namespace TBF.Rig.TestMethods.iPerlCommunication
else if (currentActivity.ToLower().Equals(SetIdleModeStr.ToLower())) error = SetIdleMode(threadID, ihead, ref resultStr);
else if (currentActivity.ToLower().Contains(ReadConfigurationStr.ToLower())) error = ReadConfiguration(ihead, wm, ref resultStr);
else if (currentActivity.ToLower().Contains( iPerlCommunicationConstants.ReadAdditionalCommonParametersStr.ToLower())) error = ReadAdditionalCommonParameters( ihead,wm, ref resultStr);
else if (currentActivity.ToLower().Contains( iPerlCommunicationConstants.SetFlipModeConstantStr.ToLower())) error = SetFlipModeConstant( ihead,wm, ref resultStr);
else if (currentActivity.ToLower().Contains( iPerlCommunicationConstants.SetFlipModeRandomizedStr.ToLower())) error = SetFlipModeRandomized( ihead,wm, ref resultStr);
///
/// RFID communication functions below require a reference to water meter entity (wm != null)
///
@@ -810,6 +814,75 @@ namespace TBF.Rig.TestMethods.iPerlCommunication
}
}
private CommErr SetFlipModeRandomized(IperlHead ihead, WaterMeter wm, ref string resultStr)
{
return SetFlipMode(
ihead,
FlipMode.EnabledRandomized,
iPerlCommunicationConstants.SetFlipModeRandomizedStr,
ref resultStr);
}
private CommErr SetFlipModeConstant(IperlHead ihead, WaterMeter wm, ref string resultStr)
{
return SetFlipMode(
ihead,
FlipMode.DisabledConstant,
iPerlCommunicationConstants.SetFlipModeConstantStr,
ref resultStr);
}
private CommErr SetFlipMode(
IperlHead ihead,
FlipMode flipMode,
string activityName,
ref string resultStr)
{
if (ihead == null)
{
resultStr = activityName + ": iPerl head is null.";
return CommErr.CommFailed;
}
if (ihead.OptoHeadTest == null)
{
resultStr = activityName + ": OptoHeadTest is not available.";
return CommErr.CommFailed;
}
ihead.CommFailed = false;
try
{
log.DebugFormat(
"{0} started: Head={1}, payload=0x{2:X2}",
activityName,
ihead,
(byte)flipMode);
bool successful = ihead.OptoHeadTest.SetFlipMode(flipMode);
if (successful)
{
resultStr = string.Format(
"{0}: OK (0x{1:X2})",
activityName,
(byte)flipMode);
return CommErr.None;
}
resultStr = activityName + ": FAILED";
ihead.CommFailed = true;
return CommErr.Write;
}
catch (Exception ex)
{
resultStr = activityName + " failed: " + ex.Message;
ihead.CommFailed = true;
log.Error(activityName + " failed.", ex);
return CommErr.Write;
}
}
private CommErr ReadSerialNr(int threadId, IperlHead ihead, ref string resultStr)
{
log.Debug("ReadSerialNr threadId=" + threadId + ", ihead=" + ihead.ToString());
@@ -53,6 +53,8 @@ namespace TBF.Rig.TestMethods.iPerlCommunication
var retVal = new List<string>();
retVal.Add(iPerlCommunicationForm.ReadConfigurationStr);
retVal.Add(iPerlCommunicationConstants.ReadAdditionalCommonParametersStr);
retVal.Add(iPerlCommunicationConstants.SetFlipModeConstantStr);
retVal.Add(iPerlCommunicationConstants.SetFlipModeRandomizedStr);
retVal.Add(iPerlCommunicationForm.ReadSerialNrStr);
retVal.Add(string.Format("{0} A0", iPerlCommunicationForm.SetTestModeStr));
retVal.Add(string.Format("{0} A4", iPerlCommunicationForm.SetTestModeStr));
@@ -5,6 +5,7 @@ using System.Web.UI.WebControls;
using System.Windows.Forms;
using TBF.Rig.Sequences;
using TBF.Rig.TestMethods.iPerlCommunication.communication;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.protocolCommons;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
@@ -38,6 +39,8 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
new {Name = "Read Additional Common Parameters", Value = "ReadAdditionalCommonParameters" },
new {Name = "Set Test Mode", Value = "SetTestMode" },
new {Name = "Set Active Mode", Value = "SetActiveMode" },
new {Name = "Set Flip Mode Constant", Value = "SetFlipModeConstant" },
new {Name = "Set Flip Mode Randomized", Value = "SetFlipModeRandomized" },
#if DEBUG
new {Name = "Start Read Opto Data", Value = "ReadOptoData" },
new {Name = "Stop Read Opto Data", Value = "StopReadOptoData" },
@@ -143,6 +146,34 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
stopWorkerThread = true;
iPerlHead.StopDataStreamProcessing(); // close opto port
break;
case "SetFlipModeConstant":
{
if (iPerlHead == null || iPerlHead.OptoHeadTest == null)
{
rfidListItem.Text = "Set Flip Mode Constant (0x00) - FAILED: iPerl head is not available.";
break;
}
bool successful = iPerlHead.OptoHeadTest.SetFlipMode(FlipMode.DisabledConstant);
rfidListItem.Text = successful
? "Set Flip Mode Constant (0x00) - OK"
: "Set Flip Mode Constant (0x00) - FAILED";
break;
}
case "SetFlipModeRandomized":
{
if (iPerlHead == null || iPerlHead.OptoHeadTest == null)
{
rfidListItem.Text = "Set Flip Mode Randomized (0x01) - FAILED: iPerl head is not available.";
break;
}
bool successful = iPerlHead.OptoHeadTest.SetFlipMode(FlipMode.EnabledRandomized);
rfidListItem.Text = successful
? "Set Flip Mode Randomized (0x01) - OK"
: "Set Flip Mode Randomized (0x01) - FAILED";
break;
}
case "ResetNfcHead":
iPerlHead.ResetNfcInterface();
break;