use phoxal_macros::phoxal_api_tree;
pub use phoxal_bus::{ApiVersion, ContractBody};
phoxal_api_tree! {
version y2026_1 {
drive {
enum StopReason {
NoTarget,
EmergencyStop,
Fault,
}
enum ActuatorAuthority {
Active,
Stopped,
}
struct Target {
linear_x_mps: f32,
angular_z_radps: f32,
curvature_limit_radpm: Option<f32>,
}
struct State {
target: Target,
limited_target: Target,
actuator_authority: ActuatorAuthority,
stop_reason: Option<StopReason>,
}
topic target: command Target;
topic state: state State;
}
battery {
struct State {
voltage_v: f32,
current_a: f32,
charge_ratio: f32,
}
topic state: state State;
}
safety {
#[derive(Copy, Eq)]
enum SafetyDecision {
Allow,
Slow,
Stop,
EmergencyStop,
UnknownConservative,
}
struct Constraint {
min: f64,
max: f64,
}
struct MotionConstraint {
linear_x_mps: Constraint,
angular_z_radps: Constraint,
}
#[derive(Copy, Eq)]
#[serde(rename_all = "snake_case")]
enum SafetyReasonCode {
ObstacleDetected,
BatteryLow,
BatteryCritical,
DriveFault,
LocalizationLost,
SourceStale,
EmergencyStopEngaged,
Unknown,
}
struct SafetyReason {
code: SafetyReasonCode,
detail: Option<String>,
}
struct SafetySourceRevision {
localization: Option<u64>,
map: Option<u64>,
}
struct SafetyAuthorization {
decision: SafetyDecision,
approved_motion: MotionConstraint,
reasons: Vec<SafetyReason>,
source_revision: SafetySourceRevision,
expires_at_ns: Option<u64>,
}
struct Status {
decision: SafetyDecision,
active_reasons: Vec<SafetyReason>,
}
#[derive(Eq)]
struct EmergencyStopRequest {
engaged: bool,
}
topic authorization: state SafetyAuthorization;
topic state: state Status;
topic estop: command EmergencyStopRequest;
}
mission {
struct Goal {
x_m: f64,
y_m: f64,
yaw_rad: Option<f64>,
}
enum Command {
Start(Goal),
Pause,
Resume,
Cancel,
}
#[derive(Copy, Eq)]
enum Phase {
Idle,
Active,
Paused,
Succeeded,
Failed,
}
struct State {
phase: Phase,
goal: Option<Goal>,
detail: Option<String>,
}
topic command: command Command;
topic goal: state Goal;
topic state: state State;
}
joint(joint) {
struct JointState {
position_rad: f64,
velocity_radps: f64,
effort_nm: Option<f64>,
}
topic state: state JointState;
}
frame {
struct FrameTransform {
parent_frame_id: String,
child_frame_id: String,
translation_m: [f64; 3],
rotation_quat_xyzw: [f64; 4],
stamp_ns: Option<u64>,
}
struct StaticTransforms {
transforms: Vec<FrameTransform>,
}
struct Tree {
transforms: Vec<FrameTransform>,
}
struct LookupRequest {
target_frame_id: String,
source_frame_id: String,
at_ns: Option<u64>,
}
struct LookupResponse {
transform: Option<FrameTransform>,
}
topic tree: state Tree;
topic static_transforms: state StaticTransforms;
topic lookup: query LookupRequest => LookupResponse;
}
power {
#[derive(Copy, Eq)]
enum Command {
Reboot,
Shutdown,
}
#[derive(Copy, Eq)]
enum Status {
Idle,
Rebooting,
ShuttingDown,
Failed,
}
#[derive(Copy, Eq)]
#[serde(rename_all = "snake_case")]
enum RejectedReason {
SupervisorUnavailable,
SupervisorReturnedHttp,
}
#[derive(Copy, Eq)]
#[serde(rename_all = "snake_case")]
enum FailedReason {
SupervisorTransport,
}
struct State {
status: Status,
detail: Option<String>,
}
topic command: command Command;
topic state: state State;
}
motion {
struct Target {
linear_x_mps: f32,
angular_z_radps: f32,
curvature_limit_radpm: Option<f32>,
}
#[derive(Copy, Eq)]
#[serde(rename_all = "snake_case")]
enum SafetyDecision {
Allow,
Slow,
Stop,
EmergencyStop,
UnknownConservative,
}
#[derive(Copy, Eq)]
#[serde(rename_all = "snake_case")]
enum MotionSource {
Manual,
Follow,
MissionStop,
Recovery,
EmergencyStop,
}
#[derive(Copy, Eq)]
#[serde(rename_all = "snake_case")]
enum MotionReason {
SafetyEmergencyStop,
ManualEscapeUnderStop,
SafetyConstrained(SafetyDecision),
NoFollowTarget,
FollowTargetStale,
SafetyAuthorizationUnavailable,
}
struct ManualCommand {
linear_x_mps: f64,
angular_z_radps: f64,
}
struct State {
active_source: Option<MotionSource>,
selected: Option<Target>,
reason: Option<MotionReason>,
}
topic manual: command ManualCommand;
topic state: state State;
}
plan {
struct PathPose {
x_m: f64,
y_m: f64,
yaw_rad: Option<f64>,
}
struct Path {
poses: Vec<PathPose>,
map_revision: Option<u64>,
}
#[derive(Copy, Eq)]
#[serde(rename_all = "snake_case")]
enum Refusal {
MissionInactive,
NoGoal,
NoMap,
NoLocalization,
Unreachable,
NonPlanarGoalUnsupported,
LocalizationInitializing,
LocalizationLost,
LocalizationRelocalizing,
UnsupportedLocalizationMode,
NoLocalizationPose,
NoLocalizationRevision,
GoalMapRevisionMismatch,
MapLocalizeRevisionMismatch,
}
struct State {
has_path: bool,
refusal: Option<Refusal>,
}
topic path: state Path;
topic state: state State;
}
follow {
struct Target {
map_revision: Option<u64>,
built_from_localize_revision: Option<u64>,
frame_id: String,
linear_x_mps: f64,
angular_z_radps: f64,
}
struct State {
active: bool,
target_index: Option<u32>,
finished: bool,
}
topic target: state Target;
topic state: state State;
}
explore {
struct Frontier {
x_m: f64,
y_m: f64,
size: u32,
score: f32,
}
struct Frontiers {
frontiers: Vec<Frontier>,
map_revision: Option<u64>,
}
struct State {
exploring: bool,
selected: Option<Frontier>,
}
topic frontiers: state Frontiers;
topic state: state State;
}
perception {
struct Detection {
class_id: String,
confidence: f32,
position_m: [f64; 3],
frame_id: String,
track_id: Option<u64>,
}
struct Detections {
detections: Vec<Detection>,
stamp_ns: Option<u64>,
}
struct State {
healthy: bool,
detector: String,
}
topic detections: state Detections;
topic state: state State;
}
video {
struct OpenRequest {
capability: String,
width_px: Option<u32>,
height_px: Option<u32>,
}
struct OpenResponse {
stream_id: String,
}
topic open: query OpenRequest => OpenResponse;
stream(stream) {
#[derive(Copy, Eq)]
#[serde(rename_all = "snake_case")]
enum StreamPhase {
Starting,
Active,
Stopped,
}
struct StreamState {
phase: StreamPhase,
frames_seen: u64,
}
topic state: state StreamState;
}
}
simulation {
struct Clock {
now_ns: u64,
running: bool,
}
#[derive(Copy, Eq)]
enum Control {
Pause,
Resume,
Reset,
}
struct RobotPose {
x_m: f64,
y_m: f64,
yaw_rad: f64,
}
struct Contact {
in_contact: bool,
detail: Option<String>,
}
topic clock: state Clock;
topic control: command Control;
topic robot_pose: state RobotPose;
topic contact: state Contact;
}
component(instance) {
motor(capability) {
enum Command {
Velocity(f32),
Torque(f32),
Stop,
}
topic command: command Command;
}
encoder(capability) {
struct Sample {
position_rad: f64,
velocity_radps: f32,
}
topic sample: state Sample;
}
accelerometer(capability) {
struct Sample {
linear_acceleration: [f32; 3],
}
topic sample: state Sample;
}
gyroscope(capability) {
struct Sample {
angular_velocity: [f32; 3],
}
topic sample: state Sample;
}
magnetometer(capability) {
struct Sample {
magnetic_field: [f32; 3],
}
topic sample: state Sample;
}
imu(capability) {
#[derive(Copy, Eq)]
#[serde(rename_all = "snake_case")]
enum SensorHealth {
Nominal,
Degraded,
Fault,
}
#[derive(Copy)]
struct Bias {
angular_velocity_radps: [f32; 3],
linear_acceleration_mps2: [f32; 3],
}
struct Sample {
orientation: Option<[f32; 4]>,
angular_velocity_radps: [f32; 3],
linear_acceleration_mps2: [f32; 3],
covariance: Option<[f32; 9]>,
noise_density: Option<[f32; 3]>,
sensor_frame_id: Option<String>,
measured_at_ns: Option<u64>,
health: SensorHealth,
bias: Option<Bias>,
}
topic sample: state Sample;
}
range(capability) {
#[derive(Copy, Eq)]
#[serde(rename_all = "snake_case")]
enum SensorHealth {
Nominal,
Degraded,
Fault,
}
#[derive(Copy)]
struct Limits {
min_m: f32,
max_m: f32,
}
#[derive(Copy)]
struct SampleQuality {
valid: bool,
confidence: Option<f32>,
}
struct Sample {
distance_m: f32,
limits: Option<Limits>,
measured_at_ns: Option<u64>,
quality: Option<SampleQuality>,
health: SensorHealth,
}
topic sample: state Sample;
}
gnss(capability) {
struct Sample {
latitude: f64,
longitude: f64,
altitude: f64,
position_covariance: [f64; 9],
}
topic sample: state Sample;
}
camera(capability) {
#[derive(Copy, Eq)]
#[serde(rename_all = "snake_case")]
enum Encoding {
Jpeg,
Png,
L8,
Rgb8,
Rgba8,
}
#[derive(Copy)]
struct Intrinsics {
fx: f32,
fy: f32,
cx: f32,
cy: f32,
}
struct Distortion {
model: String,
coefficients: Vec<f32>,
}
#[derive(Copy)]
struct ExposureTiming {
exposure_start_ns: Option<u64>,
exposure_duration_ns: Option<u64>,
}
struct CalibrationIdentity {
id: String,
version: String,
}
struct Frame {
width: u32,
height: u32,
encoding: Encoding,
intrinsics: Option<Intrinsics>,
distortion: Option<Distortion>,
exposure: Option<ExposureTiming>,
measured_at_ns: Option<u64>,
calibration: Option<CalibrationIdentity>,
#[serde(with = "serde_bytes")]
data: Vec<u8>,
}
topic frame: state Frame;
}
depth(capability) {
#[derive(Copy, Eq)]
#[serde(rename_all = "snake_case")]
enum Encoding {
U16Millimeters,
}
#[derive(Copy, Eq)]
#[serde(rename_all = "snake_case")]
enum InvalidSamplePolicy {
ZeroIsInvalid,
NonFiniteIsInvalid,
}
#[derive(Copy)]
struct Intrinsics {
fx: f32,
fy: f32,
cx: f32,
cy: f32,
}
struct Distortion {
model: String,
coefficients: Vec<f32>,
}
#[derive(Copy)]
struct ExposureTiming {
exposure_start_ns: Option<u64>,
exposure_duration_ns: Option<u64>,
}
struct CalibrationIdentity {
id: String,
version: String,
}
struct Frame {
samples_mm: Vec<u16>,
encoding: Encoding,
invalid_sample_policy: InvalidSamplePolicy,
width: Option<u32>,
height: Option<u32>,
intrinsics: Option<Intrinsics>,
distortion: Option<Distortion>,
exposure: Option<ExposureTiming>,
measured_at_ns: Option<u64>,
calibration: Option<CalibrationIdentity>,
}
topic frame: state Frame;
}
lidar(capability) {
#[derive(Copy, Eq)]
#[serde(rename_all = "snake_case")]
enum SensorHealth {
Nominal,
Degraded,
Fault,
}
#[derive(Copy)]
struct ScanGeometry {
angle_min_rad: f32,
angle_increment_rad: f32,
}
#[derive(Copy)]
struct RangeLimits {
min_m: f32,
max_m: f32,
}
#[derive(Copy)]
struct ScanQuality {
valid_points: u32,
}
struct Ranges {
ranges: Vec<f32>,
geometry: Option<ScanGeometry>,
limits: Option<RangeLimits>,
measured_at_ns: Option<u64>,
quality: Option<ScanQuality>,
health: SensorHealth,
}
struct Points {
points: Vec<[f32; 3]>,
limits: Option<RangeLimits>,
measured_at_ns: Option<u64>,
quality: Option<ScanQuality>,
health: SensorHealth,
}
#[serde(tag = "kind", rename_all = "snake_case")]
enum Scan {
Ranges(Ranges),
Points(Points),
}
topic scan: state Scan;
}
mmwave(capability) {
#[derive(Copy)]
struct Detection {
position: [f32; 3],
velocity: [f32; 3],
snr: f32,
}
struct Scan {
detections: Vec<Detection>,
}
topic scan: state Scan;
}
microphone(capability) {
struct Frame {
data: Vec<u8>,
}
topic frame: state Frame;
}
led(capability) {
#[derive(Copy, Eq)]
enum Command {
On,
Off,
}
topic command: command Command;
}
emergency_stop(capability) {
#[derive(Eq)]
struct State {
engaged: bool,
}
topic state: state State;
}
}
odometry {
struct State {
x_m: f64,
y_m: f64,
yaw_rad: f64,
linear_x_mps: f32,
angular_z_radps: f32,
}
topic state: state State;
}
localize {
struct LocalizationState {
x_m: f64,
y_m: f64,
yaw_rad: f64,
confidence: f32,
}
topic state: state LocalizationState;
}
presence {
enum Readiness {
NotStarted,
Initializing,
Ready,
Degraded,
Failed,
}
struct Heartbeat {
participant: String,
readiness: Readiness,
}
struct State {
readiness: Readiness,
}
topic heartbeat: command Heartbeat;
topic state: state State;
}
map {
struct Revision {
revision: u64,
resolution_m: f32,
}
struct SubmapRequest {
min_x_m: f64,
min_y_m: f64,
max_x_m: f64,
max_y_m: f64,
}
struct SubmapResponse {
width: u32,
height: u32,
resolution_m: f32,
cells: Vec<u8>,
}
topic revision: state Revision;
topic submap: query SubmapRequest => SubmapResponse;
}
asset {
struct GetRequest {
path: String,
}
enum GetResponse {
Found { bytes: Vec<u8> },
Missing,
InvalidPath,
}
topic get: query GetRequest => GetResponse;
}
}
}
#[cfg(test)]
mod tests;