feat: add CincinnatiPostProcessor implementing IPostProcessor
Orchestrates CincinnatiPreambleWriter and CincinnatiSheetWriter to produce a complete Cincinnati CNC output file from a Nest; includes 4 integration tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OpenNest.Posts.Cincinnati
|
||||||
|
{
|
||||||
|
public sealed class CincinnatiPostProcessor : IPostProcessor
|
||||||
|
{
|
||||||
|
public string Name => "Cincinnati CL-707";
|
||||||
|
public string Author => "OpenNest";
|
||||||
|
public string Description => "Cincinnati CL-707/CL-800/CL-900/CL-940/CLX family";
|
||||||
|
|
||||||
|
public CincinnatiPostConfig Config { get; }
|
||||||
|
|
||||||
|
public CincinnatiPostProcessor(CincinnatiPostConfig config)
|
||||||
|
{
|
||||||
|
Config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Post(Nest nest, Stream outputStream)
|
||||||
|
{
|
||||||
|
// 1. Create variable manager and register standard variables
|
||||||
|
var vars = CreateVariableManager();
|
||||||
|
|
||||||
|
// 2. Filter to non-empty plates
|
||||||
|
var plates = nest.Plates
|
||||||
|
.Where(p => p.Parts.Count > 0)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// 3. Create writers
|
||||||
|
var preamble = new CincinnatiPreambleWriter(Config);
|
||||||
|
var sheetWriter = new CincinnatiSheetWriter(Config, vars);
|
||||||
|
|
||||||
|
// 4. Build material description from first plate
|
||||||
|
var material = plates.FirstOrDefault()?.Material;
|
||||||
|
var materialDesc = material != null
|
||||||
|
? $"{material.Name}{(string.IsNullOrEmpty(material.Grade) ? "" : $", {material.Grade}")}"
|
||||||
|
: "";
|
||||||
|
|
||||||
|
// 5. Write to stream
|
||||||
|
using var writer = new StreamWriter(outputStream, Encoding.UTF8, 1024, leaveOpen: true);
|
||||||
|
|
||||||
|
// Main program
|
||||||
|
preamble.WriteMainProgram(writer, nest.Name ?? "NEST", materialDesc, plates.Count);
|
||||||
|
|
||||||
|
// Variable declaration subprogram
|
||||||
|
preamble.WriteVariableDeclaration(writer, vars);
|
||||||
|
|
||||||
|
// Sheet subprograms
|
||||||
|
for (var i = 0; i < plates.Count; i++)
|
||||||
|
{
|
||||||
|
var sheetIndex = i + 1;
|
||||||
|
var subNumber = Config.SheetSubprogramStart + i;
|
||||||
|
sheetWriter.Write(writer, plates[i], nest.Name ?? "NEST", sheetIndex, subNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Post(Nest nest, string outputFile)
|
||||||
|
{
|
||||||
|
using var fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write);
|
||||||
|
Post(nest, fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProgramVariableManager CreateVariableManager()
|
||||||
|
{
|
||||||
|
var vars = new ProgramVariableManager();
|
||||||
|
vars.GetOrCreate("ProcessFeedrate", 148); // Set by G89, no expression
|
||||||
|
vars.GetOrCreate("LeadInFeedrate", 126, $"[#148*{Config.LeadInFeedratePercent}]");
|
||||||
|
vars.GetOrCreate("LeadInArcLine2Feedrate", 127, $"[#148*{Config.LeadInArcLine2FeedratePercent}]");
|
||||||
|
vars.GetOrCreate("CircleFeedrate", 128, Config.CircleFeedrateMultiplier.ToString("0.#"));
|
||||||
|
return vars;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using OpenNest.CNC;
|
||||||
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.Posts.Cincinnati;
|
||||||
|
|
||||||
|
namespace OpenNest.Tests.Cincinnati;
|
||||||
|
|
||||||
|
public class CincinnatiPostProcessorTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Post_ProducesOutput_ForSinglePlateNest()
|
||||||
|
{
|
||||||
|
var nest = CreateTestNest();
|
||||||
|
var config = new CincinnatiPostConfig
|
||||||
|
{
|
||||||
|
ConfigurationName = "CL940",
|
||||||
|
DefaultLibraryFile = "MS135N2PANEL.lib",
|
||||||
|
PostedAccuracy = 4
|
||||||
|
};
|
||||||
|
var post = new CincinnatiPostProcessor(config);
|
||||||
|
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
post.Post(nest, ms);
|
||||||
|
|
||||||
|
var output = Encoding.UTF8.GetString(ms.ToArray());
|
||||||
|
|
||||||
|
// Main program elements
|
||||||
|
Assert.Contains("( NEST TestNest )", output);
|
||||||
|
Assert.Contains("( CONFIGURATION - CL940 )", output);
|
||||||
|
Assert.Contains("G20", output);
|
||||||
|
Assert.Contains("M30 (END OF MAIN)", output);
|
||||||
|
|
||||||
|
// Variable declaration
|
||||||
|
Assert.Contains(":100", output);
|
||||||
|
Assert.Contains("#126=", output);
|
||||||
|
|
||||||
|
// Sheet subprogram
|
||||||
|
Assert.Contains(":101", output);
|
||||||
|
Assert.Contains("( Sheet 1 )", output);
|
||||||
|
Assert.Contains("G84", output);
|
||||||
|
Assert.Contains("M99", output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Post_ImplementsIPostProcessor()
|
||||||
|
{
|
||||||
|
var post = new CincinnatiPostProcessor(new CincinnatiPostConfig());
|
||||||
|
IPostProcessor pp = post;
|
||||||
|
|
||||||
|
Assert.Equal("Cincinnati CL-707", pp.Name);
|
||||||
|
Assert.Equal("OpenNest", pp.Author);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Post_SkipsEmptyPlates()
|
||||||
|
{
|
||||||
|
var nest = new Nest("TestNest");
|
||||||
|
nest.Plates.Add(new Plate(48, 96)); // empty plate
|
||||||
|
var plate2 = new Plate(48, 96);
|
||||||
|
plate2.Parts.Add(new Part(new Drawing("Part1", CreateSquareProgram())));
|
||||||
|
nest.Plates.Add(plate2);
|
||||||
|
|
||||||
|
var config = new CincinnatiPostConfig { PostedAccuracy = 4 };
|
||||||
|
var post = new CincinnatiPostProcessor(config);
|
||||||
|
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
post.Post(nest, ms);
|
||||||
|
|
||||||
|
var output = Encoding.UTF8.GetString(ms.ToArray());
|
||||||
|
|
||||||
|
// Should only have one sheet subprogram call in main
|
||||||
|
Assert.Contains("N1M98 P101 (SHEET 1)", output);
|
||||||
|
Assert.DoesNotContain("SHEET 2", output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Post_ToFile_CreatesFile()
|
||||||
|
{
|
||||||
|
var nest = CreateTestNest();
|
||||||
|
var config = new CincinnatiPostConfig { PostedAccuracy = 4 };
|
||||||
|
var post = new CincinnatiPostProcessor(config);
|
||||||
|
var tempFile = Path.GetTempFileName() + ".CNC";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
post.Post(nest, tempFile);
|
||||||
|
Assert.True(File.Exists(tempFile));
|
||||||
|
var content = File.ReadAllText(tempFile);
|
||||||
|
Assert.Contains("M30", content);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (File.Exists(tempFile))
|
||||||
|
File.Delete(tempFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Nest CreateTestNest()
|
||||||
|
{
|
||||||
|
var nest = new Nest("TestNest");
|
||||||
|
var drawing = new Drawing("Square", CreateSquareProgram());
|
||||||
|
nest.Drawings.Add(drawing);
|
||||||
|
|
||||||
|
var plate = new Plate(48.0, 96.0);
|
||||||
|
plate.Parts.Add(new Part(drawing, new Vector(10, 10)));
|
||||||
|
nest.Plates.Add(plate);
|
||||||
|
|
||||||
|
return nest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Program CreateSquareProgram()
|
||||||
|
{
|
||||||
|
var pgm = new Program();
|
||||||
|
pgm.Codes.Add(new RapidMove(0, 0));
|
||||||
|
pgm.Codes.Add(new LinearMove(2, 0));
|
||||||
|
pgm.Codes.Add(new LinearMove(2, 2));
|
||||||
|
pgm.Codes.Add(new LinearMove(0, 2));
|
||||||
|
pgm.Codes.Add(new LinearMove(0, 0));
|
||||||
|
return pgm;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user