Compare commits

...

2 commits

Author SHA1 Message Date
Nickiel12
8924d2ab8f added restart limits and feedback 2024-05-27 11:12:06 -07:00
Nickiel12
a37f5ed266 added updating tracker status 2024-05-27 10:50:11 -07:00
5 changed files with 159 additions and 73 deletions

View file

@ -17,9 +17,9 @@ use tokio_tungstenite::{tungstenite::Message, MaybeTlsStream, WebSocketStream};
use tracing::{debug, error, info, instrument}; use tracing::{debug, error, info, instrument};
mod coord_state; mod coord_state;
mod perf_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;
@ -63,7 +63,15 @@ pub async fn start_coordinator(
let mec = pin!(mec); let mec = pin!(mec);
let mut state = CoordState::new(mec, to_mec, to_gui, runtime, tracker_state, settings, tracker_header); let mut state = CoordState::new(
mec,
to_mec,
to_gui,
runtime,
tracker_state,
settings,
tracker_header,
);
state state
.pipeline .pipeline

View file

@ -1,4 +1,4 @@
use std::{sync, time::Duration, collections::VecDeque}; use std::{collections::VecDeque, sync, time::Duration};
const MAX_RECORDED_TIMES: usize = 10; const MAX_RECORDED_TIMES: usize = 10;
const DEGRADED_TRACKER_TIME: u128 = 100; const DEGRADED_TRACKER_TIME: u128 = 100;
@ -7,51 +7,76 @@ const DEGRADED_TRACKER_TIME: u128 = 100;
pub struct TrackerMetrics { pub struct TrackerMetrics {
pub header_text: sync::Arc<sync::RwLock<String>>, pub header_text: sync::Arc<sync::RwLock<String>>,
tracker_times: VecDeque<u128>, tracker_times: VecDeque<u128>,
} }
impl TrackerMetrics { impl TrackerMetrics {
pub fn new(text_reference: sync::Arc<sync::RwLock<String>>) -> Self { pub fn new(text_reference: sync::Arc<sync::RwLock<String>>) -> Self {
TrackerMetrics { let mut ret = TrackerMetrics {
header_text: text_reference, header_text: text_reference,
tracker_times: VecDeque::with_capacity(MAX_RECORDED_TIMES), tracker_times: VecDeque::with_capacity(MAX_RECORDED_TIMES),
};
ret.clear_times();
ret
}
pub fn starting_connection(&mut self, fail_count: Option<usize>) {
self.clear_times();
if let Ok(mut writer) = self.header_text.write() {
writer.clear();
match fail_count {
None => writer.push_str("Status: Connecting ..."),
Some(v) => writer.push_str(&format!("Status: Attempt {}/5", v)),
}
} }
} }
pub fn clear_times(&mut self) { pub fn clear_times(&mut self) {
for _ in 0..10 { for _ in 0..10 {
self.tracker_times.pop_front(); self.tracker_times.pop_front();
} }
self.insert_time(Duration::new(0, 0)); self.insert_time(Duration::new(0, 0));
if let Ok(mut writer) = self.header_text.write() {
writer.clear();
writer.push_str("Status: Disconnected");
}
} }
pub fn insert_time(&mut self, new_measurement: Duration) { pub fn insert_time(&mut self, new_measurement: Duration) {
if self.tracker_times.len() > MAX_RECORDED_TIMES { if self.tracker_times.len() > MAX_RECORDED_TIMES {
let _ = self.tracker_times.pop_front(); let _ = self.tracker_times.pop_front();
} }
self.tracker_times.push_back(new_measurement.as_millis()); self.tracker_times.push_back(new_measurement.as_millis());
let avg_time = self.tracker_times.iter().sum::<u128>() / self.tracker_times.len() as u128; let avg_time = self.tracker_times.iter().sum::<u128>() / self.tracker_times.len() as u128;
if avg_time == 0 { if avg_time == 0 {
if let Ok(mut writer) = self.header_text.write() { if let Ok(mut writer) = self.header_text.write() {
writer.clear(); writer.clear();
writer.push_str(&format!("Status: Failed Avg Response: {} ms", avg_time.to_string())); writer.push_str(&format!(
"Status: Failed Avg Response: {} ms",
avg_time.to_string()
));
} }
} }
if avg_time > DEGRADED_TRACKER_TIME { if avg_time > DEGRADED_TRACKER_TIME {
if let Ok(mut writer) = self.header_text.write() { if let Ok(mut writer) = self.header_text.write() {
writer.clear(); writer.clear();
writer.push_str(&format!("Status: Degraded Avg Response: {} ms", avg_time.to_string())); writer.push_str(&format!(
"Status: Degraded Avg Response: {} ms",
avg_time.to_string()
));
} }
} else { } else {
if let Ok(mut writer) = self.header_text.write() { if let Ok(mut writer) = self.header_text.write() {
writer.clear(); writer.clear();
writer.push_str(&format!("Status: Nominal Avg Response: {} ms", avg_time.to_string())); writer.push_str(&format!(
"Status: Nominal Avg Response: {} ms",
avg_time.to_string()
));
} }
} }
} }
} }

View file

@ -18,7 +18,10 @@ use tracing::{error, info, instrument};
use crate::remote_sources::TrackerState; use crate::remote_sources::TrackerState;
use super::{perf_state::TrackerMetrics, 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(
@ -34,16 +37,33 @@ pub async fn remote_video_loop(
"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()
.expect("Could not build video info!"); .expect("Could not build video info!");
let mut fail_count = 0;
{
let mut tm = tracker_metrics.lock().await;
tm.starting_connection(None);
}
loop { loop {
socket_state.is_connected.store(true, Ordering::SeqCst); socket_state.is_connected.store(true, Ordering::SeqCst);
match connect_async(&conn_string).await { match connect_async(&conn_string).await {
Err(e) => { Err(e) => {
fail_count += 1;
{
let mut tm = tracker_metrics.lock().await;
tm.starting_connection(Some(fail_count));
}
if fail_count >= 5 {
break;
}
error!("Could not connect to remote video loop! Trying again in 1 seconds: {e}"); error!("Could not connect to remote video loop! Trying again in 1 seconds: {e}");
sleep_until(Instant::now() + Duration::from_secs(1)).await; sleep_until(Instant::now() + Duration::from_secs(1)).await;
} }
@ -56,7 +76,7 @@ pub async fn remote_video_loop(
tracker_state.clone(), tracker_state.clone(),
socket_state.clone(), socket_state.clone(),
)); ));
let mut last_iter: Instant; let mut last_iter: Instant;
loop { loop {
@ -105,7 +125,7 @@ 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; let mut tm = tracker_metrics.lock().await;
tm.insert_time(Instant::now() - last_iter); tm.insert_time(Instant::now() - last_iter);
@ -117,10 +137,15 @@ pub async fn remote_video_loop(
} }
} }
if !socket_state.stay_connected.load(Ordering::SeqCst) { if !socket_state.stay_connected.load(Ordering::SeqCst) {
info!("Shutting down remote video loop");
break; break;
} }
} }
info!("Shutting down remote video loop");
{
let mut tm = tracker_metrics.lock().await;
tm.clear_times();
}
socket_state.is_connected.store(false, Ordering::SeqCst); socket_state.is_connected.store(false, Ordering::SeqCst);
} }
@ -166,7 +191,7 @@ async fn listen_to_messages(
} }
} }
info!( info!(
"Stopping tracker connection listen with keep alive: {}", "Stopping tracker listen connection with keep alive: {}",
socket_state.stay_connected.load(Ordering::SeqCst) socket_state.stay_connected.load(Ordering::SeqCst)
); );
} }

View file

@ -1,9 +1,10 @@
use gtk::{gdk::Paintable, prelude::BoxExt, AspectFrame, Box, DrawingArea, Label, Overlay, Picture}; use gtk::{
gdk::Paintable, prelude::BoxExt, AspectFrame, Box, DrawingArea, Label, Overlay, Picture,
};
pub struct LiveViewPanel { pub struct LiveViewPanel {
top_level: gtk::Box, top_level: gtk::Box,
pub tracker_status_label: Label, pub tracker_status_label: Label,
pub cam_status_label: Label, pub cam_status_label: Label,
pub adjustment_label: Label, pub adjustment_label: Label,
@ -13,22 +14,22 @@ pub struct LiveViewPanel {
impl LiveViewPanel { impl LiveViewPanel {
pub fn new() -> Self { pub fn new() -> Self {
let right_box = gtk::Box::builder() let right_box = gtk::Box::builder()
.orientation(gtk::Orientation::Vertical) .orientation(gtk::Orientation::Vertical)
.hexpand(true) .hexpand(true)
.valign(gtk::Align::Center) .valign(gtk::Align::Center)
.build(); .build();
let tracker_status_label = Label::builder() let tracker_status_label = Label::builder()
.label("No Status Yet".to_string()) .label("No Status Yet".to_string())
.can_focus(true) .can_focus(true)
.css_classes(vec!["large-label"]) .css_classes(vec!["large-label", "NoConnection"])
.build(); .build();
// let conn_status_label = Label::new(Some(&"No Connection".to_string())); // let conn_status_label = Label::new(Some(&"No Connection".to_string()));
let cam_status_label = Label::builder() let cam_status_label = Label::builder()
.label("No Connection".to_string()) .label("No Connection".to_string())
.css_classes(vec!["NoConnection"])
.can_focus(true) .can_focus(true)
.build(); .build();
@ -53,7 +54,7 @@ impl LiveViewPanel {
right_box.append(&aspect); right_box.append(&aspect);
right_box.append(&cam_status_label); right_box.append(&cam_status_label);
right_box.append(&adjustment_label); right_box.append(&adjustment_label);
LiveViewPanel { LiveViewPanel {
adjustment_label, adjustment_label,
tracker_status_label, tracker_status_label,
@ -63,16 +64,16 @@ impl LiveViewPanel {
top_level: right_box, top_level: right_box,
} }
} }
pub fn get_top_level(&self) -> &Box { pub fn get_top_level(&self) -> &Box {
&self.top_level &self.top_level
} }
pub fn set_drawable(&self, new_drawable: &DrawingArea) { pub fn set_drawable(&self, new_drawable: &DrawingArea) {
self.overlay.add_overlay(new_drawable); self.overlay.add_overlay(new_drawable);
} }
pub fn set_paintable(&self, new_paintable: &Paintable) { pub fn set_paintable(&self, new_paintable: &Paintable) {
self.picture.set_paintable(Some(new_paintable)); self.picture.set_paintable(Some(new_paintable));
} }
} }

View file

@ -16,8 +16,8 @@ use crate::coordinator::{start_coordinator, ApplicationEvent, MoveEvent};
use crate::remote_sources::TrackerState; use crate::remote_sources::TrackerState;
mod control_panel; mod control_panel;
mod settings_modal;
mod liveview_panel; mod liveview_panel;
mod settings_modal;
use control_panel::ControlPanel; use control_panel::ControlPanel;
use liveview_panel::LiveViewPanel; use liveview_panel::LiveViewPanel;
@ -152,7 +152,7 @@ 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())); let tracker_header = Arc::new(std::sync::RwLock::new(String::new()));
runtime.spawn(start_coordinator( runtime.spawn(start_coordinator(
@ -165,7 +165,7 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
tracker_header.clone(), tracker_header.clone(),
)); ));
let control_panel = Arc::new(ControlPanel::new(tracker_state.clone())); let control_panel = ControlPanel::new(tracker_state.clone());
control_panel.connect_button_callbacks(to_mec.clone()); control_panel.connect_button_callbacks(to_mec.clone());
// left_box.append(&conn_status_label); // left_box.append(&conn_status_label);
@ -221,52 +221,79 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
}), }),
); );
glib::spawn_future_local( let tracker_status_label = liveview_panel.tracker_status_label.clone();
glib::clone!(@weak drawable => async move { let tracker_enable_toggle = control_panel
while let Ok(d) = gui_recv.recv().await { .connection_buttons
drawable.queue_draw(); .tracker_enable_toggle
if let Ok(reader) = tracker_header.read() { .clone();
liveview_panel.tracker_status_label.set_text(reader.as_str());
glib::timeout_add_seconds_local(1, move || {
if let Ok(reader) = tracker_header.read() {
tracker_status_label.set_text(reader.as_str());
if reader.contains("Failed") || reader.contains("Disconnected") {
tracker_status_label.set_css_classes(&["NoConnection"]);
tracker_enable_toggle.set_label("Press to Connect Tracker");
tracker_enable_toggle.set_active(false);
} else if reader.contains("Degraded") || reader.contains("Connecting") {
tracker_status_label.set_css_classes(&["LoadingConnection"]);
tracker_enable_toggle.set_label("Press to Connect");
tracker_enable_toggle.set_active(false);
} else if reader.contains("Nominal") {
tracker_status_label.set_css_classes(&["YesConnection"]);
tracker_enable_toggle.set_label("Press to Disconnect");
tracker_enable_toggle.set_active(true);
}
glib::ControlFlow::Continue
} else {
error!("Couldn't get rwlock on metrics");
glib::ControlFlow::Break
}
});
glib::spawn_future_local(glib::clone!(@weak drawable => async move {
while let Ok(d) = gui_recv.recv().await {
drawable.queue_draw();
match d {
GuiUpdate::MoveEvent(msg) => {
liveview_panel.adjustment_label.set_text(
format!("X: {:>4} Y: {:>4}", msg.x, msg.y).as_str()
);
} }
match d { GuiUpdate::SocketConnected => {
GuiUpdate::MoveEvent(msg) => { control_panel.connection_buttons.camera_connection.set_sensitive(true);
liveview_panel.adjustment_label.set_text( control_panel.connection_buttons.camera_connection.set_label("Press to Disconnect");
format!("X: {:>4} Y: {:>4}", msg.x, msg.y).as_str() liveview_panel.cam_status_label.set_label("Connected");
);
}
GuiUpdate::SocketConnected => {
control_panel.connection_buttons.camera_connection.set_sensitive(true);
control_panel.connection_buttons.camera_connection.set_label("Press to Disconnect");
liveview_panel.cam_status_label.set_label("Connected");
liveview_panel.cam_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");
liveview_panel.cam_status_label.set_label("Connecting"); liveview_panel.cam_status_label.set_label("Connecting");
liveview_panel.cam_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");
liveview_panel.cam_status_label.set_label("Not Connected to Camera"); liveview_panel.cam_status_label.set_label("Not Connected to Camera");
liveview_panel.cam_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");
liveview_panel.set_paintable(&paintable); liveview_panel.set_paintable(&paintable);
}
} }
} }
info!("Closing update loop"); }
}), info!("Closing update loop");
); }));
window.connect_close_request(move |_| glib::Propagation::Proceed); window.connect_close_request(move |_| glib::Propagation::Proceed);