feat(data): ship a Generic Laser default machine in place of CL-980

New installs now start with a generic fiber laser: mild steel, 304
stainless and 5052 aluminum, gauge-named thicknesses, fiber-sized
kerfs and arc lead-ins, and no lead-out. It has its own id, so
uploading local machines never replaces an existing CL-980. A test
checks that the shipped values grow sensibly with thickness.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ajandClaude Opus 5.5 committed 2026-10-29 18:51:42 -04:00
1 parent 16d4f706b2
commit 7ec11993de
6 files changed
+91 -178

No files matched your search

-174
View File
@@ -1,174 +0,0 @@
{
"id": "00000000-0000-0000-0000-000000980001",
"schemaVersion": 1,
"name": "CL-980",
"type": "laser",
"units": "inches",
"materials": [
{
"name": "Mild Steel",
"grade": "A36",
"density": 0.2836,
"thicknesses": [
{
"value": 0.060,
"kerf": 0.008,
"assistGas": "O2",
"leadIn": { "type": "Arc", "length": 0.125, "angle": 90.0, "radius": 0.0625 },
"leadOut": { "type": "Line", "length": 0.0625, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.125, "minSegmentLength": 0.5, "direction": "AwayFromOrigin" },
"plateSizes": [ "48x120", "60x120" ]
},
{
"value": 0.075,
"kerf": 0.008,
"assistGas": "O2",
"leadIn": { "type": "Arc", "length": 0.125, "angle": 90.0, "radius": 0.0625 },
"leadOut": { "type": "Line", "length": 0.0625, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.125, "minSegmentLength": 0.5, "direction": "AwayFromOrigin" },
"plateSizes": [ "48x120", "60x120" ]
},
{
"value": 0.105,
"kerf": 0.010,
"assistGas": "O2",
"leadIn": { "type": "Arc", "length": 0.1875, "angle": 90.0, "radius": 0.09375 },
"leadOut": { "type": "Line", "length": 0.09375, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.1875, "minSegmentLength": 0.75, "direction": "AwayFromOrigin" },
"plateSizes": [ "48x120", "60x120" ]
},
{
"value": 0.135,
"kerf": 0.010,
"assistGas": "O2",
"leadIn": { "type": "Arc", "length": 0.1875, "angle": 90.0, "radius": 0.09375 },
"leadOut": { "type": "Line", "length": 0.09375, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.1875, "minSegmentLength": 0.75, "direction": "AwayFromOrigin" },
"plateSizes": [ "48x120", "60x120", "60x144" ]
},
{
"value": 0.1875,
"kerf": 0.012,
"assistGas": "O2",
"leadIn": { "type": "Arc", "length": 0.25, "angle": 90.0, "radius": 0.125 },
"leadOut": { "type": "Line", "length": 0.125, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.25, "minSegmentLength": 1.0, "direction": "AwayFromOrigin" },
"plateSizes": [ "48x120", "60x120", "60x144" ]
},
{
"value": 0.250,
"kerf": 0.012,
"assistGas": "O2",
"leadIn": { "type": "Arc", "length": 0.25, "angle": 90.0, "radius": 0.125 },
"leadOut": { "type": "Line", "length": 0.125, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.25, "minSegmentLength": 1.0, "direction": "AwayFromOrigin" },
"plateSizes": [ "48x120", "60x120", "60x144" ]
},
{
"value": 0.375,
"kerf": 0.016,
"assistGas": "O2",
"leadIn": { "type": "Arc", "length": 0.375, "angle": 90.0, "radius": 0.1875 },
"leadOut": { "type": "Line", "length": 0.1875, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.3125, "minSegmentLength": 1.25, "direction": "AwayFromOrigin" },
"plateSizes": [ "60x120", "60x144", "72x120" ]
},
{
"value": 0.500,
"kerf": 0.020,
"assistGas": "O2",
"leadIn": { "type": "Arc", "length": 0.5, "angle": 90.0, "radius": 0.25 },
"leadOut": { "type": "Line", "length": 0.25, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.375, "minSegmentLength": 1.5, "direction": "AwayFromOrigin" },
"plateSizes": [ "60x120", "60x144", "72x120" ]
}
]
},
{
"name": "Stainless Steel",
"grade": "304",
"density": 0.289,
"thicknesses": [
{
"value": 0.060,
"kerf": 0.008,
"assistGas": "N2",
"leadIn": { "type": "Arc", "length": 0.125, "angle": 90.0, "radius": 0.0625 },
"leadOut": { "type": "Line", "length": 0.0625, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.125, "minSegmentLength": 0.5, "direction": "AwayFromOrigin" },
"plateSizes": [ "48x96", "48x120", "60x120" ]
},
{
"value": 0.075,
"kerf": 0.008,
"assistGas": "N2",
"leadIn": { "type": "Arc", "length": 0.125, "angle": 90.0, "radius": 0.0625 },
"leadOut": { "type": "Line", "length": 0.0625, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.125, "minSegmentLength": 0.5, "direction": "AwayFromOrigin" },
"plateSizes": [ "48x96", "48x120", "60x120" ]
},
{
"value": 0.105,
"kerf": 0.010,
"assistGas": "N2",
"leadIn": { "type": "Arc", "length": 0.1875, "angle": 90.0, "radius": 0.09375 },
"leadOut": { "type": "Line", "length": 0.09375, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.1875, "minSegmentLength": 0.75, "direction": "AwayFromOrigin" },
"plateSizes": [ "48x96", "48x120", "60x120" ]
},
{
"value": 0.250,
"kerf": 0.014,
"assistGas": "N2",
"leadIn": { "type": "Arc", "length": 0.25, "angle": 90.0, "radius": 0.125 },
"leadOut": { "type": "Line", "length": 0.125, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.25, "minSegmentLength": 1.0, "direction": "AwayFromOrigin" },
"plateSizes": [ "48x96", "48x120", "60x120" ]
}
]
},
{
"name": "Aluminum",
"grade": "5052",
"density": 0.097,
"thicknesses": [
{
"value": 0.060,
"kerf": 0.008,
"assistGas": "N2",
"leadIn": { "type": "Arc", "length": 0.125, "angle": 90.0, "radius": 0.0625 },
"leadOut": { "type": "Line", "length": 0.0625, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.125, "minSegmentLength": 0.5, "direction": "AwayFromOrigin" },
"plateSizes": [ "48x96", "48x120", "60x120" ]
},
{
"value": 0.080,
"kerf": 0.008,
"assistGas": "N2",
"leadIn": { "type": "Arc", "length": 0.125, "angle": 90.0, "radius": 0.0625 },
"leadOut": { "type": "Line", "length": 0.0625, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.125, "minSegmentLength": 0.5, "direction": "AwayFromOrigin" },
"plateSizes": [ "48x96", "48x120", "60x120" ]
},
{
"value": 0.125,
"kerf": 0.010,
"assistGas": "N2",
"leadIn": { "type": "Arc", "length": 0.1875, "angle": 90.0, "radius": 0.09375 },
"leadOut": { "type": "Line", "length": 0.09375, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.1875, "minSegmentLength": 0.75, "direction": "AwayFromOrigin" },
"plateSizes": [ "48x96", "48x120", "60x120" ]
},
{
"value": 0.250,
"kerf": 0.014,
"assistGas": "N2",
"leadIn": { "type": "Arc", "length": 0.25, "angle": 90.0, "radius": 0.125 },
"leadOut": { "type": "Line", "length": 0.125, "angle": 90.0, "radius": 0.0 },
"cutOff": { "partClearance": 0.02, "overtravel": 0.25, "minSegmentLength": 1.0, "direction": "AwayFromOrigin" },
"plateSizes": [ "48x96", "48x120", "60x120" ]
}
]
}
]
}
+52
View File
@@ -0,0 +1,52 @@
{
"id": "76974005-5de9-4de6-8ef0-4e4fa1ac6e98",
"schemaVersion": 1,
"name": "Generic Laser",
"type": "laser",
"units": "inches",
"materials": [
{
"name": "Mild Steel",
"grade": "A36",
"density": 0.2836,
"thicknesses": [
{"value": 0.06, "name": "16 ga", "kerf": 0.006, "assistGas": "O2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.0625}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.125, "minSegmentLength": 0.5, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120"]},
{"value": 0.075, "name": "14 ga", "kerf": 0.006, "assistGas": "O2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.0625}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.125, "minSegmentLength": 0.5, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120"]},
{"value": 0.105, "name": "12 ga", "kerf": 0.008, "assistGas": "O2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.09375}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.1875, "minSegmentLength": 0.75, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120"]},
{"value": 0.12, "name": "11 ga", "kerf": 0.008, "assistGas": "O2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.09375}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.1875, "minSegmentLength": 0.75, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120"]},
{"value": 0.135, "name": "10 ga", "kerf": 0.008, "assistGas": "O2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.09375}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.1875, "minSegmentLength": 0.75, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120"]},
{"value": 0.1875, "name": "3/16", "kerf": 0.01, "assistGas": "O2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.125}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.25, "minSegmentLength": 1.0, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120", "60x144"]},
{"value": 0.25, "name": "1/4", "kerf": 0.01, "assistGas": "O2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.125}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.25, "minSegmentLength": 1.0, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120", "60x144"]},
{"value": 0.375, "name": "3/8", "kerf": 0.012, "assistGas": "O2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.1875}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.3125, "minSegmentLength": 1.25, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120", "60x144"]},
{"value": 0.5, "name": "1/2", "kerf": 0.014, "assistGas": "O2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.25}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.375, "minSegmentLength": 1.5, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120", "60x144"]}
]
},
{
"name": "Stainless Steel",
"grade": "304",
"density": 0.289,
"thicknesses": [
{"value": 0.06, "name": "16 ga", "kerf": 0.006, "assistGas": "N2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.0625}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.125, "minSegmentLength": 0.5, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120"]},
{"value": 0.075, "name": "14 ga", "kerf": 0.006, "assistGas": "N2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.0625}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.125, "minSegmentLength": 0.5, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120"]},
{"value": 0.105, "name": "12 ga", "kerf": 0.008, "assistGas": "N2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.09375}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.1875, "minSegmentLength": 0.75, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120"]},
{"value": 0.12, "name": "11 ga", "kerf": 0.008, "assistGas": "N2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.09375}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.1875, "minSegmentLength": 0.75, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120"]},
{"value": 0.135, "name": "10 ga", "kerf": 0.008, "assistGas": "N2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.09375}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.1875, "minSegmentLength": 0.75, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120"]},
{"value": 0.1875, "name": "3/16", "kerf": 0.01, "assistGas": "N2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.125}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.25, "minSegmentLength": 1.0, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120", "60x144"]},
{"value": 0.25, "name": "1/4", "kerf": 0.01, "assistGas": "N2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.125}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.25, "minSegmentLength": 1.0, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120", "60x144"]}
]
},
{
"name": "Aluminum",
"grade": "5052",
"density": 0.097,
"thicknesses": [
{"value": 0.063, "name": "0.063", "kerf": 0.006, "assistGas": "N2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.0625}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.125, "minSegmentLength": 0.5, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120"]},
{"value": 0.08, "name": "0.080", "kerf": 0.006, "assistGas": "N2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.0625}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.125, "minSegmentLength": 0.5, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120"]},
{"value": 0.09, "name": "0.090", "kerf": 0.008, "assistGas": "N2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.09375}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.1875, "minSegmentLength": 0.75, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120"]},
{"value": 0.125, "name": "1/8", "kerf": 0.008, "assistGas": "N2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.09375}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.1875, "minSegmentLength": 0.75, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120"]},
{"value": 0.1875, "name": "3/16", "kerf": 0.01, "assistGas": "N2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.125}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.25, "minSegmentLength": 1.0, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120", "60x144"]},
{"value": 0.25, "name": "1/4", "kerf": 0.01, "assistGas": "N2", "leadIn": {"type": "Arc", "length": 0.0, "angle": 90.0, "radius": 0.125}, "leadOut": {"type": "None", "length": 0.0, "angle": 90.0, "radius": 0.0}, "cutOff": {"partClearance": 0.02, "overtravel": 0.25, "minSegmentLength": 1.0, "direction": "AwayFromOrigin"}, "plateSizes": ["48x96", "48x120", "60x120", "60x144"]}
]
}
]
}
+1 -1
View File
@@ -79,7 +79,7 @@ public class LocalJsonProvider : IDataProvider
var assembly = typeof(LocalJsonProvider).Assembly;
var resourceName = assembly
.GetManifestResourceNames()
.FirstOrDefault(n => n.EndsWith("CL-980.json"));
.FirstOrDefault(n => n.EndsWith("Generic-Laser.json"));
if (resourceName is null)
return;
+1 -1
View File
@@ -10,6 +10,6 @@
<ProjectReference Include="..\OpenNest.Core\OpenNest.Core.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Defaults\CL-980.json" />
<EmbeddedResource Include="Defaults\Generic-Laser.json" />
</ItemGroup>
</Project>
+36 -1
View File
@@ -24,7 +24,7 @@ public class DefaultConfigTests : IDisposable
provider.EnsureDefaults();
var machines = provider.GetMachines();
Assert.Single(machines);
Assert.Equal("CL-980", machines[0].Name);
Assert.Equal("Generic Laser", machines[0].Name);
}
[Fact]
@@ -72,4 +72,39 @@ public class DefaultConfigTests : IDisposable
}
}
}
[Fact]
public void DefaultConfig_HasSensibleValues()
{
var provider = new LocalJsonProvider(_testDir);
provider.EnsureDefaults();
var config = provider.GetMachine(provider.GetMachines()[0].Id);
foreach (var material in config.Materials)
{
Assert.True(material.Density > 0);
var thicknesses = material.Thicknesses.OrderBy(t => t.Value).ToList();
for (var i = 0; i < thicknesses.Count; i++)
{
var thickness = thicknesses[i];
var label = $"{material.Name} {thickness.Value}";
Assert.False(string.IsNullOrWhiteSpace(thickness.Name), label);
Assert.True(thickness.Kerf < thickness.Value / 4, label);
Assert.True(thickness.CutOff.PartClearance > thickness.Kerf, label);
Assert.True(thickness.CutOff.Overtravel > 0, label);
Assert.True(thickness.CutOff.MinSegmentLength > 0, label);
Assert.Equal("Arc", thickness.LeadIn.Type);
Assert.True(thickness.LeadIn.Radius >= thickness.Kerf * 4, label);
Assert.NotEmpty(thickness.PlateSizes);
Assert.All(thickness.PlateSizes, s => Assert.True(OpenNest.Shapes.PlateSizes.TryGet(s, out _), s));
if (i == 0)
continue;
var thinner = thicknesses[i - 1];
Assert.True(thickness.Value > thinner.Value, label);
Assert.True(thickness.Kerf >= thinner.Kerf, label);
Assert.True(thickness.LeadIn.Radius >= thinner.LeadIn.Radius, label);
}
}
}
}
+1 -1
View File
@@ -1130,7 +1130,7 @@ Listens on `ASPNETCORE_URLS` (default Kestrel ports) unless overridden.
Docker must be built from the repository root: the server references
`OpenNest.Data`, which references `OpenNest.Core`. The Dockerfile copies all three
project files before restore and their source/resources before publish, including
Data's embedded `Defaults/CL-980.json`. The allowlist `.dockerignore` excludes
Data's embedded `Defaults/Generic-Laser.json`. The allowlist `.dockerignore` excludes
other projects, host `bin`/`obj`, local databases, environment files, and launch
settings. The server image uses the .NET 8 SDK to build and the .NET 8 ASP.NET
runtime to run; IO/Engine are used only by the host-side smoke tool, not the image.