Files
OpenNest/OpenNest.Desktop/Forms/JobEngineNest.cs
T
aj 2db8f5f841 refactor(desktop): move EngineSelection and JobEngineNest into OpenNest.Desktop
The engine selection and the whole-job progress adapter use only Engine
and Data, so they move into the toolkit-neutral library for the Avalonia
front end to share. The namespace stays OpenNest.Forms, so WinForms
callers are unchanged.

Linux tests now cover the adapter's stage routing, one-based plate
numbers and descriptions, and the fill-strategy mapping; the saved
selection is left out because it reads and writes the real per-user file.
2026-10-29 19:14:55 -04:00

90 lines
3.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using OpenNest.Engine;
using OpenNest.Engine.Jobs;
using OpenNest.Engine.Jobs.Adapters;
using OpenNest.Geometry;
namespace OpenNest.Forms
{
/// <summary>
/// Desktop progress adapter for every whole-job engine. Building, validation and binding
/// belong to the shared NestPipeline.
/// </summary>
internal static class JobEngineNest
{
/// <summary>
/// Forwards job progress to a NestProgress sink. An engine's optional LegacyProgress detail
/// (live preview parts) passes through; otherwise the stage and committed counts become a
/// description. Committed counts are authoritative only after PlateCommitted.
/// </summary>
public static IProgress<NestJobProgress> CreateProgress(
string engineName,
IProgress<NestProgress> progress
) => new ProgressAdapter(engineName, progress, null);
/// <summary>
/// As <see cref="CreateProgress(string, IProgress{NestProgress})"/>, and also sends each
/// best-plan-so-far report's sheets to <paramref name="plans"/>.
/// </summary>
public static IProgress<NestJobProgress> CreateProgress(
string engineName,
IProgress<NestProgress> progress,
IProgress<IReadOnlyList<ProposedPlate>> plans
) => new ProgressAdapter(engineName, progress, plans);
public static string Describe(string engineName, NestJobProgress value) => value.Stage switch
{
NestJobStage.PlateCommitted =>
$"{engineName}: committed plate {value.CommittedPlates} ({value.CommittedParts} parts placed)",
NestJobStage.PlanImproved =>
$"{engineName}: found a better plan ({value.Plan?.Plates.Count ?? 0} sheets)",
_ => $"{engineName}: evaluating plate {WorkingPlate(value)} on stock {value.StockId}",
};
/// <summary>One-based plate under evaluation; nesters that do not know it report -1.</summary>
private static int WorkingPlate(NestJobProgress value) =>
value.PlateIndex >= 0 ? value.PlateIndex + 1 : value.CommittedPlates + 1;
private sealed class ProgressAdapter(
string engineName,
IProgress<NestProgress> progress,
IProgress<IReadOnlyList<ProposedPlate>> plans
) : IProgress<NestJobProgress>
{
public void Report(NestJobProgress value)
{
if (value == null)
return;
// A plan sink shows plans as sheets; the plate row keeps tracking the search.
if (value.Stage == NestJobStage.PlanImproved && plans != null)
{
if (value.PlanSheets != null)
plans.Report(value.PlanSheets);
return;
}
if (value.LegacyProgress != null)
{
progress.Report(value.LegacyProgress);
return;
}
progress.Report(
new NestProgress
{
Phase = NestPhase.Custom,
PlateNumber =
value.Stage == NestJobStage.PlateCommitted
? value.CommittedPlates
: WorkingPlate(value),
Description = Describe(engineName, value),
}
);
}
}
}
}