The open-source
Replayer class described on this page publishes messages back through the live transport layer and is available to all users. Full deterministic replay — where a DeterministicReplayer drives handlers directly without requiring a running transport stack — is a premium feature. See DeterministicReplayer below for details.The Replayer class
Replayer reads an MCAP file and republishes each message on the Basis transport as if it were being received live. Units that are subscribed to the recorded topics receive the messages through their normal subscriber callbacks.
Constructor and Config
Replayer does not own the TransportManager or CoordinatorConnector; you are responsible for their lifetimes.
Run()
Run() opens the MCAP file, iterates over every message in timestamp order, and publishes each one through transport_manager using a raw publisher keyed by topic name. It blocks until the recording is exhausted (or forever if loop is true).
Returns true on success, false if the recording could not be opened or is invalid.
StartTime() and EndTime()
Replayer but before calling Run() to know the time span of the recording.
How replay publishes messages
Internally,Replayer maintains a map from topic name to a PublisherRaw (an untyped publisher that sends pre-serialized bytes). For each message read from the MCAP file, it looks up or creates a PublisherRaw for that topic and calls Publish() with the raw bytes.
It also manages a special time publisher on the /basis/time topic so that rate-based handlers and simulated-time code receive the correct playback timestamp.
Replayer useful for both live debugging and automated integration tests that run Units in a test process alongside a replayer.
Example: replaying a recording
The DeterministicReplayer class
DeterministicReplayer is a premium feature that provides stronger guarantees than the transport-based Replayer. Instead of publishing messages through the transport layer, it drives a Unit’s HandlerPubSub map directly, bypassing all network and thread-scheduling non-determinism.
The Unit base class declares DeterministicReplayer as a friend, giving it access to the internal handlers map and deserialization_helpers:
DeterministicReplayer:
- Looks up the topic in the Unit’s
handlersmap to find theTypeErasedCallbackregistered for that topic. - Calls
deserialization_helpers[type_name]to deserialize the raw bytes into astd::shared_ptr<const void>. - Invokes the
TypeErasedCallbackdirectly with the deserialized message pointer and aHandlerExecutingCallback*. - If the synchronizer fires, calls the
HandlerExecutingCallbackto run the handler and collect outputs.
DeterministicReplayer never touches the transport, outputs, or timing subsystems, the same recording always produces the same handler invocations in the same order — regardless of system load.
To use
DeterministicReplayer, contact Basis Robotics about a commercial license. The Replayer class and all recording infrastructure are open source.Why this enables identical test results
The key insight is that robot handler code is a pure function of its inputs: given the same messages in the same order, it should always produce the same outputs. Non-determinism in conventional systems comes from:- Wall-clock timers firing at slightly different times
- Thread scheduling affecting message delivery order
- Network latency between pub and sub
DeterministicReplayer eliminates all three. It feeds messages in the exact recorded order and at the exact recorded timestamps, using UnitInitializeOptions{.create_subscribers = false} to suppress real subscriber creation. There is no transport, no threading, and no wall clock.
Practical workflow: record on robot, replay in CI
Record on the robot
Add a See Recording for the full API.
RecordingSettings block to your launch file or attach a Recorder to your Unit:launch.yaml
Archive the MCAP file
Copy the
.mcap file to your artifact store (S3, a network share, CI artifact storage, etc.). The file is self-describing — it includes all schemas — so no recompilation is needed to read it.Write a replay test
In CI, construct your Unit with For open-source users, use
create_subscribers = false, then run DeterministicReplayer against the archived file:Replayer with a live Unit in a test process:Next steps
Recording
Set up the Recorder or AsyncRecorder and configure topic filtering
Testing overview
The Inputs→Handler→Outputs model and how it enables deterministic testing