mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-10 22:24:14 -04:00
feat(mcp): delete unused session plates
This commit is contained in:
1 parent
2623517219
commit
8b94e49711
4 files changed
+124
No files matched your search
@@ -1,3 +1,4 @@
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Geometry;
|
||||
using OpenNest.IO;
|
||||
using OpenNest.Mcp;
|
||||
@@ -35,4 +36,77 @@ public class McpPlateToolsTests : IDisposable
|
||||
Assert.Same(appended, sessionOnly.GetPlate(0));
|
||||
Assert.Null(sessionOnly.GetPlate(1));
|
||||
}
|
||||
|
||||
private static string Delete(SetupTools tools, int index) => tools.DeletePlate(index);
|
||||
|
||||
[Fact]
|
||||
public void DeleteEmptyLoadedMiddlePlateRenumbersAppendedAndPersists()
|
||||
{
|
||||
var session = new NestSession { Nest = new Nest("fixture") };
|
||||
var drawing = new Drawing("square", new Program());
|
||||
session.Nest.Drawings.Add(drawing);
|
||||
var occupied = new Plate(20, 30) { Quantity = 2 };
|
||||
occupied.Parts.Add(new Part(drawing));
|
||||
var unused = new Plate(40, 50);
|
||||
var appended = new Plate(60, 70) { PartSpacing = 1.25, Quantity = 3 };
|
||||
session.Nest.Plates.Add(occupied);
|
||||
session.Nest.Plates.Add(unused);
|
||||
session.Plates.Add(appended);
|
||||
var tools = new SetupTools(session);
|
||||
|
||||
var result = Delete(tools, 1);
|
||||
Assert.Contains("Deleted plate 1", result);
|
||||
Assert.Contains("2 -> 1", result);
|
||||
Assert.Single(session.Nest.Plates);
|
||||
Assert.Same(appended, session.GetPlate(1));
|
||||
Assert.Null(session.GetPlate(2));
|
||||
Assert.Contains("60.0 x 70.0", new InspectionTools(session).GetPlateInfo(1));
|
||||
Assert.Equal(2, drawing.Quantity.Nested);
|
||||
|
||||
var path = Path.Combine(directory, "cleaned.nest");
|
||||
Assert.Contains("Saved nest", new InputTools(session).SaveNest(path));
|
||||
var reloaded = new NestReader(path).Read();
|
||||
// NestWriter already omits empty plates; the deletion is session cleanup,
|
||||
// and the saved file must still retain the occupied plate and its counts.
|
||||
var saved = Assert.Single(reloaded.Plates);
|
||||
Assert.Single(saved.Parts);
|
||||
Assert.Equal(20, saved.Size.Width);
|
||||
Assert.Equal(2, saved.Quantity);
|
||||
Assert.Equal(2, reloaded.Drawings.Single().Quantity.Nested);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeleteRefusesOccupiedCutoffAndInvalidIndicesWithoutMutation()
|
||||
{
|
||||
var session = new NestSession { Nest = new Nest("fixture") };
|
||||
var drawing = new Drawing("square", new Program());
|
||||
session.Nest.Drawings.Add(drawing);
|
||||
var occupied = new Plate(20, 30) { Quantity = 4 };
|
||||
var part = new Part(drawing);
|
||||
occupied.Parts.Add(part);
|
||||
var cutoff = new Plate(40, 50);
|
||||
var definition = new CutOff(new Vector(10, 10), CutOffAxis.Vertical);
|
||||
cutoff.CutOffs.Add(definition);
|
||||
session.Nest.Plates.Add(occupied);
|
||||
session.Plates.Add(cutoff);
|
||||
var tools = new SetupTools(session);
|
||||
|
||||
Assert.Contains("occupied", Delete(tools, 0));
|
||||
Assert.Contains("cutoff", Delete(tools, 1), StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("not found", Delete(tools, -1));
|
||||
Assert.Contains("not found", Delete(tools, 2));
|
||||
Assert.Same(occupied, session.GetPlate(0));
|
||||
Assert.Same(cutoff, session.GetPlate(1));
|
||||
Assert.Same(part, Assert.Single(occupied.Parts));
|
||||
Assert.Same(definition, Assert.Single(cutoff.CutOffs));
|
||||
Assert.Equal(4, drawing.Quantity.Nested);
|
||||
Assert.Single(session.Nest.Plates);
|
||||
Assert.Single(session.Plates);
|
||||
|
||||
// The session-only collection must also delete without requiring a loaded nest.
|
||||
var standalone = new NestSession();
|
||||
standalone.Plates.Add(new Plate(12, 13));
|
||||
Assert.Contains("Deleted plate 0", Delete(new SetupTools(standalone), 0));
|
||||
Assert.Empty(standalone.Plates);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,38 @@ namespace OpenNest.Mcp
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Remove only a genuinely unused sheet; loaded sheets precede appended sheets.</summary>
|
||||
public bool TryDeleteEmptyPlate(int index, out string reason)
|
||||
{
|
||||
var plate = GetPlate(index);
|
||||
if (plate == null)
|
||||
{
|
||||
reason = $"plate {index} not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (plate.Parts.Count != 0)
|
||||
{
|
||||
reason = $"plate {index} is occupied";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (plate.CutOffs.Count != 0)
|
||||
{
|
||||
reason = $"plate {index} has cutoff definitions";
|
||||
return false;
|
||||
}
|
||||
|
||||
var loadedCount = Nest?.Plates.Count ?? 0;
|
||||
if (index < loadedCount)
|
||||
Nest.Plates.RemoveAt(index);
|
||||
else
|
||||
Plates.RemoveAt(index - loadedCount);
|
||||
|
||||
reason = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public Drawing GetDrawing(string name)
|
||||
{
|
||||
if (Nest != null)
|
||||
|
||||
@@ -58,6 +58,22 @@ namespace OpenNest.Mcp.Tools
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "delete_plate")]
|
||||
[Description("Delete an empty plate without parts or cutoff definitions. Later plate indices shift down by one.")]
|
||||
public string DeletePlate([Description("Index of the empty plate to delete")] int plateIndex)
|
||||
{
|
||||
var oldCount = _session.AllPlates().Count;
|
||||
if (!_session.TryDeleteEmptyPlate(plateIndex, out var reason))
|
||||
return $"Error: {reason}; no plates changed";
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"Deleted plate {plateIndex}. Plates: {oldCount} -> {oldCount - 1}.");
|
||||
for (var oldIndex = plateIndex + 1; oldIndex < oldCount; oldIndex++)
|
||||
sb.AppendLine($" {oldIndex} -> {oldIndex - 1}");
|
||||
sb.AppendLine("Use get_plate_info with the new indices before further operations.");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "clear_plate")]
|
||||
[Description("Remove all parts from a plate. Returns how many parts were removed.")]
|
||||
public string ClearPlate([Description("Index of the plate to clear")] int plateIndex)
|
||||
|
||||
@@ -48,6 +48,8 @@ Invalid output is printed and rejected with exit code 2 without saving or postin
|
||||
|
||||
MCP `autonest_plate` requires an empty target. The stdio server serializes all tool calls sharing its mutable session, so another request cannot change drawings or occupy a target during a solve. `allow_invalid` defaults to false. It reports violations and makes no changes on rejection, including with an override when the output is unrepresentable or contains multiple sheets. `engine` defaults to Default, and its description lists the built-in engines with what each suits. Fill tools (`fill_plate`, `fill_area`, `fill_remnants`, `pack_plate`) remain separate and default to the Fill strategy. Console and MCP load jobs plug-ins from `Engines/` beside their executable.
|
||||
|
||||
MCP `delete_plate(plateIndex)` removes only a sheet with no parts and no cutoff definitions, from either loaded or newly created session plates. Occupied, cutoff-bearing and invalid indices are refused without changing the session. After deletion every later index shifts down by one; inspect `get_plate_info` before further edits. This is session cleanup, not a data purge or a save-time option. `save_nest` lists all session plates in its response, but the existing `.nest` writer omits empty cutoff-free plates from the serialized file; reloading may therefore have fewer plates than the response says. There is no opt-in switch to preserve empty plates in the file.
|
||||
|
||||
### MCP engine development harness
|
||||
|
||||
`test_engine` builds and runs `OpenNest.Console` in a configured, trusted checkout.
|
||||
|
||||
Reference in new issue
Block a user