Fix Flip Mode - ver. 3.9.3141.0

Add robust handling for form closure in UNI Data Entry: prevent operations on disposed/closing forms, enhance logging, and update logic in serial number and volume updates. Increment version to 3.9.3141.0.
This commit is contained in:
Michal Buzik 2026-08-19 19:13:41 +02:00
parent f34046df2a
commit b904d0ca41
4 changed files with 215 additions and 34 deletions

View File

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

View File

@ -76,6 +76,7 @@ namespace TBF.Rig.DataEntry.Uni
/// Set to 'true' when the form closes
public bool Completed { get { return completed; } }
bool completed;
private volatile bool isClosing;
private System.Windows.Forms.Timer autoCloseTimer;
private int autoCloseSecondsRemaining;
private string okButtonTextBeforeAutoClose;
@ -852,6 +853,12 @@ namespace TBF.Rig.DataEntry.Uni
private void UpdateSomethingBySerial(IRegReader eReader, string eSerialNumber)
{
if (!CanUpdateDataEntryUi())
{
log.Debug("DATA_ENTRY_SERIAL_UPDATE_SKIPPED Form is closing or disposed.");
return;
}
log.Debug($"RegReader name: {eReader.Name}, Serial No updated: " + eSerialNumber);
PopulateComboBoxWithSerialNumbers(eReader, eSerialNumber);
}
@ -910,6 +917,12 @@ namespace TBF.Rig.DataEntry.Uni
}
private void DoneUpdate()
{
if (!CanUpdateDataEntryUi())
{
log.Debug("DATA_ENTRY_SERIAL_DONE_SKIPPED Form is closing or disposed.");
return;
}
log.Debug("DoneUpdate");
//enbale only found checkboxes
@ -998,12 +1011,15 @@ namespace TBF.Rig.DataEntry.Uni
private void SetSerialNumberWatermark(IRegReader reader, string watermark)
{
if (reader == null || regReaders == null || comboBoxes == null)
if (reader == null || regReaders == null || comboBoxes == null ||
!CanUpdateDataEntryUi())
return;
if (IsHandleCreated && InvokeRequired)
if (InvokeRequired)
{
BeginInvoke(new Action(() => SetSerialNumberWatermark(reader, watermark)));
TryBeginDataEntryInvoke(
() => SetSerialNumberWatermark(reader, watermark),
"serial-number watermark");
return;
}
@ -1176,6 +1192,15 @@ namespace TBF.Rig.DataEntry.Uni
Close();
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
isClosing = true;
StopAutoCloseCountdown();
SerialNumberRead = null;
DoneUpdateBySerial = null;
base.OnFormClosing(e);
}
#endregion
public event EventHandler<SerialNumberReadEventArgs> SerialNumberRead;
@ -1211,39 +1236,93 @@ namespace TBF.Rig.DataEntry.Uni
IsHandleCreated, InvokeRequired,
System.Threading.Thread.CurrentThread.ManagedThreadId);
var handler = SerialNumberRead; // copy for thread-safety
if (handler == null) return;
if (IsHandleCreated && InvokeRequired)
if (handler == null || !CanUpdateDataEntryUi())
{
BeginInvoke(new Action(() =>
{
log.DebugFormat(
"DATA_ENTRY_SERIAL_EVENT_DISPATCH Name={0}, Position={1}, Thread={2}",
reader == null ? "<null>" : reader.Name,
reader == null ? -1 : reader.Position,
System.Threading.Thread.CurrentThread.ManagedThreadId);
handler(this, new SerialNumberReadEventArgs(reader, serial));
}));
log.Debug("DATA_ENTRY_SERIAL_EVENT_SKIPPED Form is closing or disposed.");
return;
}
else
Action dispatch = () =>
{
if (!CanUpdateDataEntryUi())
{
log.Debug("DATA_ENTRY_SERIAL_EVENT_DISPATCH_SKIPPED Form closed before dispatch.");
return;
}
log.DebugFormat(
"DATA_ENTRY_SERIAL_EVENT_DISPATCH Name={0}, Position={1}, Thread={2}",
reader == null ? "<null>" : reader.Name,
reader == null ? -1 : reader.Position,
System.Threading.Thread.CurrentThread.ManagedThreadId);
handler(this, new SerialNumberReadEventArgs(reader, serial));
};
if (InvokeRequired)
{
TryBeginDataEntryInvoke(dispatch, "serial-number result");
return;
}
dispatch();
}
private bool CanUpdateDataEntryUi()
{
return !isClosing && !IsDisposed && !Disposing;
}
private void TryBeginDataEntryInvoke(Action action, string operation)
{
if (!CanUpdateDataEntryUi() || !IsHandleCreated)
return;
try
{
BeginInvoke(action);
}
catch (ObjectDisposedException)
{
log.DebugFormat(
"DATA_ENTRY_SERIAL_INVOKE_SKIPPED Operation={0}, Reason=disposed",
operation);
}
catch (InvalidOperationException ex)
{
log.DebugFormat(
"DATA_ENTRY_SERIAL_INVOKE_SKIPPED Operation={0}, Reason={1}",
operation,
ex.Message);
}
}
protected virtual void OnReadDone()
{
var handler = DoneUpdateBySerial; // copy for thread-safety
if (handler == null) return;
if (IsHandleCreated && InvokeRequired)
if (handler == null || !CanUpdateDataEntryUi())
{
BeginInvoke(new Action(() =>
handler(this, new SerialNumberReadDoneEventArgs())));
log.Debug("DATA_ENTRY_SERIAL_DONE_EVENT_SKIPPED Form is closing or disposed.");
return;
}
else
Action dispatch = () =>
{
if (!CanUpdateDataEntryUi())
{
log.Debug("DATA_ENTRY_SERIAL_DONE_DISPATCH_SKIPPED Form closed before dispatch.");
return;
}
handler(this, new SerialNumberReadDoneEventArgs());
};
if (InvokeRequired)
{
TryBeginDataEntryInvoke(dispatch, "serial-number completion");
return;
}
dispatch();
}
public async void ReadSerialNumbersAsync(IEnumerable<IRegReader> regReaders)
@ -1305,6 +1384,12 @@ namespace TBF.Rig.DataEntry.Uni
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
if (isClosing)
{
log.Debug("DATA_ENTRY_SERIAL_RESULTS_SKIPPED Form was closed while reading.");
return;
}
foreach (var kv in results)
{
var reader = kv.Key;

View File

@ -126,6 +126,7 @@ namespace TBF.Rig.DataEntry.Uni
/// Set to 'true' when the form closes
public bool Completed { get { return completed; } }
bool completed;
private volatile bool isClosing;
private System.Windows.Forms.Timer autoCloseTimer;
private int autoCloseSecondsRemaining;
private string okButtonTextBeforeAutoClose;
@ -476,6 +477,15 @@ namespace TBF.Rig.DataEntry.Uni
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
isClosing = true;
StopAutoCloseCountdown();
VolumeStartReadbyRegReader = null;
DoneUpdateByRegReader = null;
base.OnFormClosing(e);
}
void Localize()
{
okButton.Text = Strings.OkBtnText;
@ -669,6 +679,12 @@ namespace TBF.Rig.DataEntry.Uni
private void DoneUpdate()
{
if (!CanUpdateDataEntryUi())
{
log.Debug("DATA_ENTRY_VOLUME_DONE_SKIPPED Form is closing or disposed.");
return;
}
log.Debug("DoneUpdate");
this.okButton.Enabled = GetStoredOrDefault("okButton");
this.largeTextBox.Enabled = GetStoredOrDefault("largeTextBox");
@ -687,6 +703,12 @@ namespace TBF.Rig.DataEntry.Uni
public void UpdateVolume(IRegReader eArgsReader, double eArgsVolume)
{
if (!CanUpdateDataEntryUi())
{
log.Debug("DATA_ENTRY_VOLUME_UPDATE_SKIPPED Form is closing or disposed.");
return;
}
log.DebugFormat(
"DATA_ENTRY_VOLUME_UI_HANDLER Name={0}, Position={1}, DebugLevel={2}, Volume={3}, IsNaN={4}",
eArgsReader.Name, eArgsReader.Position, eArgsReader.DebugLevel,
@ -1584,31 +1606,87 @@ namespace TBF.Rig.DataEntry.Uni
protected virtual void OnReadVolume(IRegReader reader, double volume)
{
var handler = VolumeStartReadbyRegReader; // copy for thread-safety
if (handler == null) return;
if (IsHandleCreated && InvokeRequired)
if (handler == null || !CanUpdateDataEntryUi())
{
BeginInvoke(new Action(() =>
handler(this, new VolumeReadEventArgs(reader, volume))));
log.Debug("DATA_ENTRY_VOLUME_EVENT_SKIPPED Form is closing or disposed.");
return;
}
else
Action dispatch = () =>
{
if (!CanUpdateDataEntryUi())
{
log.Debug("DATA_ENTRY_VOLUME_EVENT_DISPATCH_SKIPPED Form closed before dispatch.");
return;
}
handler(this, new VolumeReadEventArgs(reader, volume));
};
if (InvokeRequired)
{
TryBeginDataEntryInvoke(dispatch, "volume result");
return;
}
dispatch();
}
protected virtual void OnReadDone()
{
var handler = DoneUpdateByRegReader; // copy for thread-safety
if (handler == null) return;
if (IsHandleCreated && InvokeRequired)
if (handler == null || !CanUpdateDataEntryUi())
{
BeginInvoke(new Action(() =>
handler(this, new VolumeReadDoneEventArgs())));
log.Debug("DATA_ENTRY_VOLUME_DONE_EVENT_SKIPPED Form is closing or disposed.");
return;
}
else
Action dispatch = () =>
{
if (!CanUpdateDataEntryUi())
{
log.Debug("DATA_ENTRY_VOLUME_DONE_DISPATCH_SKIPPED Form closed before dispatch.");
return;
}
handler(this, new VolumeReadDoneEventArgs());
};
if (InvokeRequired)
{
TryBeginDataEntryInvoke(dispatch, "volume completion");
return;
}
dispatch();
}
private bool CanUpdateDataEntryUi()
{
return !isClosing && !IsDisposed && !Disposing;
}
private void TryBeginDataEntryInvoke(Action action, string operation)
{
if (!CanUpdateDataEntryUi() || !IsHandleCreated)
return;
try
{
BeginInvoke(action);
}
catch (ObjectDisposedException)
{
log.DebugFormat(
"DATA_ENTRY_VOLUME_INVOKE_SKIPPED Operation={0}, Reason=disposed",
operation);
}
catch (InvalidOperationException ex)
{
log.DebugFormat(
"DATA_ENTRY_VOLUME_INVOKE_SKIPPED Operation={0}, Reason={1}",
operation,
ex.Message);
}
}
@ -1663,6 +1741,12 @@ namespace TBF.Rig.DataEntry.Uni
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
if (isClosing)
{
log.Debug("DATA_ENTRY_VOLUME_RESULTS_SKIPPED Form was closed while reading.");
return;
}
foreach (var kv in results)
{
var reader = kv.Key;

View File

@ -1321,6 +1321,9 @@ namespace TBF.Rig.TestMethods.StandingStartMassCollection
Results.Entities.MeterTestRslt meterRslt = BatchRslts.GetMeterTestRslt(testName, i, Common.CompoundMeterId.Single);
GenericDevices.IRegReader regReader = sensPath.RegisterReaders[i];
#if IPERL
TestMethods.iPerlCommunication.iPerlHead.IperlHead iPerl = regReader as TestMethods.iPerlCommunication.iPerlHead.IperlHead;
#endif
if (meterRslt != null && regReader != null)
{
@ -1404,6 +1407,15 @@ namespace TBF.Rig.TestMethods.StandingStartMassCollection
{
meterRslt.WaterMeter.SerialNr = regReaderCommon.SerialNr;
}
#if IPERL
if (iPerl != null)
{
meterRslt.FlipMode = iPerl.ConfigStruct != null && iPerl.ConfigStruct.FlipMode.HasValue
? (int?)iPerl.ConfigStruct.FlipMode.Value
: null;
}
#endif
}
}