From 91281c88135b5c3f6d70bbfe832127e9b762f1f1 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sat, 14 Mar 2026 20:51:29 -0400 Subject: [PATCH] fix(engine): throttle progress reports to 150ms intervals Parallel loops were flooding the UI with per-angle/per-candidate reports faster than WinForms could render them. Use Interlocked timestamp checks to report at most every 150ms, keeping descriptions readable. Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest.Engine/NestEngine.cs | 39 ++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/OpenNest.Engine/NestEngine.cs b/OpenNest.Engine/NestEngine.cs index e6993f2..51615a4 100644 --- a/OpenNest.Engine/NestEngine.cs +++ b/OpenNest.Engine/NestEngine.cs @@ -285,6 +285,8 @@ namespace OpenNest var linearBag = new System.Collections.Concurrent.ConcurrentBag<(FillScore score, List parts)>(); var angleBag = new System.Collections.Concurrent.ConcurrentBag(); + long lastLinearReport = 0; + System.Threading.Tasks.Parallel.ForEach(angles, new System.Threading.Tasks.ParallelOptions { CancellationToken = token }, angle => @@ -305,14 +307,19 @@ namespace OpenNest angleBag.Add(new AngleResult { AngleDeg = angleDeg, Direction = NestDirection.Vertical, PartCount = v.Count }); } - var bestDir = (h?.Count ?? 0) >= (v?.Count ?? 0) ? "H" : "V"; - var bestCount = System.Math.Max(h?.Count ?? 0, v?.Count ?? 0); - progress?.Report(new NestProgress + var now = linearSw.ElapsedMilliseconds; + if (progress != null && now - Interlocked.Read(ref lastLinearReport) >= 150) { - Phase = NestPhase.Linear, - PlateNumber = PlateNumber, - Description = $"Linear: {angleDeg:F0}° {bestDir} - {bestCount} parts" - }); + Interlocked.Exchange(ref lastLinearReport, now); + var bestDir = (h?.Count ?? 0) >= (v?.Count ?? 0) ? "H" : "V"; + var bestCount = System.Math.Max(h?.Count ?? 0, v?.Count ?? 0); + progress.Report(new NestProgress + { + Phase = NestPhase.Linear, + PlateNumber = PlateNumber, + Description = $"Linear: {angleDeg:F0}° {bestDir} - {bestCount} parts" + }); + } }); linearSw.Stop(); AngleResults.AddRange(angleBag); @@ -515,6 +522,9 @@ namespace OpenNest try { + var pairsSw = Stopwatch.StartNew(); + long lastPairsReport = 0; + System.Threading.Tasks.Parallel.For(0, candidates.Count, new System.Threading.Tasks.ParallelOptions { CancellationToken = token }, i => @@ -528,12 +538,17 @@ namespace OpenNest if (filled != null && filled.Count > 0) resultBag.Add((FillScore.Compute(filled, workArea), filled)); - progress?.Report(new NestProgress + var now = pairsSw.ElapsedMilliseconds; + if (progress != null && now - Interlocked.Read(ref lastPairsReport) >= 150) { - Phase = NestPhase.Pairs, - PlateNumber = PlateNumber, - Description = $"Pairs: candidate {i + 1}/{candidates.Count} - {filled?.Count ?? 0} parts" - }); + Interlocked.Exchange(ref lastPairsReport, now); + progress.Report(new NestProgress + { + Phase = NestPhase.Pairs, + PlateNumber = PlateNumber, + Description = $"Pairs: candidate {i + 1}/{candidates.Count} - {filled?.Count ?? 0} parts" + }); + } }); } catch (OperationCanceledException)