mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-21 03:41:37 -04:00
fix(desktop): refuse Expand Spacing while a nest or fill is running
Expand now reads the plate on a worker while its Cancel window pumps messages, so a still-running fill could commit parts into the plate the search is enumerating (InvalidOperationException: collection modified). Refuse the command while whole-job nesting, a NestProgressForm or a fill on any view of the nest is active. The usual plate-action guard is not reused: ActionSelect reports busy whenever parts are selected, which Expand requires.
This commit is contained in:
1 parent
855d686a6a
commit
1db0ef04f0
3 files changed
+119
-4
No files matched your search
@@ -0,0 +1,78 @@
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using OpenNest.Forms;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.WinForms.Tests.Forms;
|
||||
|
||||
// Reads Application.OpenForms for the busy guard, so it must not overlap fill-lifetime tests
|
||||
// that transiently open NestProgressForm.
|
||||
[Collection("Fill operation lifetime")]
|
||||
public class ExpandSpacingMenuTests
|
||||
{
|
||||
[Fact]
|
||||
public void RunningFillOnTheNestRefusesExpandWithoutMovingParts() => StaTestThread.Run(() =>
|
||||
{
|
||||
using var host = new MenuHost();
|
||||
host.Show();
|
||||
var nest = new Nest("expand job");
|
||||
var plate = nest.CreatePlate();
|
||||
plate.Size = new Size(100, 100);
|
||||
plate.Parts.Add(new Part(new Drawing("left", Square()), new Vector(1, 1)));
|
||||
plate.Parts.Add(new Part(new Drawing("right", Square()), new Vector(12, 1)));
|
||||
using var editor = new EditNestForm(nest) { MdiParent = host };
|
||||
editor.PlateView.SetOverlapAutoCheck(null); // Background overlap workers are irrelevant here.
|
||||
editor.Show();
|
||||
editor.Activate();
|
||||
editor.PlateManager.LoadAt(0);
|
||||
editor.PlateView.SelectAll();
|
||||
var before = plate.Parts.Select(p => p.Location).ToArray();
|
||||
var notices = new List<string>();
|
||||
host.ExpandSpacingNotice = notices.Add;
|
||||
|
||||
SetBackingField(editor.PlateView, "IsFillInProgress", true);
|
||||
try
|
||||
{
|
||||
Assert.True(host.IsExpandSpacingBusy(editor));
|
||||
Menu(host, "mnuPlateExpandSpacing").PerformClick();
|
||||
|
||||
Assert.Contains("Finish or cancel", Assert.Single(notices));
|
||||
Assert.Equal(before, plate.Parts.Select(p => p.Location).ToArray());
|
||||
}
|
||||
finally
|
||||
{
|
||||
SetBackingField(editor.PlateView, "IsFillInProgress", false);
|
||||
}
|
||||
|
||||
// Selected parts keep ActionSelect busy; that alone must not refuse Expand.
|
||||
Assert.True(editor.PlateView.Actions.CurrentAction?.IsBusy() == true);
|
||||
Assert.False(host.IsExpandSpacingBusy(editor));
|
||||
}, TimeSpan.FromMinutes(1), "The STA test did not complete.");
|
||||
|
||||
private static CNC.Program Square()
|
||||
{
|
||||
var program = new CNC.Program();
|
||||
program.MoveTo(0, 0);
|
||||
program.LineTo(0, 10);
|
||||
program.LineTo(10, 10);
|
||||
program.LineTo(10, 0);
|
||||
program.LineTo(0, 0);
|
||||
return program;
|
||||
}
|
||||
|
||||
private static ToolStripMenuItem Menu(MainForm host, string name) =>
|
||||
(ToolStripMenuItem)host.MainMenuStrip!.Items.Find(name, true).Single();
|
||||
|
||||
// Same reflection precedent PlateViewFillLifetimeTests uses for fill-lifetime state.
|
||||
private static void SetBackingField(object target, string property, object value) =>
|
||||
target.GetType()
|
||||
.GetField($"<{property}>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||||
.SetValue(target, value);
|
||||
|
||||
private sealed class MenuHost : MainForm
|
||||
{
|
||||
// No startup migrations/automatic new document, and no settings save at close.
|
||||
protected override void OnLoad(EventArgs e) { }
|
||||
protected override void OnClosing(System.ComponentModel.CancelEventArgs e) { }
|
||||
}
|
||||
}
|
||||
@@ -1299,11 +1299,22 @@ namespace OpenNest.Forms
|
||||
|
||||
private void ExpandSpacing_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null)
|
||||
var editForm = activeForm;
|
||||
if (editForm == null)
|
||||
return;
|
||||
|
||||
// The search reads the plate on a worker, so a nest or fill that can still
|
||||
// add parts must finish first.
|
||||
if (IsExpandSpacingBusy(editForm))
|
||||
{
|
||||
ShowExpandSpacingNotice(
|
||||
"Finish or cancel the current nesting or fill before Expand Spacing."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Complex layouts can search for minutes; run behind a Cancel window.
|
||||
var expanded = activeForm.PlateView.ExpandSelected(work =>
|
||||
var expanded = editForm.PlateView.ExpandSelected(work =>
|
||||
OperationProgressForm.Run(
|
||||
this,
|
||||
"Expand Spacing",
|
||||
@@ -1313,8 +1324,34 @@ namespace OpenNest.Forms
|
||||
);
|
||||
|
||||
if (!expanded)
|
||||
ShowExpandSpacingNotice("Select at least two parts on the plate to expand.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True while whole-job nesting or a fill on any view of the nest can still
|
||||
/// change its plates. Unlike the other plate commands' guard, a busy current
|
||||
/// action does not count: ActionSelect reports busy whenever parts are
|
||||
/// selected, and Expand Spacing needs a selection.
|
||||
/// </summary>
|
||||
internal bool IsExpandSpacingBusy(EditNestForm editForm) =>
|
||||
nestingInProgress
|
||||
|| Application.OpenForms.OfType<NestProgressForm>().Any()
|
||||
|| MdiChildren
|
||||
.OfType<EditNestForm>()
|
||||
.Where(form => ReferenceEquals(form.Nest, editForm.Nest))
|
||||
.Any(form => form.PlateView.IsFillInProgress);
|
||||
|
||||
// Native tests capture this notice without opening a modal dialog in session zero.
|
||||
internal System.Action<string> ExpandSpacingNotice { get; set; }
|
||||
|
||||
private void ShowExpandSpacingNotice(string message)
|
||||
{
|
||||
if (ExpandSpacingNotice != null)
|
||||
ExpandSpacingNotice(message);
|
||||
else
|
||||
MessageBox.Show(
|
||||
"Select at least two parts on the plate to expand.",
|
||||
this,
|
||||
message,
|
||||
"Expand Spacing",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
|
||||
@@ -8,4 +8,4 @@ Probes run on scratch poses. Each trial translation is clipped against blocking
|
||||
|
||||
## Cancelling a long search
|
||||
|
||||
On a complex layout the search can run for minutes. The desktop command runs it on a worker thread; if it is still running after about 0.3 seconds, a small Expand Spacing window opens with an elapsed time and a Cancel button, and the rest of the application waits behind it. Cancel, Escape and the window's close box all request cancellation. The window then shows "Cancelling..." and stays open until the search stops at its next pair check, which took under 0.3 seconds on the real jobs measured. A cancelled search moves no part, and the status bar reads "Expand Spacing cancelled - no parts were moved". A search that finishes before the cancellation takes effect applies its result normally. The command does not save automatically.
|
||||
On a complex layout the search can run for minutes. The desktop command runs it on a worker thread; if it is still running after about 0.3 seconds, a small Expand Spacing window opens with an elapsed time and a Cancel button, and the rest of the application waits behind it. Cancel, Escape and the window's close box all request cancellation. The window then shows "Cancelling..." and stays open until the search stops at its next pair check, which took under 0.3 seconds on the real jobs measured. A cancelled search moves no part, and the status bar reads "Expand Spacing cancelled - no parts were moved". A search that finishes before the cancellation takes effect applies its result normally. While Auto Nest or a fill is still running on the nest, the command refuses with "Finish or cancel the current nesting or fill before Expand Spacing." because those operations can still add parts to the plate the search reads. The command does not save automatically.
|
||||
Reference in new issue
Block a user