feat(ui): make RunAutoNest_Click async with progress and cancellation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 08:31:31 -04:00
parent af0748fb1b
commit 0bf128b3e7
+101 -22
View File
@@ -1,8 +1,11 @@
using System; using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using OpenNest.Actions; using OpenNest.Actions;
using OpenNest.Collections; using OpenNest.Collections;
@@ -18,6 +21,8 @@ namespace OpenNest.Forms
{ {
private EditNestForm activeForm; private EditNestForm activeForm;
private bool clickUpdateLocation; private bool clickUpdateLocation;
private bool nestingInProgress;
private CancellationTokenSource nestingCts;
private const float ZoomInFactor = 1.5f; private const float ZoomInFactor = 1.5f;
private const float ZoomOutFactor = 1.0f / ZoomInFactor; private const float ZoomOutFactor = 1.0f / ZoomInFactor;
@@ -165,6 +170,25 @@ namespace OpenNest.Forms
} }
} }
private void SetNestingLockout(bool locked)
{
nestingInProgress = locked;
// Disable nesting-related menus while running
mnuNest.Enabled = !locked;
mnuPlate.Enabled = !locked;
// Lock plate navigation
mnuNestPreviousPlate.Enabled = !locked && activeForm != null && !activeForm.IsFirstPlate();
btnPreviousPlate.Enabled = mnuNestPreviousPlate.Enabled;
mnuNestNextPlate.Enabled = !locked && activeForm != null && !activeForm.IsLastPlate();
btnNextPlate.Enabled = mnuNestNextPlate.Enabled;
mnuNestFirstPlate.Enabled = !locked && activeForm != null && activeForm.PlateCount > 0 && !activeForm.IsFirstPlate();
btnFirstPlate.Enabled = mnuNestFirstPlate.Enabled;
mnuNestLastPlate.Enabled = !locked && activeForm != null && activeForm.PlateCount > 0 && !activeForm.IsLastPlate();
btnLastPlate.Enabled = mnuNestLastPlate.Enabled;
}
private void UpdateLocationStatus() private void UpdateLocationStatus()
{ {
if (activeForm == null) if (activeForm == null)
@@ -689,7 +713,7 @@ namespace OpenNest.Forms
activeForm.LoadNextPlate(); activeForm.LoadNextPlate();
} }
private void RunAutoNest_Click(object sender, EventArgs e) private async void RunAutoNest_Click(object sender, EventArgs e)
{ {
var form = new AutoNestForm(activeForm.Nest); var form = new AutoNestForm(activeForm.Nest);
form.AllowPlateCreation = true; form.AllowPlateCreation = true;
@@ -699,41 +723,96 @@ namespace OpenNest.Forms
var items = form.GetNestItems(); var items = form.GetNestItems();
while (items.Any(it => it.Quantity > 0)) if (!items.Any(it => it.Quantity > 0))
return;
nestingCts = new CancellationTokenSource();
var token = nestingCts.Token;
var progressForm = new NestProgressForm(nestingCts, showPlateRow: true);
var plateNumber = 1;
var progress = new Progress<NestProgress>(p =>
{ {
var plate = activeForm.PlateView.Plate.Parts.Count > 0 progressForm.UpdateProgress(p);
? activeForm.Nest.CreatePlate() activeForm.PlateView.SetTemporaryParts(p.BestParts);
: activeForm.PlateView.Plate; });
var engine = new NestEngine(plate); progressForm.Show(this);
var filled = false; SetNestingLockout(true);
foreach (var item in items) try
{
while (items.Any(it => it.Quantity > 0))
{ {
if (item.Quantity <= 0) if (token.IsCancellationRequested)
continue; break;
if (engine.Fill(item)) var plate = activeForm.PlateView.Plate.Parts.Count > 0
filled = true; ? activeForm.Nest.CreatePlate()
} : activeForm.PlateView.Plate;
if (!filled) // If a new plate was created, switch to it
break; if (plate != activeForm.PlateView.Plate)
activeForm.LoadLastPlate();
// Decrement requested quantities by counting parts actually var engine = new NestEngine(plate) { PlateNumber = plateNumber };
// placed on this plate, grouped by drawing. var filled = false;
foreach (var group in plate.Parts.GroupBy(p => p.BaseDrawing))
{
var placed = group.Count();
foreach (var item in items) foreach (var item in items)
{ {
if (item.Drawing == group.Key) if (item.Quantity <= 0)
item.Quantity -= placed; continue;
if (token.IsCancellationRequested)
break;
// Run the engine on a background thread
var parts = await Task.Run(() =>
engine.Fill(item, plate.WorkArea(), progress, token));
if (parts.Count == 0)
continue;
filled = true;
// Count parts per drawing before accepting (for quantity tracking)
foreach (var group in parts.GroupBy(p => p.BaseDrawing))
{
var placed = group.Count();
foreach (var ni in items)
{
if (ni.Drawing == group.Key)
ni.Quantity -= placed;
}
}
// Accept the preview parts into the real plate
activeForm.PlateView.AcceptTemporaryParts();
} }
if (!filled)
break;
plateNumber++;
} }
activeForm.Nest.UpdateDrawingQuantities(); activeForm.Nest.UpdateDrawingQuantities();
progressForm.ShowCompleted();
}
catch (Exception ex)
{
activeForm.PlateView.ClearTemporaryParts();
MessageBox.Show($"Nesting error: {ex.Message}", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
progressForm.Close();
SetNestingLockout(false);
nestingCts.Dispose();
nestingCts = null;
} }
} }