mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-08 05:35:45 -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>
123 lines
4.5 KiB
C#
123 lines
4.5 KiB
C#
using System.Text;
|
|
using OpenNest.Geometry;
|
|
using OpenNest.Posts.Cincinnati;
|
|
using OpenNest.Posts.CincinnatiCIFiber;
|
|
|
|
namespace OpenNest.Tests.Cincinnati;
|
|
|
|
/// <summary>
|
|
/// Both Cincinnati posts write the configured head-height lock and unlock blocks around a
|
|
/// cut-off's overcut past the far sheet edge, and refuse the nest when the blocks are not set.
|
|
/// </summary>
|
|
public class HeadLockedCutOffPostTests
|
|
{
|
|
private const string LockCode = "M210 (LOCK HEAD)";
|
|
private const string UnlockCode = "M211 (UNLOCK HEAD)";
|
|
|
|
[Fact]
|
|
public void ClPost_TowardTheFarEdge_LocksAroundTheOvercut()
|
|
{
|
|
var lines = ClLines(Nest(farEdgeFirst: false));
|
|
|
|
var locked = lines.IndexOf(LockCode);
|
|
Assert.True(locked > 0, string.Join("\n", lines));
|
|
AssertLine(lines, lines[locked - 1], "G1 X40 Y#110"); // The nominal far edge: the sheet width.
|
|
AssertLine(lines, lines[locked + 1], "G1 X40 Y61.5");
|
|
Assert.Equal(UnlockCode, lines[locked + 2]);
|
|
}
|
|
|
|
[Fact]
|
|
public void ClPost_FromTheFarEdge_KeepsTheHeadDownForTheRapidBack()
|
|
{
|
|
var lines = ClLines(Nest(farEdgeFirst: true));
|
|
|
|
var locked = lines.IndexOf(LockCode);
|
|
var unlocked = lines.IndexOf(UnlockCode);
|
|
Assert.True(locked > 0 && unlocked > locked, string.Join("\n", lines));
|
|
AssertLine(lines, lines[locked + 1], "G1 X40 Y61.5");
|
|
var between = lines.GetRange(locked, unlocked - locked);
|
|
// No head raise while the head is locked; the rapid back returns to the nominal edge.
|
|
Assert.DoesNotContain(between, line => line.Contains("M47"));
|
|
Assert.Contains(between, line => line.Contains("G0 X40 Y#110"));
|
|
AssertLine(lines, lines[unlocked + 1], "G1 X40 Y0");
|
|
}
|
|
|
|
[Fact]
|
|
public void ClPost_WithoutLockCodes_RefusesBeforeWriting()
|
|
{
|
|
using var stream = new MemoryStream();
|
|
var post = new CincinnatiPostProcessor(new CincinnatiPostConfig { RequireLibraries = false });
|
|
|
|
var refusal = Assert.Throws<InvalidOperationException>(() => post.Post(Nest(farEdgeFirst: false), stream));
|
|
|
|
Assert.Contains("head-height lock", refusal.Message);
|
|
Assert.Equal(0, stream.Length);
|
|
}
|
|
|
|
[Fact]
|
|
public void CiFiberPost_FromTheFarEdge_LocksAroundTheOvercutAndUnlocksBeforeCuttingBack()
|
|
{
|
|
var lines = CiFiberLines(Nest(farEdgeFirst: true));
|
|
|
|
var locked = lines.IndexOf(LockCode);
|
|
var unlocked = lines.IndexOf(UnlockCode);
|
|
Assert.True(locked > 0 && unlocked > locked, string.Join("\n", lines));
|
|
AssertLine(lines, lines[locked + 1], "G1X40Y61.5");
|
|
AssertLine(lines, lines[unlocked + 1], "G1X40Y0");
|
|
}
|
|
|
|
[Fact]
|
|
public void CiFiberPost_WithoutLockCodes_Refuses()
|
|
{
|
|
var post = new CIFiberPostProcessor(new CIFiberPostConfig());
|
|
|
|
Assert.Throws<InvalidOperationException>(() => post.Post(Nest(farEdgeFirst: false), new MemoryStream()));
|
|
}
|
|
|
|
private static Nest Nest(bool farEdgeFirst)
|
|
{
|
|
var nest = new Nest("HEADLOCK");
|
|
var plate = new Plate(60, 120);
|
|
nest.Plates.Add(plate);
|
|
plate.CutOffs.Add(new CutOff(new Vector(40, 0), CutOffAxis.Vertical)
|
|
{
|
|
HeadLockOvercut = 1.5,
|
|
FarEdgeFirst = farEdgeFirst,
|
|
});
|
|
plate.RegenerateCutOffs(new CutOffSettings());
|
|
return nest;
|
|
}
|
|
|
|
private static List<string> ClLines(Nest nest)
|
|
{
|
|
var post = new CincinnatiPostProcessor(new CincinnatiPostConfig
|
|
{
|
|
RequireLibraries = false,
|
|
HeadHeightLockCode = LockCode,
|
|
HeadHeightUnlockCode = UnlockCode,
|
|
});
|
|
using var stream = new MemoryStream();
|
|
post.Post(nest, stream);
|
|
return Lines(stream);
|
|
}
|
|
|
|
private static List<string> CiFiberLines(Nest nest)
|
|
{
|
|
var post = new CIFiberPostProcessor(new CIFiberPostConfig
|
|
{
|
|
HeadHeightLockCode = LockCode,
|
|
HeadHeightUnlockCode = UnlockCode,
|
|
});
|
|
using var stream = new MemoryStream();
|
|
post.Post(nest, stream);
|
|
return Lines(stream);
|
|
}
|
|
|
|
// A posted line holds the expected move (a kerf-compensation code may come first).
|
|
private static void AssertLine(List<string> lines, string line, string move) =>
|
|
Assert.True(line.Contains(move), $"Expected '{move}' in '{line}':\n" + string.Join("\n", lines));
|
|
|
|
private static List<string> Lines(MemoryStream stream) =>
|
|
Encoding.UTF8.GetString(stream.ToArray()).Replace("\r\n", "\n").Split('\n').Select(l => l.Trim()).ToList();
|
|
}
|