@chestnutlabs/gcode-dialects 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cnc.d.ts +10 -0
- package/dist/cnc.d.ts.map +1 -0
- package/dist/cnc.js +142 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +2 -2
package/dist/cnc.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DialectAdapter } from './contracts.js';
|
|
2
|
+
export type ValidationTier = 'validated' | 'experimental';
|
|
3
|
+
export type MachineClass = 'laser' | 'mill' | 'plotter';
|
|
4
|
+
/** GRBL diode/CO₂ laser (LightBurn and GRBL-laser post-processors). */
|
|
5
|
+
export declare function grblLaser(): DialectAdapter;
|
|
6
|
+
/** GRBL CNC milling (constant-power spindle). */
|
|
7
|
+
export declare function grblMill(): DialectAdapter;
|
|
8
|
+
/** LinuxCNC / EMC milling. */
|
|
9
|
+
export declare function linuxCnc(): DialectAdapter;
|
|
10
|
+
//# sourceMappingURL=cnc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cnc.d.ts","sourceRoot":"","sources":["../src/cnc.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAA+B,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAElF,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,cAAc,CAAC;AAC1D,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAoGxD,uEAAuE;AACvE,wBAAgB,SAAS,IAAI,cAAc,CAyB1C;AAED,iDAAiD;AACjD,wBAAgB,QAAQ,IAAI,cAAc,CAoBzC;AAED,8BAA8B;AAC9B,wBAAgB,QAAQ,IAAI,cAAc,CAsBzC"}
|
package/dist/cnc.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/** Non-extrusion capability claims whose confidence the validation tier governs (DD-012 D6). */
|
|
2
|
+
const NON_EXTRUSION_CAPS = ['cutMoves', 'toolPower', 'cannedCycles'];
|
|
3
|
+
function scoreEvidence(head) {
|
|
4
|
+
// Strip line comments (';…' and LinuxCNC/RS274 '(…)') so words inside them never score as markers.
|
|
5
|
+
const code = head
|
|
6
|
+
.split('\n')
|
|
7
|
+
.map((l) => l.split(';')[0].replace(/\([^)]*\)/g, ''))
|
|
8
|
+
.join('\n');
|
|
9
|
+
const has = (re) => re.test(code);
|
|
10
|
+
// G-code words run together without spaces (`G1M3`, `g1z-.1`), so a LEADING word-boundary fails.
|
|
11
|
+
// Match the command by its number + a trailing NON-digit lookahead: `M0*3(?!\d)` matches `M3`/`M03`
|
|
12
|
+
// (incl. mid-line `s3400 m3` and concatenated `G1M3`) but not `M30`/`M300`.
|
|
13
|
+
return {
|
|
14
|
+
// Extrusion = an `E` value on a G0–G3 MOTION line — NOT bare `E` (LinuxCNC laser uses `M67 E0 Q…`
|
|
15
|
+
// for analog power, which must not be mistaken for FDM extrusion).
|
|
16
|
+
extrusion: has(/G0*[0-3](?!\d)[^\n;]*E-?[.\d]/i) || has(/M8[23](?!\d)/i),
|
|
17
|
+
m3: has(/M0*3(?!\d)/i),
|
|
18
|
+
m4: has(/M0*4(?!\d)/i),
|
|
19
|
+
power: has(/S\d/i),
|
|
20
|
+
plunge: has(/Z\s*-\s*[.\d]/i), // negative Z incl. leading-dot decimals (`Z-.1`)
|
|
21
|
+
lightBurn: /LightBurn/i.test(head), // header-only marker; keep the raw head (comments allowed)
|
|
22
|
+
laserMode: /\$32\s*=\s*1/.test(code),
|
|
23
|
+
linuxCnc: /\b(?:LinuxCNC|EMC2?)\b/i.test(head),
|
|
24
|
+
grblBanner: /\bGrbl\s*[0-9]/i.test(head),
|
|
25
|
+
oWord: has(/[oO][\s<]*\w*\s*(?:sub|call|if|while|do|repeat|return|endsub)\b/im)
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function makeCncDialect(spec) {
|
|
29
|
+
return {
|
|
30
|
+
id: spec.id,
|
|
31
|
+
displayName: spec.displayName,
|
|
32
|
+
kind: 'firmware',
|
|
33
|
+
detect(input) {
|
|
34
|
+
const hit = spec.detect(input);
|
|
35
|
+
return hit === null
|
|
36
|
+
? null
|
|
37
|
+
: { dialectId: spec.id, kind: 'firmware', confidence: hit.confidence, evidence: hit.evidence };
|
|
38
|
+
},
|
|
39
|
+
finalize(ir, sink) {
|
|
40
|
+
sink.setRaw('cnc.controller', spec.id);
|
|
41
|
+
sink.setRaw('cnc.machineClass', spec.machineClass);
|
|
42
|
+
sink.setRaw('cnc.validationTier', spec.tier);
|
|
43
|
+
sink.setRaw('cnc.toolPowerLabel', spec.toolPowerLabel);
|
|
44
|
+
// Validation tier (DD-012 D6): until hardware-validated, non-extrusion claims are `inferred`,
|
|
45
|
+
// never `known`. Only downgrade claims the file actually made (present as 'known') — never
|
|
46
|
+
// fabricate a claim for a feature the file did not use.
|
|
47
|
+
if (spec.tier === 'experimental') {
|
|
48
|
+
let downgraded = false;
|
|
49
|
+
for (const cap of NON_EXTRUSION_CAPS) {
|
|
50
|
+
if (ir.header.capabilities[cap] === 'known') {
|
|
51
|
+
sink.upgradeCapability(cap, 'inferred');
|
|
52
|
+
downgraded = true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (downgraded) {
|
|
56
|
+
sink.warn('cnc-dialect-experimental', `${spec.displayName} support is EXPERIMENTAL (spec-derived, not yet hardware-validated); ` +
|
|
57
|
+
`non-extrusion classification is reported as 'inferred'.`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** GRBL diode/CO₂ laser (LightBurn and GRBL-laser post-processors). */
|
|
64
|
+
export function grblLaser() {
|
|
65
|
+
return makeCncDialect({
|
|
66
|
+
id: 'grbl-laser',
|
|
67
|
+
displayName: 'GRBL laser (LightBurn)',
|
|
68
|
+
machineClass: 'laser',
|
|
69
|
+
toolPowerLabel: 'laser power (S)',
|
|
70
|
+
// Hardware-validated 2026-07-29 (DD-012 D8; see docs/design/DD-012-hardware-validation-log.md):
|
|
71
|
+
// a real GRBL/LightBurn laser run — 6161 moves incl. fill + offset-fill, full 0–1000 S power ramp —
|
|
72
|
+
// confirmed machine-class detection, Cut-vs-rapid classification, and the toolPower channel against
|
|
73
|
+
// the physical cut. Claims report `known`, not `inferred`. (Mill/LinuxCNC remain experimental.)
|
|
74
|
+
tier: 'validated',
|
|
75
|
+
detect(input) {
|
|
76
|
+
const e = scoreEvidence(input.headText);
|
|
77
|
+
if (e.extrusion || e.linuxCnc)
|
|
78
|
+
return null;
|
|
79
|
+
if (e.lightBurn)
|
|
80
|
+
return { evidence: 'LightBurn header', confidence: 'known' };
|
|
81
|
+
if (e.laserMode)
|
|
82
|
+
return { evidence: 'GRBL $32=1 (laser mode)', confidence: 'inferred' };
|
|
83
|
+
// No-header GRBL laser (the common LaserGRBL case): a tool-on command with power, and NO Z
|
|
84
|
+
// plunging — lasers are planar; a mill would cut down into Z. Works whether the file uses M3
|
|
85
|
+
// (constant power) or M4 (dynamic).
|
|
86
|
+
if ((e.m3 || e.m4) && e.power && !e.plunge) {
|
|
87
|
+
return { evidence: `${e.m4 ? 'M4 dynamic' : 'M3'} + S power, planar (no Z plunge)`, confidence: 'inferred' };
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/** GRBL CNC milling (constant-power spindle). */
|
|
94
|
+
export function grblMill() {
|
|
95
|
+
return makeCncDialect({
|
|
96
|
+
id: 'grbl-mill',
|
|
97
|
+
displayName: 'GRBL mill',
|
|
98
|
+
machineClass: 'mill',
|
|
99
|
+
toolPowerLabel: 'spindle RPM (S)',
|
|
100
|
+
tier: 'experimental', // → 'validated' after a real GRBL-mill run (DD-012 §8)
|
|
101
|
+
detect(input) {
|
|
102
|
+
const e = scoreEvidence(input.headText);
|
|
103
|
+
if (e.extrusion || e.lightBurn || e.laserMode || e.linuxCnc)
|
|
104
|
+
return null;
|
|
105
|
+
// A spindle (M3) that plunges into the material (negative Z) with no extrusion — a milling
|
|
106
|
+
// fingerprint that distinguishes it from a planar laser. GRBL/family-level, so a banner is a
|
|
107
|
+
// stronger signal but not required (most CAM output has no banner).
|
|
108
|
+
if (e.m3 && e.plunge) {
|
|
109
|
+
const ev = e.grblBanner ? 'Grbl banner + M3 + Z-plunge' : 'M3 spindle + Z-plunge, no extrusion';
|
|
110
|
+
return { evidence: ev, confidence: 'inferred' };
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/** LinuxCNC / EMC milling. */
|
|
117
|
+
export function linuxCnc() {
|
|
118
|
+
return makeCncDialect({
|
|
119
|
+
id: 'linuxcnc',
|
|
120
|
+
displayName: 'LinuxCNC mill',
|
|
121
|
+
machineClass: 'mill',
|
|
122
|
+
toolPowerLabel: 'spindle RPM (S)',
|
|
123
|
+
tier: 'experimental', // → 'validated' after a real LinuxCNC run (DD-012 §8)
|
|
124
|
+
detect(input) {
|
|
125
|
+
const e = scoreEvidence(input.headText);
|
|
126
|
+
if (e.extrusion)
|
|
127
|
+
return null;
|
|
128
|
+
if (e.linuxCnc)
|
|
129
|
+
return { evidence: 'LinuxCNC/EMC header', confidence: 'known' };
|
|
130
|
+
// RS274NGC O-word subroutines / flow control (`O100 sub`, `o<name> call`) are the LinuxCNC
|
|
131
|
+
// family's distinguishing dialect feature — GRBL/TinyG do not have them.
|
|
132
|
+
if (e.oWord)
|
|
133
|
+
return { evidence: 'RS274NGC O-word subroutine/flow', confidence: 'inferred' };
|
|
134
|
+
// A `%` program envelope with a spindle and no extrusion (also common in Fanuc-style output;
|
|
135
|
+
// reported family-level, not as a specific controller).
|
|
136
|
+
if (/^\s*%/m.test(input.headText) && e.m3) {
|
|
137
|
+
return { evidence: '%-program envelope + M3 spindle, no extrusion', confidence: 'inferred' };
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export { prusaSlicer } from './prusaslicer.js';
|
|
|
15
15
|
export { orcaBambu } from './orca-bambu.js';
|
|
16
16
|
export { cura } from './cura.js';
|
|
17
17
|
export { klipper, marlin, repRap } from './firmware.js';
|
|
18
|
+
export { grblLaser, grblMill, linuxCnc } from './cnc.js';
|
|
19
|
+
export type { ValidationTier, MachineClass } from './cnc.js';
|
|
18
20
|
export { segAtOrAfterByte, applyMarkerRanges, parseKeyValue, parseAreaPoints, bedFromPoints, decodeBase64, ThumbnailCollector } from './annotate.js';
|
|
19
21
|
export type { RangeMarker } from './annotate.js';
|
|
20
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,UAAU,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACzG,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,YAAY,EACZ,kBAAkB,EACnB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,UAAU,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACzG,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,YAAY,EACZ,kBAAkB,EACnB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,4 +4,5 @@ export { prusaSlicer } from './prusaslicer.js';
|
|
|
4
4
|
export { orcaBambu } from './orca-bambu.js';
|
|
5
5
|
export { cura } from './cura.js';
|
|
6
6
|
export { klipper, marlin, repRap } from './firmware.js';
|
|
7
|
+
export { grblLaser, grblMill, linuxCnc } from './cnc.js';
|
|
7
8
|
export { segAtOrAfterByte, applyMarkerRanges, parseKeyValue, parseAreaPoints, bedFromPoints, decodeBase64, ThumbnailCollector } from './annotate.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chestnutlabs/gcode-dialects",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Slicer/firmware dialect adapters for ToolpathIR annotation (DD-005). Adapters annotate metadata and optional channels — they can never alter geometry.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gcode",
|
|
@@ -45,6 +45,6 @@
|
|
|
45
45
|
"test": "vitest run"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@chestnutlabs/toolpath-core": "0.
|
|
48
|
+
"@chestnutlabs/toolpath-core": "0.5.0"
|
|
49
49
|
}
|
|
50
50
|
}
|