Use native LinuxCNC homing and consolidate web panel status
This commit is contained in:
@@ -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"
|
||||
|
||||
15
wasm-core/include/sim_native_homing.h
Normal file
15
wasm-core/include/sim_native_homing.h
Normal 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
|
||||
168
wasm-core/src/sim_native_homing.c
Normal file
168
wasm-core/src/sim_native_homing.c
Normal 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;
|
||||
}
|
||||
43
wasm-core/tests/sim_native_homing_test.c
Normal file
43
wasm-core/tests/sim_native_homing_test.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user