Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show currently loading recordings in Recordings menu #3307

Merged
merged 8 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/re_renderer/examples/framework.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ impl<E: Example + 'static> Application<E> {
return;
}
Err(err) => {
re_log::warn!(%err, "dropped frame");
re_log::warn!("Dropped frame: {err}");
return;
}
};
Expand Down
5 changes: 5 additions & 0 deletions crates/re_smart_channel/src/receive_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ impl<T: Send> ReceiveSet<T> {
rx.push(r);
}

/// Disconnect from any channel with the given source.
pub fn remove(&self, source: &SmartChannelSource) {
self.receivers.lock().retain(|r| r.source() != source);
}

/// List of connected receiver sources.
///
/// This gets culled after calling one of the `recv` methods.
Expand Down
9 changes: 5 additions & 4 deletions crates/re_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -702,12 +702,13 @@ impl ReUi {
}

/// Two-column grid to be used in selection view.
///
/// Use this when you expect the right column to have multi-line entries.
#[allow(clippy::unused_self)]
pub fn selection_grid(&self, ui: &mut egui::Ui, id: &str) -> egui::Grid {
pub fn selection_grid(&self, _ui: &mut egui::Ui, id: &str) -> egui::Grid {
// Spread rows a bit to make it easier to see the groupings
egui::Grid::new(id)
.num_columns(2)
.spacing(ui.style().spacing.item_spacing + egui::vec2(0.0, 8.0))
let spacing = egui::vec2(8.0, 16.0);
egui::Grid::new(id).num_columns(2).spacing(spacing)
}

/// Draws a shadow into the given rect with the shadow direction given from dark to light
Expand Down
4 changes: 2 additions & 2 deletions crates/re_viewer/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,9 +712,9 @@ impl App {
re_smart_channel::SmartMessagePayload::Msg(msg) => msg,
re_smart_channel::SmartMessagePayload::Quit(err) => {
if let Some(err) = err {
re_log::warn!(%msg.source, err, "data source has left unexpectedly");
re_log::warn!("Data source {} has left unexpectedly: {err}", msg.source);
} else {
re_log::debug!(%msg.source, "data source has left");
re_log::debug!("Data source {} has left", msg.source);
}
continue;
}
Expand Down
2 changes: 1 addition & 1 deletion crates/re_viewer/src/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ impl AppState {
// before drawing the blueprint panel.
ui.spacing_mut().item_spacing.y = 0.0;

let recording_shown = recordings_panel_ui(&mut ctx, ui);
let recording_shown = recordings_panel_ui(&mut ctx, rx, ui);

if recording_shown {
ui.add_space(4.0);
Expand Down
2 changes: 1 addition & 1 deletion crates/re_viewer/src/store_hub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl StoreHub {
StoreContext {
blueprint,
recording,
alternate_recordings: self.store_dbs.recordings().collect_vec(),
all_recordings: self.store_dbs.recordings().collect_vec(),
}
})
}
Expand Down
157 changes: 149 additions & 8 deletions crates/re_viewer/src/ui/recordings_panel.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
use re_viewer_context::{CommandSender, SystemCommand, SystemCommandSender, ViewerContext};
use std::collections::BTreeMap;

use time::macros::format_description;

use re_log_types::LogMsg;
use re_smart_channel::{ReceiveSet, SmartChannelSource};
use re_viewer_context::{CommandSender, SystemCommand, SystemCommandSender, ViewerContext};

static TIME_FORMAT_DESCRIPTION: once_cell::sync::Lazy<
&'static [time::format_description::FormatItem<'static>],
> = once_cell::sync::Lazy::new(|| format_description!(version = 2, "[hour]:[minute]:[second]Z"));

/// Show the currently open Recordings in a selectable list.
/// Also shows the currently loading receivers.
///
/// Returns `true` if any recordings were shown.
pub fn recordings_panel_ui(ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui) -> bool {
pub fn recordings_panel_ui(
ctx: &mut ViewerContext<'_>,
rx: &ReceiveSet<LogMsg>,
ui: &mut egui::Ui,
) -> bool {
ctx.re_ui.panel_content(ui, |re_ui, ui| {
re_ui.panel_title_bar_with_buttons(
ui,
Expand All @@ -26,16 +35,87 @@ pub fn recordings_panel_ui(ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui) -> bo
.auto_shrink([false, true])
.max_height(300.)
.show(ui, |ui| {
ctx.re_ui
.panel_content(ui, |_re_ui, ui| recording_list_ui(ctx, ui))
ctx.re_ui.panel_content(ui, |_re_ui, ui| {
let mut any_shown = false;
any_shown |= recording_list_ui(ctx, ui);

// Show currently loading things after.
// They will likely end up here as recordings soon.
any_shown |= loading_receivers_ui(ctx, rx, ui);

any_shown
})
})
.inner
}

fn loading_receivers_ui(
ctx: &mut ViewerContext<'_>,
rx: &ReceiveSet<LogMsg>,
ui: &mut egui::Ui,
) -> bool {
let sources_with_stores: ahash::HashSet<SmartChannelSource> = ctx
.store_context
.all_recordings
.iter()
.filter_map(|store| store.data_source.clone())
.collect();

let mut any_shown = false;

for source in rx.sources() {
let (always_show, string) = match source.as_ref() {
SmartChannelSource::File(path) => (false, format!("Loading {}…", path.display())),

SmartChannelSource::RrdHttpStream { url } => (false, format!("Loading {url}…")),

SmartChannelSource::RrdWebEventListener => {
(false, "Waiting on Web Event Listener…".to_owned())
}

SmartChannelSource::Sdk => (false, "Waiting on SDK…".to_owned()),

SmartChannelSource::WsClient { ws_server_url } => {
(false, format!("Loading from {ws_server_url}…"))
}

SmartChannelSource::TcpServer { port } => {
// We have a TcpServer when running just `cargo rerun`
(true, format!("Hosting a TCP Server on port {port}"))
}
};

// Only show if we don't have a recording for this source,
// i.e. if this source hasn't sent anything yet.
// Note that usually there is a one-to-one mapping between a source and a recording,
// but it is possible to send multiple recordings over the same channel.
if always_show || !sources_with_stores.contains(&source) {
any_shown = true;
let response = ctx
.re_ui
.list_item(string)
.with_buttons(|re_ui, ui| {
let resp = re_ui
.small_icon_button(ui, &re_ui::icons::REMOVE)
.on_hover_text("Disconnect from this source");
if resp.clicked() {
rx.remove(&source);
}
resp
})
.show(ui);
if let SmartChannelSource::TcpServer { .. } = source.as_ref() {
response.on_hover_text("You can connect to this viewer from a Rerun SDK");
}
}
}

any_shown
}

/// Draw the recording list.
///
/// Returns `true` if any recordings were shown.
#[allow(clippy::blocks_in_if_conditions)]
fn recording_list_ui(ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui) -> bool {
let ViewerContext {
store_context,
Expand All @@ -44,7 +124,7 @@ fn recording_list_ui(ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui) -> bool {
} = ctx;

let mut store_dbs_map: BTreeMap<_, Vec<_>> = BTreeMap::new();
for store_db in &store_context.alternate_recordings {
for store_db in &store_context.all_recordings {
let key = store_db
.store_info()
.map_or("<unknown>", |info| info.application_id.as_str());
Expand Down Expand Up @@ -133,7 +213,7 @@ fn recording_ui(
})
.unwrap_or("<unknown time>".to_owned());

re_ui
let response = re_ui
.list_item(format!("{prefix}{name}"))
.with_buttons(|re_ui, ui| {
let resp = re_ui
Expand All @@ -155,7 +235,68 @@ fn recording_ui(
ui.painter()
.circle(rect.center(), 4.0, color, egui::Stroke::NONE);
})
.show(ui)
.show(ui);

response.on_hover_ui(|ui| {
recording_hover_ui(re_ui, ui, store_db);
})
}

fn recording_hover_ui(re_ui: &re_ui::ReUi, ui: &mut egui::Ui, store_db: &re_data_store::StoreDb) {
egui::Grid::new("recording_hover_ui")
.num_columns(2)
.show(ui, |ui| {
re_ui.grid_left_hand_label(ui, "Store ID");
ui.label(store_db.store_id().to_string());
ui.end_row();

if let Some(data_source) = &store_db.data_source {
re_ui.grid_left_hand_label(ui, "Data source");
ui.label(data_source_string(data_source));
ui.end_row();
}

if let Some(set_store_info) = store_db.recording_msg() {
let re_log_types::StoreInfo {
application_id,
store_id: _,
is_official_example: _,
started,
store_source,
store_kind,
} = &set_store_info.info;

re_ui.grid_left_hand_label(ui, "Application ID");
ui.label(application_id.to_string());
ui.end_row();

re_ui.grid_left_hand_label(ui, "Recording started");
ui.label(started.format());
ui.end_row();

re_ui.grid_left_hand_label(ui, "Source");
ui.label(store_source.to_string());
ui.end_row();

// We are in the recordings menu, we know the kind
if false {
re_ui.grid_left_hand_label(ui, "Kind");
ui.label(store_kind.to_string());
ui.end_row();
}
}
});
}

fn data_source_string(data_source: &re_smart_channel::SmartChannelSource) -> String {
match data_source {
SmartChannelSource::File(path) => path.display().to_string(),
SmartChannelSource::RrdHttpStream { url } => url.clone(),
SmartChannelSource::RrdWebEventListener => "Web Event Listener".to_owned(),
SmartChannelSource::Sdk => "SDK".to_owned(),
SmartChannelSource::WsClient { ws_server_url } => ws_server_url.clone(),
SmartChannelSource::TcpServer { port } => format!("TCP Server, port {port}"),
}
}

fn add_button_ui(ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui) {
Expand Down
2 changes: 1 addition & 1 deletion crates/re_viewer_context/src/store_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ use re_data_store::StoreDb;
pub struct StoreContext<'a> {
pub blueprint: &'a StoreDb,
pub recording: Option<&'a StoreDb>,
pub alternate_recordings: Vec<&'a StoreDb>,
pub all_recordings: Vec<&'a StoreDb>,
}
4 changes: 2 additions & 2 deletions crates/re_ws_comms/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,9 @@ fn to_broadcast_stream(
}
re_smart_channel::SmartMessagePayload::Quit(err) => {
if let Some(err) = err {
re_log::warn!(%msg.source, err, "sender has left unexpectedly");
re_log::warn!("Sender {} has left unexpectedly: {err}", msg.source);
} else {
re_log::debug!(%msg.source, "sender has left");
re_log::debug!("Sender {} has left", msg.source);
}
}
}
Expand Down