added untested tracker state metrics
This commit is contained in:
parent
8164eca473
commit
81a4f04d6b
7 changed files with 180 additions and 46 deletions
|
@ -20,6 +20,7 @@ use crate::remote_sources::TrackerState;
|
|||
use crate::{gstreamer_pipeline, remote_sources};
|
||||
use crate::{joystick_source::joystick_loop, ui::GuiUpdate};
|
||||
|
||||
use super::perf_state::TrackerMetrics;
|
||||
use super::remote_video_processor::remote_video_loop;
|
||||
use super::{ApplicationEvent, ConnectionType};
|
||||
|
||||
|
@ -32,6 +33,7 @@ pub struct SocketState {
|
|||
#[derive(Debug)]
|
||||
pub struct CoordState<'a> {
|
||||
pub settings: Arc<RwLock<AppConfig>>,
|
||||
pub tracker_metrics: Arc<tokio::sync::Mutex<TrackerMetrics>>,
|
||||
|
||||
pub sck_outbound: Option<SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>>,
|
||||
pub remote_sources_state: Arc<SocketState>,
|
||||
|
@ -60,9 +62,11 @@ impl<'a> CoordState<'a> {
|
|||
rt: Handle,
|
||||
tracker_state: Arc<Mutex<TrackerState>>,
|
||||
settings: Arc<RwLock<AppConfig>>,
|
||||
tracker_header: Arc<std::sync::RwLock<String>>,
|
||||
) -> Self {
|
||||
let this = CoordState {
|
||||
settings,
|
||||
tracker_metrics: Arc::new(tokio::sync::Mutex::new(TrackerMetrics::new(tracker_header))),
|
||||
|
||||
sck_outbound: None,
|
||||
stay_alive_sck_recvr: Arc::new(AtomicBool::new(false)),
|
||||
|
@ -181,6 +185,7 @@ impl<'a> CoordState<'a> {
|
|||
self.to_mec.clone(),
|
||||
self.tracker_connection_state.clone(),
|
||||
self.tracker_state.clone(),
|
||||
self.tracker_metrics.clone(),
|
||||
self.rt.clone(),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ use tracing::{debug, error, info, instrument};
|
|||
mod coord_state;
|
||||
mod process_box_string;
|
||||
mod remote_video_processor;
|
||||
mod perf_state;
|
||||
|
||||
use crate::config::AppConfig;
|
||||
use crate::remote_sources::TrackerState;
|
||||
|
@ -56,12 +57,13 @@ pub async fn start_coordinator(
|
|||
runtime: Handle,
|
||||
tracker_state: Arc<Mutex<TrackerState>>,
|
||||
settings: Arc<RwLock<AppConfig>>,
|
||||
tracker_header: Arc<std::sync::RwLock<String>>,
|
||||
) {
|
||||
info!("Starting coordinator!");
|
||||
|
||||
let mec = pin!(mec);
|
||||
|
||||
let mut state = CoordState::new(mec, to_mec, to_gui, runtime, tracker_state, settings);
|
||||
let mut state = CoordState::new(mec, to_mec, to_gui, runtime, tracker_state, settings, tracker_header);
|
||||
|
||||
state
|
||||
.pipeline
|
||||
|
|
57
src/coordinator/perf_state.rs
Normal file
57
src/coordinator/perf_state.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use std::{sync, time::Duration, collections::VecDeque};
|
||||
|
||||
const MAX_RECORDED_TIMES: usize = 10;
|
||||
const DEGRADED_TRACKER_TIME: u128 = 100;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TrackerMetrics {
|
||||
pub header_text: sync::Arc<sync::RwLock<String>>,
|
||||
tracker_times: VecDeque<u128>,
|
||||
|
||||
}
|
||||
|
||||
impl TrackerMetrics {
|
||||
pub fn new(text_reference: sync::Arc<sync::RwLock<String>>) -> Self {
|
||||
TrackerMetrics {
|
||||
header_text: text_reference,
|
||||
|
||||
tracker_times: VecDeque::with_capacity(MAX_RECORDED_TIMES),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_times(&mut self) {
|
||||
for _ in 0..10 {
|
||||
self.tracker_times.pop_front();
|
||||
}
|
||||
self.insert_time(Duration::new(0, 0));
|
||||
}
|
||||
|
||||
pub fn insert_time(&mut self, new_measurement: Duration) {
|
||||
if self.tracker_times.len() > MAX_RECORDED_TIMES {
|
||||
let _ = self.tracker_times.pop_front();
|
||||
}
|
||||
self.tracker_times.push_back(new_measurement.as_millis());
|
||||
|
||||
let avg_time = self.tracker_times.iter().sum::<u128>() / self.tracker_times.len() as u128;
|
||||
|
||||
if avg_time == 0 {
|
||||
if let Ok(mut writer) = self.header_text.write() {
|
||||
writer.clear();
|
||||
writer.push_str(&format!("Status: Failed Avg Response: {} ms", avg_time.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
if avg_time > DEGRADED_TRACKER_TIME {
|
||||
if let Ok(mut writer) = self.header_text.write() {
|
||||
writer.clear();
|
||||
writer.push_str(&format!("Status: Degraded Avg Response: {} ms", avg_time.to_string()));
|
||||
}
|
||||
} else {
|
||||
if let Ok(mut writer) = self.header_text.write() {
|
||||
writer.clear();
|
||||
writer.push_str(&format!("Status: Nominal Avg Response: {} ms", avg_time.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ use tracing::{error, info, instrument};
|
|||
|
||||
use crate::remote_sources::TrackerState;
|
||||
|
||||
use super::{process_box_string::process_incoming_string, ApplicationEvent, SocketState};
|
||||
use super::{perf_state::TrackerMetrics, process_box_string::process_incoming_string, ApplicationEvent, SocketState};
|
||||
|
||||
#[instrument]
|
||||
pub async fn remote_video_loop(
|
||||
|
@ -27,12 +27,14 @@ pub async fn remote_video_loop(
|
|||
to_mec: Sender<ApplicationEvent>,
|
||||
socket_state: Arc<SocketState>,
|
||||
tracker_state: Arc<Mutex<TrackerState>>,
|
||||
tracker_metrics: Arc<tokio::sync::Mutex<TrackerMetrics>>,
|
||||
runtime: Handle,
|
||||
) {
|
||||
info!(
|
||||
"Starting remote tracker processing connection to: {}",
|
||||
conn_string
|
||||
);
|
||||
|
||||
let video_info =
|
||||
gstreamer_video::VideoInfo::builder(gstreamer_video::VideoFormat::Rgb, 640, 480)
|
||||
.build()
|
||||
|
@ -54,8 +56,11 @@ pub async fn remote_video_loop(
|
|||
tracker_state.clone(),
|
||||
socket_state.clone(),
|
||||
));
|
||||
|
||||
let mut last_iter: Instant;
|
||||
|
||||
loop {
|
||||
last_iter = Instant::now();
|
||||
// Do this in an encloser to not keep a lock on the appsink
|
||||
let video_frame = match {
|
||||
let appsnk = match appsink.lock() {
|
||||
|
@ -100,6 +105,12 @@ pub async fn remote_video_loop(
|
|||
info!("Shutting down remote video loop");
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
let mut tm = tracker_metrics.lock().await;
|
||||
tm.insert_time(Instant::now() - last_iter);
|
||||
}
|
||||
|
||||
// rate limit updates
|
||||
sleep_until(Instant::now() + Duration::from_millis(50)).await;
|
||||
}
|
||||
|
|
78
src/ui/liveview_panel.rs
Normal file
78
src/ui/liveview_panel.rs
Normal file
|
@ -0,0 +1,78 @@
|
|||
use gtk::{gdk::Paintable, prelude::BoxExt, AspectFrame, Box, DrawingArea, Label, Overlay, Picture};
|
||||
|
||||
|
||||
pub struct LiveViewPanel {
|
||||
top_level: gtk::Box,
|
||||
|
||||
pub tracker_status_label: Label,
|
||||
pub cam_status_label: Label,
|
||||
pub adjustment_label: Label,
|
||||
picture: Picture,
|
||||
overlay: Overlay,
|
||||
}
|
||||
|
||||
impl LiveViewPanel {
|
||||
pub fn new() -> Self {
|
||||
|
||||
let right_box = gtk::Box::builder()
|
||||
.orientation(gtk::Orientation::Vertical)
|
||||
.hexpand(true)
|
||||
.valign(gtk::Align::Center)
|
||||
.build();
|
||||
|
||||
let tracker_status_label = Label::builder()
|
||||
.label("No Status Yet".to_string())
|
||||
.can_focus(true)
|
||||
.css_classes(vec!["large-label"])
|
||||
.build();
|
||||
|
||||
// let conn_status_label = Label::new(Some(&"No Connection".to_string()));
|
||||
let cam_status_label = Label::builder()
|
||||
.label("No Connection".to_string())
|
||||
.can_focus(true)
|
||||
.build();
|
||||
|
||||
let adjustment_label = Label::builder()
|
||||
.label("X: 0 Y: )")
|
||||
.justify(gtk::Justification::Center)
|
||||
.css_classes(vec!["JoystickCurrent"])
|
||||
.build();
|
||||
|
||||
let webcam_picture = gtk::Picture::builder().can_focus(false).build();
|
||||
|
||||
let overlay_box = gtk::Overlay::builder().build();
|
||||
overlay_box.set_child(Some(&webcam_picture));
|
||||
|
||||
let aspect = AspectFrame::builder()
|
||||
.ratio(16.0 / 9.0)
|
||||
.obey_child(false)
|
||||
.child(&overlay_box)
|
||||
.build();
|
||||
|
||||
right_box.append(&tracker_status_label);
|
||||
right_box.append(&aspect);
|
||||
right_box.append(&cam_status_label);
|
||||
right_box.append(&adjustment_label);
|
||||
|
||||
LiveViewPanel {
|
||||
adjustment_label,
|
||||
tracker_status_label,
|
||||
cam_status_label,
|
||||
overlay: overlay_box,
|
||||
picture: webcam_picture,
|
||||
top_level: right_box,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_top_level(&self) -> &Box {
|
||||
&self.top_level
|
||||
}
|
||||
|
||||
pub fn set_drawable(&self, new_drawable: &DrawingArea) {
|
||||
self.overlay.add_overlay(new_drawable);
|
||||
}
|
||||
|
||||
pub fn set_paintable(&self, new_paintable: &Paintable) {
|
||||
self.picture.set_paintable(Some(new_paintable));
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ use std::time::Instant;
|
|||
use gtk::cairo::Context;
|
||||
use gtk::gdk::Paintable;
|
||||
use gtk::glib::clone;
|
||||
use gtk::{gio, glib, prelude::*, AspectFrame, CssProvider, Label};
|
||||
use gtk::{gio, glib, prelude::*, CssProvider};
|
||||
use gtk::{Application, ApplicationWindow};
|
||||
use log::{error, info};
|
||||
use tokio::runtime::Handle;
|
||||
|
@ -17,8 +17,10 @@ use crate::remote_sources::TrackerState;
|
|||
|
||||
mod control_panel;
|
||||
mod settings_modal;
|
||||
mod liveview_panel;
|
||||
|
||||
use control_panel::ControlPanel;
|
||||
use liveview_panel::LiveViewPanel;
|
||||
|
||||
pub enum GuiUpdate {
|
||||
SocketDisconnected,
|
||||
|
@ -115,12 +117,6 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
|
|||
.orientation(gtk::Orientation::Vertical)
|
||||
.build();
|
||||
|
||||
let right_box = gtk::Box::builder()
|
||||
.orientation(gtk::Orientation::Vertical)
|
||||
.hexpand(true)
|
||||
.valign(gtk::Align::Center)
|
||||
.build();
|
||||
|
||||
// Create a window
|
||||
let window = ApplicationWindow::builder()
|
||||
.application(app)
|
||||
|
@ -156,6 +152,8 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
|
|||
}));
|
||||
|
||||
let coord_config = config.clone();
|
||||
|
||||
let tracker_header = Arc::new(std::sync::RwLock::new(String::new()));
|
||||
|
||||
runtime.spawn(start_coordinator(
|
||||
mec,
|
||||
|
@ -164,42 +162,19 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
|
|||
runtime.clone(),
|
||||
tracker_state.clone(),
|
||||
coord_config,
|
||||
tracker_header.clone(),
|
||||
));
|
||||
|
||||
// let conn_status_label = Label::new(Some(&"No Connection".to_string()));
|
||||
let conn_status_label = Label::builder()
|
||||
.label("No Connection".to_string())
|
||||
.can_focus(true)
|
||||
.build();
|
||||
|
||||
let control_panel = Arc::new(ControlPanel::new(tracker_state.clone()));
|
||||
control_panel.connect_button_callbacks(to_mec.clone());
|
||||
|
||||
let axis_label = Label::builder()
|
||||
.label("X: 0 Y: )")
|
||||
.justify(gtk::Justification::Center)
|
||||
.css_classes(vec!["JoystickCurrent"])
|
||||
.build();
|
||||
|
||||
// left_box.append(&conn_status_label);
|
||||
left_box.append(control_panel.get_top_level());
|
||||
|
||||
main_box.append(&left_box);
|
||||
|
||||
let webcam_picture = gtk::Picture::builder().can_focus(false).build();
|
||||
|
||||
let overlay_box = gtk::Overlay::builder().build();
|
||||
let aspect = AspectFrame::builder()
|
||||
.ratio(16.0 / 9.0)
|
||||
.obey_child(false)
|
||||
.child(&overlay_box)
|
||||
.build();
|
||||
|
||||
right_box.append(&aspect);
|
||||
right_box.append(&conn_status_label);
|
||||
right_box.append(&axis_label);
|
||||
|
||||
main_box.append(&right_box);
|
||||
let liveview_panel = LiveViewPanel::new();
|
||||
main_box.append(liveview_panel.get_top_level());
|
||||
|
||||
let drawable = gtk::DrawingArea::builder().build();
|
||||
|
||||
|
@ -208,8 +183,7 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
|
|||
draw_boxes(width, height, &ctx, &drawable_ts);
|
||||
});
|
||||
|
||||
overlay_box.set_child(Some(&webcam_picture));
|
||||
overlay_box.add_overlay(&drawable);
|
||||
liveview_panel.set_drawable(&drawable);
|
||||
|
||||
let items = control_panel.items.clone();
|
||||
let id_label = control_panel.current_id.clone();
|
||||
|
@ -248,42 +222,45 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
|
|||
);
|
||||
|
||||
glib::spawn_future_local(
|
||||
glib::clone!(@weak axis_label, @weak conn_status_label, @weak drawable => async move {
|
||||
glib::clone!(@weak drawable => async move {
|
||||
while let Ok(d) = gui_recv.recv().await {
|
||||
drawable.queue_draw();
|
||||
if let Ok(reader) = tracker_header.read() {
|
||||
liveview_panel.tracker_status_label.set_text(reader.as_str());
|
||||
}
|
||||
match d {
|
||||
GuiUpdate::MoveEvent(msg) => {
|
||||
axis_label.set_text(
|
||||
liveview_panel.adjustment_label.set_text(
|
||||
format!("X: {:>4} Y: {:>4}", msg.x, msg.y).as_str()
|
||||
);
|
||||
}
|
||||
GuiUpdate::SocketConnected => {
|
||||
control_panel.connection_buttons.camera_connection.set_sensitive(true);
|
||||
control_panel.connection_buttons.camera_connection.set_label("Press to Disconnect");
|
||||
conn_status_label.set_label("Connected");
|
||||
liveview_panel.cam_status_label.set_label("Connected");
|
||||
|
||||
conn_status_label.set_css_classes(&["YesConnection"]);
|
||||
liveview_panel.cam_status_label.set_css_classes(&["YesConnection"]);
|
||||
|
||||
},
|
||||
GuiUpdate::SocketConnecting => {
|
||||
control_panel.connection_buttons.camera_connection.set_sensitive(false);
|
||||
control_panel.connection_buttons.camera_connection.set_label("Please wait");
|
||||
conn_status_label.set_label("Connecting");
|
||||
liveview_panel.cam_status_label.set_label("Connecting");
|
||||
|
||||
conn_status_label.set_css_classes(&["LoadingConnection"]);
|
||||
liveview_panel.cam_status_label.set_css_classes(&["LoadingConnection"]);
|
||||
|
||||
},
|
||||
GuiUpdate::SocketDisconnected => {
|
||||
control_panel.connection_buttons.camera_connection.set_sensitive(true);
|
||||
control_panel.connection_buttons.camera_connection.set_label("Press to Connect to Camera");
|
||||
conn_status_label.set_label("Not Connected to Camera");
|
||||
liveview_panel.cam_status_label.set_label("Not Connected to Camera");
|
||||
|
||||
conn_status_label.set_css_classes(&["NoConnection"]);
|
||||
liveview_panel.cam_status_label.set_css_classes(&["NoConnection"]);
|
||||
}
|
||||
GuiUpdate::UpdatePaintable(sink) => {
|
||||
let paintable = sink.property::<Paintable>("paintable");
|
||||
|
||||
webcam_picture.set_paintable(Some(&paintable));
|
||||
liveview_panel.set_paintable(&paintable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,10 @@ label.current-id {
|
|||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
label.large-label {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
entry {
|
||||
font-size: 16pt;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue