nc-gcode-interpreter 0.2.9__tar.gz → 0.3.0__tar.gz
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.
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/.github/workflows/build-and-release.yml +6 -6
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/CHANGELOG.md +282 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/Cargo.lock +37 -1
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/Cargo.toml +7 -1
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/Development.md +1 -1
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/PKG-INFO +2 -2
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/README.md +22 -5
- nc_gcode_interpreter-0.3.0/benches/extensions.rs +160 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/docs/resumable-interpretation-design.md +1 -1
- nc_gcode_interpreter-0.3.0/examples/axis_index_assignment.csv +5 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/axis_index_assignment.mpf +1 -1
- nc_gcode_interpreter-0.3.0/examples/defaults.def +6 -0
- nc_gcode_interpreter-0.3.0/examples/defaults_start.json +1 -0
- nc_gcode_interpreter-0.3.0/examples/function_calls.csv +3 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/function_calls.mpf +1 -1
- nc_gcode_interpreter-0.3.0/examples/multiple_m_codes.csv +9 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/multiple_m_codes.mpf +1 -1
- nc_gcode_interpreter-0.3.0/examples/r_param.csv +3 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/r_param.mpf +1 -1
- nc_gcode_interpreter-0.3.0/examples/simple.csv +6 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/simple.mpf +1 -1
- nc_gcode_interpreter-0.3.0/examples/trans_ic.csv +14 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/trans_ic.mpf +1 -1
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/ggroups/generate_g_commands.py +14 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/pyproject.toml +1 -1
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/nc_gcode_interpreter/__init__.py +184 -33
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/nc_gcode_interpreter/_internal.pyi +19 -3
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/nc_gcode_interpreter/cli.py +23 -4
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_batch_variables.py +7 -4
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_batches.py +25 -8
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_diagnostics.py +3 -3
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_expected_output.py +14 -14
- nc_gcode_interpreter-0.3.0/python/tests/test_feed_check.py +180 -0
- nc_gcode_interpreter-0.3.0/python/tests/test_feed_group.py +132 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_flags.py +2 -2
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_flatten.py +8 -3
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_frames.py +41 -41
- nc_gcode_interpreter-0.3.0/python/tests/test_g_group_enum.py +138 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_g_vocabulary.py +3 -3
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_real_comparisons.py +1 -1
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_stage1.py +29 -11
- nc_gcode_interpreter-0.3.0/python/tests/test_start_inputs.py +292 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_streaming.py +11 -8
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_string_operations.py +3 -3
- nc_gcode_interpreter-0.3.0/python/tests/test_subprograms.py +628 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_unsupported.py +2 -2
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/src/errors.rs +45 -7
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/src/flatten.rs +70 -36
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/src/grammar.pest +59 -8
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/src/interpret_rules.rs +1246 -34
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/src/interpreter.rs +246 -37
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/src/lib.rs +558 -46
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/src/line_driver.rs +117 -23
- nc_gcode_interpreter-0.3.0/src/main.rs +318 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/src/modal_groups.rs +214 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/src/output.rs +1047 -60
- nc_gcode_interpreter-0.3.0/src/start.rs +1010 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/src/state.rs +117 -2
- nc_gcode_interpreter-0.3.0/src/subprogram.rs +655 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/src/vm.rs +2 -1
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/tests/cli.rs +48 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/uv.lock +59 -47
- nc_gcode_interpreter-0.2.9/examples/axis_index_assignment.csv +0 -5
- nc_gcode_interpreter-0.2.9/examples/function_calls.csv +0 -3
- nc_gcode_interpreter-0.2.9/examples/multiple_m_codes.csv +0 -9
- nc_gcode_interpreter-0.2.9/examples/r_param.csv +0 -3
- nc_gcode_interpreter-0.2.9/examples/simple.csv +0 -6
- nc_gcode_interpreter-0.2.9/examples/trans_ic.csv +0 -14
- nc_gcode_interpreter-0.2.9/src/main.rs +0 -168
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/.github/workflows/benchmark.yml +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/.gitignore +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/CONTRIBUTING.md +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/LICENSE +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/TODO.md +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/benches/fixtures.rs +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/benches/workloads.rs +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/build.rs +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/build_backend/nc_build_backend.py +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/docs/sinumerik-execution-model.md +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/actual_position.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/actual_position.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/arc.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/arc.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/arrays.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/arrays.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/basic_math.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/basic_math.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/case.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/case.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/case_insensitive_variables.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/case_insensitive_variables.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/custom_vars.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/custom_vars.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/def_string.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/def_string.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/defaults.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/defaults.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/edge_cases.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/edge_cases.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/flattening/flatten_demo.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/flattening/flatten_demo_flattened.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/flattening/flatten_demo_raw.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/for_loop.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/for_loop.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/goto.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/goto.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/if_statement.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/if_statement.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/increment.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/increment.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/logic_operators.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/logic_operators.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/loop.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/loop.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/precedence.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/precedence.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/repeat.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/repeat.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/rot.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/rot.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/spline.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/spline.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/tool.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/tool.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/trans.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/trans.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/trans2.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/trans2.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/trans_reset.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/trans_reset.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/trig.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/trig.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/variables.csv +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/examples/variables.mpf +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/example/minimal.py +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/example/streaming.py +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/nc_gcode_interpreter/ggroups.json +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/nc_gcode_interpreter/py.typed +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/nc_gcode_interpreter/viz.py +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/nc_source.py +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_arithmetic_functions.py +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_compare_benchmarks.py +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_field_defects.py +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_g_groups.py +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_jumps.py +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/python/tests/test_structured_errors.py +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/rustfmt.toml +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/scripts/benchmark.py +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/scripts/compare_benchmarks.py +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/src/frame.rs +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/src/line_driver/experiments.rs +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/src/structure_scan.rs +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/src/types.rs +0 -0
- {nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/tests/performance_contract.rs +0 -0
{nc_gcode_interpreter-0.2.9 → nc_gcode_interpreter-0.3.0}/.github/workflows/build-and-release.yml
RENAMED
|
@@ -80,7 +80,7 @@ jobs:
|
|
|
80
80
|
uses: PyO3/maturin-action@v1
|
|
81
81
|
with:
|
|
82
82
|
target: ${{ matrix.platform.target }}
|
|
83
|
-
args: --release --out dist
|
|
83
|
+
args: --release --out dist -i python3.12 -i python3.13 -i python3.14 -i python3.14t -i python3.15 -i python3.15t
|
|
84
84
|
sccache: 'true'
|
|
85
85
|
manylinux: auto
|
|
86
86
|
- name: Upload wheels
|
|
@@ -156,8 +156,8 @@ jobs:
|
|
|
156
156
|
uses: PyO3/maturin-action@v1
|
|
157
157
|
with:
|
|
158
158
|
target: ${{ matrix.platform.target }}
|
|
159
|
-
#
|
|
160
|
-
#
|
|
159
|
+
# Every wheel job names its interpreters (the set 0.2.8 shipped per platform):
|
|
160
|
+
# --find-interpreter picks up whatever the image has, e.g. PyPy, which fails.
|
|
161
161
|
args: --release --out dist -i python3.12 -i python3.13 -i python3.14 -i python3.14t
|
|
162
162
|
sccache: 'true'
|
|
163
163
|
manylinux: auto
|
|
@@ -216,7 +216,7 @@ jobs:
|
|
|
216
216
|
uses: PyO3/maturin-action@v1
|
|
217
217
|
with:
|
|
218
218
|
target: ${{ matrix.platform.target }}
|
|
219
|
-
args: --release --out dist
|
|
219
|
+
args: --release --out dist -i python3.12 -i python3.13 -i python3.14
|
|
220
220
|
sccache: 'true'
|
|
221
221
|
- name: Upload wheels
|
|
222
222
|
uses: actions/upload-artifact@v4
|
|
@@ -256,7 +256,7 @@ jobs:
|
|
|
256
256
|
uses: PyO3/maturin-action@v1
|
|
257
257
|
with:
|
|
258
258
|
target: ${{ matrix.platform.target }}
|
|
259
|
-
args: --release --out dist
|
|
259
|
+
args: --release --out dist -i python3.12 -i python3.13 -i python3.14 -i python3.15
|
|
260
260
|
sccache: 'true'
|
|
261
261
|
- name: Upload wheels
|
|
262
262
|
uses: actions/upload-artifact@v4
|
|
@@ -305,7 +305,7 @@ jobs:
|
|
|
305
305
|
contents: write
|
|
306
306
|
attestations: write
|
|
307
307
|
steps:
|
|
308
|
-
# Do NOT use `merge-multiple: true`. With
|
|
308
|
+
# Do NOT use `merge-multiple: true`. With a shared interpreter list, every
|
|
309
309
|
# build matrix leg (py3.12 AND py3.13) produces cp312/cp313/cp314 wheels
|
|
310
310
|
# with identical filenames but non-identical bytes (sccache makes builds
|
|
311
311
|
# nondeterministic). Merging them into one directory makes
|
|
@@ -4,6 +4,288 @@ Notable changes to **nc-gcode-interpreter**. The format loosely follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/); versions are git tags,
|
|
5
5
|
released to PyPI.
|
|
6
6
|
|
|
7
|
+
## [v0.3.0] - 2026-11-11
|
|
8
|
+
|
|
9
|
+
This release changes the API and the output. To upgrade:
|
|
10
|
+
|
|
11
|
+
- Replace `initial_state` with `definitions` (definition files),
|
|
12
|
+
`variables` (values) and `start` (machine state); see the first entry
|
|
13
|
+
under Changed.
|
|
14
|
+
- Program a feed before the first path motion, or pass it in `start` (key
|
|
15
|
+
`"feed"`): a missing feed is now the `feed_not_programmed` error, as on
|
|
16
|
+
the control (alarm 10860).
|
|
17
|
+
- The G-group columns are `pl.Enum`, and polars 1.32 or newer is required.
|
|
18
|
+
`cast(pl.String)` restores the old dtype.
|
|
19
|
+
- New loud errors for constructs that used to be misread: two calls in one
|
|
20
|
+
block, `G4` with axis words, `FGROUP`/`FGREF` on non-axes, reading
|
|
21
|
+
`FGREF`, and others listed below.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- Subprogram execution (#48). `nc_to_dataframe` and `nc_to_batches` take
|
|
26
|
+
`subprograms`, a mapping of subprogram name to source text. A call whose
|
|
27
|
+
name is in the mapping runs that text; any other call stays a
|
|
28
|
+
`non_returning_function_call` cell as before. Supported: a `PROC` header
|
|
29
|
+
with by-value parameters (numbers and strings, with defaults), `VAR`
|
|
30
|
+
parameters and `VAR` array parameters, local `DEF` variables (invisible to
|
|
31
|
+
callees, gone on return), return on `M17` or `RET`, and nested calls up to
|
|
32
|
+
16 levels. Rows produced inside a call keep the main-program line of the
|
|
33
|
+
call in `line_no` and carry two new columns: `subprogram`, the call path
|
|
34
|
+
(`OUTER/INNER`), and `subprogram_line`, the line inside that subprogram's
|
|
35
|
+
text. The interpreter does not read files; the caller supplies the texts.
|
|
36
|
+
- `m_function_subprograms`, a mapping of M function number to subprogram
|
|
37
|
+
name: the subprogram runs at the end of every block that programs that M
|
|
38
|
+
function, as a control does for a tool-change cycle on `M6`. It does not
|
|
39
|
+
apply inside the replacement subprogram itself.
|
|
40
|
+
- External variables. `external_variables` takes a provider object with
|
|
41
|
+
`names`, `read(name, indices)` and `write(name, indices, value)`. Every
|
|
42
|
+
read or write of a listed name (`$A_IN`, `$A_DBB`, `$AA_IM`, or any other
|
|
43
|
+
variable name) goes to the provider and is not stored by the interpreter,
|
|
44
|
+
so a program can branch and wait on machine state the interpreter does not
|
|
45
|
+
model. Indices arrive as ints, or as the axis name where an axis is the
|
|
46
|
+
index. With `nc_to_batches` the rows produced so far are yielded before the
|
|
47
|
+
provider is asked, and the provider is called on the consuming thread, so
|
|
48
|
+
a simulator can play those rows first. An exception raised by the provider
|
|
49
|
+
ends the run and propagates unchanged.
|
|
50
|
+
- Rust API: `interpreter::nc_to_table_with` and
|
|
51
|
+
`interpreter::nc_to_batch_stream_with` take a `subprogram::Extensions`
|
|
52
|
+
value with the same extensions; the provider is the
|
|
53
|
+
`subprogram::ExternalVariables` trait.
|
|
54
|
+
- `required_subprograms`: a call of a listed name that is not among
|
|
55
|
+
`subprograms` is an error that names the call path, where it would
|
|
56
|
+
otherwise stay an opaque cell.
|
|
57
|
+
- `stop_on_setal`: a `SETAL` call ends the run with an error that carries the
|
|
58
|
+
call as written and the call path.
|
|
59
|
+
- String comparison with `==` and `<>` in conditions and expressions
|
|
60
|
+
(`IF (KIND == "MILL")`). Other operators on strings, and a string compared
|
|
61
|
+
with a number, are errors. A served external variable compared with a
|
|
62
|
+
string is read from the provider as text (`read` returns a `str`).
|
|
63
|
+
- A frame value assigned to a served external variable
|
|
64
|
+
(`$P_UIFR[7] = CTRANS(X, 10) : CROT(Z, 90)`) reaches the provider's `write`
|
|
65
|
+
as `[("CTRANS", ("X", 10.0)), ("CROT", ("Z", 90.0))]`. Assigned to any
|
|
66
|
+
other variable it is an error.
|
|
67
|
+
- Machine axis names (`AX1`) and frame components (`TR`, `FI`, `RT`, `SC`,
|
|
68
|
+
`MI`) are passed to the provider as index names.
|
|
69
|
+
- `transformation` column: the last `TRAORI`, `TRANSMIT`, `TRACYL`, `TRAANG`,
|
|
70
|
+
`TRACON` or `TRAFOOF` call as written, forward-filled like a modal G group.
|
|
71
|
+
The call also stays in `non_returning_function_call`.
|
|
72
|
+
- Definition files parse: the `DEF` attributes for range, preprocessing stop,
|
|
73
|
+
access rights, unit and limits (`DEF CHAN APW 4 REAL LLI 0 ULI 10 NAME`),
|
|
74
|
+
the `CHAR` type, and attribute-only `REDEF` lines are accepted and not
|
|
75
|
+
interpreted.
|
|
76
|
+
- Array assignment as a statement: `NAME[n] = SET(v, ...)` and
|
|
77
|
+
`NAME[n] = REP(v[, count])` write consecutive elements starting at the
|
|
78
|
+
indexed one, last index fastest. Before, only the `DEF` initializer form
|
|
79
|
+
was interpreted and the statement form was an internal error.
|
|
80
|
+
- A subprogram whose `PROC` header has the `SAVE` attribute gives the caller
|
|
81
|
+
back its modal G functions on return: each G group the subprogram changed
|
|
82
|
+
gets the caller's value on the row after the body's rows. A group the
|
|
83
|
+
program never set before the call cannot be restored and gives a warning.
|
|
84
|
+
- Loud errors for constructs the subprogram mechanism does not model: a
|
|
85
|
+
`FOR` counter, a `PROC` parameter or a local `DEF` with the name of an
|
|
86
|
+
external variable, a string assigned to an external variable, a `PROC` parameter with the name of an axis or
|
|
87
|
+
address, `SET`/`REP` statements that write past the declared array, and
|
|
88
|
+
two `subprograms` entries whose names differ only in case.
|
|
89
|
+
- Program repetitions: `L6 P2` and `L6 P=2` call `L6` twice (manual
|
|
90
|
+
4.2.3.3). A supplied subprogram runs that many times, with its arguments
|
|
91
|
+
passed once; without a supplied body the call keeps its name and the
|
|
92
|
+
count goes to a new `call_repetitions` column. A count outside 1 to 9999
|
|
93
|
+
is an error. Before, `L6 P2` was recorded as a call named `P2` and
|
|
94
|
+
`L6 P=2` set a user variable `P` (part of #105).
|
|
95
|
+
- `PCALL NAME(...)` calls `NAME` (manual 4.2.3.8). A path before the name
|
|
96
|
+
(`PCALL/_N_WKS_DIR/_N_X_WPD/NAME`) is not interpreted and gives a
|
|
97
|
+
warning; the call goes to `NAME`. Before, `PCALL` was dropped silently.
|
|
98
|
+
|
|
99
|
+
- `FGROUP(<axes>)` and `FGREF[<axis>]=<radius>` are modal state (manual
|
|
100
|
+
3.7.1). A forward-filled, dictionary-encoded `FGROUP` column holds the axes
|
|
101
|
+
the path feed refers to, comma-separated as programmed; `FGROUP()` writes
|
|
102
|
+
`""` for the machine-data default, which the interpreter does not know.
|
|
103
|
+
Each programmed axis gets a forward-filled `FGREF[<axis>]` float column;
|
|
104
|
+
null means the control default. Both columns appear only when the program
|
|
105
|
+
uses them, after the axis columns. Before, `FGROUP` was stored as a
|
|
106
|
+
subprogram call in `non_returning_function_call` and `FGREF[<axis>]` needed
|
|
107
|
+
an `axis_index_map` entry and became the variable `FGREF[<index>]`; it now
|
|
108
|
+
resolves by axis name and is no longer a variable. An argument that is not
|
|
109
|
+
a configured channel axis (including N, F, S, D and T) is an
|
|
110
|
+
`unexpected_axis` error. Reading `FGREF[<axis>]` is an
|
|
111
|
+
`unsupported_statement` error, and so is a user array named `FGREF` (#107).
|
|
112
|
+
|
|
113
|
+
### Changed
|
|
114
|
+
|
|
115
|
+
- **Breaking:** `initial_state` is removed from `nc_to_dataframe`,
|
|
116
|
+
`nc_to_batches`, `nc_to_rows`, the Rust interpreter functions and the CLIs
|
|
117
|
+
(`-i/--initial_state`, `--initial-state`). It ran a string of NC text before
|
|
118
|
+
the program and discarded its rows, which mixed three jobs. Three inputs
|
|
119
|
+
replace it, applied in this order before the main program:
|
|
120
|
+
- `definitions`: a list of definition files (GUD/MGUD/UGUD `.DEF`), each
|
|
121
|
+
read on its own. A file may hold DEF and REDEF statements, assignments to
|
|
122
|
+
variables and comments; the `%_N_..._DEF` header and `$PATH=` line are
|
|
123
|
+
skipped. M17, M30 or M2 ends that file and reading goes on with the next,
|
|
124
|
+
so several files no longer need their end codes stripped. `CHANDATA(1)`
|
|
125
|
+
is accepted and ignored; another channel number is an error. `TRAFOOF`
|
|
126
|
+
on a block of its own sets the start transformation to off, in the first
|
|
127
|
+
row and the returned state, unless `start["transformation"]` gives
|
|
128
|
+
another, so a restore program (CHANDATA(1), TRAFOOF, SET array
|
|
129
|
+
assignments, M30) reads unchanged. The program stops M0 and M1 are
|
|
130
|
+
ignored. A subprogram call stays an error naming the call. Assigning
|
|
131
|
+
to a name the files have not declared is an error, unless it is an R
|
|
132
|
+
parameter or `allow_undefined_variables` is set. Motion, G codes or
|
|
133
|
+
anything else is an error naming the file (`NcError.definition_file`,
|
|
134
|
+
its index in the list) and the line within it. Migration: pass the DEF
|
|
135
|
+
files as a list instead of concatenating them into `initial_state`.
|
|
136
|
+
- `variables`: a dict of values (`float`, `int`, `bool`, `str`) written
|
|
137
|
+
straight into the variable tables and checked against the declared type
|
|
138
|
+
(2.5 into an INT, or a string into a REAL or an R parameter, is an
|
|
139
|
+
error). Names must be
|
|
140
|
+
defined unless they are R parameters, system variables or
|
|
141
|
+
`allow_undefined_variables` is set. Migration: replace generated
|
|
142
|
+
`NAME=VALUE` lines with `variables={"NAME": value}`; strings now work.
|
|
143
|
+
- `start`: the machine state at program start, in the shape of the
|
|
144
|
+
returned state dict (`axes`, `translation`, `rotation`, `symbol_table`,
|
|
145
|
+
`string_table`, plus the new `g_groups`, `feed`, `tool`,
|
|
146
|
+
`transformation`, `fgroup` and `fgref`), so a returned state can be
|
|
147
|
+
passed back. `fgroup` is the FGROUP column value and `fgref` maps a
|
|
148
|
+
channel axis to its FGREF reference radius. Modal G groups
|
|
149
|
+
are checked against the G group table, and they, `feed`, `tool` (the `T`
|
|
150
|
+
column), `transformation`, `fgroup` and `fgref` fill the first output row, so those columns
|
|
151
|
+
no longer start as null. Axis positions and the frame only seed the internal state.
|
|
152
|
+
A name in both `variables` and `start` is an error. Migration: replace
|
|
153
|
+
axis words and frame instructions in `initial_state` with
|
|
154
|
+
`start={"axes": {...}, "translation": {...}}`, and modal G codes and F
|
|
155
|
+
with `start={"g_groups": {...}, "feed": ...}`.
|
|
156
|
+
The returned state gains `g_groups` (modal G group column to code, as of
|
|
157
|
+
the last row), `feed`, `tool`, `transformation`, `fgroup` and `fgref`. The CLIs take `--definitions FILE` (repeatable),
|
|
158
|
+
`--variables FILE.json` and `--start FILE.json`. The Rust functions take
|
|
159
|
+
`Option<&start::StartInputs>` where they took `Option<&str>`.
|
|
160
|
+
|
|
161
|
+
- **Breaking:** a block that traverses a path (G1, G2, G3, CIP, splines and
|
|
162
|
+
the other path motions) while no feed is programmed is an error
|
|
163
|
+
(`NcError.kind == "feed_not_programmed"`), as alarm 10860 is on the
|
|
164
|
+
control. G0, the G4 dwell, G74/G75 and the thread commands need no F;
|
|
165
|
+
G63 tapping does. A change of feed unit deletes F and FZ (Axes and
|
|
166
|
+
spindles manual 7.2.1, 7.2.2): inverse time (G93), traversing time
|
|
167
|
+
(G931), per minute (G94, G961, G971, G942) and per revolution (G95, G96,
|
|
168
|
+
G97, G952, G973); G962 and G972 keep the unit that was active. A change
|
|
169
|
+
within one unit (G95 to G96) keeps F. When the previous feed type is
|
|
170
|
+
unknown (never programmed, not in `start["g_groups"]`) nothing is
|
|
171
|
+
deleted. A feed seeded from `start` is start state, not an F of the first
|
|
172
|
+
block, so a feed type change there deletes it too. `FZ=` (feed per tooth)
|
|
173
|
+
counts as a feed under a revolutional feed type and `FB=` (non-modal feed,
|
|
174
|
+
7.4.10) for its own block; both are now output columns (`FZ`
|
|
175
|
+
forward-filled, `FB` not), where they used to become variables. G4 with
|
|
176
|
+
axis words in the same block is an error (G4 stands alone, manual
|
|
177
|
+
3.14.7). G93 recommends but does not
|
|
178
|
+
require F in every block, so that is not checked. The control's default
|
|
179
|
+
feed SD42110 `$SC_DEFAULT_FEED` (0 unless set) is evaluated at program
|
|
180
|
+
start: pass it as `start["feed"]`. The check is off when F is not a
|
|
181
|
+
configured axis. FGROUP only selects the axes F refers to, so a block
|
|
182
|
+
moving only synchronized or rotary axes needs F the same way, and FGROUP
|
|
183
|
+
and FGREF neither move nor delete F. Tree walker, VM and line fast path
|
|
184
|
+
raise the same error.
|
|
185
|
+
Migration: program F before the first path motion or pass
|
|
186
|
+
`start={"feed": ...}`. The examples `simple`, `trans_ic`, `r_param`,
|
|
187
|
+
`axis_index_assignment`, `multiple_m_codes` and `function_calls` gained
|
|
188
|
+
`F1000` on their first path motion, and their expected CSVs gained the
|
|
189
|
+
`F` column (no other column changed).
|
|
190
|
+
|
|
191
|
+
- **Breaking:** every G-group column (`gg01_motion`, `gg02_wait`, ...) in the
|
|
192
|
+
output of `nc_to_batches`, `nc_to_dataframe` and `sanitize_dataframe` is
|
|
193
|
+
now a `pl.Enum` instead of `String`. Its categories are that group's
|
|
194
|
+
commands in manual order, the same in every batch and every run, so batches
|
|
195
|
+
and runs concatenate without a cast and a value outside the group cannot
|
|
196
|
+
occur. Rust hands the codes over as an Arrow dictionary that polars imports
|
|
197
|
+
as `Enum` directly, with no cast. The new `G_GROUP_DTYPES` maps each G-group column name to its
|
|
198
|
+
`pl.Enum`, for matching schemas and empty frames. `T`, `comment`,
|
|
199
|
+
`non_returning_function_call` and other free-text columns stay `String`,
|
|
200
|
+
and `nc_to_rows` still yields `str`. On a 1M-row program the G-group cells
|
|
201
|
+
take 1 byte each instead of a 16-byte string view, and filtering or
|
|
202
|
+
grouping on them is about 1.4 to 2 times faster, at unchanged
|
|
203
|
+
interpretation time. Consequences
|
|
204
|
+
for code that reads these columns:
|
|
205
|
+
- `.str` methods raise `InvalidOperationError`, and joining against a
|
|
206
|
+
`String` column raises `SchemaError`.
|
|
207
|
+
- `fill_null("")` silently leaves the nulls in place, because `""` is not a
|
|
208
|
+
category.
|
|
209
|
+
- `is_in` with a value outside the group raises; `==` with such a literal
|
|
210
|
+
returns no matches.
|
|
211
|
+
- Sorting follows manual order (`G0`, `G1`, `G2`, ...), not alphabetical
|
|
212
|
+
order.
|
|
213
|
+
- A strict `pl.concat` with frames from an older version, or
|
|
214
|
+
`assert_frame_equal` against `String` frames, fails on the dtype.
|
|
215
|
+
- Migration: `df.with_columns(pl.col(c).cast(pl.String) for c in
|
|
216
|
+
G_GROUP_DTYPES if c in df.columns)` restores the old dtype.
|
|
217
|
+
|
|
218
|
+
- **Breaking:** the minimum polars version is now 1.32 (was 1.9). The Enum
|
|
219
|
+
handoff relies on the Arrow field metadata polars reads from 1.32 on.
|
|
220
|
+
|
|
221
|
+
- A program that calls `TRAORI`, `TRANSMIT`, `TRACYL`, `TRAANG`, `TRACON` or
|
|
222
|
+
`TRAFOOF` gets the new `transformation` column, also when it uses none of
|
|
223
|
+
the new arguments.
|
|
224
|
+
- Two subprogram calls in one block (`FOO BAR`) are an error. Before, the
|
|
225
|
+
second silently replaced the first. Keyword G commands such as `SOFT`
|
|
226
|
+
next to a call are unaffected. A repeat count before the name (`P3 FOO`,
|
|
227
|
+
`P=3 FOO`) is an error as well (part of #105).
|
|
228
|
+
- `PROC`, `EXTERN` and `RET` are reserved words and no longer read as
|
|
229
|
+
subprogram calls. A `PROC` header and an `EXTERN` declaration produce no
|
|
230
|
+
output; `RET` ends the program at the top level like `M17`. Before,
|
|
231
|
+
`PROC FOO SAVE` was recorded as a call named `SAVE` and `RET` as a call
|
|
232
|
+
while interpretation continued (part of #105). A main program whose `PROC`
|
|
233
|
+
header declares parameters is an error.
|
|
234
|
+
- A program without control structures keeps the line fast path when an
|
|
235
|
+
extension is used. Only its lines with a call, an M function mapped to a
|
|
236
|
+
subprogram, or a word that names a served variable go through the
|
|
237
|
+
grammar, which dispatches them. Before, any extension sent the whole
|
|
238
|
+
program through the grammar path, about seven times slower per line.
|
|
239
|
+
- `DEF REAL A[n]` declares the elements `A[0]` to `A[n-1]`. Before, it also
|
|
240
|
+
created `A[n]`, so an out-of-range access went unnoticed.
|
|
241
|
+
- `REDEF` and `PCALL` are reserved words.
|
|
242
|
+
- A supplied subprogram is checked and parsed on its first call, and later
|
|
243
|
+
calls in the same run reuse the parse tree. Before, every call parsed the
|
|
244
|
+
text again, which was about two thirds of the time of a call of a 20-line
|
|
245
|
+
body. Adds the `self_cell` dependency, which holds the tree next to the
|
|
246
|
+
text it borrows from.
|
|
247
|
+
- `nc_to_dataframe` with `external_variables` no longer sends the rows
|
|
248
|
+
produced so far as a separate batch before each provider call, since its
|
|
249
|
+
caller reads no row before the run ends. A program that reads the provider
|
|
250
|
+
on every motion row built one batch per row. `nc_to_batches` and the Rust
|
|
251
|
+
API keep handing the rows over first; Rust callers can opt out with
|
|
252
|
+
`Extensions::defer_rows`.
|
|
253
|
+
|
|
254
|
+
## [v0.2.10] - 2026-11-10
|
|
255
|
+
|
|
256
|
+
### Changed
|
|
257
|
+
|
|
258
|
+
- `nc_to_batches` and `nc_to_dataframe` do less work per output row. The
|
|
259
|
+
G-group columns are held as per-batch dictionary codes and written straight
|
|
260
|
+
to the Arrow string array, instead of one `String` per row per column that
|
|
261
|
+
the consumer thread then freed; the batch builder finds a cell's column
|
|
262
|
+
without hashing its name; the streaming driver reuses its decode buffers.
|
|
263
|
+
Python output is unchanged (same columns, `String` dtype, values and
|
|
264
|
+
`variable_events`). With `flatten_tolerance`, line numbers and variables on,
|
|
265
|
+
a 7.2M-row, 442 MiB linear program takes 3.3 s instead of 8.3 s and a
|
|
266
|
+
22M-row, 1.1 GB program 10.5 s instead of 24 s, at the same peak memory
|
|
267
|
+
(#89).
|
|
268
|
+
|
|
269
|
+
- `flatten_tolerance` costs almost nothing on rows that are not curves: the
|
|
270
|
+
flattener tracked positions with an allocation and a lookup per configured
|
|
271
|
+
axis for every row, about 2 s on the 7.2M-row program above, now about
|
|
272
|
+
0.15 s. B-spline flattening reuses one working buffer per curve; a
|
|
273
|
+
5.4M-row, 380 MB spline program takes 8 s instead of 16 s (#89).
|
|
274
|
+
|
|
275
|
+
- Rust API: `Column` has a new `Dict(DictColumn)` variant, used for the
|
|
276
|
+
G-group columns (`values` plus one `u32` code per row,
|
|
277
|
+
`DictColumn::NULL` for a null cell). Code that matches `Column`
|
|
278
|
+
exhaustively needs one more arm; `DictColumn::iter` yields the cells as
|
|
279
|
+
`Option<&str>` (#89).
|
|
280
|
+
|
|
281
|
+
### Fixed
|
|
282
|
+
|
|
283
|
+
- A numeric tool selection (`T2`, `T=3`, `T=R1`) now reaches the output table.
|
|
284
|
+
It is written to the same string `T` column as `T="name"`, as the number's
|
|
285
|
+
text (`"2"`), on the row of the block that programs it. Before, only named
|
|
286
|
+
tools appeared and a numeric one was dropped without a warning. The value is
|
|
287
|
+
still stored as the variable `T` in the final state, as before (#101).
|
|
288
|
+
|
|
7
289
|
## [v0.2.9] - 2026-11-01
|
|
8
290
|
|
|
9
291
|
### Changed
|
|
@@ -506,7 +506,7 @@ checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
|
|
506
506
|
|
|
507
507
|
[[package]]
|
|
508
508
|
name = "nc-gcode-interpreter"
|
|
509
|
-
version = "0.
|
|
509
|
+
version = "0.3.0"
|
|
510
510
|
dependencies = [
|
|
511
511
|
"arrow-array",
|
|
512
512
|
"arrow-data",
|
|
@@ -518,6 +518,8 @@ dependencies = [
|
|
|
518
518
|
"pyo3",
|
|
519
519
|
"rayon",
|
|
520
520
|
"rustc-hash",
|
|
521
|
+
"self_cell",
|
|
522
|
+
"serde_json",
|
|
521
523
|
"thiserror",
|
|
522
524
|
]
|
|
523
525
|
|
|
@@ -745,6 +747,21 @@ version = "1.0.23"
|
|
|
745
747
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
746
748
|
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
|
747
749
|
|
|
750
|
+
[[package]]
|
|
751
|
+
name = "self_cell"
|
|
752
|
+
version = "1.3.0"
|
|
753
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
754
|
+
checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813"
|
|
755
|
+
|
|
756
|
+
[[package]]
|
|
757
|
+
name = "serde"
|
|
758
|
+
version = "1.0.228"
|
|
759
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
760
|
+
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
|
761
|
+
dependencies = [
|
|
762
|
+
"serde_core",
|
|
763
|
+
]
|
|
764
|
+
|
|
748
765
|
[[package]]
|
|
749
766
|
name = "serde_core"
|
|
750
767
|
version = "1.0.228"
|
|
@@ -765,6 +782,19 @@ dependencies = [
|
|
|
765
782
|
"syn",
|
|
766
783
|
]
|
|
767
784
|
|
|
785
|
+
[[package]]
|
|
786
|
+
name = "serde_json"
|
|
787
|
+
version = "1.0.154"
|
|
788
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
789
|
+
checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
|
|
790
|
+
dependencies = [
|
|
791
|
+
"itoa",
|
|
792
|
+
"memchr",
|
|
793
|
+
"serde",
|
|
794
|
+
"serde_core",
|
|
795
|
+
"zmij",
|
|
796
|
+
]
|
|
797
|
+
|
|
768
798
|
[[package]]
|
|
769
799
|
name = "sha2"
|
|
770
800
|
version = "0.10.9"
|
|
@@ -1023,3 +1053,9 @@ dependencies = [
|
|
|
1023
1053
|
"quote",
|
|
1024
1054
|
"syn",
|
|
1025
1055
|
]
|
|
1056
|
+
|
|
1057
|
+
[[package]]
|
|
1058
|
+
name = "zmij"
|
|
1059
|
+
version = "1.0.23"
|
|
1060
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1061
|
+
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "nc-gcode-interpreter"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
|
|
@@ -20,6 +20,7 @@ thiserror = "2.0.18"
|
|
|
20
20
|
clap = { version = "4.6", features = ["derive"] }
|
|
21
21
|
rayon = "1.10"
|
|
22
22
|
rustc-hash = "2.1"
|
|
23
|
+
serde_json = "1"
|
|
23
24
|
|
|
24
25
|
# NOTE: keep every dependency version inline (`crate = { version = "..." }`),
|
|
25
26
|
# NOT as a `[dependencies.crate]` block with a standalone `version = "..."`
|
|
@@ -44,6 +45,7 @@ pyo3 = { version = "0.29", optional = true }
|
|
|
44
45
|
arrow-array = { version = "59", optional = true, default-features = false, features = ["ffi"] }
|
|
45
46
|
arrow-schema = { version = "59", optional = true, default-features = false }
|
|
46
47
|
arrow-data = { version = "59", optional = true, default-features = false }
|
|
48
|
+
self_cell = "1"
|
|
47
49
|
|
|
48
50
|
[features]
|
|
49
51
|
default = []
|
|
@@ -54,3 +56,7 @@ python = ["pyo3", "arrow-array", "arrow-schema", "arrow-data"]
|
|
|
54
56
|
name = "workloads"
|
|
55
57
|
harness = false
|
|
56
58
|
required-features = ["bench-experiments"]
|
|
59
|
+
|
|
60
|
+
[[bench]]
|
|
61
|
+
name = "extensions"
|
|
62
|
+
harness = false
|
|
@@ -78,7 +78,7 @@ evaluated for its extra build time across the wheel matrix.
|
|
|
78
78
|
There are a bunch of csv files in the examples directory. To test the tool on all of them (use git to check changes)
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
|
-
rm **/*.csv && cargo build --release && find examples -name "*.mpf" -type f -print0 | xargs -0 -I {} sh -c './target/release/nc-gcode-interpreter --axis-index-map E:4 --
|
|
81
|
+
rm **/*.csv && cargo build --release && find examples -name "*.mpf" -type f -print0 | xargs -0 -I {} sh -c './target/release/nc-gcode-interpreter --axis-index-map E:4 --definitions examples/defaults.def --start examples/defaults_start.json "$1" || echo "Failed to process $1" >&2' sh {}
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
## python test
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nc-gcode-interpreter
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -9,7 +9,7 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
10
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
11
11
|
Requires-Dist: maturin>=1.7.4
|
|
12
|
-
Requires-Dist: polars>=1.
|
|
12
|
+
Requires-Dist: polars>=1.32.0
|
|
13
13
|
Requires-Dist: jupyter>=1.1.1 ; extra == 'dev'
|
|
14
14
|
Requires-Dist: pytest>=8.3.3 ; extra == 'test'
|
|
15
15
|
Requires-Dist: mypy ; extra == 'test'
|
|
@@ -20,6 +20,7 @@ The **NC-GCode-Interpreter** offers a streamlined and efficient solution for int
|
|
|
20
20
|
- **Array Operations**: Manages arrays and allows operations on them.
|
|
21
21
|
- **Incremental Changes**: Facilitates incremental changes in axes positions like `X=IC(2)`.
|
|
22
22
|
- **Spline Programming**: `ASPLINE`, `BSPLINE` and `CSPLINE` blocks with their start/end conditions (`BAUTO`/`BNAT`/`BTAN`, `EAUTO`/`ENAT`/`ETAN`) and the spline block addresses `PW` (point weight), `SD` (spline degree) and `PL` (parameter interval length). Block addresses appear as output columns; unlike axes they receive no `TRANS` offset and are not forward-filled, since e.g. a point weight only applies to the point it is programmed with.
|
|
23
|
+
- **Feed group**: `FGROUP(<axes>)` and `FGREF[<axis>]=<radius>` are modal state, not calls or variables. The `FGROUP` column holds the axes the path feed `F` refers to, comma-separated as programmed (`"RA1,RA2,RA3"`); `FGROUP()` gives `""`, the machine-data default, as does a null before the first `FGROUP`. Each `FGREF[<axis>]` column holds the reference radius of that axis; null means the control default (360 mm / 2π for a rotary axis). Both are forward-filled, appear only when the program uses them, and need no `axis_index_map` entry. The interpreter computes no timing from them.
|
|
23
24
|
|
|
24
25
|
### Additional Functionality
|
|
25
26
|
|
|
@@ -28,7 +29,8 @@ The **NC-GCode-Interpreter** offers a streamlined and efficient solution for int
|
|
|
28
29
|
- **Curve flattening**: pass `flatten_tolerance` (Python) or `--flatten-tolerance` (CLI) to convert curved motions — `G2`/`G3` arcs (`I`/`J`/`K` centre offsets or `CR=` radius form, `G17`/`G18`/`G19` planes, helical third axis with `TURN=` multi-turn support, full circles) and `ASPLINE`/`BSPLINE`/`CSPLINE` splines (including `PW` weights and `SD` degree) — into runs of plain `G1` rows in the same table format. `CIP`/`CT`/`POLY`/thread/involute blocks pass through unchanged with a warning; spline start/end conditions (`BAUTO`/`BNAT`/... ) are approximated by natural (CSPLINE) / Akima (ASPLINE) boundaries — see the `flatten` module docs for the full list of checked approximations. The single tolerance is the maximum deviation between the emitted polyline and the true curve, in path units: arcs use the exact sagitta bound, splines adaptive subdivision. The interpolation addresses (`I`/`J`/`K`/`CR`/`PW`/`SD`/`PL`) are consumed and do not appear in the output; sampled rows keep the source block's line number, and its other cells (`F`, `M`, comments, ...) ride on the first sample so modal behavior is preserved. Generated samples carry a `flattened = 1` marker column (never forward-filled); programmed positions stay null there, so filtering on null recovers the original toolpath points.
|
|
29
30
|
- **Toolpath visualization** (optional): with the `viz` extra (`pip install 'nc-gcode-interpreter[viz]'`), `nc_gcode_interpreter.viz.view_toolpath(df)` renders an interpreted toolpath in [threejs-viewer](https://pypi.org/project/threejs-viewer/) as an extruded bead tube with a draw-range animation replaying the program at feed-rate-proportional speed. Combined with `flatten_tolerance`, programmed points render orange and flattener-generated samples blue. The extra also installs the `nc-view` command: `nc-view part.mpf` interprets, flattens (default tolerance 0.1, `--no-flatten` to skip) and opens the animated toolpath in the browser; `--speed` sets the time-lapse factor (default 60 = one machine-minute per second).
|
|
30
31
|
- **Custom Axes**: Allows users to define additional axes beyond the standard `X`, `Y`, `Z`.
|
|
31
|
-
- **
|
|
32
|
+
- **Definitions, variables and start state**: `definitions` takes the definition files a control loads (GUD/MGUD/UGUD `.DEF`: DEF and REDEF statements, assignments, comments; M17/M30/M2 ends a file), `variables` takes variable values checked against their declared type, and `start` takes the machine state at program start in the shape of the returned `state` dict (axes, frame, variables, modal G groups, feed, tool, transformation, FGROUP and FGREF radii). The G groups, feed, tool, transformation, FGROUP and FGREF fill the first output row; seeded G groups have the same `pl.Enum` dtype as the rest of the column. They are applied in that order, before the main program. In a definition file `CHANDATA(1)` is ignored, another channel is an error, `TRAFOOF` sets the start transformation to off (a later `start["transformation"]` wins), M0 and M1 are ignored, a subprogram call is an error, and assigning to an undeclared name is an error unless it is an R parameter or `allow_undefined_variables` is set.
|
|
33
|
+
- **Feed check**: a path motion (G1, G2, G3, CIP, splines) with no feed programmed is an error, as alarm 10860 is on the control; a change of feed unit (inverse time, per minute, per revolution) deletes F; `FZ=` counts as a feed under a revolutional feed type and `FB=` for its own block. FGROUP does not change this: a block moving only axes outside the FGROUP, or only rotary axes, needs F too. Pass the control's default feed (SD42110 `$SC_DEFAULT_FEED`) as `start["feed"]`. The check is off when F is not a configured axis. On the CLI: `--definitions FILE` (repeatable), `--variables FILE.json` and `--start FILE.json`.
|
|
32
34
|
- **CLI Options**: Numerous command-line options to customize the processing, such as axis overriding, loop limits, and more.
|
|
33
35
|
|
|
34
36
|
## Example Usage
|
|
@@ -66,7 +68,9 @@ Arguments:
|
|
|
66
68
|
Options:
|
|
67
69
|
-a, --axes <AXIS> Override default axis identifiers (comma-separated, e.g., "X,Y,Z")
|
|
68
70
|
-e, --extra-axes <EXTRA_AXIS> Add extra axis identifiers (comma-separated, e.g., "RA1,RA2")
|
|
69
|
-
|
|
71
|
+
--definitions <FILE> Definition file (GUD/MGUD .DEF) to read before the program; repeat for several, read in order
|
|
72
|
+
--variables <FILE> JSON object of variable values applied after the definitions, e.g. {"COUNT": 3}
|
|
73
|
+
--start <FILE> JSON object with the machine state at program start (the shape of the returned state)
|
|
70
74
|
-l, --iteration_limit <LIMIT> Maximum number of iterations for loops [default: 10000]
|
|
71
75
|
-f, --disable-forward-fill Disable forward-filling of null values in axes columns
|
|
72
76
|
-h, --help Print help
|
|
@@ -113,7 +117,7 @@ shape: (14, 7)
|
|
|
113
117
|
┌───────┬───────┬──────┬────────┬───────────┬─────────────┬─────────────────────────────────┐
|
|
114
118
|
│ X ┆ Y ┆ Z ┆ F ┆ M ┆ gg01_motion ┆ comment │
|
|
115
119
|
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
|
|
116
|
-
│ f64 ┆ f64 ┆ f64 ┆ f64 ┆ list[str] ┆
|
|
120
|
+
│ f64 ┆ f64 ┆ f64 ┆ f64 ┆ list[str] ┆ enum ┆ str │
|
|
117
121
|
╞═══════╪═══════╪══════╪════════╪═══════════╪═════════════╪═════════════════════════════════╡
|
|
118
122
|
│ null ┆ null ┆ null ┆ null ┆ null ┆ null ┆ ;size of the square │
|
|
119
123
|
│ null ┆ null ┆ null ┆ null ┆ null ┆ null ┆ ;size of the square │
|
|
@@ -131,6 +135,17 @@ shape: (14, 7)
|
|
|
131
135
|
|
|
132
136
|
The Python bindings also return the state of the program after execution, which can be used for inspection.
|
|
133
137
|
|
|
138
|
+
#### Column dtypes
|
|
139
|
+
|
|
140
|
+
Axis and other value columns are `Float64`, `N` is `Int64` and `M` is `List(String)`. Each G-group column (`gg01_motion`, `gg06_plane_select`, ...) is a `pl.Enum` whose categories are that group's commands in manual order; `G_GROUP_DTYPES` maps every G-group column name to its dtype, for building matching schemas or empty frames. Every batch and every run has the same Enum per column, so frames concatenate without a cast. The Python package requires polars 1.32 or newer. `T`, `comment`, `non_returning_function_call` and other free-text columns stay `String`. `nc_to_rows` yields plain `str` values.
|
|
141
|
+
|
|
142
|
+
Things that differ from a `String` column:
|
|
143
|
+
|
|
144
|
+
- `pl.col("gg01_motion") == "G1"` and `is_in(["G0", "G1"])` work; `is_in` with a value outside the group raises.
|
|
145
|
+
- `.str` methods and joins against a `String` column raise; cast first with `.cast(pl.String)`.
|
|
146
|
+
- `fill_null("")` leaves the nulls in place, because `""` is not a category. Cast to `String` first.
|
|
147
|
+
- Sorting follows manual order (`G0`, `G1`, `G2`, ...), not alphabetical order.
|
|
148
|
+
|
|
134
149
|
#### Streaming
|
|
135
150
|
|
|
136
151
|
For long programs (or when you want results before the whole file is interpreted), `nc_to_rows` yields rows lazily while the interpreter runs on a background thread. Each row carries the 1-based source line it came from — loops and jumps repeat line numbers, which is exactly what a visualizer needs to map trace rows back to the program:
|
|
@@ -178,7 +193,7 @@ Measure a local program with
|
|
|
178
193
|
--threads 4 --samples 1 --batch-size 4096 --allow-undefined --axis-index-e 4
|
|
179
194
|
--axis-index-elx 5 --output tmp/memory.json`.
|
|
180
195
|
|
|
181
|
-
**Cancellation.** Dropping the iterator is the supported way to cancel a `nc_to_rows`/`nc_to_batches` run — leaving a `for` loop early or `del`-ing it drops it immediately; relying on garbage collection works too but only whenever the runtime happens to collect it, so it is not prompt on all interpreters: the background interpreter thread's next attempt to send a row hits a closed channel and returns internally via `ParsingError::StreamClosed`, so the thread unwinds promptly instead of running the rest of the program to completion. No explicit `.close()`/`.cancel()` call is needed or provided — drop is the whole contract, and downstreams (
|
|
196
|
+
**Cancellation.** Dropping the iterator is the supported way to cancel a `nc_to_rows`/`nc_to_batches` run — leaving a `for` loop early or `del`-ing it drops it immediately; relying on garbage collection works too but only whenever the runtime happens to collect it, so it is not prompt on all interpreters: the background interpreter thread's next attempt to send a row hits a closed channel and returns internally via `ParsingError::StreamClosed`, so the thread unwinds promptly instead of running the rest of the program to completion. No explicit `.close()`/`.cancel()` call is needed or provided — drop is the whole contract, and downstreams (for example a live-edit re-interpret in an editor) rely on it for latest-wins cancellation.
|
|
182
197
|
|
|
183
198
|
Additionally, conversion from a Polars DataFrame back to an MPF (NC) program is also supported:
|
|
184
199
|
|
|
@@ -202,7 +217,9 @@ Arguments:
|
|
|
202
217
|
Options:
|
|
203
218
|
-a, --axes <AXIS> Override default axis identifiers (comma-separated, e.g., "X,Y,Z")
|
|
204
219
|
-e, --extra-axes <EXTRA_AXIS> Add extra axis identifiers (comma-separated, e.g., "RA1,RA2")
|
|
205
|
-
|
|
220
|
+
--definitions <FILE> Definition file (GUD/MGUD .DEF) to read before the program; repeat for several, read in order
|
|
221
|
+
--variables <FILE> JSON object of variable values applied after the definitions, e.g. {"COUNT": 3}
|
|
222
|
+
--start <FILE> JSON object with the machine state at program start (the shape of the returned state)
|
|
206
223
|
-l, --iteration_limit <LIMIT> Maximum number of iterations for loops [default: 10000]
|
|
207
224
|
-f, --disable-forward-fill Disable forward-filling of null values in axes columns
|
|
208
225
|
-h, --help Print help
|