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::{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(),
));
}

View file

@ -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

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 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
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::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);
}
}
}

View file

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