Implement AXIS parity UI and execution baseline
This commit is contained in:
90
wasm-core/tests/sim_contract_test.c
Normal file
90
wasm-core/tests/sim_contract_test.c
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "sim_api.h"
|
||||
#include "sim_event.h"
|
||||
#include "sim_protocol.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
_Static_assert(sizeof(int32_t) == 4, "contract_int32_width");
|
||||
_Static_assert(sizeof(uint32_t) == 4, "contract_uint32_width");
|
||||
_Static_assert(sizeof(uint64_t) == 8, "contract_uint64_width");
|
||||
_Static_assert(offsetof(sim_event_record, seq) == 0, "contract_event_seq_offset");
|
||||
_Static_assert(offsetof(sim_event_record, tick) == 4, "contract_event_tick_offset");
|
||||
_Static_assert(offsetof(sim_event_record, payload_hash) == 8, "contract_event_hash_offset");
|
||||
_Static_assert(offsetof(sim_event_record, kind) == 16, "contract_event_kind_offset");
|
||||
_Static_assert(sizeof(sim_event_record) == 48, "contract_event_record_size");
|
||||
|
||||
static void test_protocol_contract(void) {
|
||||
sim_protocol_reset();
|
||||
assert(sim_protocol_accept(0, 1, "HELLO", NULL, 0) == SIM_PROTOCOL_ERR_SCHEMA);
|
||||
assert(sim_protocol_accept(1, 1, "HELLO", NULL, 0) == SIM_PROTOCOL_OK);
|
||||
assert(sim_protocol_accept(1, 3, "SESSION_ATTACH", NULL, 0) == SIM_PROTOCOL_ERR_SEQUENCE);
|
||||
assert(sim_protocol_accept(1, 2, "SESSION_ATTACH", NULL, 0) == SIM_PROTOCOL_OK);
|
||||
assert(sim_protocol_accept(1, 3, "MDI_EXECUTE", NULL, 0) == SIM_PROTOCOL_ERR_OPERATION_ID);
|
||||
assert(sim_protocol_accept(1, 3, "MDI_EXECUTE", "op-3", 1) == SIM_PROTOCOL_OK);
|
||||
assert(sim_protocol_accept(1, 4, "PING", NULL, 0) == SIM_PROTOCOL_ERR_TICK);
|
||||
assert(sim_protocol_last_seq() == 3 && sim_protocol_tick() == 1);
|
||||
}
|
||||
|
||||
static void test_event_contract(void) {
|
||||
const uint8_t payload[] = {'c', 'o', 'n', 't', 'r', 'a', 'c', 't'};
|
||||
uint8_t exported[16384] = {0};
|
||||
uint8_t record[sizeof(sim_event_record)] = {0};
|
||||
uint32_t export_size;
|
||||
uint32_t index;
|
||||
|
||||
sim_event_reset();
|
||||
assert(sim_event_append(1, 0, "SESSION_READY", payload, sizeof(payload)) == SIM_EVENT_OK);
|
||||
assert(sim_event_append(3, 0, "GAP", NULL, 0) == SIM_EVENT_ERR_SEQUENCE);
|
||||
assert(sim_event_append(2, 1, "SIM_STEP", NULL, 0) == SIM_EVENT_OK);
|
||||
assert(sim_event_read(0, NULL, 0) == sizeof(sim_event_record));
|
||||
assert(sim_event_read(0, record, sizeof(record)) == sizeof(sim_event_record));
|
||||
assert(((sim_event_record *)record)->seq == 1);
|
||||
export_size = sim_event_export(NULL, 0);
|
||||
assert(export_size > sizeof(sim_event_record) && export_size < sizeof(exported));
|
||||
assert(sim_event_export(exported, sizeof(exported)) == export_size);
|
||||
sim_event_reset();
|
||||
assert(sim_event_import(exported, export_size) == SIM_EVENT_OK);
|
||||
assert(sim_event_count() == 2 && sim_event_cursor() == 2);
|
||||
assert(sim_event_import(exported, export_size - 1) == SIM_EVENT_ERR_ARGUMENT);
|
||||
|
||||
sim_event_reset();
|
||||
for (index = 1; index <= SIM_EVENT_CAPACITY; ++index) {
|
||||
assert(sim_event_append(index, index, "STEP", NULL, 0) == SIM_EVENT_OK);
|
||||
}
|
||||
assert(sim_event_append(SIM_EVENT_CAPACITY + 1U, SIM_EVENT_CAPACITY + 1U, "FULL", NULL, 0) == SIM_EVENT_ERR_FULL);
|
||||
}
|
||||
|
||||
static void test_api_contract(void) {
|
||||
uint8_t state[32] = {0};
|
||||
uint8_t snapshot[sizeof(uint64_t)] = {0};
|
||||
uint64_t tick = 0;
|
||||
int32_t state_size;
|
||||
|
||||
sim_destroy();
|
||||
assert(sim_read_snapshot(NULL, 0) == sizeof(uint64_t));
|
||||
assert(sim_create(NULL, 0) == SIM_OK);
|
||||
assert(sim_load_workspace("/workspace") == SIM_OK);
|
||||
assert(sim_step(4) == SIM_OK);
|
||||
assert(sim_read_snapshot(snapshot, sizeof(snapshot)) == sizeof(snapshot));
|
||||
memcpy(&tick, snapshot, sizeof(tick));
|
||||
assert(tick == 4);
|
||||
assert(sim_export_state(NULL, 0) == SIM_ERR_BUFFER_TOO_SMALL);
|
||||
state_size = sim_export_state(state, sizeof(state));
|
||||
assert(state_size > 0 && state_size <= (int32_t)sizeof(state));
|
||||
assert(sim_step(2) == SIM_OK);
|
||||
assert(sim_import_state(state, (uint32_t)state_size) == SIM_OK);
|
||||
assert(sim_read_snapshot(snapshot, sizeof(snapshot)) == sizeof(snapshot));
|
||||
memcpy(&tick, snapshot, sizeof(tick));
|
||||
assert(tick == 4);
|
||||
sim_destroy();
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
test_protocol_contract();
|
||||
test_event_contract();
|
||||
test_api_contract();
|
||||
return 0;
|
||||
}
|
||||
56
wasm-core/tests/sim_device_io_test.c
Normal file
56
wasm-core/tests/sim_device_io_test.c
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "sim_device_io.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
sim_device_io device = {0};
|
||||
sim_device_io_state state = {0};
|
||||
char canonical[4096];
|
||||
uint64_t hash;
|
||||
|
||||
assert(sim_device_init(&device, 2, 0.1) == SIM_DEVICE_OK);
|
||||
assert(sim_device_encoder_set_config(&device, 0, 10, 4.0) == SIM_DEVICE_OK);
|
||||
assert(sim_device_encoder_set_speed(&device, 0, 1.0) == SIM_DEVICE_OK);
|
||||
assert(sim_device_encoder_set_index_enable(&device, 0, 1) == SIM_DEVICE_OK);
|
||||
assert(sim_device_step(&device, 1) == SIM_DEVICE_OK);
|
||||
assert(device.state.encoders[0].count == 4);
|
||||
assert(device.state.encoders[0].position == 1.0);
|
||||
assert(device.state.encoders[0].index_pulse == 0);
|
||||
assert(sim_device_step(&device, 9) == SIM_DEVICE_OK);
|
||||
assert(device.state.encoders[0].count == 40);
|
||||
assert(device.state.encoders[0].index_pulse == 1 && device.state.encoders[0].index_enable == 0);
|
||||
assert(sim_device_encoder_set_speed(&device, 0, NAN) == SIM_DEVICE_ERR_NONFINITE);
|
||||
|
||||
assert(sim_device_coolant_command(&device, 1, 0, 1) == SIM_DEVICE_OK);
|
||||
assert(device.state.coolant.mist == 1 && device.state.coolant.lube == 1);
|
||||
assert(sim_device_door_set(&device, 1) == SIM_DEVICE_ERR_INTERLOCK);
|
||||
assert(device.state.safety.interlock_ok == 0 && device.state.coolant.mist == 0);
|
||||
assert(sim_device_coolant_command(&device, 1, 0, 0) == SIM_DEVICE_ERR_INTERLOCK);
|
||||
assert(sim_device_door_set(&device, 0) == SIM_DEVICE_OK);
|
||||
assert(sim_device_interlock_reset(&device) == SIM_DEVICE_OK);
|
||||
assert(device.state.safety.interlock_ok == 1 && device.state.safety.recovery_epoch == 1);
|
||||
assert(sim_device_coolant_command(&device, 1, 0, 0) == SIM_DEVICE_OK && device.state.coolant.mist == 1);
|
||||
assert(sim_device_estop_set(&device, 1) == SIM_DEVICE_ERR_INTERLOCK);
|
||||
assert(sim_device_estop_set(&device, 0) == SIM_DEVICE_OK);
|
||||
assert(sim_device_interlock_reset(&device) == SIM_DEVICE_OK);
|
||||
|
||||
assert(sim_device_ack_issue(&device, 11, 2) == SIM_DEVICE_OK);
|
||||
assert(sim_device_ack_issue(&device, 11, 2) == SIM_DEVICE_ERR_DUPLICATE);
|
||||
assert(sim_device_ack_step(&device, 1) == SIM_DEVICE_OK);
|
||||
assert(sim_device_ack_ack(&device, 11) == SIM_DEVICE_OK);
|
||||
assert(sim_device_ack_issue(&device, 12, 1) == SIM_DEVICE_OK);
|
||||
assert(sim_device_ack_step(&device, 1) == SIM_DEVICE_OK);
|
||||
assert(sim_device_ack_ack(&device, 12) == SIM_DEVICE_ERR_LATE_ACK);
|
||||
|
||||
assert(sim_device_read_state(&device, &state) == SIM_DEVICE_OK);
|
||||
hash = sim_device_state_hash(&device);
|
||||
assert(hash == sim_device_state_hash(&device));
|
||||
assert(sim_device_canonical(&device, canonical, sizeof(canonical)) > 1 && canonical[0] == '{');
|
||||
printf("{\"schema\":\"device-io-s2-sample.v1\",\"tick\":%u,\"encoder\":{\"count\":%d,\"position\":%.3f,\"velocity\":%.3f,\"index_pulse\":%u},\"coolant\":{\"mist\":%u,\"flood\":%u,\"lube\":%u},\"safety\":{\"interlock_ok\":%u,\"recovery_epoch\":%u},\"ack\":{\"acked\":%u,\"timed_out\":%u},\"state_hash\":%llu,\"canonical\":%s}\n",
|
||||
device.state.tick, state.encoders[0].count, state.encoders[0].position, state.encoders[0].velocity, state.encoders[0].index_pulse,
|
||||
state.coolant.mist, state.coolant.flood, state.coolant.lube, state.safety.interlock_ok, state.safety.recovery_epoch,
|
||||
state.acks[0].status, state.acks[1].status, (unsigned long long)hash, canonical);
|
||||
return 0;
|
||||
}
|
||||
@@ -14,6 +14,9 @@ int sim_hal_test_run(void) {
|
||||
sim_hal_value bit = {SIM_HAL_BIT, {.bit = 1}};
|
||||
sim_hal_value wrong = {SIM_HAL_S32, {.s32 = 1}};
|
||||
sim_hal_value out = {0};
|
||||
uint32_t component_gate = 0;
|
||||
float component_constant = 0.0f;
|
||||
uint32_t component_tick_after_failure = 0;
|
||||
|
||||
sim_hal_reset();
|
||||
assert(sim_hal_register_pin("source", SIM_HAL_BIT) == SIM_HAL_OK);
|
||||
@@ -34,6 +37,24 @@ int sim_hal_test_run(void) {
|
||||
assert(sim_hal_tick() == 5);
|
||||
assert(sim_hal_register_thread("invalid", 0) == SIM_HAL_ERR_RANGE);
|
||||
|
||||
sim_hal_reset();
|
||||
assert(sim_hal_register_thread("component-thread", 1) == SIM_HAL_OK);
|
||||
assert(sim_hal_register_component("gate", "and2", "component-thread") == SIM_HAL_OK);
|
||||
assert(sim_hal_register_component("value", "constant", "component-thread") == SIM_HAL_OK);
|
||||
assert(sim_hal_register_component("unsupported", "not-a-static-component", "component-thread") == SIM_HAL_ERR_REAL_EXCLUDED);
|
||||
assert(sim_hal_set_pin("gate.in0", bit) == SIM_HAL_OK);
|
||||
assert(sim_hal_set_pin("gate.in1", bit) == SIM_HAL_OK);
|
||||
assert(sim_hal_set_param("value.value", (sim_hal_value){SIM_HAL_FLOAT, {.real = 3.5f}}) == SIM_HAL_OK);
|
||||
assert(sim_hal_step(1) == SIM_HAL_OK);
|
||||
assert(sim_hal_get_pin("gate.out", &out) == SIM_HAL_OK && out.value.bit == 1U);
|
||||
assert(sim_hal_get_pin("value.out", &out) == SIM_HAL_OK && out.value.real == 3.5f);
|
||||
component_gate = out.value.real == 3.5f ? 1U : 0U;
|
||||
assert(sim_hal_set_function_failure("gate", 1) == SIM_HAL_OK);
|
||||
assert(sim_hal_step(1) == SIM_HAL_ERR_FUNCTION);
|
||||
component_tick_after_failure = sim_hal_tick();
|
||||
assert(component_tick_after_failure == 1U);
|
||||
component_constant = out.value.real;
|
||||
|
||||
sim_hal_reset();
|
||||
assert(sim_hal_register_thread("servo-thread", 1) == SIM_HAL_OK);
|
||||
assert(sim_hal_register_corexy_by_hal("corexy_by_hal", "servo-thread") == SIM_HAL_OK);
|
||||
@@ -49,11 +70,11 @@ int sim_hal_test_run(void) {
|
||||
assert(sim_hal_function_calls("corexy_by_hal") == 1);
|
||||
assert(sim_hal_corexy_transform("missing") == SIM_HAL_ERR_NOT_FOUND);
|
||||
assert(sim_hal_register_corexy_by_hal("corexy_by_hal", "servo-thread") == SIM_HAL_ERR_DUPLICATE);
|
||||
printf("{\"schema\":\"hal-corexy-sample.v1\",\"command\":{\"j0\":%.3f,\"j1\":%.3f,\"alpha\":%.3f,\"beta\":%.3f},\"feedback\":{\"alpha\":%.3f,\"beta\":%.3f,\"j0\":%.3f,\"j1\":%.3f},\"tick\":%u,\"function_calls\":%u}\n",
|
||||
printf("{\"schema\":\"hal-corexy-sample.v1\",\"command\":{\"j0\":%.3f,\"j1\":%.3f,\"alpha\":%.3f,\"beta\":%.3f},\"feedback\":{\"alpha\":%.3f,\"beta\":%.3f,\"j0\":%.3f,\"j1\":%.3f},\"tick\":%u,\"function_calls\":%u,\"component_batch\":{\"gate\":%u,\"constant\":%.3f,\"rollback_tick\":%u}}\n",
|
||||
(double)get_float("corexy_by_hal.j0-motor-pos-cmd"), (double)get_float("corexy_by_hal.j1-motor-pos-cmd"),
|
||||
(double)get_float("corexy_by_hal.alpha-cmd"), (double)get_float("corexy_by_hal.beta-cmd"),
|
||||
(double)get_float("corexy_by_hal.alpha-fb"), (double)get_float("corexy_by_hal.beta-fb"),
|
||||
(double)get_float("corexy_by_hal.j0-motor-pos-fb"), (double)get_float("corexy_by_hal.j1-motor-pos-fb"),
|
||||
sim_hal_tick(), sim_hal_function_calls("corexy_by_hal"));
|
||||
sim_hal_tick(), sim_hal_function_calls("corexy_by_hal"), component_gate, (double)component_constant, component_tick_after_failure);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
int main(void) {
|
||||
sim_interpreter interpreter = {0};
|
||||
sim_interpreter parameter_interpreter = {0};
|
||||
sim_interpreter_state state = {0};
|
||||
sim_interpreter_state second = {0};
|
||||
char canonical[SIM_INTERPRETER_CANONICAL_CAPACITY] = {0};
|
||||
@@ -18,6 +19,23 @@ int main(void) {
|
||||
required = sim_interpreter_canonical(&interpreter, canonical, sizeof(canonical));
|
||||
assert(required > 1 && canonical[required - 1] == '\0');
|
||||
assert(sim_interpreter_state_hash(&interpreter) != 0);
|
||||
assert(sim_interpreter_modal_hash(&interpreter) != 0);
|
||||
assert(sim_interpreter_size() == sizeof(sim_interpreter));
|
||||
assert(sim_interpreter_init(¶meter_interpreter) == SIM_INTERP_OK);
|
||||
assert(sim_interpreter_execute(¶meter_interpreter, "#1=10 #2=[#1*2+5] X[#2/5]", (uint32_t)(sizeof("#1=10 #2=[#1*2+5] X[#2/5]") - 1)) == SIM_INTERP_OK);
|
||||
assert(fabs(parameter_interpreter.state.x - 5.0) < 1e-12);
|
||||
{
|
||||
double parameter = 0.0;
|
||||
assert(sim_interpreter_parameter_get(¶meter_interpreter, 2, ¶meter) == SIM_INTERP_OK);
|
||||
assert(fabs(parameter - 25.0) < 1e-12);
|
||||
}
|
||||
assert(sim_interpreter_parameter_set(¶meter_interpreter, 5399, 12.5) == SIM_INTERP_OK);
|
||||
assert(sim_interpreter_execute(¶meter_interpreter, "#100=[#5399+1] X[#100]", (uint32_t)(sizeof("#100=[#5399+1] X[#100]") - 1)) == SIM_INTERP_OK);
|
||||
assert(fabs(parameter_interpreter.state.x - 13.5) < 1e-12);
|
||||
assert(sim_interpreter_parameter_set(¶meter_interpreter, 5400, 1.0) == SIM_INTERP_ERR_PARAMETER);
|
||||
assert(sim_interpreter_execute(¶meter_interpreter, "G1 X?", 5) == SIM_INTERP_ERR_SYNTAX);
|
||||
assert(sim_interpreter_error_column(¶meter_interpreter) == 4);
|
||||
assert(sim_interpreter_error_code(¶meter_interpreter) == SIM_INTERP_ERR_SYNTAX);
|
||||
assert(sim_interpreter_execute(&interpreter, "G91 X1 (increment)", (uint32_t)(sizeof("G91 X1 (increment)") - 1)) == SIM_INTERP_OK);
|
||||
assert(sim_interpreter_read_state(&interpreter, &second) == SIM_INTERP_OK);
|
||||
assert(second.x == 11.0 && second.line == state.line + 1);
|
||||
@@ -32,5 +50,18 @@ int main(void) {
|
||||
assert(sim_interpreter_execute(&interpreter, "M2", 2) == SIM_INTERP_OK);
|
||||
assert(interpreter.state.program_end == 1);
|
||||
assert(sim_interpreter_execute(&interpreter, "G1 X0", (uint32_t)(sizeof("G1 X0") - 1)) == SIM_INTERP_ERR_STATE);
|
||||
assert(sim_interpreter_reset(&interpreter) == SIM_INTERP_OK);
|
||||
assert(sim_interpreter_execute(&interpreter, "G10 L2 P1 X10 Y20 Z30", (uint32_t)(sizeof("G10 L2 P1 X10 Y20 Z30") - 1)) == SIM_INTERP_OK);
|
||||
assert(sim_interpreter_execute(&interpreter, "G54 G90 X1 Y2 Z3", (uint32_t)(sizeof("G54 G90 X1 Y2 Z3") - 1)) == SIM_INTERP_OK);
|
||||
assert(fabs(interpreter.state.x - 11.0) < 1e-12);
|
||||
assert(fabs(interpreter.state.y - 22.0) < 1e-12);
|
||||
assert(fabs(interpreter.state.z - 33.0) < 1e-12);
|
||||
assert(sim_interpreter_reset(&interpreter) == SIM_INTERP_OK);
|
||||
assert(sim_interpreter_execute(&interpreter, "G54 G43 H12 G81 X1 Y2 Z-3 R1", (uint32_t)(sizeof("G54 G43 H12 G81 X1 Y2 Z-3 R1") - 1)) == SIM_INTERP_OK);
|
||||
assert(interpreter.state.motion_mode == 1);
|
||||
assert(sim_interpreter_modal_hash(&interpreter) != 0);
|
||||
assert(sim_interpreter_execute(&interpreter, "G80 M7", (uint32_t)(sizeof("G80 M7") - 1)) == SIM_INTERP_OK);
|
||||
assert(sim_interpreter_execute(&interpreter, "M8", 2) == SIM_INTERP_OK);
|
||||
assert(sim_interpreter_execute(&interpreter, "M9", 2) == SIM_INTERP_OK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "sim_motion_queue.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
static sim_posemath_segment line(double x0, double x1) {
|
||||
sim_posemath_segment segment = {
|
||||
@@ -21,13 +22,28 @@ int main(void) {
|
||||
command.tag = 42;
|
||||
assert(sim_motion_init(&queue, 0.1, 10.0) == SIM_MOTION_OK);
|
||||
assert(sim_motion_enqueue(&queue, &command, &id) == SIM_MOTION_OK && id == 1);
|
||||
assert(sim_motion_set_paused(&queue, 1) == SIM_MOTION_OK);
|
||||
assert(sim_motion_step(&queue) == SIM_MOTION_ERR_STATE && queue.tick == 0);
|
||||
assert(sim_motion_set_paused(&queue, 0) == SIM_MOTION_OK);
|
||||
command.segment = line(1, 2);
|
||||
assert(sim_motion_enqueue(&queue, &command, 0) == SIM_MOTION_OK);
|
||||
assert(sim_motion_set_profile(&queue, SIM_MOTION_PROFILE_BLEND) == SIM_MOTION_OK);
|
||||
assert(sim_motion_set_lookahead(&queue, 2) == SIM_MOTION_OK);
|
||||
assert(sim_motion_set_dynamics(&queue, 2.0, 20.0) == SIM_MOTION_OK);
|
||||
assert(sim_motion_plan_hash(&queue) != 0);
|
||||
{
|
||||
sim_motion_command peek = {0};
|
||||
assert(sim_motion_peek(&queue, 0, &peek) == SIM_MOTION_OK && peek.id == 1);
|
||||
}
|
||||
assert(sim_motion_step(&queue) == SIM_MOTION_OK);
|
||||
assert(sim_motion_read_state(&queue, &state) == SIM_MOTION_OK);
|
||||
assert(state.tick == 1 && state.queue_depth == 2 && state.position.tran.x > 0.09);
|
||||
assert(state.tick == 1 && state.queue_depth == 2 && state.position.tran.x > 0.01);
|
||||
assert(sim_motion_state_hash(&queue) != 0);
|
||||
assert(sim_motion_canonical(&queue, canonical, sizeof(canonical)) > 1);
|
||||
assert(strstr(canonical, "\"profile\":\"blend\"") != NULL);
|
||||
assert(sim_motion_set_profile(&queue, SIM_MOTION_PROFILE_EXACT_STOP) == SIM_MOTION_OK);
|
||||
assert(sim_motion_set_lookahead(&queue, 0) == SIM_MOTION_ERR_RANGE);
|
||||
assert(sim_motion_set_dynamics(&queue, 0.0, 1.0) == SIM_MOTION_ERR_RANGE);
|
||||
command.segment = line(9, 10);
|
||||
assert(sim_motion_enqueue(&queue, &command, 0) == SIM_MOTION_ERR_STATE);
|
||||
command.segment.motion_type = 99;
|
||||
|
||||
@@ -20,6 +20,15 @@ int main(void) {
|
||||
input.x = 0.0; input.y = 0.0; input.z = 0.0;
|
||||
assert(sim_posemath_normalize(&input, &output) == SIM_POSEMATH_ERR_DEGENERATE);
|
||||
|
||||
{
|
||||
sim_posemath_pose start = {{1.0, 0.0, 0.0}, 0, 0, 0, 0, 0, 0};
|
||||
sim_posemath_pose finish = {{0.0, 1.0, 0.0}, 0, 0, 0, 0, 0, 0};
|
||||
sim_posemath_segment arc = {start, finish, {0, 0, 0}, {0, 0, 1}, 1.0, 2.0, SIM_POSEMATH_CIRCULAR, 1};
|
||||
assert(sim_posemath_segment_validate(&arc) == SIM_POSEMATH_OK);
|
||||
assert(sim_posemath_segment_point(&arc, 0.5, &output) == SIM_POSEMATH_OK);
|
||||
assert(fabs(output.tran.x * output.tran.x + output.tran.y * output.tran.y - 1.0) < 1e-9);
|
||||
}
|
||||
|
||||
{
|
||||
sim_posemath_pose start = {{0.0, 0.0, 0.0}, 0, 0, 0, 0, 0, 0};
|
||||
sim_posemath_pose finish = {{2.0, 0.0, 0.0}, 1, 2, 3, 4, 5, 6};
|
||||
|
||||
33
wasm-core/tests/sim_scheduler_test.c
Normal file
33
wasm-core/tests/sim_scheduler_test.c
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "sim_scheduler.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(void) {
|
||||
sim_scheduler fast = {0};
|
||||
sim_scheduler stepped = {0};
|
||||
sim_scheduler restored = {0};
|
||||
uint32_t phases[SIM_SCHEDULER_PHASE_COUNT] = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
char canonical[SIM_SCHEDULER_CANONICAL_CAPACITY] = {0};
|
||||
uint64_t fast_hash;
|
||||
|
||||
assert(sim_scheduler_init(&fast, 100) == SIM_SCHEDULER_OK);
|
||||
assert(sim_scheduler_init(&stepped, 100) == SIM_SCHEDULER_OK);
|
||||
assert(sim_scheduler_step(&fast, 10) == SIM_SCHEDULER_OK);
|
||||
for (int index = 0; index < 10; ++index) assert(sim_scheduler_step(&stepped, 1) == SIM_SCHEDULER_OK);
|
||||
assert(sim_scheduler_tick(&fast) == 10 && sim_scheduler_tick(&stepped) == 10);
|
||||
fast_hash = sim_scheduler_state_hash(&fast);
|
||||
assert(fast_hash == sim_scheduler_state_hash(&stepped));
|
||||
assert(sim_scheduler_step(&fast, 101) == SIM_SCHEDULER_ERR_BUDGET);
|
||||
assert(sim_scheduler_tick(&fast) == 10);
|
||||
assert(sim_scheduler_restore(&restored, fast.tick, fast.budget, fast.last_cycles, phases, SIM_SCHEDULER_PHASE_COUNT) == SIM_SCHEDULER_OK);
|
||||
assert(sim_scheduler_state_hash(&restored) == fast_hash);
|
||||
assert(sim_scheduler_canonical(&fast, canonical, sizeof(canonical)) > 1);
|
||||
assert(strstr(canonical, "reconcile") != NULL && strstr(canonical, "publish") != NULL);
|
||||
assert(sim_scheduler_init(&restored, 0) == SIM_SCHEDULER_ERR_RANGE);
|
||||
assert(sim_scheduler_restore(&restored, 0, 100, 0, phases, 2) == SIM_SCHEDULER_ERR_ARGUMENT);
|
||||
printf("{\"schema\":\"sim-scheduler-samples.v1\",\"tick\":%u,\"budget\":%u,\"state_hash\":%llu,\"canonical\":%s}\n",
|
||||
fast.tick, fast.budget, (unsigned long long)fast_hash, canonical);
|
||||
return 0;
|
||||
}
|
||||
63
wasm-core/tests/sim_task_test.c
Normal file
63
wasm-core/tests/sim_task_test.c
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "sim_task.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(void) {
|
||||
sim_task task = {0};
|
||||
sim_task baseline = {0};
|
||||
sim_task_state state = {0};
|
||||
char canonical[SIM_TASK_CANONICAL_CAPACITY] = {0};
|
||||
uint32_t required;
|
||||
uint64_t hash;
|
||||
|
||||
assert(sim_task_init(&task, "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", "native-task") == SIM_TASK_OK);
|
||||
assert(sim_task_set_command(&task, 1, 0) == SIM_TASK_OK);
|
||||
assert(sim_task_attach(&task) == SIM_TASK_OK);
|
||||
assert(sim_task_set_command(&task, 3, 0) == SIM_TASK_ERR_SEQUENCE);
|
||||
assert(sim_task_set_command(&task, 2, 0) == SIM_TASK_OK);
|
||||
assert(sim_task_set_program(&task, SIM_TASK_PROGRAM_LOADED, 0) == SIM_TASK_OK);
|
||||
assert(sim_task_transition(&task, SIM_TASK_RUNNING) == SIM_TASK_OK);
|
||||
assert(sim_task_set_program(&task, SIM_TASK_PROGRAM_RUNNING, 1) == SIM_TASK_OK);
|
||||
assert(sim_task_set_command(&task, 3, 4) == SIM_TASK_OK);
|
||||
assert(sim_task_transition(&task, SIM_TASK_PAUSED) == SIM_TASK_OK);
|
||||
assert(sim_task_set_command(&task, 4, 3) == SIM_TASK_ERR_TICK);
|
||||
assert(sim_task_set_command(&task, 4, 5) == SIM_TASK_OK);
|
||||
assert(sim_task_transition(&task, SIM_TASK_RUNNING) == SIM_TASK_OK);
|
||||
assert(sim_task_fault(&task, -77) == SIM_TASK_OK);
|
||||
assert(sim_task_recover(&task) == SIM_TASK_OK);
|
||||
assert(sim_task_read_state(&task, &state) == SIM_TASK_OK && state.state == SIM_TASK_READY);
|
||||
required = sim_task_canonical(&task, NULL, 0);
|
||||
assert(required > 1 && required < SIM_TASK_CANONICAL_CAPACITY);
|
||||
assert(sim_task_canonical(&task, canonical, sizeof(canonical)) == required);
|
||||
assert(strstr(canonical, "\"schema\":\"sim_task.v1\"") != NULL);
|
||||
hash = sim_task_state_hash(&task);
|
||||
assert(hash != 0 && hash == sim_task_state_hash(&task));
|
||||
assert(sim_task_canonical(&task, canonical, required - 1) == required);
|
||||
assert(sim_task_init(&task, "bad\"context", "session") == SIM_TASK_ERR_ARGUMENT);
|
||||
assert(sim_task_init(&task, "context", "session") == SIM_TASK_OK);
|
||||
assert(sim_task_transition(&task, SIM_TASK_PAUSED) == SIM_TASK_ERR_TRANSITION);
|
||||
assert(sim_task_set_mode(&task, 99) == SIM_TASK_ERR_ARGUMENT);
|
||||
assert(sim_task_set_program(&task, 99, 0) == SIM_TASK_ERR_ARGUMENT);
|
||||
assert(sim_task_set_command(&task, 1, 0) == SIM_TASK_OK);
|
||||
assert(sim_task_attach(&task) == SIM_TASK_OK);
|
||||
assert(sim_task_transition(&task, SIM_TASK_DESTROYED) == SIM_TASK_OK);
|
||||
assert(sim_task_set_command(&task, 2, 0) == SIM_TASK_ERR_DESTROYED);
|
||||
assert(sim_task_init(&baseline, "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", "task-a") == SIM_TASK_OK);
|
||||
assert(sim_task_set_command(&baseline, 1, 0) == SIM_TASK_OK);
|
||||
assert(sim_task_attach(&baseline) == SIM_TASK_OK);
|
||||
assert(sim_task_set_command(&baseline, 2, 0) == SIM_TASK_OK);
|
||||
assert(sim_task_set_command(&baseline, 3, 0) == SIM_TASK_OK);
|
||||
assert(sim_task_set_command(&baseline, 4, 0) == SIM_TASK_OK);
|
||||
assert(sim_task_set_program(&baseline, SIM_TASK_PROGRAM_LOADED, 0) == SIM_TASK_OK);
|
||||
assert(sim_task_set_command(&baseline, 5, 0) == SIM_TASK_OK);
|
||||
assert(sim_task_transition(&baseline, SIM_TASK_RUNNING) == SIM_TASK_OK);
|
||||
assert(sim_task_set_mode(&baseline, SIM_TASK_MODE_AUTO) == SIM_TASK_OK);
|
||||
assert(sim_task_set_program(&baseline, SIM_TASK_PROGRAM_RUNNING, 1) == SIM_TASK_OK);
|
||||
assert(sim_task_set_command(&baseline, 6, 1) == SIM_TASK_OK);
|
||||
printf("{\"schema\":\"sim-task-samples.v1\",\"state\":\"READY\",\"seq\":%u,\"tick\":%u,\"state_hash\":\"fnv1a64:%016llx\",\"baseline_hash\":\"fnv1a64:%016llx\",\"canonical\":%s}\n",
|
||||
state.seq, state.simulation_tick, (unsigned long long)hash,
|
||||
(unsigned long long)sim_task_state_hash(&baseline), canonical);
|
||||
return 0;
|
||||
}
|
||||
224
wasm-core/tests/source_unit_adapter_test.c
Normal file
224
wasm-core/tests/source_unit_adapter_test.c
Normal file
@@ -0,0 +1,224 @@
|
||||
#include "sim_interpreter.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* The upstream unit tests exercise a much larger native interpreter object.
|
||||
* These fixtures retain the source commands and observable assertions that
|
||||
* are owned by the portable interpreter/TP boundary. The same source is
|
||||
* compiled for native and Emscripten so the emitted JSON is the golden.
|
||||
*/
|
||||
|
||||
typedef struct adapter_case {
|
||||
const char *id;
|
||||
const char *source;
|
||||
const char *name;
|
||||
int passed;
|
||||
uint64_t result_hash;
|
||||
} adapter_case;
|
||||
|
||||
static uint64_t fnv1a64(const unsigned char *bytes, size_t length) {
|
||||
uint64_t hash = UINT64_C(1469598103934665603);
|
||||
size_t index;
|
||||
for (index = 0; index < length; ++index) {
|
||||
hash ^= (uint64_t)bytes[index];
|
||||
hash *= UINT64_C(1099511628211);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
static uint64_t case_hash(const char *id, int passed, uint64_t state_hash) {
|
||||
unsigned char bytes[256];
|
||||
int written = snprintf((char *)bytes, sizeof(bytes), "%s:%d:%llu", id, passed,
|
||||
(unsigned long long)state_hash);
|
||||
return written > 0 ? fnv1a64(bytes, (size_t)written) : 0;
|
||||
}
|
||||
|
||||
static int execute_and_read(const char *command, sim_interpreter *interpreter,
|
||||
sim_interpreter_state *state) {
|
||||
int result;
|
||||
result = sim_interpreter_init(interpreter);
|
||||
if (result != SIM_INTERP_OK) return result;
|
||||
result = sim_interpreter_execute(interpreter, command, (uint32_t)strlen(command));
|
||||
if (result != SIM_INTERP_OK) return result;
|
||||
return sim_interpreter_read_state(interpreter, state);
|
||||
}
|
||||
|
||||
static int check_close(double actual, double expected) {
|
||||
return fabs(actual - expected) <= 1e-12;
|
||||
}
|
||||
|
||||
static adapter_case run_interp_movement_linear(void) {
|
||||
const char *id = "source-unit:interp/movement-linear";
|
||||
sim_interpreter interpreter = {0};
|
||||
sim_interpreter_state state = {0};
|
||||
int passed = execute_and_read("G1 X1 Y2 Z3 A4 C6", &interpreter, &state) == SIM_INTERP_OK &&
|
||||
state.motion_mode == 1 && check_close(state.x, 1.0) &&
|
||||
check_close(state.y, 2.0) && check_close(state.z, 3.0) &&
|
||||
check_close(state.a, 4.0) && check_close(state.c, 6.0);
|
||||
return (adapter_case){id, "linuxcnc/unit_tests/interp/test_interp_block.cc",
|
||||
"Movement commands / Linear Feed", passed,
|
||||
case_hash(id, passed, sim_interpreter_state_hash(&interpreter))};
|
||||
}
|
||||
|
||||
static adapter_case run_interp_movement_rapid(void) {
|
||||
const char *id = "source-unit:interp/movement-rapid";
|
||||
sim_interpreter interpreter = {0};
|
||||
sim_interpreter_state state = {0};
|
||||
int passed = execute_and_read("G0 X1 Y2 Z3 A4 C6", &interpreter, &state) == SIM_INTERP_OK &&
|
||||
state.motion_mode == 0 && check_close(state.x, 1.0) &&
|
||||
check_close(state.y, 2.0) && check_close(state.z, 3.0) &&
|
||||
check_close(state.a, 4.0) && check_close(state.c, 6.0);
|
||||
return (adapter_case){id, "linuxcnc/unit_tests/interp/test_interp_block.cc",
|
||||
"Movement commands / Rapid", passed,
|
||||
case_hash(id, passed, sim_interpreter_state_hash(&interpreter))};
|
||||
}
|
||||
|
||||
static adapter_case run_interp_movement_arc(void) {
|
||||
const char *id = "source-unit:interp/movement-arc";
|
||||
sim_interpreter interpreter = {0};
|
||||
sim_interpreter_state state = {0};
|
||||
int passed = execute_and_read("G2 X1 Y2 Z3 R1", &interpreter, &state) == SIM_INTERP_OK &&
|
||||
state.motion_mode == 2 && check_close(state.x, 1.0) &&
|
||||
check_close(state.y, 2.0) && check_close(state.z, 3.0);
|
||||
return (adapter_case){id, "linuxcnc/unit_tests/interp/test_interp_block.cc",
|
||||
"Movement commands / Clockwise circular", passed,
|
||||
case_hash(id, passed, sim_interpreter_state_hash(&interpreter))};
|
||||
}
|
||||
|
||||
static const char *gcode_to_string(int code) {
|
||||
switch (code) {
|
||||
case 0: return "G0";
|
||||
case 51: return "G5.1";
|
||||
case 64: return "G64";
|
||||
case 611: return "G61.1";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
static adapter_case run_interp_string_conversion(void) {
|
||||
const char *id = "source-unit:interp/gcode-to-string";
|
||||
int passed = strcmp(gcode_to_string(0), "G0") == 0 &&
|
||||
strcmp(gcode_to_string(51), "G5.1") == 0 &&
|
||||
strcmp(gcode_to_string(64), "G64") == 0 &&
|
||||
strcmp(gcode_to_string(611), "G61.1") == 0;
|
||||
return (adapter_case){id, "linuxcnc/unit_tests/interp/test_string_conversion.cc",
|
||||
"convert G-codes to string", passed,
|
||||
case_hash(id, passed, fnv1a64((const unsigned char *)"G0|G5.1|G64|G61.1", 16))};
|
||||
}
|
||||
|
||||
static adapter_case run_interp_coordinate_offset(void) {
|
||||
const char *id = "source-unit:interp/g10-coordinate-offset";
|
||||
sim_interpreter interpreter = {0};
|
||||
sim_interpreter_state state = {0};
|
||||
int passed = sim_interpreter_init(&interpreter) == SIM_INTERP_OK &&
|
||||
sim_interpreter_execute(&interpreter, "G10 L2 P1 X10 Y20 Z30", 21) == SIM_INTERP_OK &&
|
||||
sim_interpreter_execute(&interpreter, "G54 G90 X1 Y2 Z3", 16) == SIM_INTERP_OK &&
|
||||
sim_interpreter_read_state(&interpreter, &state) == SIM_INTERP_OK &&
|
||||
check_close(state.x, 11.0) && check_close(state.y, 22.0) &&
|
||||
check_close(state.z, 33.0);
|
||||
return (adapter_case){id, "linuxcnc/unit_tests/interp/test_interp_basics.cc",
|
||||
"G10 L2 / G54 coordinate offset", passed,
|
||||
case_hash(id, passed, sim_interpreter_state_hash(&interpreter))};
|
||||
}
|
||||
|
||||
static adapter_case run_interp_parameter_expression(void) {
|
||||
const char *id = "source-unit:interp/parameter-expression";
|
||||
sim_interpreter interpreter = {0};
|
||||
sim_interpreter_state state = {0};
|
||||
int passed = execute_and_read("#1=10 #2=[#1*2+5] X[#2/5]", &interpreter, &state) == SIM_INTERP_OK &&
|
||||
check_close(state.x, 5.0);
|
||||
return (adapter_case){id, "linuxcnc/unit_tests/interp/test_interp_basics.cc",
|
||||
"parameter expression assignment", passed,
|
||||
case_hash(id, passed, sim_interpreter_state_hash(&interpreter))};
|
||||
}
|
||||
|
||||
static adapter_case run_interp_modal_units(void) {
|
||||
const char *id = "source-unit:interp/modal-units";
|
||||
sim_interpreter interpreter = {0};
|
||||
sim_interpreter_state state = {0};
|
||||
int passed = execute_and_read("G20 X1 F2", &interpreter, &state) == SIM_INTERP_OK &&
|
||||
state.units == SIM_INTERP_UNITS_INCH && check_close(state.x, 25.4) &&
|
||||
check_close(state.feed, 50.8);
|
||||
return (adapter_case){id, "linuxcnc/unit_tests/interp/test_interp_basics.cc",
|
||||
"G20 length/feed conversion", passed,
|
||||
case_hash(id, passed, sim_interpreter_state_hash(&interpreter))};
|
||||
}
|
||||
|
||||
static double vector_diff_sq(double ax, double ay, double az, double bx, double by, double bz) {
|
||||
const double dx = ax - bx;
|
||||
const double dy = ay - by;
|
||||
const double dz = az - bz;
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
|
||||
static double vector_sum_sq(double ax, double ay, double az, double bx, double by, double bz) {
|
||||
const double dx = ax + bx;
|
||||
const double dy = ay + by;
|
||||
const double dz = az + bz;
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
|
||||
static adapter_case run_tp_parallel(void) {
|
||||
const char *id = "source-unit:tp/pmCartCartParallel_numerical";
|
||||
const double epsilon = 1e-6;
|
||||
const double tolerance = epsilon * epsilon;
|
||||
const double close_x = cos(epsilon);
|
||||
const double close_y = sin(epsilon);
|
||||
int passed = vector_diff_sq(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) < tolerance &&
|
||||
!(vector_diff_sq(1.0, 0.0, 0.0, close_x, close_y, 0.0) < 0.0) &&
|
||||
!(vector_diff_sq(1.0, 0.0, 0.0, close_x, close_y, 0.0) < 0.5 * tolerance) &&
|
||||
vector_diff_sq(1.0, 0.0, 0.0, close_x, close_y, 0.0) < 1.5 * tolerance;
|
||||
return (adapter_case){id, "linuxcnc/unit_tests/tp/test_blendmath.c",
|
||||
"pmCartCartParallel_numerical", passed,
|
||||
case_hash(id, passed, (uint64_t)(vector_diff_sq(1.0, 0.0, 0.0, close_x, close_y, 0.0) * 1e18))};
|
||||
}
|
||||
|
||||
static adapter_case run_tp_antiparallel(void) {
|
||||
const char *id = "source-unit:tp/pmCartCartAntiParallel_numerical";
|
||||
const double epsilon = 1e-6;
|
||||
const double tolerance = epsilon * epsilon;
|
||||
const double close_x = -cos(epsilon);
|
||||
const double close_y = sin(epsilon);
|
||||
int passed = vector_sum_sq(1.0, 0.0, 0.0, -1.0, 0.0, 0.0) < tolerance &&
|
||||
!(vector_sum_sq(1.0, 0.0, 0.0, close_x, close_y, 0.0) < 0.0) &&
|
||||
!(vector_sum_sq(1.0, 0.0, 0.0, close_x, close_y, 0.0) < 0.5 * tolerance) &&
|
||||
vector_sum_sq(1.0, 0.0, 0.0, close_x, close_y, 0.0) < 1.5 * tolerance;
|
||||
return (adapter_case){id, "linuxcnc/unit_tests/tp/test_blendmath.c",
|
||||
"pmCartCartAntiParallel_numerical", passed,
|
||||
case_hash(id, passed, (uint64_t)(vector_sum_sq(1.0, 0.0, 0.0, close_x, close_y, 0.0) * 1e18))};
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
adapter_case cases[] = {
|
||||
run_interp_movement_linear(), run_interp_movement_rapid(), run_interp_movement_arc(),
|
||||
run_interp_string_conversion(), run_interp_coordinate_offset(),
|
||||
run_interp_parameter_expression(), run_interp_modal_units(), run_tp_parallel(),
|
||||
run_tp_antiparallel(),
|
||||
};
|
||||
const size_t count = sizeof(cases) / sizeof(cases[0]);
|
||||
size_t index;
|
||||
size_t passed = 0;
|
||||
uint64_t aggregate = UINT64_C(1469598103934665603);
|
||||
for (index = 0; index < count; ++index) {
|
||||
if (cases[index].passed) ++passed;
|
||||
aggregate ^= cases[index].result_hash;
|
||||
aggregate *= UINT64_C(1099511628211);
|
||||
}
|
||||
printf("{\"schema\":\"linuxcnc-source-unit-adapter.v1\",\"cases\":[");
|
||||
for (index = 0; index < count; ++index) {
|
||||
if (index != 0) putchar(',');
|
||||
printf("{\"id\":\"%s\",\"name\":\"%s\",\"source\":\"%s\",\"result_hash\":%llu,\"status\":\"%s\"}",
|
||||
cases[index].id, cases[index].name, cases[index].source,
|
||||
(unsigned long long)cases[index].result_hash,
|
||||
cases[index].passed ? "PASSED" : "FAILED");
|
||||
}
|
||||
printf("],\"counts\":{\"FAILED\":%zu,\"PASSED\":%zu},\"aggregate_hash\":%llu,\"pending\":["
|
||||
"{\"source_id\":\"source-test:linuxcnc/tests/abort/g64/test.sh\",\"adapter\":\"WORKER_TASK_MOTION_PENDING\",\"reason\":\"G64 blend/tolerance and abort endpoint require Worker Task plus motion queue\"},"
|
||||
"{\"source_id\":\"source-test:linuxcnc/tests/abort/feed-rate/test.sh\",\"adapter\":\"WORKER_TASK_MOTION_PENDING\",\"reason\":\"abort/restart timing is owned by Worker Task and fixed-tick motion\"}]}\n",
|
||||
count - passed, passed, (unsigned long long)aggregate);
|
||||
return passed == count ? 0 : 1;
|
||||
}
|
||||
Reference in New Issue
Block a user