fixed event/live transition
This commit is contained in:
1 parent
bb99be5889
commit
881b52748e
9 files changed
+314
-2
No files matched your search
+93
-1
@@ -141,11 +141,94 @@ bool record_event(App& app) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Remembers the live view's settings before a file view is opened over them.
|
||||
///
|
||||
/// A no-op unless the view being left *is* the live one: replaying a second event from inside the
|
||||
/// first, or a group opened from a replay, must not overwrite the snapshot with a file's settings --
|
||||
/// those are the very values *Go live* exists to undo.
|
||||
void snapshot_live_state(App& app) {
|
||||
if (app.replaying || app.model == nullptr || app.rows == nullptr) return;
|
||||
App::LiveState& s = app.live_state;
|
||||
s.valid = true;
|
||||
s.window_s = app.rows->time_window();
|
||||
s.trigger_on = app.trigger_toggle != nullptr &&
|
||||
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(app.trigger_toggle)) != FALSE;
|
||||
s.level_touched = app.trigger_level_touched;
|
||||
std::lock_guard<std::mutex> lock(app.model->mutex());
|
||||
s.trigger = app.model->trigger();
|
||||
s.trigger_channel = s.trigger.channel >= 0 &&
|
||||
s.trigger.channel < static_cast<int>(app.model->channels().size())
|
||||
? app.model->channels()[static_cast<std::size_t>(s.trigger.channel)].name
|
||||
: std::string();
|
||||
}
|
||||
|
||||
/// Puts those settings back on the way out of a file view.
|
||||
///
|
||||
/// Deferred by `go_live_again()` to the first tick that sees the stream's *own* channel list, which
|
||||
/// is what makes this the last writer over the two things that would otherwise overwrite it: the
|
||||
/// deferred layout re-apply (which ends in `apply_time_window()` with the *saved* window, not the
|
||||
/// live one) and `rebuild_trigger_signals()`, which, finding a `channel` of `-1` after a rebuild
|
||||
/// renumbered or renamed the channels, silently settles on the first signal and re-defaults the level
|
||||
/// with it.
|
||||
///
|
||||
/// Inside, the order is the whole of it:
|
||||
///
|
||||
/// - The configuration goes in under the lock, with the channel resolved from the stored *name*.
|
||||
/// `enabled` and `armed` are deliberately *not* written: they belong to the toolbar toggle, which
|
||||
/// owns an atomic mirror of `enabled` that a plain field write would leave stale.
|
||||
/// - The two controls a file view actually moved are restated from it -- the signal dropdown and the
|
||||
/// level spin. The rest of the panel was never touched, and writing to a control fires its handler,
|
||||
/// so the ones that have nothing to put back are left alone.
|
||||
/// - `apply_time_window()`, the only path allowed to restate the time scale; it also drops the event
|
||||
/// pin the file view left on the axis and re-arms the live follow.
|
||||
/// - The toggle last, because its handler reads the configuration it is turning on.
|
||||
void restore_live_state(App& app) {
|
||||
App::LiveState& s = app.live_state;
|
||||
if (!s.valid) return;
|
||||
s.valid = false; // consumed: the next way in takes its own snapshot
|
||||
int channel = -1;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(app.model->mutex());
|
||||
const std::vector<ChannelInfo>& channels = app.model->channels();
|
||||
for (std::size_t i = 0; i < channels.size(); ++i) {
|
||||
if (channels[i].name == s.trigger_channel) {
|
||||
channel = static_cast<int>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
TriggerConfig& tc = app.model->trigger();
|
||||
const bool enabled = tc.enabled;
|
||||
const bool armed = tc.armed;
|
||||
tc = s.trigger;
|
||||
tc.enabled = enabled;
|
||||
tc.armed = armed;
|
||||
tc.channel = channel;
|
||||
}
|
||||
app.trigger_level_touched = s.level_touched;
|
||||
app.updating_panels = true;
|
||||
if (channel >= 0 && app.trigger_signal_drop != nullptr) {
|
||||
gtk_drop_down_set_selected(GTK_DROP_DOWN(app.trigger_signal_drop),
|
||||
static_cast<guint>(channel));
|
||||
}
|
||||
if (app.trigger_level_spin != nullptr) {
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(app.trigger_level_spin), s.trigger.condition.level);
|
||||
}
|
||||
app.updating_panels = false;
|
||||
apply_time_window(app, s.window_s);
|
||||
if (app.trigger_toggle != nullptr &&
|
||||
(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(app.trigger_toggle)) != FALSE) !=
|
||||
s.trigger_on) {
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(app.trigger_toggle), s.trigger_on ? TRUE : FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/// Replays a cached event: the same loader `--open` uses, on the event's file. The acquisition is
|
||||
/// stopped first, because a live producer overwrites the loaded samples within a frame -- so the
|
||||
/// scope becomes a file viewer until *Go live* starts it again.
|
||||
bool replay_event(App& app, const EventRecord& record) {
|
||||
if (app.acq == nullptr || app.model == nullptr) return false;
|
||||
// Before the stop, while the live view is still the view: everything below overwrites it.
|
||||
snapshot_live_state(app);
|
||||
app.acq->stop();
|
||||
WaveformLoadReport report;
|
||||
std::string error;
|
||||
@@ -199,6 +282,13 @@ bool go_live_again(App& app) {
|
||||
app.layout_loaded = false; // the session's layout applies to the source again
|
||||
app.event_held = false;
|
||||
app.model->set_display_epoch(0.0);
|
||||
// What the live view had, put back from the tick rather than here: the stream's own channel list is
|
||||
// what the stored trigger channel has to be resolved against, and it does not exist until the first
|
||||
// CONFIG of the restarted client lands on the producer thread. The generation read here is what the
|
||||
// tick compares against to know that it has (see `restore_live_state()` for what else that ordering
|
||||
// buys).
|
||||
app.live_restore_generation = app.model->generation();
|
||||
app.live_restore_pending = app.live_state.valid;
|
||||
try {
|
||||
app.acq->start();
|
||||
} catch (const std::exception& e) {
|
||||
@@ -307,7 +397,9 @@ bool build_event_group(App& app, const std::vector<EventRecord>& records, EventG
|
||||
|
||||
// The acquisition first: a live producer keeps pushing into the model it was started with -- and it
|
||||
// pushes *four* stream signals into the group's eight channels, so the group's own samples are the
|
||||
// ones that go missing.
|
||||
// ones that go missing. The live view's settings are taken ahead of it, for `go_live_again()`:
|
||||
// after the checks above, because a group that is refused leaves the live view alone.
|
||||
snapshot_live_state(app);
|
||||
app.acq->stop();
|
||||
|
||||
std::vector<EventSamples> events(records.size());
|
||||
|
||||
Reference in new issue
Block a user