Files
OpenNest/Posts/OpenNest.Posts.CincinnatiCIFiber/CIFiberPostProcessor.cs
T

172 lines
6.4 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
: IConfigurablePostProcessor,
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; }
object IConfigurablePostProcessor.Config => Config;
public CIFiberPostProcessor()
{
var configPath = GetConfigPath();
if (File.Exists(configPath))
{
var json = File.ReadAllText(configPath);
Config =
JsonSerializer.Deserialize<CIFiberPostConfig>(json, JsonOptions)
?? new CIFiberPostConfig();
}
else
{
Config = new CIFiberPostConfig();
SaveConfig();
}
}
public CIFiberPostProcessor(CIFiberPostConfig config)
{
Config = config ?? throw new ArgumentNullException(nameof(config));
}
public void SaveConfig()
{
var configPath = GetConfigPath();
var json = JsonSerializer.Serialize(Config, JsonOptions);
File.WriteAllText(configPath, json);
}
private static string GetConfigPath()
{
var assemblyPath = typeof(CIFiberPostProcessor).Assembly.Location;
var dir = Path.GetDirectoryName(assemblyPath);
var name = Path.GetFileNameWithoutExtension(assemblyPath);
return Path.Combine(dir, name + ".json");
}
/// <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();
}
}
}