Design for OpenNest.Api project providing a stateless NestRequest/NestResponse facade over the engine, IO, and timing layers. Includes CutParameters unification, multi-plate loop, .nestquote persistence format, and .opnest → .nest rename. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
239 lines
9.2 KiB
Markdown
239 lines
9.2 KiB
Markdown
# Nest API Design
|
|
|
|
## Overview
|
|
|
|
A new `OpenNest.Api` project providing a clean programmatic facade for nesting operations. A single `NestRequest` goes in, a self-contained `NestResponse` comes out. Designed for external callers (MCP server, console app, LaserQuote, future web API) that don't want to manually wire engine + timing + IO.
|
|
|
|
## Motivation
|
|
|
|
Today, running a nest programmatically requires manually coordinating:
|
|
1. DXF import (IO layer)
|
|
2. Plate/NestItem setup (Core)
|
|
3. Engine selection and execution (Engine layer)
|
|
4. Timing calculation (Core's `Timing` class)
|
|
5. File persistence (IO layer)
|
|
|
|
This design wraps all five steps behind a single stateless call. The response captures the original request, making nests reproducible and re-priceable months later.
|
|
|
|
## New Project: `OpenNest.Api`
|
|
|
|
Class library targeting `net8.0-windows`. References Core, Engine, and IO.
|
|
|
|
```
|
|
OpenNest.Api/
|
|
CutParameters.cs
|
|
NestRequest.cs
|
|
NestRequestPart.cs
|
|
NestStrategy.cs
|
|
NestResponse.cs
|
|
NestRunner.cs
|
|
```
|
|
|
|
All types live in the `OpenNest.Api` namespace.
|
|
|
|
## Types
|
|
|
|
### CutParameters
|
|
|
|
Unified timing and quoting parameters. Replaces the existing `OpenNest.CutParameters` in Core.
|
|
|
|
```csharp
|
|
namespace OpenNest.Api;
|
|
|
|
public class CutParameters
|
|
{
|
|
public double Feedrate { get; init; } // in/min or mm/sec depending on Units
|
|
public double RapidTravelRate { get; init; } // in/min or mm/sec
|
|
public TimeSpan PierceTime { get; init; }
|
|
public double LeadInLength { get; init; } // forward-looking: unused until Timing rework
|
|
public string PostProcessor { get; init; } // forward-looking: unused until Timing rework
|
|
public Units Units { get; init; }
|
|
|
|
public static CutParameters Default => new()
|
|
{
|
|
Feedrate = 100,
|
|
RapidTravelRate = 300,
|
|
PierceTime = TimeSpan.FromSeconds(0.5),
|
|
Units = Units.Inches
|
|
};
|
|
}
|
|
```
|
|
|
|
`LeadInLength` and `PostProcessor` are included for forward compatibility but will not be wired into `Timing.CalculateTime` until the Timing rework. Implementers should not attempt to use them in the initial implementation.
|
|
|
|
### NestRequestPart
|
|
|
|
A part to nest, identified by DXF file path. No `Drawing` reference — keeps the request fully serializable for persistence.
|
|
|
|
```csharp
|
|
namespace OpenNest.Api;
|
|
|
|
public class NestRequestPart
|
|
{
|
|
public string DxfPath { get; init; }
|
|
public int Quantity { get; init; } = 1;
|
|
public bool AllowRotation { get; init; } = true;
|
|
public int Priority { get; init; } = 0;
|
|
}
|
|
```
|
|
|
|
### NestStrategy
|
|
|
|
```csharp
|
|
namespace OpenNest.Api;
|
|
|
|
public enum NestStrategy { Auto }
|
|
```
|
|
|
|
- `Auto` maps to `DefaultNestEngine` (multi-phase fill).
|
|
- Additional strategies (`Linear`, `BestFit`, `Pack`) will be added later, driven by ML-based auto-detection of part type during the training work. The intelligence for selecting the best strategy for a given part will live inside `DefaultNestEngine`.
|
|
|
|
### NestRequest
|
|
|
|
Immutable input capturing everything needed to run and reproduce a nest.
|
|
|
|
```csharp
|
|
namespace OpenNest.Api;
|
|
|
|
public class NestRequest
|
|
{
|
|
public IReadOnlyList<NestRequestPart> Parts { get; init; } = [];
|
|
public Size SheetSize { get; init; } = new(60, 120); // OpenNest.Geometry.Size(width, length)
|
|
public string Material { get; init; } = "Steel, A1011 HR";
|
|
public double Thickness { get; init; } = 0.06;
|
|
public double Spacing { get; init; } = 0.1; // part-to-part spacing; edge spacing defaults to zero
|
|
public NestStrategy Strategy { get; init; } = NestStrategy.Auto;
|
|
public CutParameters Cutting { get; init; } = CutParameters.Default;
|
|
}
|
|
```
|
|
|
|
- `Parts` uses `IReadOnlyList<T>` to prevent mutation after construction, preserving reproducibility when the request is stored in the response.
|
|
- `Spacing` maps to `Plate.PartSpacing`. `Plate.EdgeSpacing` defaults to zero on all sides.
|
|
- `SheetSize` is `OpenNest.Geometry.Size` (not `System.Drawing.Size`).
|
|
|
|
### NestResponse
|
|
|
|
Immutable output containing computed metrics, the resulting `Nest`, and the original request for reproducibility.
|
|
|
|
```csharp
|
|
namespace OpenNest.Api;
|
|
|
|
public class NestResponse
|
|
{
|
|
public int SheetCount { get; init; }
|
|
public double Utilization { get; init; }
|
|
public TimeSpan CutTime { get; init; }
|
|
public TimeSpan Elapsed { get; init; }
|
|
public Nest Nest { get; init; }
|
|
public NestRequest Request { get; init; }
|
|
|
|
public Task SaveAsync(string path) => ...;
|
|
public static Task<NestResponse> LoadAsync(string path) => ...;
|
|
}
|
|
```
|
|
|
|
`SaveAsync`/`LoadAsync` live on the data class for API simplicity — a pragmatic choice over a separate IO helper class.
|
|
|
|
### NestRunner
|
|
|
|
Stateless orchestrator. Single public method.
|
|
|
|
```csharp
|
|
namespace OpenNest.Api;
|
|
|
|
public static class NestRunner
|
|
{
|
|
public static async Task<NestResponse> RunAsync(
|
|
NestRequest request,
|
|
IProgress<NestProgress> progress = null,
|
|
CancellationToken token = default)
|
|
{
|
|
// 1. Validate request (non-empty parts list, all DXF paths exist)
|
|
// 2. Import DXFs → Drawings via DxfImporter + ConvertGeometry.ToProgram
|
|
// 3. Create Plate from request.SheetSize / Thickness / Spacing
|
|
// 4. Convert NestRequestParts → NestItems
|
|
// 5. Multi-plate loop:
|
|
// a. Create engine via NestEngineRegistry
|
|
// b. Fill plate
|
|
// c. Deduct placed quantities
|
|
// d. If remaining quantities > 0, create next plate and repeat
|
|
// 6. Compute TimingInfo → CutTime using request.Cutting (placeholder for Timing rework)
|
|
// 7. Build and return NestResponse (with stopwatch for Elapsed)
|
|
}
|
|
}
|
|
```
|
|
|
|
- Static class, no state, no DI. If we need dependency injection later, we add an instance-based overload.
|
|
- `Timing.CalculateTime` is called with the new `CutParameters` (placeholder integration — `Timing` will be reworked later).
|
|
|
|
#### Multi-plate Loop
|
|
|
|
`NestRunner` handles multi-plate nesting: it fills a plate, deducts placed quantities from the remaining request, creates a new plate, and repeats until all quantities are met or no progress is made (a part doesn't fit on a fresh sheet). This is new logic — `AutoNester` and `NestEngineBase` are single-plate only.
|
|
|
|
#### Error Handling
|
|
|
|
- If any DXF file path does not exist or fails to import (empty geometry, conversion failure), `RunAsync` throws `FileNotFoundException` or `InvalidOperationException` with a message identifying the failing file. Fail-fast on first bad DXF — no partial results.
|
|
- If cancellation is requested, the method throws `OperationCanceledException` per standard .NET patterns.
|
|
|
|
## Renames
|
|
|
|
| Current | New | Reason |
|
|
|---------|-----|--------|
|
|
| `OpenNest.NestResult` (Engine) | `OpenNest.OptimizationResult` | Frees the "result" name for the public API; this type is engine-internal (sequence/score/iterations) |
|
|
| `OpenNest.CutParameters` (Core) | Deleted | Replaced by `OpenNest.Api.CutParameters` |
|
|
| `.opnest` file extension | `.nest` | Standardize file extensions |
|
|
|
|
All references to the renamed types and extensions must be updated across the solution: Engine, Core, IO, MCP, Console, Training, Tests, and WinForms.
|
|
|
|
The WinForms project gains a reference to `OpenNest.Api` to use the new `CutParameters` type (it already references Core and Engine, so no circular dependency).
|
|
|
|
## Persistence
|
|
|
|
### File Extensions
|
|
|
|
- **`.nest`** — nest files (renamed from `.opnest`)
|
|
- **`.nestquote`** — quote files (new)
|
|
|
|
### `.nestquote` Format
|
|
|
|
ZIP archive containing:
|
|
|
|
```
|
|
quote.nestquote (ZIP)
|
|
├── request.json ← serialized NestRequest
|
|
├── response.json ← computed metrics (SheetCount, Utilization, CutTime, Elapsed)
|
|
└── nest.nest ← embedded .nest file (existing format, produced by NestWriter)
|
|
```
|
|
|
|
- `NestResponse.SaveAsync(path)` writes this ZIP. The embedded `nest.nest` is written to a `MemoryStream` via `NestWriter`, then added as a ZIP entry alongside the JSON files.
|
|
- `NestResponse.LoadAsync(path)` reads it back using `NestReader` for the `.nest` payload and JSON deserialization for the metadata.
|
|
- Source DXF files are **not** embedded — they are referenced by path in `request.json`. The actual geometry is captured in the `.nest`. Paths exist so the request can be re-run with different parameters if the DXFs are still available.
|
|
|
|
## Consumer Integration
|
|
|
|
### MCP Server
|
|
|
|
The MCP server can expose a single `nest_and_quote` tool that takes request parameters and calls `NestRunner.RunAsync()` internally, replacing the current multi-tool orchestration for batch nesting workflows.
|
|
|
|
### Console App
|
|
|
|
The console app gains a one-liner for batch nesting:
|
|
|
|
```csharp
|
|
var response = await NestRunner.RunAsync(request, progress, token);
|
|
await response.SaveAsync(outputPath);
|
|
```
|
|
|
|
### WinForms
|
|
|
|
The WinForms app continues using the engine directly for its interactive workflow. It gains a reference to `OpenNest.Api` only for the shared `CutParameters` type used by `TimingForm` and `CutParametersForm`.
|
|
|
|
## Out of Scope
|
|
|
|
- ML-based auto-strategy detection in `DefaultNestEngine` (future, part of training work)
|
|
- `Timing` rework (will happen separately; placeholder integration for now)
|
|
- Embedding source DXFs in `.nestquote` files
|
|
- Builder pattern for `NestRequest` (C# `init` properties suffice)
|
|
- DI/instance-based `NestRunner` (add later if needed)
|
|
- Additional `NestStrategy` enum values beyond `Auto` (added with ML work)
|