repose_robot_interface/lib.rs
1//! Vendor-neutral contract for robot bridges in `copper-rs`.
2//!
3//! Every Repose robot bridge, whether simulator or hardware and whatever the
4//! vendor, exposes the same channels, so an application swaps robots by
5//! changing one bridge entry in its RON config:
6//!
7//! | Dir | Channel | Payload | Required |
8//! |---|---|---|---|
9//! | Rx | `joint_state` | `repose_kinematics::CuJointStatePayload<N>` | always |
10//! | Tx | `joint_command` | `repose_controller::CuJointCommandPayload<N>` | bridges that move the robot |
11//! | Tx | `session` | [`CuSessionCommand`] | bridges with a control session |
12//! | Rx | `session_state` | [`CuSessionState`] | bridges with a control session |
13//!
14//! Bridges may add vendor channels (raw diagnostics, controller-reported
15//! poses) under vendor-prefixed names such as `cri_status`.
16//!
17//! # Session lifecycle
18//!
19//! A hardware controller has to grant control and energise its drives before
20//! it accepts motion. The [`CuSessionCommand`] vocabulary covers that
21//! lifecycle in vendor-neutral terms, and each bridge maps it to its protocol:
22//!
23//! ```text
24//! AcquireControl → ClearFaults → EnableDrives → … motion … → DisableDrives → ReleaseControl
25//! ```
26//!
27//! [`CuSessionTracker`] holds the protocol-independent rules, so every bridge
28//! enforces them the same way:
29//! - one command in flight; a request while one is pending is refused and
30//! counted,
31//! - every command except [`CuSessionCommand::AcquireControl`] is refused
32//! locally while the controller reports this client as not in control,
33//! - acknowledgements and rejections only count for the sequence number that
34//! was sent,
35//! - shutdown disables drives, then releases control, but only what this
36//! session itself changed.
37//!
38//! ```rust
39//! use repose_robot_interface::{CuSessionCommand, CuSessionDecision, CuSessionResult, CuSessionTracker};
40//!
41//! let mut tracker = CuSessionTracker::default();
42//! tracker.on_control_report(false);
43//! assert_eq!(tracker.request(CuSessionCommand::EnableDrives), CuSessionDecision::Refused);
44//!
45//! assert_eq!(tracker.request(CuSessionCommand::AcquireControl), CuSessionDecision::Send);
46//! let command = tracker.take_outgoing().unwrap();
47//! tracker.mark_sent(command, 7);
48//! tracker.on_control_report(true); // completes AcquireControl
49//! assert_eq!(tracker.state().result, CuSessionResult::Acknowledged);
50//! assert!(tracker.state().control_acquired);
51//! ```
52#![cfg_attr(not(feature = "std"), no_std)]
53
54mod session;
55
56pub use session::CuSessionCommand;
57pub use session::CuSessionDecision;
58pub use session::CuSessionDetail;
59pub use session::CuSessionRejection;
60pub use session::CuSessionResult;
61pub use session::CuSessionState;
62pub use session::CuSessionTracker;