mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-26 22:01:06 -04:00
feat(ui): allow clearing lead-ins and cut-offs while a cut plan runs
The Clear Lead-ins & Cut-offs button stays enabled during planning. Confirming cancels the running plan, clears once the cancellation lands so the plates never change under the planner, then replans. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
78ca08efe1
commit
bbe0f17a64
3 files changed
+94
-7
No files matched your search
@@ -675,6 +675,58 @@ public class CuttingInspectionPanelTests
|
||||
panel.RequestClose();
|
||||
});
|
||||
|
||||
[Fact]
|
||||
public void ClearingWhilePlanningCancelsTheRunThenClearsAndReplans() => RunSta(() =>
|
||||
{
|
||||
using var editor = Editor();
|
||||
editor.Show();
|
||||
editor.PlateManager.LoadAt(0);
|
||||
var plate = editor.PlateView.Plate;
|
||||
editor.OpenCuttingInspection();
|
||||
var first = editor.CuttingInspection!;
|
||||
PumpUntil(() => first.Proposal != null && !first.IsPlanning);
|
||||
Field<Button>(first, "applyButton").PerformClick();
|
||||
Assert.All(plate.Parts, part => Assert.True(part.HasManualLeadIns));
|
||||
|
||||
using var entered = new ManualResetEventSlim();
|
||||
using var release = new ManualResetEventSlim();
|
||||
var runs = 0;
|
||||
var leadsDuringRuns = new List<bool>();
|
||||
editor.CuttingBeforePlan = _ =>
|
||||
{
|
||||
leadsDuringRuns.Add(plate.Parts.Any(part => part.HasManualLeadIns));
|
||||
if (Interlocked.Increment(ref runs) > 1) return;
|
||||
entered.Set();
|
||||
release.Wait(TimeSpan.FromSeconds(30));
|
||||
};
|
||||
try
|
||||
{
|
||||
editor.OpenCuttingInspection();
|
||||
var panel = editor.CuttingInspection!;
|
||||
PumpUntil(() => entered.IsSet);
|
||||
Assert.True(panel.IsPlanning);
|
||||
var button = Field<Button>(panel, "clearExistingButton");
|
||||
Assert.True(button.Enabled);
|
||||
|
||||
panel.ConfirmClearExisting = _ => true;
|
||||
button.PerformClick();
|
||||
// The clear waits for the running plan to cancel; the plates are untouched until then.
|
||||
Assert.All(plate.Parts, part => Assert.True(part.HasManualLeadIns));
|
||||
release.Set();
|
||||
|
||||
PumpUntil(() => panel.Proposal != null && !panel.IsPlanning);
|
||||
Assert.Equal([true, false], leadsDuringRuns);
|
||||
Assert.All(plate.Parts, part => Assert.False(part.HasManualLeadIns));
|
||||
Assert.False(Field<Control>(panel, "existingWorkBanner").Visible);
|
||||
Assert.True(panel.Proposal.CanApply);
|
||||
panel.RequestClose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
release.Set();
|
||||
}
|
||||
});
|
||||
|
||||
[Fact]
|
||||
public void CutOffSettingsOpensTheAutomaticCutOffDialogAndReplansWithItsChoice() => RunSta(() =>
|
||||
{
|
||||
|
||||
@@ -39,6 +39,7 @@ internal sealed class CuttingInspectionPanel : UserControl
|
||||
private CuttingInspectionMode? pendingMode;
|
||||
private bool configuringMode;
|
||||
private bool pendingReplan;
|
||||
private bool pendingClear;
|
||||
internal CuttingInspectionMode Mode { get; private set; }
|
||||
internal PostVerificationReport ValidationReport { get; private set; }
|
||||
internal Func<Nest, CancellationToken, Task<PostVerificationReport>> Analyze { get; set; }
|
||||
@@ -647,7 +648,7 @@ internal sealed class CuttingInspectionPanel : UserControl
|
||||
inspection.CompletePlanning(work);
|
||||
if (closeRequested || pendingMode != null)
|
||||
{
|
||||
pendingReplan = false;
|
||||
pendingReplan = pendingClear = false;
|
||||
FinishTransition();
|
||||
return;
|
||||
}
|
||||
@@ -656,6 +657,15 @@ internal sealed class CuttingInspectionPanel : UserControl
|
||||
pendingReplan = false;
|
||||
if (closed) return;
|
||||
inspection = new CuttingProposalInspection();
|
||||
if (pendingClear)
|
||||
{
|
||||
pendingClear = false; // The cancelled run no longer reads the plates.
|
||||
if (!ClearAppliedWork())
|
||||
{
|
||||
SetPlanning(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
StartPlanning();
|
||||
return;
|
||||
}
|
||||
@@ -799,7 +809,8 @@ internal sealed class CuttingInspectionPanel : UserControl
|
||||
{
|
||||
settingsButton.Enabled = keepOrderCheckBox.Enabled = planButton.Enabled = !running;
|
||||
cutOffSettingsButton.Enabled = !running;
|
||||
clearExistingButton.Enabled = !running;
|
||||
// Clearing mid-plan cancels the run and replans, so it stays enabled unless closing or switching.
|
||||
clearExistingButton.Enabled = !running || Mode == CuttingInspectionMode.Plan && !closeRequested && pendingMode == null;
|
||||
previousButton.Enabled = !running && selectedPlate > 0;
|
||||
nextButton.Enabled = !running && selectedPlate < plates.Length - 1;
|
||||
acceptWarningsCheckBox.Enabled = !running;
|
||||
@@ -1149,9 +1160,13 @@ internal sealed class CuttingInspectionPanel : UserControl
|
||||
existingWorkBanner.Visible = Mode == CuttingInspectionMode.Plan && summary.HasExistingWork;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asks, then clears the applied lead-ins and cut-offs and replans. While a plan is running the
|
||||
/// clear waits for that run's cancellation to land, so the live plates never change under it.
|
||||
/// </summary>
|
||||
private void ClearExistingWork()
|
||||
{
|
||||
if (closed || closeRequested || IsPlanning || Mode != CuttingInspectionMode.Plan)
|
||||
if (closed || closeRequested || pendingMode != null || pendingClear || Mode != CuttingInspectionMode.Plan)
|
||||
return;
|
||||
var summary = AppliedCuttingWork.Summarize(plates, plateNumbers, stagedCutOffs?.Definitions);
|
||||
if (!summary.HasExistingWork)
|
||||
@@ -1163,8 +1178,26 @@ internal sealed class CuttingInspectionPanel : UserControl
|
||||
var confirmed = ConfirmClearExisting?.Invoke(message)
|
||||
?? MessageBox.Show(editor, message, "Clear Lead-ins and Cut-offs", MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes;
|
||||
if (!confirmed || closed || closeRequested || IsPlanning)
|
||||
if (!confirmed || closed || closeRequested || pendingMode != null || pendingClear)
|
||||
return;
|
||||
if (IsPlanning)
|
||||
{
|
||||
pendingClear = true;
|
||||
pendingReplan = true;
|
||||
ClearResult();
|
||||
SetPlanning(true);
|
||||
SetStatus(Tone.Neutral, "Cancelling the plan to clear lead-ins and cut-offs…");
|
||||
if (!inspection.CloseRequested)
|
||||
inspection.RequestClose();
|
||||
return;
|
||||
}
|
||||
if (ClearAppliedWork())
|
||||
StartPlanning();
|
||||
}
|
||||
|
||||
/// <summary>Removes the applied lead-ins and cut-offs from the plates in scope; false when it stopped part-way.</summary>
|
||||
private bool ClearAppliedWork()
|
||||
{
|
||||
ClearResult();
|
||||
try
|
||||
{
|
||||
@@ -1175,7 +1208,7 @@ internal sealed class CuttingInspectionPanel : UserControl
|
||||
{
|
||||
RefreshExistingWork();
|
||||
SetStatus(Tone.Bad, "Clearing stopped part-way. Review the plates before planning or posting.", ex.Message);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -1187,7 +1220,7 @@ internal sealed class CuttingInspectionPanel : UserControl
|
||||
view.Invalidate();
|
||||
editor.UpdatePlateList();
|
||||
}
|
||||
StartPlanning();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void RequestClose()
|
||||
|
||||
@@ -519,7 +519,9 @@ panel is wired in the editor. Non-editor callers still use the shared analyzer.
|
||||
plan keeps locked lead-ins and existing cut-offs unchanged. `Clear Lead-ins &
|
||||
Cut-offs...` asks first, then removes every applied lead-in (locked and hand-placed
|
||||
ones too), every cut-off and the plates' recorded cutting settings, keeping part
|
||||
poses, order and quantities, and replans from scratch. There is no undo.
|
||||
poses, order and quantities, and replans from scratch. There is no undo. The button stays
|
||||
enabled while a plan runs: confirming cancels that run, then clears once the cancellation
|
||||
lands (so the plates never change under the planner) and replans.
|
||||
- `Add scrap cut-offs` runs the automatic cut-off planner (defaults 35 in /
|
||||
889 mm strips, half-strip minimum, 12 in / 304.8 mm tail) on every plate in scope
|
||||
*before* capture, so the cutting plan sequences the new lines like any
|
||||
|
||||
Reference in new issue
Block a user