Fix ver. 3.9.3139 iPerlCommunicationForm.cs problems + exceptions if another regreadrer has mistakely same ports

Enhance iPerl dialog layout and error handling: improve `ShuffleTextBoxes` with positional mapping, refine checkbox state tracking, add detailed logging for labels and checkboxes, and handle serial port exceptions. Increment version to 3.9.3139.0.
This commit is contained in:
Michal Buzik 2026-08-18 16:09:21 +02:00
parent be6b40b5cd
commit a4ef208233
3 changed files with 122 additions and 37 deletions

View File

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// Build Number
// Revision
//
[assembly: AssemblyVersion("3.9.3138.0")]
[assembly: AssemblyFileVersion("3.9.3138.0")]
[assembly: AssemblyVersion("3.9.3139.0")]
[assembly: AssemblyFileVersion("3.9.3139.0")]

View File

@ -257,9 +257,17 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication.Utils
_responseReceived.Set();
}
}
catch (TimeoutException te)
catch (TimeoutException)
{
// Ignore shutdown race conditions
log.WarnFormat("DataReceivedHandler() - Read timeout on {0}.", GetPortName(sender));
}
catch (System.IO.IOException ex)
{
log.WarnFormat("DataReceivedHandler() - I/O operation on {0} was aborted: {1}", GetPortName(sender), ex.Message);
}
catch (InvalidOperationException ex)
{
log.WarnFormat("DataReceivedHandler() - Serial port {0} was closed while receiving data: {1}", GetPortName(sender), ex.Message);
}
finally
{
@ -267,6 +275,12 @@ namespace TBF.Rig.TestMethods.iPerlCommunication.communication.Utils
}
}
}
private static string GetPortName(object sender)
{
SerialPort port = sender as SerialPort;
return port != null ? port.PortName : "<unknown>";
}
public byte[] SendAndWait(byte[] data, int timeoutMs)
{

View File

@ -188,6 +188,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication
TextBox[] messages;
CheckBoxImage[] checkBoxes;
static int[] ckbIndex;
static IList<int> displayedHeadPositions0;
static bool[] ckbState;
static IList<IperlHead> iperlHeads;
@ -345,7 +346,24 @@ namespace TBF.Rig.TestMethods.iPerlCommunication
}
WaterMetersCount = iperlHeads.Count;
ShuffleTextBoxes(WaterMetersCount, ProcessData.LineSize);
IList<int> displayPositions0 = iperlHeads
.Select(head => head.Position - 1)
.ToList();
log.WarnFormat(
"IPERL_DIALOG_LAYOUT_V3 Prepare: Assembly={0}, Version={1}, RegisterReaders={2}, Heads={3}, DisplayPositions=[{4}], ReaderIndexes=[{5}]",
typeof(iPerlCommunicationForm).Assembly.Location,
typeof(iPerlCommunicationForm).Assembly.GetName().Version,
ProcessData.RegisterReaders.Length,
string.Join(", ", iperlHeads.Select((head, index) =>
string.Format("local{0}:{1}/Position={2}", index, head.Name, head.Position))),
string.Join(",", displayPositions0.Select(position => position + 1)),
string.Join(",", waterMeterPositions0));
ShuffleTextBoxes(
WaterMetersCount,
ProcessData.LineSize,
displayPositions0);
///
/// Prepare worker threads, 'rfidPortNrs', 'lastGroup', etc..
@ -386,7 +404,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication
/// </summary>
/// <param name="wmsCount">Number of watermeters</param>
/// <param name="lineSize">Number of watermeters in one line</param>
void ShuffleTextBoxes(int wmsCount, int lineSize)
void ShuffleTextBoxes(int wmsCount, int lineSize, IList<int> positions0 = null)
{
labels = new Label[MaxTextBoxesCount]
{
@ -421,21 +439,62 @@ namespace TBF.Rig.TestMethods.iPerlCommunication
checkBoxImage41, checkBoxImage42, checkBoxImage43, checkBoxImage44, checkBoxImage45, checkBoxImage46, checkBoxImage47, checkBoxImage48,
};
ckbIndex = new int[MaxTextBoxesCount];
for (int i = 0; i < ckbIndex.Length; i++)
{
ckbIndex[i] = -1;
}
ckbState = new bool[MaxTextBoxesCount];
textBoxesCount = MaxTextBoxesCount;
///
if (wmsCount < textBoxesCount && lineSize > 0)
log.WarnFormat(
"IPERL_DIALOG_LAYOUT_V3 Shuffle start: wmsCount={0}, lineSize={1}, editMode={2}, positions=[{3}]",
wmsCount,
lineSize,
checkBoxesEditMode,
positions0 == null ? "<null>" : string.Join(",", positions0.Select(position => position + 1)));
if (positions0 != null && positions0.Count == wmsCount)
{
int layoutLines = lineSize > 0
? (ProcessData.WMsCount + lineSize - 1) / lineSize
: 1;
int layoutGap = layoutLines > 0
? (MaxTextBoxesCount - ProcessData.WMsCount) / layoutLines
: 0;
for (int dest = 0; dest < positions0.Count; dest++)
{
int headPosition0 = positions0[dest];
int visualLine = lineSize > 0 ? headPosition0 / lineSize : 0;
int origin = headPosition0 + visualLine * layoutGap;
if (origin < 0 || origin >= MaxTextBoxesCount)
throw new ArgumentOutOfRangeException(nameof(positions0));
labels[dest] = labels[origin];
counters[dest] = counters[origin];
messages[dest] = messages[origin];
checkBoxes[dest] = checkBoxes[origin];
ckbIndex[origin] = dest;
log.WarnFormat(
"IPERL_DIALOG_LAYOUT_V3 Map: localIndex={0}, headPosition={1}, visualSlot={2}, labelControl={3}, messageControl={4}, checkBoxControl={5}",
dest,
headPosition0 + 1,
origin + 1,
labels[dest].Name,
messages[dest].Name,
checkBoxes[dest].Name);
}
textBoxesCount = wmsCount;
}
else if (wmsCount < textBoxesCount && lineSize > 0)
{
int nrLines = (wmsCount + lineSize - 1) / lineSize;
int gap = (textBoxesCount - wmsCount) / nrLines;
for (int i = 0; i < ckbIndex.Length; i++)
{
ckbIndex[i] = -1; /// Initialize with invalid indices
}
int dest = 0;
for (int l = 0; l < nrLines; l++)
{
@ -454,10 +513,20 @@ namespace TBF.Rig.TestMethods.iPerlCommunication
textBoxesCount = wmsCount;
}
displayedHeadPositions0 = positions0 != null && positions0.Count == textBoxesCount
? positions0.ToList()
: Enumerable.Range(0, textBoxesCount).ToList();
int count = checkBoxesEditMode ? textBoxesCount : Math.Min(textBoxesCount, iperlHeads.Count);
for (int j = 0; j < count; j++)
{
labels[j].Text = (j + 1).ToString();
labels[j].Text = (displayedHeadPositions0[j] + 1).ToString();
log.WarnFormat(
"IPERL_DIALOG_LAYOUT_V3 Label: localIndex={0}, control={1}, text={2}",
j,
labels[j].Name,
labels[j].Text);
}
ResizeDlgToFitEnabledControls();
@ -3009,13 +3078,19 @@ namespace TBF.Rig.TestMethods.iPerlCommunication
/// <returns>Long bitfield</returns>
private long GetCheckBoxStates()
{
long result = 0;
for (int i = 0; i < 48; i++)
long result = Program.LocalSettings.OptoHeadsEnabled;
for (int localIndex = 0; localIndex < textBoxesCount; localIndex++)
{
int wmNr0 = ckbIndex[i];
if (wmNr0 >= 0 && ckbState[wmNr0])
int headPosition0 = displayedHeadPositions0[localIndex];
long mask = 1L << headPosition0;
if (ckbState[localIndex])
{
result += (1L << wmNr0);
result |= mask;
}
else
{
result &= ~mask;
}
}
return result;
@ -3026,26 +3101,22 @@ namespace TBF.Rig.TestMethods.iPerlCommunication
/// </summary>
private void SetCheckBoxStates(long state)
{
CheckBoxImage[] chkBoxes = new CheckBoxImage[48]
{
checkBoxImage1, checkBoxImage2, checkBoxImage3, checkBoxImage4, checkBoxImage5,
checkBoxImage6, checkBoxImage7, checkBoxImage8, checkBoxImage9, checkBoxImage10,
checkBoxImage11, checkBoxImage12, checkBoxImage13, checkBoxImage14, checkBoxImage15,
checkBoxImage16, checkBoxImage17, checkBoxImage18, checkBoxImage19, checkBoxImage20,
checkBoxImage21, checkBoxImage22, checkBoxImage23, checkBoxImage24, checkBoxImage25,
checkBoxImage26, checkBoxImage27, checkBoxImage28, checkBoxImage29, checkBoxImage30,
checkBoxImage31, checkBoxImage32, checkBoxImage33, checkBoxImage34, checkBoxImage35,
checkBoxImage36, checkBoxImage37, checkBoxImage38, checkBoxImage39, checkBoxImage40,
checkBoxImage41, checkBoxImage42, checkBoxImage43, checkBoxImage44, checkBoxImage45,
checkBoxImage46, checkBoxImage47, checkBoxImage48,
};
for (int i = 0; i < 48; i++)
log.WarnFormat(
"IPERL_DIALOG_LAYOUT_V3 SetCheckBoxStates: state=0x{0:X12}, displayedHeadPositions=[{1}]",
state,
string.Join(",", displayedHeadPositions0.Select(position => position + 1)));
for (int localIndex = 0; localIndex < textBoxesCount; localIndex++)
{
int wmNr0 = ckbIndex[i];
if (wmNr0 >= 0)
{
chkBoxes[i].Checked = ckbState[wmNr0] = ((state & (1L << wmNr0)) != 0);
}
int headPosition0 = displayedHeadPositions0[localIndex];
bool enabled = (state & (1L << headPosition0)) != 0;
checkBoxes[localIndex].Checked = ckbState[localIndex] = enabled;
log.WarnFormat(
"IPERL_DIALOG_LAYOUT_V3 Enabled: headPosition={0}, localIndex={1}, enabled={2}, control={3}",
headPosition0 + 1,
localIndex,
enabled,
checkBoxes[localIndex].Name);
}
}