<Feat>: Add per-phase retries for failed preadjustment slots, increase revision 3.9.3146.112
Cause: - Failed meters were excluded without retrying the current phase. - The procedure Temperature column was redundant when using T1/T2 sensors. Solution: 1. Added configurable retries - Replace the Temperature column with Retries on failure. - Interpret the value as additional attempts; default to zero. - Preserve legacy Temperature XML compatibility. 2. Added failed-slot retry execution - Retry only failed slots, in parallel, within the current phase. - Preserve successful slots and their captured results. - Restore the complete GCI batch after each attempt. - Exclude unsuccessful slots after retries are exhausted. 3. Improved attempt diagnostics - Log attempt numbers and outcomes to slot windows and files. - Keep successful slot tabs visible during retries. 4. Preserved execution behavior - Keep existing measurement algorithms, sensor readings and port handling. - Process STOP during retries and hold once after all attempts finish. - Prevent further retries after cancellation or streaming cleanup failure. 5. Increase revision 3.9.3146.112
This commit is contained in:
@@ -1656,6 +1656,7 @@ namespace GenesisCordonelInterface.API
|
||||
CancellationToken token)
|
||||
{
|
||||
const string operation = nameof(Preadjustment_Initialization);
|
||||
VerifyPreadjustmentAttemptAccess();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -1731,6 +1732,7 @@ namespace GenesisCordonelInterface.API
|
||||
CancellationToken token = default)
|
||||
{
|
||||
const string operation = nameof(Preadjustment_Detect);
|
||||
VerifyPreadjustmentAttemptAccess();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -1996,6 +1998,7 @@ namespace GenesisCordonelInterface.API
|
||||
CancellationToken token = default)
|
||||
{
|
||||
const string operation = nameof(PreAdjustment_Preparation);
|
||||
VerifyPreadjustmentAttemptAccess();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -2100,6 +2103,7 @@ namespace GenesisCordonelInterface.API
|
||||
CancellationToken token = default)
|
||||
{
|
||||
const string operation = nameof(PreAdjustment_AmplitudeTest);
|
||||
VerifyPreadjustmentAttemptAccess();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -2217,6 +2221,7 @@ namespace GenesisCordonelInterface.API
|
||||
int slot, CancellationToken token, CalibrationTemperatureInput temperature)
|
||||
{
|
||||
const string operation = nameof(PreAdjustment_TemperatureCalibration);
|
||||
VerifyPreadjustmentAttemptAccess();
|
||||
BaseProcess process = null;
|
||||
int[] participatingSlots = new int[0];
|
||||
try
|
||||
@@ -2353,6 +2358,7 @@ namespace GenesisCordonelInterface.API
|
||||
CancellationToken token = default)
|
||||
{
|
||||
const string operation = nameof(PreAdjustment_OffsetTest);
|
||||
VerifyPreadjustmentAttemptAccess();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -2456,6 +2462,7 @@ namespace GenesisCordonelInterface.API
|
||||
CancellationToken token = default)
|
||||
{
|
||||
const string operation = nameof(PreAdjustment_Completion);
|
||||
VerifyPreadjustmentAttemptAccess();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -2572,6 +2579,101 @@ namespace GenesisCordonelInterface.API
|
||||
#endregion
|
||||
|
||||
#region ================================== PreAdjustmentUI ExecuteProcess ==================================
|
||||
private readonly AsyncLocal<object> preadjustmentAttemptContext = new AsyncLocal<object>();
|
||||
private object activePreadjustmentAttempt;
|
||||
|
||||
private void VerifyPreadjustmentAttemptAccess()
|
||||
{
|
||||
lock (preadjustmentProcessGate)
|
||||
{
|
||||
if (activePreadjustmentAttempt != null &&
|
||||
!ReferenceEquals(activePreadjustmentAttempt, preadjustmentAttemptContext.Value))
|
||||
throw new InvalidOperationException("Another preadjustment attempt owns the slot selection.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes one synchronous phase attempt with a temporary list of participating slots.
|
||||
/// The callback must return only after all phase workers and streaming leases are drained.
|
||||
/// This changes process participation only; it does not connect, disconnect or log in.
|
||||
/// </summary>
|
||||
public T ExecutePreadjustmentAttempt<T>(IEnumerable<int> slots, bool retry,
|
||||
bool initialization, CancellationToken token, Func<T> action)
|
||||
{
|
||||
if (slots == null) throw new ArgumentNullException(nameof(slots));
|
||||
if (action == null) throw new ArgumentNullException(nameof(action));
|
||||
var selected = new HashSet<int>(slots);
|
||||
if (selected.Count == 0 || selected.Any(s => s <= 0))
|
||||
throw new ArgumentException("A phase attempt requires valid slots.", nameof(slots));
|
||||
|
||||
List<MeterStateControl> original;
|
||||
Dictionary<int, bool> originalSelection;
|
||||
object previousContext = preadjustmentAttemptContext.Value;
|
||||
var owner = new object();
|
||||
lock (preadjustmentProcessGate)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
if (activePreadjustmentAttempt != null || activePreadjustmentCancellation != null ||
|
||||
(_progressProcess != null && _progressProcess.IsBusy))
|
||||
throw new InvalidOperationException("A preadjustment process is still running.");
|
||||
original = (_meterControls ?? new List<MeterStateControl>()).ToList();
|
||||
if (!initialization && selected.Any(s => !original.Any(c => c.Slot == s)))
|
||||
throw new InvalidOperationException("Initialize preadjustment before retrying a phase.");
|
||||
originalSelection = original.ToDictionary(c => c.Slot, c => IsSlotSelected(c.Slot));
|
||||
activePreadjustmentAttempt = owner;
|
||||
preadjustmentAttemptContext.Value = owner;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!initialization)
|
||||
{
|
||||
_meterControls = original.Where(c => selected.Contains(c.Slot)).ToList();
|
||||
foreach (var control in _meterControls)
|
||||
{
|
||||
if (retry)
|
||||
{
|
||||
// Retry only this phase's failed slots; keep meter calibration data.
|
||||
control.Failed = false;
|
||||
control.Ok = false;
|
||||
control.SetChecked(true);
|
||||
control.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
if (retry && _progressProcess != null) _progressProcess.Aborted = false;
|
||||
foreach (var control in original)
|
||||
SetSlotSelected(control.Slot, selected.Contains(control.Slot) && control.IsEnabled);
|
||||
}
|
||||
token.ThrowIfCancellationRequested();
|
||||
return action();
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
// Detect can create replacement controls. Merge these back by slot so successful
|
||||
// meters from an earlier attempt remain available for subsequent phases.
|
||||
if (!initialization)
|
||||
{
|
||||
var updated = (_meterControls ?? new List<MeterStateControl>())
|
||||
.Where(c => selected.Contains(c.Slot)).ToDictionary(c => c.Slot);
|
||||
_meterControls = original.Select(c => updated.ContainsKey(c.Slot) ? updated[c.Slot] : c).ToList();
|
||||
foreach (var control in _meterControls)
|
||||
SetSlotSelected(control.Slot, selected.Contains(control.Slot)
|
||||
? control.IsEnabled && !control.Failed : originalSelection[control.Slot]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (preadjustmentProcessGate)
|
||||
{
|
||||
activePreadjustmentAttempt = null;
|
||||
preadjustmentAttemptContext.Value = previousContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly object preadjustmentProcessGate = new object();
|
||||
private CancellationTokenSource activePreadjustmentCancellation;
|
||||
|
||||
@@ -2591,6 +2693,7 @@ namespace GenesisCordonelInterface.API
|
||||
CancellationTokenSource runSource;
|
||||
lock (preadjustmentProcessGate)
|
||||
{
|
||||
VerifyPreadjustmentAttemptAccess();
|
||||
if (activePreadjustmentCancellation != null || pp.IsBusy)
|
||||
throw new InvalidOperationException("A preadjustment process is still running.");
|
||||
token.ThrowIfCancellationRequested();
|
||||
@@ -2626,7 +2729,9 @@ namespace GenesisCordonelInterface.API
|
||||
// a modal MessageBox/PDF UI and would leave the batch waiting forever.
|
||||
// The interactive PreAdjustmentControl retains responsibility for reports.
|
||||
pp.DebugMessage(
|
||||
process.ProcessName + " failed; slot excluded from remaining preadjustment phases.",
|
||||
process.ProcessName + (activePreadjustmentAttempt != null
|
||||
? " failed in this attempt."
|
||||
: " failed; slot excluded from remaining preadjustment phases."),
|
||||
meterCtrl.Slot,
|
||||
"GCI",
|
||||
meterCtrl.Meter == null ? string.Empty : meterCtrl.Meter.PcbId);
|
||||
|
||||
Reference in New Issue
Block a user