added untested tracker state metrics

This commit is contained in:
Nickiel12 2024-05-26 20:59:19 -06:00
parent 8164eca473
commit 81a4f04d6b
7 changed files with 180 additions and 46 deletions

View file

@ -20,6 +20,7 @@ use crate::remote_sources::TrackerState;
use crate::{gstreamer_pipeline, remote_sources}; use crate::{gstreamer_pipeline, remote_sources};
use crate::{joystick_source::joystick_loop, ui::GuiUpdate}; use crate::{joystick_source::joystick_loop, ui::GuiUpdate};
use super::perf_state::TrackerMetrics;
use super::remote_video_processor::remote_video_loop; use super::remote_video_processor::remote_video_loop;
use super::{ApplicationEvent, ConnectionType}; use super::{ApplicationEvent, ConnectionType};
@ -32,6 +33,7 @@ pub struct SocketState {
#[derive(Debug)] #[derive(Debug)]
pub struct CoordState<'a> { pub struct CoordState<'a> {
pub settings: Arc<RwLock<AppConfig>>, pub settings: Arc<RwLock<AppConfig>>,
pub tracker_metrics: Arc<tokio::sync::Mutex<TrackerMetrics>>,
pub sck_outbound: Option<SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>>, pub sck_outbound: Option<SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>>,
pub remote_sources_state: Arc<SocketState>, pub remote_sources_state: Arc<SocketState>,
@ -60,9 +62,11 @@ impl<'a> CoordState<'a> {
rt: Handle, rt: Handle,
tracker_state: Arc<Mutex<TrackerState>>, tracker_state: Arc<Mutex<TrackerState>>,
settings: Arc<RwLock<AppConfig>>, settings: Arc<RwLock<AppConfig>>,
tracker_header: Arc<std::sync::RwLock<String>>,
) -> Self { ) -> Self {
let this = CoordState { let this = CoordState {
settings, settings,
tracker_metrics: Arc::new(tokio::sync::Mutex::new(TrackerMetrics::new(tracker_header))),
sck_outbound: None, sck_outbound: None,
stay_alive_sck_recvr: Arc::new(AtomicBool::new(false)), stay_alive_sck_recvr: Arc::new(AtomicBool::new(false)),
@ -181,6 +185,7 @@ impl<'a> CoordState<'a> {
self.to_mec.clone(), self.to_mec.clone(),
self.tracker_connection_state.clone(), self.tracker_connection_state.clone(),
self.tracker_state.clone(), self.tracker_state.clone(),
self.tracker_metrics.clone(),
self.rt.clone(), self.rt.clone(),
)); ));
} }

View file

@ -19,6 +19,7 @@ use tracing::{debug, error, info, instrument};
mod coord_state; mod coord_state;
mod process_box_string; mod process_box_string;
mod remote_video_processor; mod remote_video_processor;
mod perf_state;
use crate::config::AppConfig; use crate::config::AppConfig;
use crate::remote_sources::TrackerState; use crate::remote_sources::TrackerState;
@ -56,12 +57,13 @@ pub async fn start_coordinator(
runtime: Handle, runtime: Handle,
tracker_state: Arc<Mutex<TrackerState>>, tracker_state: Arc<Mutex<TrackerState>>,
settings: Arc<RwLock<AppConfig>>, settings: Arc<RwLock<AppConfig>>,
tracker_header: Arc<std::sync::RwLock<String>>,
) { ) {
info!("Starting coordinator!"); info!("Starting coordinator!");
let mec = pin!(mec); 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 state
.pipeline .pipeline

View 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()));
}
}
}
}

View file

@ -18,7 +18,7 @@ use tracing::{error, info, instrument};
use crate::remote_sources::TrackerState; 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] #[instrument]
pub async fn remote_video_loop( pub async fn remote_video_loop(
@ -27,12 +27,14 @@ pub async fn remote_video_loop(
to_mec: Sender<ApplicationEvent>, to_mec: Sender<ApplicationEvent>,
socket_state: Arc<SocketState>, socket_state: Arc<SocketState>,
tracker_state: Arc<Mutex<TrackerState>>, tracker_state: Arc<Mutex<TrackerState>>,
tracker_metrics: Arc<tokio::sync::Mutex<TrackerMetrics>>,
runtime: Handle, runtime: Handle,
) { ) {
info!( info!(
"Starting remote tracker processing connection to: {}", "Starting remote tracker processing connection to: {}",
conn_string conn_string
); );
let video_info = let video_info =
gstreamer_video::VideoInfo::builder(gstreamer_video::VideoFormat::Rgb, 640, 480) gstreamer_video::VideoInfo::builder(gstreamer_video::VideoFormat::Rgb, 640, 480)
.build() .build()
@ -54,8 +56,11 @@ pub async fn remote_video_loop(
tracker_state.clone(), tracker_state.clone(),
socket_state.clone(), socket_state.clone(),
)); ));
let mut last_iter: Instant;
loop { loop {
last_iter = Instant::now();
// Do this in an encloser to not keep a lock on the appsink // Do this in an encloser to not keep a lock on the appsink
let video_frame = match { let video_frame = match {
let appsnk = match appsink.lock() { let appsnk = match appsink.lock() {
@ -100,6 +105,12 @@ pub async fn remote_video_loop(
info!("Shutting down remote video loop"); info!("Shutting down remote video loop");
break; break;
} }
{
let mut tm = tracker_metrics.lock().await;
tm.insert_time(Instant::now() - last_iter);
}
// rate limit updates // rate limit updates
sleep_until(Instant::now() + Duration::from_millis(50)).await; sleep_until(Instant::now() + Duration::from_millis(50)).await;
} }

78
src/ui/liveview_panel.rs Normal file
View 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));
}
}

View file

@ -5,7 +5,7 @@ use std::time::Instant;
use gtk::cairo::Context; use gtk::cairo::Context;
use gtk::gdk::Paintable; use gtk::gdk::Paintable;
use gtk::glib::clone; use gtk::glib::clone;
use gtk::{gio, glib, prelude::*, AspectFrame, CssProvider, Label}; use gtk::{gio, glib, prelude::*, CssProvider};
use gtk::{Application, ApplicationWindow}; use gtk::{Application, ApplicationWindow};
use log::{error, info}; use log::{error, info};
use tokio::runtime::Handle; use tokio::runtime::Handle;
@ -17,8 +17,10 @@ use crate::remote_sources::TrackerState;
mod control_panel; mod control_panel;
mod settings_modal; mod settings_modal;
mod liveview_panel;
use control_panel::ControlPanel; use control_panel::ControlPanel;
use liveview_panel::LiveViewPanel;
pub enum GuiUpdate { pub enum GuiUpdate {
SocketDisconnected, SocketDisconnected,
@ -115,12 +117,6 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
.orientation(gtk::Orientation::Vertical) .orientation(gtk::Orientation::Vertical)
.build(); .build();
let right_box = gtk::Box::builder()
.orientation(gtk::Orientation::Vertical)
.hexpand(true)
.valign(gtk::Align::Center)
.build();
// Create a window // Create a window
let window = ApplicationWindow::builder() let window = ApplicationWindow::builder()
.application(app) .application(app)
@ -156,6 +152,8 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
})); }));
let coord_config = config.clone(); let coord_config = config.clone();
let tracker_header = Arc::new(std::sync::RwLock::new(String::new()));
runtime.spawn(start_coordinator( runtime.spawn(start_coordinator(
mec, mec,
@ -164,42 +162,19 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
runtime.clone(), runtime.clone(),
tracker_state.clone(), tracker_state.clone(),
coord_config, 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())); let control_panel = Arc::new(ControlPanel::new(tracker_state.clone()));
control_panel.connect_button_callbacks(to_mec.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(&conn_status_label);
left_box.append(control_panel.get_top_level()); left_box.append(control_panel.get_top_level());
main_box.append(&left_box); main_box.append(&left_box);
let webcam_picture = gtk::Picture::builder().can_focus(false).build(); let liveview_panel = LiveViewPanel::new();
main_box.append(liveview_panel.get_top_level());
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 drawable = gtk::DrawingArea::builder().build(); 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); draw_boxes(width, height, &ctx, &drawable_ts);
}); });
overlay_box.set_child(Some(&webcam_picture)); liveview_panel.set_drawable(&drawable);
overlay_box.add_overlay(&drawable);
let items = control_panel.items.clone(); let items = control_panel.items.clone();
let id_label = control_panel.current_id.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::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 { while let Ok(d) = gui_recv.recv().await {
drawable.queue_draw(); drawable.queue_draw();
if let Ok(reader) = tracker_header.read() {
liveview_panel.tracker_status_label.set_text(reader.as_str());
}
match d { match d {
GuiUpdate::MoveEvent(msg) => { GuiUpdate::MoveEvent(msg) => {
axis_label.set_text( liveview_panel.adjustment_label.set_text(
format!("X: {:>4} Y: {:>4}", msg.x, msg.y).as_str() format!("X: {:>4} Y: {:>4}", msg.x, msg.y).as_str()
); );
} }
GuiUpdate::SocketConnected => { GuiUpdate::SocketConnected => {
control_panel.connection_buttons.camera_connection.set_sensitive(true); control_panel.connection_buttons.camera_connection.set_sensitive(true);
control_panel.connection_buttons.camera_connection.set_label("Press to Disconnect"); 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 => { GuiUpdate::SocketConnecting => {
control_panel.connection_buttons.camera_connection.set_sensitive(false); control_panel.connection_buttons.camera_connection.set_sensitive(false);
control_panel.connection_buttons.camera_connection.set_label("Please wait"); 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 => { GuiUpdate::SocketDisconnected => {
control_panel.connection_buttons.camera_connection.set_sensitive(true); control_panel.connection_buttons.camera_connection.set_sensitive(true);
control_panel.connection_buttons.camera_connection.set_label("Press to Connect to Camera"); 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) => { GuiUpdate::UpdatePaintable(sink) => {
let paintable = sink.property::<Paintable>("paintable"); let paintable = sink.property::<Paintable>("paintable");
webcam_picture.set_paintable(Some(&paintable)); liveview_panel.set_paintable(&paintable);
} }
} }
} }

View file

@ -15,6 +15,10 @@ label.current-id {
margin-bottom: 4px; margin-bottom: 4px;
} }
label.large-label {
font-size: 10pt;
}
entry { entry {
font-size: 16pt; font-size: 16pt;
} }