按规划持续工作

结论:新增 5axis native remap 执行验证,使用 vendored LinuxCNC REMAP/NGC 路径执行 xyzac-trt 与 xyzbc-trt 的 M429 -> M428 -> M430 -> M429,并确认 _hal[motion.switchkins-type] 随 LinuxCNC M68/M66 流程正确读回。
This commit is contained in:
2026-06-08 17:30:23 +08:00
parent 42da16214d
commit 7cccc58acb
4 changed files with 242 additions and 1 deletions

View File

@@ -213,6 +213,7 @@ The validation fails if:
| `linuxcnc_interp_init_harness` | Validates vendored LinuxCNC `Interp::init()` emits canonical initialization boundaries, reads metric/inch machine units, and synchronizes current/selected tool slots through standalone status adapters. | | `linuxcnc_interp_init_harness` | Validates vendored LinuxCNC `Interp::init()` emits canonical initialization boundaries, reads metric/inch machine units, and synchronizes current/selected tool slots through standalone status adapters. |
| `linuxcnc_indexer_harness` | Validates vendored LinuxCNC single-axis rotary indexer dispatch emits lock/unlock and motion 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_remap_hal_sync_harness` | Validates vendored LinuxCNC `M68`/`M66` execution can drive the standalone HAL adapter boundary used by 5-axis switchkins remap files, including `_hal[motion.switchkins-type]` readback. | | `linuxcnc_remap_hal_sync_harness` | Validates vendored LinuxCNC `M68`/`M66` execution can drive the standalone HAL adapter boundary used by 5-axis switchkins remap files, including `_hal[motion.switchkins-type]` readback. |
| `linuxcnc_5axis_remap_execute_harness` | Validates vendored LinuxCNC `REMAP` parsing plus NGC remap execution for the 5-axis `M429 -> M428 -> M430 -> M429` switchkins path in the `xyzac-trt` and `xyzbc-trt` sample machines. |
| `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. |

View File

@@ -0,0 +1,186 @@
#define private public
#include "emc/rs274ngc/rs274ngc_interp.hh"
#undef private
#include <cmath>
#include <cstring>
#include <filesystem>
#include <iostream>
#include <string>
#include <vector>
#include "canon_event_sink.hh"
#include "emc/ini/inifile.hh"
#include "emc/rs274ngc/interp_internal.hh"
#include "linuxcnc_hal_adapter.hh"
#include "linuxcnc_tool_adapter.hh"
#ifndef LINUXCNC_VENDOR_DIR
#error "LINUXCNC_VENDOR_DIR must point at wasm-port/vendor/linuxcnc"
#endif
namespace {
struct MachineCase {
const char *label;
const char *ini_rel;
};
std::filesystem::path vendor_path(const std::string &rel)
{
return std::filesystem::path(LINUXCNC_VENDOR_DIR) / rel;
}
void print_bool(const std::string &name, bool value)
{
std::cout << name << "=" << (value ? 1 : 0) << "\n";
}
void seed_switchkins_hal()
{
standalone::reset_hal_adapter();
hal_data_u switchkins{};
switchkins.f = 0.0;
standalone::set_hal_value(standalone::HalValueKind::Pin, "motion.switchkins-type",
HAL_FLOAT, switchkins);
standalone::set_hal_value(standalone::HalValueKind::Pin, "motion.analog-out-03",
HAL_FLOAT, switchkins);
standalone::link_hal_pin_alias("motion.analog-out-03", "motion.switchkins-type");
}
void initialize_interp(Interp &interp)
{
seed_switchkins_hal();
standalone::reset_tool_adapter();
standalone::reset_canon_events();
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 = 0;
interp._setup.parameter_occurrence = 0;
interp._setup.num_spindles = 1;
interp._setup.feature_set = FEATURE_INI_VARS | FEATURE_HAL_PIN_VARS;
interp._setup.parameter_g73_peck_clearance = 1.0;
interp._setup.parameter_g83_peck_clearance = 1.0;
interp._setup.parameters[5599] = 1.0;
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
interp.init_named_parameters();
}
bool read_hal_named(Interp &interp, const std::string &label, const char *name, double expected)
{
int status = 0;
double value = 0.0;
const int rc = interp.find_named_param(name, &status, &value);
std::cout << label << "_" << name << ": rc=" << rc << " found=" << status
<< " value=" << value << "\n";
return rc == INTERP_OK && status == 1 && std::fabs(value - expected) < 0.000001;
}
bool parse_machine_remaps(Interp &interp, const MachineCase &machine)
{
const auto ini_path = vendor_path(machine.ini_rel);
const auto machine_dir = ini_path.parent_path();
const auto remap_dir = machine_dir / "remap_subs";
linuxcnc::IniFile ini(ini_path.string());
const bool opened = static_cast<bool>(ini);
print_bool(std::string(machine.label) + "_ini_open", opened);
if (!opened) {
return false;
}
std::snprintf(interp._setup.program_prefix, sizeof(interp._setup.program_prefix), "%s",
machine_dir.c_str());
interp._setup.subroutines[0] = strstore(remap_dir.c_str());
bool ok = true;
int remap_count = 0;
int line_number = 0;
while (auto remap = ini.findString(++remap_count, "REMAP", "RS274NGC")) {
line_number = ini.lineOf(remap_count, "REMAP", "RS274NGC").second;
const int rc = interp.parse_remap(remap->c_str(), line_number);
std::cout << machine.label << "_parse_remap_" << remap_count << "=" << rc << "\n";
ok &= rc == INTERP_OK;
}
remap_count -= 1;
std::cout << machine.label << "_parsed_remap_count=" << remap_count << "\n";
ok &= remap_count == 3;
print_bool(std::string(machine.label) + "_remaps_ready", ok);
return ok;
}
std::string command_label(const char *command)
{
std::string value = command ? command : "";
while (!value.empty() && (value.back() == '\n' || value.back() == '\r')) {
value.pop_back();
}
return value;
}
bool execute_step(Interp &interp, const std::string &label, const char *command, double expected)
{
const std::string step = command_label(command);
int rc = interp.execute(command);
std::cout << label << "_" << step << "_execute_0=" << rc << "\n";
int finish_count = 0;
while (rc == INTERP_EXECUTE_FINISH && finish_count < 8) {
++finish_count;
rc = interp.execute();
std::cout << label << "_" << step << "_execute_" << finish_count << "=" << rc << "\n";
}
std::cout << label << "_" << step << "_execute_finish_count=" << finish_count << "\n";
bool ok = rc == INTERP_OK;
ok &= read_hal_named(interp, label, "_hal[motion.switchkins-type]", expected);
return ok;
}
bool check_machine(const MachineCase &machine)
{
Interp interp;
initialize_interp(interp);
bool ok = parse_machine_remaps(interp, machine);
ok &= read_hal_named(interp, machine.label, "_hal[motion.switchkins-type]", 0.0);
ok &= execute_step(interp, machine.label, "M429\n", 0.0);
ok &= execute_step(interp, machine.label, "M428\n", 1.0);
ok &= execute_step(interp, machine.label, "M430\n", 2.0);
ok &= execute_step(interp, machine.label, "M429\n", 0.0);
print_bool(std::string(machine.label) + "_linuxcnc_remap_execute_path", ok);
return ok;
}
} // namespace
int main()
{
const MachineCase cases[] = {
{
"xyzac_trt",
"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
},
{
"xyzbc_trt",
"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini",
},
};
bool ok = true;
for (const auto &machine : cases) {
ok &= check_machine(machine);
}
print_bool("fiveaxis_linuxcnc_remap_execute_path", ok);
return ok ? 0 : 1;
}

View File

@@ -163,6 +163,8 @@ check_exitcode linuxcnc_remap_parse_harness
check_exitcode linuxcnc_remap_parse_harness.run check_exitcode linuxcnc_remap_parse_harness.run
check_exitcode linuxcnc_remap_hal_sync_harness check_exitcode linuxcnc_remap_hal_sync_harness
check_exitcode linuxcnc_remap_hal_sync_harness.run check_exitcode linuxcnc_remap_hal_sync_harness.run
check_exitcode linuxcnc_5axis_remap_execute_harness
check_exitcode linuxcnc_5axis_remap_execute_harness.run
check_exitcode linuxcnc_indexer_harness check_exitcode linuxcnc_indexer_harness
check_exitcode linuxcnc_indexer_harness.run check_exitcode linuxcnc_indexer_harness.run
check_exitcode linuxcnc_rs274_compile_probe check_exitcode linuxcnc_rs274_compile_probe
@@ -281,6 +283,27 @@ grep -Fq "canon_has_m66_sync=1" "$REMAP_HAL_SYNC_STDOUT"
grep -Fq "canon_has_m68_identity=1" "$REMAP_HAL_SYNC_STDOUT" grep -Fq "canon_has_m68_identity=1" "$REMAP_HAL_SYNC_STDOUT"
grep -Fq "fiveaxis_remap_hal_sync_boundary=1" "$REMAP_HAL_SYNC_STDOUT" grep -Fq "fiveaxis_remap_hal_sync_boundary=1" "$REMAP_HAL_SYNC_STDOUT"
FIVEAXIS_REMAP_EXECUTE_STDOUT="$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.run.stdout.log"
grep -Fq "xyzac_trt_ini_open=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_remaps_ready=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_M429_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_M429_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_M428_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_M428_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_M430_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_M430_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_linuxcnc_remap_execute_path=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_ini_open=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_remaps_ready=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_M429_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_M429_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_M428_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_M428_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_M430_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_M430_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_linuxcnc_remap_execute_path=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "fiveaxis_linuxcnc_remap_execute_path=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
NAMEDPARAM_STDOUT="$BUILD_DIR/linuxcnc_namedparam_harness.run.stdout.log" NAMEDPARAM_STDOUT="$BUILD_DIR/linuxcnc_namedparam_harness.run.stdout.log"
grep -Fq "init_named_parameters=0" "$NAMEDPARAM_STDOUT" grep -Fq "init_named_parameters=0" "$NAMEDPARAM_STDOUT"
grep -Fq "global_named_count=57" "$NAMEDPARAM_STDOUT" grep -Fq "global_named_count=57" "$NAMEDPARAM_STDOUT"

View File

@@ -667,7 +667,7 @@ INDEXER_HARNESS_SOURCES=(
"$WRAP_DIR/linuxcnc_indexer_harness.cpp" "$WRAP_DIR/linuxcnc_indexer_harness.cpp"
) )
REMAP_PARSE_SOURCES=( REMAP_RUNTIME_CORE_SOURCES=(
"$VENDOR_DIR/src/emc/rs274ngc/modal_state.cc" "$VENDOR_DIR/src/emc/rs274ngc/modal_state.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_array.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_array.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_internal.cc" "$VENDOR_DIR/src/emc/rs274ngc/interp_internal.cc"
@@ -694,6 +694,10 @@ REMAP_PARSE_SOURCES=(
"$WRAP_DIR/linuxcnc_tool_adapter.cpp" "$WRAP_DIR/linuxcnc_tool_adapter.cpp"
"$WRAP_DIR/linuxcnc_interp_minimal_runtime.cpp" "$WRAP_DIR/linuxcnc_interp_minimal_runtime.cpp"
"$VENDOR_DIR/src/emc/ini/inifile.cc" "$VENDOR_DIR/src/emc/ini/inifile.cc"
)
REMAP_PARSE_SOURCES=(
"${REMAP_RUNTIME_CORE_SOURCES[@]}"
"$WRAP_DIR/linuxcnc_remap_parse_harness.cpp" "$WRAP_DIR/linuxcnc_remap_parse_harness.cpp"
) )
@@ -702,6 +706,11 @@ REMAP_HAL_SYNC_SOURCES=(
"$WRAP_DIR/linuxcnc_remap_hal_sync_harness.cpp" "$WRAP_DIR/linuxcnc_remap_hal_sync_harness.cpp"
) )
FIVEAXIS_REMAP_EXECUTE_SOURCES=(
"${REMAP_RUNTIME_CORE_SOURCES[@]}"
"$WRAP_DIR/linuxcnc_5axis_remap_execute_harness.cpp"
)
build_binary_target \ build_binary_target \
linuxcnc_ini_probe \ linuxcnc_ini_probe \
"$BUILD_DIR/linuxcnc_ini_probe" \ "$BUILD_DIR/linuxcnc_ini_probe" \
@@ -1309,6 +1318,28 @@ else
"$BUILD_DIR/linuxcnc_remap_hal_sync_harness.run.stderr.log" "$BUILD_DIR/linuxcnc_remap_hal_sync_harness.run.stderr.log"
fi fi
build_binary_target \
linuxcnc_5axis_remap_execute_harness \
"$BUILD_DIR/linuxcnc_5axis_remap_execute_harness" \
REMAP_RUNTIME_FLAGS \
FIVEAXIS_REMAP_EXECUTE_SOURCES \
MINIMAL_LINK_FLAGS
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.exitcode")" == "0" ]]; then
set +e
"$BUILD_DIR/linuxcnc_5axis_remap_execute_harness" \
>"$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.run.stdout.log" \
2>"$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.run.stderr.log"
FIVEAXIS_REMAP_EXECUTE_RUN_RC=$?
set -e
echo "$FIVEAXIS_REMAP_EXECUTE_RUN_RC" > "$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.run.exitcode"
else
rm -f \
"$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.run.exitcode" \
"$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.run.stdout.log" \
"$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.run.stderr.log"
fi
build_binary_target \ build_binary_target \
linuxcnc_indexer_harness \ linuxcnc_indexer_harness \
"$BUILD_DIR/linuxcnc_indexer_harness" \ "$BUILD_DIR/linuxcnc_indexer_harness" \