Replaying¶
If you have a Sen archive, you can play it back using the replayer component.
Starting a replay¶
The simplest way to start a replay is:
# load the archive and start the playback straight away
$ sen replay my_archive
# load the archive but keep the replay stopped
$ sen replay my_archive --stopped
You can see that this approach creates the replay in an independent process. While convenient, sometimes you might want to embed a replay in your own process. For example, in testing or during development.
This can be done by adding the replayer component to your configuration file. For example:
load:
- name: replayer
autoOpen: school_recording # this is the path to the archive
autoPlay: true # start the playback right away
group: 20
The configuration options are defined in the component's STL:
package sen.components.replayer;
// Overall component configuration
struct Configuration
{
bus : string, // bus to publish the object (local.replay by default)
samplingPeriod : Duration, // max sampling period for flushing the replayed data
autoOpen : string, // optional archive path to auto open on start-up (empty by default)
autoPlay : bool // if autoOpen is not empty, try to play it on start-up (false by default)
}
The Replayer object¶
The main object of the replayer component is the Replayer object. It allows you to open multiple
archives for replay. Every time you open an archive, a Replay object is created, which is the one
you can use to control the playback.
The interface for these objects is this:
import "stl/sen/db/basic_types.stl"
package sen.components.replayer;
// represents the replayer component and manages Replay objects.
class Replayer
{
// if true, automatically calls play when opening a replay
var autoPlay : bool [static];
// opens an archive in a given path, and creates a named Replay object.
fn open(name: string, path: string) [confirmed];
// closes the named Replay object, if any.
fn close(name: string) [confirmed];
// closes all previously opened replays
fn closeAll() [confirmed];
}
// the state in which a Replay can be
enum ReplayStatus: u8
{
stopped, // not running, at the start of the archive
paused, // not running, at the position when pause was called
playing // performing the replay
}
// controls the replay of a given archive
class Replay
{
// information about the archive being replayed
var archiveInfo : sen.db.Summary [static];
// file path to the archive
var archivePath : string [static];
// playback status
var status : ReplayStatus [confirmed];
// playback time
var playbackTime : TimeStamp [bestEffort];
// starts the playback from the current position
fn play() [confirmed];
// hold the playback position, if playing
fn pause() [confirmed];
// goes to the start of the playback, erasing all objects
fn stop() [confirmed];
// restores the playback to the keyframe that's closest to a given time
fn seek(time: TimeStamp) [confirmed];
// if paused or stopped, advances the playback a given time delta
fn advance(time: Duration);
}
The typical usage would be something like this:
sequenceDiagram
actor You
You->>+Replayer: open(name, path)
Replayer->>+Replay: create()
You->>+Replay: play()
You->>+Replay: pause()
You->>+Replay: seek(time)
You->>+Replayer: close(name)
You-xReplay: delete
What a replay reproduces¶
A replay reproduces what was recorded, which is state and events. Nothing is re-executed. Method calls were never in the archive, so components that called each other during the original run do not do so again.
Playback advances by the replayer's own cycle. Each update takes the time elapsed since the last one and applies every entry whose recorded timestamp falls in that window, so recorded timing is reproduced to the resolution of one cycle, and entries closer together than that arrive together. Because the step comes from the execution time, a replay follows the kernel's run mode, whether that is real time or a virtual clock you drive at your own pace.
Seeking lands exactly on the time you ask for. The replayer jumps to the keyframe at or before it and then replays forward, so the keyframe period decides what a seek costs. It does not affect where the seek ends up. An archive recorded without keyframes does not support seeking, and says so.