using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using OpenNest.Data.MaterialLedger; using OpenNest.Engine; using OpenNest.Engine.Jobs; namespace OpenNest.Desktop { /// /// What Make Nests' renest step kept, its note and the ledger codes' locations. /// public sealed record ReleaseRenestResult(bool Nested, string Note, IReadOnlyDictionary LedgerLocations); /// /// Make Nests' Auto Nest of one released nest: on the run's MaterialLedger stock first, /// then on the nest's plate defaults (), with the /// ledger codes given to the plates inside each commit, so they are part of its undo step. /// Shared by the headless and the live (main window) release. /// public static class ReleaseRenest { /// /// Attaches a kept result's sheets to the nest's plates and returns them. It must call /// finish once with those plates before its undo step closes; that gives them /// their ledger codes. Not calling it, or calling it twice, throws. /// public delegate IReadOnlyList Commit(NestPipelineResult result, Action> finish); /// /// Nests , which has no parts placed, on 's /// stock (null: the plate defaults only) and then its plate defaults. The ledger is not /// read when nothing has a quantity to nest. /// public static async Task RunAsync(Nest nest, ReleaseLedgerStock ledger, string engineName, double minimumSalvageDimension, Func> solve, Commit commit, CancellationToken token) { ArgumentNullException.ThrowIfNull(nest); ArgumentNullException.ThrowIfNull(solve); ArgumentNullException.ThrowIfNull(commit); var wanted = nest.Drawings.Any(d => !d.IsCutOff && d.Quantity.Remaining > 0); var offer = ledger != null && wanted ? await ledger.OfferAsync(nest, token) : null; var locations = new Dictionary(StringComparer.Ordinal); var sheets = 0; var renested = await NestRenest.RunStagedAsync(nest, engineName, offer?.Options, minimumSalvageDimension, solve, result => { var finished = false; var plates = commit(result, applied => { if (finished) throw new InvalidOperationException( "A release commit finished its plates twice."); finished = true; if (offer == null) return; var stock = NestPipelineCommit.PairWithStock(result, applied) .Select(p => (p.Plate, p.Stock.Id)) .ToList(); foreach (var (code, location) in ledger.AssignCodes(nest, offer, stock)) locations[code] = location; sheets += stock.Sum(p => p.Plate.StockItemCodes.Count); }); if (!finished) throw new InvalidOperationException( "A release commit did not finish its plates, so they have no codes."); return plates; }, token); var ledgerNote = offer == null ? null : ReleaseLedgerStock.Note(offer, sheets, renested.FirstStockNotKept); return new ReleaseRenestResult(renested.Nested, Join(renested.Note, ledgerNote), locations); } /// /// for a nest no one is looking at: every plate is removed, the /// layouts are solved on the calling thread and attached as new plates. /// public static Task RunHeadlessAsync(Nest nest, ReleaseLedgerStock ledger, INestingEngine engine, string engineName, double minimumSalvageDimension, CancellationToken token) { ArgumentNullException.ThrowIfNull(nest); ArgumentNullException.ThrowIfNull(engine); token.ThrowIfCancellationRequested(); nest.Plates.Clear(); nest.UpdateDrawingQuantities(); return RunAsync(nest, ledger, engineName, minimumSalvageDimension, request => Task.FromResult( NestPipeline.Run(engine, engineName, request, null, token)), (result, finish) => { var plates = NestPipelineCommit.ApplyToPlates(result, nest.CreatePlate, token: token); finish(plates); return plates; }, token); } /// Auto Nest's note, then what MaterialLedger stock the nest used. public static string Join(string renestNote, string ledgerNote) => string.Join(" ", new[] { renestNote, ledgerNote }.Where(n => !string.IsNullOrEmpty(n))); } }