按建议继续完成后续工作

结论:native 兼容性验证已接入 interpreter init、rotary indexer、canonical 边界 fixture,并新增 upstream rs274 side-by-side 基线;完整 verify_native_probes.sh 已通过。
This commit is contained in:
2026-06-07 22:37:29 +08:00
parent 59cfc8406e
commit 7a20531ae4
20 changed files with 530 additions and 25 deletions

View File

@@ -24,10 +24,14 @@ The native validation script runs these checks in order:
vendored file exists, and every vendored file is byte-identical to upstream. vendored file exists, and every vendored file is byte-identical to upstream.
3. `tools/verify_no_standalone_cnc_semantics.sh` 3. `tools/verify_no_standalone_cnc_semantics.sh`
Confirms standalone code has not reintroduced `Interp::convert_g()`. Confirms standalone code has not reintroduced `Interp::convert_g()`.
4. `tools/build_native_probes.sh` 4. `tools/verify_native_linuxcnc_fixture_baseline.sh`
Runs a narrow side-by-side fixture baseline through upstream
`../linuxcnc/bin/rs274` and compares normalized canonical events for
fixtures that do not require standalone-only runtime adapters.
5. `tools/build_native_probes.sh`
Builds native source probes and standalone harnesses from vendored Builds native source probes and standalone harnesses from vendored
LinuxCNC source plus narrow runtime wrappers. LinuxCNC source plus narrow runtime wrappers.
5. `tests/native/verify_native_probes.sh` 6. `tests/native/verify_native_probes.sh`
Checks probe exit codes, source-probe coverage, harness stdout, canonical Checks probe exit codes, source-probe coverage, harness stdout, canonical
fixture events, and expected error behavior. fixture events, and expected error behavior.
@@ -51,6 +55,8 @@ The validation fails if:
| `linuxcnc_namedparam_harness` | Validates LinuxCNC named parameter behavior, `_ini[...]`, and `_hal[...]` adapter resolution. | | `linuxcnc_namedparam_harness` | Validates LinuxCNC named parameter behavior, `_ini[...]`, and `_hal[...]` adapter resolution. |
| `linuxcnc_interp_minimal_harness` | Runs G-code fixtures through vendored LinuxCNC parser/execution/conversion code and captures canonical events. | | `linuxcnc_interp_minimal_harness` | Runs G-code fixtures through vendored LinuxCNC parser/execution/conversion code and captures canonical events. |
| `linuxcnc_parameter_file_harness` | Validates LinuxCNC parameter file restore/save behavior and required/read-only parameter handling. | | `linuxcnc_parameter_file_harness` | Validates LinuxCNC parameter file restore/save behavior and required/read-only parameter handling. |
| `linuxcnc_interp_init_harness` | Validates vendored LinuxCNC `Interp::init()` emits canonical initialization boundaries through the standalone event sink. |
| `linuxcnc_indexer_harness` | Validates vendored LinuxCNC single-axis rotary indexer dispatch emits lock/unlock and motion boundaries through the standalone event sink. |
| `linuxcnc_tp_api_probe` | Validates vendored LinuxCNC trajectory planner calls for linear, arc, and queued motion paths. | | `linuxcnc_tp_api_probe` | Validates vendored LinuxCNC trajectory planner calls for linear, arc, and queued motion paths. |
| `linuxcnc_kinematics_probe` | Validates vendored LinuxCNC `trivkins.c` plus `kins_util.c` initialize and perform identity forward/inverse mapping through the standalone HAL/RTAPI boundary. | | `linuxcnc_kinematics_probe` | Validates vendored LinuxCNC `trivkins.c` plus `kins_util.c` initialize and perform identity forward/inverse mapping through the standalone HAL/RTAPI boundary. |
| `linuxcnc_5axis_kinematics_probe` | Validates vendored LinuxCNC `5axiskins.c` through `switchkins.c`, including 5-axis forward/inverse round-trip behavior and switching to identity kinematics. | | `linuxcnc_5axis_kinematics_probe` | Validates vendored LinuxCNC `5axiskins.c` through `switchkins.c`, including 5-axis forward/inverse round-trip behavior and switching to identity kinematics. |
@@ -75,6 +81,11 @@ Positive G-code fixtures currently cover:
- spindle orient - spindle orient
- tool semantics - tool semantics
- tool table setup - tool table setup
- tool-data reload boundary
- interpreter state-tag boundary
- percent-delimited file `FINISH` boundary
- file-open `ON_RESET` boundary
- comment logging canonical calls
- named and numbered parameters - named and numbered parameters
- O-word subroutines - O-word subroutines
- program-end modal reset - program-end modal reset
@@ -96,9 +107,11 @@ Negative fixtures currently cover:
Current validation is native-only. WASM, browser, SDK, OPFS, and full Current validation is native-only. WASM, browser, SDK, OPFS, and full
machine-session validation remain future work. machine-session validation remain future work.
The current fixture expectations validate standalone behavior against the The current fixture expectations validate standalone behavior against both the
vendored LinuxCNC source path. They do not yet run a side-by-side native vendored LinuxCNC source path and a first upstream `rs274` side-by-side
LinuxCNC executable comparison for each fixture. baseline for simple parser/conversion fixtures. Fixtures that depend on
standalone-only runtime adapters, HAL/INI/tool table setup, or richer machine
session state still need dedicated native LinuxCNC baselines.
The kinematics probes currently cover LinuxCNC identity/trivial kinematics, The kinematics probes currently cover LinuxCNC identity/trivial kinematics,
the switchable `5axiskins` XYZBCW bridge-mill model, and the TRT `xyzac`/`xyzbc` the switchable `5axiskins` XYZBCW bridge-mill model, and the TRT `xyzac`/`xyzbc`

View File

@@ -26,6 +26,13 @@ semantic rewrites:
| HAL lookup | Standalone HAL adapter for `_hal[...]` named parameter reads. | | HAL lookup | Standalone HAL adapter for `_hal[...]` named parameter reads. |
| INI lookup | Standalone INI adapter around vendored LinuxCNC INI parser behavior. | | INI lookup | Standalone INI adapter around vendored LinuxCNC INI parser behavior. |
| Canonical output | Canonical calls are captured as test events instead of driving hardware. | | Canonical output | Canonical calls are captured as test events instead of driving hardware. |
| Tool-data reload | LinuxCNC `RELOAD_TOOLDATA` is captured as a canonical test event; browser/native tool-table reload plumbing remains a future host/runtime adapter. |
| Interpreter state tags | LinuxCNC `UPDATE_TAG` callbacks are captured from the state tags packed by vendored interpreter code; standalone code does not derive modal state. |
| Rotary indexer lock state | LinuxCNC `UNLOCK_ROTARY` and `LOCK_ROTARY` callbacks are captured from the vendored single-axis indexer path; standalone code does not implement rotary-indexing semantics. |
| File flush | LinuxCNC `FINISH` is captured as a canonical test event on the vendored `%`-delimited file-reading path. |
| Interpreter reset | LinuxCNC `ON_RESET` is captured as a canonical test event on the vendored interpreter reset/file-open path. |
| Interpreter init | LinuxCNC `INIT_CANON` and the canonical initialization sequence from vendored `Interp::init()` are captured as test events. |
| Comment logging | LinuxCNC `LOGOPEN`, `LOG`, `LOGAPPEND`, and `LOGCLOSE` callbacks are captured as canonical test events instead of writing host log files. |
| Python/remap | Python/remap hooks are stubbed at the runtime edge. | | Python/remap | Python/remap hooks are stubbed at the runtime edge. |
| Dynamic interpreter path | `interp_base.cc` probe uses standalone `EMC2_HOME` compile-time path boundary. | | Dynamic interpreter path | `interp_base.cc` probe uses standalone `EMC2_HOME` compile-time path boundary. |
| Realtime scheduler | TP probes seed deterministic status/config data instead of running LinuxCNC realtime process topology. | | Realtime scheduler | TP probes seed deterministic status/config data instead of running LinuxCNC realtime process topology. |
@@ -58,8 +65,10 @@ semantic rewrites:
- Positive cutter-compensated motion and cutter compensation rejection paths - Positive cutter-compensated motion and cutter compensation rejection paths
now have standalone interpreter fixture coverage through vendored LinuxCNC now have standalone interpreter fixture coverage through vendored LinuxCNC
source. source.
- Fixture expectations are currently checked against the standalone vendored - Fixture expectations are checked against the standalone vendored source
source path, not by running a native LinuxCNC binary for every fixture. path. A first upstream `rs274` side-by-side baseline now covers simple
parser/conversion fixtures; adapter-heavy fixtures still need dedicated
native LinuxCNC baselines.
## Current Drift Conclusion ## Current Drift Conclusion

View File

@@ -456,6 +456,58 @@ Current verified progress:
`tests/fixtures/gcode/tool_table_setup.ngc` and `tests/fixtures/gcode/tool_table_setup.ngc` and
`tests/fixtures/canon/tool_table_setup.events` pin `G10 L1` tool offset, `tests/fixtures/canon/tool_table_setup.events` pin `G10 L1` tool offset,
diameter, front/back angle, and orientation behavior. diameter, front/back angle, and orientation behavior.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
the tool-data reload boundary call emitted by vendored
`interp_convert.cc::convert_modal_0()`: `RELOAD_TOOLDATA`.
`tests/fixtures/gcode/tool_reload.ngc` and
`tests/fixtures/canon/tool_reload.events` pin `G10 L0` dispatch without
adding standalone tool-table reload semantics.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
interpreter state-tag boundary calls emitted by vendored LinuxCNC
`interp_write.cc::write_state_tag()` and
`interp_convert.cc::update_tag()`: `UPDATE_TAG`.
`tests/fixtures/gcode/state_tag_motion.ngc` and
`tests/fixtures/canon/state_tag_motion.events` pin straight-motion state tag
emission without deriving modal state in standalone code.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
rotary indexer lock/unlock boundary calls emitted by vendored
`interp_convert.cc::issue_straight_index()`: `UNLOCK_ROTARY` and
`LOCK_ROTARY`. A dedicated `linuxcnc_indexer_harness` pins the LinuxCNC
single-axis `G0 A...` indexer path, including the surrounding motion-control
boundary calls, without adding standalone rotary-indexing semantics.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
the canonical flush boundary emitted by vendored LinuxCNC file-reading
code: `FINISH`. `tests/fixtures/gcode/percent_file_finish.ngc` and
`tests/fixtures/canon/percent_file_finish.events` pin `%`-delimited file
handling from `rs274ngc_pre.cc::open()` and `interp_read.cc::read_text()`.
The native verification script treats this fixture as file-mode-only
because `%` is a LinuxCNC program-file delimiter, not a valid MDI command.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
the reset/queue-drop boundary emitted by vendored
`rs274ngc_pre.cc::reset()`: `ON_RESET`.
`tests/fixtures/gcode/file_open_reset.ngc` and
`tests/fixtures/canon/file_open_reset.events` pin the LinuxCNC file-open
reset path without adding standalone queue-management semantics.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
the interpreter initialization boundary emitted by vendored
`rs274ngc_pre.cc::init()`: `INIT_CANON`. A dedicated
`linuxcnc_interp_init_harness` calls vendored `Interp::init()` and pins the
LinuxCNC initialization sequence: `INIT_CANON`, `USE_LENGTH_UNITS`,
`SET_G5X_OFFSET`, `SET_G92_OFFSET`, `SET_XY_ROTATION`, and
`SET_FEED_REFERENCE`, without changing the minimal G-code fixture harness
startup path.
- `runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` now records
comment logging canonical boundary calls emitted by vendored
`interp_convert.cc::convert_comment()`: `LOGOPEN`, `LOG`, `LOGAPPEND`,
and `LOGCLOSE`. `tests/fixtures/gcode/comment_logging.ngc` and
`tests/fixtures/canon/comment_logging.events` pin LinuxCNC comment logging
dispatch without adding standalone comment semantics.
- `tools/verify_native_linuxcnc_fixture_baseline.sh` now runs a first
side-by-side baseline against upstream `../linuxcnc/bin/rs274`. It normalizes
native LinuxCNC canonical output for simple parser/conversion fixtures
(`minimal_linear`, `length_units`, `plane_selection`, and
`modal_incremental`) and compares it with the standalone fixture events,
without introducing a project-authored CNC semantics oracle.
- `tests/fixtures/gcode_errors/g1_zero_feed.ngc` and - `tests/fixtures/gcode_errors/g1_zero_feed.ngc` and
`tests/fixtures/canon_errors/g1_zero_feed.expected` pin the negative `tests/fixtures/canon_errors/g1_zero_feed.expected` pin the negative
`G1` zero-feed case. The source basis is LinuxCNC `G1` zero-feed case. The source basis is LinuxCNC

View File

@@ -0,0 +1,51 @@
#define private public
#include "emc/rs274ngc/rs274ngc_interp.hh"
#undef private
#include <cstring>
#include <iostream>
#include "canon_event_sink.hh"
#include "linuxcnc_hal_adapter.hh"
#include "linuxcnc_tool_adapter.hh"
namespace {
void initialize_indexer_interp(Interp &interp)
{
standalone::reset_hal_adapter();
standalone::reset_tool_adapter();
interp._setup.length_units = CANON_UNITS_MM;
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
interp._setup.ijk_distance_mode = DISTANCE_MODE::ABSOLUTE;
interp._setup.feed_mode = FEED_MODE::UNITS_PER_MINUTE;
interp._setup.plane = CANON_PLANE::XY;
interp._setup.motion_mode = G_0;
interp._setup.percent_flag = false;
interp._setup.sequence_number = 1;
interp._setup.parameter_occurrence = 0;
interp._setup.num_spindles = 1;
interp._setup.a_indexer_jnum = 0;
interp._setup.parameters[5599] = 1.0;
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
}
} // namespace
int main()
{
Interp interp;
standalone::reset_canon_events();
initialize_indexer_interp(interp);
const int rc = interp.execute("G0 A90\n");
std::cout << "execute=" << rc << "\n";
std::cout << "post_a=" << interp._setup.AA_current << "\n";
std::cout << "canon_event_count=" << standalone::canon_events().size() << "\n";
for (const auto &event : standalone::canon_events()) {
std::cout << "canon_event=" << event << "\n";
}
return rc == INTERP_OK ? 0 : 1;
}

View File

@@ -0,0 +1,49 @@
#define private public
#include "emc/rs274ngc/rs274ngc_interp.hh"
#undef private
#include <cstdlib>
#include <filesystem>
#include <iostream>
#include <unistd.h>
#include "canon_event_sink.hh"
namespace {
std::filesystem::path make_temp_dir()
{
std::string pattern = "/tmp/linuxcnc-init-XXXXXX";
char *path = mkdtemp(pattern.data());
if (path == nullptr) {
std::perror("mkdtemp");
std::exit(2);
}
return path;
}
} // namespace
int main()
{
const auto temp_dir = make_temp_dir();
std::filesystem::current_path(temp_dir);
Interp interp;
standalone::reset_canon_events();
const int rc = interp.init();
std::cout << "init=" << rc << "\n";
std::cout << "setup.length_units=" << static_cast<int>(interp._setup.length_units) << "\n";
std::cout << "setup.origin_index=" << interp._setup.origin_index << "\n";
std::cout << "setup.distance_mode=" << static_cast<int>(interp._setup.distance_mode) << "\n";
std::cout << "setup.feed_mode=" << static_cast<int>(interp._setup.feed_mode) << "\n";
std::cout << "setup.motion_mode=" << interp._setup.motion_mode << "\n";
std::cout << "canon_event_count=" << standalone::canon_events().size() << "\n";
for (const auto &event : standalone::canon_events()) {
std::cout << "canon_event=" << event << "\n";
}
std::filesystem::remove_all(temp_dir);
return rc == INTERP_OK ? 0 : 1;
}

View File

@@ -36,8 +36,8 @@ const std::vector<std::string> &canon_events()
} // namespace standalone } // namespace standalone
bool GET_BLOCK_DELETE(void) { return false; } bool GET_BLOCK_DELETE(void) { return false; }
void FINISH(void) {} void FINISH(void) { standalone::push_canon_event("FINISH"); }
void ON_RESET(void) {} void ON_RESET(void) { standalone::push_canon_event("ON_RESET"); }
USER_DEFINED_FUNCTION_TYPE USER_DEFINED_FUNCTION[USER_DEFINED_FUNCTION_NUM] = {}; USER_DEFINED_FUNCTION_TYPE USER_DEFINED_FUNCTION[USER_DEFINED_FUNCTION_NUM] = {};
@@ -50,7 +50,7 @@ int USER_DEFINED_FUNCTION_ADD(USER_DEFINED_FUNCTION_TYPE func, int num)
return num; return num;
} }
void INIT_CANON() {} void INIT_CANON() { standalone::push_canon_event("INIT_CANON"); }
void SET_G5X_OFFSET(int index, void SET_G5X_OFFSET(int index,
double x, double y, double z, double x, double y, double z,
double a, double b, double c, double a, double b, double c,
@@ -307,7 +307,7 @@ void CHANGE_TOOL_NUMBER(int pocket)
oss << "CHANGE_TOOL_NUMBER pocket=" << pocket; oss << "CHANGE_TOOL_NUMBER pocket=" << pocket;
standalone::push_canon_event(oss.str()); standalone::push_canon_event(oss.str());
} }
void RELOAD_TOOLDATA(void) {} void RELOAD_TOOLDATA(void) { standalone::push_canon_event("RELOAD_TOOLDATA"); }
void CLAMP_AXIS(CANON_AXIS) {} void CLAMP_AXIS(CANON_AXIS) {}
void DISABLE_ADAPTIVE_FEED() { standalone::push_canon_event("DISABLE_ADAPTIVE_FEED"); } void DISABLE_ADAPTIVE_FEED() { standalone::push_canon_event("DISABLE_ADAPTIVE_FEED"); }
void ENABLE_ADAPTIVE_FEED() { standalone::push_canon_event("ENABLE_ADAPTIVE_FEED"); } void ENABLE_ADAPTIVE_FEED() { standalone::push_canon_event("ENABLE_ADAPTIVE_FEED"); }
@@ -390,15 +390,52 @@ int WAIT(int index, int input_type, int wait_type, double timeout)
standalone::push_canon_event(oss.str()); standalone::push_canon_event(oss.str());
return 0; return 0;
} }
int UNLOCK_ROTARY(int, int) { return 0; } int UNLOCK_ROTARY(int line_no, int joint_num)
int LOCK_ROTARY(int, int) { return 0; } {
void UPDATE_TAG(const StateTag &) {} std::ostringstream oss;
oss << "UNLOCK_ROTARY line=" << line_no << " joint=" << joint_num;
standalone::push_canon_event(oss.str());
return 0;
}
int LOCK_ROTARY(int line_no, int joint_num)
{
std::ostringstream oss;
oss << "LOCK_ROTARY line=" << line_no << " joint=" << joint_num;
standalone::push_canon_event(oss.str());
return 0;
}
void UPDATE_TAG(const StateTag &tag)
{
std::ostringstream oss;
oss << "UPDATE_TAG"
<< " line=" << tag.fields[GM_FIELD_LINE_NUMBER]
<< " g0=" << tag.fields[GM_FIELD_G_MODE_0]
<< " motion=" << tag.fields[GM_FIELD_MOTION_MODE]
<< " plane=" << tag.fields[GM_FIELD_PLANE]
<< " origin=" << tag.fields[GM_FIELD_ORIGIN]
<< " feed=" << tag.fields_float[GM_FIELD_FLOAT_FEED]
<< " speed=" << tag.fields_float[GM_FIELD_FLOAT_SPEED]
<< " flags=" << tag.flags.to_ulong();
standalone::push_canon_event(oss.str());
}
void MESSAGE(char *s) { standalone::push_canon_event(std::string("MESSAGE: ") + (s ? s : "")); } void MESSAGE(char *s) { standalone::push_canon_event(std::string("MESSAGE: ") + (s ? s : "")); }
void LOG(char *) {} void LOG(char *s)
void LOGOPEN(char *) {} {
void LOGAPPEND(char *) {} standalone::push_canon_event(std::string("LOG: ") + (s ? s : ""));
void LOGCLOSE() {} }
void LOGOPEN(char *s)
{
standalone::push_canon_event(std::string("LOGOPEN: ") + (s ? s : ""));
}
void LOGAPPEND(char *s)
{
standalone::push_canon_event(std::string("LOGAPPEND: ") + (s ? s : ""));
}
void LOGCLOSE()
{
standalone::push_canon_event("LOGCLOSE");
}
void CANON_ERROR(const char *fmt, ...) void CANON_ERROR(const char *fmt, ...)
{ {

View File

@@ -187,9 +187,9 @@ setup::setup()
a_axis_wrapped(0), a_axis_wrapped(0),
b_axis_wrapped(0), b_axis_wrapped(0),
c_axis_wrapped(0), c_axis_wrapped(0),
a_indexer_jnum(0), a_indexer_jnum(-1),
b_indexer_jnum(0), b_indexer_jnum(-1),
c_indexer_jnum(0), c_indexer_jnum(-1),
lathe_diameter_mode(0), lathe_diameter_mode(0),
mdi_interrupt(0), mdi_interrupt(0),
feature_set(0), feature_set(0),

View File

@@ -0,0 +1,4 @@
canon_event=LOGOPEN: session.log
canon_event=LOG: feed log line
canon_event=LOGAPPEND: append log line
canon_event=LOGCLOSE

View File

@@ -0,0 +1,2 @@
canon_event=ON_RESET
canon_event=STRAIGHT_TRAVERSE line=1 x=1 y=0 z=0 a=0 b=0 c=0 u=0 v=0 w=0

View File

@@ -0,0 +1,2 @@
canon_event=STRAIGHT_TRAVERSE line=2 x=1 y=0 z=0 a=0 b=0 c=0 u=0 v=0 w=0
canon_event=FINISH

View File

@@ -0,0 +1,2 @@
canon_event=UPDATE_TAG line=1
canon_event=UPDATE_TAG line=2

View File

@@ -0,0 +1 @@
canon_event=RELOAD_TOOLDATA

View File

@@ -0,0 +1,4 @@
(LOGOPEN,session.log)
(LOG,feed log line)
(LOGAPPEND,append log line)
(LOGCLOSE)

View File

@@ -0,0 +1 @@
G0 X1

View File

@@ -0,0 +1,3 @@
%
G0 X1
%

View File

@@ -0,0 +1,2 @@
G90 G17 G0 X1 Y2
G1 X3 Y4 F120

View File

@@ -0,0 +1 @@
G10 L0

View File

@@ -12,6 +12,7 @@ NAMEDPARAM_INI_FIXTURE="$ROOT_DIR/tests/fixtures/ini/namedparams.ini"
"$ROOT_DIR/tools/verify_upstream_baseline.sh" "$ROOT_DIR/tools/verify_upstream_baseline.sh"
"$ROOT_DIR/tools/verify_vendor_sync.sh" "$ROOT_DIR/tools/verify_vendor_sync.sh"
"$ROOT_DIR/tools/verify_no_standalone_cnc_semantics.sh" "$ROOT_DIR/tools/verify_no_standalone_cnc_semantics.sh"
"$ROOT_DIR/tools/verify_native_linuxcnc_fixture_baseline.sh"
"$ROOT_DIR/tools/build_native_probes.sh" "$ROOT_DIR/tools/build_native_probes.sh"
check_source_probe_coverage() { check_source_probe_coverage() {
@@ -152,6 +153,10 @@ check_exitcode linuxcnc_interp_minimal_harness
check_exitcode linuxcnc_interp_minimal_harness.run check_exitcode linuxcnc_interp_minimal_harness.run
check_exitcode linuxcnc_parameter_file_harness check_exitcode linuxcnc_parameter_file_harness
check_exitcode linuxcnc_parameter_file_harness.run check_exitcode linuxcnc_parameter_file_harness.run
check_exitcode linuxcnc_interp_init_harness
check_exitcode linuxcnc_interp_init_harness.run
check_exitcode linuxcnc_indexer_harness
check_exitcode linuxcnc_indexer_harness.run
check_exitcode linuxcnc_rs274_compile_probe check_exitcode linuxcnc_rs274_compile_probe
check_exitcode linuxcnc_kins_util_source_probe check_exitcode linuxcnc_kins_util_source_probe
check_exitcode linuxcnc_cubic_source_probe check_exitcode linuxcnc_cubic_source_probe
@@ -238,6 +243,31 @@ grep -Fq "saved_has_5399=1" "$PARAMETER_FILE_STDOUT"
grep -Fq "saved_has_named_param=0" "$PARAMETER_FILE_STDOUT" grep -Fq "saved_has_named_param=0" "$PARAMETER_FILE_STDOUT"
grep -Fq "backup_has_original_5161=1" "$PARAMETER_FILE_STDOUT" grep -Fq "backup_has_original_5161=1" "$PARAMETER_FILE_STDOUT"
INIT_HARNESS_STDOUT="$BUILD_DIR/linuxcnc_interp_init_harness.run.stdout.log"
grep -Fq "init=0" "$INIT_HARNESS_STDOUT"
grep -Fq "setup.length_units=2" "$INIT_HARNESS_STDOUT"
grep -Fq "setup.origin_index=1" "$INIT_HARNESS_STDOUT"
grep -Fq "setup.distance_mode=0" "$INIT_HARNESS_STDOUT"
grep -Fq "setup.feed_mode=0" "$INIT_HARNESS_STDOUT"
grep -Fq "setup.motion_mode=800" "$INIT_HARNESS_STDOUT"
grep -Fq "canon_event=INIT_CANON" "$INIT_HARNESS_STDOUT"
grep -Fq "canon_event=USE_LENGTH_UNITS units=2" "$INIT_HARNESS_STDOUT"
grep -Fq "canon_event=SET_G5X_OFFSET index=1" "$INIT_HARNESS_STDOUT"
grep -Fq "canon_event=SET_G92_OFFSET x=0 y=0 z=0" "$INIT_HARNESS_STDOUT"
grep -Fq "canon_event=SET_XY_ROTATION rotation=0" "$INIT_HARNESS_STDOUT"
grep -Fq "canon_event=SET_FEED_REFERENCE reference=2" "$INIT_HARNESS_STDOUT"
INDEXER_HARNESS_STDOUT="$BUILD_DIR/linuxcnc_indexer_harness.run.stdout.log"
grep -Fq "execute=0" "$INDEXER_HARNESS_STDOUT"
grep -Fq "post_a=90" "$INDEXER_HARNESS_STDOUT"
grep -Fq "canon_event=SET_MOTION_CONTROL_MODE mode=2 tolerance=0" "$INDEXER_HARNESS_STDOUT"
grep -Fq "canon_event=UNLOCK_ROTARY line=1 joint=0" "$INDEXER_HARNESS_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=1 x=0 y=0 z=0 a=90 b=0 c=0 u=0 v=0 w=0" "$INDEXER_HARNESS_STDOUT"
grep -Fq "canon_event=LOCK_ROTARY line=1 joint=0" "$INDEXER_HARNESS_STDOUT"
grep -Fq "canon_event=SET_MOTION_CONTROL_MODE mode=1 tolerance=0" "$INDEXER_HARNESS_STDOUT"
grep -Fq "canon_event=SET_NAIVECAM_TOLERANCE tolerance=0" "$INDEXER_HARNESS_STDOUT"
grep -Fq "canon_event=UPDATE_TAG line=1" "$INDEXER_HARNESS_STDOUT"
TP_API_STDOUT="$BUILD_DIR/linuxcnc_tp_api_probe.run.stdout.log" TP_API_STDOUT="$BUILD_DIR/linuxcnc_tp_api_probe.run.stdout.log"
grep -Fq "tp_default_queue_size=32" "$TP_API_STDOUT" grep -Fq "tp_default_queue_size=32" "$TP_API_STDOUT"
grep -Fq "tp_err_ok=0" "$TP_API_STDOUT" grep -Fq "tp_err_ok=0" "$TP_API_STDOUT"
@@ -438,10 +468,10 @@ check_fixture_output() {
local stdout_file="$3" local stdout_file="$3"
local require_mdi="${4:-1}" local require_mdi="${4:-1}"
grep -Fq "read=0" "$stdout_file"
grep -Fq "parse_line=0" "$stdout_file"
if [[ "$require_mdi" == "1" ]]; then if [[ "$require_mdi" == "1" ]]; then
grep -Fq "read=0" "$stdout_file"
grep -Fq "parse_line=0" "$stdout_file"
local line_count local line_count
line_count="$(grep -cv '^[[:space:]]*$' "$fixture")" line_count="$(grep -cv '^[[:space:]]*$' "$fixture")"
local line_number local line_number
@@ -484,7 +514,7 @@ for fixture in "$GCODE_FIXTURE_DIR"/*.ngc; do
>"$stdout_file" \ >"$stdout_file" \
2>"$stderr_file" 2>"$stderr_file"
require_mdi=1 require_mdi=1
if [[ "$name" == "oword_subroutine" ]]; then if [[ "$name" == "oword_subroutine" || "$name" == "percent_file_finish" ]]; then
require_mdi=0 require_mdi=0
fi fi
check_fixture_output "$fixture" "$expected" "$stdout_file" "$require_mdi" check_fixture_output "$fixture" "$expected" "$stdout_file" "$require_mdi"

View File

@@ -629,6 +629,16 @@ PARAMETER_FILE_SOURCES=(
"$WRAP_DIR/linuxcnc_parameter_file_harness.cpp" "$WRAP_DIR/linuxcnc_parameter_file_harness.cpp"
) )
INIT_HARNESS_SOURCES=(
"${INTERP_CORE_SOURCES[@]}"
"$WRAP_DIR/linuxcnc_interp_init_harness.cpp"
)
INDEXER_HARNESS_SOURCES=(
"${INTERP_CORE_SOURCES[@]}"
"$WRAP_DIR/linuxcnc_indexer_harness.cpp"
)
build_binary_target \ build_binary_target \
linuxcnc_ini_probe \ linuxcnc_ini_probe \
"$BUILD_DIR/linuxcnc_ini_probe" \ "$BUILD_DIR/linuxcnc_ini_probe" \
@@ -1123,6 +1133,52 @@ else
"$BUILD_DIR/linuxcnc_parameter_file_harness.run.stderr.log" "$BUILD_DIR/linuxcnc_parameter_file_harness.run.stderr.log"
fi fi
build_binary_target \
linuxcnc_interp_init_harness \
"$BUILD_DIR/linuxcnc_interp_init_harness" \
MINIMAL_FLAGS \
INIT_HARNESS_SOURCES \
MINIMAL_LINK_FLAGS \
-lfmt
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_interp_init_harness.exitcode")" == "0" ]]; then
set +e
"$BUILD_DIR/linuxcnc_interp_init_harness" \
>"$BUILD_DIR/linuxcnc_interp_init_harness.run.stdout.log" \
2>"$BUILD_DIR/linuxcnc_interp_init_harness.run.stderr.log"
INTERP_INIT_RUN_RC=$?
set -e
echo "$INTERP_INIT_RUN_RC" > "$BUILD_DIR/linuxcnc_interp_init_harness.run.exitcode"
else
rm -f \
"$BUILD_DIR/linuxcnc_interp_init_harness.run.exitcode" \
"$BUILD_DIR/linuxcnc_interp_init_harness.run.stdout.log" \
"$BUILD_DIR/linuxcnc_interp_init_harness.run.stderr.log"
fi
build_binary_target \
linuxcnc_indexer_harness \
"$BUILD_DIR/linuxcnc_indexer_harness" \
MINIMAL_FLAGS \
INDEXER_HARNESS_SOURCES \
MINIMAL_LINK_FLAGS \
-lfmt
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_indexer_harness.exitcode")" == "0" ]]; then
set +e
"$BUILD_DIR/linuxcnc_indexer_harness" \
>"$BUILD_DIR/linuxcnc_indexer_harness.run.stdout.log" \
2>"$BUILD_DIR/linuxcnc_indexer_harness.run.stderr.log"
INDEXER_RUN_RC=$?
set -e
echo "$INDEXER_RUN_RC" > "$BUILD_DIR/linuxcnc_indexer_harness.run.exitcode"
else
rm -f \
"$BUILD_DIR/linuxcnc_indexer_harness.run.exitcode" \
"$BUILD_DIR/linuxcnc_indexer_harness.run.stdout.log" \
"$BUILD_DIR/linuxcnc_indexer_harness.run.stderr.log"
fi
build_object_target \ build_object_target \
linuxcnc_rs274_compile_probe \ linuxcnc_rs274_compile_probe \
"$BUILD_DIR/linuxcnc_rs274_compile_probe.o" \ "$BUILD_DIR/linuxcnc_rs274_compile_probe.o" \

View File

@@ -0,0 +1,186 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
UPSTREAM_RS274="$ROOT_DIR/../linuxcnc/bin/rs274"
GCODE_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode"
CANON_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon"
FIXTURES=(
minimal_linear
length_units
plane_selection
modal_incremental
)
if [[ ! -x "$UPSTREAM_RS274" ]]; then
echo "missing upstream rs274 executable: $UPSTREAM_RS274" >&2
exit 1
fi
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
normalize_standalone_events() {
awk '
function norm(value) {
return sprintf("%.12g", value + 0)
}
/^canon_event=/ {
line = $0
sub(/^canon_event=/, "", line)
if (line ~ /^SELECT_PLANE plane=/) {
value = line
sub(/^SELECT_PLANE plane=/, "", value)
if (value == "1") {
print "SELECT_PLANE|XY"
} else if (value == "2") {
print "SELECT_PLANE|YZ"
} else if (value == "3") {
print "SELECT_PLANE|XZ"
}
next
}
if (line ~ /^USE_LENGTH_UNITS units=/) {
value = line
sub(/^USE_LENGTH_UNITS units=/, "", value)
if (value == "1") {
print "USE_LENGTH_UNITS|INCHES"
} else if (value == "2") {
print "USE_LENGTH_UNITS|MM"
}
next
}
if (line ~ /^SET_FEED_RATE rate=/) {
value = line
sub(/^SET_FEED_RATE rate=/, "", value)
print "SET_FEED_RATE|" norm(value)
next
}
if (line ~ /^STRAIGHT_(TRAVERSE|FEED) /) {
split(line, fields, /[[:space:]]+/)
for (i in fields) {
split(fields[i], pair, "=")
values[pair[1]] = pair[2]
}
print fields[1] "|" norm(values["x"]) "|" norm(values["y"]) "|" \
norm(values["z"]) "|" norm(values["a"]) "|" norm(values["b"]) "|" \
norm(values["c"])
delete values
next
}
}
'
}
normalize_upstream_rs274_output() {
awk '
function norm(value) {
return sprintf("%.12g", value + 0)
}
function args_of(call, args) {
args = call
sub(/^[^(]+\(/, "", args)
sub(/\).*$/, "", args)
gsub(/[[:space:]]/, "", args)
return args
}
/^[[:space:]]*[0-9]+[[:space:]]+N\.\.\.\.\./ {
line = $0
sub(/^[[:space:]]*[0-9]+[[:space:]]+N\.\.\.\.\.[[:space:]]+/, "", line)
if (line == "ON_RESET()") {
seen_reset = 1
next
}
if (!seen_reset) {
next
}
if (line ~ /^SELECT_PLANE\(CANON_PLANE_/) {
value = line
sub(/^SELECT_PLANE\(CANON_PLANE_/, "", value)
sub(/\)$/, "", value)
print "SELECT_PLANE|" value
next
}
if (line ~ /^USE_LENGTH_UNITS\(CANON_UNITS_/) {
value = line
sub(/^USE_LENGTH_UNITS\(CANON_UNITS_/, "", value)
sub(/\)$/, "", value)
print "USE_LENGTH_UNITS|" value
next
}
if (line ~ /^SET_FEED_RATE\(/) {
args = args_of(line)
print "SET_FEED_RATE|" norm(args)
next
}
if (line ~ /^STRAIGHT_(TRAVERSE|FEED)\(/) {
op = line
sub(/\(.*/, "", op)
args = args_of(line)
split(args, values, ",")
print op "|" norm(values[1]) "|" norm(values[2]) "|" norm(values[3]) "|" \
norm(values[4]) "|" norm(values[5]) "|" norm(values[6])
next
}
}
'
}
for name in "${FIXTURES[@]}"; do
gcode_file="$GCODE_FIXTURE_DIR/$name.ngc"
expected_file="$CANON_FIXTURE_DIR/$name.events"
native_raw="$TMP_DIR/$name.rs274.raw"
native_norm="$TMP_DIR/$name.rs274.normalized"
expected_norm="$TMP_DIR/$name.expected.normalized"
if [[ ! -f "$gcode_file" ]]; then
echo "missing G-code fixture: $gcode_file" >&2
exit 1
fi
if [[ ! -f "$expected_file" ]]; then
echo "missing canonical fixture: $expected_file" >&2
exit 1
fi
set +e
"$UPSTREAM_RS274" -g "$gcode_file" > "$native_raw" 2>&1
rs274_rc=$?
set -e
if [[ "$rs274_rc" != "0" ]] && ! grep -Fq "File ended with no percent sign (%) or program end (M2)" "$native_raw"; then
echo "upstream rs274 failed for fixture: $name" >&2
sed -n '1,160p' "$native_raw" >&2
exit 1
fi
normalize_upstream_rs274_output < "$native_raw" > "$native_norm"
normalize_standalone_events < "$expected_file" > "$expected_norm"
if [[ ! -s "$native_norm" ]]; then
echo "no normalized upstream rs274 events for fixture: $name" >&2
sed -n '1,160p' "$native_raw" >&2
exit 1
fi
if ! diff -u "$expected_norm" "$native_norm"; then
echo "native LinuxCNC rs274 baseline mismatch for fixture: $name" >&2
exit 1
fi
done
echo "native LinuxCNC fixture baseline validation complete"