phoxal_api/lib.rs
1//! The single API layer (D60/D61/D1).
2//!
3//! This crate is the versioned API contract tree. It depends only on the
4//! [`phoxal-bus`](phoxal_bus) ABI floor (the contract primitive traits and the
5//! typed-topic builders) and the [`phoxal-macros`](phoxal_macros) proc-macros; it
6//! does **not** depend on the `phoxal` engine. Normal participants import the
7//! train-selected facade with `use phoxal::api`; concrete modules such as
8//! `phoxal_api::v0_1` remain available to compatibility adapters.
9//!
10//! # Concrete API revisions
11//!
12//! An API revision is a conventional `vM_N` module generated by
13//! [`phoxal_api_tree!`]. Each version module carries:
14//!
15//! - a zero-variant marker `enum Api {}` implementing [`ApiVersion`], whose
16//! [`ApiVersion::ID`] is the concrete wire identity (for example `"v0.1"`);
17//! - the version-local wire bodies, one `pub mod` per contract node holding plain
18//! serde structs/enums and their [`ContractBody`] impls;
19//! - an api-local `topic` builder rooted at `topic::new()`.
20//!
21//! From 1.0, published concrete revisions are immutable. Before 1.0 the
22//! framework may make an approved in-place breaking edit without adding a shim
23//! or a new revision; every participant on a robot must move as one train
24//! because mixed pre-1.0 framework trains are unsupported. A child may extend
25//! one earlier revision; the generator materializes the complete child tree
26//! with its own identity. Exactly one `latest` alias is selected for each
27//! framework train.
28//!
29//! [`Api`]: v0_2::Api
30//!
31//! # Train-selected revision and per-contract identity
32//!
33//! A participant declares its bus surface with a companion
34//! `#[derive(phoxal::Api)]` handle struct.
35//! Official handle fields name types through the complete train-selected facade.
36//! The derive records each field's resolved [`ContractBody::VERSION`] and
37//! [`ContractBody::CONTRACT`] in the participant's embedded metadata and does
38//! not declare a participant-wide API version.
39//! Across the graph, compatibility is **name identity** (D1) - two participants
40//! interoperate on a contract iff they use the exact same version-qualified name
41//! (`v0.1::drive::Target`), which is real on the wire because the revision
42//! is folded into the key ([`ContractBody::TOPIC`]). There is no `schema_id`:
43//! from 1.0 onward a stable contract type is immutable, so the name alone is
44//! the whole identity. Before 1.0, that identity is train-scoped and an
45//! in-place edit requires the whole robot graph to upgrade together.
46//!
47//! # Plain serde wire bodies, provenance in metadata
48//!
49//! A wire body is just its serde encoding - there is no `{"v":…}` envelope or any
50//! other version tag inside the payload (D62). Identity lives entirely in the
51//! Zenoh key (the version-qualified [`ContractBody::TOPIC`]); the bus metadata
52//! alongside the encoded body carries only provenance (source + logical time) and
53//! the codec that produced the bytes - never schema/family/version. Keeping
54//! identity out of both the payload and the metadata means the body bytes for an
55//! unchanged contract are identical across codecs, and a receiver's per-key
56//! subscription is the whole fast-reject.
57//!
58//! # Topic
59//!
60//! [`ContractBody::TOPIC`] is derived from the contract node's path in the tree,
61//! never written by hand: the version, then the `/`-joined node path plus the
62//! topic leaf, with each dynamic node contributing a `{var}` placeholder, e.g.
63//! `v0.1/component/{instance}/motor/{capability}/command`. A fully static path
64//! has a literal key (`v0.1/drive/state`). Folding the revision into the key
65//! (D1) is what makes two differently-versioned contracts physically distinct
66//! Zenoh keys - they cannot collide, so there is no `SCHEMA_ID`/`FAMILY` needed to
67//! disambiguate them.
68//!
69//! # The api-local topic builder
70//!
71//! Each version module exposes a `topic` builder that mirrors the node tree:
72//! `api::topic::new()` returns a root, one method per top-level node walks down the
73//! tree, a dynamic node's method takes its variable as `impl Display`, and a leaf
74//! method binds the topic's side-branded kind to its version-local body. For
75//! example `api::topic::new().drive().state()` yields a
76//! `Topic<Subscribe<drive::State>>` (the CLIENT observes the owner's `state`) over
77//! the version-qualified key `v0.1/drive/state`, and
78//! `api::topic::new().component("base").motor("left").command()` fills the dynamic
79//! segments to produce `v0.1/component/base/motor/left/command`. Because the
80//! builder is generated from the same tree as `TOPIC`, the built key and the
81//! documented key stay in lockstep.
82//!
83//! ## Owner side: `topic::internal`
84//!
85//! The PUBLIC `topic::new()...` chain above is the **client** side. The matching
86//! **owner** side lives at `api::topic::internal::new(cap)...` (L1 + L2, plan #00):
87//! the same node tree and keys, but the leaf brands flip so the owner gets the side
88//! it must take - `api::topic::internal::new(cap).drive().state()` is
89//! `Topic<Publish<drive::State>>` (the owner publishes its telemetry), and
90//! `api::topic::internal::new(cap).drive().target()` is `Topic<Subscribe<drive::Target>>`
91//! (the owner reads its command input). A query owner reaches its `ServeQuery`
92//! brand the same way. The `internal` chain is the deliberate, greppable owner
93//! opt-in; a participant acquires the topics of its OWN node through it and everything
94//! it consumes through the public chain.
95//!
96//! The `internal::new` entry requires the runner-minted owner capability
97//! ([`OwnerCap`](phoxal_bus::OwnerCap), Layer 2): a participant obtains it from
98//! `phoxal::SetupContext::owner_capability()` and passes it in. On the documented
99//! surface, owning a topic therefore cannot happen by accident - only with a
100//! capability the runner mints.
101
102use phoxal_macros::phoxal_api_tree;
103
104/// The contract primitive traits, re-exported from the `phoxal-bus` crate (the
105/// ABI floor) so they stay addressable at `phoxal_api::ApiVersion` /
106/// `phoxal_api::ContractBody`.
107///
108/// - [`ApiVersion`] is the marker trait identifying one API version (D60),
109/// implemented only by the zero-variant `enum Api {}` that [`phoxal_api_tree!`]
110/// generates inside each revision module; its `ID` is the concrete dotted
111/// wire identity (for example `"v0.1"`).
112/// - [`ContractBody`] is a version-local wire body (D61): a plain serde type
113/// bound to exactly one [`ApiVersion`] and one contract topic. Every body
114/// declared inside a [`phoxal_api_tree!`] node gets a generated impl; handles,
115/// `SetupContext` builders, and the `Service`/`Driver` derive assertions key
116/// off its `Api`/`TOPIC`. `TOPIC` is version-qualified (D1) and *is* the
117/// compatibility key - there is no `SCHEMA_ID`/`FAMILY`; its serde encoding
118/// *is* the wire payload, with no version envelope (D62).
119pub use phoxal_bus::{ApiVersion, ContractBody};
120
121phoxal_api_tree! {
122 version v0_1 {
123 drive {
124 /// Why actuation authority is in its current state.
125 enum StopReason {
126 NoTarget,
127 TargetStale,
128 TargetFromFuture,
129 TargetNotFinite,
130 ActuatorCommandNotFinite,
131 Inactive,
132 EmergencyStop,
133 Fault,
134 }
135
136 /// Whether the drive is actively commanding the actuators.
137 enum ActuatorAuthority {
138 Active,
139 Stopped,
140 }
141
142 /// A requested or limited planar velocity.
143 struct Target {
144 linear_x_mps: f32,
145 angular_z_radps: f32,
146 curvature_limit_radpm: Option<f32>,
147 }
148
149 /// The drive participant's published control state.
150 struct State {
151 target: Target,
152 limited_target: Target,
153 actuator_authority: ActuatorAuthority,
154 stop_reason: Option<StopReason>,
155 target_age_ns: Option<u64>,
156 }
157
158 topic target: command Target;
159 topic state: state State;
160 }
161
162 joint(joint) {
163 /// Per-joint position/velocity (and optional effort) on a dynamic
164 /// per-joint key.
165 struct JointState {
166 position_rad: f64,
167 velocity_radps: f64,
168 effort_nm: Option<f64>,
169 }
170
171 topic state: state JointState;
172 }
173
174 frame {
175 /// A parent → child rigid transform (translation + xyzw quaternion).
176 struct FrameTransform {
177 parent_frame_id: String,
178 child_frame_id: String,
179 translation_m: [f64; 3],
180 rotation_quat_xyzw: [f64; 4],
181 stamp_ns: Option<u64>,
182 }
183
184 /// Transforms that do not change over time.
185 struct StaticTransforms {
186 transforms: Vec<FrameTransform>,
187 }
188
189 /// The current transform tree.
190 struct Tree {
191 transforms: Vec<FrameTransform>,
192 }
193
194 /// Ask for the transform between two frames, optionally at a time.
195 struct LookupRequest {
196 target_frame_id: String,
197 source_frame_id: String,
198 at_ns: Option<u64>,
199 }
200
201 /// The resolved transform, or `None` if it is not available.
202 struct LookupResponse {
203 transform: Option<FrameTransform>,
204 }
205
206 topic tree: state Tree;
207 topic static_transforms: state StaticTransforms;
208 topic lookup: query LookupRequest => LookupResponse;
209 }
210
211 power {
212 /// A platform power command.
213 #[derive(Copy, Eq)]
214 enum Command {
215 Reboot,
216 Shutdown,
217 }
218
219 /// Where the power participant is in handling a command.
220 #[derive(Copy, Eq)]
221 enum Status {
222 Idle,
223 Rebooting,
224 ShuttingDown,
225 Failed,
226 }
227
228 /// Why a power command was rejected outright.
229 #[derive(Copy, Eq)]
230 #[serde(rename_all = "snake_case")]
231 enum RejectedReason {
232 HostIntegrationUnavailable,
233 CommandRejected,
234 }
235
236 /// Why an accepted power command later failed.
237 #[derive(Copy, Eq)]
238 #[serde(rename_all = "snake_case")]
239 enum FailedReason {
240 HostCommandFailed,
241 }
242
243 /// The power participant's published state.
244 struct State {
245 status: Status,
246 detail: Option<String>,
247 }
248
249 topic command: command Command;
250 topic state: state State;
251 }
252
253 motion {
254 struct Target {
255 linear_x_mps: f32,
256 angular_z_radps: f32,
257 curvature_limit_radpm: Option<f32>,
258 }
259
260 #[derive(Copy, Eq)]
261 #[serde(rename_all = "snake_case")]
262 enum Source {
263 Manual,
264 Navigation,
265 EmergencyStop,
266 }
267
268 #[derive(Copy, Eq)]
269 #[serde(rename_all = "snake_case")]
270 enum ZeroReason {
271 NoCandidate,
272 ManualCandidateStale,
273 NavigationCandidateStale,
274 ManualCandidateFromFuture,
275 NavigationCandidateFromFuture,
276 ManualCandidateNotFinite,
277 NavigationCandidateNotFinite,
278 EmergencyStopEngaged,
279 SafetyConstraintsUnavailable,
280 SafetyProtectiveStop,
281 }
282
283 #[derive(Copy, Eq)]
284 #[serde(rename_all = "snake_case")]
285 enum SafetyRuntime {
286 Absent,
287 Present,
288 }
289
290 struct ManualCommand {
291 linear_x_mps: f64,
292 angular_z_radps: f64,
293 }
294
295 #[derive(Eq)]
296 struct EmergencyStopRequest {
297 engaged: bool,
298 }
299
300 struct State {
301 manual_candidate_age_ns: Option<u64>,
302 autonomous_candidate_age_ns: Option<u64>,
303 safety_constraints_age_ns: Option<u64>,
304 selected_source: Option<Source>,
305 final_target: Target,
306 zero_reason: Option<ZeroReason>,
307 safety_runtime: SafetyRuntime,
308 software_estop_engaged: bool,
309 component_estop_blocked: bool,
310 active_safety_constraints: Vec<super::safety::Constraint>,
311 }
312
313 topic manual: command ManualCommand;
314 topic estop: command EmergencyStopRequest;
315 topic state: state State;
316 }
317
318 safety {
319 /// Why safety is stopping or limiting body motion.
320 #[derive(Copy, Eq)]
321 #[serde(rename_all = "snake_case")]
322 enum ConstraintReason {
323 WorldUnavailable,
324 MapUnavailable,
325 DrivableSpaceUnavailable,
326 LocalizationUnavailable,
327 LocalizationUncertain,
328 ObstacleProximity,
329 RangeSensorFault,
330 DriveFault,
331 BatteryLow,
332 BatteryCritical,
333 SpeedZone,
334 OperatorPolicy,
335 }
336
337 /// Typed origin of one constraint, suitable for operator diagnosis.
338 #[derive(Copy, Eq)]
339 #[serde(rename_all = "snake_case")]
340 enum ConstraintSourceKind {
341 WorldModel,
342 Map,
343 Localization,
344 Range,
345 Drive,
346 Battery,
347 Operator,
348 }
349
350 struct ConstraintSource {
351 kind: ConstraintSourceKind,
352 participant_id: String,
353 component_id: Option<String>,
354 capability_id: Option<String>,
355 }
356
357 struct Constraint {
358 reason: ConstraintReason,
359 source: ConstraintSource,
360 stop: bool,
361 max_linear_speed_mps: Option<f32>,
362 max_angular_speed_radps: Option<f32>,
363 observed_value: Option<f32>,
364 valid_from_ns: u64,
365 expires_at_ns: u64,
366 }
367
368 /// The sole safety-to-motion control product. Motion accepts it only
369 /// in the same epoch and before `expires_at_ns`.
370 struct MotionConstraints {
371 sequence: u64,
372 stop: bool,
373 max_linear_speed_mps: Option<f32>,
374 max_angular_speed_radps: Option<f32>,
375 constraints: Vec<Constraint>,
376 expires_at_ns: u64,
377 }
378
379 /// Operator-facing state mirrors the exact product consumed by motion.
380 struct State {
381 clear: bool,
382 motion: MotionConstraints,
383 }
384
385 topic constraints: state MotionConstraints;
386 topic state: state State;
387 }
388
389 navigation {
390 #[derive(Eq)]
391 struct RequestId {
392 value: String,
393 }
394
395 struct Pose {
396 x_m: f64,
397 y_m: f64,
398 yaw_rad: Option<f64>,
399 }
400
401 struct Path {
402 poses: Vec<Pose>,
403 map_revision: Option<u64>,
404 }
405
406 enum RequestKind {
407 GotoPose(Pose),
408 FollowPath(Path),
409 Cancel(RequestId),
410 }
411
412 struct Request {
413 request_id: RequestId,
414 kind: RequestKind,
415 }
416
417 enum State {
418 Idle,
419 Accepted(RequestId),
420 Running(RequestId),
421 }
422
423 #[derive(Copy, Eq)]
424 #[serde(rename_all = "snake_case")]
425 enum FailureReason {
426 LocalizationUnavailable,
427 MapUnavailable,
428 MapChanged,
429 NoPath,
430 Blocked,
431 Internal,
432 }
433
434 #[derive(Copy, Eq)]
435 #[serde(rename_all = "snake_case")]
436 enum RefusalReason {
437 Busy,
438 InvalidRequest,
439 Unsupported,
440 }
441
442 enum Outcome {
443 Succeeded,
444 Failed(FailureReason),
445 Refused(RefusalReason),
446 Cancelled,
447 TimedOut,
448 }
449
450 struct Progress {
451 request_id: RequestId,
452 distance_remaining_m: f64,
453 path_index: u32,
454 }
455
456 struct Result {
457 request_id: RequestId,
458 outcome: Outcome,
459 }
460
461 struct Candidate {
462 request_id: RequestId,
463 linear_x_mps: f32,
464 angular_z_radps: f32,
465 }
466
467 struct FrontierRequest {
468 map_revision: Option<u64>,
469 }
470
471 struct Frontier {
472 x_m: f64,
473 y_m: f64,
474 score: f32,
475 size: u32,
476 }
477
478 struct FrontierResponse {
479 frontier: Option<Frontier>,
480 map_revision: Option<u64>,
481 }
482
483 topic request: command Request;
484 topic state: state State;
485 topic progress: state Progress;
486 topic result: state Result;
487 topic candidate: state Candidate;
488 topic next_frontier: query FrontierRequest => FrontierResponse;
489 }
490
491 behavior {
492 #[derive(Eq)]
493 struct RequestId {
494 value: String,
495 }
496
497 #[derive(Copy, Eq)]
498 #[serde(rename_all = "snake_case")]
499 enum ConflictPolicy {
500 Reject,
501 Queue,
502 Interrupt,
503 }
504
505 enum Value {
506 Bool(bool),
507 Integer(i64),
508 Number(f64),
509 String(String),
510 Pose(super::navigation::Pose),
511 }
512
513 struct Request {
514 request_id: RequestId,
515 behavior_id: String,
516 args: ::std::collections::BTreeMap<String, Value>,
517 priority: u8,
518 conflict_policy: ConflictPolicy,
519 }
520
521 enum Command {
522 Pause,
523 Resume,
524 Cancel,
525 }
526
527 #[derive(Copy, Eq)]
528 #[serde(rename_all = "snake_case")]
529 enum ExecutionStatus {
530 Idle,
531 Running,
532 Paused,
533 Succeeded,
534 Failed,
535 Cancelled,
536 Abandoned,
537 }
538
539 #[derive(Copy, Eq)]
540 #[serde(rename_all = "snake_case")]
541 enum NodeStatus {
542 Idle,
543 Running,
544 Succeeded,
545 Failed,
546 Skipped,
547 Waiting,
548 Cancelling,
549 }
550
551 #[derive(Copy, Eq)]
552 #[serde(rename_all = "snake_case")]
553 enum FailureReason {
554 MissingCapability,
555 ActionRefused,
556 ActionFailed,
557 ActionTimedOut,
558 ActionCancelled,
559 ConditionFailed,
560 SafetyStopped,
561 EmergencyStopped,
562 ResourceConflict,
563 InvalidArgument,
564 InvalidBlackboardValue,
565 SubtreeFailed,
566 ExecutionAbandoned,
567 InternalError,
568 }
569
570 struct Failure {
571 reason: FailureReason,
572 detail: Option<String>,
573 node_path: Option<String>,
574 action_id: Option<String>,
575 }
576
577 struct DefinitionRef {
578 id: String,
579 version: String,
580 content_hash: String,
581 }
582
583 struct State {
584 execution_id: Option<String>,
585 root_behavior_id: Option<String>,
586 active_request_id: Option<RequestId>,
587 active_behavior_id: Option<String>,
588 status: ExecutionStatus,
589 active_node_path: Option<String>,
590 failure: Option<Failure>,
591 }
592
593 struct Snapshot {
594 execution_id: Option<String>,
595 root: Option<DefinitionRef>,
596 definition_stack: Vec<DefinitionRef>,
597 active_request_id: Option<RequestId>,
598 active_behavior_id: Option<String>,
599 status: ExecutionStatus,
600 node_statuses: ::std::collections::BTreeMap<String, NodeStatus>,
601 active_node_path: Option<String>,
602 blackboard: ::std::collections::BTreeMap<String, Value>,
603 args: ::std::collections::BTreeMap<String, Value>,
604 started_at_ns: Option<u64>,
605 updated_at_ns: u64,
606 failure: Option<Failure>,
607 }
608
609 enum EventKind {
610 ExecutionStarted,
611 ExecutionPaused,
612 ExecutionResumed,
613 ExecutionCompleted,
614 ExecutionCancelled,
615 ExecutionAbandoned,
616 NodeTransition(NodeStatus),
617 RequestAccepted,
618 RequestCompleted(ExecutionStatus),
619 RequestRejected(FailureReason),
620 }
621
622 struct Event {
623 sequence: u64,
624 execution_id: Option<String>,
625 request_id: Option<RequestId>,
626 behavior_id: Option<String>,
627 content_hash: Option<String>,
628 node_path: Option<String>,
629 kind: EventKind,
630 failure: Option<Failure>,
631 participant_id: String,
632 logical_time_ns: u64,
633 }
634
635 topic command: command Command;
636 topic request: command Request;
637 topic state: state State;
638 topic snapshot: state Snapshot;
639 topic event: state Event;
640 }
641
642 logs(participant_id) {
643 /// Wall-clock timestamp carried by a structured bus log event.
644 struct Timestamp {
645 unix_seconds: i64,
646 nanos: u32,
647 }
648
649 /// The severity level of a structured bus log event.
650 #[derive(Copy, Eq)]
651 #[serde(rename_all = "snake_case")]
652 enum Level {
653 Error,
654 Warn,
655 Info,
656 Debug,
657 Trace,
658 }
659
660 /// A scalar tracing field value captured from a log event.
661 #[serde(untagged)]
662 enum LogValue {
663 Bool(bool),
664 I64(i64),
665 U64(u64),
666 F64(f64),
667 String(String),
668 }
669
670 /// One structured runner log event published out-of-band.
671 struct Event {
672 seq: u64,
673 time: Timestamp,
674 level: Level,
675 target: String,
676 message: String,
677 fields: ::std::collections::BTreeMap<String, LogValue>,
678 /// Complete records lost before publication because a bounded
679 /// queue or publish attempt was saturated.
680 dropped: u32,
681 /// Values or fields truncated inside this published record to
682 /// keep its wire representation bounded.
683 #[serde(default)]
684 truncated: u32,
685 }
686
687 topic self: state Event;
688 }
689
690 tool {
691 /// Opaque identity for one retention-tool process together with a
692 /// position in its completed follow stream. A snapshot cursor
693 /// covers the retained completed items; a bus snapshot's optional
694 /// `current` window deliberately has the next sequence. Consumers
695 /// compare `generation` for equality only and must never parse or
696 /// order it.
697 #[derive(Eq)]
698 struct Cursor {
699 generation: String,
700 sequence: u64,
701 }
702
703 /// Which side of one participant-local bus buffer a runtime row
704 /// measures. The version-qualified `topic` field remains the wire
705 /// identity; direction is never inferred from its spelling.
706 #[derive(Copy, Eq, Ord, PartialOrd)]
707 #[serde(rename_all = "snake_case")]
708 enum RuntimeDirection {
709 Publish,
710 Subscribe,
711 /// Used only by the bounded overflow row, which may combine
712 /// omitted rows from both directions.
713 Mixed,
714 }
715
716 /// The concrete bounded buffer whose pressure a runtime row
717 /// measures.
718 #[derive(Copy, Eq, Ord, PartialOrd)]
719 #[serde(rename_all = "snake_case")]
720 enum RuntimeBufferKind {
721 /// Per-topic view of the one shared process outbound queue.
722 /// Its sample capacity is repeated on each row and must not be
723 /// summed; the queue's separate byte pressure is not in v0.1.
724 Outbound,
725 /// Keep-last slot. Depth `1` means occupied, never backlog.
726 Latest,
727 Subscriber,
728 /// Used only by the bounded overflow row.
729 Mixed,
730 }
731
732 /// Host-monotonic scheduled-step work completed during one rollup
733 /// window. An unscheduled participant reports `None` instead.
734 struct RuntimeStep {
735 target_period_ns: u64,
736 completed: u64,
737 errors: u64,
738 mean_duration_ns: u64,
739 max_duration_ns: u64,
740 mean_lateness_ns: u64,
741 max_lateness_ns: u64,
742 missed_ticks: u64,
743 overruns: u64,
744 }
745
746 /// One exact version-qualified topic/direction/buffer row. These
747 /// are process-lifetime setup declarations: dropping an authoring
748 /// handle does not dynamically unregister a row. Empty `topic`
749 /// plus `Mixed` direction/kind identifies the explicit overflow
750 /// row; `overflowed_rows` is zero on normal rows.
751 struct RuntimeTopic {
752 topic: String,
753 direction: RuntimeDirection,
754 buffer_kind: RuntimeBufferKind,
755 count: u64,
756 /// Finite, non-negative message rate. Retention tools clamp
757 /// malformed non-finite inputs before they reach snapshots.
758 rate_hz: f32,
759 drops: u64,
760 latest_overwrites: u64,
761 bounded_evictions: u64,
762 /// Sample capacity. Outbound rows repeat the shared process
763 /// queue capacity and are non-additive; byte pressure is not
764 /// represented. Latest capacity/depth describe slot occupancy.
765 capacity: u64,
766 current_depth: u64,
767 high_water_depth: u64,
768 decode_errors: u64,
769 /// Samples discarded because they belonged to a retired
770 /// simulation epoch, or because an unmatched pending epoch
771 /// was purged when another epoch became authoritative.
772 epoch_filtered: u64,
773 overflowed_rows: u32,
774 }
775
776 log {
777 /// Requests tool-log's complete current bounded snapshot. The
778 /// first protocol version intentionally has no pagination or
779 /// filtering surface.
780 struct SnapshotRequest {}
781
782 /// Wall-clock timestamp copied from one participant-originated
783 /// structured `v0.1::logs` event.
784 struct Timestamp {
785 unix_seconds: i64,
786 nanos: u32,
787 }
788
789 #[derive(Copy, Eq)]
790 #[serde(rename_all = "snake_case")]
791 enum Level {
792 Error,
793 Warn,
794 Info,
795 Debug,
796 Trace,
797 }
798
799 #[serde(untagged)]
800 enum LogValue {
801 Bool(bool),
802 I64(i64),
803 U64(u64),
804 F64(f64),
805 String(String),
806 }
807
808 /// One retained participant log. `sequence` is assigned by
809 /// tool-log at ingest and is independent of the producer's
810 /// `source_sequence`.
811 struct Record {
812 sequence: u64,
813 participant_id: String,
814 source_sequence: u64,
815 time: Timestamp,
816 level: Level,
817 target: String,
818 message: String,
819 fields: ::std::collections::BTreeMap<String, LogValue>,
820 dropped: u32,
821 truncated: u32,
822 }
823
824 /// The complete bounded tool-log state at `cursor`.
825 struct Snapshot {
826 cursor: crate::v0_1::tool::Cursor,
827 /// Cumulative structured log samples evicted from
828 /// tool-log's bounded ingest subscriber in this process.
829 /// An increase is observable, unrecoverable source loss;
830 /// it is distinct from producer-side `Record::dropped`.
831 ingest_dropped: u64,
832 records: Vec<Record>,
833 }
834
835 /// One live record following the snapshot query. A consumer
836 /// must re-query when the generation changes or the sequence is
837 /// not exactly one after its installed cursor.
838 struct Follow {
839 cursor: crate::v0_1::tool::Cursor,
840 /// Current cumulative tool-log ingest loss counter.
841 ingest_dropped: u64,
842 record: Record,
843 }
844
845 topic snapshot: query SnapshotRequest => Snapshot;
846 topic follow: state Follow;
847 }
848
849 bus {
850 /// Requests tool-bus's complete current bounded snapshot. The
851 /// first protocol version intentionally has no pagination or
852 /// filtering surface.
853 struct SnapshotRequest {}
854
855 /// One topic-producer pair measured during a one-second window.
856 /// Empty topic and producer identify the bounded overflow row.
857 struct TopicMetric {
858 topic: String,
859 from_participant: String,
860 ingress_rate_hz: f32,
861 count: u64,
862 }
863
864 /// One retained bus-rate window. `window_ns` is a duration, not
865 /// a production timestamp.
866 struct Window {
867 sequence: u64,
868 topics: Vec<TopicMetric>,
869 throughput_msg_s: f32,
870 window_ns: u64,
871 }
872
873 /// The current partial counters plus the bounded recent
874 /// completed-window history. The partial `current` window has
875 /// the next sequence that its eventual follow item will use;
876 /// the snapshot cursor covers only `windows`.
877 struct Snapshot {
878 cursor: crate::v0_1::tool::Cursor,
879 current: Option<Window>,
880 windows: Vec<Window>,
881 }
882
883 /// One newly completed bus-rate window.
884 struct Follow {
885 cursor: crate::v0_1::tool::Cursor,
886 window: Window,
887 }
888
889 topic snapshot: query SnapshotRequest => Snapshot;
890 topic follow: state Follow;
891 }
892
893 device {
894 /// One mounted filesystem capacity observation. The contract
895 /// intentionally excludes OS-specific device names and I/O
896 /// counters; an unavailable storage inventory is represented
897 /// by `Sample::disks == None`, never fabricated zero rows.
898 struct Disk {
899 mount_point: String,
900 file_system: String,
901 used_bytes: u64,
902 total_bytes: u64,
903 }
904
905 /// Portable whole-device observations produced by one
906 /// runner-owned tool-device. Every optional field is `None`
907 /// when the current platform cannot provide a truthful value.
908 /// These totals must never be attributed to a runtime.
909 struct Sample {
910 /// Logical device identity within the robot root. The
911 /// current execution environment is always `main`.
912 device_id: String,
913 cpu_pct: Option<f32>,
914 ram_used_bytes: Option<u64>,
915 ram_total_bytes: Option<u64>,
916 swap_used_bytes: Option<u64>,
917 swap_total_bytes: Option<u64>,
918 load_1m: Option<f32>,
919 load_5m: Option<f32>,
920 load_15m: Option<f32>,
921 uptime_s: Option<u64>,
922 disks: Option<Vec<Disk>>,
923 /// Host-monotonic duration between sampler refreshes.
924 window_ns: u64,
925 }
926
927 /// Requests tool-telemetry's bounded device history.
928 struct SnapshotRequest {
929 /// Optional exact logical device filter.
930 device_id: Option<String>,
931 /// Maximum newest records to return. Zero selects the
932 /// tool's bounded default.
933 limit: u32,
934 /// Exclusive global ingest-sequence upper bound.
935 before_sequence: Option<u64>,
936 }
937
938 struct Record {
939 sequence: u64,
940 sample: Sample,
941 /// Text fields or rows truncated by tool-telemetry.
942 truncated: u32,
943 }
944
945 struct Snapshot {
946 cursor: crate::v0_1::tool::Cursor,
947 records: Vec<Record>,
948 /// Records evicted by the absolute capacity bound before
949 /// their five-minute age horizon elapsed.
950 capacity_evictions: u64,
951 next_before_sequence: Option<u64>,
952 }
953
954 struct Follow {
955 cursor: crate::v0_1::tool::Cursor,
956 record: Record,
957 }
958
959 topic sample: state Sample;
960 topic snapshot: query SnapshotRequest => Snapshot;
961 topic follow: state Follow;
962 }
963
964 runtime {
965 /// Runner-originated portable performance rollup. The runner
966 /// publishes at most one per host-monotonic grid interval;
967 /// envelope provenance identifies the participant. Interval
968 /// counters are best-effort sequential atomic samples, not a
969 /// transactional stop-the-world boundary, so concurrent queue
970 /// activity may land on either neighboring rollup.
971 struct Rollup {
972 window_ns: u64,
973 step: Option<crate::v0_1::tool::RuntimeStep>,
974 topics: Vec<crate::v0_1::tool::RuntimeTopic>,
975 overflow: Option<crate::v0_1::tool::RuntimeTopic>,
976 }
977
978 /// Requests tool-telemetry's current bounded five-minute
979 /// runtime history.
980 struct SnapshotRequest {
981 /// Optional exact participant filter. `None` selects the
982 /// complete per-robot history.
983 participant_id: Option<String>,
984 /// Maximum newest records to return. Zero selects the
985 /// tool's bounded default.
986 limit: u32,
987 /// Exclusive global ingest-sequence upper bound for
988 /// backward pagination. `None` starts at the newest record.
989 before_sequence: Option<u64>,
990 }
991
992 /// One retained rollup. `sequence` is assigned by
993 /// tool-telemetry at ingest, independent of producer metadata.
994 /// Duplicate normal topic keys are deterministically
995 /// re-aggregated before row bounds are applied.
996 struct Record {
997 sequence: u64,
998 participant_id: String,
999 /// Text values truncated by tool-telemetry's ingest bound.
1000 /// Oversized/excess topic identities are not truncated;
1001 /// they are aggregated into the explicit overflow row.
1002 truncated: u32,
1003 window_ns: u64,
1004 step: Option<crate::v0_1::tool::RuntimeStep>,
1005 topics: Vec<crate::v0_1::tool::RuntimeTopic>,
1006 overflow: Option<crate::v0_1::tool::RuntimeTopic>,
1007 }
1008
1009 struct Snapshot {
1010 cursor: crate::v0_1::tool::Cursor,
1011 records: Vec<Record>,
1012 /// Records evicted by the absolute memory cap before their
1013 /// five-minute age horizon elapsed.
1014 capacity_evictions: u64,
1015 /// Pass this as the next request's `before_sequence` to
1016 /// continue backward. `None` means the retained matching
1017 /// history is complete.
1018 next_before_sequence: Option<u64>,
1019 }
1020
1021 struct Follow {
1022 cursor: crate::v0_1::tool::Cursor,
1023 record: Record,
1024 }
1025
1026 topic rollup: state Rollup;
1027 topic snapshot: query SnapshotRequest => Snapshot;
1028 topic follow: state Follow;
1029 }
1030 }
1031
1032 bus {
1033 uplink {
1034 /// Reserved observable state for a future authenticated router
1035 /// uplink. Phase 1 has no publisher for this contract.
1036 #[derive(Copy, Eq)]
1037 #[serde(rename_all = "snake_case")]
1038 enum UplinkPhase {
1039 Disabled,
1040 Connecting,
1041 Connected,
1042 /// Reserved for future retry telemetry.
1043 Retrying,
1044 }
1045
1046 /// Reserved router-owned state for a future optional site uplink.
1047 struct State {
1048 phase: UplinkPhase,
1049 connect: Option<String>,
1050 /// Reserved for future retry telemetry.
1051 retry_attempt: u32,
1052 /// Optional human-readable diagnostic while not connected,
1053 /// such as DNS failure or waiting for the configured remote
1054 /// link. Invalid endpoints fail configuration before startup.
1055 detail: Option<String>,
1056 }
1057
1058 topic state: state State;
1059 }
1060 }
1061
1062
1063
1064
1065 perception {
1066 /// A single detected object: class, confidence, and pose in a frame.
1067 struct Detection {
1068 class_id: String,
1069 confidence: f32,
1070 position_m: [f64; 3],
1071 frame_id: String,
1072 track_id: Option<u64>,
1073 }
1074
1075 /// A batch of detections from one perception cycle.
1076 struct Detections {
1077 detections: Vec<Detection>,
1078 stamp_ns: Option<u64>,
1079 }
1080
1081 /// The perception participant's published health.
1082 struct State {
1083 healthy: bool,
1084 detector: String,
1085 }
1086
1087 topic detections: state Detections;
1088 topic state: state State;
1089 }
1090
1091 video {
1092 /// Ask to open a video stream for a capability at an optional size.
1093 struct OpenRequest {
1094 capability: String,
1095 width_px: Option<u32>,
1096 height_px: Option<u32>,
1097 }
1098
1099 /// The id of the stream that was opened.
1100 struct OpenResponse {
1101 stream_id: String,
1102 }
1103
1104 topic open: query OpenRequest => OpenResponse;
1105
1106 stream(stream) {
1107 /// Where one open video stream is in its lifecycle.
1108 #[derive(Copy, Eq)]
1109 #[serde(rename_all = "snake_case")]
1110 enum StreamPhase {
1111 Starting,
1112 Active,
1113 Stopped,
1114 }
1115
1116 /// The published state of one video stream: its lifecycle phase
1117 /// and the number of source frames seen so far. The video participant
1118 /// publishes it per stream; clients subscribe, hence `state`.
1119 struct StreamState {
1120 phase: StreamPhase,
1121 frames_seen: u64,
1122 }
1123
1124 topic state: state StreamState;
1125 }
1126 }
1127
1128 simulation {
1129 /// The authoritative advancing simulation clock. Publication means
1130 /// the world advanced; silence means it did not.
1131 struct Clock {
1132 /// Opaque identity minted once by the controller process.
1133 epoch: u64,
1134 now_ns: u64,
1135 step: u64,
1136 }
1137
1138 topic clock: state Clock;
1139 }
1140
1141 // Per-instance component capabilities (D17/D38: framework participant / driver
1142 // territory). `component(instance)` selects a manifest-declared component;
1143 // each child `kind(capability)` is a self-contained node whose key is
1144 // `component/{instance}/<kind>/{capability}/<leaf>`. Nodes duplicate any
1145 // types they share by design - the node path disambiguates, so the names
1146 // are path-local.
1147 component(instance) {
1148 motor(capability) {
1149 /// A per-actuator command.
1150 enum Command {
1151 Velocity(f32),
1152 Torque(f32),
1153 Stop,
1154 }
1155
1156 topic command: command Command;
1157 }
1158
1159 encoder(capability) {
1160 /// Per-encoder sample on a dynamic per-instance key.
1161 struct Sample {
1162 position_rad: f64,
1163 velocity_radps: f32,
1164 }
1165
1166 topic sample: state Sample;
1167 }
1168
1169 accelerometer(capability) {
1170 /// Raw accelerometer sample in the sensor-local frame in m/s^2.
1171 struct Sample {
1172 linear_acceleration: [f32; 3],
1173 }
1174
1175 topic sample: state Sample;
1176 }
1177
1178 gyroscope(capability) {
1179 /// Raw angular velocity sample in the sensor-local frame in rad/s.
1180 struct Sample {
1181 angular_velocity: [f32; 3],
1182 }
1183
1184 topic sample: state Sample;
1185 }
1186
1187 magnetometer(capability) {
1188 /// Raw magnetic-field sample in the sensor-local frame.
1189 struct Sample {
1190 magnetic_field: [f32; 3],
1191 }
1192
1193 topic sample: state Sample;
1194 }
1195
1196 imu(capability) {
1197 #[derive(Copy, Eq)]
1198 #[serde(rename_all = "snake_case")]
1199 enum SensorHealth {
1200 Nominal,
1201 Degraded,
1202 Fault,
1203 }
1204
1205 #[derive(Copy)]
1206 struct Bias {
1207 angular_velocity_radps: [f32; 3],
1208 linear_acceleration_mps2: [f32; 3],
1209 }
1210
1211 struct Sample {
1212 orientation: Option<[f32; 4]>,
1213 angular_velocity_radps: [f32; 3],
1214 linear_acceleration_mps2: [f32; 3],
1215 covariance: Option<[f32; 9]>,
1216 noise_density: Option<[f32; 3]>,
1217 sensor_frame_id: Option<String>,
1218 measured_at_ns: Option<u64>,
1219 health: SensorHealth,
1220 bias: Option<Bias>,
1221 }
1222
1223 topic sample: state Sample;
1224 }
1225
1226 range(capability) {
1227 #[derive(Copy, Eq)]
1228 #[serde(rename_all = "snake_case")]
1229 enum SensorHealth {
1230 Nominal,
1231 Degraded,
1232 Fault,
1233 }
1234
1235 #[derive(Copy)]
1236 struct Limits {
1237 min_m: f32,
1238 max_m: f32,
1239 }
1240
1241 #[derive(Copy)]
1242 struct SampleQuality {
1243 valid: bool,
1244 confidence: Option<f32>,
1245 }
1246
1247 struct Sample {
1248 distance_m: f32,
1249 limits: Option<Limits>,
1250 measured_at_ns: Option<u64>,
1251 quality: Option<SampleQuality>,
1252 health: SensorHealth,
1253 }
1254
1255 topic sample: state Sample;
1256 }
1257
1258 gnss(capability) {
1259 /// A GNSS fix: geodetic position plus a 3x3 position covariance.
1260 struct Sample {
1261 latitude: f64,
1262 longitude: f64,
1263 altitude: f64,
1264 position_covariance: [f64; 9],
1265 }
1266
1267 topic sample: state Sample;
1268 }
1269
1270 camera(capability) {
1271 #[derive(Copy, Eq)]
1272 #[serde(rename_all = "snake_case")]
1273 enum Encoding {
1274 Jpeg,
1275 Png,
1276 L8,
1277 Rgb8,
1278 Rgba8,
1279 }
1280
1281 #[derive(Copy)]
1282 struct Intrinsics {
1283 fx: f32,
1284 fy: f32,
1285 cx: f32,
1286 cy: f32,
1287 }
1288
1289 struct Distortion {
1290 model: String,
1291 coefficients: Vec<f32>,
1292 }
1293
1294 #[derive(Copy)]
1295 struct ExposureTiming {
1296 exposure_start_ns: Option<u64>,
1297 exposure_duration_ns: Option<u64>,
1298 }
1299
1300 struct CalibrationIdentity {
1301 id: String,
1302 version: String,
1303 }
1304
1305 /// One camera frame: encoded pixel bytes plus optional calibration
1306 /// and timing metadata.
1307 struct Frame {
1308 width: u32,
1309 height: u32,
1310 encoding: Encoding,
1311 intrinsics: Option<Intrinsics>,
1312 distortion: Option<Distortion>,
1313 exposure: Option<ExposureTiming>,
1314 measured_at_ns: Option<u64>,
1315 calibration: Option<CalibrationIdentity>,
1316 #[serde(with = "serde_bytes")]
1317 data: Vec<u8>,
1318 }
1319
1320 topic frame: state Frame;
1321 }
1322
1323 depth(capability) {
1324 #[derive(Copy, Eq)]
1325 #[serde(rename_all = "snake_case")]
1326 enum Encoding {
1327 U16Millimeters,
1328 }
1329
1330 #[derive(Copy, Eq)]
1331 #[serde(rename_all = "snake_case")]
1332 enum InvalidSamplePolicy {
1333 ZeroIsInvalid,
1334 NonFiniteIsInvalid,
1335 }
1336
1337 #[derive(Copy)]
1338 struct Intrinsics {
1339 fx: f32,
1340 fy: f32,
1341 cx: f32,
1342 cy: f32,
1343 }
1344
1345 struct Distortion {
1346 model: String,
1347 coefficients: Vec<f32>,
1348 }
1349
1350 #[derive(Copy)]
1351 struct ExposureTiming {
1352 exposure_start_ns: Option<u64>,
1353 exposure_duration_ns: Option<u64>,
1354 }
1355
1356 struct CalibrationIdentity {
1357 id: String,
1358 version: String,
1359 }
1360
1361 /// One depth frame: per-pixel millimetre samples plus optional
1362 /// calibration and timing metadata.
1363 struct Frame {
1364 samples_mm: Vec<u16>,
1365 encoding: Encoding,
1366 invalid_sample_policy: InvalidSamplePolicy,
1367 width: Option<u32>,
1368 height: Option<u32>,
1369 intrinsics: Option<Intrinsics>,
1370 distortion: Option<Distortion>,
1371 exposure: Option<ExposureTiming>,
1372 measured_at_ns: Option<u64>,
1373 calibration: Option<CalibrationIdentity>,
1374 }
1375
1376 topic frame: state Frame;
1377 }
1378
1379 lidar(capability) {
1380 #[derive(Copy, Eq)]
1381 #[serde(rename_all = "snake_case")]
1382 enum SensorHealth {
1383 Nominal,
1384 Degraded,
1385 Fault,
1386 }
1387
1388 #[derive(Copy)]
1389 struct ScanGeometry {
1390 angle_min_rad: f32,
1391 angle_increment_rad: f32,
1392 }
1393
1394 #[derive(Copy)]
1395 struct RangeLimits {
1396 min_m: f32,
1397 max_m: f32,
1398 }
1399
1400 #[derive(Copy)]
1401 struct ScanQuality {
1402 valid_points: u32,
1403 }
1404
1405 struct Ranges {
1406 ranges: Vec<f32>,
1407 geometry: Option<ScanGeometry>,
1408 limits: Option<RangeLimits>,
1409 measured_at_ns: Option<u64>,
1410 quality: Option<ScanQuality>,
1411 health: SensorHealth,
1412 }
1413
1414 struct Points {
1415 points: Vec<[f32; 3]>,
1416 limits: Option<RangeLimits>,
1417 measured_at_ns: Option<u64>,
1418 quality: Option<ScanQuality>,
1419 health: SensorHealth,
1420 }
1421
1422 /// One lidar scan, either as polar ranges or as cartesian points.
1423 #[serde(tag = "kind", rename_all = "snake_case")]
1424 enum Scan {
1425 Ranges(Ranges),
1426 Points(Points),
1427 }
1428
1429 topic scan: state Scan;
1430 }
1431
1432 mmwave(capability) {
1433 /// One mmWave radar detection: position, velocity, and SNR.
1434 #[derive(Copy)]
1435 struct Detection {
1436 position: [f32; 3],
1437 velocity: [f32; 3],
1438 snr: f32,
1439 }
1440
1441 /// One mmWave radar scan as a set of detections.
1442 struct Scan {
1443 detections: Vec<Detection>,
1444 }
1445
1446 topic scan: state Scan;
1447 }
1448
1449 microphone(capability) {
1450 /// One audio frame as raw encoded bytes.
1451 struct Frame {
1452 data: Vec<u8>,
1453 }
1454
1455 topic frame: state Frame;
1456 }
1457
1458 led(capability) {
1459 /// A per-LED on/off command.
1460 #[derive(Copy, Eq)]
1461 enum Command {
1462 On,
1463 Off,
1464 }
1465
1466 topic command: command Command;
1467 }
1468
1469 emergency_stop(capability) {
1470 /// Per-instance emergency-stop state.
1471 #[derive(Eq)]
1472 struct State {
1473 engaged: bool,
1474 }
1475
1476 topic state: state State;
1477 }
1478 }
1479
1480 odometry {
1481 /// A planar pose + twist estimate in the odometry frame.
1482 struct State {
1483 x_m: f64,
1484 y_m: f64,
1485 yaw_rad: f64,
1486 linear_x_mps: f32,
1487 angular_z_radps: f32,
1488 }
1489
1490 topic state: state State;
1491 }
1492
1493 localize {
1494 /// A planar localization estimate in the map frame.
1495 struct LocalizationState {
1496 x_m: f64,
1497 y_m: f64,
1498 yaw_rad: f64,
1499 confidence: f32,
1500 }
1501
1502 topic state: state LocalizationState;
1503 }
1504
1505 map {
1506 /// A published map revision marker.
1507 struct Revision {
1508 revision: u64,
1509 resolution_m: f32,
1510 }
1511
1512 /// Request a rectangular submap window (map-frame metres).
1513 struct SubmapRequest {
1514 min_x_m: f64,
1515 min_y_m: f64,
1516 max_x_m: f64,
1517 max_y_m: f64,
1518 }
1519
1520 /// An occupancy-grid window: row-major cells, 0..=100 + 255 = unknown.
1521 struct SubmapResponse {
1522 width: u32,
1523 height: u32,
1524 resolution_m: f32,
1525 cells: Vec<u8>,
1526 }
1527
1528 topic revision: state Revision;
1529 topic submap: query SubmapRequest => SubmapResponse;
1530 }
1531
1532 asset {
1533 /// Fetch a stored asset by path.
1534 struct GetRequest {
1535 path: String,
1536 }
1537
1538 /// The asset bytes, a not-found marker, or a rejected path.
1539 enum GetResponse {
1540 Found { bytes: Vec<u8> },
1541 Missing,
1542 InvalidPath,
1543 }
1544
1545 topic get: query GetRequest => GetResponse;
1546 }
1547
1548 battery {
1549 /// Battery state reported by the active simulator or hardware owner.
1550 struct State {
1551 voltage_v: f32,
1552 current_a: f32,
1553 charge_ratio: f32,
1554 }
1555
1556 topic state: state State;
1557 }
1558
1559 joypad {
1560 /// Whether an observed controller is ready for the fixed manual
1561 /// input preset, disconnected, or connected without a compatible
1562 /// control mapping.
1563 enum DeviceStatus {
1564 Ready,
1565 Disconnected,
1566 Unsupported,
1567 }
1568
1569 /// One gamepad the tool can see. `id` is a STABLE wire id the tool
1570 /// assigns (name/guid-derived) - NOT a process-local gilrs id.
1571 struct Device {
1572 id: String,
1573 name: String,
1574 status: DeviceStatus,
1575 }
1576
1577 /// The joypad tool's published device state.
1578 struct Devices {
1579 available: Vec<Device>,
1580 selected: Option<String>,
1581 enabled: bool,
1582 /// Structural reason manual input cannot be enabled in this
1583 /// session (for example robot-model or backend limitations),
1584 /// independent of transient device/request errors.
1585 unavailable_reason: Option<String>,
1586 /// One-shot acknowledgement of a failed select/enable/rescan
1587 /// request. Event-driven consumers may show it once; periodic
1588 /// state heartbeats omit it. The tool also writes the failure
1589 /// to its log stream for durable diagnostics.
1590 last_error: Option<String>,
1591 }
1592
1593 /// Client asks the tool to select a device by its stable id.
1594 struct Select {
1595 id: String,
1596 }
1597
1598 /// Client asks the tool to enable or disable manual input.
1599 struct SetEnabled {
1600 enabled: bool,
1601 }
1602
1603 /// Client asks the tool to re-enumerate devices.
1604 struct Rescan {}
1605
1606 topic devices: state Devices;
1607 topic select: command Select;
1608 topic set_enabled: command SetEnabled;
1609 topic rescan: command Rescan;
1610 }
1611 }
1612 version v0_2 extends v0_1 {
1613 tool {
1614 device {
1615 /// Portable whole-device observations produced by one
1616 /// runner-owned tool-device. Every optional field is `None`
1617 /// when the current platform cannot provide a truthful value.
1618 /// These totals must never be attributed to a runtime.
1619 replace struct Sample {
1620 /// Bounded execution-device label supplied by the project
1621 /// supervisor. Every per-robot sampler in one project uses
1622 /// the same value, so robot-rooted records remain joinable
1623 /// without creating device-rooted bus authority.
1624 device_id: String,
1625 cpu_pct: Option<f32>,
1626 ram_used_bytes: Option<u64>,
1627 ram_total_bytes: Option<u64>,
1628 swap_used_bytes: Option<u64>,
1629 swap_total_bytes: Option<u64>,
1630 load_1m: Option<f32>,
1631 load_5m: Option<f32>,
1632 load_15m: Option<f32>,
1633 uptime_s: Option<u64>,
1634 disks: Option<Vec<Disk>>,
1635 /// Host-monotonic duration between sampler refreshes.
1636 window_ns: u64,
1637 }
1638 }
1639 }
1640 }
1641 latest v0_2;
1642}
1643
1644#[cfg(test)]
1645mod tests;