#include "sim_protocol.h" #include #include #include 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; }