fix: add proper spacing between G-code words in Cincinnati post output
G-code output was concatenated without spaces (e.g. N1005G0X1.4375Y-0.6562). Now emits standard spacing (N1005 G0 X1.4375 Y-0.6562) across all motion commands, line numbers, kerf comp, feedrates, M-codes, and comments. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ public sealed class FeatureContext
|
||||
public bool IsLastFeatureOnSheet { get; set; }
|
||||
public bool IsSafetyHeadraise { get; set; }
|
||||
public bool IsExteriorFeature { get; set; }
|
||||
public bool IsEtch { get; set; }
|
||||
public string LibraryFile { get; set; } = "";
|
||||
public double CutDistance { get; set; }
|
||||
public double SheetDiagonal { get; set; }
|
||||
@@ -61,17 +62,24 @@ public sealed class CincinnatiFeatureWriter
|
||||
if (ctx.IsFirstFeatureOfPart && !string.IsNullOrEmpty(ctx.PartName))
|
||||
writer.WriteLine(CoordinateFormatter.Comment($"PART: {ctx.PartName}"));
|
||||
|
||||
// 3. G89 process params (if RepeatG89BeforeEachFeature)
|
||||
if (_config.RepeatG89BeforeEachFeature && _config.ProcessParameterMode == G89Mode.LibraryFile)
|
||||
// 3. G89 process params
|
||||
if (_config.ProcessParameterMode == G89Mode.LibraryFile)
|
||||
{
|
||||
var lib = ctx.LibraryFile;
|
||||
if (!string.IsNullOrEmpty(lib))
|
||||
{
|
||||
var lib = !string.IsNullOrEmpty(ctx.LibraryFile) ? ctx.LibraryFile : _config.DefaultLibraryFile;
|
||||
var speedClass = _speedClassifier.Classify(ctx.CutDistance, ctx.SheetDiagonal);
|
||||
var cutDist = _speedClassifier.FormatCutDist(ctx.CutDistance, ctx.SheetDiagonal);
|
||||
writer.WriteLine($"G89 P {lib} ({speedClass} {cutDist})");
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteLine("(WARNING: No library found)");
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Pierce and start cut
|
||||
writer.WriteLine("G84");
|
||||
// 4. Pierce/beam on — G85 for etch (no pierce), G84 for cut
|
||||
writer.WriteLine(ctx.IsEtch ? "G85" : "G84");
|
||||
|
||||
// 5. Anti-dive off
|
||||
if (_config.UseAntiDive)
|
||||
@@ -90,8 +98,8 @@ public sealed class CincinnatiFeatureWriter
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
// Kerf compensation on first cutting move
|
||||
if (!kerfEmitted && _config.KerfCompensation == KerfMode.ControllerSide)
|
||||
// Kerf compensation on first cutting move (skip for etch)
|
||||
if (!ctx.IsEtch && !kerfEmitted && _config.KerfCompensation == KerfMode.ControllerSide)
|
||||
{
|
||||
sb.Append(_config.DefaultKerfSide == KerfSide.Left ? "G41 " : "G42 ");
|
||||
kerfEmitted = true;
|
||||
@@ -99,8 +107,8 @@ public sealed class CincinnatiFeatureWriter
|
||||
|
||||
sb.Append($"G1 X{_fmt.FormatCoord(linear.EndPoint.X)} Y{_fmt.FormatCoord(linear.EndPoint.Y)}");
|
||||
|
||||
// Feedrate
|
||||
var feedVar = GetFeedVariable(linear.Layer);
|
||||
// Feedrate — etch always uses process feedrate
|
||||
var feedVar = ctx.IsEtch ? "#148" : GetFeedVariable(linear.Layer);
|
||||
if (feedVar != lastFeedVar)
|
||||
{
|
||||
sb.Append($" F{feedVar}");
|
||||
@@ -114,8 +122,8 @@ public sealed class CincinnatiFeatureWriter
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
// Kerf compensation on first cutting move
|
||||
if (!kerfEmitted && _config.KerfCompensation == KerfMode.ControllerSide)
|
||||
// Kerf compensation on first cutting move (skip for etch)
|
||||
if (!ctx.IsEtch && !kerfEmitted && _config.KerfCompensation == KerfMode.ControllerSide)
|
||||
{
|
||||
sb.Append(_config.DefaultKerfSide == KerfSide.Left ? "G41 " : "G42 ");
|
||||
kerfEmitted = true;
|
||||
@@ -130,9 +138,11 @@ public sealed class CincinnatiFeatureWriter
|
||||
var j = arc.CenterPoint.Y - currentPos.Y;
|
||||
sb.Append($" I{_fmt.FormatCoord(i)} J{_fmt.FormatCoord(j)}");
|
||||
|
||||
// Feedrate — full circles use multiplied feedrate
|
||||
// Feedrate — etch always uses process feedrate, cut uses layer-based
|
||||
var isFullCircle = IsFullCircle(currentPos, arc.EndPoint);
|
||||
var feedVar = isFullCircle ? "[#148*#128]" : GetFeedVariable(arc.Layer);
|
||||
var feedVar = ctx.IsEtch ? "#148"
|
||||
: isFullCircle ? "[#148*#128]"
|
||||
: GetFeedVariable(arc.Layer);
|
||||
if (feedVar != lastFeedVar)
|
||||
{
|
||||
sb.Append($" F{feedVar}");
|
||||
|
||||
@@ -27,19 +27,22 @@ public sealed class CincinnatiPartSubprogramWriter
|
||||
/// The program coordinates must already be normalized to origin (0,0).
|
||||
/// </summary>
|
||||
public void Write(TextWriter w, Program normalizedProgram, string drawingName,
|
||||
int subNumber, string libraryFile, double sheetDiagonal)
|
||||
int subNumber, string cutLibrary, string etchLibrary, double sheetDiagonal)
|
||||
{
|
||||
var features = SplitFeatures(normalizedProgram.Codes);
|
||||
if (features.Count == 0)
|
||||
var allFeatures = SplitFeatures(normalizedProgram.Codes);
|
||||
if (allFeatures.Count == 0)
|
||||
return;
|
||||
|
||||
// Classify and order: etch features first, then cut features
|
||||
var ordered = OrderFeatures(allFeatures);
|
||||
|
||||
w.WriteLine("(*****************************************************)");
|
||||
w.WriteLine($":{subNumber}");
|
||||
w.WriteLine(CoordinateFormatter.Comment($"PART: {drawingName}"));
|
||||
|
||||
for (var i = 0; i < features.Count; i++)
|
||||
for (var i = 0; i < ordered.Count; i++)
|
||||
{
|
||||
var codes = features[i];
|
||||
var (codes, isEtch) = ordered[i];
|
||||
var featureNumber = i == 0
|
||||
? _config.FeatureLineNumberStart
|
||||
: 1000 + i + 1;
|
||||
@@ -51,10 +54,11 @@ public sealed class CincinnatiPartSubprogramWriter
|
||||
FeatureNumber = featureNumber,
|
||||
PartName = drawingName,
|
||||
IsFirstFeatureOfPart = false,
|
||||
IsLastFeatureOnSheet = i == features.Count - 1,
|
||||
IsLastFeatureOnSheet = i == ordered.Count - 1,
|
||||
IsSafetyHeadraise = false,
|
||||
IsExteriorFeature = false,
|
||||
LibraryFile = libraryFile,
|
||||
IsEtch = isEtch,
|
||||
LibraryFile = isEtch ? etchLibrary : cutLibrary,
|
||||
CutDistance = cutDistance,
|
||||
SheetDiagonal = sheetDiagonal
|
||||
};
|
||||
@@ -66,6 +70,28 @@ public sealed class CincinnatiPartSubprogramWriter
|
||||
w.WriteLine($"M99 (END OF {drawingName})");
|
||||
}
|
||||
|
||||
internal static List<(List<ICode> codes, bool isEtch)> OrderFeatures(List<List<ICode>> features)
|
||||
{
|
||||
var result = new List<(List<ICode>, bool)>();
|
||||
var etch = new List<List<ICode>>();
|
||||
var cut = new List<List<ICode>>();
|
||||
|
||||
foreach (var f in features)
|
||||
{
|
||||
if (CincinnatiSheetWriter.IsFeatureEtch(f))
|
||||
etch.Add(f);
|
||||
else
|
||||
cut.Add(f);
|
||||
}
|
||||
|
||||
foreach (var f in etch)
|
||||
result.Add((f, true));
|
||||
foreach (var f in cut)
|
||||
result.Add((f, false));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a sub-program key for matching parts to their sub-programs.
|
||||
/// </summary>
|
||||
|
||||
@@ -21,7 +21,9 @@ public sealed class CincinnatiPreambleWriter
|
||||
/// <summary>
|
||||
/// Writes the main program header block.
|
||||
/// </summary>
|
||||
public void WriteMainProgram(TextWriter w, string nestName, string materialDescription, int sheetCount)
|
||||
/// <param name="initialLibrary">Resolved G89 library file for the initial process setup.</param>
|
||||
public void WriteMainProgram(TextWriter w, string nestName, string materialDescription,
|
||||
int sheetCount, string initialLibrary)
|
||||
{
|
||||
w.WriteLine(CoordinateFormatter.Comment($"NEST {nestName}"));
|
||||
w.WriteLine(CoordinateFormatter.Comment($"CONFIGURATION - {_config.ConfigurationName}"));
|
||||
@@ -39,8 +41,8 @@ public sealed class CincinnatiPreambleWriter
|
||||
|
||||
w.WriteLine("M42");
|
||||
|
||||
if (_config.ProcessParameterMode == G89Mode.LibraryFile && !string.IsNullOrEmpty(_config.DefaultLibraryFile))
|
||||
w.WriteLine($"G89 P {_config.DefaultLibraryFile}");
|
||||
if (_config.ProcessParameterMode == G89Mode.LibraryFile && !string.IsNullOrEmpty(initialLibrary))
|
||||
w.WriteLine($"G89 P {initialLibrary}");
|
||||
|
||||
w.WriteLine($"M98 P{_config.VariableDeclarationSubprogram} (Variable Declaration)");
|
||||
|
||||
|
||||
@@ -30,11 +30,14 @@ public sealed class CincinnatiSheetWriter
|
||||
/// <summary>
|
||||
/// Writes a complete sheet subprogram for the given plate.
|
||||
/// </summary>
|
||||
/// <param name="cutLibrary">Resolved G89 library file for cut operations.</param>
|
||||
/// <param name="etchLibrary">Resolved G89 library file for etch operations.</param>
|
||||
/// <param name="partSubprograms">
|
||||
/// Optional mapping of (drawingId, rotationKey) to sub-program number.
|
||||
/// When provided, non-cutoff parts are emitted as M98 calls instead of inline features.
|
||||
/// </param>
|
||||
public void Write(TextWriter w, Plate plate, string nestName, int sheetIndex, int subNumber,
|
||||
string cutLibrary, string etchLibrary,
|
||||
Dictionary<(int, long), int> partSubprograms = null)
|
||||
{
|
||||
if (plate.Parts.Count == 0)
|
||||
@@ -43,7 +46,6 @@ public sealed class CincinnatiSheetWriter
|
||||
var width = plate.Size.Width;
|
||||
var length = plate.Size.Length;
|
||||
var sheetDiagonal = System.Math.Sqrt(width * width + length * length);
|
||||
var libraryFile = _config.DefaultLibraryFile ?? "";
|
||||
var varDeclSub = _config.VariableDeclarationSubprogram;
|
||||
var partCount = plate.Parts.Count(p => !p.BaseDrawing.IsCutOff);
|
||||
|
||||
@@ -62,13 +64,13 @@ public sealed class CincinnatiSheetWriter
|
||||
w.WriteLine("M42");
|
||||
w.WriteLine("N10000");
|
||||
w.WriteLine("G92 X#5021 Y#5022");
|
||||
if (!string.IsNullOrEmpty(libraryFile))
|
||||
w.WriteLine($"G89 P {libraryFile}");
|
||||
if (!string.IsNullOrEmpty(cutLibrary))
|
||||
w.WriteLine($"G89 P {cutLibrary}");
|
||||
w.WriteLine($"M98 P{varDeclSub} (Variable Declaration)");
|
||||
w.WriteLine("G90");
|
||||
w.WriteLine("M47(CPT)");
|
||||
if (!string.IsNullOrEmpty(libraryFile))
|
||||
w.WriteLine($"G89 P {libraryFile}");
|
||||
w.WriteLine("M47");
|
||||
if (!string.IsNullOrEmpty(cutLibrary))
|
||||
w.WriteLine($"G89 P {cutLibrary}");
|
||||
w.WriteLine("GOTO1( Goto Feature )");
|
||||
|
||||
// 3. Order parts: non-cutoff sorted by Bottom then Left, cutoffs last
|
||||
@@ -86,9 +88,9 @@ public sealed class CincinnatiSheetWriter
|
||||
|
||||
// 4. Emit parts
|
||||
if (partSubprograms != null)
|
||||
WritePartsWithSubprograms(w, allParts, libraryFile, sheetDiagonal, partSubprograms);
|
||||
WritePartsWithSubprograms(w, allParts, cutLibrary, etchLibrary, sheetDiagonal, partSubprograms);
|
||||
else
|
||||
WritePartsInline(w, allParts, libraryFile, sheetDiagonal);
|
||||
WritePartsInline(w, allParts, cutLibrary, etchLibrary, sheetDiagonal);
|
||||
|
||||
// 5. Footer
|
||||
w.WriteLine("M42");
|
||||
@@ -99,7 +101,7 @@ public sealed class CincinnatiSheetWriter
|
||||
}
|
||||
|
||||
private void WritePartsWithSubprograms(TextWriter w, List<Part> allParts,
|
||||
string libraryFile, double sheetDiagonal,
|
||||
string cutLibrary, string etchLibrary, double sheetDiagonal,
|
||||
Dictionary<(int, long), int> partSubprograms)
|
||||
{
|
||||
var lastPartName = "";
|
||||
@@ -126,26 +128,28 @@ public sealed class CincinnatiSheetWriter
|
||||
else
|
||||
{
|
||||
// Inline features for cutoffs or parts without sub-programs
|
||||
var features = SplitPartFeatures(part);
|
||||
var features = SplitAndOrderFeatures(part);
|
||||
for (var f = 0; f < features.Count; f++)
|
||||
{
|
||||
var (codes, isEtch) = features[f];
|
||||
var featureNumber = featureIndex == 0
|
||||
? _config.FeatureLineNumberStart
|
||||
: 1000 + featureIndex + 1;
|
||||
|
||||
var isLastFeature = isLastPart && f == features.Count - 1;
|
||||
var cutDistance = ComputeCutDistance(features[f]);
|
||||
var cutDistance = ComputeCutDistance(codes);
|
||||
|
||||
var ctx = new FeatureContext
|
||||
{
|
||||
Codes = features[f],
|
||||
Codes = codes,
|
||||
FeatureNumber = featureNumber,
|
||||
PartName = partName,
|
||||
IsFirstFeatureOfPart = isNewPart && f == 0,
|
||||
IsLastFeatureOnSheet = isLastFeature,
|
||||
IsSafetyHeadraise = isSafetyHeadraise && f == 0,
|
||||
IsExteriorFeature = false,
|
||||
LibraryFile = libraryFile,
|
||||
IsEtch = isEtch,
|
||||
LibraryFile = isEtch ? etchLibrary : cutLibrary,
|
||||
CutDistance = cutDistance,
|
||||
SheetDiagonal = sheetDiagonal
|
||||
};
|
||||
@@ -195,36 +199,22 @@ public sealed class CincinnatiSheetWriter
|
||||
}
|
||||
|
||||
private void WritePartsInline(TextWriter w, List<Part> allParts,
|
||||
string libraryFile, double sheetDiagonal)
|
||||
string cutLibrary, string etchLibrary, double sheetDiagonal)
|
||||
{
|
||||
// Multi-contour splitting
|
||||
var features = new List<(Part part, List<ICode> codes)>();
|
||||
// Split and classify features, ordering etch before cut per part
|
||||
var features = new List<(Part part, List<ICode> codes, bool isEtch)>();
|
||||
foreach (var part in allParts)
|
||||
{
|
||||
List<ICode> current = null;
|
||||
foreach (var code in part.Program.Codes)
|
||||
{
|
||||
if (code is RapidMove)
|
||||
{
|
||||
if (current != null)
|
||||
features.Add((part, current));
|
||||
current = new List<ICode> { code };
|
||||
}
|
||||
else
|
||||
{
|
||||
current ??= new List<ICode>();
|
||||
current.Add(code);
|
||||
}
|
||||
}
|
||||
if (current != null && current.Count > 0)
|
||||
features.Add((part, current));
|
||||
var partFeatures = SplitAndOrderFeatures(part);
|
||||
foreach (var (codes, isEtch) in partFeatures)
|
||||
features.Add((part, codes, isEtch));
|
||||
}
|
||||
|
||||
// Emit features
|
||||
var lastPartName = "";
|
||||
for (var i = 0; i < features.Count; i++)
|
||||
{
|
||||
var (part, codes) = features[i];
|
||||
var (part, codes, isEtch) = features[i];
|
||||
var partName = part.BaseDrawing.Name;
|
||||
var isFirstFeatureOfPart = partName != lastPartName;
|
||||
var isSafetyHeadraise = partName != lastPartName && lastPartName != "";
|
||||
@@ -245,7 +235,8 @@ public sealed class CincinnatiSheetWriter
|
||||
IsLastFeatureOnSheet = isLastFeature,
|
||||
IsSafetyHeadraise = isSafetyHeadraise,
|
||||
IsExteriorFeature = false,
|
||||
LibraryFile = libraryFile,
|
||||
IsEtch = isEtch,
|
||||
LibraryFile = isEtch ? etchLibrary : cutLibrary,
|
||||
CutDistance = cutDistance,
|
||||
SheetDiagonal = sheetDiagonal
|
||||
};
|
||||
@@ -255,9 +246,14 @@ public sealed class CincinnatiSheetWriter
|
||||
}
|
||||
}
|
||||
|
||||
private static List<List<ICode>> SplitPartFeatures(Part part)
|
||||
/// <summary>
|
||||
/// Splits a part's program into features (by rapids), classifies each as etch or cut,
|
||||
/// and orders etch features before cut features.
|
||||
/// </summary>
|
||||
public static List<(List<ICode> codes, bool isEtch)> SplitAndOrderFeatures(Part part)
|
||||
{
|
||||
var features = new List<List<ICode>>();
|
||||
var etchFeatures = new List<List<ICode>>();
|
||||
var cutFeatures = new List<List<ICode>>();
|
||||
List<ICode> current = null;
|
||||
|
||||
foreach (var code in part.Program.Codes)
|
||||
@@ -265,7 +261,7 @@ public sealed class CincinnatiSheetWriter
|
||||
if (code is RapidMove)
|
||||
{
|
||||
if (current != null)
|
||||
features.Add(current);
|
||||
ClassifyAndAdd(current, etchFeatures, cutFeatures);
|
||||
current = new List<ICode> { code };
|
||||
}
|
||||
else
|
||||
@@ -276,9 +272,40 @@ public sealed class CincinnatiSheetWriter
|
||||
}
|
||||
|
||||
if (current != null && current.Count > 0)
|
||||
features.Add(current);
|
||||
ClassifyAndAdd(current, etchFeatures, cutFeatures);
|
||||
|
||||
return features;
|
||||
// Etch features first, then cut features
|
||||
var result = new List<(List<ICode>, bool)>();
|
||||
foreach (var f in etchFeatures)
|
||||
result.Add((f, true));
|
||||
foreach (var f in cutFeatures)
|
||||
result.Add((f, false));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void ClassifyAndAdd(List<ICode> codes,
|
||||
List<List<ICode>> etchFeatures, List<List<ICode>> cutFeatures)
|
||||
{
|
||||
if (IsFeatureEtch(codes))
|
||||
etchFeatures.Add(codes);
|
||||
else
|
||||
cutFeatures.Add(codes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A feature is etch if any non-rapid move has LayerType.Scribe.
|
||||
/// </summary>
|
||||
public static bool IsFeatureEtch(List<ICode> codes)
|
||||
{
|
||||
foreach (var code in codes)
|
||||
{
|
||||
if (code is LinearMove linear && linear.Layer == LayerType.Scribe)
|
||||
return true;
|
||||
if (code is ArcMove arc && arc.Layer == LayerType.Scribe)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static double ComputeCutDistance(List<ICode> codes)
|
||||
|
||||
@@ -13,9 +13,7 @@ public class CincinnatiFeatureWriterTests
|
||||
UseAntiDive = true,
|
||||
KerfCompensation = KerfMode.ControllerSide,
|
||||
DefaultKerfSide = KerfSide.Left,
|
||||
RepeatG89BeforeEachFeature = true,
|
||||
ProcessParameterMode = G89Mode.LibraryFile,
|
||||
DefaultLibraryFile = "MILD10",
|
||||
InteriorM47 = M47Mode.Always,
|
||||
ExteriorM47 = M47Mode.Always,
|
||||
UseSpeedGas = false,
|
||||
@@ -289,12 +287,10 @@ public class CincinnatiFeatureWriterTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void G89_EmittedWhenRepeatEnabled()
|
||||
public void G89_EmittedWithLibraryFile()
|
||||
{
|
||||
var config = DefaultConfig();
|
||||
config.RepeatG89BeforeEachFeature = true;
|
||||
config.ProcessParameterMode = G89Mode.LibraryFile;
|
||||
config.DefaultLibraryFile = "MILD10";
|
||||
var ctx = SimpleContext();
|
||||
ctx.LibraryFile = "MILD10";
|
||||
ctx.CutDistance = 18.0;
|
||||
@@ -305,14 +301,65 @@ public class CincinnatiFeatureWriterTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void G89_NotEmittedWhenRepeatDisabled()
|
||||
public void G89_WarningEmittedWhenNoLibrary()
|
||||
{
|
||||
var config = DefaultConfig();
|
||||
config.RepeatG89BeforeEachFeature = false;
|
||||
config.ProcessParameterMode = G89Mode.LibraryFile;
|
||||
var ctx = SimpleContext();
|
||||
ctx.LibraryFile = "";
|
||||
var output = WriteFeature(config, ctx);
|
||||
|
||||
Assert.DoesNotContain("G89", output);
|
||||
Assert.Contains("WARNING: No library found", output);
|
||||
Assert.DoesNotContain("G89 P", output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Etch_UsesG85InsteadOfG84()
|
||||
{
|
||||
var config = DefaultConfig();
|
||||
var ctx = SimpleContext();
|
||||
ctx.IsEtch = true;
|
||||
ctx.LibraryFile = "EtchN2.lib";
|
||||
var output = WriteFeature(config, ctx);
|
||||
|
||||
Assert.Contains("G85", output);
|
||||
Assert.DoesNotContain("G84", output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Etch_SkipsKerfCompensation()
|
||||
{
|
||||
var config = DefaultConfig();
|
||||
config.KerfCompensation = KerfMode.ControllerSide;
|
||||
var ctx = SimpleContext();
|
||||
ctx.IsEtch = true;
|
||||
ctx.LibraryFile = "EtchN2.lib";
|
||||
var output = WriteFeature(config, ctx);
|
||||
|
||||
Assert.DoesNotContain("G41", output);
|
||||
Assert.DoesNotContain("G42", output);
|
||||
Assert.DoesNotContain("G40", output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Etch_AllMovesUseProcessFeedrate()
|
||||
{
|
||||
var config = DefaultConfig();
|
||||
config.KerfCompensation = KerfMode.PreApplied;
|
||||
var codes = new List<ICode>
|
||||
{
|
||||
new RapidMove(1.0, 1.0),
|
||||
new LinearMove(2.0, 1.0) { Layer = LayerType.Leadin },
|
||||
new LinearMove(3.0, 1.0) { Layer = LayerType.Cut }
|
||||
};
|
||||
var ctx = SimpleContext(codes);
|
||||
ctx.IsEtch = true;
|
||||
ctx.LibraryFile = "EtchN2.lib";
|
||||
var output = WriteFeature(config, ctx);
|
||||
|
||||
// Should use #148 for all moves, not #126 for lead-in
|
||||
Assert.DoesNotContain("F#126", output);
|
||||
Assert.Contains("F#148", output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -17,7 +17,6 @@ public class CincinnatiPostProcessorTests
|
||||
var config = new CincinnatiPostConfig
|
||||
{
|
||||
ConfigurationName = "CL940",
|
||||
DefaultLibraryFile = "MS135N2PANEL.lib",
|
||||
PostedAccuracy = 4
|
||||
};
|
||||
var post = new CincinnatiPostProcessor(config);
|
||||
@@ -104,10 +103,19 @@ public class CincinnatiPostProcessorTests
|
||||
var config = new CincinnatiPostConfig
|
||||
{
|
||||
ConfigurationName = "CL940_CORONA",
|
||||
DefaultLibraryFile = "MS135N2PANEL.lib",
|
||||
DefaultAssistGas = "N2",
|
||||
DefaultEtchGas = "N2",
|
||||
PostedUnits = Units.Inches,
|
||||
KerfCompensation = KerfMode.ControllerSide,
|
||||
UseAntiDive = true
|
||||
UseAntiDive = true,
|
||||
MaterialLibraries = new()
|
||||
{
|
||||
new MaterialLibraryEntry { Material = "Mild Steel", Thickness = 0.135, Gas = "N2", Library = "MS135N2PANEL.lib" }
|
||||
},
|
||||
EtchLibraries = new()
|
||||
{
|
||||
new EtchLibraryEntry { Gas = "N2", Library = "EtchN2.lib" }
|
||||
}
|
||||
};
|
||||
|
||||
var opts = new JsonSerializerOptions
|
||||
@@ -119,10 +127,15 @@ public class CincinnatiPostProcessorTests
|
||||
var deserialized = JsonSerializer.Deserialize<CincinnatiPostConfig>(json, opts);
|
||||
|
||||
Assert.Equal("CL940_CORONA", deserialized.ConfigurationName);
|
||||
Assert.Equal("MS135N2PANEL.lib", deserialized.DefaultLibraryFile);
|
||||
Assert.Equal("N2", deserialized.DefaultAssistGas);
|
||||
Assert.Equal("N2", deserialized.DefaultEtchGas);
|
||||
Assert.Equal(Units.Inches, deserialized.PostedUnits);
|
||||
Assert.Equal(KerfMode.ControllerSide, deserialized.KerfCompensation);
|
||||
Assert.True(deserialized.UseAntiDive);
|
||||
Assert.Single(deserialized.MaterialLibraries);
|
||||
Assert.Equal("MS135N2PANEL.lib", deserialized.MaterialLibraries[0].Library);
|
||||
Assert.Single(deserialized.EtchLibraries);
|
||||
Assert.Equal("EtchN2.lib", deserialized.EtchLibraries[0].Library);
|
||||
|
||||
// Enums serialize as strings
|
||||
Assert.Contains("\"Inches\"", json);
|
||||
@@ -198,7 +211,7 @@ public class CincinnatiPostProcessorTests
|
||||
var output = Encoding.UTF8.GetString(ms.ToArray());
|
||||
|
||||
// Both parts should call the same sub-program
|
||||
var m98Count = System.Text.RegularExpressions.Regex.Matches(output, "M98P200").Count;
|
||||
var m98Count = System.Text.RegularExpressions.Regex.Matches(output, @"M98 P200\b").Count;
|
||||
Assert.Equal(2, m98Count);
|
||||
|
||||
// Only one sub-program definition
|
||||
|
||||
@@ -13,14 +13,13 @@ public class CincinnatiPreambleWriterTests
|
||||
var config = new CincinnatiPostConfig
|
||||
{
|
||||
ConfigurationName = "CL940",
|
||||
PostedUnits = Units.Inches,
|
||||
DefaultLibraryFile = "MS135N2PANEL.lib"
|
||||
PostedUnits = Units.Inches
|
||||
};
|
||||
var sb = new StringBuilder();
|
||||
using var sw = new StringWriter(sb);
|
||||
var writer = new CincinnatiPreambleWriter(config);
|
||||
|
||||
writer.WriteMainProgram(sw, "TestNest", "Mild Steel, 10GA", 2);
|
||||
writer.WriteMainProgram(sw, "TestNest", "Mild Steel, 10GA", 2, "MS135N2PANEL.lib");
|
||||
|
||||
var output = sb.ToString();
|
||||
Assert.Contains("( NEST TestNest )", output);
|
||||
@@ -43,7 +42,7 @@ public class CincinnatiPreambleWriterTests
|
||||
using var sw = new StringWriter(sb);
|
||||
var writer = new CincinnatiPreambleWriter(config);
|
||||
|
||||
writer.WriteMainProgram(sw, "Test", "", 1);
|
||||
writer.WriteMainProgram(sw, "Test", "", 1, "");
|
||||
|
||||
Assert.Contains("G21", sb.ToString());
|
||||
}
|
||||
@@ -56,7 +55,7 @@ public class CincinnatiPreambleWriterTests
|
||||
using var sw = new StringWriter(sb);
|
||||
var writer = new CincinnatiPreambleWriter(config);
|
||||
|
||||
writer.WriteMainProgram(sw, "Test", "", 1);
|
||||
writer.WriteMainProgram(sw, "Test", "", 1, "");
|
||||
|
||||
Assert.Contains("G61", sb.ToString());
|
||||
}
|
||||
@@ -69,7 +68,7 @@ public class CincinnatiPreambleWriterTests
|
||||
using var sw = new StringWriter(sb);
|
||||
var writer = new CincinnatiPreambleWriter(config);
|
||||
|
||||
writer.WriteMainProgram(sw, "Test", "", 1);
|
||||
writer.WriteMainProgram(sw, "Test", "", 1, "");
|
||||
|
||||
Assert.DoesNotContain("G61", sb.ToString());
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -14,7 +15,6 @@ public class CincinnatiSheetWriterTests
|
||||
{
|
||||
var config = new CincinnatiPostConfig
|
||||
{
|
||||
DefaultLibraryFile = "MS135N2PANEL.lib",
|
||||
PostedAccuracy = 4
|
||||
};
|
||||
var plate = new Plate(48.0, 96.0);
|
||||
@@ -24,7 +24,7 @@ public class CincinnatiSheetWriterTests
|
||||
using var sw = new StringWriter(sb);
|
||||
var sheetWriter = new CincinnatiSheetWriter(config, new ProgramVariableManager());
|
||||
|
||||
sheetWriter.Write(sw, plate, "TestNest", 1, 101);
|
||||
sheetWriter.Write(sw, plate, "TestNest", 1, 101, "MS135N2PANEL.lib", "EtchN2.lib");
|
||||
|
||||
var output = sb.ToString();
|
||||
Assert.Contains(":101", output);
|
||||
@@ -32,6 +32,7 @@ public class CincinnatiSheetWriterTests
|
||||
Assert.Contains("#110=", output);
|
||||
Assert.Contains("#111=", output);
|
||||
Assert.Contains("G92 X#5021 Y#5022", output);
|
||||
Assert.Contains("G89 P MS135N2PANEL.lib", output);
|
||||
Assert.Contains("M99", output);
|
||||
}
|
||||
|
||||
@@ -50,7 +51,7 @@ public class CincinnatiSheetWriterTests
|
||||
using var sw = new StringWriter(sb);
|
||||
var sheetWriter = new CincinnatiSheetWriter(config, new ProgramVariableManager());
|
||||
|
||||
sheetWriter.Write(sw, plate, "TestNest", 1, 101);
|
||||
sheetWriter.Write(sw, plate, "TestNest", 1, 101, "", "");
|
||||
|
||||
var output = sb.ToString();
|
||||
Assert.Contains("M42", output);
|
||||
@@ -68,7 +69,7 @@ public class CincinnatiSheetWriterTests
|
||||
using var sw = new StringWriter(sb);
|
||||
var sheetWriter = new CincinnatiSheetWriter(config, new ProgramVariableManager());
|
||||
|
||||
sheetWriter.Write(sw, plate, "TestNest", 1, 101);
|
||||
sheetWriter.Write(sw, plate, "TestNest", 1, 101, "", "");
|
||||
|
||||
Assert.Equal("", sb.ToString());
|
||||
}
|
||||
@@ -96,7 +97,7 @@ public class CincinnatiSheetWriterTests
|
||||
using var sw = new StringWriter(sb);
|
||||
var sheetWriter = new CincinnatiSheetWriter(config, new ProgramVariableManager());
|
||||
|
||||
sheetWriter.Write(sw, plate, "TestNest", 1, 101);
|
||||
sheetWriter.Write(sw, plate, "TestNest", 1, 101, "", "");
|
||||
|
||||
var output = sb.ToString();
|
||||
// Should have two G84 pierce commands (one per contour)
|
||||
@@ -104,6 +105,80 @@ public class CincinnatiSheetWriterTests
|
||||
Assert.Equal(2, g84Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSheet_EtchFeaturesOrderedBeforeCut()
|
||||
{
|
||||
var config = new CincinnatiPostConfig { PostedAccuracy = 4 };
|
||||
var pgm = new Program();
|
||||
// Cut contour first in program
|
||||
pgm.Codes.Add(new RapidMove(0, 0));
|
||||
pgm.Codes.Add(new LinearMove(5, 0) { Layer = LayerType.Cut });
|
||||
pgm.Codes.Add(new LinearMove(5, 5) { Layer = LayerType.Cut });
|
||||
// Etch contour second in program
|
||||
pgm.Codes.Add(new RapidMove(1, 1));
|
||||
pgm.Codes.Add(new LinearMove(2, 1) { Layer = LayerType.Scribe });
|
||||
pgm.Codes.Add(new LinearMove(2, 2) { Layer = LayerType.Scribe });
|
||||
|
||||
var plate = new Plate(48.0, 96.0);
|
||||
plate.Parts.Add(new Part(new Drawing("MixedPart", pgm)));
|
||||
|
||||
var sb = new StringBuilder();
|
||||
using var sw = new StringWriter(sb);
|
||||
var sheetWriter = new CincinnatiSheetWriter(config, new ProgramVariableManager());
|
||||
|
||||
sheetWriter.Write(sw, plate, "TestNest", 1, 101, "MS250O2.lib", "EtchN2.lib");
|
||||
|
||||
var output = sb.ToString();
|
||||
// Etch (G85) should appear before cut (G84)
|
||||
var g85Idx = output.IndexOf("G85");
|
||||
var g84Idx = output.IndexOf("G84");
|
||||
Assert.True(g85Idx >= 0, "G85 should be present for etch");
|
||||
Assert.True(g84Idx >= 0, "G84 should be present for cut");
|
||||
Assert.True(g85Idx < g84Idx, "G85 (etch) should come before G84 (cut)");
|
||||
|
||||
// Etch uses etch library
|
||||
Assert.Contains("G89 P EtchN2.lib", output);
|
||||
// Cut uses cut library
|
||||
Assert.Contains("G89 P MS250O2.lib", output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsFeatureEtch_ReturnsTrueForScribeLayer()
|
||||
{
|
||||
var codes = new List<ICode>
|
||||
{
|
||||
new RapidMove(0, 0),
|
||||
new LinearMove(1, 0) { Layer = LayerType.Scribe },
|
||||
new LinearMove(1, 1) { Layer = LayerType.Scribe }
|
||||
};
|
||||
|
||||
Assert.True(CincinnatiSheetWriter.IsFeatureEtch(codes));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsFeatureEtch_ReturnsFalseForCutLayer()
|
||||
{
|
||||
var codes = new List<ICode>
|
||||
{
|
||||
new RapidMove(0, 0),
|
||||
new LinearMove(1, 0) { Layer = LayerType.Cut },
|
||||
new LinearMove(1, 1) { Layer = LayerType.Cut }
|
||||
};
|
||||
|
||||
Assert.False(CincinnatiSheetWriter.IsFeatureEtch(codes));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsFeatureEtch_ReturnsFalseForRapidsOnly()
|
||||
{
|
||||
var codes = new List<ICode>
|
||||
{
|
||||
new RapidMove(0, 0)
|
||||
};
|
||||
|
||||
Assert.False(CincinnatiSheetWriter.IsFeatureEtch(codes));
|
||||
}
|
||||
|
||||
private static Program CreateSimpleProgram()
|
||||
{
|
||||
var pgm = new Program();
|
||||
|
||||
Reference in New Issue
Block a user