Use native LinuxCNC homing and consolidate web panel status

This commit is contained in:
2026-09-09 12:28:02 +08:00
parent eb5c58cdb3
commit 0be2ee8ce1
35 changed files with 881 additions and 350 deletions

View File

@@ -37,6 +37,8 @@ add_library(linuxcnc_tp_source STATIC
${CMAKE_CURRENT_SOURCE_DIR}/../linuxcnc/src/emc/tp/spherical_arc.c
${CMAKE_CURRENT_SOURCE_DIR}/../linuxcnc/src/emc/nml_intf/emcpose.c
src/linuxcnc_tp_compat.c
src/sim_native_homing.c
${CMAKE_CURRENT_SOURCE_DIR}/../linuxcnc/src/emc/motion/simple_tp.c
)
target_include_directories(linuxcnc_tp_source PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../linuxcnc/src
@@ -64,11 +66,16 @@ target_include_directories(sim_core PRIVATE
target_link_libraries(sim_core PUBLIC linuxcnc_tp_source)
if(NOT EMSCRIPTEN)
add_executable(sim_native_homing_test tests/sim_native_homing_test.c)
target_include_directories(sim_native_homing_test PRIVATE include)
target_compile_options(sim_native_homing_test PRIVATE -UNDEBUG)
target_link_libraries(sim_native_homing_test PRIVATE sim_core)
add_executable(sim_api_test tests/sim_api_test.c)
target_link_libraries(sim_api_test PRIVATE sim_core)
target_include_directories(sim_api_test PRIVATE include)
enable_testing()
add_test(NAME sim_native_homing_test COMMAND sim_native_homing_test)
add_test(NAME sim_api_test COMMAND sim_api_test)
add_executable(sim_protocol_test tests/sim_protocol_test.c)
@@ -289,6 +296,7 @@ if(EMSCRIPTEN)
)
target_compile_options(sim_core_module PRIVATE -ffp-contract=off -fno-fast-math)
target_link_libraries(sim_core_module PRIVATE sim_posemath linuxcnc_tp_source)
target_link_options(sim_core_module PRIVATE "-Wl,-u,sim_native_home_init")
set_target_properties(sim_core_module PROPERTIES
OUTPUT_NAME "linuxcnc-sim-core"
SUFFIX ".js"

View File

@@ -0,0 +1,15 @@
#ifndef SIM_NATIVE_HOMING_H
#define SIM_NATIVE_HOMING_H
#include <stdint.h>
typedef struct native_home native_home;
uint32_t sim_native_home_size(void);
int sim_native_home_init(native_home *, int count, double period);
int sim_native_home_config(native_home *, int joint, const double *parameters);
int sim_native_home_sync(native_home *, const double *joints);
int sim_native_home_mode(native_home *, int mode);
int sim_native_home_start(native_home *, int joint, uint32_t seq);
int sim_native_home_step(native_home *);
int sim_native_home_cancel(native_home *, int volatile_only);
int sim_native_home_input(native_home *, int joint, int home_switch, int index_enable, int negative_limit, int positive_limit);
double sim_native_home_get(native_home *, int joint, int field);
#endif

View File

@@ -0,0 +1,168 @@
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "../include/sim_native_homing.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#define API EMSCRIPTEN_KEEPALIVE
#else
#define API
#endif
/* Only HAL allocation is adapted. The state machine below is the original
* homemod source, including sequence, switch, index and final-move rules. */
#define hal_malloc native_home_hal_malloc
#define hal_pin_bit_newf native_home_hal_bit
#define hal_pin_s32_newf native_home_hal_s32
#include "../../linuxcnc/src/emc/motion/homing.c"
#include "simple_tp.h"
static union { double align; unsigned char bytes[8192]; } pin_arena;
static size_t pin_used;
void *native_home_hal_malloc(long size) {
size_t start = (pin_used + 7) & ~(size_t)7;
if (size < 0 || start + (size_t)size > sizeof(pin_arena)) return NULL;
pin_used = start + size;
return pin_arena.bytes + start;
}
int native_home_hal_bit(hal_pin_dir_t dir, hal_bit_t **pin, int id, const char *fmt, ...) {
(void)dir; (void)id; (void)fmt;
*pin = native_home_hal_malloc(sizeof(**pin)); return *pin ? 0 : -1;
}
int native_home_hal_s32(hal_pin_dir_t dir, hal_s32_t **pin, int id, const char *fmt, ...) {
(void)dir; (void)id; (void)fmt;
*pin = native_home_hal_malloc(sizeof(**pin)); return *pin ? 0 : -1;
}
struct native_home {
emcmot_joint_t joint[EMCMOT_MAX_JOINTS];
home_local_data local[EMCMOT_MAX_JOINTS];
double period;
int count, sequence, current, active, sync, mode;
uint32_t tick, command_seq;
};
/* homemod is a singleton in LinuxCNC. Swap its private data at the ABI
* boundary to isolate multiple simulation sessions in one WASM module. */
static void load(native_home *h) {
joints = h->joint; all_joints = h->count; extra_joints = 0;
servo_freq = 1 / h->period;
memcpy(H, h->local, sizeof(H));
sequence_state = h->sequence; current_sequence = h->current;
homing_active = h->active; sync_now = h->sync;
}
static void save(native_home *h) {
memcpy(h->local, H, sizeof(H));
h->sequence = sequence_state; h->current = current_sequence;
h->active = homing_active; h->sync = sync_now;
}
API uint32_t sim_native_home_size(void) { return sizeof(native_home); }
API int sim_native_home_init(native_home *h, int count, double period) {
if (!h || count < 1 || count > EMCMOT_MAX_JOINTS || !isfinite(period) || period <= 0) return -1;
memset(h, 0, sizeof(*h)); memset(H, 0, sizeof(H));
pin_used = 0; memset(&pin_arena, 0, sizeof(pin_arena));
h->count = count; h->period = period; h->mode = 1; /* FREE */
sequence_state = HOME_SEQUENCE_IDLE; current_sequence = 0; sync_now = 0;
if (homing_init(0, period, count, 0, h->joint)) return -1;
for (int j = 0; j < count; j++) {
h->joint[j].flag |= EMCMOT_JOINT_ACTIVE_BIT | EMCMOT_JOINT_INPOS_BIT;
}
save(h); return 0;
}
/* p: offset, home, final velocity, search velocity, latch velocity, flags,
* sequence, volatile, min limit, max limit, max velocity, max acceleration. */
API int sim_native_home_config(native_home *h, int j, const double *p) {
if (!h || !p || j < 0 || j >= h->count) return -1;
for (int i = 0; i < 12; i++) if (!isfinite(p[i])) return -1;
if (p[8] > p[9] || p[10] <= 0 || p[11] <= 0 || ((int)p[5] & HOME_UNLOCK_FIRST)) return -1;
load(h);
set_joint_homing_params(j, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7] != 0);
joints[j].min_pos_limit = p[8]; joints[j].max_pos_limit = p[9];
joints[j].vel_limit = p[10]; joints[j].acc_limit = p[11];
joints[j].free_tp.max_acc = p[11]; save(h); return 0;
}
API int sim_native_home_sync(native_home *h, const double *position) {
if (!h || !position || h->active || h->sequence != HOME_SEQUENCE_IDLE) return -1;
for (int j = 0; j < h->count; j++) if (!isfinite(position[j])) return -1;
for (int j = 0; j < h->count; j++) {
emcmot_joint_t *p = &h->joint[j];
p->pos_cmd = p->pos_fb = p->free_tp.curr_pos = position[j];
p->motor_pos_fb = position[j] + p->motor_offset;
}
return 0;
}
API int sim_native_home_mode(native_home *h, int mode) {
if (!h || mode < 1 || mode > 3 || h->active || h->sequence != HOME_SEQUENCE_IDLE) return -1;
load(h);
if (mode != 1 && !get_allhomed()) return -2;
h->mode = mode; return 0;
}
API int sim_native_home_start(native_home *h, int j, uint32_t seq) {
if (!h || j < -1 || j >= h->count || h->active || h->sequence != HOME_SEQUENCE_IDLE) return -1;
load(h); h->mode = 1; h->command_seq = seq;
do_home_joint(j); save(h); return 0;
}
API int sim_native_home_step(native_home *h) {
if (!h) return -1;
load(h);
/* control.c switches to TELEOP on the native all-homed edge. */
if (do_homing()) h->mode = 3;
/* control.c: homing runs before free-mode position generation. Ideal
* feedback follows the commanded motor position in this simulation. */
for (int j = 0; j < h->count; j++) {
simple_tp_update(&joints[j].free_tp, h->period);
joints[j].pos_cmd = joints[j].free_tp.curr_pos;
joints[j].pos_fb = joints[j].pos_cmd;
joints[j].motor_pos_fb = joints[j].pos_cmd + joints[j].motor_offset;
if (joints[j].free_tp.active) joints[j].flag &= ~EMCMOT_JOINT_INPOS_BIT;
else joints[j].flag |= EMCMOT_JOINT_INPOS_BIT;
}
h->tick++; save(h); return 0;
}
API int sim_native_home_cancel(native_home *h, int volatile_only) {
if (!h) return -1;
load(h);
for (int j = 0; j < h->count; j++) {
if (H[j].homing || H[j].home_state != HOME_IDLE || (homing_active && H[j].joint_in_sequence)) {
H[j].home_state = HOME_ABORT;
base_1joint_state_machine(j);
}
joints[j].free_tp.enable = 0;
if (volatile_only) {
joints[j].free_tp.curr_vel = 0;
joints[j].free_tp.active = 0;
joints[j].flag |= EMCMOT_JOINT_INPOS_BIT;
}
}
sequence_state = HOME_SEQUENCE_IDLE; homing_active = 0;
if (volatile_only) set_unhomed(-2, EMCMOT_MOTION_DISABLED);
h->mode = 1; save(h); return 0;
}
API int sim_native_home_input(native_home *h, int j, int sw, int index, int neg, int pos) {
if (!h || j < 0 || j >= h->count) return -1;
h->local[j].home_sw = sw != 0; h->local[j].index_enable = index != 0;
h->joint[j].on_neg_limit = neg != 0; h->joint[j].on_pos_limit = pos != 0; return 0;
}
API double sim_native_home_get(native_home *h, int j, int field) {
if (!h) return -1;
if (field == 0) {
for (int i = 0; i < h->count; i++) if (h->joint[i].free_tp.active) return 1;
return h->active || h->sequence != HOME_SEQUENCE_IDLE;
}
if (field == 1) return h->mode;
if (field == 2) return h->tick;
if (field == 3) return h->command_seq;
if (j < 0 || j >= h->count) return -1;
switch (field) {
case 4: return h->local[j].homing;
case 5: return h->local[j].homed;
case 6: return h->local[j].home_state;
case 7: return h->joint[j].pos_fb;
case 8: return h->joint[j].motor_pos_fb;
case 9: return h->joint[j].motor_offset;
case 10: return h->local[j].home_sequence;
case 11: return h->joint[j].free_tp.curr_vel;
case 12: return h->local[j].index_enable;
}
return -1;
}

View File

@@ -0,0 +1,43 @@
#include "sim_native_homing.h"
#include <assert.h>
#include <math.h>
#include <stdlib.h>
int main(void) {
native_home *h = malloc(sim_native_home_size());
native_home *other = malloc(sim_native_home_size());
double p[12] = {0,0,-1,0,0,0,0,0,-200,200,20,300};
double position[5] = {10,20,30,15,45};
assert(sim_native_home_init(h,5,.001) == 0);
assert(sim_native_home_init(other,5,.001) == 0);
for (int j=0;j<5;j++) assert(sim_native_home_config(h,j,p) == 0);
assert(sim_native_home_sync(h,position) == 0);
assert(sim_native_home_start(h,0,70000) == 0);
assert(sim_native_home_get(h,0,7) == 10);
assert(sim_native_home_step(h) == 0);
assert(sim_native_home_get(h,0,6) == 21);
assert(sim_native_home_get(h,0,4) == 1);
assert(sim_native_home_get(h,0,5) == 0);
assert(sim_native_home_get(h,0,7) == 0);
assert(sim_native_home_get(h,0,8) == 10);
assert(sim_native_home_get(h,1,7) == 20);
for(int i=0;i<10;i++) assert(sim_native_home_step(h) == 0);
assert(sim_native_home_get(h,0,5) == 1);
assert(sim_native_home_get(other,0,5) == 0);
p[1]=5;
assert(sim_native_home_config(h,1,p) == 0);
assert(sim_native_home_start(h,1,70001) == 0);
assert(sim_native_home_step(h) == 0);
assert(sim_native_home_get(h,1,7)>0 && sim_native_home_get(h,1,7)<5);
for(int i=0;i<2000 && sim_native_home_get(h,0,0);i++) sim_native_home_step(h);
assert(fabs(sim_native_home_get(h,1,7)-5)<1e-8);
assert(fabs(sim_native_home_get(h,1,8)-25)<1e-8);
assert(sim_native_home_start(h,1,70002) == 0);
sim_native_home_step(h);
assert(sim_native_home_cancel(h,0) == 0);
assert(sim_native_home_get(h,1,5) == 0);
for (int i=0;i<1000 && sim_native_home_get(h,0,0);i++) sim_native_home_step(h);
assert(sim_native_home_get(h,0,0) == 0);
free(h); free(other);
return 0;
}