按规划持续工作
This commit is contained in:
@@ -362,6 +362,12 @@ int main()
|
||||
"xyzac_switchkins_test_1.ngc",
|
||||
0.0,
|
||||
},
|
||||
{
|
||||
"xyzac_switchkins_test_2",
|
||||
"configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/"
|
||||
"xyzac_switchkins_test_2.ngc",
|
||||
0.0,
|
||||
},
|
||||
{
|
||||
"xyzac_switchkins_test_3",
|
||||
"configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/"
|
||||
@@ -404,10 +410,11 @@ int main()
|
||||
ok &= execute_demo_file(cases[0], demos[2]);
|
||||
ok &= execute_demo_file(cases[0], demos[3]);
|
||||
ok &= execute_demo_file(cases[0], demos[4]);
|
||||
ok &= execute_demo_file(cases[1], demos[5]);
|
||||
ok &= execute_demo_file(cases[0], demos[6]);
|
||||
ok &= execute_demo_file(cases[2], demos[7]);
|
||||
ok &= execute_demo_file(cases[3], demos[8]);
|
||||
ok &= execute_demo_file(cases[0], demos[5]);
|
||||
ok &= execute_demo_file(cases[1], demos[6]);
|
||||
ok &= execute_demo_file(cases[0], demos[7]);
|
||||
ok &= execute_demo_file(cases[2], demos[8]);
|
||||
ok &= execute_demo_file(cases[3], demos[9]);
|
||||
|
||||
print_bool("fiveaxis_linuxcnc_remap_execute_path", ok);
|
||||
return ok ? 0 : 1;
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
#define private public
|
||||
#include "emc/rs274ngc/rs274ngc_interp.hh"
|
||||
#undef private
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#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 {
|
||||
|
||||
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 print_error_text(Interp &interp, const std::string &name, int rc)
|
||||
{
|
||||
char error_buf[LINELEN] = {0};
|
||||
interp.error_text(rc, error_buf, sizeof(error_buf));
|
||||
std::cout << name << error_buf << "\n";
|
||||
}
|
||||
|
||||
void initialize_interp(Interp &interp)
|
||||
{
|
||||
standalone::reset_hal_adapter();
|
||||
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();
|
||||
}
|
||||
|
||||
std::filesystem::path subroutine_path(const std::filesystem::path &machine_dir,
|
||||
const std::string &entry)
|
||||
{
|
||||
const std::filesystem::path path(entry);
|
||||
if (path.is_absolute()) {
|
||||
return path;
|
||||
}
|
||||
return machine_dir / path;
|
||||
}
|
||||
|
||||
bool configure_remaps(Interp &interp,
|
||||
const std::filesystem::path &ini_path,
|
||||
const char *label,
|
||||
int expected_remaps)
|
||||
{
|
||||
linuxcnc::IniFile ini(ini_path.string());
|
||||
const bool opened = static_cast<bool>(ini);
|
||||
print_bool(std::string(label) + "_ini_open", opened);
|
||||
if (!opened) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto machine_dir = ini_path.parent_path();
|
||||
std::snprintf(interp._setup.program_prefix, sizeof(interp._setup.program_prefix), "%s",
|
||||
machine_dir.c_str());
|
||||
|
||||
int subroutine_count = 0;
|
||||
if (auto subroutine_path_text = ini.findString("SUBROUTINE_PATH", "RS274NGC")) {
|
||||
std::stringstream paths(*subroutine_path_text);
|
||||
std::string entry;
|
||||
while (std::getline(paths, entry, ':') && subroutine_count < MAX_SUB_DIRS) {
|
||||
const auto resolved = subroutine_path(machine_dir, entry);
|
||||
interp._setup.subroutines[subroutine_count++] = strstore(resolved.c_str());
|
||||
}
|
||||
}
|
||||
std::cout << label << "_subroutine_path_count=" << subroutine_count << "\n";
|
||||
|
||||
if (ini.findBoolV("OWORD_NARGS", "RS274NGC", false)) {
|
||||
interp._setup.feature_set |= FEATURE_OWORD_N_ARGS;
|
||||
}
|
||||
|
||||
bool ok = subroutine_count > 0;
|
||||
int remap_count = 0;
|
||||
while (auto remap = ini.findString(++remap_count, "REMAP", "RS274NGC")) {
|
||||
const int line_number = ini.lineOf(remap_count, "REMAP", "RS274NGC").second;
|
||||
const int rc = interp.parse_remap(remap->c_str(), line_number);
|
||||
std::cout << label << "_parse_remap_" << remap_count << "=" << rc << "\n";
|
||||
ok &= rc == INTERP_OK;
|
||||
}
|
||||
remap_count -= 1;
|
||||
std::cout << label << "_parsed_remap_count=" << remap_count << "\n";
|
||||
ok &= remap_count == expected_remaps;
|
||||
|
||||
print_bool(std::string(label) + "_remaps_ready", ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool execute_file(Interp &interp,
|
||||
const std::filesystem::path &program_path,
|
||||
const char *label,
|
||||
bool continue_on_error,
|
||||
bool expect_error)
|
||||
{
|
||||
bool ok = true;
|
||||
const int open_rc = interp.open(program_path.c_str());
|
||||
std::cout << label << "_file_open=" << open_rc << "\n";
|
||||
if (open_rc != INTERP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr int max_file_steps = 20000;
|
||||
int read_count = 0;
|
||||
int execute_count = 0;
|
||||
int finish_count = 0;
|
||||
int final_rc = INTERP_OK;
|
||||
bool reached_exit = false;
|
||||
bool reached_endfile = false;
|
||||
bool saw_error = false;
|
||||
|
||||
while (read_count < max_file_steps && execute_count < max_file_steps) {
|
||||
const int read_rc = interp.read();
|
||||
if (read_rc == INTERP_ENDFILE) {
|
||||
final_rc = read_rc;
|
||||
reached_endfile = true;
|
||||
break;
|
||||
}
|
||||
|
||||
++read_count;
|
||||
if ((read_rc != INTERP_OK) && (read_rc != INTERP_EXECUTE_FINISH)) {
|
||||
std::cout << label << "_file_read_" << read_count << "=" << read_rc << "\n";
|
||||
print_error_text(interp, std::string(label) + "_file_error_text=", read_rc);
|
||||
final_rc = read_rc;
|
||||
saw_error = true;
|
||||
if (!expect_error) {
|
||||
ok = false;
|
||||
}
|
||||
if (!continue_on_error) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const int execute_rc = interp.execute();
|
||||
++execute_count;
|
||||
final_rc = execute_rc;
|
||||
if (execute_rc == INTERP_EXECUTE_FINISH) {
|
||||
++finish_count;
|
||||
}
|
||||
if ((execute_rc != INTERP_OK) &&
|
||||
(execute_rc != INTERP_EXECUTE_FINISH) &&
|
||||
(execute_rc != INTERP_EXIT)) {
|
||||
std::cout << label << "_file_execute_" << execute_count << "=" << execute_rc << "\n";
|
||||
print_error_text(interp, std::string(label) + "_file_error_text=", execute_rc);
|
||||
saw_error = true;
|
||||
if (!expect_error) {
|
||||
ok = false;
|
||||
}
|
||||
if (!continue_on_error) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (execute_rc == INTERP_EXIT) {
|
||||
reached_exit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << label << "_file_read_count=" << read_count << "\n";
|
||||
std::cout << label << "_file_execute_count=" << execute_count << "\n";
|
||||
std::cout << label << "_file_finish_count=" << finish_count << "\n";
|
||||
std::cout << label << "_file_final_rc=" << final_rc << "\n";
|
||||
print_bool(std::string(label) + "_file_reached_exit", reached_exit);
|
||||
print_bool(std::string(label) + "_file_reached_endfile", reached_endfile);
|
||||
print_bool(std::string(label) + "_file_saw_error", saw_error);
|
||||
std::cout << label << "_canon_event_count=" << standalone::canon_events().size() << "\n";
|
||||
for (const auto &event : standalone::canon_events()) {
|
||||
std::cout << label << "_canon_event=" << event << "\n";
|
||||
}
|
||||
|
||||
ok &= read_count > 0;
|
||||
ok &= execute_count > 0;
|
||||
ok &= read_count < max_file_steps;
|
||||
ok &= execute_count < max_file_steps;
|
||||
ok &= reached_exit || reached_endfile;
|
||||
ok &= expect_error ? saw_error : !saw_error;
|
||||
print_bool(std::string(label) + "_linuxcnc_remap_file_execute", 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_mdi(Interp &interp, const char *label, const char *command)
|
||||
{
|
||||
int rc = interp.execute(command);
|
||||
std::cout << label << "_mdi_" << command_label(command) << "_execute_0=" << rc << "\n";
|
||||
|
||||
int finish_count = 0;
|
||||
while (rc == INTERP_EXECUTE_FINISH && finish_count < 16) {
|
||||
++finish_count;
|
||||
rc = interp.execute();
|
||||
std::cout << label << "_mdi_" << command_label(command)
|
||||
<< "_execute_" << finish_count << "=" << rc << "\n";
|
||||
}
|
||||
|
||||
std::cout << label << "_mdi_" << command_label(command)
|
||||
<< "_execute_finish_count=" << finish_count << "\n";
|
||||
return rc == INTERP_OK;
|
||||
}
|
||||
|
||||
bool execute_remap_io_mdi_case()
|
||||
{
|
||||
const auto test_dir = vendor_path("tests/remap/remap-io");
|
||||
const auto ini_path = test_dir / "test-ngc.ini";
|
||||
setenv("INI_FILE_NAME", ini_path.c_str(), 1);
|
||||
|
||||
Interp interp;
|
||||
initialize_interp(interp);
|
||||
|
||||
bool ok = configure_remaps(interp, ini_path, "remap_io_ngc", 7);
|
||||
|
||||
const struct MdiStep {
|
||||
const char *command;
|
||||
bool digital_input;
|
||||
int digital_value;
|
||||
bool analog_input;
|
||||
double analog_value;
|
||||
} steps[] = {
|
||||
{"M62 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X1.0\n", false, 0, false, 0.0},
|
||||
{"M63 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X2.0\n", false, 0, false, 0.0},
|
||||
{"M62 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X3.0\n", false, 0, false, 0.0},
|
||||
{"M63 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X4.0\n", false, 0, false, 0.0},
|
||||
{"M64 P1\n", false, 0, false, 0.0},
|
||||
{"M65 P1\n", false, 0, false, 0.0},
|
||||
{"M64 P1\n", false, 0, false, 0.0},
|
||||
{"M65 P1\n", false, 0, false, 0.0},
|
||||
{"M66 P1\n", true, 1, false, 0.0},
|
||||
{"M66 P1\n", true, 0, false, 0.0},
|
||||
{"M66 E1 L0\n", false, 0, true, 42.13},
|
||||
{"M66 E1 L0\n", false, 0, true, -13.42},
|
||||
{"M67 E1 Q42.13\n", false, 0, false, 0.0},
|
||||
{"G0 X-1.0\n", false, 0, false, 0.0},
|
||||
{"M67 E1 Q-13.42\n", false, 0, false, 0.0},
|
||||
{"G0 X-2.0\n", false, 0, false, 0.0},
|
||||
{"M67 E1 Q0.0\n", false, 0, false, 0.0},
|
||||
{"G0 X-3.0\n", false, 0, false, 0.0},
|
||||
{"M68 E1 Q42.13\n", false, 0, false, 0.0},
|
||||
{"M68 E1 Q-13.42\n", false, 0, false, 0.0},
|
||||
{"M68 E1 Q0.0\n", false, 0, false, 0.0},
|
||||
};
|
||||
|
||||
for (const auto &step : steps) {
|
||||
if (step.digital_input) {
|
||||
standalone::set_external_digital_input(0, step.digital_value);
|
||||
}
|
||||
if (step.analog_input) {
|
||||
standalone::set_external_analog_input(0, step.analog_value);
|
||||
}
|
||||
ok &= execute_mdi(interp, "remap_io_ngc", step.command);
|
||||
std::cout << "remap_io_ngc_parameter_5399=" << interp._setup.parameters[5399] << "\n";
|
||||
std::cout << "remap_io_ngc_parameter_100=" << interp._setup.parameters[100] << "\n";
|
||||
}
|
||||
|
||||
std::cout << "remap_io_ngc_canon_event_count=" << standalone::canon_events().size() << "\n";
|
||||
for (const auto &event : standalone::canon_events()) {
|
||||
std::cout << "remap_io_ngc_canon_event=" << event << "\n";
|
||||
}
|
||||
|
||||
ok &= std::fabs(interp._setup.parameters[5399] - -13.42) < 1e-9;
|
||||
ok &= std::fabs(interp._setup.parameters[100] - -13.42) < 1e-9;
|
||||
print_bool("remap_io_ngc_linuxcnc_mdi_sequence", ok);
|
||||
unsetenv("INI_FILE_NAME");
|
||||
return ok;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
{
|
||||
struct RegressionCase {
|
||||
const char *label;
|
||||
const char *rel;
|
||||
int expected_remaps;
|
||||
bool continue_on_error;
|
||||
bool expect_error;
|
||||
};
|
||||
|
||||
const RegressionCase cases[] = {
|
||||
{"duplicate_oword", "tests/remap/duplicate-o-word", 2, false, false},
|
||||
{"fail_args_0", "tests/remap/fail/args.0", 1, false, false},
|
||||
{"fail_args_1", "tests/remap/fail/args.1", 1, true, true},
|
||||
{"fail_args_2", "tests/remap/fail/args.2", 1, true, true},
|
||||
{"fail_body_ngc", "tests/remap/fail/body-ngc", 1, true, true},
|
||||
{"m30_interaction", "tests/remap/m30-interaction", 1, false, false},
|
||||
{"nested_remaps_oword", "tests/remap/nested-remaps-oword", 4, false, false},
|
||||
{"posargs_0", "tests/remap/posargs.0", 1, false, false},
|
||||
{"sequencing", "tests/remap/sequencing", 7, false, false},
|
||||
};
|
||||
|
||||
bool all_ok = true;
|
||||
for (const auto &test : cases) {
|
||||
const auto test_dir = vendor_path(test.rel);
|
||||
const auto ini_path = test_dir / "test.ini";
|
||||
const auto program_path = test_dir / "test.ngc";
|
||||
|
||||
setenv("INI_FILE_NAME", ini_path.c_str(), 1);
|
||||
|
||||
Interp interp;
|
||||
initialize_interp(interp);
|
||||
|
||||
bool ok = configure_remaps(interp, ini_path, test.label, test.expected_remaps);
|
||||
ok &= execute_file(interp,
|
||||
program_path,
|
||||
test.label,
|
||||
test.continue_on_error,
|
||||
test.expect_error);
|
||||
print_bool(std::string(test.label) + "_linuxcnc_regression_path", ok);
|
||||
all_ok &= ok;
|
||||
|
||||
unsetenv("INI_FILE_NAME");
|
||||
}
|
||||
|
||||
all_ok &= execute_remap_io_mdi_case();
|
||||
|
||||
print_bool("linuxcnc_upstream_remap_regression_path", all_ok);
|
||||
return all_ok ? 0 : 1;
|
||||
}
|
||||
@@ -59,6 +59,18 @@ std::unordered_map<std::string, std::vector<std::string>> &pin_aliases()
|
||||
return values;
|
||||
}
|
||||
|
||||
std::unordered_map<int, int> &digital_inputs()
|
||||
{
|
||||
static std::unordered_map<int, int> values;
|
||||
return values;
|
||||
}
|
||||
|
||||
std::unordered_map<int, double> &analog_inputs()
|
||||
{
|
||||
static std::unordered_map<int, double> values;
|
||||
return values;
|
||||
}
|
||||
|
||||
int lookup_hal_value(const char *name, std::unordered_map<std::string, HalEntry> &values,
|
||||
hal_type_t *type, hal_data_u **ptr, bool *connected)
|
||||
{
|
||||
@@ -89,6 +101,8 @@ void reset_hal_adapter()
|
||||
signals().clear();
|
||||
params().clear();
|
||||
pin_aliases().clear();
|
||||
digital_inputs().clear();
|
||||
analog_inputs().clear();
|
||||
}
|
||||
|
||||
void set_hal_value(HalValueKind kind, const char *name, hal_type_t type, hal_data_u value,
|
||||
@@ -130,6 +144,28 @@ void sync_motion_analog_output(int index, double value)
|
||||
}
|
||||
}
|
||||
|
||||
void set_external_digital_input(int index, int value)
|
||||
{
|
||||
digital_inputs()[index] = value ? 1 : 0;
|
||||
}
|
||||
|
||||
void set_external_analog_input(int index, double value)
|
||||
{
|
||||
analog_inputs()[index] = value;
|
||||
}
|
||||
|
||||
int external_digital_input(int index, int def)
|
||||
{
|
||||
auto it = digital_inputs().find(index);
|
||||
return it == digital_inputs().end() ? def : it->second;
|
||||
}
|
||||
|
||||
double external_analog_input(int index, double def)
|
||||
{
|
||||
auto it = analog_inputs().find(index);
|
||||
return it == analog_inputs().end() ? def : it->second;
|
||||
}
|
||||
|
||||
} // namespace standalone
|
||||
|
||||
int hal_init(const char *)
|
||||
|
||||
@@ -15,5 +15,9 @@ void set_hal_value(HalValueKind kind, const char *name, hal_type_t type, hal_dat
|
||||
bool connected = true);
|
||||
void link_hal_pin_alias(const char *source, const char *alias);
|
||||
void sync_motion_analog_output(int index, double value);
|
||||
void set_external_digital_input(int index, int value);
|
||||
void set_external_analog_input(int index, double value);
|
||||
int external_digital_input(int index, int def);
|
||||
double external_analog_input(int index, double def);
|
||||
|
||||
} // namespace standalone
|
||||
|
||||
@@ -3,13 +3,19 @@
|
||||
#undef private
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "canon_event_sink.hh"
|
||||
#include "emc/ini/inifile.hh"
|
||||
#include "linuxcnc_hal_adapter.hh"
|
||||
#include "linuxcnc_machine_config.hh"
|
||||
#include "linuxcnc_runtime_state.hh"
|
||||
#include "linuxcnc_tool_adapter.hh"
|
||||
|
||||
namespace {
|
||||
@@ -70,6 +76,7 @@ void initialize_minimal_interp(Interp &interp)
|
||||
{
|
||||
seed_hal_values();
|
||||
seed_tool_values();
|
||||
standalone::reset_external_axis_mask();
|
||||
interp._setup.length_units = CANON_UNITS_MM;
|
||||
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
interp._setup.ijk_distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
@@ -85,7 +92,45 @@ void initialize_minimal_interp(Interp &interp)
|
||||
interp._setup.parameter_g83_peck_clearance = 1.0;
|
||||
interp._setup.parameters[5599] = 1.0;
|
||||
interp.load_tool_table();
|
||||
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
|
||||
standalone::apply_axis_mask_to_interp(interp, standalone::axis_mask_from_coordinates("XYZABCUVW"));
|
||||
standalone::reset_user_m_code_registry();
|
||||
}
|
||||
|
||||
void apply_ini_search_paths(Interp &interp, const char *ini_path)
|
||||
{
|
||||
if (!ini_path || ini_path[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
if (!ini) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto machine_dir = std::filesystem::path(ini_path).parent_path();
|
||||
standalone::apply_machine_axis_config(interp, ini);
|
||||
standalone::load_tool_table_from_ini(interp, ini, machine_dir.c_str());
|
||||
standalone::register_user_m_codes_from_ini(ini, machine_dir.c_str());
|
||||
|
||||
std::snprintf(interp._setup.program_prefix, sizeof(interp._setup.program_prefix), "%s",
|
||||
machine_dir.c_str());
|
||||
|
||||
int subroutine_count = 0;
|
||||
if (auto subroutine_path_text = ini.findString("SUBROUTINE_PATH", "RS274NGC")) {
|
||||
std::stringstream paths(*subroutine_path_text);
|
||||
std::string entry;
|
||||
while (std::getline(paths, entry, ':') && subroutine_count < MAX_SUB_DIRS) {
|
||||
std::filesystem::path resolved(entry);
|
||||
if (resolved.is_relative()) {
|
||||
resolved = machine_dir / resolved;
|
||||
}
|
||||
interp._setup.subroutines[subroutine_count++] = strstore(resolved.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (ini.findBoolV("OWORD_NARGS", "RS274NGC", false)) {
|
||||
interp._setup.feature_set |= FEATURE_OWORD_N_ARGS;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> load_program(int argc, char **argv)
|
||||
@@ -117,11 +162,25 @@ std::vector<std::string> load_program(int argc, char **argv)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
bool continue_on_error = false;
|
||||
int arg_offset = 1;
|
||||
if (argc > 1 && std::strcmp(argv[1], "--continue-on-error") == 0) {
|
||||
continue_on_error = true;
|
||||
arg_offset = 2;
|
||||
}
|
||||
|
||||
Interp interp;
|
||||
standalone::reset_canon_events();
|
||||
const char *ini_path = argc > arg_offset + 1 ? argv[arg_offset + 1] : nullptr;
|
||||
if (ini_path && ini_path[0] != '\0') {
|
||||
setenv("INI_FILE_NAME", ini_path, 1);
|
||||
} else {
|
||||
unsetenv("INI_FILE_NAME");
|
||||
}
|
||||
initialize_minimal_interp(interp);
|
||||
apply_ini_search_paths(interp, ini_path);
|
||||
|
||||
const std::vector<std::string> program = load_program(argc, argv);
|
||||
const std::vector<std::string> program = load_program(argc - arg_offset + 1, argv + arg_offset - 1);
|
||||
if (program.empty()) {
|
||||
std::cerr << "empty fixture\n";
|
||||
return 2;
|
||||
@@ -138,6 +197,7 @@ int main(int argc, char **argv)
|
||||
const int read_rc = interp.read(program.front().c_str());
|
||||
|
||||
initialize_minimal_interp(interp);
|
||||
apply_ini_search_paths(interp, ini_path);
|
||||
for (std::size_t idx = 0; idx < program.size(); ++idx) {
|
||||
interp._setup.sequence_number = static_cast<int>(idx + 1);
|
||||
const int execute_rc = interp.execute(program[idx].c_str());
|
||||
@@ -152,10 +212,13 @@ int main(int argc, char **argv)
|
||||
int file_open_rc = INTERP_FILE_NOT_OPEN;
|
||||
int file_read_count = 0;
|
||||
int file_execute_count = 0;
|
||||
if (argc > 1) {
|
||||
bool file_saw_error = false;
|
||||
if (argc > arg_offset) {
|
||||
Interp file_interp;
|
||||
standalone::reset_canon_events();
|
||||
initialize_minimal_interp(file_interp);
|
||||
file_open_rc = file_interp.open(argv[1]);
|
||||
apply_ini_search_paths(file_interp, ini_path);
|
||||
file_open_rc = file_interp.open(argv[arg_offset]);
|
||||
if (file_open_rc == INTERP_OK) {
|
||||
while (true) {
|
||||
const int file_read_rc = file_interp.read();
|
||||
@@ -166,7 +229,16 @@ int main(int argc, char **argv)
|
||||
++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;
|
||||
if (file_read_rc > INTERP_MIN_ERROR) {
|
||||
char error_buf[LINELEN] = {0};
|
||||
file_interp.error_text(file_read_rc, error_buf, sizeof(error_buf));
|
||||
std::cout << "file_error_text=" << error_buf << "\n";
|
||||
}
|
||||
file_saw_error = true;
|
||||
if (!continue_on_error) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const int file_execute_rc = file_interp.execute();
|
||||
@@ -180,6 +252,7 @@ int main(int argc, char **argv)
|
||||
if ((file_execute_rc != INTERP_OK) &&
|
||||
(file_execute_rc != INTERP_EXECUTE_FINISH) &&
|
||||
(file_execute_rc != INTERP_EXIT)) {
|
||||
file_saw_error = true;
|
||||
break;
|
||||
}
|
||||
if (file_execute_rc == INTERP_EXIT) {
|
||||
@@ -200,6 +273,7 @@ int main(int argc, char **argv)
|
||||
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 << "file_saw_error=" << (file_saw_error ? 1 : 0) << "\n";
|
||||
std::cout << "raw_line=" << raw_line << "\n";
|
||||
std::cout << "cooked_line=" << cooked_line << "\n";
|
||||
std::cout << "line_length=" << length << "\n";
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "emc/rs274ngc/rs274ngc_return.hh"
|
||||
#include "canon_event_sink.hh"
|
||||
#include "linuxcnc_hal_adapter.hh"
|
||||
#include "linuxcnc_runtime_state.hh"
|
||||
#include "linuxcnc_tool_adapter.hh"
|
||||
#include "nml_intf/emc.hh"
|
||||
|
||||
@@ -20,6 +21,7 @@ namespace standalone {
|
||||
|
||||
static std::vector<std::string> g_canon_events;
|
||||
static double g_external_feed_rate = 0.0;
|
||||
static int g_external_axis_mask = Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z;
|
||||
|
||||
void reset_canon_events()
|
||||
{
|
||||
@@ -36,6 +38,23 @@ const std::vector<std::string> &canon_events()
|
||||
return g_canon_events;
|
||||
}
|
||||
|
||||
void reset_external_axis_mask()
|
||||
{
|
||||
g_external_axis_mask = Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z;
|
||||
}
|
||||
|
||||
void set_external_axis_mask(int axis_mask)
|
||||
{
|
||||
g_external_axis_mask = axis_mask == 0
|
||||
? (Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z)
|
||||
: axis_mask;
|
||||
}
|
||||
|
||||
int external_axis_mask()
|
||||
{
|
||||
return g_external_axis_mask;
|
||||
}
|
||||
|
||||
} // namespace standalone
|
||||
|
||||
bool GET_BLOCK_DELETE(void) { return false; }
|
||||
@@ -523,9 +542,15 @@ int GET_EXTERNAL_FEED_OVERRIDE_ENABLE() { return 1; }
|
||||
int GET_EXTERNAL_SPINDLE_OVERRIDE_ENABLE(int) { return 1; }
|
||||
int GET_EXTERNAL_ADAPTIVE_FEED_ENABLE() { return 1; }
|
||||
int GET_EXTERNAL_FEED_HOLD_ENABLE() { return 1; }
|
||||
int GET_EXTERNAL_DIGITAL_INPUT(int, int def) { return def; }
|
||||
double GET_EXTERNAL_ANALOG_INPUT(int, double def) { return def; }
|
||||
int GET_EXTERNAL_AXIS_MASK() { return 7; }
|
||||
int GET_EXTERNAL_DIGITAL_INPUT(int index, int def)
|
||||
{
|
||||
return standalone::external_digital_input(index, def);
|
||||
}
|
||||
double GET_EXTERNAL_ANALOG_INPUT(int index, double def)
|
||||
{
|
||||
return standalone::external_analog_input(index, def);
|
||||
}
|
||||
int GET_EXTERNAL_AXIS_MASK() { return standalone::external_axis_mask(); }
|
||||
int GET_EXTERNAL_OFFSET_APPLIED() { return 0; }
|
||||
EmcPose GET_EXTERNAL_OFFSETS() { return {{0, 0, 0}, 0, 0, 0, 0, 0, 0}; }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "emc/rs274ngc/rs274ngc_interp.hh"
|
||||
#undef private
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
@@ -13,6 +14,8 @@
|
||||
#include "canon_event_sink.hh"
|
||||
#include "emc/ini/inifile.hh"
|
||||
#include "linuxcnc_hal_adapter.hh"
|
||||
#include "linuxcnc_machine_config.hh"
|
||||
#include "linuxcnc_runtime_state.hh"
|
||||
#include "linuxcnc_tool_adapter.hh"
|
||||
#include "nml_intf/emc.hh"
|
||||
#include "tooldata.hh"
|
||||
@@ -105,6 +108,7 @@ void initialize_minimal_interp(Interp &interp)
|
||||
{
|
||||
seed_hal_values();
|
||||
seed_tool_values();
|
||||
standalone::reset_external_axis_mask();
|
||||
interp._setup.length_units = CANON_UNITS_MM;
|
||||
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
interp._setup.ijk_distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
@@ -120,13 +124,15 @@ void initialize_minimal_interp(Interp &interp)
|
||||
interp._setup.parameter_g83_peck_clearance = 1.0;
|
||||
interp._setup.parameters[5599] = 1.0;
|
||||
interp.load_tool_table();
|
||||
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
|
||||
standalone::apply_axis_mask_to_interp(interp, standalone::axis_mask_from_coordinates("XYZABCUVW"));
|
||||
standalone::reset_user_m_code_registry();
|
||||
}
|
||||
|
||||
void initialize_fiveaxis_remap_interp(Interp &interp)
|
||||
{
|
||||
seed_switchkins_hal();
|
||||
seed_tool_values();
|
||||
standalone::reset_external_axis_mask();
|
||||
interp._setup.length_units = CANON_UNITS_MM;
|
||||
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
interp._setup.ijk_distance_mode = DISTANCE_MODE::INCREMENTAL;
|
||||
@@ -142,7 +148,8 @@ void initialize_fiveaxis_remap_interp(Interp &interp)
|
||||
interp._setup.parameter_g83_peck_clearance = 1.0;
|
||||
interp._setup.parameters[5599] = 1.0;
|
||||
interp.load_tool_table();
|
||||
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
|
||||
standalone::apply_axis_mask_to_interp(interp, standalone::axis_mask_from_coordinates("XYZABCUVW"));
|
||||
standalone::reset_user_m_code_registry();
|
||||
interp.init_named_parameters();
|
||||
}
|
||||
|
||||
@@ -274,6 +281,15 @@ void append_named_value(std::ostringstream &output, Interp &interp, const char *
|
||||
output << name << ": rc=" << rc << " found=" << status << " value=" << value << "\n";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::string parent_path(const char *path)
|
||||
{
|
||||
std::string value = path ? path : "";
|
||||
@@ -329,6 +345,88 @@ bool parse_machine_remaps(Interp &interp, std::ostringstream &output, const char
|
||||
return ok;
|
||||
}
|
||||
|
||||
std::string remap_subroutine_path(const std::string &machine_dir, const std::string &entry)
|
||||
{
|
||||
if (!entry.empty() && entry[0] == '/') {
|
||||
return entry;
|
||||
}
|
||||
if (machine_dir == "/") {
|
||||
return machine_dir + entry;
|
||||
}
|
||||
return machine_dir + "/" + entry;
|
||||
}
|
||||
|
||||
int apply_ini_search_paths(Interp &interp, const linuxcnc::IniFile &ini, const std::string &machine_dir)
|
||||
{
|
||||
standalone::apply_machine_axis_config(interp, ini);
|
||||
standalone::load_tool_table_from_ini(interp, ini, machine_dir.c_str());
|
||||
standalone::register_user_m_codes_from_ini(ini, machine_dir.c_str());
|
||||
|
||||
std::snprintf(interp._setup.program_prefix, sizeof(interp._setup.program_prefix), "%s",
|
||||
machine_dir.c_str());
|
||||
|
||||
int subroutine_count = 0;
|
||||
if (auto subroutine_path_text = ini.findString("SUBROUTINE_PATH", "RS274NGC")) {
|
||||
std::istringstream paths(*subroutine_path_text);
|
||||
std::string entry;
|
||||
while (std::getline(paths, entry, ':') && subroutine_count < MAX_SUB_DIRS) {
|
||||
const std::string resolved = remap_subroutine_path(machine_dir, entry);
|
||||
interp._setup.subroutines[subroutine_count++] = strstore(resolved.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (ini.findBoolV("OWORD_NARGS", "RS274NGC", false)) {
|
||||
interp._setup.feature_set |= FEATURE_OWORD_N_ARGS;
|
||||
}
|
||||
|
||||
return subroutine_count;
|
||||
}
|
||||
|
||||
bool apply_ini_search_paths(Interp &interp, const char *ini_path)
|
||||
{
|
||||
if (!ini_path || ini_path[0] == '\0') {
|
||||
return true;
|
||||
}
|
||||
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
if (!ini) {
|
||||
return false;
|
||||
}
|
||||
|
||||
apply_ini_search_paths(interp, ini, parent_path(ini_path));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_remaps_from_ini(Interp &interp, std::ostringstream &output, const char *ini_path)
|
||||
{
|
||||
const std::string machine_dir = parent_path(ini_path);
|
||||
|
||||
linuxcnc::IniFile ini(ini_path ? ini_path : "");
|
||||
const bool opened = static_cast<bool>(ini);
|
||||
output << "remap_ini_open=" << (opened ? 1 : 0) << "\n";
|
||||
if (!opened) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int subroutine_count = apply_ini_search_paths(interp, ini, machine_dir);
|
||||
output << "remap_subroutine_path_count=" << subroutine_count << "\n";
|
||||
|
||||
bool ok = subroutine_count > 0;
|
||||
int remap_count = 0;
|
||||
while (auto remap = ini.findString(++remap_count, "REMAP", "RS274NGC")) {
|
||||
const int line_number = ini.lineOf(remap_count, "REMAP", "RS274NGC").second;
|
||||
const int rc = interp.parse_remap(remap->c_str(), line_number);
|
||||
output << "remap_parse_remap_" << remap_count << "=" << rc << "\n";
|
||||
ok &= rc == INTERP_OK;
|
||||
}
|
||||
|
||||
remap_count -= 1;
|
||||
output << "remap_parsed_remap_count=" << remap_count << "\n";
|
||||
ok &= remap_count > 0;
|
||||
output << "remap_remaps_ready=" << (ok ? 1 : 0) << "\n";
|
||||
return ok;
|
||||
}
|
||||
|
||||
void apply_parameter_assignments(Interp &interp, const char *assignments)
|
||||
{
|
||||
std::istringstream input(assignments ? assignments : "");
|
||||
@@ -371,6 +469,7 @@ char *lcinterp_run_program_with_ini(const char *program_text, const char *ini_pa
|
||||
Interp interp;
|
||||
standalone::reset_canon_events();
|
||||
initialize_minimal_interp(interp);
|
||||
apply_ini_search_paths(interp, ini_path);
|
||||
|
||||
const std::vector<std::string> program = split_program_lines(program_text);
|
||||
std::ostringstream output;
|
||||
@@ -401,8 +500,7 @@ char *lcinterp_run_program(const char *program_text)
|
||||
return lcinterp_run_program_with_ini(program_text, nullptr);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
|
||||
char *run_file_with_ini_internal(const char *path, const char *ini_path, bool continue_on_error)
|
||||
{
|
||||
if (ini_path && ini_path[0] != '\0') {
|
||||
setenv("INI_FILE_NAME", ini_path, 1);
|
||||
@@ -415,6 +513,8 @@ char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
|
||||
initialize_minimal_interp(interp);
|
||||
|
||||
std::ostringstream output;
|
||||
apply_ini_search_paths(interp, ini_path);
|
||||
|
||||
const int open_rc = interp.open(path);
|
||||
output << "file_open=" << open_rc << "\n";
|
||||
if (open_rc != INTERP_OK) {
|
||||
@@ -429,6 +529,7 @@ char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
|
||||
|
||||
int file_read_count = 0;
|
||||
int file_execute_count = 0;
|
||||
bool saw_error = false;
|
||||
while (true) {
|
||||
const int read_rc = interp.read();
|
||||
if (read_rc == INTERP_ENDFILE) {
|
||||
@@ -444,7 +545,11 @@ char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
|
||||
interp.error_text(read_rc, error_buf, sizeof(error_buf));
|
||||
output << "file_error_text=" << error_buf << "\n";
|
||||
}
|
||||
break;
|
||||
saw_error = true;
|
||||
if (!continue_on_error) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const int execute_rc = interp.execute();
|
||||
@@ -458,6 +563,7 @@ char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
|
||||
if ((execute_rc != INTERP_OK) &&
|
||||
(execute_rc != INTERP_EXECUTE_FINISH) &&
|
||||
(execute_rc != INTERP_EXIT)) {
|
||||
saw_error = true;
|
||||
break;
|
||||
}
|
||||
if (execute_rc == INTERP_EXIT) {
|
||||
@@ -467,10 +573,23 @@ char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
|
||||
|
||||
output << "file_read_count=" << file_read_count << "\n";
|
||||
output << "file_execute_count=" << file_execute_count << "\n";
|
||||
output << "file_saw_error=" << (saw_error ? 1 : 0) << "\n";
|
||||
append_events_and_state(output, interp);
|
||||
return copy_result(output.str());
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
|
||||
{
|
||||
return run_file_with_ini_internal(path, ini_path, false);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_run_file_with_ini_continue_on_error(const char *path, const char *ini_path)
|
||||
{
|
||||
return run_file_with_ini_internal(path, ini_path, true);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_run_file(const char *path)
|
||||
{
|
||||
@@ -573,6 +692,230 @@ char *lcinterp_run_fiveaxis_remap_file(const char *path, const char *ini_path)
|
||||
return copy_result(output.str());
|
||||
}
|
||||
|
||||
char *run_remap_file_internal(const char *path,
|
||||
const char *ini_path,
|
||||
bool continue_on_error,
|
||||
bool expect_error)
|
||||
{
|
||||
if (ini_path && ini_path[0] != '\0') {
|
||||
setenv("INI_FILE_NAME", ini_path, 1);
|
||||
} else {
|
||||
unsetenv("INI_FILE_NAME");
|
||||
}
|
||||
|
||||
Interp interp;
|
||||
standalone::reset_canon_events();
|
||||
initialize_minimal_interp(interp);
|
||||
interp.init_named_parameters();
|
||||
|
||||
std::ostringstream output;
|
||||
bool ok = parse_remaps_from_ini(interp, output, ini_path);
|
||||
|
||||
const int open_rc = interp.open(path);
|
||||
output << "remap_file_open=" << open_rc << "\n";
|
||||
if (open_rc != INTERP_OK) {
|
||||
append_error_text(output, interp, "remap_file_error_text=", open_rc);
|
||||
output << "remap_linuxcnc_file_execute=0\n";
|
||||
unsetenv("INI_FILE_NAME");
|
||||
return copy_result(output.str());
|
||||
}
|
||||
|
||||
constexpr int max_file_steps = 20000;
|
||||
int file_read_count = 0;
|
||||
int file_execute_count = 0;
|
||||
int file_finish_count = 0;
|
||||
int final_rc = INTERP_OK;
|
||||
bool reached_exit = false;
|
||||
bool reached_endfile = false;
|
||||
bool saw_error = false;
|
||||
|
||||
while ((file_read_count < max_file_steps) && (file_execute_count < max_file_steps)) {
|
||||
const int read_rc = interp.read();
|
||||
if (read_rc == INTERP_ENDFILE) {
|
||||
output << "remap_file_read_eof=" << read_rc << "\n";
|
||||
final_rc = read_rc;
|
||||
reached_endfile = true;
|
||||
break;
|
||||
}
|
||||
|
||||
++file_read_count;
|
||||
if ((read_rc != INTERP_OK) && (read_rc != INTERP_EXECUTE_FINISH)) {
|
||||
output << "remap_file_read_" << file_read_count << "=" << read_rc << "\n";
|
||||
append_error_text(output, interp, "remap_file_error_text=", read_rc);
|
||||
final_rc = read_rc;
|
||||
saw_error = true;
|
||||
if (!expect_error) {
|
||||
ok = false;
|
||||
}
|
||||
if (!continue_on_error) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const int execute_rc = interp.execute();
|
||||
++file_execute_count;
|
||||
final_rc = execute_rc;
|
||||
if (execute_rc == INTERP_EXECUTE_FINISH) {
|
||||
++file_finish_count;
|
||||
}
|
||||
if ((execute_rc != INTERP_OK) && (execute_rc != INTERP_EXIT) &&
|
||||
(execute_rc != INTERP_EXECUTE_FINISH)) {
|
||||
output << "remap_file_execute_" << file_execute_count << "=" << execute_rc << "\n";
|
||||
append_error_text(output, interp, "remap_file_error_text=", execute_rc);
|
||||
saw_error = true;
|
||||
if (!expect_error) {
|
||||
ok = false;
|
||||
}
|
||||
if (!continue_on_error) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (execute_rc == INTERP_EXIT) {
|
||||
reached_exit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
output << "remap_file_read_count=" << file_read_count << "\n";
|
||||
output << "remap_file_execute_count=" << file_execute_count << "\n";
|
||||
output << "remap_file_finish_count=" << file_finish_count << "\n";
|
||||
output << "remap_file_final_rc=" << final_rc << "\n";
|
||||
output << "remap_file_reached_exit=" << (reached_exit ? 1 : 0) << "\n";
|
||||
output << "remap_file_reached_endfile=" << (reached_endfile ? 1 : 0) << "\n";
|
||||
output << "remap_file_saw_error=" << (saw_error ? 1 : 0) << "\n";
|
||||
for (const auto &event : standalone::canon_events()) {
|
||||
output << "remap_canon_event=" << event << "\n";
|
||||
}
|
||||
|
||||
ok &= file_read_count > 0;
|
||||
ok &= file_execute_count > 0;
|
||||
ok &= file_read_count < max_file_steps;
|
||||
ok &= file_execute_count < max_file_steps;
|
||||
ok &= reached_exit || reached_endfile;
|
||||
ok &= expect_error ? saw_error : !saw_error;
|
||||
|
||||
output << "remap_linuxcnc_file_execute=" << (ok ? 1 : 0) << "\n";
|
||||
unsetenv("INI_FILE_NAME");
|
||||
return copy_result(output.str());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
bool execute_remap_mdi_command(Interp &interp, std::ostringstream &output, const char *command)
|
||||
{
|
||||
const std::string label = command_label(command);
|
||||
int rc = interp.execute(command);
|
||||
output << "remap_mdi_" << label << "_execute_0=" << rc << "\n";
|
||||
|
||||
int finish_count = 0;
|
||||
while (rc == INTERP_EXECUTE_FINISH && finish_count < 16) {
|
||||
++finish_count;
|
||||
rc = interp.execute();
|
||||
output << "remap_mdi_" << label << "_execute_" << finish_count << "=" << rc << "\n";
|
||||
}
|
||||
|
||||
output << "remap_mdi_" << label << "_execute_finish_count=" << finish_count << "\n";
|
||||
return rc == INTERP_OK;
|
||||
}
|
||||
|
||||
char *run_remap_io_mdi_sequence_internal(const char *ini_path)
|
||||
{
|
||||
if (ini_path && ini_path[0] != '\0') {
|
||||
setenv("INI_FILE_NAME", ini_path, 1);
|
||||
} else {
|
||||
unsetenv("INI_FILE_NAME");
|
||||
}
|
||||
|
||||
Interp interp;
|
||||
standalone::reset_canon_events();
|
||||
initialize_minimal_interp(interp);
|
||||
interp.init_named_parameters();
|
||||
|
||||
std::ostringstream output;
|
||||
bool ok = parse_remaps_from_ini(interp, output, ini_path);
|
||||
ok &= output.str().find("remap_parsed_remap_count=7") != std::string::npos;
|
||||
|
||||
const struct MdiStep {
|
||||
const char *command;
|
||||
bool digital_input;
|
||||
int digital_value;
|
||||
bool analog_input;
|
||||
double analog_value;
|
||||
} steps[] = {
|
||||
{"M62 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X1.0\n", false, 0, false, 0.0},
|
||||
{"M63 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X2.0\n", false, 0, false, 0.0},
|
||||
{"M62 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X3.0\n", false, 0, false, 0.0},
|
||||
{"M63 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X4.0\n", false, 0, false, 0.0},
|
||||
{"M64 P1\n", false, 0, false, 0.0},
|
||||
{"M65 P1\n", false, 0, false, 0.0},
|
||||
{"M64 P1\n", false, 0, false, 0.0},
|
||||
{"M65 P1\n", false, 0, false, 0.0},
|
||||
{"M66 P1\n", true, 1, false, 0.0},
|
||||
{"M66 P1\n", true, 0, false, 0.0},
|
||||
{"M66 E1 L0\n", false, 0, true, 42.13},
|
||||
{"M66 E1 L0\n", false, 0, true, -13.42},
|
||||
{"M67 E1 Q42.13\n", false, 0, false, 0.0},
|
||||
{"G0 X-1.0\n", false, 0, false, 0.0},
|
||||
{"M67 E1 Q-13.42\n", false, 0, false, 0.0},
|
||||
{"G0 X-2.0\n", false, 0, false, 0.0},
|
||||
{"M67 E1 Q0.0\n", false, 0, false, 0.0},
|
||||
{"G0 X-3.0\n", false, 0, false, 0.0},
|
||||
{"M68 E1 Q42.13\n", false, 0, false, 0.0},
|
||||
{"M68 E1 Q-13.42\n", false, 0, false, 0.0},
|
||||
{"M68 E1 Q0.0\n", false, 0, false, 0.0},
|
||||
};
|
||||
|
||||
for (const auto &step : steps) {
|
||||
if (step.digital_input) {
|
||||
standalone::set_external_digital_input(0, step.digital_value);
|
||||
}
|
||||
if (step.analog_input) {
|
||||
standalone::set_external_analog_input(0, step.analog_value);
|
||||
}
|
||||
ok &= execute_remap_mdi_command(interp, output, step.command);
|
||||
output << "remap_mdi_parameter_5399=" << interp._setup.parameters[5399] << "\n";
|
||||
output << "remap_mdi_parameter_100=" << interp._setup.parameters[100] << "\n";
|
||||
}
|
||||
|
||||
output << "remap_mdi_canon_event_count=" << standalone::canon_events().size() << "\n";
|
||||
for (const auto &event : standalone::canon_events()) {
|
||||
output << "remap_mdi_canon_event=" << event << "\n";
|
||||
}
|
||||
|
||||
ok &= std::fabs(interp._setup.parameters[5399] - -13.42) < 1e-9;
|
||||
ok &= std::fabs(interp._setup.parameters[100] - -13.42) < 1e-9;
|
||||
output << "remap_io_ngc_linuxcnc_mdi_sequence=" << (ok ? 1 : 0) << "\n";
|
||||
unsetenv("INI_FILE_NAME");
|
||||
return copy_result(output.str());
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_run_remap_file(const char *path, const char *ini_path)
|
||||
{
|
||||
return run_remap_file_internal(path, ini_path, false, false);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_run_remap_file_continue_on_error(const char *path, const char *ini_path)
|
||||
{
|
||||
return run_remap_file_internal(path, ini_path, true, true);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_run_remap_io_mdi_sequence(const char *ini_path)
|
||||
{
|
||||
return run_remap_io_mdi_sequence_internal(ini_path);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_restore_parameters(const char *path)
|
||||
{
|
||||
|
||||
280
wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_machine_config.cpp
Normal file
280
wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_machine_config.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
#define private public
|
||||
#include "emc/rs274ngc/rs274ngc_interp.hh"
|
||||
#undef private
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "emc/ini/inifile.hh"
|
||||
#include "canon_event_sink.hh"
|
||||
#include "linuxcnc_machine_config.hh"
|
||||
#include "linuxcnc_runtime_state.hh"
|
||||
#include "tooldata.hh"
|
||||
|
||||
namespace standalone {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kDefaultAxisMask = Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z;
|
||||
constexpr int kMaxUserMDirs = 6;
|
||||
|
||||
std::string g_user_m_paths[USER_DEFINED_FUNCTION_NUM];
|
||||
|
||||
bool is_executable_file(const std::filesystem::path &path)
|
||||
{
|
||||
struct stat st {};
|
||||
return stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode) && (st.st_mode & S_IXUSR);
|
||||
}
|
||||
|
||||
std::filesystem::path resolve_machine_path(const std::filesystem::path &machine_dir, const std::string &entry)
|
||||
{
|
||||
std::filesystem::path path(entry);
|
||||
if (path.is_relative()) {
|
||||
path = machine_dir / path;
|
||||
}
|
||||
return path.lexically_normal();
|
||||
}
|
||||
|
||||
void standalone_user_m_code(int num, double arg1, double arg2)
|
||||
{
|
||||
FINISH();
|
||||
const int code = num + 100;
|
||||
std::ostringstream event;
|
||||
event << "USER_M_COMMAND code=M" << code
|
||||
<< " path=" << g_user_m_paths[num]
|
||||
<< " p=" << arg1
|
||||
<< " q=" << arg2;
|
||||
push_canon_event(event.str());
|
||||
}
|
||||
|
||||
int axis_mask_for_letter(char letter)
|
||||
{
|
||||
switch (std::toupper(static_cast<unsigned char>(letter))) {
|
||||
case 'X':
|
||||
return Interp::AXIS_MASK_X;
|
||||
case 'Y':
|
||||
return Interp::AXIS_MASK_Y;
|
||||
case 'Z':
|
||||
return Interp::AXIS_MASK_Z;
|
||||
case 'A':
|
||||
return Interp::AXIS_MASK_A;
|
||||
case 'B':
|
||||
return Interp::AXIS_MASK_B;
|
||||
case 'C':
|
||||
return Interp::AXIS_MASK_C;
|
||||
case 'U':
|
||||
return Interp::AXIS_MASK_U;
|
||||
case 'V':
|
||||
return Interp::AXIS_MASK_V;
|
||||
case 'W':
|
||||
return Interp::AXIS_MASK_W;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int axis_mask_from_coordinates(const char *coordinates)
|
||||
{
|
||||
if (!coordinates || coordinates[0] == '\0') {
|
||||
return kDefaultAxisMask;
|
||||
}
|
||||
|
||||
int axis_mask = 0;
|
||||
for (const char *cursor = coordinates; *cursor; ++cursor) {
|
||||
axis_mask |= axis_mask_for_letter(*cursor);
|
||||
}
|
||||
return axis_mask == 0 ? kDefaultAxisMask : axis_mask;
|
||||
}
|
||||
|
||||
int axis_mask_from_ini(const char *ini_path)
|
||||
{
|
||||
if (!ini_path || ini_path[0] == '\0') {
|
||||
return kDefaultAxisMask;
|
||||
}
|
||||
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
if (!ini) {
|
||||
return kDefaultAxisMask;
|
||||
}
|
||||
|
||||
if (auto coordinates = ini.findString("COORDINATES", "TRAJ")) {
|
||||
return axis_mask_from_coordinates(coordinates->c_str());
|
||||
}
|
||||
return kDefaultAxisMask;
|
||||
}
|
||||
|
||||
void apply_axis_mask_to_interp(Interp &interp, int axis_mask)
|
||||
{
|
||||
if (axis_mask == 0) {
|
||||
axis_mask = kDefaultAxisMask;
|
||||
}
|
||||
|
||||
set_external_axis_mask(axis_mask);
|
||||
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
|
||||
if (!(axis_mask & Interp::AXIS_MASK_X)) {
|
||||
interp._readers[static_cast<int>('x')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_Y)) {
|
||||
interp._readers[static_cast<int>('y')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_Z)) {
|
||||
interp._readers[static_cast<int>('z')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_A)) {
|
||||
interp._readers[static_cast<int>('a')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_B)) {
|
||||
interp._readers[static_cast<int>('b')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_C)) {
|
||||
interp._readers[static_cast<int>('c')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_U)) {
|
||||
interp._readers[static_cast<int>('u')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_V)) {
|
||||
interp._readers[static_cast<int>('v')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_W)) {
|
||||
interp._readers[static_cast<int>('w')] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void apply_machine_axis_config(Interp &interp, const linuxcnc::IniFile &ini)
|
||||
{
|
||||
interp._setup.orient_offset = ini.findRealV("ORIENT_OFFSET", "RS274NGC", 0.0);
|
||||
interp._setup.disable_fanuc_style_sub =
|
||||
ini.findBoolV("DISABLE_FANUC_STYLE_SUB", "RS274NGC", false);
|
||||
|
||||
if (auto coordinates = ini.findString("COORDINATES", "TRAJ")) {
|
||||
apply_axis_mask_to_interp(interp, axis_mask_from_coordinates(coordinates->c_str()));
|
||||
return;
|
||||
}
|
||||
apply_axis_mask_to_interp(interp, kDefaultAxisMask);
|
||||
}
|
||||
|
||||
bool apply_machine_axis_config(Interp &interp, const char *ini_path)
|
||||
{
|
||||
if (!ini_path || ini_path[0] == '\0') {
|
||||
apply_axis_mask_to_interp(interp, kDefaultAxisMask);
|
||||
return true;
|
||||
}
|
||||
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
if (!ini) {
|
||||
apply_axis_mask_to_interp(interp, kDefaultAxisMask);
|
||||
return false;
|
||||
}
|
||||
apply_machine_axis_config(interp, ini);
|
||||
return true;
|
||||
}
|
||||
|
||||
int load_tool_table_from_ini(Interp &interp, const linuxcnc::IniFile &ini, const char *machine_dir)
|
||||
{
|
||||
if (auto tool_table = ini.findString("TOOL_TABLE", "EMCIO")) {
|
||||
const std::filesystem::path base_dir = (machine_dir && machine_dir[0] != '\0')
|
||||
? std::filesystem::path(machine_dir)
|
||||
: std::filesystem::path();
|
||||
const std::filesystem::path tool_path = resolve_machine_path(base_dir, *tool_table);
|
||||
const bool random_toolchanger = ini.findBoolV("RANDOM_TOOLCHANGER", "EMCIO", false);
|
||||
|
||||
interp._setup.random_toolchanger = random_toolchanger;
|
||||
tooldata_init(random_toolchanger);
|
||||
tooldata_set_db(DB_NOTUSED);
|
||||
const int rc = tooldata_load(tool_path.c_str());
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
return interp.load_tool_table();
|
||||
}
|
||||
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
int load_tool_table_from_ini(Interp &interp, const char *ini_path)
|
||||
{
|
||||
if (!ini_path || ini_path[0] == '\0') {
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
if (!ini) {
|
||||
return -1;
|
||||
}
|
||||
const std::filesystem::path machine_dir = std::filesystem::path(ini_path).parent_path();
|
||||
return load_tool_table_from_ini(interp, ini, machine_dir.c_str());
|
||||
}
|
||||
|
||||
void reset_user_m_code_registry()
|
||||
{
|
||||
for (int index = 0; index < USER_DEFINED_FUNCTION_NUM; ++index) {
|
||||
USER_DEFINED_FUNCTION[index] = nullptr;
|
||||
g_user_m_paths[index].clear();
|
||||
}
|
||||
}
|
||||
|
||||
int register_user_m_codes_from_ini(const linuxcnc::IniFile &ini, const char *machine_dir)
|
||||
{
|
||||
std::filesystem::path base_dir = (machine_dir && machine_dir[0] != '\0')
|
||||
? std::filesystem::path(machine_dir)
|
||||
: std::filesystem::current_path();
|
||||
|
||||
std::string program_prefix = "nc_files";
|
||||
if (auto prefix = ini.findString("PROGRAM_PREFIX", "DISPLAY")) {
|
||||
program_prefix = *prefix;
|
||||
}
|
||||
|
||||
std::filesystem::path dirs[kMaxUserMDirs];
|
||||
int dir_count = 0;
|
||||
dirs[dir_count++] = resolve_machine_path(base_dir, program_prefix);
|
||||
|
||||
if (auto user_m_path = ini.findString("USER_M_PATH", "RS274NGC")) {
|
||||
std::istringstream entries(*user_m_path);
|
||||
std::string entry;
|
||||
while (std::getline(entries, entry, ':') && dir_count < kMaxUserMDirs) {
|
||||
if (!entry.empty()) {
|
||||
dirs[dir_count++] = resolve_machine_path(base_dir, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int registered = 0;
|
||||
for (int num = 0; num < USER_DEFINED_FUNCTION_NUM; ++num) {
|
||||
for (int dir_index = 0; dir_index < dir_count; ++dir_index) {
|
||||
char file_name[8] = {0};
|
||||
std::snprintf(file_name, sizeof(file_name), "M1%02d", num);
|
||||
std::filesystem::path candidate = dirs[dir_index] / file_name;
|
||||
if (!is_executable_file(candidate)) {
|
||||
continue;
|
||||
}
|
||||
USER_DEFINED_FUNCTION_ADD(standalone_user_m_code, num);
|
||||
g_user_m_paths[num] = candidate.string();
|
||||
++registered;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return registered;
|
||||
}
|
||||
|
||||
int register_user_m_codes_from_ini(const char *ini_path)
|
||||
{
|
||||
if (!ini_path || ini_path[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
if (!ini) {
|
||||
return 0;
|
||||
}
|
||||
const std::filesystem::path machine_dir = std::filesystem::path(ini_path).parent_path();
|
||||
return register_user_m_codes_from_ini(ini, machine_dir.c_str());
|
||||
}
|
||||
|
||||
} // namespace standalone
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
class Interp;
|
||||
|
||||
namespace linuxcnc {
|
||||
class IniFile;
|
||||
}
|
||||
|
||||
namespace standalone {
|
||||
|
||||
int axis_mask_from_coordinates(const char *coordinates);
|
||||
int axis_mask_from_ini(const char *ini_path);
|
||||
void apply_axis_mask_to_interp(Interp &interp, int axis_mask);
|
||||
void apply_machine_axis_config(Interp &interp, const linuxcnc::IniFile &ini);
|
||||
bool apply_machine_axis_config(Interp &interp, const char *ini_path);
|
||||
int load_tool_table_from_ini(Interp &interp, const linuxcnc::IniFile &ini, const char *machine_dir);
|
||||
int load_tool_table_from_ini(Interp &interp, const char *ini_path);
|
||||
void reset_user_m_code_registry();
|
||||
int register_user_m_codes_from_ini(const linuxcnc::IniFile &ini, const char *machine_dir);
|
||||
int register_user_m_codes_from_ini(const char *ini_path);
|
||||
|
||||
} // namespace standalone
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
namespace standalone {
|
||||
|
||||
void reset_external_axis_mask();
|
||||
void set_external_axis_mask(int axis_mask);
|
||||
int external_axis_mask();
|
||||
|
||||
} // namespace standalone
|
||||
Reference in New Issue
Block a user