Robot interface · repose-robot-interface
Session lifecycle
A real controller won't accept motion from just anyone. A client has to take control, clear faults and energise the drives first, and should hand everything back when it's done. Repose gives that lifecycle one vendor-neutral vocabulary.
Commands
AcquireControl → ClearFaults → EnableDrives → … motion … → DisableDrives → ReleaseControlAcquireControl | Ask the controller to accept commands from this client. |
ClearFaults | Reset resettable controller and drive faults. |
EnableDrives | Energise the drives and release the brakes. The robot holds position; nothing moves. |
DisableDrives | De-energise the drives. |
ReleaseControl | Hand control back. |
No session command moves the robot. Each bridge maps them to its protocol: CRI commands on the igus, dashboard and RTDE on a UR (where EnableDrives also powers the arm on and releases the brakes).
State: CuSessionState
command, sequence | The most recent command and the protocol sequence number it went out with. |
result | Idle, Pending, Acknowledged or Rejected. |
rejection, detail | Who rejected it (NotInControl locally, or Controller) and the controller's text, up to 32 bytes. |
control_acquired | The controller reports this client in control. |
drives_enabled, drives_ready | Enable was acknowledged, and every drive reports ready. drives_ready lags on controllers that bring axes up one at a time. |
motion_allowed | The controller would accept motion right now. Wait for this before streaming commands. |
refused | Requests refused because another was still pending. |
The rules: CuSessionTracker
The rules live in one place, not in each bridge. CuSessionTracker owns no I/O and never allocates. The bridge feeds it requests from Copper and events decoded from the controller, and it decides:
- One command in flight. A request while one is pending is refused and counted in
refused. - Control first. Everything except
AcquireControlis refused locally while the controller says this client isn't in control. - Answers must match. Acknowledgements and rejections count only for the sequence number that was actually sent.
- Clean shutdown. Shutdown disables drives, then releases control, but only what this session itself changed. A robot someone else enabled stays enabled.
let mut tracker = CuSessionTracker::default();
// From Copper: a session command arrives on the `session` channel.
if tracker.request(cmd) == CuSessionDecision::Send {
let cmd = tracker.take_outgoing().unwrap();
let seq = protocol.send(cmd)?; // vendor-specific
tracker.mark_sent(cmd, seq);
}
// From the controller, as they are decoded:
tracker.on_acknowledged(seq);
tracker.on_rejected(seq, "brakes locked");
tracker.on_control_report(in_control);
tracker.on_drive_report(drives_ready, motion_allowed);
tracker.on_disconnected();
// Publish on `session_state` when it changed:
if tracker.take_changed() { publish(*tracker.state()); }
// On stop: wind down one step at a time.
if !tracker.is_busy() {
if let Some(cmd) = tracker.shutdown_step() { tracker.request(cmd); }
}Who sends the commands
In the robot-app template, the runner sends the session sequence on start, waits for motion_allowed, runs the program, and winds down on exit or Ctrl-C. In your own graph, any task can publish CuSessionCommand on the bridge's session channel and watch session_state.