# Using rover_nexus_core
A library for Cap'n Proto message serialization between robots and fleet management servers.
## Add Dependency
```toml
# Cargo.toml
[dependencies]
rover_nexus_core = { path = "../rover_nexus_core" } # or git URL
```
## Quick Start
```rust
use rover_nexus_core::core_model::*;
use rover_nexus_core::spatial_types::*; // GeoPose, LocalPose, Pose, SpatialData, …
use rover_nexus_core::capnp_messages::{
serialize_robot_uplink_msg, deserialize_robot_uplink_msg,
serialize_robot_command, deserialize_robot_command,
};
```
## Robot → Server (RobotUplinkMsg)
### Create and Send Telemetry
```rust
use rover_nexus_core::core_model::*;
use rover_nexus_core::spatial_types::*;
use rover_nexus_core::capnp_messages::serialize_robot_uplink_msg;
// Create global (GPS) motion telemetry
let motion = GlobalMotionTelemetry {
unix_time_ms: 1_700_000_000_000, // milliseconds since Unix epoch
pose: Some(GeoPose {
lon_deg: -122.4194,
lat_deg: 37.7749,
altitude_m: 10.0,
heading_deg: 45.0,
}),
velocity: Some(VelTwist {
forward_mps: 1.5,
angular_radps: 0.1,
}),
gps_fix: Some(GpsFixType::FixedRTK),
gps_source: GpsSource::Real,
};
// Wrap in RobotUplinkMsg and serialize
let msg = RobotUplinkMsg::GlobalMotionTelemetry(motion);
let bytes: Vec<u8> = serialize_robot_uplink_msg(&msg).expect("serialization failed");
// Send `bytes` over the transport (SecureLink QUIC to the server, or Zenoh to
// the on-device agent), framed with a wire_envelope MessageClass byte.
```
Robots that report odometry in a local frame (rather than GPS) send
`LocalMotionTelemetry` (with a `LocalPose` and required `VelTwist`), wrapped as
`RobotUplinkMsg::LocalMotionTelemetry`.
### Create Status Telemetry
```rust
let status = StatusTelemetry {
unix_time_ms: 1_700_000_000_000,
battery: Some(BatteryStatus {
soc_pct: 85.0, // state of charge, 0 to 100
voltage_v: Some(48.2),
current_a: Some(-2.5),
charge_ah: None,
capacity_ah: None,
design_capacity_ah: None,
temperature: Some(25.0),
power_supply_status: PowerSupplyStatus::Discharging,
power_supply_health: PowerSupplyHealth::Good,
power_supply_technology: PowerSupplyTechnology::LiIon,
}),
fuel: None,
range_remaining_m: Some(1200.0),
runtime_remaining_s: Some(3600.0),
mode: Some(RobotMode::Auto),
estop: EStop {
unix_time_ms: 1_700_000_000_000,
active: false,
ids: vec![],
},
faulted: false,
accepting_missions: true,
status: "Mowing zone A".to_string(),
};
let msg = RobotUplinkMsg::StatusTelemetry(status);
let bytes = serialize_robot_uplink_msg(&msg).unwrap();
```
### Receive RobotUplinkMsg (Server Side)
```rust
use rover_nexus_core::capnp_messages::deserialize_robot_uplink_msg;
fn handle_uplink(bytes: &[u8]) {
match deserialize_robot_uplink_msg(bytes) {
Ok(RobotUplinkMsg::GlobalMotionTelemetry(m)) => {
println!("Robot at {:?}", m.pose);
}
Ok(RobotUplinkMsg::StatusTelemetry(s)) => {
println!("Battery: {}%", s.battery.map(|b| b.soc_pct).unwrap_or(0.0));
}
Ok(RobotUplinkMsg::Fault(f)) => {
println!("FAULT: {} - {}", f.fault_id, f.description);
}
Ok(other) => println!("Received: {:?}", other),
Err(e) => eprintln!("Deserialize error: {}", e),
}
}
```
## Server → Robot (RobotCommand)
### Send Commands
```rust
use rover_nexus_core::core_model::*;
use rover_nexus_core::spatial_types::*;
use rover_nexus_core::capnp_messages::serialize_robot_command;
// Velocity command (teleop)
let cmd = RobotCommand::VelocityCmd(VelTwist {
forward_mps: 0.5,
angular_radps: 0.2,
});
let bytes = serialize_robot_command(&cmd).unwrap();
// Change mode
let cmd = RobotCommand::SetRobotMode(RobotMode::Auto);
// Navigate to a location (Pose is WGS84 or local-frame)
let cmd = RobotCommand::NavigateTo(Pose::GeoPose(GeoPose {
lon_deg: -122.418,
lat_deg: 37.775,
altitude_m: 0.0,
heading_deg: 90.0,
}));
```
### Receive Commands (Robot Side)
```rust
use rover_nexus_core::capnp_messages::deserialize_robot_command;
fn handle_command(bytes: &[u8]) {
match deserialize_robot_command(bytes) {
Ok(RobotCommand::VelocityCmd(vel)) => {
drive(vel.forward_mps, vel.angular_radps);
}
Ok(RobotCommand::SetRobotMode(mode)) => {
set_robot_mode(mode);
}
Ok(RobotCommand::NavigateTo(target)) => {
navigate_to(target);
}
Ok(cmd) => println!("Unhandled command: {:?}", cmd),
Err(e) => eprintln!("Deserialize error: {}", e),
}
}
```
### Wrapping a command with a UUID (NexusCommand)
The server wraps each `RobotCommand` in a `NexusCommand` carrying a per-command
UUID. The robot agent acks the UUID (see `RobotAck` in `internal_model`) and
forwards the inner `command` to the robot software.
```rust
use rover_nexus_core::core_model::{NexusCommand, RobotCommand, RobotMode};
use rover_nexus_core::capnp_messages::{serialize_nexus_command, deserialize_nexus_command};
use uuid::Uuid;
let nexus_cmd = NexusCommand {
id: Uuid::new_v4(),
command: RobotCommand::SetRobotMode(RobotMode::Auto),
};
let bytes = serialize_nexus_command(&nexus_cmd).unwrap();
let parsed = deserialize_nexus_command(&bytes).unwrap();
```
## JSON Serialization
All types derive `Serialize`/`Deserialize` for JSON:
```rust
use rover_nexus_core::core_model::*;
let motion = GlobalMotionTelemetry { /* ... */ };
// To JSON
let json = serde_json::to_string(&motion).unwrap();
// From JSON
let parsed: GlobalMotionTelemetry = serde_json::from_str(&json).unwrap();
```
Note: Wall-clock time fields are `i64` milliseconds since the Unix epoch and
serialize as `unixTimeMs` (camelCase). There is no nanosecond representation.
Enums and fields serialize in camelCase via `#[serde(rename_all = "camelCase")]`.
## Capabilities
For advertising robot capabilities, sensors, and settings:
```rust
use rover_nexus_core::core_model::*;
// Advertise custom services
let capabilities = Capabilities {
services: vec![
ServiceCapability {
name: "start_mowing".into(),
description: "Begin autonomous mowing routine".into(),
capability_type: CapabilityType::Trigger,
confirmation_message: Some("This will start the blades".into()),
state_key: None,
requires_role: Role::Operator,
danger_level: DangerLevel::Warning,
},
ServiceCapability {
name: "blade_toggle".into(),
description: "Toggle blade on/off".into(),
capability_type: CapabilityType::SetBool,
confirmation_message: None,
state_key: Some("blade_on".into()),
requires_role: Role::Operator,
danger_level: DangerLevel::Normal,
},
],
sensors: vec![
SensorDescriptor {
key: "blade_rpm".into(),
label: "Blade Speed".into(),
value_type: ValueType::Int,
unit: Some("RPM".into()),
range: Some(Range { min: 0.0, max: 5000.0 }),
display_hint: DisplayHint::Gauge,
},
],
settings: vec![
SettingDescriptor {
key: "max_speed".into(),
label: "Maximum Speed".into(),
value_type: ValueType::Number,
default: serde_json::json!(1.0),
range: Some(Range { min: 0.0, max: 2.0 }),
step: Some(0.1),
},
],
// Shared features/resources the robot can produce for missions & operations
resource_producers: vec![
ResourceProducerDescriptor {
resource_producer_id: "coverage".into(),
label: "Mowed Area".into(),
description: Some("Area the robot has mowed".into()),
layer_role: LayerRole::Coverage,
geometry_type: GeometryType::MultiPolygon,
},
],
};
// Send arbitrary key-value sensor telemetry
let extras = SensorTelemetry {
unix_time_ms: 1_700_000_000_000,
kv: vec![
SensorReading {
key: "blade_rpm".into(),
value: Value::Int(3200),
unix_time_ms: None,
},
SensorReading {
key: "deck_lowered".into(),
value: Value::Bool(true),
unix_time_ms: None,
},
],
};
```
## All RobotUplinkMsg Variants
```rust
pub enum RobotUplinkMsg {
MissionRunStatus(MissionRunStatus),
GlobalMotionTelemetry(GlobalMotionTelemetry),
LocalMotionTelemetry(LocalMotionTelemetry),
StatusTelemetry(StatusTelemetry),
SensorTelemetry(SensorTelemetry),
Capabilities(Capabilities),
AllowedCommands(AllowedCommands),
Fault(Fault),
Feature(ReportedFeatureUpdate),
Object(ObjectOp),
SystemHealth(SystemHealth),
UsageTelemetry(UsageTelemetry),
ConsumableStatus(ConsumableStatus),
CurrentSettings(Vec<SettingUpdate>),
Message(Message),
AgentInfo(AgentInfo),
SpatialDirectiveStatus(SpatialDirectiveStatus),
}
```
## All RobotCommand Variants
```rust
pub enum RobotCommand {
SetRobotMode(RobotMode),
Pause,
Resume,
AssignMission(MissionCommand),
ControlMissionRun(ControlMissionRun),
InvokeService(ServiceCall),
UpdateSettings(Vec<SettingUpdate>),
UpdateFeature(FeatureOp),
SpatialDirective(SpatialDirectiveOp),
Object(ObjectOp),
NavigateTo(Pose),
VelocityCmd(VelTwist),
TeleopJoy(TeleopJoy),
AgentTextRequest(AgentTextRequest),
SayText(SayTextRequest),
MessageConfirmation(MessageConfirmation),
}
```