feat: Cincinnati post emits user variables as numbered #variables
When programs have user-defined variables, the Cincinnati post now: - Assigns numbered machine variables (#200, #201, etc.) to non-inline variables - Emits declarations like #200=48.0 (SHEET WIDTH) in the variable declaration subprogram - Emits X#200 instead of X48.0 in coordinates that have VariableRefs - Handles global variables (shared number across drawings) vs local (per-drawing number) - Inline variables emit the literal value as before Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,17 @@ public sealed class FeatureContext
|
|||||||
/// so part-relative programs become plate-absolute under G90.
|
/// so part-relative programs become plate-absolute under G90.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Vector PartLocation { get; set; } = Vector.Zero;
|
public Vector PartLocation { get; set; } = Vector.Zero;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps (drawingId, variableName) to assigned machine variable numbers.
|
||||||
|
/// Used to emit #number references instead of literal values for user variables.
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<(int drawingId, string varName), int> UserVariableMapping { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The drawing ID for the current part, used to look up user variable mappings.
|
||||||
|
/// </summary>
|
||||||
|
public int DrawingId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -112,7 +123,9 @@ public sealed class CincinnatiFeatureWriter
|
|||||||
kerfEmitted = true;
|
kerfEmitted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.Append($"G1 X{_fmt.FormatCoord(linear.EndPoint.X + offset.X)} Y{_fmt.FormatCoord(linear.EndPoint.Y + offset.Y)}");
|
var xCoord = FormatCoordWithVars(linear.EndPoint.X + offset.X, "X", linear.VariableRefs, ctx);
|
||||||
|
var yCoord = FormatCoordWithVars(linear.EndPoint.Y + offset.Y, "Y", linear.VariableRefs, ctx);
|
||||||
|
sb.Append($"G1 X{xCoord} Y{yCoord}");
|
||||||
|
|
||||||
// Feedrate — etch always uses process feedrate
|
// Feedrate — etch always uses process feedrate
|
||||||
var feedVar = ctx.IsEtch ? "#148" : GetLinearFeedVariable(linear.Layer);
|
var feedVar = ctx.IsEtch ? "#148" : GetLinearFeedVariable(linear.Layer);
|
||||||
@@ -138,7 +151,9 @@ public sealed class CincinnatiFeatureWriter
|
|||||||
|
|
||||||
// G2 = CW, G3 = CCW
|
// G2 = CW, G3 = CCW
|
||||||
var gCode = arc.Rotation == RotationType.CW ? "G2" : "G3";
|
var gCode = arc.Rotation == RotationType.CW ? "G2" : "G3";
|
||||||
sb.Append($"{gCode} X{_fmt.FormatCoord(arc.EndPoint.X + offset.X)} Y{_fmt.FormatCoord(arc.EndPoint.Y + offset.Y)}");
|
var xCoord = FormatCoordWithVars(arc.EndPoint.X + offset.X, "X", arc.VariableRefs, ctx);
|
||||||
|
var yCoord = FormatCoordWithVars(arc.EndPoint.Y + offset.Y, "Y", arc.VariableRefs, ctx);
|
||||||
|
sb.Append($"{gCode} X{xCoord} Y{yCoord}");
|
||||||
|
|
||||||
// Convert absolute center to incremental I/J
|
// Convert absolute center to incremental I/J
|
||||||
var i = arc.CenterPoint.X - currentPos.X;
|
var i = arc.CenterPoint.X - currentPos.X;
|
||||||
@@ -177,6 +192,25 @@ public sealed class CincinnatiFeatureWriter
|
|||||||
WriteM47(writer, ctx);
|
WriteM47(writer, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Formats a coordinate value, using a #number variable reference if the motion
|
||||||
|
/// has a VariableRef for this axis and the variable is mapped (non-inline).
|
||||||
|
/// Inline variables fall through to literal formatting.
|
||||||
|
/// </summary>
|
||||||
|
private string FormatCoordWithVars(double value, string axis,
|
||||||
|
Dictionary<string, string> variableRefs, FeatureContext ctx)
|
||||||
|
{
|
||||||
|
if (variableRefs != null
|
||||||
|
&& variableRefs.TryGetValue(axis, out var varName)
|
||||||
|
&& ctx.UserVariableMapping != null
|
||||||
|
&& ctx.UserVariableMapping.TryGetValue((ctx.DrawingId, varName), out var varNum))
|
||||||
|
{
|
||||||
|
return $"#{varNum}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return _fmt.FormatCoord(value);
|
||||||
|
}
|
||||||
|
|
||||||
private Vector FindPiercePoint(List<ICode> codes)
|
private Vector FindPiercePoint(List<ICode> codes)
|
||||||
{
|
{
|
||||||
foreach (var code in codes)
|
foreach (var code in codes)
|
||||||
|
|||||||
@@ -253,6 +253,11 @@ namespace OpenNest.Posts.Cincinnati
|
|||||||
new() { MaxRadius = 4.500, FeedratePercent = 0.80, VariableNumber = 125 }
|
new() { MaxRadius = 4.500, FeedratePercent = 0.80, VariableNumber = 125 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[Category("A. Variables")]
|
||||||
|
[DisplayName("User Variable Start")]
|
||||||
|
[Description("Starting variable number for user-defined variables (#200, #201, etc.).")]
|
||||||
|
public int UserVariableStart { get; set; } = 200;
|
||||||
|
|
||||||
[Category("A. Variables")]
|
[Category("A. Variables")]
|
||||||
[DisplayName("Sheet Width Variable")]
|
[DisplayName("Sheet Width Variable")]
|
||||||
[Description("Variable number for sheet width.")]
|
[Description("Variable number for sheet width.")]
|
||||||
|
|||||||
@@ -70,7 +70,10 @@ namespace OpenNest.Posts.Cincinnati
|
|||||||
.Where(p => p.Parts.Count > 0)
|
.Where(p => p.Parts.Count > 0)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
// 3. Resolve gas and library files
|
// 3. Register user variables from drawing programs
|
||||||
|
var userVarMapping = RegisterUserVariables(vars, plates);
|
||||||
|
|
||||||
|
// 4. Resolve gas and library files
|
||||||
var resolver = new MaterialLibraryResolver(Config);
|
var resolver = new MaterialLibraryResolver(Config);
|
||||||
var gas = MaterialLibraryResolver.ResolveGas(nest, Config);
|
var gas = MaterialLibraryResolver.ResolveGas(nest, Config);
|
||||||
var etchLibrary = resolver.ResolveEtchLibrary(Config.DefaultEtchGas);
|
var etchLibrary = resolver.ResolveEtchLibrary(Config.DefaultEtchGas);
|
||||||
@@ -79,24 +82,24 @@ namespace OpenNest.Posts.Cincinnati
|
|||||||
var firstPlate = plates.FirstOrDefault();
|
var firstPlate = plates.FirstOrDefault();
|
||||||
var initialCutLibrary = resolver.ResolveCutLibrary(nest.Material?.Name ?? "", nest.Thickness, gas);
|
var initialCutLibrary = resolver.ResolveCutLibrary(nest.Material?.Name ?? "", nest.Thickness, gas);
|
||||||
|
|
||||||
// 4. Build part sub-program registry (if enabled)
|
// 5. Build part sub-program registry (if enabled)
|
||||||
Dictionary<(int, long), int> partSubprograms = null;
|
Dictionary<(int, long), int> partSubprograms = null;
|
||||||
List<(int subNum, string name, Program program)> subprogramEntries = null;
|
List<(int subNum, string name, Program program)> subprogramEntries = null;
|
||||||
|
|
||||||
if (Config.UsePartSubprograms)
|
if (Config.UsePartSubprograms)
|
||||||
(partSubprograms, subprogramEntries) = CincinnatiPartSubprogramWriter.BuildRegistry(plates, Config.PartSubprogramStart);
|
(partSubprograms, subprogramEntries) = CincinnatiPartSubprogramWriter.BuildRegistry(plates, Config.PartSubprogramStart);
|
||||||
|
|
||||||
// 5. Create writers
|
// 6. Create writers
|
||||||
var preamble = new CincinnatiPreambleWriter(Config);
|
var preamble = new CincinnatiPreambleWriter(Config);
|
||||||
var sheetWriter = new CincinnatiSheetWriter(Config, vars);
|
var sheetWriter = new CincinnatiSheetWriter(Config, vars);
|
||||||
|
|
||||||
// 6. Build material description from nest
|
// 7. Build material description from nest
|
||||||
var material = nest.Material;
|
var material = nest.Material;
|
||||||
var materialDesc = material != null
|
var materialDesc = material != null
|
||||||
? $"{material.Name}{(string.IsNullOrEmpty(material.Grade) ? "" : $", {material.Grade}")}"
|
? $"{material.Name}{(string.IsNullOrEmpty(material.Grade) ? "" : $", {material.Grade}")}"
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
// 7. Write to stream
|
// 8. Write to stream
|
||||||
using var writer = new StreamWriter(outputStream, Encoding.UTF8, 1024, leaveOpen: true);
|
using var writer = new StreamWriter(outputStream, Encoding.UTF8, 1024, leaveOpen: true);
|
||||||
|
|
||||||
// Main program
|
// Main program
|
||||||
@@ -114,7 +117,7 @@ namespace OpenNest.Posts.Cincinnati
|
|||||||
var cutLibrary = resolver.ResolveCutLibrary(nest.Material?.Name ?? "", nest.Thickness, gas);
|
var cutLibrary = resolver.ResolveCutLibrary(nest.Material?.Name ?? "", nest.Thickness, gas);
|
||||||
var isLastSheet = i == plates.Count - 1;
|
var isLastSheet = i == plates.Count - 1;
|
||||||
sheetWriter.Write(writer, plate, nest.Name ?? "NEST", sheetIndex, subNumber,
|
sheetWriter.Write(writer, plate, nest.Name ?? "NEST", sheetIndex, subNumber,
|
||||||
cutLibrary, etchLibrary, partSubprograms, isLastSheet);
|
cutLibrary, etchLibrary, partSubprograms, isLastSheet, userVarMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Part sub-programs (if enabled)
|
// Part sub-programs (if enabled)
|
||||||
@@ -142,6 +145,103 @@ namespace OpenNest.Posts.Cincinnati
|
|||||||
Post(nest, fs);
|
Post(nest, fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Dictionary<(int drawingId, string varName), int> RegisterUserVariables(
|
||||||
|
ProgramVariableManager vars, List<Plate> plates)
|
||||||
|
{
|
||||||
|
var mapping = new Dictionary<(int drawingId, string varName), int>();
|
||||||
|
var nextNumber = Config.UserVariableStart;
|
||||||
|
|
||||||
|
// Track global variables by name so they share a single number
|
||||||
|
var globalNumbers = new Dictionary<string, int>(System.StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
// Collect unique drawings from all plates
|
||||||
|
var seenDrawings = new HashSet<int>();
|
||||||
|
foreach (var plate in plates)
|
||||||
|
{
|
||||||
|
foreach (var part in plate.Parts)
|
||||||
|
{
|
||||||
|
var drawing = part.BaseDrawing;
|
||||||
|
if (drawing.IsCutOff || !seenDrawings.Add(drawing.Id))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (var kvp in drawing.Program.Variables)
|
||||||
|
{
|
||||||
|
var varDef = kvp.Value;
|
||||||
|
|
||||||
|
// Skip inline variables — they emit literal values
|
||||||
|
if (varDef.Inline)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (varDef.Global)
|
||||||
|
{
|
||||||
|
if (!globalNumbers.TryGetValue(varDef.Name, out var globalNum))
|
||||||
|
{
|
||||||
|
globalNum = nextNumber++;
|
||||||
|
globalNumbers[varDef.Name] = globalNum;
|
||||||
|
|
||||||
|
// Register once in the variable manager
|
||||||
|
var commentName = ToPascalCase(varDef.Name);
|
||||||
|
var expression = FormatVariableValue(varDef.Value);
|
||||||
|
vars.GetOrCreate(commentName, globalNum, expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping[(drawing.Id, varDef.Name)] = globalNum;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var num = nextNumber++;
|
||||||
|
mapping[(drawing.Id, varDef.Name)] = num;
|
||||||
|
|
||||||
|
// Register with drawing name prefix in the comment
|
||||||
|
var drawingLabel = ToPascalCase(drawing.Name);
|
||||||
|
var varLabel = ToPascalCase(varDef.Name);
|
||||||
|
var commentName = $"{drawingLabel}{varLabel}";
|
||||||
|
var expression = FormatVariableValue(varDef.Value);
|
||||||
|
vars.GetOrCreate(commentName, num, expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a variable name from snake_case or camelCase to PascalCase.
|
||||||
|
/// Examples: "sheet_width" → "SheetWidth", "holeSpacing" → "HoleSpacing"
|
||||||
|
/// </summary>
|
||||||
|
private static string ToPascalCase(string name)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder(name.Length);
|
||||||
|
var capitalizeNext = true;
|
||||||
|
|
||||||
|
foreach (var c in name)
|
||||||
|
{
|
||||||
|
if (c == '_')
|
||||||
|
{
|
||||||
|
capitalizeNext = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capitalizeNext)
|
||||||
|
{
|
||||||
|
sb.Append(char.ToUpper(c));
|
||||||
|
capitalizeNext = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatVariableValue(double value)
|
||||||
|
{
|
||||||
|
return value.ToString("0.####", System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
private ProgramVariableManager CreateVariableManager()
|
private ProgramVariableManager CreateVariableManager()
|
||||||
{
|
{
|
||||||
var vars = new ProgramVariableManager();
|
var vars = new ProgramVariableManager();
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ public sealed class CincinnatiSheetWriter
|
|||||||
public void Write(TextWriter w, Plate plate, string nestName, int sheetIndex, int subNumber,
|
public void Write(TextWriter w, Plate plate, string nestName, int sheetIndex, int subNumber,
|
||||||
string cutLibrary, string etchLibrary,
|
string cutLibrary, string etchLibrary,
|
||||||
Dictionary<(int, long), int> partSubprograms = null,
|
Dictionary<(int, long), int> partSubprograms = null,
|
||||||
bool isLastSheet = false)
|
bool isLastSheet = false,
|
||||||
|
Dictionary<(int drawingId, string varName), int> userVarMapping = null)
|
||||||
{
|
{
|
||||||
if (plate.Parts.Count == 0)
|
if (plate.Parts.Count == 0)
|
||||||
return;
|
return;
|
||||||
@@ -88,9 +89,9 @@ public sealed class CincinnatiSheetWriter
|
|||||||
|
|
||||||
// 4. Emit parts
|
// 4. Emit parts
|
||||||
if (partSubprograms != null)
|
if (partSubprograms != null)
|
||||||
WritePartsWithSubprograms(w, allParts, cutLibrary, etchLibrary, sheetDiagonal, partSubprograms);
|
WritePartsWithSubprograms(w, allParts, cutLibrary, etchLibrary, sheetDiagonal, partSubprograms, userVarMapping);
|
||||||
else
|
else
|
||||||
WritePartsInline(w, allParts, cutLibrary, etchLibrary, sheetDiagonal);
|
WritePartsInline(w, allParts, cutLibrary, etchLibrary, sheetDiagonal, userVarMapping);
|
||||||
|
|
||||||
// 5. Footer
|
// 5. Footer
|
||||||
w.WriteLine("M42");
|
w.WriteLine("M42");
|
||||||
@@ -104,7 +105,8 @@ public sealed class CincinnatiSheetWriter
|
|||||||
|
|
||||||
private void WritePartsWithSubprograms(TextWriter w, List<Part> allParts,
|
private void WritePartsWithSubprograms(TextWriter w, List<Part> allParts,
|
||||||
string cutLibrary, string etchLibrary, double sheetDiagonal,
|
string cutLibrary, string etchLibrary, double sheetDiagonal,
|
||||||
Dictionary<(int, long), int> partSubprograms)
|
Dictionary<(int, long), int> partSubprograms,
|
||||||
|
Dictionary<(int drawingId, string varName), int> userVarMapping)
|
||||||
{
|
{
|
||||||
var lastPartName = "";
|
var lastPartName = "";
|
||||||
var featureIndex = 0;
|
var featureIndex = 0;
|
||||||
@@ -154,7 +156,9 @@ public sealed class CincinnatiSheetWriter
|
|||||||
LibraryFile = isEtch ? etchLibrary : cutLibrary,
|
LibraryFile = isEtch ? etchLibrary : cutLibrary,
|
||||||
CutDistance = cutDistance,
|
CutDistance = cutDistance,
|
||||||
SheetDiagonal = sheetDiagonal,
|
SheetDiagonal = sheetDiagonal,
|
||||||
PartLocation = part.Location
|
PartLocation = part.Location,
|
||||||
|
UserVariableMapping = userVarMapping,
|
||||||
|
DrawingId = part.BaseDrawing.Id
|
||||||
};
|
};
|
||||||
|
|
||||||
_featureWriter.Write(w, ctx);
|
_featureWriter.Write(w, ctx);
|
||||||
@@ -202,7 +206,8 @@ public sealed class CincinnatiSheetWriter
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void WritePartsInline(TextWriter w, List<Part> allParts,
|
private void WritePartsInline(TextWriter w, List<Part> allParts,
|
||||||
string cutLibrary, string etchLibrary, double sheetDiagonal)
|
string cutLibrary, string etchLibrary, double sheetDiagonal,
|
||||||
|
Dictionary<(int drawingId, string varName), int> userVarMapping)
|
||||||
{
|
{
|
||||||
// Split and classify features, ordering etch before cut per part
|
// Split and classify features, ordering etch before cut per part
|
||||||
var features = new List<(Part part, List<ICode> codes, bool isEtch)>();
|
var features = new List<(Part part, List<ICode> codes, bool isEtch)>();
|
||||||
@@ -242,7 +247,9 @@ public sealed class CincinnatiSheetWriter
|
|||||||
LibraryFile = isEtch ? etchLibrary : cutLibrary,
|
LibraryFile = isEtch ? etchLibrary : cutLibrary,
|
||||||
CutDistance = cutDistance,
|
CutDistance = cutDistance,
|
||||||
SheetDiagonal = sheetDiagonal,
|
SheetDiagonal = sheetDiagonal,
|
||||||
PartLocation = part.Location
|
PartLocation = part.Location,
|
||||||
|
UserVariableMapping = userVarMapping,
|
||||||
|
DrawingId = part.BaseDrawing.Id
|
||||||
};
|
};
|
||||||
|
|
||||||
_featureWriter.Write(w, ctx);
|
_featureWriter.Write(w, ctx);
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
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 { 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 { 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 { UserVariableStart = 300 };
|
||||||
|
var output = PostNestWithVariables("width = 48.0\nG90\nG01X$widthY0", config);
|
||||||
|
|
||||||
|
Assert.Contains("#300=48", 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 { 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user