export const PROTOCOL_SCHEMA = 1 as const; export const CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1" as const; export type MessageKind = | "HELLO" | "CAPABILITIES" | "SESSION_ATTACH" | "SESSION_READY" | "LOAD_WORKSPACE" | "COMMAND_RESULT" | "STATE_SNAPSHOT" | "EVENT" | "ALARM" | "RECOVERY_STATUS" | "ERROR" | "UI_STATE_RESULT"; export type MessageEnvelope = { schema_version: typeof PROTOCOL_SCHEMA; kind: MessageKind | string; context_id: string; session_id: string; seq: number; operation_id?: string; simulation_tick?: number; payload: TPayload; }; export type MotionSnapshot = { homing?: boolean; stepping?: boolean; axes?: number[]; joints?: number[]; velocity?: number[]; homed?: boolean | boolean[]; homed_axes?: boolean[]; limits?: number | Record; on_any_limit?: boolean; fault?: string | null; velocity_limit?: number; velocity_mm_min?: number; feed_command_mm_min?: number; velocity_scale?: number; jog_scale?: number; jog_velocity_linear?: number; jog_velocity_angular?: number; spindle_override?: number; spindle_speed_rpm?: number; spindle_command_rpm?: number; spindle_enabled?: boolean; spindle_at_speed?: boolean; max_velocity?: number; }; export type HomeJointStatus = { joint: number; homing: boolean; homed: boolean; home_state: number; home_state_name: string; position: number; motor_position: number; motor_offset: number; velocity: number; home_sequence: number; }; export type SourceStatus = { task: { mode: string; state: string; interpState: string }; traj: { mode: string; motion_type: number }; homing_active: boolean; joint: HomeJointStatus[]; homing_history: Array<{ tick: number; joint: HomeJointStatus[] }>; }; export type TrajectorySnapshot = { queue?: Array<{ id?: number; source_line?: number | null; start?: Record; end?: Record }>; active?: { id?: number; source_line?: number | null; start?: Record; end?: Record } | null; queue_depth?: number; }; export type SimSnapshotV1 = { source_status?: SourceStatus; schema?: string; snapshot_id?: string; base_snapshot_id?: string; full?: boolean; simulation_tick: number; state_hash: string; task?: { state: string; mode: string; interp_state?: string; current_line?: number | null }; program?: Record | null; motion?: MotionSnapshot; wasm_motion?: Record; authoritative?: { state_hash?: string; planner?: TrajectorySnapshot; kinematics?: Record }; coordinates?: { machine?: number[]; relative?: number[]; g5x?: number; g92?: number[]; tool_offset?: number[] }; spindle?: Array<{ enabled?: boolean; direction?: number; commanded?: number; actual?: number; at_speed?: boolean }>; // XYZAC panel snapshots carry the canonical LinuxCNC WASM IO envelope // (`{ schema, source, state, ... }`). Generic legacy cases may still add // coolant/probe fields, so keep this boundary open to both shapes. io?: Record; tool?: { current?: number; prepared?: number; pocket?: number; change_state?: string }; overrides?: { feed?: number; rapid?: number; spindle?: number[]; adaptive?: number; feed_hold?: boolean }; hal?: { graph_revision?: number; changed_values?: ArrayBuffer }; alarms?: Array<{ id?: string; severity?: string; code?: string; acknowledged?: boolean }>; recovery?: { status?: string; last_verified_seq?: number; checkpoint_tick?: number }; panel_snapshot?: PanelSnapshotV1; configuration?: Record | null; visual?: PanelSnapshotV1["visual"]; motion_sample?: { tick?: number; pose?: Record; joints?: number[]; active_segment_id?: number; active_progress?: number }; snapshot_mode?: "full" | "motion_delta"; kinematics?: XyzacKinematicsSnapshot; xyzac?: XyzacCapabilitySnapshot; gates?: Record; ui_gates?: Record; ui_relief?: Record; motion_authority?: string; task_authority?: string; }; export type TrtKinematicsSnapshot = { schema: "wasm-xyzac-kinematics.v1"; status: "verified" | "error" | "blocked"; active_type: 0 | 1 | 2; native_type: string; coordinates: "XYZAC" | "XYZBC"; joint_count: 5; linear_units: "mm"; angular_units: "deg"; pose: Record<"x" | "y" | "z" | "a" | "b" | "c" | "u" | "v" | "w", number>; joints: [number, number, number, number, number]; offsets: { x_rot_point: number; y_offset: number; z_offset: number; tool_offset: number; y_rot_point?: number; z_rot_point?: number; x_offset?: number }; generation: number; switch_count: number; state_hash: string; wasm_state_hash: string; }; export type XyzacKinematicsSnapshot = TrtKinematicsSnapshot; export type XyzacCapabilitySnapshot = { coordinates: "XYZAC" | "XYZBC"; joint_count: 5; active_type: 0 | 1 | 2; hal_pin_vars: boolean; switchkins_net: boolean; finite_offsets: boolean; userk_capability: boolean; }; export type PanelSnapshotV1 = { source_status?: SourceStatus; schema: "axis-panel-snapshot.v1"; context_id: string; session_id: string; simulation_tick: number; state_hash: string; task: Record; program: Record | null; motion: Record; coordinates?: { machine?: number[]; relative?: number[]; commanded?: number[] }; overrides?: { feed?: number; spindle?: number[] }; wasm_motion: Record; home_limit: Record; hal: Record; devices: Record; device_io: Record; configuration: Record | null; axis_state: AxisStateView; gates: Record; ui_gates?: Record; ui_relief?: Record; visual: { schema: "axis-visual.v1"; pose: Record; joints: number[]; pose_epoch?: number; live_path_epoch?: number; toolpath?: Array>; active_segment_id?: number | null; active_progress?: number; motion_trace?: Array<{ tick: number; pose: Record; joints: number[]; active_segment_id: number; active_progress: number }>; machine_model?: XyzacMachineModel; source: "wasm_canonical_motion"; }; authoritative: Record; motion_authority: string; task_authority: string; kinematics?: XyzacKinematicsSnapshot; xyzac?: XyzacCapabilitySnapshot; io?: Record; }; export type XyzacMachineModel = { schema: "linuxcnc-vismach-model.v1"; id: string; source: "linuxcnc-wasm-worker"; source_path: string; source_hash: string | null; coordinate_system: "XYZAC" | "XYZBC"; geometry_variant?: "moving-knee-x-tilt" | "moving-head-y-tilt"; tilt_axis?: "X" | "Y"; rotary_axis?: "Z"; joints: string[]; components: string[]; }; export type Capabilities = { context_id?: string; abi?: Record; snapshot?: Record; commands?: string[]; streams?: string[]; limits?: Record; exclusions?: string[]; }; export type AxisStateView = { state: "ESTOP" | "ESTOP_RESET" | "OFF" | "ON" | "HOME" | "IDLE" | "RUNNING" | "PAUSED"; machine_state: "ESTOP" | "ESTOP_RESET" | "OFF" | "ON"; operation_state: "HOME" | "IDLE" | "RUNNING" | "PAUSED"; source_task_state: string; source_interp_state: string; source_mode: "MANUAL" | "MDI" | "AUTO"; }; export function isEnvelope(value: unknown): value is MessageEnvelope { if (!value || typeof value !== "object") return false; const candidate = value as Partial; return candidate.schema_version === PROTOCOL_SCHEMA && typeof candidate.kind === "string" && typeof candidate.context_id === "string" && typeof candidate.session_id === "string" && Number.isSafeInteger(candidate.seq) && "payload" in candidate; }