mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-04 05:51:34 -05:00
Ahead of the new laser schedule, three ways a post could silently cut with the wrong process, and one way its settings silently reset: - Cincinnati CL wrote no G89 when no cut library matched the nest's material, thickness and gas (or no etch library for etch features), so the controller cut with whatever library it loaded last. It now refuses, unless the new "Require libraries" setting is turned off. - Cincinnati CL declared G20/G21 from its own setting without comparing the nest's units, so a metric nest posted 25.4x too large. It now refuses a mismatch. - CI Fiber posted any material missing from its table as the default code (MSN), so stainless or aluminum ran with mild-steel technology. A named, unmapped material is now refused; the default code covers only nests with no material. - Posts saved their settings beside their DLL, inside the install folder a Velopack update replaces, so every update reset them to the shipped defaults. Saves now go to %APPDATA%\OpenNest\Posts and the shipped JSON only seeds them; constructing a post no longer writes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
214 lines
7.6 KiB
C#
214 lines
7.6 KiB
C#
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using OpenNest.CNC;
|
|
using OpenNest.Geometry;
|
|
using OpenNest.IO;
|
|
using OpenNest.Posts.Cincinnati;
|
|
|
|
namespace OpenNest.Tests.Cincinnati;
|
|
|
|
public class UserVariablePostTests
|
|
{
|
|
[Fact]
|
|
public void UserVariables_EmittedInDeclarationSubprogram()
|
|
{
|
|
var output = PostNestWithVariables("width = 48.0\nG90\nG01X$widthY0");
|
|
|
|
Assert.Contains("#200=48", output);
|
|
Assert.Contains("WIDTH", output.ToUpper());
|
|
}
|
|
|
|
[Fact]
|
|
public void UserVariables_InlineVariable_NotEmittedAsNumbered()
|
|
{
|
|
var output = PostNestWithVariables("kerf = 0.06 inline\nG90\nG01X1Y0");
|
|
|
|
Assert.DoesNotContain("#200", output);
|
|
}
|
|
|
|
[Fact]
|
|
public void UserVariables_CoordinateUsesNumberedVariable()
|
|
{
|
|
var output = PostNestWithVariables("width = 48.0\nG90\nG01X$widthY0");
|
|
|
|
Assert.Contains("X#200", output);
|
|
}
|
|
|
|
[Fact]
|
|
public void UserVariables_InlineVariable_CoordinateUsesLiteral()
|
|
{
|
|
var output = PostNestWithVariables("kerf = 0.06 inline\nG90\nG01X$kerfY0");
|
|
|
|
Assert.Contains("X0.06", output);
|
|
// G1 coordinate lines should not use X#nnn variable references for inline vars
|
|
var g1Lines = output.Split('\n').Where(l => l.TrimStart().StartsWith("G1 ")).ToList();
|
|
Assert.All(g1Lines, line => Assert.DoesNotContain("X#", line));
|
|
}
|
|
|
|
[Fact]
|
|
public void UserVariables_GlobalVariables_SharedAcrossDrawings()
|
|
{
|
|
var pgm1 = ParseProgram("sheet_width = 48.0 global\nG90\nG01X$sheet_widthY0");
|
|
var pgm2 = ParseProgram("sheet_width = 48.0 global\nG90\nG01X$sheet_widthY0");
|
|
|
|
var drawing1 = new Drawing("Part1", pgm1);
|
|
var drawing2 = new Drawing("Part2", pgm2);
|
|
var nest = new Nest { Name = "Test" };
|
|
nest.Drawings.Add(drawing1);
|
|
nest.Drawings.Add(drawing2);
|
|
var plate = new Plate(new Size(100, 100));
|
|
plate.Parts.Add(new Part(drawing1, new Vector(0, 0)));
|
|
plate.Parts.Add(new Part(drawing2, new Vector(50, 0)));
|
|
nest.Plates.Add(plate);
|
|
|
|
var config = new CincinnatiPostConfig { RequireLibraries = false, UserVariableStart = 200 };
|
|
var post = new CincinnatiPostProcessor(config);
|
|
var output = PostToString(post, nest);
|
|
|
|
// Both should use the same #200 — only one declaration
|
|
var declarationCount = output
|
|
.Split('\n')
|
|
.Count(l => l.Contains("#200=") && l.ToUpper().Contains("SHEET WIDTH"));
|
|
Assert.Equal(1, declarationCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void UserVariables_LocalVariables_GetSeparateNumbers()
|
|
{
|
|
var pgm1 = ParseProgram("diameter = 0.3\nG90\nG01X$diameterY0");
|
|
var pgm2 = ParseProgram("diameter = 0.5\nG90\nG01X$diameterY0");
|
|
|
|
var drawing1 = new Drawing("TubeA", pgm1);
|
|
var drawing2 = new Drawing("TubeB", pgm2);
|
|
var nest = new Nest { Name = "Test" };
|
|
nest.Drawings.Add(drawing1);
|
|
nest.Drawings.Add(drawing2);
|
|
var plate = new Plate(new Size(100, 100));
|
|
plate.Parts.Add(new Part(drawing1, new Vector(0, 0)));
|
|
plate.Parts.Add(new Part(drawing2, new Vector(50, 0)));
|
|
nest.Plates.Add(plate);
|
|
|
|
var config = new CincinnatiPostConfig { RequireLibraries = false, UserVariableStart = 200 };
|
|
var post = new CincinnatiPostProcessor(config);
|
|
var output = PostToString(post, nest);
|
|
|
|
// Two separate declarations with different numbers
|
|
Assert.Contains("#200=0.3", output);
|
|
Assert.Contains("#201=0.5", output);
|
|
Assert.Contains("TUBE A", output.ToUpper());
|
|
Assert.Contains("TUBE B", output.ToUpper());
|
|
}
|
|
|
|
[Fact]
|
|
public void UserVariables_StartNumberConfigurable()
|
|
{
|
|
var config = new CincinnatiPostConfig { RequireLibraries = false, UserVariableStart = 300 };
|
|
var output = PostNestWithVariables("width = 48.0\nG90\nG01X$widthY0", config);
|
|
|
|
Assert.Contains("#300=48", output);
|
|
}
|
|
|
|
[Fact]
|
|
public void CutOff_VerticalCut_UsesSheetWidthVariable()
|
|
{
|
|
// Create a plate with a vertical cutoff
|
|
var config = new CincinnatiPostConfig
|
|
{
|
|
RequireLibraries = false,
|
|
SheetWidthVariable = 110,
|
|
SheetLengthVariable = 111,
|
|
};
|
|
var nest = new Nest { Name = "Test" };
|
|
var plate = new Plate(new Size(48, 96));
|
|
|
|
// Add a simple part so the plate isn't empty
|
|
var partPgm = new Program();
|
|
partPgm.Codes.Add(new RapidMove(0, 0));
|
|
partPgm.Codes.Add(new LinearMove(10, 0));
|
|
partPgm.Codes.Add(new LinearMove(10, 10));
|
|
partPgm.Codes.Add(new LinearMove(0, 10));
|
|
partPgm.Codes.Add(new LinearMove(0, 0));
|
|
var drawing = new Drawing("Part1", partPgm);
|
|
nest.Drawings.Add(drawing);
|
|
plate.Parts.Add(new Part(drawing, new Vector(0, 0)));
|
|
|
|
// Add a vertical cutoff that goes full width (Y=0 to Y=48)
|
|
var cutoff = new CutOff(new Vector(20, 0), CutOffAxis.Vertical);
|
|
plate.CutOffs.Add(cutoff);
|
|
plate.RegenerateCutOffs(new CutOffSettings());
|
|
|
|
nest.Plates.Add(plate);
|
|
|
|
var post = new CincinnatiPostProcessor(config);
|
|
var output = PostToString(post, nest);
|
|
|
|
// The cutoff line end at Y=48 (sheet width) should use #110
|
|
Assert.Contains("Y#110", output);
|
|
}
|
|
|
|
[Fact]
|
|
public void CutOff_SegmentedCut_OnlyEdgeUsesVariable()
|
|
{
|
|
// Create a plate with a part in the middle and a vertical cutoff
|
|
var config = new CincinnatiPostConfig { RequireLibraries = false, SheetWidthVariable = 110 };
|
|
var nest = new Nest { Name = "Test" };
|
|
var plate = new Plate(new Size(48, 96));
|
|
|
|
// Part in the middle — cutoff will be segmented around it
|
|
var partPgm = new Program();
|
|
partPgm.Codes.Add(new RapidMove(0, 0));
|
|
partPgm.Codes.Add(new LinearMove(10, 0));
|
|
partPgm.Codes.Add(new LinearMove(10, 10));
|
|
partPgm.Codes.Add(new LinearMove(0, 10));
|
|
partPgm.Codes.Add(new LinearMove(0, 0));
|
|
var drawing = new Drawing("Part1", partPgm);
|
|
nest.Drawings.Add(drawing);
|
|
plate.Parts.Add(new Part(drawing, new Vector(15, 20))); // Part at Y=20-30, should create gap
|
|
|
|
var cutoff = new CutOff(new Vector(20, 0), CutOffAxis.Vertical);
|
|
plate.CutOffs.Add(cutoff);
|
|
plate.RegenerateCutOffs(new CutOffSettings());
|
|
|
|
nest.Plates.Add(plate);
|
|
|
|
var post = new CincinnatiPostProcessor(config);
|
|
var output = PostToString(post, nest);
|
|
|
|
// The last segment endpoint at Y=48 should use #110
|
|
Assert.Contains("Y#110", output);
|
|
}
|
|
|
|
private static string PostNestWithVariables(string gcode, CincinnatiPostConfig config = null)
|
|
{
|
|
var program = ParseProgram(gcode);
|
|
var drawing = new Drawing("TestPart", program);
|
|
var nest = new Nest { Name = "Test" };
|
|
nest.Drawings.Add(drawing);
|
|
var plate = new Plate(new Size(100, 100));
|
|
plate.Parts.Add(new Part(drawing, new Vector(0, 0)));
|
|
nest.Plates.Add(plate);
|
|
|
|
config ??= new CincinnatiPostConfig { RequireLibraries = false, UserVariableStart = 200 };
|
|
var post = new CincinnatiPostProcessor(config);
|
|
return PostToString(post, nest);
|
|
}
|
|
|
|
private static string PostToString(CincinnatiPostProcessor post, Nest nest)
|
|
{
|
|
var ms = new MemoryStream();
|
|
post.Post(nest, ms);
|
|
ms.Position = 0;
|
|
return new StreamReader(ms).ReadToEnd();
|
|
}
|
|
|
|
private static Program ParseProgram(string gcode)
|
|
{
|
|
var stream = new MemoryStream(Encoding.UTF8.GetBytes(gcode));
|
|
var reader = new ProgramReader(stream);
|
|
var program = reader.Read();
|
|
reader.Close();
|
|
return program;
|
|
}
|
|
}
|