wasm-port: broaden native LinuxCNC interpreter probes

This commit is contained in:
2026-06-07 05:49:34 +08:00
parent 5025b0c1a1
commit eb261462c1
49 changed files with 1276 additions and 798 deletions

View File

@@ -13,6 +13,9 @@ This workspace is separate from the upstream LinuxCNC tree in
Build a standalone CNC simulation system that: Build a standalone CNC simulation system that:
- reuses LinuxCNC source as the semantic source of truth; - reuses LinuxCNC source as the semantic source of truth;
- derives the CNC simulation program primarily from the LinuxCNC source
program and strictly follows LinuxCNC behavior, structure, and semantics
unless a documented standalone runtime boundary requires adaptation;
- compiles core CNC logic to WASM; - compiles core CNC logic to WASM;
- uses HTML + JavaScript for the frontend; - uses HTML + JavaScript for the frontend;
- uses OPFS for browser persistence; - uses OPFS for browser persistence;
@@ -44,24 +47,28 @@ expanded into a separate implementation of G-code behavior.
## Engineering Rules ## Engineering Rules
1. Reuse LinuxCNC source before reimplementing any CNC logic. 1. Reuse LinuxCNC source before reimplementing any CNC logic.
2. Do not add new project-authored CNC semantics when LinuxCNC source exists. 2. Treat the LinuxCNC source program as the primary implementation source for
the CNC simulation program. Standalone code must strictly follow vendored
LinuxCNC behavior and may only adapt runtime edges such as filesystem, HAL,
IPC, process model, and browser integration.
3. Do not add new project-authored CNC semantics when LinuxCNC source exists.
Replace temporary wrapper behavior with direct calls into vendored LinuxCNC Replace temporary wrapper behavior with direct calls into vendored LinuxCNC
source, or with the narrowest shims needed to make those calls compile. source, or with the narrowest shims needed to make those calls compile.
3. Prefer wrappers, shims, and extraction scripts over invasive source edits. 4. Prefer wrappers, shims, and extraction scripts over invasive source edits.
4. Preserve LinuxCNC semantics for: 5. Preserve LinuxCNC semantics for:
- G-code execution; - G-code execution;
- modal state; - modal state;
- parameter and variable behavior; - parameter and variable behavior;
- kinematics; - kinematics;
- planner behavior; - planner behavior;
- machine and controller state visible to software. - machine and controller state visible to software.
5. Replace only the native runtime edges: 6. Replace only the native runtime edges:
- file IO; - file IO;
- process model; - process model;
- HAL runtime; - HAL runtime;
- IPC; - IPC;
- GUI. - GUI.
6. Frontend code must be implemented with web technology, not migrated from 7. Frontend code must be implemented with web technology, not migrated from
native GUI code. native GUI code.
## Required Layout ## Required Layout

View File

@@ -9,6 +9,11 @@ LinuxCNC WASM Simulation Port
This skill guides work inside `wasm-port/` for building a standalone This skill guides work inside `wasm-port/` for building a standalone
LinuxCNC-based CNC simulation program. LinuxCNC-based CNC simulation program.
The CNC simulation program must be derived primarily from the LinuxCNC source
program. Work in this port must strictly follow LinuxCNC behavior, structure,
and semantics, except where a documented standalone runtime boundary requires
an adapter.
It is intended for tasks involving: It is intended for tasks involving:
- source extraction from upstream LinuxCNC; - source extraction from upstream LinuxCNC;
@@ -33,12 +38,15 @@ Use this skill when the work involves any of the following:
## Core Principles ## Core Principles
1. Upstream LinuxCNC is the semantic source of truth. 1. Upstream LinuxCNC is the semantic source of truth.
2. The standalone port is a separate program and separate workspace. 2. The CNC simulation program is primarily sourced from LinuxCNC source code;
3. Reuse comes before rewrite. port-specific code must adapt LinuxCNC to standalone native/WASM execution,
4. Native runtime dependencies are replaced at the edges, not copied whole. not replace LinuxCNC CNC behavior.
5. Machine state and controller-visible behavior are first-class compatibility 3. The standalone port is a separate program and separate workspace.
4. Reuse comes before rewrite.
5. Native runtime dependencies are replaced at the edges, not copied whole.
6. Machine state and controller-visible behavior are first-class compatibility
targets, not optional nice-to-haves. targets, not optional nice-to-haves.
6. Do not continue building a project-authored CNC program once the porting 7. Do not continue building a project-authored CNC program once the porting
harness runs; move behavior back to vendored LinuxCNC source functions. harness runs; move behavior back to vendored LinuxCNC source functions.
## Source Reuse Priorities ## Source Reuse Priorities

View File

@@ -0,0 +1,109 @@
#include "linuxcnc_hal_adapter.hh"
#include <string>
#include <unordered_map>
namespace {
struct HalEntry {
hal_type_t type = HAL_TYPE_UNINITIALIZED;
hal_data_u value{};
bool connected = true;
};
std::unordered_map<std::string, HalEntry> &pins()
{
static std::unordered_map<std::string, HalEntry> values;
return values;
}
std::unordered_map<std::string, HalEntry> &signals()
{
static std::unordered_map<std::string, HalEntry> values;
return values;
}
std::unordered_map<std::string, HalEntry> &params()
{
static std::unordered_map<std::string, HalEntry> values;
return values;
}
std::unordered_map<std::string, HalEntry> &values_for(standalone::HalValueKind kind)
{
switch (kind) {
case standalone::HalValueKind::Pin:
return pins();
case standalone::HalValueKind::Signal:
return signals();
case standalone::HalValueKind::Param:
return params();
}
return pins();
}
int lookup_hal_value(const char *name, std::unordered_map<std::string, HalEntry> &values,
hal_type_t *type, hal_data_u **ptr, bool *connected)
{
if (!name || !type || !ptr) {
return -1;
}
auto it = values.find(name);
if (it == values.end()) {
return -1;
}
*type = it->second.type;
*ptr = &it->second.value;
if (connected) {
*connected = it->second.connected;
}
return 0;
}
} // namespace
namespace standalone {
void reset_hal_adapter()
{
pins().clear();
signals().clear();
params().clear();
}
void set_hal_value(HalValueKind kind, const char *name, hal_type_t type, hal_data_u value,
bool connected)
{
values_for(kind)[name] = HalEntry{type, value, connected};
}
} // namespace standalone
int hal_init(const char *)
{
return 1;
}
int hal_ready(int)
{
return 0;
}
int hal_get_pin_value_by_name(const char *name, hal_type_t *type, hal_data_u **ptr,
bool *connected)
{
return lookup_hal_value(name, pins(), type, ptr, connected);
}
int hal_get_signal_value_by_name(const char *name, hal_type_t *type, hal_data_u **ptr,
bool *connected)
{
return lookup_hal_value(name, signals(), type, ptr, connected);
}
int hal_get_param_value_by_name(const char *name, hal_type_t *type, hal_data_u **ptr)
{
return lookup_hal_value(name, params(), type, ptr, nullptr);
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include "hal.h"
namespace standalone {
enum class HalValueKind {
Pin,
Signal,
Param,
};
void reset_hal_adapter();
void set_hal_value(HalValueKind kind, const char *name, hal_type_t type, hal_data_u value,
bool connected = true);
} // namespace standalone

View File

@@ -0,0 +1,49 @@
#include <string>
#define private public
#include "emc/rs274ngc/rs274ngc_interp.hh"
#undef private
#include "emc/rs274ngc/rs274ngc_return.hh"
#include "interp_python.hh"
#include "pythonplugin/python_plugin.hh"
PythonPlugin *python_plugin = nullptr;
int _task = 0;
struct _inittab {
const char *name;
void *initfunc;
};
_inittab builtin_modules[] = {{nullptr, nullptr}};
InterpBase::~InterpBase() {}
PythonPlugin *PythonPlugin::instantiate(struct _inittab *)
{
static PythonPlugin standalone_plugin;
python_plugin = &standalone_plugin;
return python_plugin;
}
std::string handle_pyerror()
{
return {};
}
bool Interp::is_pycallable(setup_pointer, const char *, const char *) { return false; }
bool Interp::is_user_defined_g_code(int) { return false; }
bool Interp::is_any_m_code_remapped(block_pointer, setup_pointer) { return false; }
bool Interp::is_user_defined_m_code(block_pointer, setup_pointer, int) { return false; }
bool Interp::is_m_code_remappable(int) { return false; }
bool Interp::is_g_code_remappable(int) { return false; }
bool Interp::remap_in_progress(const char *) { return false; }
remap_pointer Interp::remapping(const char *) { return nullptr; }
remap_pointer Interp::remapping(const char, int) { return nullptr; }
int Interp::parse_remap(const char *, int) { return INTERP_ERROR; }
int Interp::convert_remapped_code(block_pointer, setup_pointer, int, char, int) { return INTERP_ERROR; }
int Interp::add_parameters(setup_pointer, block_pointer, char *) { return INTERP_ERROR; }
int Interp::pycall(setup_pointer, context_pointer, const char *, const char *, int) { return INTERP_ERROR; }
int Interp::py_execute(const char *, bool) { return INTERP_ERROR; }
int Interp::py_reload() { return INTERP_OK; }

View File

@@ -9,11 +9,67 @@
#include <vector> #include <vector>
#include "canon_event_sink.hh" #include "canon_event_sink.hh"
#include "linuxcnc_hal_adapter.hh"
#include "linuxcnc_tool_adapter.hh"
namespace { namespace {
void seed_hal_values()
{
standalone::reset_hal_adapter();
hal_data_u pin_bit{};
pin_bit.b = true;
standalone::set_hal_value(standalone::HalValueKind::Pin, "standalone.pin-bit", HAL_BIT,
pin_bit);
hal_data_u signal_float{};
signal_float.f = 98.25;
standalone::set_hal_value(standalone::HalValueKind::Signal, "standalone.signal-float",
HAL_FLOAT, signal_float);
hal_data_u param_s32{};
param_s32.s = -17;
standalone::set_hal_value(standalone::HalValueKind::Param, "standalone.param-s32", HAL_S32,
param_s32);
hal_data_u pin_u32{};
pin_u32.u = 123456789u;
standalone::set_hal_value(standalone::HalValueKind::Pin, "standalone.pin-u32", HAL_U32,
pin_u32);
hal_data_u signal_s64{};
signal_s64.ls = -9000000000LL;
standalone::set_hal_value(standalone::HalValueKind::Signal, "standalone.signal-s64", HAL_S64,
signal_s64);
hal_data_u param_u64{};
param_u64.lu = 9000000000ULL;
standalone::set_hal_value(standalone::HalValueKind::Param, "standalone.param-u64", HAL_U64,
param_u64);
hal_data_u disconnected{};
disconnected.f = 12.5;
standalone::set_hal_value(standalone::HalValueKind::Pin, "standalone.disconnected-float",
HAL_FLOAT, disconnected, false);
}
void seed_tool_values()
{
standalone::reset_tool_adapter();
CANON_TOOL_TABLE tool = standalone::tool_entry_init();
tool.toolno = 2;
tool.pocketno = 2;
tool.offset.tran.z = 1.25;
tool.diameter = 0.25;
standalone::set_tool_entry(2, tool);
}
void initialize_minimal_interp(Interp &interp) void initialize_minimal_interp(Interp &interp)
{ {
seed_hal_values();
seed_tool_values();
interp._setup.length_units = CANON_UNITS_MM; interp._setup.length_units = CANON_UNITS_MM;
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE; interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
interp._setup.ijk_distance_mode = DISTANCE_MODE::ABSOLUTE; interp._setup.ijk_distance_mode = DISTANCE_MODE::ABSOLUTE;
@@ -23,6 +79,10 @@ void initialize_minimal_interp(Interp &interp)
interp._setup.percent_flag = false; interp._setup.percent_flag = false;
interp._setup.sequence_number = 0; interp._setup.sequence_number = 0;
interp._setup.parameter_occurrence = 0; interp._setup.parameter_occurrence = 0;
interp._setup.num_spindles = 1;
interp._setup.feature_set = FEATURE_INI_VARS | FEATURE_HAL_PIN_VARS;
interp._setup.parameters[5599] = 1.0;
interp.load_tool_table();
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers)); std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
} }
@@ -80,6 +140,51 @@ int main(int argc, char **argv)
interp._setup.sequence_number = static_cast<int>(idx + 1); interp._setup.sequence_number = static_cast<int>(idx + 1);
const int execute_rc = interp.execute(program[idx].c_str()); const int execute_rc = interp.execute(program[idx].c_str());
std::cout << "execute_line_" << (idx + 1) << "=" << execute_rc << "\n"; std::cout << "execute_line_" << (idx + 1) << "=" << execute_rc << "\n";
if (execute_rc > INTERP_MIN_ERROR) {
char error_buf[LINELEN] = {0};
interp.error_text(execute_rc, error_buf, sizeof(error_buf));
std::cout << "error_text=" << error_buf << "\n";
}
}
int file_open_rc = INTERP_FILE_NOT_OPEN;
int file_read_count = 0;
int file_execute_count = 0;
if (argc > 1) {
Interp file_interp;
initialize_minimal_interp(file_interp);
file_open_rc = file_interp.open(argv[1]);
if (file_open_rc == INTERP_OK) {
while (true) {
const int file_read_rc = file_interp.read();
if (file_read_rc == INTERP_ENDFILE) {
std::cout << "file_read_eof=" << file_read_rc << "\n";
break;
}
++file_read_count;
std::cout << "file_read_" << file_read_count << "=" << file_read_rc << "\n";
if ((file_read_rc != INTERP_OK) && (file_read_rc != INTERP_EXECUTE_FINISH)) {
break;
}
const int file_execute_rc = file_interp.execute();
++file_execute_count;
std::cout << "file_execute_" << file_execute_count << "=" << file_execute_rc << "\n";
if (file_execute_rc > INTERP_MIN_ERROR) {
char error_buf[LINELEN] = {0};
file_interp.error_text(file_execute_rc, error_buf, sizeof(error_buf));
std::cout << "file_error_text=" << error_buf << "\n";
}
if ((file_execute_rc != INTERP_OK) &&
(file_execute_rc != INTERP_EXECUTE_FINISH) &&
(file_execute_rc != INTERP_EXIT)) {
break;
}
if (file_execute_rc == INTERP_EXIT) {
break;
}
}
}
} }
block block{}; block block{};
@@ -90,6 +195,9 @@ int main(int argc, char **argv)
} }
std::cout << "read_text=" << read_text_rc << "\n"; std::cout << "read_text=" << read_text_rc << "\n";
std::cout << "file_open=" << file_open_rc << "\n";
std::cout << "file_read_count=" << file_read_count << "\n";
std::cout << "file_execute_count=" << file_execute_count << "\n";
std::cout << "raw_line=" << raw_line << "\n"; std::cout << "raw_line=" << raw_line << "\n";
std::cout << "cooked_line=" << cooked_line << "\n"; std::cout << "cooked_line=" << cooked_line << "\n";
std::cout << "line_length=" << length << "\n"; std::cout << "line_length=" << length << "\n";

View File

@@ -12,8 +12,7 @@
#include "emc/rs274ngc/rs274ngc_return.hh" #include "emc/rs274ngc/rs274ngc_return.hh"
#include "canon_event_sink.hh" #include "canon_event_sink.hh"
#include "linuxcnc_tool_adapter.hh"
PythonPlugin *python_plugin = nullptr;
namespace standalone { namespace standalone {
@@ -71,13 +70,45 @@ void STOP_SPEED_FEED_SYNCH() {}
void RIGID_TAP(int, double, double, double, double) {} void RIGID_TAP(int, double, double, double, double) {}
void STRAIGHT_PROBE(int, double, double, double, double, double, double, double, double, double, unsigned char) {} void STRAIGHT_PROBE(int, double, double, double, double, double, double, double, double, double, unsigned char) {}
void STOP() {} void STOP() {}
void DWELL(double) {} void DWELL(double seconds)
void SET_SPINDLE_MODE(int, double) {} {
std::ostringstream oss;
oss << "DWELL seconds=" << seconds;
standalone::push_canon_event(oss.str());
}
void SET_SPINDLE_MODE(int spindle, double css_max)
{
std::ostringstream oss;
oss << "SET_SPINDLE_MODE spindle=" << spindle << " css_max=" << css_max;
standalone::push_canon_event(oss.str());
}
void SPINDLE_RETRACT_TRAVERSE() {} void SPINDLE_RETRACT_TRAVERSE() {}
void START_SPINDLE_CLOCKWISE(int, int) {} void START_SPINDLE_CLOCKWISE(int spindle, int wait_for_at_speed)
void START_SPINDLE_COUNTERCLOCKWISE(int, int) {} {
void SET_SPINDLE_SPEED(int, double) {} std::ostringstream oss;
void STOP_SPINDLE_TURNING(int) {} oss << "START_SPINDLE_CLOCKWISE spindle=" << spindle
<< " wait_for_at_speed=" << wait_for_at_speed;
standalone::push_canon_event(oss.str());
}
void START_SPINDLE_COUNTERCLOCKWISE(int spindle, int wait_for_at_speed)
{
std::ostringstream oss;
oss << "START_SPINDLE_COUNTERCLOCKWISE spindle=" << spindle
<< " wait_for_at_speed=" << wait_for_at_speed;
standalone::push_canon_event(oss.str());
}
void SET_SPINDLE_SPEED(int spindle, double speed)
{
std::ostringstream oss;
oss << "SET_SPINDLE_SPEED spindle=" << spindle << " speed=" << speed;
standalone::push_canon_event(oss.str());
}
void STOP_SPINDLE_TURNING(int spindle)
{
std::ostringstream oss;
oss << "STOP_SPINDLE_TURNING spindle=" << spindle;
standalone::push_canon_event(oss.str());
}
void SPINDLE_RETRACT() {} void SPINDLE_RETRACT() {}
void ORIENT_SPINDLE(int, double, int) {} void ORIENT_SPINDLE(int, double, int) {}
void WAIT_SPINDLE_ORIENT_COMPLETE(int, double) {} void WAIT_SPINDLE_ORIENT_COMPLETE(int, double) {}
@@ -85,10 +116,40 @@ void LOCK_SPINDLE_Z() {}
void USE_SPINDLE_FORCE() {} void USE_SPINDLE_FORCE() {}
void USE_NO_SPINDLE_FORCE() {} void USE_NO_SPINDLE_FORCE() {}
void SET_TOOL_TABLE_ENTRY(int, int, const EmcPose &, double, double, double, int) {} void SET_TOOL_TABLE_ENTRY(int, int, const EmcPose &, double, double, double, int) {}
void USE_TOOL_LENGTH_OFFSET(const EmcPose &) {} void USE_TOOL_LENGTH_OFFSET(const EmcPose &offset)
void CHANGE_TOOL() {} {
void SELECT_TOOL(int) {} std::ostringstream oss;
void CHANGE_TOOL_NUMBER(int) {} oss << "USE_TOOL_LENGTH_OFFSET"
<< " x=" << offset.tran.x
<< " y=" << offset.tran.y
<< " z=" << offset.tran.z
<< " a=" << offset.a
<< " b=" << offset.b
<< " c=" << offset.c
<< " u=" << offset.u
<< " v=" << offset.v
<< " w=" << offset.w;
standalone::push_canon_event(oss.str());
}
void CHANGE_TOOL()
{
standalone::change_selected_tool();
standalone::push_canon_event("CHANGE_TOOL");
}
void SELECT_TOOL(int tool)
{
standalone::select_tool(tool);
std::ostringstream oss;
oss << "SELECT_TOOL tool=" << tool;
standalone::push_canon_event(oss.str());
}
void CHANGE_TOOL_NUMBER(int pocket)
{
standalone::change_tool_number(pocket);
std::ostringstream oss;
oss << "CHANGE_TOOL_NUMBER pocket=" << pocket;
standalone::push_canon_event(oss.str());
}
void RELOAD_TOOLDATA(void) {} void RELOAD_TOOLDATA(void) {}
void CLAMP_AXIS(CANON_AXIS) {} void CLAMP_AXIS(CANON_AXIS) {}
void DISABLE_ADAPTIVE_FEED() {} void DISABLE_ADAPTIVE_FEED() {}
@@ -99,10 +160,10 @@ void DISABLE_SPEED_OVERRIDE(int) {}
void ENABLE_SPEED_OVERRIDE(int) {} void ENABLE_SPEED_OVERRIDE(int) {}
void DISABLE_FEED_HOLD() {} void DISABLE_FEED_HOLD() {}
void ENABLE_FEED_HOLD() {} void ENABLE_FEED_HOLD() {}
void FLOOD_OFF() {} void FLOOD_OFF() { standalone::push_canon_event("FLOOD_OFF"); }
void FLOOD_ON() {} void FLOOD_ON() { standalone::push_canon_event("FLOOD_ON"); }
void MIST_OFF() {} void MIST_OFF() { standalone::push_canon_event("MIST_OFF"); }
void MIST_ON() {} void MIST_ON() { standalone::push_canon_event("MIST_ON"); }
void PALLET_SHUTTLE() {} void PALLET_SHUTTLE() {}
void TURN_PROBE_OFF() {} void TURN_PROBE_OFF() {}
void TURN_PROBE_ON() {} void TURN_PROBE_ON() {}
@@ -144,7 +205,8 @@ void CANON_ERROR(const char *fmt, ...)
double GET_EXTERNAL_FEED_RATE() { return 0.0; } double GET_EXTERNAL_FEED_RATE() { return 0.0; }
int GET_EXTERNAL_FLOOD() { return 0; } int GET_EXTERNAL_FLOOD() { return 0; }
double GET_EXTERNAL_LENGTH_UNITS() { return CANON_UNITS_MM; } CANON_UNITS GET_EXTERNAL_LENGTH_UNIT_TYPE() { return CANON_UNITS_MM; }
double GET_EXTERNAL_LENGTH_UNITS() { return 1.0; }
double GET_EXTERNAL_ANGLE_UNITS() { return 1.0; } double GET_EXTERNAL_ANGLE_UNITS() { return 1.0; }
int GET_EXTERNAL_MIST() { return 0; } int GET_EXTERNAL_MIST() { return 0; }
CANON_MOTION_MODE GET_EXTERNAL_MOTION_CONTROL_MODE() { return CANON_EXACT_STOP; } CANON_MOTION_MODE GET_EXTERNAL_MOTION_CONTROL_MODE() { return CANON_EXACT_STOP; }
@@ -198,7 +260,12 @@ double GET_EXTERNAL_TOOL_LENGTH_VOFFSET() { return 0.0; }
double GET_EXTERNAL_TOOL_LENGTH_WOFFSET() { return 0.0; } double GET_EXTERNAL_TOOL_LENGTH_WOFFSET() { return 0.0; }
int GET_EXTERNAL_TOOL_SLOT() { return 0; } int GET_EXTERNAL_TOOL_SLOT() { return 0; }
int GET_EXTERNAL_SELECTED_TOOL_SLOT() { return -1; } int GET_EXTERNAL_SELECTED_TOOL_SLOT() { return -1; }
CANON_TOOL_TABLE GET_EXTERNAL_TOOL_TABLE(int) { return tooldata_entry_init(); } CANON_TOOL_TABLE GET_EXTERNAL_TOOL_TABLE(int index)
{
CANON_TOOL_TABLE tool = standalone::tool_entry_init();
standalone::get_tool_entry(&tool, index);
return tool;
}
int GET_EXTERNAL_TC_FAULT() { return 0; } int GET_EXTERNAL_TC_FAULT() { return 0; }
int GET_EXTERNAL_TC_REASON() { return 0; } int GET_EXTERNAL_TC_REASON() { return 0; }
double GET_EXTERNAL_TRAVERSE_RATE() { return 0.0; } double GET_EXTERNAL_TRAVERSE_RATE() { return 0.0; }
@@ -288,277 +355,3 @@ void SET_FEED_RATE(double rate)
oss << "SET_FEED_RATE rate=" << rate; oss << "SET_FEED_RATE rate=" << rate;
standalone::push_canon_event(oss.str()); standalone::push_canon_event(oss.str());
} }
InterpBase::~InterpBase() {}
Interp::Interp()
: log_file(stderr),
_setup{}
{
_setup.init_once = 1;
memset(_readers, 0, sizeof(_readers));
}
Interp::~Interp() {
if (log_file && log_file != stderr) {
fclose(log_file);
}
log_file = nullptr;
}
void Interp::doLog(unsigned int, const char *, int, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
const char *Interp::interp_status(int status)
{
static char statustext[64];
snprintf(statustext, sizeof(statustext), "%d", status);
return statustext;
}
const char *Interp::getSavedError()
{
return "";
}
int Interp::setSavedError(const char *)
{
return INTERP_OK;
}
void Interp::setError(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fputc('\n', stderr);
va_end(ap);
}
int Interp::unwind_call(int status, const char *, int, const char *)
{
return status;
}
int Interp::close() { return INTERP_OK; }
int Interp::execute(const char *command)
{
int status = read(command);
if (status != INTERP_OK) {
if (status > INTERP_MIN_ERROR) {
unwind_call(status, __FILE__, __LINE__, __FUNCTION__);
}
return status;
}
if (_setup.line_length != 0) {
status = execute_block(&EXECUTING_BLOCK(_setup), &_setup);
if (status > INTERP_MIN_ERROR) {
unwind_call(status, __FILE__, __LINE__, __FUNCTION__);
}
return status;
}
return INTERP_OK;
}
int Interp::execute() { return execute(nullptr); }
int Interp::execute(const char *command, int line_number)
{
if (command && line_number) {
_setup.sequence_number = line_number;
}
int status = execute(command);
if ((_setup.call_level == 0) &&
(status == INTERP_EXECUTE_FINISH) &&
(_setup.mdi_interrupt)) {
_setup.mdi_interrupt = false;
}
return status;
}
int Interp::exit() { return INTERP_OK; }
int Interp::init() { return INTERP_OK; }
void Interp::set_loop_on_main_m99(bool state) { _setup.loop_on_main_m99 = state; }
int Interp::open(const char *) { return INTERP_ERROR; }
int Interp::read_inputs(setup_pointer)
{
return INTERP_OK;
}
int Interp::_read(const char *command)
{
int read_status = INTERP_OK;
block_pointer eblock = &EXECUTING_BLOCK(_setup);
if ((_setup.call_state > CS_NORMAL) &&
(eblock->call_type != CT_NGC_OWORD_SUB) &&
(eblock->call_type != CT_NGC_M98_SUB) &&
(eblock->call_type != CT_NONE) &&
((eblock->o_type == O_call) ||
(eblock->o_type == M_98) ||
(eblock->o_type == O_return) ||
(eblock->o_type == O_endsub) ||
(eblock->o_type == M_99))) {
_setup.line_length = 0;
_setup.linetext[0] = 0;
return INTERP_OK;
}
_setup.call_state = CS_NORMAL;
if (read_inputs(&_setup) != INTERP_OK) {
return INTERP_ERROR;
}
if ((command == nullptr) && (_setup.file_pointer == nullptr)) {
return INTERP_FILE_NOT_OPEN;
}
_setup.parameters[5420] = _setup.current_x;
_setup.parameters[5421] = _setup.current_y;
_setup.parameters[5422] = _setup.current_z;
_setup.parameters[5423] = _setup.AA_current;
_setup.parameters[5424] = _setup.BB_current;
_setup.parameters[5425] = _setup.CC_current;
_setup.parameters[5426] = _setup.u_current;
_setup.parameters[5427] = _setup.v_current;
_setup.parameters[5428] = _setup.w_current;
if (_setup.file_pointer) {
EXECUTING_BLOCK(_setup).offset = ftell(_setup.file_pointer);
}
read_status = read_text(command, _setup.file_pointer, _setup.linetext,
_setup.blocktext, &_setup.line_length);
if ((read_status == INTERP_EXECUTE_FINISH) || (read_status == INTERP_OK)) {
if (_setup.line_length != 0) {
if (parse_line(_setup.blocktext, &(EXECUTING_BLOCK(_setup)), &_setup) != INTERP_OK) {
return INTERP_ERROR;
}
} else {
if (EXECUTING_BLOCK(_setup).o_type != O_none) {
EXECUTING_BLOCK(_setup).o_type = 0;
}
}
} else if (read_status == INTERP_ENDFILE) {
if (_setup.skipping_o != nullptr) {
return INTERP_ERROR;
}
} else {
return read_status;
}
return read_status;
}
int Interp::read(const char *command)
{
int status = _read(command);
if (status > INTERP_MIN_ERROR) {
unwind_call(status, __FILE__, __LINE__, __FUNCTION__);
}
return status;
}
int Interp::read()
{
return read(nullptr);
}
int Interp::reset() { return INTERP_OK; }
int Interp::synch() { return INTERP_OK; }
void Interp::active_g_codes(int *codes) { std::memcpy(codes, _setup.active_g_codes, sizeof(_setup.active_g_codes)); }
void Interp::active_m_codes(int *codes) { std::memcpy(codes, _setup.active_m_codes, sizeof(_setup.active_m_codes)); }
void Interp::active_settings(double *settings) { std::memcpy(settings, _setup.active_settings, sizeof(_setup.active_settings)); }
int Interp::active_modes(int *, int *, double *, StateTag const &) { return INTERP_ERROR; }
void Interp::print_state_tag(StateTag const &) {}
char *Interp::error_text(int, char *buf, size_t max_size)
{
if (max_size) buf[0] = '\0';
return buf;
}
char *Interp::file_name(char *buf, size_t max_size)
{
if (max_size) {
std::strncpy(buf, _setup.filename, max_size - 1);
buf[max_size - 1] = '\0';
}
return buf;
}
size_t Interp::line_length() { return static_cast<size_t>(_setup.line_length); }
char *Interp::line_text(char *buf, size_t max_size)
{
if (max_size) {
std::strncpy(buf, _setup.linetext, max_size - 1);
buf[max_size - 1] = '\0';
}
return buf;
}
int Interp::sequence_number() { return _setup.sequence_number; }
char *Interp::stack_name(int idx, char *buf, size_t max_size)
{
if (!max_size) return buf;
if (idx < 0 || idx >= STACK_LEN) {
buf[0] = '\0';
return buf;
}
std::strncpy(buf, _setup.stack[idx], max_size - 1);
buf[max_size - 1] = '\0';
return buf;
}
int Interp::ini_load(const char *) { return INTERP_OK; }
int Interp::on_abort(int, const char *) { return INTERP_OK; }
void Interp::set_loglevel(int level) { _setup.loggingLevel = level; }
int Interp::find_remappings(block_pointer, setup_pointer) { return 0; }
int Interp::load_tool_table() { return INTERP_OK; }
int Interp::init_tool_parameters() { return INTERP_OK; }
int Interp::default_tool_parameters() { return INTERP_OK; }
int Interp::set_tool_parameters() { return INTERP_OK; }
bool Interp::is_pycallable(setup_pointer, const char *, const char *) { return false; }
bool Interp::is_user_defined_g_code(int) { return false; }
bool Interp::is_any_m_code_remapped(block_pointer, setup_pointer) { return false; }
bool Interp::is_user_defined_m_code(block_pointer, setup_pointer, int) { return false; }
void Interp::loop_to_beginning(setup_pointer) {}
int Interp::convert_control_functions(block_pointer, setup_pointer) { return INTERP_OK; }
int Interp::convert_remapped_code(block_pointer, setup_pointer, int, char, int) { return INTERP_ERROR; }
int Interp::py_execute(const char *, bool) { return INTERP_OK; }
int Interp::py_reload() { return INTERP_OK; }
const char *o_ops[] = {
"O_none", "O_sub", "O_endsub", "O_call", "O_do", "O_while", "O_if",
"O_elseif", "O_else", "O_endif", "O_break", "O_continue",
"O_endwhile", "O_return", "O_repeat", "O_endrepeat", "M_98", "M_99", "O_"
};
int Interp::read_named_parameter(char *, int *, double *double_ptr, double *, bool check_exists)
{
if (check_exists) {
*double_ptr = 0.0;
}
return INTERP_ERROR;
}
int Interp::find_named_param(const char *, int *status, double *value)
{
*status = 0;
*value = 0.0;
return INTERP_OK;
}
int Interp::store_named_param(setup_pointer, const char *, double, int)
{
return INTERP_ERROR;
}
int Interp::add_named_param(const char *, int)
{
return INTERP_OK;
}

View File

@@ -4,338 +4,104 @@
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
#include <string>
#include "config.h" #include "linuxcnc_hal_adapter.hh"
#include "emc/ini/inifile.hh"
namespace { namespace {
enum predefined_named_parameters { void seed_hal_values()
NP_LINE, {
NP_MOTION_MODE, standalone::reset_hal_adapter();
NP_PLANE,
NP_CCOMP,
NP_METRIC,
NP_IMPERIAL,
NP_ABSOLUTE,
NP_INCREMENTAL,
NP_INVERSE_TIME,
NP_UNITS_PER_MINUTE,
NP_UNITS_PER_REV,
NP_COORD_SYSTEM,
NP_TOOL_OFFSET,
NP_RETRACT_R_PLANE,
NP_RETRACT_OLD_Z,
NP_SPINDLE_RPM_MODE,
NP_SPINDLE_CSS_MODE,
NP_IJK_ABSOLUTE_MODE,
NP_LATHE_DIAMETER_MODE,
NP_LATHE_RADIUS_MODE,
NP_SPINDLE_ON,
NP_SPINDLE_CW,
NP_MIST,
NP_FLOOD,
NP_SPEED_OVERRIDE,
NP_FEED_OVERRIDE,
NP_ADAPTIVE_FEED,
NP_FEED_HOLD,
NP_FEED,
NP_RPM,
NP_CURRENT_TOOL,
NP_SELECTED_POCKET,
NP_CURRENT_POCKET,
NP_X,
NP_Y,
NP_Z,
NP_A,
NP_B,
NP_C,
NP_U,
NP_V,
NP_W,
NP_ABS_X,
NP_ABS_Y,
NP_ABS_Z,
NP_ABS_A,
NP_ABS_B,
NP_ABS_C,
NP_VALUE,
NP_CALL_LEVEL,
NP_REMAP_LEVEL,
NP_SELECTED_TOOL,
NP_VALUE_RETURNED,
NP_TASK,
};
struct NamedParamRuntime { hal_data_u pin_bit{};
setup state; pin_bit.b = true;
standalone::set_hal_value(standalone::HalValueKind::Pin, "standalone.pin-bit", HAL_BIT,
pin_bit);
int fetch_ini_param(const char *nameBuf, int *status, double *value) { hal_data_u signal_float{};
*status = 0; signal_float.f = 98.25;
const int n = static_cast<int>(strlen(nameBuf)); standalone::set_hal_value(standalone::HalValueKind::Signal, "standalone.signal-float",
if (n < 8) { HAL_FLOAT, signal_float);
return INTERP_OK;
}
std::string sect = nameBuf + 5; hal_data_u param_s32{};
for (auto &c : sect) { param_s32.s = -17;
c = static_cast<char>(toupper(c)); standalone::set_hal_value(standalone::HalValueKind::Param, "standalone.param-s32", HAL_S32,
} param_s32);
const size_t i = sect.find(']');
if (i == std::string::npos) {
return INTERP_ERROR;
}
std::string var = sect.substr(i + 1);
sect.erase(i);
const char *iniFileName = getenv("INI_FILE_NAME"); hal_data_u pin_u32{};
if (!iniFileName) { pin_u32.u = 123456789u;
return INTERP_OK; standalone::set_hal_value(standalone::HalValueKind::Pin, "standalone.pin-u32", HAL_U32,
} pin_u32);
linuxcnc::IniFile inifile(iniFileName);
if (!inifile) {
return INTERP_OK;
}
if (auto inival = inifile.findReal(var, sect)) { hal_data_u signal_s64{};
*value = *inival; signal_s64.ls = -9000000000LL;
*status = 1; standalone::set_hal_value(standalone::HalValueKind::Signal, "standalone.signal-s64", HAL_S64,
} signal_s64);
return INTERP_OK;
}
int lookup_named_param(const char *nameBuf, double index, double *value) { hal_data_u param_u64{};
const int cmd = round_to_int(index); param_u64.lu = 9000000000ULL;
switch (cmd) { standalone::set_hal_value(standalone::HalValueKind::Param, "standalone.param-u64", HAL_U64,
case NP_LINE: param_u64);
*value = state.sequence_number;
break;
case NP_MOTION_MODE:
*value = state.motion_mode;
break;
case NP_METRIC:
*value = (state.length_units == CANON_UNITS_MM);
break;
case NP_IMPERIAL:
*value = (state.length_units == CANON_UNITS_INCHES);
break;
case NP_ABSOLUTE:
*value = (state.distance_mode == DISTANCE_MODE::ABSOLUTE);
break;
case NP_INCREMENTAL:
*value = (state.distance_mode == DISTANCE_MODE::INCREMENTAL);
break;
case NP_FEED:
*value = state.feed_rate;
break;
case NP_RPM:
*value = state.speed[0];
break;
case NP_X:
*value = state.current_x;
break;
case NP_Y:
*value = state.current_y;
break;
case NP_Z:
*value = state.current_z;
break;
case NP_CURRENT_TOOL:
*value = state.parameters[interp_param_global::TOOL_NUMBER];
break;
case NP_CALL_LEVEL:
*value = state.call_level;
break;
default:
return INTERP_ERROR;
}
return INTERP_OK;
}
int find_named_param(const char *nameBuf, int *status, double *value) { hal_data_u disconnected{};
const int level = (nameBuf[0] == '_') ? 0 : state.call_level; disconnected.f = 12.5;
context_pointer frame = &state.sub_context[level]; standalone::set_hal_value(standalone::HalValueKind::Pin, "standalone.disconnected-float",
*status = 0; HAL_FLOAT, disconnected, false);
}
auto pi = frame->named_params.find(nameBuf); void print_named_value(Interp &interp, const char *name)
if (pi == frame->named_params.end()) { {
int exists = 0;
double inivalue = 0.0;
if ((state.feature_set & FEATURE_INI_VARS) && (strncasecmp(nameBuf, "_ini[", 5) == 0)) {
fetch_ini_param(nameBuf, &exists, &inivalue);
if (exists) {
*value = inivalue;
*status = 1;
parameter_value param;
param.value = inivalue;
param.attr = PA_GLOBAL | PA_READONLY | PA_FROM_INI;
state.sub_context[0].named_params[strstore(nameBuf)] = param;
return INTERP_OK;
}
}
*value = 0.0;
*status = 0;
} else {
parameter_pointer pv = &pi->second;
if (pv->attr & PA_USE_LOOKUP) {
if (lookup_named_param(nameBuf, pv->value, value) != INTERP_OK) {
return INTERP_ERROR;
}
*status = 1;
} else {
*value = pv->value;
*status = 1;
}
}
return INTERP_OK;
}
int store_named_param(const char *nameBuf, double value, bool override_readonly) {
const int level = (nameBuf[0] == '_') ? 0 : state.call_level;
context_pointer frame = &state.sub_context[level];
auto pi = frame->named_params.find(nameBuf);
if (pi == frame->named_params.end()) {
return INTERP_ERROR;
}
parameter_pointer pv = &pi->second;
if ((pv->attr & PA_READONLY) && !override_readonly) {
return INTERP_ERROR;
}
pv->value = value;
pv->attr &= ~PA_UNSET;
return INTERP_OK;
}
int add_named_param(const char *nameBuf, int attr) {
int findStatus = 0;
double value = 0.0;
find_named_param(nameBuf, &findStatus, &value);
if (findStatus) {
return INTERP_OK;
}
int level = 0;
if (nameBuf[0] != '_') {
level = state.call_level;
} else {
level = 0;
attr |= PA_GLOBAL;
}
attr |= PA_UNSET;
parameter_value param;
param.value = 0.0;
param.attr = attr;
state.sub_context[level].named_params[strstore(nameBuf)] = param;
return INTERP_OK;
}
int init_readonly_param(const char *nameBuf, double value, int attr) {
if (add_named_param(nameBuf, PA_READONLY | attr) != INTERP_OK) {
return INTERP_ERROR;
}
if (store_named_param(nameBuf, value, true) != INTERP_OK) {
return INTERP_ERROR;
}
return INTERP_OK;
}
double inicheck() {
const char *filename = getenv("INI_FILE_NAME");
if (!filename) {
return -1.0;
}
linuxcnc::IniFile inifile(filename);
if (!inifile) {
return -1.0;
}
if (auto inistring = inifile.findString("LINEAR_UNITS", "TRAJ")) {
if ((strcasecmp("mm", inistring->c_str()) == 0) ||
(strcasecmp("metric", inistring->c_str()) == 0)) {
return 1.0;
}
return 0.0;
}
return -1.0;
}
int init_named_parameters() {
const char *pkgversion = PACKAGE_VERSION;
const char *version_major = "_vmajor";
const char *version_minor = "_vminor";
const char *metric_machine = "_metric_machine";
double vmajor = 0.0;
double vminor = 0.0;
double munits = 1.0;
sscanf(pkgversion, "%lf%lf", &vmajor, &vminor);
if (init_readonly_param(version_major, vmajor, 0) != INTERP_OK) return INTERP_ERROR;
if (init_readonly_param(version_minor, vminor, 0) != INTERP_OK) return INTERP_ERROR;
munits = inicheck();
if (init_readonly_param(metric_machine, munits, 0) != INTERP_OK) return INTERP_ERROR;
if (init_readonly_param("_line", NP_LINE, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
if (init_readonly_param("_motion_mode", NP_MOTION_MODE, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
if (init_readonly_param("_metric", NP_METRIC, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
if (init_readonly_param("_imperial", NP_IMPERIAL, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
if (init_readonly_param("_absolute", NP_ABSOLUTE, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
if (init_readonly_param("_incremental", NP_INCREMENTAL, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
if (init_readonly_param("_feed", NP_FEED, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
if (init_readonly_param("_rpm", NP_RPM, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
if (init_readonly_param("_x", NP_X, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
if (init_readonly_param("_y", NP_Y, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
if (init_readonly_param("_z", NP_Z, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
if (init_readonly_param("_current_tool", NP_CURRENT_TOOL, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
if (init_readonly_param("_call_level", NP_CALL_LEVEL, PA_USE_LOOKUP) != INTERP_OK) return INTERP_ERROR;
return INTERP_OK;
}
};
void print_named_value(NamedParamRuntime &runtime, const char *name) {
int status = 0; int status = 0;
double value = 0.0; double value = 0.0;
const int rc = runtime.find_named_param(name, &status, &value); const int rc = interp.find_named_param(name, &status, &value);
std::cout << name << ": rc=" << rc << " found=" << status << " value=" << value << "\n"; std::cout << name << ": rc=" << rc << " found=" << status << " value=" << value << "\n";
} }
} // namespace } // namespace
int main(int argc, char **argv) { int main(int argc, char **argv)
{
if (argc == 2) { if (argc == 2) {
setenv("INI_FILE_NAME", argv[1], 1); setenv("INI_FILE_NAME", argv[1], 1);
} }
NamedParamRuntime runtime; Interp interp;
runtime.state.feature_set = FEATURE_INI_VARS; seed_hal_values();
runtime.state.length_units = CANON_UNITS_MM; interp._setup.feature_set = FEATURE_INI_VARS | FEATURE_HAL_PIN_VARS;
runtime.state.distance_mode = DISTANCE_MODE::ABSOLUTE; interp._setup.length_units = CANON_UNITS_MM;
runtime.state.motion_mode = G_1; interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
runtime.state.feed_rate = 123.45; interp._setup.motion_mode = G_1;
runtime.state.speed[0] = 678.9; interp._setup.feed_rate = 123.45;
runtime.state.current_x = 1.25; interp._setup.speed[0] = 678.9;
runtime.state.current_y = 2.5; interp._setup.current_x = 1.25;
runtime.state.current_z = 3.75; interp._setup.current_y = 2.5;
runtime.state.parameters[interp_param_global::TOOL_NUMBER] = 12.0; interp._setup.current_z = 3.75;
interp._setup.parameters[interp_param_global::TOOL_NUMBER] = 12.0;
const int rc = runtime.init_named_parameters(); const int rc = interp.init_named_parameters();
std::cout << "init_named_parameters=" << rc << "\n"; std::cout << "init_named_parameters=" << rc << "\n";
std::cout << "global_named_count=" << runtime.state.sub_context[0].named_params.size() << "\n"; std::cout << "global_named_count=" << interp._setup.sub_context[0].named_params.size() << "\n";
std::cout << "required_parameter_first=" << interp_param_global::G28_X << "\n"; std::cout << "required_parameter_first=" << interp_param_global::G28_X << "\n";
std::cout << "readonly_tool_number_index=" << interp_param_global::TOOL_NUMBER << "\n"; std::cout << "readonly_tool_number_index=" << interp_param_global::TOOL_NUMBER << "\n";
print_named_value(runtime, "_vmajor"); print_named_value(interp, "_vmajor");
print_named_value(runtime, "_vminor"); print_named_value(interp, "_vminor");
print_named_value(runtime, "_metric_machine"); print_named_value(interp, "_metric_machine");
print_named_value(runtime, "_motion_mode"); print_named_value(interp, "_motion_mode");
print_named_value(runtime, "_metric"); print_named_value(interp, "_metric");
print_named_value(runtime, "_feed"); print_named_value(interp, "_feed");
print_named_value(runtime, "_rpm"); print_named_value(interp, "_rpm");
print_named_value(runtime, "_x"); print_named_value(interp, "_x");
print_named_value(runtime, "_current_tool"); print_named_value(interp, "_current_tool");
print_named_value(runtime, "_ini[traj]max_linear_velocity"); print_named_value(interp, "_ini[traj]max_linear_velocity");
print_named_value(interp, "_hal[standalone.pin-bit]");
print_named_value(interp, "_hal[standalone.signal-float]");
print_named_value(interp, "_hal[standalone.param-s32]");
print_named_value(interp, "_hal[standalone.pin-u32]");
print_named_value(interp, "_hal[standalone.signal-s64]");
print_named_value(interp, "_hal[standalone.param-u64]");
print_named_value(interp, "_hal[standalone.disconnected-float]");
print_named_value(interp, "_hal[standalone.missing]");
return rc == INTERP_OK ? 0 : 1; return rc == INTERP_OK ? 0 : 1;
} }

View File

@@ -5,8 +5,7 @@
#include <unordered_set> #include <unordered_set>
#include "emc/rs274ngc/interp_internal.hh" #include "emc/rs274ngc/interp_internal.hh"
#include "interp_python.hh"
struct pycontext_impl {};
pycontext::pycontext() : impl(new pycontext_impl) {} pycontext::pycontext() : impl(new pycontext_impl) {}
pycontext::~pycontext() { delete impl; } pycontext::~pycontext() { delete impl; }
@@ -20,6 +19,7 @@ pycontext &pycontext::operator=(const pycontext &other) {
return *this; return *this;
} }
#ifndef LINUXCNC_STANDALONE_USE_RS274_PRE_STATE
const char *strstore(const char *s) const char *strstore(const char *s)
{ {
static std::unordered_set<std::string> stringtable; static std::unordered_set<std::string> stringtable;
@@ -45,6 +45,7 @@ void context_struct::clear()
{ {
new (this) context_struct(); new (this) context_struct();
} }
#endif
setup::setup() setup::setup()
: AA_axis_offset(0.0), : AA_axis_offset(0.0),

View File

@@ -0,0 +1,101 @@
#include "linuxcnc_tool_adapter.hh"
#include <array>
namespace {
std::array<CANON_TOOL_TABLE, CANON_POCKETS_MAX> &tool_table()
{
static std::array<CANON_TOOL_TABLE, CANON_POCKETS_MAX> tools{};
return tools;
}
int &selected_tool_index()
{
static int index = -1;
return index;
}
CANON_TOOL_TABLE empty_tool()
{
CANON_TOOL_TABLE tool{};
tool.toolno = 0;
tool.pocketno = 0;
return tool;
}
} // namespace
namespace standalone {
void reset_tool_adapter()
{
selected_tool_index() = -1;
auto &tools = tool_table();
for (int index = 0; index < CANON_POCKETS_MAX; ++index) {
tools[index] = empty_tool();
tools[index].pocketno = index;
}
tools[0].pocketno = 0;
}
void set_tool_entry(int index, const CANON_TOOL_TABLE &tool)
{
if ((index < 0) || (index >= CANON_POCKETS_MAX)) {
return;
}
tool_table()[index] = tool;
}
CANON_TOOL_TABLE tool_entry_init()
{
return empty_tool();
}
int find_tool_index_for_tool(int toolno)
{
if (toolno == 0) {
return 0;
}
const auto &tools = tool_table();
for (int index = 0; index < CANON_POCKETS_MAX; ++index) {
if (tools[index].toolno == toolno) {
return index;
}
}
return -1;
}
int get_tool_entry(CANON_TOOL_TABLE *tool, int index)
{
if ((tool == nullptr) || (index < 0) || (index >= CANON_POCKETS_MAX)) {
return -1;
}
*tool = tool_table()[index];
return 0;
}
void select_tool(int toolno)
{
selected_tool_index() = find_tool_index_for_tool(toolno);
}
void change_tool_number(int index)
{
if ((index < 0) || (index >= CANON_POCKETS_MAX)) {
return;
}
auto &tools = tool_table();
CANON_TOOL_TABLE spindle_tool = tools[index];
spindle_tool.pocketno = 0;
tools[0] = spindle_tool;
}
void change_selected_tool()
{
change_tool_number(selected_tool_index());
}
} // namespace standalone

View File

@@ -0,0 +1,16 @@
#pragma once
#include "emc/nml_intf/emctool.h"
namespace standalone {
void reset_tool_adapter();
void set_tool_entry(int index, const CANON_TOOL_TABLE &tool);
CANON_TOOL_TABLE tool_entry_init();
int find_tool_index_for_tool(int toolno);
int get_tool_entry(CANON_TOOL_TABLE *tool, int index);
void select_tool(int toolno);
void change_selected_tool();
void change_tool_number(int index);
} // namespace standalone

View File

@@ -0,0 +1,3 @@
#pragma once
#include "boost/python/object_fwd.hpp"

View File

@@ -0,0 +1,3 @@
#pragma once
#include "boost/python/object_fwd.hpp"

View File

@@ -0,0 +1,3 @@
#pragma once
#include "boost/python/object_fwd.hpp"

View File

@@ -0,0 +1,3 @@
#pragma once
#include "boost/python/object_fwd.hpp"

View File

@@ -1,9 +1,88 @@
#pragma once #pragma once
#include <string>
struct _typeobject {
const char *tp_name = "standalone-python-shim";
};
struct _object {
_typeobject *ob_type = nullptr;
};
typedef _object PyObject;
inline int PyCallable_Check(PyObject *) { return 0; }
inline int PyErr_ExceptionMatches(PyObject *) { return 0; }
inline void PyErr_Clear() {}
inline int PyUnicode_Check(PyObject *) { return 0; }
inline int PyLong_Check(PyObject *) { return 0; }
inline int PyFloat_Check(PyObject *) { return 0; }
inline PyObject *PyObject_Str(PyObject *) { return nullptr; }
inline void Py_XDECREF(PyObject *) {}
inline const char *PyUnicode_AsUTF8(PyObject *) { return ""; }
inline PyObject *PyExc_KeyError = nullptr;
inline PyObject *Py_None = nullptr;
namespace boost { namespace boost {
namespace python { namespace python {
class object {}; class error_already_set {};
class object {
public:
object() = default;
template <typename T>
explicit object(const T &) {}
object attr(const char *) const { return object(); }
object operator[](const char *) const { return object(); }
object operator[](const std::string &) const { return object(); }
object operator[](int) const { return object(); }
PyObject *ptr() const { return nullptr; }
};
class list : public object {
public:
using object::object;
list() = default;
explicit list(const object &) {}
template <typename T>
void append(const T &) {}
};
class tuple : public object {
public:
tuple() = default;
explicit tuple(const list &) {}
};
class dict : public object {
public:
object keys() const { return object(); }
};
class scope {
public:
explicit scope(const object &) {}
object attr(const char *) const { return object(); }
};
template <typename T>
object import(const T &) { return object(); }
template <typename T>
class extract {
public:
explicit extract(const object &) {}
operator T() const { return T(); }
};
inline int len(const object &) { return 0; }
inline void handle_exception() {}
} // namespace python } // namespace python
} // namespace boost } // namespace boost

View File

@@ -0,0 +1,3 @@
#pragma once
#include "boost/python/object_fwd.hpp"

View File

@@ -0,0 +1,3 @@
#pragma once
#include "boost/python/object_fwd.hpp"

View File

@@ -0,0 +1,30 @@
#pragma once
#include <stdbool.h>
#define HAL_NAME_LEN 64
typedef enum {
HAL_TYPE_UNINITIALIZED = 0,
HAL_BIT,
HAL_FLOAT,
HAL_S32,
HAL_U32,
HAL_S64,
HAL_U64
} hal_type_t;
typedef union {
bool b;
double f;
int s;
unsigned int u;
long long ls;
unsigned long long lu;
} hal_data_u;
int hal_init(const char *);
int hal_ready(int);
int hal_get_pin_value_by_name(const char *, hal_type_t *, hal_data_u **, bool *);
int hal_get_signal_value_by_name(const char *, hal_type_t *, hal_data_u **, bool *);
int hal_get_param_value_by_name(const char *, hal_type_t *, hal_data_u **);

View File

@@ -0,0 +1,16 @@
#pragma once
#include <string>
#include "boost/python/object_fwd.hpp"
struct pycontext_impl {
boost::python::object tupleargs;
boost::python::object kwargs;
int py_return_type = 0;
double py_returned_double = 0.0;
int py_returned_int = 0;
boost::python::object generator_next;
};
std::string handle_pyerror();

View File

@@ -2,12 +2,37 @@
#include <string> #include <string>
#include "boost/python/object_fwd.hpp"
enum pp_status {
PLUGIN_NO_SECTION = -1,
PLUGIN_OK = 0,
PLUGIN_NO_CALLABLE = 1,
PLUGIN_EXCEPTION = 2
};
std::string handle_pyerror();
class PythonPlugin { class PythonPlugin {
public: public:
static PythonPlugin *instantiate(struct _inittab * = nullptr);
int configure(const char * = nullptr, const char * = nullptr) { return PLUGIN_NO_SECTION; }
bool is_callable(const char *, const char *) { return false; }
int call(const char *, const char *, boost::python::object, boost::python::object,
boost::python::object &) { return PLUGIN_NO_CALLABLE; }
int run_string(const char *, boost::python::object &, bool = false) { return PLUGIN_NO_CALLABLE; }
int call_method(boost::python::object, boost::python::object &) { return PLUGIN_NO_CALLABLE; }
int plugin_status() const { return PLUGIN_NO_SECTION; }
bool usable() const { return false; } bool usable() const { return false; }
int plugin_status() const { return 0; } int initialize() { return PLUGIN_NO_SECTION; }
const std::string &last_exception() const { const std::string &last_exception() const {
static std::string empty; static std::string empty;
return empty; return empty;
} }
const std::string &last_errmsg() const {
static std::string empty;
return empty;
}
boost::python::object main_namespace;
}; };

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "emc/nml_intf/emctool.h" #include "emc/nml_intf/emctool.h"
#include "linuxcnc_tool_adapter.hh"
enum { enum {
IDX_OK = 0, IDX_OK = 0,
@@ -8,22 +9,15 @@ enum {
inline CANON_TOOL_TABLE tooldata_entry_init() inline CANON_TOOL_TABLE tooldata_entry_init()
{ {
CANON_TOOL_TABLE tool{}; return standalone::tool_entry_init();
tool.toolno = 0;
tool.pocketno = 0;
return tool;
} }
inline int tooldata_find_index_for_tool(int toolno) inline int tooldata_find_index_for_tool(int toolno)
{ {
return toolno == 0 ? 0 : -1; return standalone::find_tool_index_for_tool(toolno);
} }
inline int tooldata_get(CANON_TOOL_TABLE *tool, int index) inline int tooldata_get(CANON_TOOL_TABLE *tool, int index)
{ {
if ((tool == nullptr) || (index != 0)) { return standalone::get_tool_entry(tool, index) == 0 ? IDX_OK : -1;
return -1;
}
*tool = tooldata_entry_init();
return IDX_OK;
} }

View File

@@ -0,0 +1,13 @@
canon_event=STRAIGHT_TRAVERSE line=1 x=0 y=0 z=0 a=0 b=0 c=0 u=0 v=0 w=0
canon_event=COMMENT: interpreter: IJK distance mode changed to incremental
canon_event=SET_FEED_RATE rate=60
canon_event=ARC_FEED line=3 first_end=1 second_end=1 first_axis=1 second_axis=0 rotation=-1 axis_end_point=0 a=0 b=0 c=0 u=0 v=0 w=0
canon_event=COMMENT: interpreter: IJK distance mode changed to absolute
canon_event=ARC_FEED line=5 first_end=0 second_end=0 first_axis=0 second_axis=1 rotation=1 axis_end_point=0 a=0 b=0 c=0 u=0 v=0 w=0
canon_event=ARC_FEED line=6 first_end=1 second_end=0 first_axis=0.5 second_axis=-3.06162e-17 rotation=-1 axis_end_point=0 a=0 b=0 c=0 u=0 v=0 w=0
canon_event=STRAIGHT_TRAVERSE line=7 x=0 y=0 z=0 a=0 b=0 c=0 u=0 v=0 w=0
canon_event=ARC_FEED line=9 first_end=1 second_end=1 first_axis=0 second_axis=1 rotation=-1 axis_end_point=0 a=0 b=0 c=0 u=0 v=0 w=0
canon_event=STRAIGHT_TRAVERSE line=10 x=0 y=0 z=0 a=0 b=0 c=0 u=0 v=0 w=0
canon_event=ARC_FEED line=11 first_end=1 second_end=1 first_axis=0 second_axis=1 rotation=1 axis_end_point=0 a=0 b=0 c=0 u=0 v=0 w=0
canon_event=STRAIGHT_TRAVERSE line=12 x=0 y=0 z=0 a=0 b=0 c=0 u=0 v=0 w=0
canon_event=ARC_FEED line=13 first_end=1 second_end=0 first_axis=0.5 second_axis=0.866025 rotation=-1 axis_end_point=2 a=0 b=0 c=0 u=0 v=0 w=0

View File

@@ -0,0 +1,11 @@
canon_event=SET_SPINDLE_SPEED spindle=0 speed=1200
canon_event=START_SPINDLE_CLOCKWISE spindle=0 wait_for_at_speed=1
canon_event=STOP_SPINDLE_TURNING spindle=0
canon_event=SET_SPINDLE_SPEED spindle=0 speed=900
canon_event=START_SPINDLE_COUNTERCLOCKWISE spindle=0 wait_for_at_speed=1
canon_event=STOP_SPINDLE_TURNING spindle=0
canon_event=MIST_ON
canon_event=FLOOD_ON
canon_event=MIST_OFF
canon_event=FLOOD_OFF
canon_event=DWELL seconds=0.25

View File

@@ -0,0 +1,7 @@
canon_event=MESSAGE: named global=42.5000
canon_event=MESSAGE: named local=7.2500
canon_event=MESSAGE: exists global=1 local=1 missing=0
canon_event=MESSAGE: ini velocity=35
canon_event=MESSAGE: hal pin=1 signal=98.2500 param=-17
canon_event=MESSAGE: hal u32=123456789 s64=-9000000000 u64=9000000000
canon_event=MESSAGE: hal disconnected=12.5000 missing_exists=0

View File

@@ -0,0 +1,3 @@
canon_event=MESSAGE: numbered p1=12.5000 p2=15.5000 exists1=1 exists5602=0
canon_event=STRAIGHT_TRAVERSE line=6 x=2 y=3 z=4 a=0 b=0 c=0 u=0 v=0 w=0
canon_event=MESSAGE: numbered pos x=2.0000 y=3.0000 z=4.0000

View File

@@ -0,0 +1,3 @@
STRAIGHT_TRAVERSE line=4 x=0 y=0 z=0
SET_FEED_RATE rate=100
STRAIGHT_FEED line=2 x=2 y=3 z=0

View File

@@ -0,0 +1,11 @@
canon_event=SELECT_TOOL tool=2
canon_event=MESSAGE: tool selected=2 selected_pocket=2
canon_event=STOP_SPINDLE_TURNING spindle=0
canon_event=CHANGE_TOOL
canon_event=MESSAGE: tool after_m6 current_pocket=2 current_tool=2
canon_event=USE_TOOL_LENGTH_OFFSET x=0 y=0 z=1.25 a=0 b=0 c=0 u=0 v=0 w=0
canon_event=MESSAGE: tool g43 offset=1 x=0.0000 y=0.0000 z=1.2500
canon_event=USE_TOOL_LENGTH_OFFSET x=0 y=0 z=0 a=0 b=0 c=0 u=0 v=0 w=0
canon_event=MESSAGE: tool g49 offset=0 x=0.0000 y=0.0000 z=0.0000
canon_event=CHANGE_TOOL_NUMBER pocket=0
canon_event=MESSAGE: tool after_m61 current_pocket=0 current_tool=2

View File

@@ -0,0 +1,4 @@
execute_line_1=0
execute_line_2=5
error_text=Radius to end of arc differs from radius to start
absent=canon_event=ARC_FEED

View File

@@ -0,0 +1,4 @@
execute_line_1=0
execute_line_2=5
error_text=Zero-radius arc
absent=canon_event=ARC_FEED

View File

@@ -0,0 +1,2 @@
execute_line_1=5
error_text=Cannot assign to read-only parameter #<_feed>

View File

@@ -0,0 +1,2 @@
execute_line_1=5
error_text=Parameter is readonly

View File

@@ -0,0 +1,3 @@
execute_line_1=5
error_text=Requested tool 999 not found in the tool table
absent=canon_event=USE_TOOL_LENGTH_OFFSET

View File

@@ -0,0 +1,2 @@
execute_line_1=5
error_text=Requested tool 999 not found in the tool table

View File

@@ -0,0 +1,13 @@
G90 G17 G0 X0 Y0 Z0
G91.1
G2 X1 Y1 I1 J0 F60
G90.1
G3 X0 Y0 I0 J1
G2 X1 Y0 R0.5
G18 G0 X0 Y0 Z0
G91.1
G2 X1 Z1 I1 K0
G19 G0 X0 Y0 Z0
G3 Y1 Z1 J0 K1
G17 G0 X0 Y0 Z0
G2 X1 Y0 Z2 R-1

View File

@@ -0,0 +1,8 @@
S1200 M3
M5
S900 M4
M5
M7
M8
M9
G4 P0.25

View File

@@ -0,0 +1,21 @@
#<_global_probe> = 42.5
#<local_probe> = 7.25
#<exists_global> = EXISTS[#<_global_probe>]
#<exists_local> = EXISTS[#<local_probe>]
#<exists_missing> = EXISTS[#<missing_probe>]
#<ini_velocity> = #<_ini[traj]max_linear_velocity>
#<hal_pin> = #<_hal[standalone.pin-bit]>
#<hal_signal> = #<_hal[standalone.signal-float]>
#<hal_param> = #<_hal[standalone.param-s32]>
#<hal_u32> = #<_hal[standalone.pin-u32]>
#<hal_s64> = #<_hal[standalone.signal-s64]>
#<hal_u64> = #<_hal[standalone.param-u64]>
#<hal_disconnected> = #<_hal[standalone.disconnected-float]>
#<hal_missing_exists> = EXISTS[#<_hal[standalone.missing]>]
(DEBUG, named global=%f#<_global_probe>)
(DEBUG, named local=%f#<local_probe>)
(DEBUG, exists global=%d#<exists_global> local=%d#<exists_local> missing=%d#<exists_missing>)
(DEBUG, ini velocity=%d#<ini_velocity>)
(DEBUG, hal pin=%d#<hal_pin> signal=%f#<hal_signal> param=%d#<hal_param>)
(DEBUG, hal u32=%d#<hal_u32> s64=%.0f#<hal_s64> u64=%.0f#<hal_u64>)
(DEBUG, hal disconnected=%f#<hal_disconnected> missing_exists=%d#<hal_missing_exists>)

View File

@@ -0,0 +1,7 @@
#1 = 12.5
#2 = [#1 + 3]
#3 = EXISTS[#1]
#4 = EXISTS[#5602]
(DEBUG, numbered p1=%f#1 p2=%f#2 exists1=%d#3 exists5602=%d#4)
G90 G0 X2 Y3 Z4
(DEBUG, numbered pos x=%f#5420 y=%f#5421 z=%f#5422)

View File

@@ -0,0 +1,5 @@
O<move> sub
G1 X#1 Y#2 F100
O<move> endsub
G0 X0 Y0
O<move> call [2] [3]

View File

@@ -0,0 +1,10 @@
T2
(DEBUG, tool selected=%d#<_selected_tool> selected_pocket=%d#<_selected_pocket>)
M6
(DEBUG, tool after_m6 current_pocket=%d#<_current_pocket> current_tool=%d#<_current_tool>)
G43 H2
(DEBUG, tool g43 offset=%d#<_tool_offset> x=%f#5401 y=%f#5402 z=%f#5403)
G49
(DEBUG, tool g49 offset=%d#<_tool_offset> x=%f#5401 y=%f#5402 z=%f#5403)
M61 Q2
(DEBUG, tool after_m61 current_pocket=%d#<_current_pocket> current_tool=%d#<_current_tool>)

View File

@@ -0,0 +1,2 @@
G90 G17 G0 X0 Y0
G2 X1 Y1 I0.2 J0 F60

View File

@@ -0,0 +1,2 @@
G90 G17 G0 X0 Y0
G2 X1 Y0 I0 J0 F60

View File

@@ -0,0 +1 @@
#<_feed> = 1

View File

@@ -0,0 +1 @@
#5400 = 1

View File

@@ -0,0 +1 @@
G43 H999

View File

@@ -0,0 +1 @@
T999

View File

@@ -0,0 +1,3 @@
[TRAJ]
LINEAR_UNITS = mm
MAX_LINEAR_VELOCITY = 35

View File

@@ -7,6 +7,7 @@ GCODE_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode"
CANON_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon" CANON_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon"
GCODE_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode_errors" GCODE_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode_errors"
CANON_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon_errors" CANON_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon_errors"
NAMEDPARAM_INI_FIXTURE="$ROOT_DIR/tests/fixtures/ini/namedparams.ini"
"$ROOT_DIR/tools/build_native_probes.sh" "$ROOT_DIR/tools/build_native_probes.sh"
@@ -32,25 +33,56 @@ check_exitcode() {
check_exitcode linuxcnc_interp_state_probe check_exitcode linuxcnc_interp_state_probe
check_exitcode linuxcnc_namedparam_harness check_exitcode linuxcnc_namedparam_harness
check_exitcode linuxcnc_namedparam_harness.run
check_exitcode linuxcnc_interp_minimal_harness check_exitcode linuxcnc_interp_minimal_harness
check_exitcode linuxcnc_interp_minimal_harness.run check_exitcode linuxcnc_interp_minimal_harness.run
check_exitcode linuxcnc_rs274_compile_probe check_exitcode linuxcnc_rs274_compile_probe
check_exitcode linuxcnc_interp_convert_source_probe check_exitcode linuxcnc_interp_convert_source_probe
NAMEDPARAM_STDOUT="$BUILD_DIR/linuxcnc_namedparam_harness.run.stdout.log"
grep -Fq "init_named_parameters=0" "$NAMEDPARAM_STDOUT"
grep -Fq "global_named_count=57" "$NAMEDPARAM_STDOUT"
grep -Fq "_metric_machine: rc=0 found=1 value=1" "$NAMEDPARAM_STDOUT"
grep -Fq "_motion_mode: rc=0 found=1 value=10" "$NAMEDPARAM_STDOUT"
grep -Fq "_metric: rc=0 found=1 value=1" "$NAMEDPARAM_STDOUT"
grep -Fq "_feed: rc=0 found=1 value=123.45" "$NAMEDPARAM_STDOUT"
grep -Fq "_rpm: rc=0 found=1 value=678.9" "$NAMEDPARAM_STDOUT"
grep -Fq "_x: rc=0 found=1 value=1.25" "$NAMEDPARAM_STDOUT"
grep -Fq "_current_tool: rc=0 found=1 value=12" "$NAMEDPARAM_STDOUT"
grep -Fq "_ini[traj]max_linear_velocity: rc=0 found=1 value=35" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.pin-bit]: rc=0 found=1 value=1" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.signal-float]: rc=0 found=1 value=98.25" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.param-s32]: rc=0 found=1 value=-17" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.pin-u32]: rc=0 found=1 value=1.23457e+08" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.signal-s64]: rc=0 found=1 value=-9e+09" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.param-u64]: rc=0 found=1 value=9e+09" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.disconnected-float]: rc=0 found=1 value=12.5" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.missing]: rc=0 found=0 value=0" "$NAMEDPARAM_STDOUT"
check_fixture_output() { check_fixture_output() {
local fixture="$1" local fixture="$1"
local expected="$2" local expected="$2"
local stdout_file="$3" local stdout_file="$3"
local require_mdi="${4:-1}"
grep -Fq "read=0" "$stdout_file" grep -Fq "read=0" "$stdout_file"
grep -Fq "parse_line=0" "$stdout_file" grep -Fq "parse_line=0" "$stdout_file"
local line_count if [[ "$require_mdi" == "1" ]]; then
line_count="$(grep -cv '^[[:space:]]*$' "$fixture")" local line_count
local line_number line_count="$(grep -cv '^[[:space:]]*$' "$fixture")"
for ((line_number = 1; line_number <= line_count; line_number += 1)); do local line_number
grep -Fq "execute_line_${line_number}=0" "$stdout_file" for ((line_number = 1; line_number <= line_count; line_number += 1)); do
done if ! grep -Fq "execute_line_${line_number}=0" "$stdout_file" &&
! grep -Fq "execute_line_${line_number}=2" "$stdout_file"; then
echo "missing successful execute status for line $line_number in $stdout_file" >&2
exit 1
fi
done
else
grep -Fq "file_open=0" "$stdout_file"
grep -Fq "file_execute_1=0" "$stdout_file"
fi
while IFS= read -r expected_event; do while IFS= read -r expected_event; do
[[ -z "$expected_event" ]] && continue [[ -z "$expected_event" ]] && continue
@@ -74,10 +106,14 @@ for fixture in "$GCODE_FIXTURE_DIR"/*.ngc; do
stdout_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stdout.log" stdout_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stdout.log"
stderr_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stderr.log" stderr_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stderr.log"
"$BUILD_DIR/linuxcnc_interp_minimal_harness" "$fixture" \ INI_FILE_NAME="$NAMEDPARAM_INI_FIXTURE" "$BUILD_DIR/linuxcnc_interp_minimal_harness" "$fixture" \
>"$stdout_file" \ >"$stdout_file" \
2>"$stderr_file" 2>"$stderr_file"
check_fixture_output "$fixture" "$expected" "$stdout_file" require_mdi=1
if [[ "$name" == "oword_subroutine" ]]; then
require_mdi=0
fi
check_fixture_output "$fixture" "$expected" "$stdout_file" "$require_mdi"
done done
for fixture in "$GCODE_ERROR_FIXTURE_DIR"/*.ngc; do for fixture in "$GCODE_ERROR_FIXTURE_DIR"/*.ngc; do
@@ -90,7 +126,7 @@ for fixture in "$GCODE_ERROR_FIXTURE_DIR"/*.ngc; do
stdout_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stdout.log" stdout_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stdout.log"
stderr_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stderr.log" stderr_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stderr.log"
"$BUILD_DIR/linuxcnc_interp_minimal_harness" "$fixture" \ INI_FILE_NAME="$NAMEDPARAM_INI_FIXTURE" "$BUILD_DIR/linuxcnc_interp_minimal_harness" "$fixture" \
>"$stdout_file" \ >"$stdout_file" \
2>"$stderr_file" 2>"$stderr_file"
@@ -103,7 +139,7 @@ for fixture in "$GCODE_ERROR_FIXTURE_DIR"/*.ngc; do
exit 1 exit 1
fi fi
elif [[ "$expected_line" == error_text=* ]]; then elif [[ "$expected_line" == error_text=* ]]; then
grep -Fq "${expected_line#error_text=}" "$stderr_file" grep -Fq "$expected_line" "$stdout_file"
else else
grep -Fq "$expected_line" "$stdout_file" grep -Fq "$expected_line" "$stdout_file"
fi fi

View File

@@ -8,141 +8,332 @@ WRAP_DIR="$ROOT_DIR/runtime/core/linuxcnc_wrap"
SHIM_DIR="$ROOT_DIR/runtime/core/shims" SHIM_DIR="$ROOT_DIR/runtime/core/shims"
INCLUDE_DIR="$ROOT_DIR/runtime/core/include" INCLUDE_DIR="$ROOT_DIR/runtime/core/include"
MINIMAL_GCODE_FIXTURE="$ROOT_DIR/tests/fixtures/gcode/minimal_linear.ngc" MINIMAL_GCODE_FIXTURE="$ROOT_DIR/tests/fixtures/gcode/minimal_linear.ngc"
NAMEDPARAM_INI_FIXTURE="$ROOT_DIR/tests/fixtures/ini/namedparams.ini"
CXX="${CXX:-g++}"
mkdir -p "$BUILD_DIR" mkdir -p "$BUILD_DIR"
rm -f \ COMMON_FLAGS=(
-std=c++20
-O2
-D_GNU_SOURCE
-DM_PI=3.14159265358979323846
-DOBJECT_FWD_DWA2002724_HPP
-I"$WRAP_DIR"
-I"$SHIM_DIR"
-I"$INCLUDE_DIR"
-I"$VENDOR_DIR/src"
-I"$VENDOR_DIR/src/rtapi"
-I"$VENDOR_DIR/src/emc"
-I"$VENDOR_DIR/src/emc/nml_intf"
-I"$VENDOR_DIR/src/emc/motion"
-I"$VENDOR_DIR/src/libnml/posemath"
)
COMMON_INI_FLAGS=(
"${COMMON_FLAGS[@]}"
-I"$VENDOR_DIR/src/emc/ini"
)
MINIMAL_FLAGS=(
"${COMMON_INI_FLAGS[@]}"
-ffunction-sections
-fdata-sections
-DUNIT_TEST
-DLINUXCNC_STANDALONE_USE_RS274_PRE_STATE
)
MINIMAL_LINK_FLAGS=(
-Wl,--gc-sections
)
write_command_file() {
local file="$1"
shift
printf '%q\n' "$@" > "$file"
}
command_matches() {
local file="$1"
shift
local tmp
tmp="$(mktemp)"
write_command_file "$tmp" "$@"
if [[ -f "$file" ]] && cmp -s "$tmp" "$file"; then
rm -f "$tmp"
return 0
fi
rm -f "$tmp"
return 1
}
depfile_newer_than() {
local depfile="$1"
local output="$2"
[[ -f "$depfile" ]] || return 0
local deps
deps="$(tr '\\\n' ' ' < "$depfile" | sed -e 's/^[^:]*://')"
local dep
for dep in $deps; do
if [[ -f "$dep" && "$dep" -nt "$output" ]]; then
return 0
fi
done
return 1
}
object_needs_rebuild() {
local obj="$1"
local depfile="$2"
local cmdfile="$3"
shift 3
[[ -f "$obj" ]] || return 0
command_matches "$cmdfile" "$@" || return 0
depfile_newer_than "$depfile" "$obj" && return 0
return 1
}
compile_object() {
local target="$1"
local source="$2"
local obj="$3"
shift 3
local stdout_log="$BUILD_DIR/$target.stdout.log"
local stderr_log="$BUILD_DIR/$target.stderr.log"
local depfile="$obj.d"
local cmdfile="$obj.cmd"
local cmd=("$CXX" "$@" -MMD -MP -MF "$depfile" -c "$source" -o "$obj")
mkdir -p "$(dirname "$obj")"
if ! object_needs_rebuild "$obj" "$depfile" "$cmdfile" "${cmd[@]}"; then
printf 'up to date: %s\n' "$source" >> "$stdout_log"
return 0
fi
printf 'compile: %s\n' "$source" >> "$stdout_log"
if "${cmd[@]}" >> "$stdout_log" 2>> "$stderr_log"; then
write_command_file "$cmdfile" "${cmd[@]}"
return 0
fi
return 1
}
link_needs_rebuild() {
local output="$1"
local cmdfile="$2"
shift 2
[[ -f "$output" ]] || return 0
command_matches "$cmdfile" "$@" || return 0
local arg
for arg in "$@"; do
if [[ -f "$arg" && "$arg" -nt "$output" ]]; then
return 0
fi
done
return 1
}
link_binary() {
local target="$1"
local output="$2"
shift 2
local stdout_log="$BUILD_DIR/$target.stdout.log"
local stderr_log="$BUILD_DIR/$target.stderr.log"
local cmdfile="$output.cmd"
local cmd=("$CXX" "$@" -o "$output")
if ! link_needs_rebuild "$output" "$cmdfile" "${cmd[@]}"; then
printf 'up to date: %s\n' "$output" >> "$stdout_log"
return 0
fi
printf 'link: %s\n' "$output" >> "$stdout_log"
if "${cmd[@]}" >> "$stdout_log" 2>> "$stderr_log"; then
write_command_file "$cmdfile" "${cmd[@]}"
return 0
fi
return 1
}
compile_target_objects() {
local target="$1"
local flags_name="$2"
local sources_name="$3"
local objs_name="$4"
local -n flags="$flags_name"
local -n sources="$sources_name"
local -n objs="$objs_name"
local obj_dir="$BUILD_DIR/obj/$target"
local status=0
objs=()
local source
for source in "${sources[@]}"; do
local base
base="$(basename "$source")"
local obj="$obj_dir/${base%.*}.o"
objs+=("$obj")
if ! compile_object "$target" "$source" "$obj" "${flags[@]}"; then
status=1
fi
done
return "$status"
}
build_binary_target() {
local target="$1"
local output="$2"
local flags_name="$3"
local sources_name="$4"
local link_flags_name="$5"
shift 5
local stdout_log="$BUILD_DIR/$target.stdout.log"
local stderr_log="$BUILD_DIR/$target.stderr.log"
: > "$stdout_log"
: > "$stderr_log"
local target_objs=()
local status=0
if ! compile_target_objects "$target" "$flags_name" "$sources_name" target_objs; then
status=1
fi
if [[ "$status" -eq 0 ]]; then
local -n link_flags="$link_flags_name"
if ! link_binary "$target" "$output" "${link_flags[@]}" "${target_objs[@]}" "$@"; then
status=1
fi
fi
echo "$status" > "$BUILD_DIR/$target.exitcode"
return "$status"
}
build_object_target() {
local target="$1"
local output="$2"
local source="$3"
local flags_name="$4"
local stdout_log="$BUILD_DIR/$target.stdout.log"
local stderr_log="$BUILD_DIR/$target.stderr.log"
: > "$stdout_log"
: > "$stderr_log"
local -n flags="$flags_name"
local status=0
if ! compile_object "$target" "$source" "$output" "${flags[@]}"; then
status=1
fi
echo "$status" > "$BUILD_DIR/$target.exitcode"
return "$status"
}
NO_LINK_FLAGS=()
INI_PROBE_SOURCES=(
"$VENDOR_DIR/src/emc/ini/inifile.cc"
"$WRAP_DIR/linuxcnc_ini_probe.cpp"
)
STATE_PROBE_SOURCES=(
"$WRAP_DIR/linuxcnc_interp_state_probe.cpp"
)
INTERP_CORE_SOURCES=(
"$VENDOR_DIR/src/emc/rs274ngc/modal_state.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_array.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_internal.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_read.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_check.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_arc.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_inverse.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_execute.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_convert.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_cycles.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_g7x.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_queue.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_find.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_namedparams.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_write.cc"
"$VENDOR_DIR/src/emc/rs274ngc/interp_o_word.cc"
"$VENDOR_DIR/src/emc/rs274ngc/rs274ngc_pre.cc"
"$WRAP_DIR/linuxcnc_hal_adapter.cpp"
"$WRAP_DIR/linuxcnc_interp_edge_stubs.cpp"
"$WRAP_DIR/linuxcnc_runtime_state_stubs.cpp"
"$WRAP_DIR/linuxcnc_tool_adapter.cpp"
"$WRAP_DIR/linuxcnc_interp_minimal_runtime.cpp"
"$VENDOR_DIR/src/emc/ini/inifile.cc"
)
NAMEDPARAM_SOURCES=(
"${INTERP_CORE_SOURCES[@]}"
"$WRAP_DIR/linuxcnc_namedparam_harness.cpp"
)
MINIMAL_SOURCES=(
"${INTERP_CORE_SOURCES[@]}"
"$WRAP_DIR/linuxcnc_interp_minimal_harness.cpp"
)
build_binary_target \
linuxcnc_ini_probe \
"$BUILD_DIR/linuxcnc_ini_probe" \ "$BUILD_DIR/linuxcnc_ini_probe" \
COMMON_FLAGS \
INI_PROBE_SOURCES \
NO_LINK_FLAGS \
-lfmt
build_binary_target \
linuxcnc_interp_state_probe \
"$BUILD_DIR/linuxcnc_interp_state_probe" \ "$BUILD_DIR/linuxcnc_interp_state_probe" \
"$BUILD_DIR/linuxcnc_interp_state_probe.exitcode" \ COMMON_FLAGS \
"$BUILD_DIR/linuxcnc_interp_state_probe.stdout.log" \ STATE_PROBE_SOURCES \
"$BUILD_DIR/linuxcnc_interp_state_probe.stderr.log" \ NO_LINK_FLAGS
build_binary_target \
linuxcnc_namedparam_harness \
"$BUILD_DIR/linuxcnc_namedparam_harness" \ "$BUILD_DIR/linuxcnc_namedparam_harness" \
"$BUILD_DIR/linuxcnc_namedparam_harness.exitcode" \ MINIMAL_FLAGS \
"$BUILD_DIR/linuxcnc_namedparam_harness.stdout.log" \ NAMEDPARAM_SOURCES \
"$BUILD_DIR/linuxcnc_namedparam_harness.stderr.log" \ MINIMAL_LINK_FLAGS \
-lfmt
if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_namedparam_harness.exitcode")" == "0" ]]; then
set +e
"$BUILD_DIR/linuxcnc_namedparam_harness" "$NAMEDPARAM_INI_FIXTURE" \
>"$BUILD_DIR/linuxcnc_namedparam_harness.run.stdout.log" \
2>"$BUILD_DIR/linuxcnc_namedparam_harness.run.stderr.log"
NAMEDPARAM_RUN_RC=$?
set -e
echo "$NAMEDPARAM_RUN_RC" > "$BUILD_DIR/linuxcnc_namedparam_harness.run.exitcode"
else
rm -f \
"$BUILD_DIR/linuxcnc_namedparam_harness.run.exitcode" \
"$BUILD_DIR/linuxcnc_namedparam_harness.run.stdout.log" \
"$BUILD_DIR/linuxcnc_namedparam_harness.run.stderr.log"
fi
build_binary_target \
linuxcnc_interp_minimal_harness \
"$BUILD_DIR/linuxcnc_interp_minimal_harness" \ "$BUILD_DIR/linuxcnc_interp_minimal_harness" \
"$BUILD_DIR/linuxcnc_interp_minimal_harness.exitcode" \ MINIMAL_FLAGS \
"$BUILD_DIR/linuxcnc_interp_minimal_harness.stdout.log" \ MINIMAL_SOURCES \
"$BUILD_DIR/linuxcnc_interp_minimal_harness.stderr.log" \ MINIMAL_LINK_FLAGS \
"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.exitcode" \ -lfmt
"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stdout.log" \
"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stderr.log" \
"$BUILD_DIR/linuxcnc_rs274_compile_probe.o" \
"$BUILD_DIR/linuxcnc_rs274_compile_probe.exitcode" \
"$BUILD_DIR/linuxcnc_rs274_compile_probe.stdout.log" \
"$BUILD_DIR/linuxcnc_rs274_compile_probe.stderr.log" \
"$BUILD_DIR/linuxcnc_interp_convert_source_probe.o" \
"$BUILD_DIR/linuxcnc_interp_convert_source_probe.exitcode" \
"$BUILD_DIR/linuxcnc_interp_convert_source_probe.stdout.log" \
"$BUILD_DIR/linuxcnc_interp_convert_source_probe.stderr.log"
g++ -std=c++20 -O2 \ if [[ "$(tr -d '[:space:]' < "$BUILD_DIR/linuxcnc_interp_minimal_harness.exitcode")" == "0" ]]; then
-D_GNU_SOURCE \
-DM_PI=3.14159265358979323846 \
-DOBJECT_FWD_DWA2002724_HPP \
-I"$SHIM_DIR" \
-I"$INCLUDE_DIR" \
-I"$VENDOR_DIR/src" \
-I"$VENDOR_DIR/src/rtapi" \
-I"$VENDOR_DIR/src/emc" \
-I"$VENDOR_DIR/src/emc/nml_intf" \
-I"$VENDOR_DIR/src/emc/motion" \
-I"$VENDOR_DIR/src/libnml/posemath" \
"$VENDOR_DIR/src/emc/ini/inifile.cc" \
"$WRAP_DIR/linuxcnc_ini_probe.cpp" \
-lfmt \
-o "$BUILD_DIR/linuxcnc_ini_probe"
set +e
g++ -std=c++20 -O2 \
-D_GNU_SOURCE \
-DM_PI=3.14159265358979323846 \
-DOBJECT_FWD_DWA2002724_HPP \
-I"$SHIM_DIR" \
-I"$INCLUDE_DIR" \
-I"$VENDOR_DIR/src" \
-I"$VENDOR_DIR/src/rtapi" \
-I"$VENDOR_DIR/src/emc" \
-I"$VENDOR_DIR/src/emc/nml_intf" \
-I"$VENDOR_DIR/src/emc/motion" \
-I"$VENDOR_DIR/src/libnml/posemath" \
"$WRAP_DIR/linuxcnc_interp_state_probe.cpp" \
-o "$BUILD_DIR/linuxcnc_interp_state_probe" \
>"$BUILD_DIR/linuxcnc_interp_state_probe.stdout.log" \
2>"$BUILD_DIR/linuxcnc_interp_state_probe.stderr.log"
STATE_RC=$?
set -e
echo "$STATE_RC" > "$BUILD_DIR/linuxcnc_interp_state_probe.exitcode"
set +e
g++ -std=c++20 -O2 \
-D_GNU_SOURCE \
-DM_PI=3.14159265358979323846 \
-DOBJECT_FWD_DWA2002724_HPP \
-I"$SHIM_DIR" \
-I"$INCLUDE_DIR" \
-I"$VENDOR_DIR/src" \
-I"$VENDOR_DIR/src/rtapi" \
-I"$VENDOR_DIR/src/emc" \
-I"$VENDOR_DIR/src/emc/nml_intf" \
-I"$VENDOR_DIR/src/emc/motion" \
-I"$VENDOR_DIR/src/libnml/posemath" \
"$VENDOR_DIR/src/emc/rs274ngc/modal_state.cc" \
"$WRAP_DIR/linuxcnc_runtime_state_stubs.cpp" \
"$WRAP_DIR/linuxcnc_namedparam_harness.cpp" \
"$VENDOR_DIR/src/emc/ini/inifile.cc" \
-lfmt \
-o "$BUILD_DIR/linuxcnc_namedparam_harness" \
>"$BUILD_DIR/linuxcnc_namedparam_harness.stdout.log" \
2>"$BUILD_DIR/linuxcnc_namedparam_harness.stderr.log"
NAMEDPARAM_RC=$?
set -e
echo "$NAMEDPARAM_RC" > "$BUILD_DIR/linuxcnc_namedparam_harness.exitcode"
set +e
g++ -std=c++20 -O2 -ffunction-sections -fdata-sections \
-Wl,--gc-sections \
-D_GNU_SOURCE \
-DM_PI=3.14159265358979323846 \
-DOBJECT_FWD_DWA2002724_HPP \
-I"$SHIM_DIR" \
-I"$INCLUDE_DIR" \
-I"$VENDOR_DIR/src" \
-I"$VENDOR_DIR/src/rtapi" \
-I"$VENDOR_DIR/src/emc" \
-I"$VENDOR_DIR/src/emc/nml_intf" \
-I"$VENDOR_DIR/src/emc/motion" \
-I"$VENDOR_DIR/src/libnml/posemath" \
"$VENDOR_DIR/src/emc/rs274ngc/modal_state.cc" \
"$VENDOR_DIR/src/emc/rs274ngc/interp_array.cc" \
"$VENDOR_DIR/src/emc/rs274ngc/interp_internal.cc" \
"$VENDOR_DIR/src/emc/rs274ngc/interp_read.cc" \
"$VENDOR_DIR/src/emc/rs274ngc/interp_check.cc" \
"$VENDOR_DIR/src/emc/rs274ngc/interp_arc.cc" \
"$VENDOR_DIR/src/emc/rs274ngc/interp_inverse.cc" \
"$VENDOR_DIR/src/emc/rs274ngc/interp_execute.cc" \
"$VENDOR_DIR/src/emc/rs274ngc/interp_convert.cc" \
"$VENDOR_DIR/src/emc/rs274ngc/interp_cycles.cc" \
"$VENDOR_DIR/src/emc/rs274ngc/interp_g7x.cc" \
"$VENDOR_DIR/src/emc/rs274ngc/interp_queue.cc" \
"$VENDOR_DIR/src/emc/rs274ngc/interp_find.cc" \
"$VENDOR_DIR/src/emc/rs274ngc/interp_write.cc" \
"$WRAP_DIR/linuxcnc_runtime_state_stubs.cpp" \
"$WRAP_DIR/linuxcnc_interp_minimal_runtime.cpp" \
"$WRAP_DIR/linuxcnc_interp_minimal_harness.cpp" \
-lfmt \
-o "$BUILD_DIR/linuxcnc_interp_minimal_harness" \
>"$BUILD_DIR/linuxcnc_interp_minimal_harness.stdout.log" \
2>"$BUILD_DIR/linuxcnc_interp_minimal_harness.stderr.log"
INTERP_MIN_RC=$?
set -e
echo "$INTERP_MIN_RC" > "$BUILD_DIR/linuxcnc_interp_minimal_harness.exitcode"
if [[ "$INTERP_MIN_RC" -eq 0 ]]; then
set +e set +e
"$BUILD_DIR/linuxcnc_interp_minimal_harness" "$MINIMAL_GCODE_FIXTURE" \ "$BUILD_DIR/linuxcnc_interp_minimal_harness" "$MINIMAL_GCODE_FIXTURE" \
>"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stdout.log" \ >"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stdout.log" \
@@ -150,49 +341,23 @@ if [[ "$INTERP_MIN_RC" -eq 0 ]]; then
INTERP_MIN_RUN_RC=$? INTERP_MIN_RUN_RC=$?
set -e set -e
echo "$INTERP_MIN_RUN_RC" > "$BUILD_DIR/linuxcnc_interp_minimal_harness.run.exitcode" echo "$INTERP_MIN_RUN_RC" > "$BUILD_DIR/linuxcnc_interp_minimal_harness.run.exitcode"
else
rm -f \
"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.exitcode" \
"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stdout.log" \
"$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stderr.log"
fi fi
set +e build_object_target \
g++ -std=c++20 -O2 \ linuxcnc_rs274_compile_probe \
-D_GNU_SOURCE \ "$BUILD_DIR/linuxcnc_rs274_compile_probe.o" \
-DM_PI=3.14159265358979323846 \ "$WRAP_DIR/linuxcnc_rs274_compile_probe.cpp" \
-DOBJECT_FWD_DWA2002724_HPP \ COMMON_FLAGS
-I"$SHIM_DIR" \
-I"$INCLUDE_DIR" \
-I"$VENDOR_DIR/src" \
-I"$VENDOR_DIR/src/rtapi" \
-I"$VENDOR_DIR/src/emc" \
-I"$VENDOR_DIR/src/emc/nml_intf" \
-I"$VENDOR_DIR/src/emc/motion" \
-I"$VENDOR_DIR/src/libnml/posemath" \
-c "$WRAP_DIR/linuxcnc_rs274_compile_probe.cpp" \
-o "$BUILD_DIR/linuxcnc_rs274_compile_probe.o" \
>"$BUILD_DIR/linuxcnc_rs274_compile_probe.stdout.log" \
2>"$BUILD_DIR/linuxcnc_rs274_compile_probe.stderr.log"
RS274_RC=$?
set -e
echo "$RS274_RC" > "$BUILD_DIR/linuxcnc_rs274_compile_probe.exitcode" build_object_target \
linuxcnc_interp_convert_source_probe \
"$BUILD_DIR/linuxcnc_interp_convert_source_probe.o" \
"$VENDOR_DIR/src/emc/rs274ngc/interp_convert.cc" \
COMMON_FLAGS
set +e
g++ -std=c++20 -O2 \
-D_GNU_SOURCE \
-DM_PI=3.14159265358979323846 \
-DOBJECT_FWD_DWA2002724_HPP \
-I"$SHIM_DIR" \
-I"$INCLUDE_DIR" \
-I"$VENDOR_DIR/src" \
-I"$VENDOR_DIR/src/rtapi" \
-I"$VENDOR_DIR/src/emc" \
-I"$VENDOR_DIR/src/emc/nml_intf" \
-I"$VENDOR_DIR/src/emc/motion" \
-I"$VENDOR_DIR/src/libnml/posemath" \
-c "$VENDOR_DIR/src/emc/rs274ngc/interp_convert.cc" \
-o "$BUILD_DIR/linuxcnc_interp_convert_source_probe.o" \
>"$BUILD_DIR/linuxcnc_interp_convert_source_probe.stdout.log" \
2>"$BUILD_DIR/linuxcnc_interp_convert_source_probe.stderr.log"
CONVERT_SOURCE_RC=$?
set -e
echo "$CONVERT_SOURCE_RC" > "$BUILD_DIR/linuxcnc_interp_convert_source_probe.exitcode"
echo "native probes complete" echo "native probes complete"