mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-04 10:11:37 -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>
159 lines
6.2 KiB
C#
159 lines
6.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using OpenNest.CNC;
|
|
|
|
namespace OpenNest.Posts.CincinnatiCIFiber
|
|
{
|
|
/// <summary>
|
|
/// Post-processor for the CI Fiber family (e.g. the CI Fiber 4020 8kW with
|
|
/// nLight CLX / Beckhoff TF5200 / Precitec ProCutter). Named by machine
|
|
/// family, not table size: the same post drives other CI Fiber tables via
|
|
/// the configured table envelope. Emits the TF5200 skippable-macro program
|
|
/// structure of the machine sample (see <see cref="CIFiberProgramWriter"/>).
|
|
/// </summary>
|
|
public sealed class CIFiberPostProcessor
|
|
: IJsonConfigPostProcessor,
|
|
IMultiFilePostProcessor,
|
|
IPostVerificationSupport
|
|
{
|
|
private static readonly JsonSerializerOptions JsonOptions = new()
|
|
{
|
|
WriteIndented = true,
|
|
Converters = { new JsonStringEnumConverter() },
|
|
};
|
|
|
|
public string Name => "Cincinnati CI Fiber";
|
|
public bool PreservesPlacedProgramOrder => true;
|
|
public string Author => "OpenNest";
|
|
public string Description =>
|
|
"CI Fiber family laser (TF5200 / nLight CLX), e.g. CI Fiber 4020 8kW";
|
|
|
|
public CIFiberPostConfig Config { get; private set; }
|
|
|
|
object IConfigurablePostProcessor.Config => Config;
|
|
|
|
/// <summary>Loads this PC's saved settings, else the shipped defaults (see <see cref="PostSettingsFile"/>).</summary>
|
|
public CIFiberPostProcessor()
|
|
{
|
|
var json = PostSettingsFile.Read(typeof(CIFiberPostProcessor).Assembly);
|
|
Config = json == null
|
|
? new CIFiberPostConfig()
|
|
: JsonSerializer.Deserialize<CIFiberPostConfig>(json, JsonOptions) ?? new CIFiberPostConfig();
|
|
}
|
|
|
|
public CIFiberPostProcessor(CIFiberPostConfig config)
|
|
{
|
|
Config = config ?? throw new ArgumentNullException(nameof(config));
|
|
}
|
|
|
|
public void SaveConfig() => PostSettingsFile.Write(typeof(CIFiberPostProcessor).Assembly, SerializeConfig());
|
|
|
|
public string SerializeConfig() => JsonSerializer.Serialize(Config, JsonOptions);
|
|
|
|
public void LoadConfig(string json) =>
|
|
Config =
|
|
JsonSerializer.Deserialize<CIFiberPostConfig>(json, JsonOptions)
|
|
?? throw new JsonException("The post settings are empty.");
|
|
|
|
|
|
/// <summary>
|
|
/// Writes every sheet into one program. A stream holds a single program,
|
|
/// so this throws when "One program per sheet" is on and the nest has more
|
|
/// than one sheet; use <see cref="Post(Nest, string)"/> for that.
|
|
/// </summary>
|
|
public void Post(Nest nest, Stream outputStream)
|
|
{
|
|
if (nest == null)
|
|
throw new ArgumentNullException(nameof(nest));
|
|
if (outputStream == null)
|
|
throw new ArgumentNullException(nameof(outputStream));
|
|
|
|
if (SplitsSheets(nest))
|
|
throw new InvalidOperationException(
|
|
"\"One program per sheet\" writes several files; post this nest to a file path."
|
|
);
|
|
|
|
var program = Render(w => new CIFiberProgramWriter(Config).Write(nest, w));
|
|
outputStream.Write(program, 0, program.Length);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes the program(s) for the nest. With "One program per sheet" and more
|
|
/// than one sheet, <c>JOB.cnc</c> becomes <c>JOB-1.cnc</c>, <c>JOB-2.cnc</c>, ...;
|
|
/// otherwise the single program goes to <paramref name="outputFile"/>. All
|
|
/// programs are generated before any file is written, so a validation
|
|
/// failure leaves no partial output.
|
|
/// </summary>
|
|
public void Post(Nest nest, string outputFile)
|
|
{
|
|
if (nest == null)
|
|
throw new ArgumentNullException(nameof(nest));
|
|
if (string.IsNullOrEmpty(outputFile))
|
|
throw new ArgumentNullException(nameof(outputFile));
|
|
|
|
var files = GetOutputFiles(nest, outputFile);
|
|
var programs = new List<byte[]>();
|
|
|
|
if (files.Count == 1)
|
|
{
|
|
programs.Add(Render(w => new CIFiberProgramWriter(Config).Write(nest, w)));
|
|
}
|
|
else
|
|
{
|
|
var sheets = CIFiberProgramWriter.PostedSheets(nest);
|
|
var writer = new CIFiberProgramWriter(Config);
|
|
writer.Validate(sheets, singleProgram: false);
|
|
for (var i = 0; i < sheets.Count; i++)
|
|
{
|
|
var sheetNumber = i + 1;
|
|
var sheet = sheets[i];
|
|
programs.Add(Render(w => writer.WriteSheet(nest, sheet, sheetNumber, w)));
|
|
}
|
|
}
|
|
|
|
for (var i = 0; i < files.Count; i++)
|
|
File.WriteAllBytes(files[i], programs[i]);
|
|
}
|
|
|
|
public IReadOnlyList<string> GetOutputFiles(Nest nest, string outputFile)
|
|
{
|
|
if (nest == null)
|
|
throw new ArgumentNullException(nameof(nest));
|
|
if (string.IsNullOrEmpty(outputFile))
|
|
throw new ArgumentNullException(nameof(outputFile));
|
|
|
|
if (!SplitsSheets(nest))
|
|
return new[] { outputFile };
|
|
|
|
var dir = Path.GetDirectoryName(outputFile) ?? "";
|
|
var name = Path.GetFileNameWithoutExtension(outputFile);
|
|
var ext = Path.GetExtension(outputFile);
|
|
var count = CIFiberProgramWriter.PostedSheets(nest).Count;
|
|
|
|
return Enumerable
|
|
.Range(1, count)
|
|
.Select(n => Path.Combine(dir, $"{name}-{n}{ext}"))
|
|
.ToList();
|
|
}
|
|
|
|
private bool SplitsSheets(Nest nest) =>
|
|
Config.OneProgramPerSheet && CIFiberProgramWriter.PostedSheets(nest).Count > 1;
|
|
|
|
// CRLF file, ASCII (UTF-8 without BOM), matching the machine sample.
|
|
private static byte[] Render(Action<TextWriter> write)
|
|
{
|
|
using var ms = new MemoryStream();
|
|
using (var writer = new StreamWriter(ms, new UTF8Encoding(false), 1024, leaveOpen: true))
|
|
{
|
|
write(writer);
|
|
}
|
|
return ms.ToArray();
|
|
}
|
|
}
|
|
}
|