diff --git a/OpenNest.Mcp/Tools/InspectionTools.cs b/OpenNest.Mcp/Tools/InspectionTools.cs new file mode 100644 index 0000000..3968a7f --- /dev/null +++ b/OpenNest.Mcp/Tools/InspectionTools.cs @@ -0,0 +1,135 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using ModelContextProtocol.Server; +using OpenNest.Geometry; +using OpenNest.Math; + +namespace OpenNest.Mcp.Tools +{ + public class InspectionTools + { + private readonly NestSession _session; + + public InspectionTools(NestSession session) + { + _session = session; + } + + [McpServerTool(Name = "get_plate_info")] + [Description("Get detailed information about a plate including dimensions, part count, utilization, remnants, and drawing breakdown.")] + public string GetPlateInfo( + [Description("Index of the plate")] int plateIndex) + { + var plate = _session.GetPlate(plateIndex); + if (plate == null) + return $"Error: plate {plateIndex} not found"; + + var work = plate.WorkArea(); + var remnants = plate.GetRemnants(); + + var sb = new StringBuilder(); + sb.AppendLine($"Plate {plateIndex}:"); + sb.AppendLine($" Size: {plate.Size.Width:F1} x {plate.Size.Height:F1}"); + sb.AppendLine($" Quadrant: {plate.Quadrant}"); + sb.AppendLine($" Thickness: {plate.Thickness:F2}"); + sb.AppendLine($" Material: {plate.Material.Name}"); + sb.AppendLine($" Part spacing: {plate.PartSpacing:F2}"); + sb.AppendLine($" Edge spacing: L={plate.EdgeSpacing.Left:F2} B={plate.EdgeSpacing.Bottom:F2} R={plate.EdgeSpacing.Right:F2} T={plate.EdgeSpacing.Top:F2}"); + sb.AppendLine($" Work area: {work.X:F1},{work.Y:F1} {work.Width:F1}x{work.Height:F1}"); + sb.AppendLine($" Parts: {plate.Parts.Count}"); + sb.AppendLine($" Utilization: {plate.Utilization():P1}"); + sb.AppendLine($" Quantity: {plate.Quantity}"); + + // Drawing breakdown + if (plate.Parts.Count > 0) + { + sb.AppendLine(" Drawings:"); + var groups = plate.Parts.GroupBy(p => p.BaseDrawing.Name); + foreach (var group in groups) + sb.AppendLine($" {group.Key}: {group.Count()}"); + } + + // Remnants + sb.AppendLine($" Remnants: {remnants.Count}"); + for (var i = 0; i < remnants.Count; i++) + { + var r = remnants[i]; + sb.AppendLine($" Remnant {i}: ({r.X:F1},{r.Y:F1}) {r.Width:F1}x{r.Height:F1}, area={r.Area():F1}"); + } + + return sb.ToString(); + } + + [McpServerTool(Name = "get_parts")] + [Description("List placed parts on a plate with index, drawing name, location, rotation, and bounding box.")] + public string GetParts( + [Description("Index of the plate")] int plateIndex, + [Description("Maximum number of parts to list (default 50)")] int limit = 50) + { + var plate = _session.GetPlate(plateIndex); + if (plate == null) + return $"Error: plate {plateIndex} not found"; + + if (plate.Parts.Count == 0) + return $"Plate {plateIndex} has no parts"; + + var sb = new StringBuilder(); + sb.AppendLine($"Plate {plateIndex}: {plate.Parts.Count} parts (showing up to {limit})"); + + var count = System.Math.Min(plate.Parts.Count, limit); + + for (var i = 0; i < count; i++) + { + var part = plate.Parts[i]; + var bbox = part.BoundingBox; + var rotDeg = Angle.ToDegrees(part.Rotation); + + sb.AppendLine($" [{i}] {part.BaseDrawing.Name}: " + + $"loc=({part.Location.X:F2},{part.Location.Y:F2}), " + + $"rot={rotDeg:F1} deg, " + + $"bbox=({bbox.X:F2},{bbox.Y:F2} {bbox.Width:F2}x{bbox.Height:F2})"); + } + + if (plate.Parts.Count > limit) + sb.AppendLine($" ... and {plate.Parts.Count - limit} more"); + + return sb.ToString(); + } + + [McpServerTool(Name = "check_overlaps")] + [Description("Check a plate for overlapping parts. Reports collision points if any.")] + public string CheckOverlaps( + [Description("Index of the plate")] int plateIndex) + { + var plate = _session.GetPlate(plateIndex); + if (plate == null) + return $"Error: plate {plateIndex} not found"; + + if (plate.Parts.Count < 2) + return $"Plate {plateIndex}: no overlaps possible (fewer than 2 parts)"; + + var hasOverlaps = plate.HasOverlappingParts(out var pts); + + if (!hasOverlaps) + return $"Plate {plateIndex}: no overlapping parts detected"; + + var sb = new StringBuilder(); + sb.AppendLine($"Plate {plateIndex}: {pts.Count} collision point(s) detected!"); + + var limit = System.Math.Min(pts.Count, 20); + + for (var i = 0; i < limit; i++) + { + var pt = pts[i]; + sb.AppendLine($" Collision at ({pt.X:F2}, {pt.Y:F2})"); + } + + if (pts.Count > limit) + sb.AppendLine($" ... and {pts.Count - limit} more collision points"); + + return sb.ToString(); + } + } +}