ProgramReader now supports G-code user variables with a two-pass approach: first pass collects variable definitions (name = expression [inline] [global]) and evaluates them via topological sort and ExpressionEvaluator; second pass parses G-code lines with $name substitution and VariableRef tracking on motion and feedrate objects. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
137 lines
4.2 KiB
C#
137 lines
4.2 KiB
C#
using System.IO;
|
|
using System.Text;
|
|
using OpenNest.CNC;
|
|
using OpenNest.IO;
|
|
|
|
namespace OpenNest.Tests.IO;
|
|
|
|
public class ProgramReaderVariableTests
|
|
{
|
|
private Program Parse(string gcode)
|
|
{
|
|
var stream = new MemoryStream(Encoding.UTF8.GetBytes(gcode));
|
|
var reader = new ProgramReader(stream);
|
|
var program = reader.Read();
|
|
reader.Close();
|
|
return program;
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_SimpleVariable_StoredInVariables()
|
|
{
|
|
var pgm = Parse("diameter = 0.3\nG90\nG01X1Y0");
|
|
Assert.True(pgm.Variables.ContainsKey("diameter"));
|
|
Assert.Equal(0.3, pgm.Variables["diameter"].Value);
|
|
Assert.Equal("0.3", pgm.Variables["diameter"].Expression);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_VariableWithInlineFlag()
|
|
{
|
|
var pgm = Parse("kerf = 0.06 inline\nG90\nG01X1Y0");
|
|
Assert.True(pgm.Variables["kerf"].Inline);
|
|
Assert.False(pgm.Variables["kerf"].Global);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_VariableWithGlobalFlag()
|
|
{
|
|
var pgm = Parse("sheet_width = 48.0 global\nG90\nG01X1Y0");
|
|
Assert.True(pgm.Variables["sheet_width"].Global);
|
|
Assert.False(pgm.Variables["sheet_width"].Inline);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_VariableWithBothFlags()
|
|
{
|
|
var pgm = Parse("speed = 200 global inline\nG90\nG01X1Y0");
|
|
Assert.True(pgm.Variables["speed"].Global);
|
|
Assert.True(pgm.Variables["speed"].Inline);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_VariableReference_SubstitutedInCoordinate()
|
|
{
|
|
var pgm = Parse("width = 48.0\nG90\nG01X$widthY0");
|
|
var linear = (LinearMove)pgm.Codes[0];
|
|
Assert.Equal(48.0, linear.EndPoint.X);
|
|
Assert.Equal(0.0, linear.EndPoint.Y);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_VariableReference_TrackedInVariableRefs()
|
|
{
|
|
var pgm = Parse("width = 48.0\nG90\nG01X$widthY0");
|
|
var linear = (LinearMove)pgm.Codes[0];
|
|
Assert.NotNull(linear.VariableRefs);
|
|
Assert.Equal("width", linear.VariableRefs["X"]);
|
|
Assert.False(linear.VariableRefs.ContainsKey("Y"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_VariableExpression_WithReference()
|
|
{
|
|
var pgm = Parse("diameter = 0.6\nradius = $diameter / 2\nG90\nG02X1Y0I$radiusJ0");
|
|
Assert.Equal(0.3, pgm.Variables["radius"].Value, 10);
|
|
var arc = (ArcMove)pgm.Codes[0];
|
|
Assert.Equal(0.3, arc.CenterPoint.X, 10);
|
|
Assert.Equal("radius", arc.VariableRefs["I"]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_FeedVariable_TrackedOnFeedrate()
|
|
{
|
|
var pgm = Parse("speed = 100\nG90\nF$speed\nG01X1Y0");
|
|
var feedrate = (Feedrate)pgm.Codes[0];
|
|
Assert.Equal(100.0, feedrate.Value);
|
|
Assert.Equal("speed", feedrate.VariableRef);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_VariablesCollectedInPrepass_OrderIndependent()
|
|
{
|
|
var pgm = Parse("radius = $diameter / 2\ndiameter = 0.6\nG90\nG01X$radiusY0");
|
|
Assert.Equal(0.3, pgm.Variables["radius"].Value, 10);
|
|
var linear = (LinearMove)pgm.Codes[0];
|
|
Assert.Equal(0.3, linear.EndPoint.X, 10);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_NoVariables_WorksAsNormal()
|
|
{
|
|
var pgm = Parse("G90\nG01X1.5Y2.5");
|
|
Assert.Empty(pgm.Variables);
|
|
var linear = (LinearMove)pgm.Codes[0];
|
|
Assert.Equal(1.5, linear.EndPoint.X);
|
|
Assert.Null(linear.VariableRefs);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_RapidMove_WithVariableRef()
|
|
{
|
|
var pgm = Parse("start_x = 5.0\nG90\nG00X$start_xY0");
|
|
var rapid = (RapidMove)pgm.Codes[0];
|
|
Assert.Equal(5.0, rapid.EndPoint.X);
|
|
Assert.Equal("start_x", rapid.VariableRefs["X"]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_ArcMove_VariableOnMultipleAxes()
|
|
{
|
|
var pgm = Parse("r = 0.5\nG90\nG03X1Y0I$rJ$r");
|
|
var arc = (ArcMove)pgm.Codes[0];
|
|
Assert.Equal(0.5, arc.CenterPoint.X);
|
|
Assert.Equal(0.5, arc.CenterPoint.Y);
|
|
Assert.Equal("r", arc.VariableRefs["I"]);
|
|
Assert.Equal("r", arc.VariableRefs["J"]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_CaseInsensitive_VariableReference()
|
|
{
|
|
var pgm = Parse("Diameter = 0.3\nG90\nG01X$diameterY0");
|
|
var linear = (LinearMove)pgm.Codes[0];
|
|
Assert.Equal(0.3, linear.EndPoint.X);
|
|
}
|
|
}
|