Initial LinuxCNC WebAssembly workspace snapshot

This commit is contained in:
2026-08-20 18:21:15 +08:00
commit 5dd614c335
92 changed files with 200203 additions and 0 deletions

79
wasm-core/src/sim_api.c Normal file
View File

@@ -0,0 +1,79 @@
#include "sim_api.h"
#include <string.h>
typedef struct {
uint32_t created;
uint32_t loaded;
uint64_t tick;
uint32_t pending_event;
} sim_state;
static sim_state state;
static int valid_buffer(const uint8_t *data, uint32_t len) {
return len == 0 || data != NULL;
}
int32_t sim_create(const uint8_t *config, uint32_t config_len) {
if (!valid_buffer(config, config_len)) return SIM_ERR_INVALID_ARGUMENT;
memset(&state, 0, sizeof(state));
state.created = 1;
state.pending_event = 1;
return SIM_OK;
}
int32_t sim_load_workspace(const char *root_path) {
if (!state.created) return SIM_ERR_NOT_CREATED;
if (root_path == NULL || root_path[0] == '\0') return SIM_ERR_INVALID_ARGUMENT;
state.loaded = 1;
state.pending_event = 1;
return SIM_OK;
}
int32_t sim_command(const uint8_t *command, uint32_t command_len) {
if (!state.created || !state.loaded) return SIM_ERR_INVALID_STATE;
if (!valid_buffer(command, command_len) || command_len == 0) return SIM_ERR_INVALID_ARGUMENT;
state.pending_event = 1;
return SIM_OK;
}
int32_t sim_step(uint32_t cycles) {
if (!state.created || !state.loaded) return SIM_ERR_INVALID_STATE;
state.tick += cycles;
state.pending_event = 1;
return SIM_OK;
}
uint32_t sim_read_snapshot(uint8_t *out, uint32_t capacity) {
const uint32_t size = (uint32_t)sizeof(state.tick);
if (out == NULL || capacity < size) return size;
memcpy(out, &state.tick, size);
return size;
}
uint32_t sim_read_event(uint8_t *out, uint32_t capacity) {
const uint32_t size = 1;
if (!state.pending_event) return 0;
if (out == NULL || capacity < size) return size;
out[0] = 1;
state.pending_event = 0;
return size;
}
int32_t sim_export_state(uint8_t *out, uint32_t capacity) {
const uint32_t size = (uint32_t)sizeof(state);
if (out == NULL || capacity < size) return SIM_ERR_BUFFER_TOO_SMALL;
memcpy(out, &state, size);
return (int32_t)size;
}
int32_t sim_import_state(const uint8_t *data, uint32_t len) {
if (data == NULL || len != sizeof(state)) return SIM_ERR_INVALID_ARGUMENT;
memcpy(&state, data, sizeof(state));
return SIM_OK;
}
void sim_destroy(void) {
memset(&state, 0, sizeof(state));
}

82
wasm-core/src/sim_event.c Normal file
View File

@@ -0,0 +1,82 @@
#include "sim_event.h"
#include <stddef.h>
#include <string.h>
typedef struct sim_event_state {
uint32_t count;
uint32_t last_seq;
uint32_t last_tick;
sim_event_record records[SIM_EVENT_CAPACITY];
} sim_event_state;
static sim_event_state state;
static uint64_t hash_payload(const uint8_t *payload, uint32_t payload_len) {
uint64_t hash = 1469598103934665603ULL;
uint32_t index;
if (payload == NULL && payload_len != 0U) return 0;
for (index = 0; index < payload_len; ++index) {
hash ^= payload[index];
hash *= 1099511628211ULL;
}
return hash;
}
void sim_event_reset(void) {
memset(&state, 0, sizeof(state));
}
int32_t sim_event_append(uint32_t seq, uint32_t tick, const char *kind,
const uint8_t *payload, uint32_t payload_len) {
sim_event_record *record;
size_t kind_len;
if (seq == 0U || seq != state.last_seq + 1U) return SIM_EVENT_ERR_SEQUENCE;
if (tick < state.last_tick) return SIM_EVENT_ERR_TICK;
if (kind == NULL || kind[0] == '\0') return SIM_EVENT_ERR_ARGUMENT;
kind_len = strlen(kind);
if (kind_len >= SIM_EVENT_KIND_CAPACITY || (payload == NULL && payload_len != 0U)) return SIM_EVENT_ERR_ARGUMENT;
if (state.count >= SIM_EVENT_CAPACITY) return SIM_EVENT_ERR_FULL;
record = &state.records[state.count++];
memset(record, 0, sizeof(*record));
memcpy(record->kind, kind, kind_len);
record->seq = seq;
record->tick = tick;
record->payload_hash = hash_payload(payload, payload_len);
state.last_seq = seq;
state.last_tick = tick;
return SIM_EVENT_OK;
}
uint32_t sim_event_count(void) {
return state.count;
}
uint32_t sim_event_cursor(void) {
return state.last_seq;
}
uint32_t sim_event_read(uint32_t index, uint8_t *out, uint32_t capacity) {
const uint32_t size = (uint32_t)sizeof(sim_event_record);
if (index >= state.count) return 0;
if (out == NULL || capacity < size) return size;
memcpy(out, &state.records[index], size);
return size;
}
uint32_t sim_event_export(uint8_t *out, uint32_t capacity) {
const uint32_t size = (uint32_t)sizeof(state);
if (out == NULL || capacity < size) return size;
memcpy(out, &state, size);
return size;
}
int32_t sim_event_import(const uint8_t *data, uint32_t len) {
if (data == NULL || len != sizeof(state)) return SIM_EVENT_ERR_ARGUMENT;
memcpy(&state, data, sizeof(state));
if (state.count > SIM_EVENT_CAPACITY) {
sim_event_reset();
return SIM_EVENT_ERR_ARGUMENT;
}
return SIM_EVENT_OK;
}

166
wasm-core/src/sim_hal.c Normal file
View File

@@ -0,0 +1,166 @@
#include "sim_hal.h"
#include <stddef.h>
#include <string.h>
typedef struct {
uint32_t used;
char name[SIM_HAL_NAME_CAPACITY];
sim_hal_value value;
} pin_entry;
typedef struct {
uint32_t used;
char name[SIM_HAL_NAME_CAPACITY];
uint32_t period;
} thread_entry;
typedef struct {
uint32_t used;
char name[SIM_HAL_NAME_CAPACITY];
uint32_t thread;
uint32_t calls;
uint32_t fail;
} function_entry;
typedef struct {
uint32_t tick;
pin_entry pins[SIM_HAL_PIN_CAPACITY];
thread_entry threads[SIM_HAL_THREAD_CAPACITY];
function_entry functions[SIM_HAL_FUNCTION_CAPACITY];
} hal_state;
static hal_state state;
static int valid_name(const char *name) {
return name != NULL && name[0] != '\0' && strlen(name) < SIM_HAL_NAME_CAPACITY;
}
static int find_pin(const char *name) {
uint32_t index;
for (index = 0; index < SIM_HAL_PIN_CAPACITY; ++index) {
if (state.pins[index].used && strcmp(state.pins[index].name, name) == 0) return (int)index;
}
return -1;
}
static int find_thread(const char *name) {
uint32_t index;
for (index = 0; index < SIM_HAL_THREAD_CAPACITY; ++index) {
if (state.threads[index].used && strcmp(state.threads[index].name, name) == 0) return (int)index;
}
return -1;
}
static int find_function(const char *name) {
uint32_t index;
for (index = 0; index < SIM_HAL_FUNCTION_CAPACITY; ++index) {
if (state.functions[index].used && strcmp(state.functions[index].name, name) == 0) return (int)index;
}
return -1;
}
void sim_hal_reset(void) {
memset(&state, 0, sizeof(state));
}
int32_t sim_hal_register_pin(const char *name, uint32_t type) {
uint32_t index;
if (!valid_name(name) || type < SIM_HAL_BIT || type > SIM_HAL_FLOAT) return SIM_HAL_ERR_ARGUMENT;
if (find_pin(name) >= 0) return SIM_HAL_ERR_DUPLICATE;
for (index = 0; index < SIM_HAL_PIN_CAPACITY; ++index) {
if (!state.pins[index].used) {
state.pins[index].used = 1;
strcpy(state.pins[index].name, name);
state.pins[index].value.type = type;
return SIM_HAL_OK;
}
}
return SIM_HAL_ERR_FULL;
}
int32_t sim_hal_set_pin(const char *name, sim_hal_value value) {
int index = find_pin(name);
if (index < 0) return SIM_HAL_ERR_NOT_FOUND;
if (value.type != state.pins[index].value.type) return SIM_HAL_ERR_TYPE;
if (value.type == SIM_HAL_BIT && value.value.bit > 1U) return SIM_HAL_ERR_RANGE;
state.pins[index].value = value;
return SIM_HAL_OK;
}
int32_t sim_hal_get_pin(const char *name, sim_hal_value *out) {
int index = find_pin(name);
if (index < 0 || out == NULL) return index < 0 ? SIM_HAL_ERR_NOT_FOUND : SIM_HAL_ERR_ARGUMENT;
*out = state.pins[index].value;
return SIM_HAL_OK;
}
int32_t sim_hal_connect(const char *source, const char *target) {
int source_index = find_pin(source);
int target_index = find_pin(target);
if (source_index < 0 || target_index < 0) return SIM_HAL_ERR_NOT_FOUND;
if (state.pins[source_index].value.type != state.pins[target_index].value.type) return SIM_HAL_ERR_CONNECTION;
state.pins[target_index].value = state.pins[source_index].value;
return SIM_HAL_OK;
}
int32_t sim_hal_register_thread(const char *name, uint32_t period_ticks) {
uint32_t index;
if (!valid_name(name) || period_ticks == 0U || period_ticks > 1000000U) return SIM_HAL_ERR_RANGE;
if (find_thread(name) >= 0) return SIM_HAL_ERR_DUPLICATE;
for (index = 0; index < SIM_HAL_THREAD_CAPACITY; ++index) {
if (!state.threads[index].used) {
state.threads[index].used = 1;
strcpy(state.threads[index].name, name);
state.threads[index].period = period_ticks;
return SIM_HAL_OK;
}
}
return SIM_HAL_ERR_FULL;
}
int32_t sim_hal_register_function(const char *thread, const char *name) {
uint32_t index;
int thread_index = find_thread(thread);
if (thread_index < 0) return SIM_HAL_ERR_NOT_FOUND;
if (!valid_name(name)) return SIM_HAL_ERR_ARGUMENT;
if (find_function(name) >= 0) return SIM_HAL_ERR_DUPLICATE;
for (index = 0; index < SIM_HAL_FUNCTION_CAPACITY; ++index) {
if (!state.functions[index].used) {
state.functions[index].used = 1;
strcpy(state.functions[index].name, name);
state.functions[index].thread = (uint32_t)thread_index;
return SIM_HAL_OK;
}
}
return SIM_HAL_ERR_FULL;
}
int32_t sim_hal_set_function_failure(const char *name, uint32_t fail) {
int index = find_function(name);
if (index < 0) return SIM_HAL_ERR_NOT_FOUND;
state.functions[index].fail = fail ? 1U : 0U;
return SIM_HAL_OK;
}
int32_t sim_hal_step(uint32_t ticks) {
uint32_t target = state.tick + ticks;
uint32_t tick;
uint32_t function_index;
for (tick = state.tick + 1U; tick <= target; ++tick) {
for (function_index = 0; function_index < SIM_HAL_FUNCTION_CAPACITY; ++function_index) {
function_entry *function = &state.functions[function_index];
if (!function->used || tick % state.threads[function->thread].period != 0U) continue;
if (function->fail) return SIM_HAL_ERR_FUNCTION;
function->calls += 1U;
}
}
state.tick = target;
return SIM_HAL_OK;
}
uint32_t sim_hal_tick(void) { return state.tick; }
uint32_t sim_hal_function_calls(const char *name) { int index = find_function(name); return index < 0 ? 0U : state.functions[index].calls; }
uint32_t sim_hal_pin_count(void) { uint32_t i, count = 0; for (i = 0; i < SIM_HAL_PIN_CAPACITY; ++i) count += state.pins[i].used; return count; }
uint32_t sim_hal_thread_count(void) { uint32_t i, count = 0; for (i = 0; i < SIM_HAL_THREAD_CAPACITY; ++i) count += state.threads[i].used; return count; }
uint32_t sim_hal_function_count(void) { uint32_t i, count = 0; for (i = 0; i < SIM_HAL_FUNCTION_CAPACITY; ++i) count += state.functions[i].used; return count; }

View File

@@ -0,0 +1,62 @@
#include "sim_posemath.h"
#include "posemath.h"
#include <math.h>
static int finite_cart(const sim_posemath_cartesian *value) {
return value != 0 && isfinite(value->x) && isfinite(value->y) && isfinite(value->z);
}
static PmCartesian to_pm(const sim_posemath_cartesian *value) {
PmCartesian result = {value->x, value->y, value->z};
return result;
}
static void from_pm(const PmCartesian *value, sim_posemath_cartesian *output) {
output->x = value->x;
output->y = value->y;
output->z = value->z;
}
int sim_posemath_is_finite(const sim_posemath_cartesian *value) {
return finite_cart(value);
}
int sim_posemath_normalize(const sim_posemath_cartesian *input, sim_posemath_cartesian *output) {
PmCartesian source;
PmCartesian result;
double magnitude_squared = 0.0;
if (!finite_cart(input) || output == 0) return SIM_POSEMATH_ERR_ARGUMENT;
source = to_pm(input);
if (pmCartMagSq(&source, &magnitude_squared) != 0 || !isfinite(magnitude_squared)) return SIM_POSEMATH_ERR_NONFINITE;
if (magnitude_squared <= 1e-24) return SIM_POSEMATH_ERR_DEGENERATE;
if (pmCartUnit(&source, &result) != 0) return SIM_POSEMATH_ERR_DEGENERATE;
from_pm(&result, output);
return SIM_POSEMATH_OK;
}
int sim_posemath_dot(const sim_posemath_cartesian *left, const sim_posemath_cartesian *right, double *output) {
PmCartesian lhs;
PmCartesian rhs;
if (!finite_cart(left) || !finite_cart(right) || output == 0) return SIM_POSEMATH_ERR_ARGUMENT;
lhs = to_pm(left);
rhs = to_pm(right);
if (pmCartCartDot(&lhs, &rhs, output) != 0 || !isfinite(*output)) return SIM_POSEMATH_ERR_NONFINITE;
return SIM_POSEMATH_OK;
}
int sim_posemath_line_point(const sim_posemath_cartesian *start, const sim_posemath_cartesian *end,
double fraction, sim_posemath_cartesian *output) {
PmCartesian begin;
PmCartesian finish;
PmCartesian result;
PmCartesian delta;
if (!finite_cart(start) || !finite_cart(end) || output == 0) return SIM_POSEMATH_ERR_ARGUMENT;
if (!isfinite(fraction) || fraction < 0.0 || fraction > 1.0) return SIM_POSEMATH_ERR_RANGE;
begin = to_pm(start);
finish = to_pm(end);
if (pmCartCartSub(&finish, &begin, &delta) != 0 || pmCartScalMult(&delta, fraction, &delta) != 0 || pmCartCartAdd(&begin, &delta, &result) != 0) return SIM_POSEMATH_ERR_DEGENERATE;
from_pm(&result, output);
return SIM_POSEMATH_OK;
}

View File

@@ -0,0 +1,112 @@
#include "sim_protocol.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
typedef struct {
uint32_t state;
uint32_t last_seq;
uint32_t tick;
} protocol_state;
static protocol_state state;
static int has_text(const char *value) {
return value != NULL && value[0] != '\0' && strlen(value) <= 128;
}
static int is_kind(const char *kind, const char *expected) {
return kind != NULL && strcmp(kind, expected) == 0;
}
static int is_side_effect(const char *kind) {
static const char *const commands[] = {
"SESSION_CREATE", "LOAD_PROJECT", "START", "STOP", "RESET",
"LOAD_WORKSPACE", "SIM_STEP", "COMMAND",
"PROGRAM_LOAD", "PROGRAM_RUN", "PROGRAM_PAUSE", "PROGRAM_RESUME",
"PROGRAM_ABORT", "PROGRAM_STEP", "PROGRAM_SEEK", "MDI_EXECUTE",
"JOG_START", "JOG_STOP", "HOME", "SET_MODE", "SET_OVERRIDE",
"SPINDLE_COMMAND", "COOLANT_COMMAND", "TOOL_PREPARE", "TOOL_CHANGE",
"PROBE_CONFIG", "HAL_COMMAND", "HAL_SET", "PLC_START", "PLC_STOP",
"PROJECT_SAVE", "IMPORT", "EXPORT", "CHECKPOINT", "RECOVER",
"SUBSCRIBE", "UNSUBSCRIBE", "CANCEL", NULL,
};
size_t index;
if (!has_text(kind)) return 0;
for (index = 0; commands[index] != NULL; ++index) {
if (is_kind(kind, commands[index])) return 1;
}
return 0;
}
static int state_accepts_kind(uint32_t current, const char *kind) {
if (current == SIM_PROTOCOL_NEW) return is_kind(kind, "HELLO");
if (current == SIM_PROTOCOL_HELLO) return is_kind(kind, "SESSION_ATTACH") || is_kind(kind, "CAPABILITIES");
if (current == SIM_PROTOCOL_ATTACHED) return is_kind(kind, "SESSION_READY");
if (current == SIM_PROTOCOL_READY) return 1;
if (current == SIM_PROTOCOL_PAUSED) {
return is_kind(kind, "PROGRAM_RESUME") || is_kind(kind, "RESUME") ||
is_kind(kind, "PING") || is_kind(kind, "SNAPSHOT_REQUEST") ||
is_kind(kind, "DESTROY");
}
return is_kind(kind, "RESET");
}
void sim_protocol_reset(void) {
memset(&state, 0, sizeof(state));
}
int32_t sim_protocol_accept(uint32_t schema_version, uint32_t seq,
const char *kind, const char *operation_id,
uint32_t simulation_tick) {
if (schema_version != 1U) return SIM_PROTOCOL_ERR_SCHEMA;
if (!has_text(kind)) return SIM_PROTOCOL_ERR_KIND;
if (state.state == SIM_PROTOCOL_DESTROYED && !is_kind(kind, "RESET")) return SIM_PROTOCOL_ERR_STATE;
if (seq == 0U || seq != state.last_seq + 1U) return SIM_PROTOCOL_ERR_SEQUENCE;
if (simulation_tick < state.tick) return SIM_PROTOCOL_ERR_TICK;
if (!state_accepts_kind(state.state, kind)) return SIM_PROTOCOL_ERR_STATE;
if (is_side_effect(kind) && !has_text(operation_id)) return SIM_PROTOCOL_ERR_OPERATION_ID;
state.last_seq = seq;
state.tick = simulation_tick;
if (is_kind(kind, "HELLO")) state.state = SIM_PROTOCOL_HELLO;
/* SESSION_READY is the worker response; the next client command is valid immediately. */
else if (is_kind(kind, "SESSION_ATTACH")) state.state = SIM_PROTOCOL_READY;
else if (is_kind(kind, "SESSION_READY")) state.state = SIM_PROTOCOL_READY;
else if (is_kind(kind, "PROGRAM_PAUSE")) state.state = SIM_PROTOCOL_PAUSED;
else if (is_kind(kind, "PROGRAM_RESUME") || is_kind(kind, "RESUME")) state.state = SIM_PROTOCOL_READY;
else if (is_kind(kind, "DESTROY")) state.state = SIM_PROTOCOL_DESTROYED;
else if (is_kind(kind, "RESET")) sim_protocol_reset();
return SIM_PROTOCOL_OK;
}
int32_t sim_protocol_pause(void) {
if (state.state != SIM_PROTOCOL_READY) return SIM_PROTOCOL_ERR_STATE;
state.state = SIM_PROTOCOL_PAUSED;
return SIM_PROTOCOL_OK;
}
int32_t sim_protocol_resume(void) {
if (state.state != SIM_PROTOCOL_PAUSED) return SIM_PROTOCOL_ERR_STATE;
state.state = SIM_PROTOCOL_READY;
return SIM_PROTOCOL_OK;
}
int32_t sim_protocol_state_value(void) {
return (int32_t)state.state;
}
uint32_t sim_protocol_last_seq(void) {
return state.last_seq;
}
uint32_t sim_protocol_tick(void) {
return state.tick;
}
int32_t sim_protocol_set_tick(uint32_t simulation_tick) {
if (simulation_tick < state.tick) return SIM_PROTOCOL_ERR_TICK;
state.tick = simulation_tick;
return SIM_PROTOCOL_OK;
}