better state updates; moved ui elements; moved broke coord mod into 2 files

This commit is contained in:
Nickiel12 2024-05-23 20:23:13 -06:00
parent 1fb0a279c8
commit 8164eca473
7 changed files with 340 additions and 284 deletions

View file

@ -0,0 +1,250 @@
use std::pin::Pin;
use std::sync::Mutex;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use std::time::Instant;
use async_channel::{Receiver, Sender};
use futures_util::{stream::SplitSink, SinkExt, StreamExt};
use tokio::net::TcpStream;
use tokio::runtime::Handle;
use tokio::sync::RwLock;
use tokio_tungstenite::{connect_async, tungstenite::Message, MaybeTlsStream, WebSocketStream};
use tracing::{debug, error, info, instrument};
use crate::config::AppConfig;
use crate::coordinator::socket_listen;
use crate::remote_sources::TrackerState;
use crate::{gstreamer_pipeline, remote_sources};
use crate::{joystick_source::joystick_loop, ui::GuiUpdate};
use super::remote_video_processor::remote_video_loop;
use super::{ApplicationEvent, ConnectionType};
#[derive(Debug)]
pub struct SocketState {
pub is_connected: AtomicBool,
pub stay_connected: AtomicBool,
}
#[derive(Debug)]
pub struct CoordState<'a> {
pub settings: Arc<RwLock<AppConfig>>,
pub sck_outbound: Option<SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>>,
pub remote_sources_state: Arc<SocketState>,
pub stay_alive_sck_recvr: Arc<AtomicBool>,
pub joystick_loop_alive: Arc<AtomicBool>,
pub current_priority: ConnectionType,
pub last_update_of_priority: Instant,
pub mec: Pin<&'a mut Receiver<ApplicationEvent>>,
pub to_mec: Sender<ApplicationEvent>,
pub to_gui: Sender<GuiUpdate>,
pub rt: Handle,
pub pipeline: gstreamer_pipeline::WebcamPipeline,
pub tracker_state: Arc<Mutex<TrackerState>>,
pub tracker_connection_state: Arc<SocketState>,
}
impl<'a> CoordState<'a> {
pub fn new(
mec: Pin<&'a mut Receiver<ApplicationEvent>>,
to_mec: Sender<ApplicationEvent>,
to_gui: Sender<GuiUpdate>,
rt: Handle,
tracker_state: Arc<Mutex<TrackerState>>,
settings: Arc<RwLock<AppConfig>>,
) -> Self {
let this = CoordState {
settings,
sck_outbound: None,
stay_alive_sck_recvr: Arc::new(AtomicBool::new(false)),
remote_sources_state: Arc::new(SocketState {
stay_connected: AtomicBool::new(false),
is_connected: AtomicBool::new(false),
}),
joystick_loop_alive: Arc::new(AtomicBool::new(false)),
current_priority: ConnectionType::Local,
last_update_of_priority: Instant::now(),
mec,
to_mec,
to_gui,
rt,
pipeline: gstreamer_pipeline::WebcamPipeline::new().unwrap(),
tracker_state,
tracker_connection_state: Arc::new(SocketState {
stay_connected: AtomicBool::new(false),
is_connected: AtomicBool::new(false),
}),
};
this
}
#[instrument]
pub async fn socket_send(&mut self, message: Message) {
if let Some(mut socket) = self.sck_outbound.take() {
if let Err(e) = socket.send(message).await {
error!("There was an error sending to the socket: {:#?}", e);
} else {
self.sck_outbound = Some(socket);
}
}
}
pub fn socket_connected(&self) -> bool {
self.sck_outbound.is_some()
}
pub async fn socket_start(&mut self) {
self.stay_alive_sck_recvr.store(true, Ordering::SeqCst);
if let Err(e) = self.to_gui.send(GuiUpdate::SocketConnecting).await {
error!("Cannot send message to gui thread: {e}");
}
let conn_string: String = {
let read_settings = self.settings.read().await;
format!(
"ws://{}:{}",
read_settings.camera_ip,
read_settings.camera_port.to_string()
)
};
match connect_async(conn_string).await {
Ok((val, _)) => {
info!("Socket connection to camera made successfully");
let (outbound, inbound) = val.split();
self.rt.spawn(socket_listen(
self.to_mec.clone(),
self.stay_alive_sck_recvr.clone(),
inbound,
));
self.sck_outbound = Some(outbound);
if let Err(e) = self.to_gui.send(GuiUpdate::SocketConnected).await {
error!("Cannot send message to gui thread: {e}");
}
}
Err(_) => {
error!("Couldn't connect to URL!");
if let Err(e) = self.to_gui.send(GuiUpdate::SocketDisconnected).await {
error!("Cannot send message to gui thread: {e}");
}
}
}
}
pub async fn socket_close(&mut self) {
debug!("Cleaning up camera socket state");
if let Some(mut socket) = self.sck_outbound.take() {
if let Err(e) = socket.close().await {
error!("Couldn't close socket during shutdown: {e}");
}
}
if let Err(e) = self.to_gui.send(GuiUpdate::SocketDisconnected).await {
error!("Cannot send message to gui thread: {e}");
}
self.stay_alive_sck_recvr.store(false, Ordering::SeqCst);
}
pub async fn start_video_loop(&mut self) {
let conn_string: String = {
let read_settings = self.settings.read().await;
format!(
"ws://{}:{}",
read_settings.tracker_ip,
read_settings.tracker_port.to_string()
)
};
self.rt.spawn(remote_video_loop(
conn_string,
self.pipeline.sink_frame.clone(),
self.to_mec.clone(),
self.tracker_connection_state.clone(),
self.tracker_state.clone(),
self.rt.clone(),
));
}
pub async fn check_states(&mut self) {
// This one needs to always be alive, and restart after a crash
if !self.joystick_loop_alive.load(Ordering::SeqCst) {
info!("Restarting joystick loop");
self.rt.spawn(joystick_loop(
self.to_mec.clone(),
self.joystick_loop_alive.clone(),
));
}
// If tracker state is not connected, and it should be
if !self
.tracker_connection_state
.is_connected
.load(Ordering::SeqCst)
&& self
.tracker_connection_state
.stay_connected
.load(Ordering::SeqCst)
{
self.start_video_loop().await;
}
//
if self
.remote_sources_state
.stay_connected
.load(Ordering::SeqCst)
&& !self
.remote_sources_state
.is_connected
.load(Ordering::SeqCst)
{
info!("Restarting socket server");
self.rt.spawn(remote_sources::start_socketserver(
self.rt.clone(),
self.to_mec.clone(),
self.remote_sources_state.clone(),
self.tracker_state.clone(),
));
}
// if stay alive is false, and there is a connection, kill it
if !self.stay_alive_sck_recvr.load(Ordering::SeqCst) && self.sck_outbound.is_some() {
self.socket_close().await;
}
}
pub async fn close(&mut self) {
info!("closing coord state");
self.tracker_connection_state
.stay_connected
.store(false, Ordering::SeqCst);
self.socket_close().await;
self.joystick_loop_alive.store(false, Ordering::SeqCst);
self.remote_sources_state
.stay_connected
.store(false, Ordering::SeqCst);
self.to_gui.close();
self.mec.close();
}
}

View file

@ -1,4 +1,4 @@
use std::pin::{pin, Pin};
use std::pin::pin;
use std::sync::Mutex;
use std::sync::{
atomic::{AtomicBool, Ordering},
@ -7,27 +7,23 @@ use std::sync::{
use std::time::{Duration, Instant};
use async_channel::{Receiver, Sender};
use futures_util::{
stream::{SplitSink, SplitStream},
SinkExt, StreamExt,
};
use futures_util::{stream::SplitStream, StreamExt};
use gstreamer::prelude::ElementExt;
use gstreamer::State;
use tokio::net::TcpStream;
use tokio::runtime::Handle;
use tokio::sync::RwLock;
use tokio_tungstenite::{connect_async, tungstenite::Message, MaybeTlsStream, WebSocketStream};
use tokio_tungstenite::{tungstenite::Message, MaybeTlsStream, WebSocketStream};
use tracing::{debug, error, info, instrument};
mod coord_state;
mod process_box_string;
mod remote_video_processor;
use crate::config::AppConfig;
use crate::remote_sources::TrackerState;
use crate::{gstreamer_pipeline, remote_sources};
use crate::{joystick_source::joystick_loop, ui::GuiUpdate};
use self::remote_video_processor::remote_video_loop;
use crate::ui::GuiUpdate;
pub use coord_state::{CoordState, SocketState};
const PRIORITY_TIMEOUT: Duration = Duration::from_secs(2);
@ -45,230 +41,12 @@ pub enum ConnectionType {
}
pub enum ApplicationEvent {
StartCameraSocket,
CameraConnectionPress,
SocketMessage(Message),
MoveEvent(MoveEvent, ConnectionType),
EnableAutomatic(bool),
}
#[derive(Debug)]
struct SocketState {
pub is_connected: AtomicBool,
pub stay_connected: AtomicBool,
}
#[derive(Debug)]
struct CoordState<'a> {
pub settings: Arc<RwLock<AppConfig>>,
pub sck_outbound: Option<SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>>,
pub keep_remote_sources_alive: Arc<AtomicBool>,
pub sck_alive_recvr: Arc<AtomicBool>,
pub joystick_loop_alive: Arc<AtomicBool>,
pub current_priority: ConnectionType,
pub last_update_of_priority: Instant,
pub mec: Pin<&'a mut Receiver<ApplicationEvent>>,
pub to_mec: Sender<ApplicationEvent>,
pub to_gui: Sender<GuiUpdate>,
pub rt: Handle,
pub pipeline: gstreamer_pipeline::WebcamPipeline,
pub tracker_state: Arc<Mutex<TrackerState>>,
pub tracker_connection_state: Arc<SocketState>,
}
impl<'a> CoordState<'a> {
pub fn new(
mec: Pin<&'a mut Receiver<ApplicationEvent>>,
to_mec: Sender<ApplicationEvent>,
to_gui: Sender<GuiUpdate>,
rt: Handle,
tracker_state: Arc<Mutex<TrackerState>>,
settings: Arc<RwLock<AppConfig>>,
) -> Self {
let this = CoordState {
settings,
sck_outbound: None,
sck_alive_recvr: Arc::new(AtomicBool::new(false)),
keep_remote_sources_alive: Arc::new(AtomicBool::new(false)),
joystick_loop_alive: Arc::new(AtomicBool::new(false)),
current_priority: ConnectionType::Local,
last_update_of_priority: Instant::now(),
mec,
to_mec,
to_gui,
rt,
pipeline: gstreamer_pipeline::WebcamPipeline::new().unwrap(),
tracker_state,
tracker_connection_state: Arc::new(SocketState {
stay_connected: AtomicBool::new(false),
is_connected: AtomicBool::new(false),
}),
};
this
}
#[instrument]
pub async fn socket_send(&mut self, message: Message) {
if let Some(mut socket) = self.sck_outbound.take() {
if let Err(e) = socket.send(message).await {
error!("There was an error sending to the socket: {:#?}", e);
} else {
self.sck_outbound = Some(socket);
}
}
}
pub fn socket_connected(&self) -> bool {
self.sck_outbound.is_some()
}
async fn socket_start(&mut self) {
debug!("Starting socket");
self.sck_alive_recvr.store(true, Ordering::SeqCst);
if let Err(e) = self.to_gui.send(GuiUpdate::SocketConnecting).await {
error!("Cannot send message to gui thread: {e}");
}
let conn_string: String = {
let read_settings = self.settings.read().await;
format!(
"ws://{}:{}",
read_settings.camera_ip,
read_settings.camera_port.to_string()
)
};
match connect_async(conn_string).await {
Ok((val, _)) => {
info!("Socket connection to camera made successfully");
let (outbound, inbound) = val.split();
self.rt.spawn(socket_listen(
self.to_mec.clone(),
self.sck_alive_recvr.clone(),
inbound,
));
self.sck_outbound = Some(outbound);
if let Err(e) = self.to_gui.send(GuiUpdate::SocketConnected).await {
error!("Cannot send message to gui thread: {e}");
}
}
Err(_) => {
error!("Couldn't connect to URL!");
if let Err(e) = self.to_gui.send(GuiUpdate::SocketDisconnected).await {
error!("Cannot send message to gui thread: {e}");
}
}
}
}
pub async fn socket_close(&mut self) {
if let Some(mut socket) = self.sck_outbound.take() {
if let Err(e) = socket.close().await {
error!("Couldnt' close socket during shutdown: {e}");
}
}
if let Err(e) = self.to_gui.send(GuiUpdate::SocketDisconnected).await {
error!("Cannot send message to gui thread: {e}");
}
self.sck_alive_recvr.store(false, Ordering::SeqCst);
}
pub async fn start_video_loop(&mut self) {
let conn_string: String = {
let read_settings = self.settings.read().await;
format!(
"ws://{}:{}",
read_settings.tracker_ip,
read_settings.tracker_port.to_string()
)
};
self.rt.spawn(remote_video_loop(
conn_string,
self.pipeline.sink_frame.clone(),
self.to_mec.clone(),
self.tracker_connection_state.clone(),
self.tracker_state.clone(),
self.rt.clone(),
));
}
pub async fn check_states(&mut self) {
if !self.joystick_loop_alive.load(Ordering::SeqCst) {
info!("Restarting joystick loop");
self.rt.spawn(joystick_loop(
self.to_mec.clone(),
self.joystick_loop_alive.clone(),
));
}
if !self
.tracker_connection_state
.is_connected
.load(Ordering::SeqCst)
{
if self
.tracker_connection_state
.stay_connected
.load(Ordering::SeqCst)
{
self.start_video_loop().await;
}
}
if !self.keep_remote_sources_alive.load(Ordering::SeqCst) {
info!("Restarting socket server");
self.keep_remote_sources_alive.store(true, Ordering::SeqCst);
self.rt.spawn(remote_sources::start_socketserver(
self.rt.clone(),
self.to_mec.clone(),
self.keep_remote_sources_alive.clone(),
self.tracker_state.clone(),
));
}
if !self.sck_alive_recvr.load(Ordering::SeqCst) || self.sck_outbound.is_none() {
self.socket_close().await;
if let Err(e) = self.to_gui.send(GuiUpdate::SocketDisconnected).await {
error!("Cannot send message to gui thread: {e}");
}
} else if let Err(e) = self.to_gui.send(GuiUpdate::SocketConnected).await {
error!("Cannot send message to gui thread: {e}");
self.close().await;
}
}
pub async fn close(&mut self) {
info!("closing coord state");
self.tracker_connection_state
.stay_connected
.store(false, Ordering::SeqCst);
self.socket_close().await;
self.joystick_loop_alive.store(false, Ordering::SeqCst);
self.keep_remote_sources_alive
.store(false, Ordering::SeqCst);
self.to_gui.close();
self.mec.close();
}
}
#[instrument]
pub async fn start_coordinator(
// Main_Event_Channel
@ -307,16 +85,14 @@ pub async fn start_coordinator(
state.check_states().await;
match msg {
ApplicationEvent::StartCameraSocket => {
state.socket_start().await;
ApplicationEvent::CameraConnectionPress => {
if state.socket_connected() {
state.socket_close().await;
} else {
state.socket_start().await;
}
}
ApplicationEvent::SocketMessage(socket_message) => {
if let Err(e) = state.to_gui.send(GuiUpdate::SocketConnected).await {
error!("Could not send to gui thread! Closing coordinator: {e}");
state.close().await;
break;
}
state.socket_send(socket_message).await;
}
ApplicationEvent::EnableAutomatic(do_enable) => {
@ -365,16 +141,17 @@ pub async fn start_coordinator(
.set_state(State::Null)
.expect("Could not set pipeline state to playing");
state.close().await;
info!("Stopping Coordinator");
}
#[instrument]
async fn socket_listen(
mec: Sender<ApplicationEvent>,
socket_recv_is_alive: Arc<AtomicBool>,
stay_alive_sck_recvr: Arc<AtomicBool>,
mut reader: SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>,
) {
if socket_recv_is_alive.load(std::sync::atomic::Ordering::SeqCst) {
if stay_alive_sck_recvr.load(std::sync::atomic::Ordering::SeqCst) {
while let Some(msg) = reader.next().await {
match msg {
Ok(val) => {
@ -389,7 +166,15 @@ async fn socket_listen(
}
}
socket_recv_is_alive.store(false, Ordering::SeqCst);
// setting this will call the internal state.socket_close next check states
stay_alive_sck_recvr.store(false, Ordering::SeqCst);
}
info!("Closed socket reading thread");
// If the mec is closed or full, then this socket should be closing anyways
// as there was most likely an unrecoverable error
let _ = mec
.send(ApplicationEvent::SocketMessage(Message::Close(None)))
.await;
debug!("Closed socket reading thread");
}

View file

@ -25,14 +25,17 @@ fn main() -> glib::ExitCode {
{
let file_appender = tracing_appender::rolling::daily(".", "joystick-log");
let (non_blocking, _gaurd) = tracing_appender::non_blocking(file_appender);
tracing_subscriber::fmt().with_writer(non_blocking).init();
tracing_subscriber::fmt()
.with_writer(non_blocking)
.with_max_level(tracing::Level::DEBUG)
.init();
}
#[cfg(debug_assertions)]
{
tracing_subscriber::fmt()
// .compact()
.pretty()
.with_max_level(tracing::Level::TRACE)
.with_max_level(tracing::Level::DEBUG)
.init();
}

View file

@ -1,9 +1,6 @@
use std::{
net::SocketAddr,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
sync::{atomic::Ordering, Arc, Mutex},
time::Instant,
};
@ -24,7 +21,7 @@ use tracing::instrument;
mod remote_source;
use crate::{
coordinator::{ApplicationEvent, ConnectionType},
coordinator::{ApplicationEvent, ConnectionType, SocketState},
ui::NormalizedBoxCoords,
};
@ -43,14 +40,19 @@ pub struct TrackerState {
pub async fn start_socketserver(
rt: Handle,
mec: Sender<ApplicationEvent>,
stay_alive: Arc<AtomicBool>,
connection_state: Arc<SocketState>,
tracker_state: Arc<Mutex<TrackerState>>,
) {
let addr = "127.0.0.1:9002";
let listener = TcpListener::bind(&addr).await.expect("Can't listen");
info!("Listening on: {}", addr);
connection_state.is_connected.store(true, Ordering::SeqCst);
while let Ok((stream, _)) = listener.accept().await {
if !connection_state.stay_connected.load(Ordering::SeqCst) {
break;
}
let peer = stream
.peer_addr()
.expect("connected streams should have a peer address");
@ -64,7 +66,7 @@ pub async fn start_socketserver(
));
}
stay_alive.store(false, Ordering::SeqCst);
connection_state.is_connected.store(false, Ordering::SeqCst);
}
#[instrument]

View file

@ -1,14 +1,15 @@
use std::sync::{Arc, Mutex};
use async_channel::Sender;
use gtk::{
glib::{self, object::CastNone}, prelude::{
BoxExt, ButtonExt, Cast, GObjectPropertyExpressionExt, ListItemExt,
ToggleButtonExt,
}, Box, Button, Expander, Label, ListItem, ListView, ScrolledWindow, SignalListItemFactory, SingleSelection, StringList, StringObject, ToggleButton, Widget
glib::{self, object::CastNone},
prelude::{
BoxExt, ButtonExt, Cast, GObjectPropertyExpressionExt, ListItemExt, ToggleButtonExt,
},
Box, Button, Expander, Label, ListItem, ListView, ScrolledWindow, SignalListItemFactory,
SingleSelection, StringList, StringObject, ToggleButton, Widget,
};
use tracing::{error, span, Level, event};
use tracing::{error, event, span, Level};
#[cfg(feature = "tracker-state-debug")]
use tracing::debug;
@ -28,7 +29,7 @@ pub struct ControlPanel {
#[derive(Debug)]
pub struct ExpanderMenu {
pub top_level: Expander,
pub camera_connection: Button,
pub tracker_enable_toggle: ToggleButton,
}
@ -121,20 +122,22 @@ impl ControlPanel {
}
pub fn connect_button_callbacks(&self, to_mec: Sender<ApplicationEvent>) {
self.connection_buttons.tracker_enable_toggle.connect_clicked(glib::clone!(@strong to_mec => move |button| {
let span = span!(Level::TRACE, "tracker_enable_toggle callback");
let _enter = span.enter();
if let Err(e) =
to_mec.send_blocking(ApplicationEvent::EnableAutomatic(button.is_active()))
{
event!(Level::ERROR, error = ?e, "Could not send message to the MEC");
}
}));
self.connection_buttons
.tracker_enable_toggle
.connect_clicked(glib::clone!(@strong to_mec => move |button| {
let span = span!(Level::TRACE, "tracker_enable_toggle callback");
let _enter = span.enter();
if let Err(e) =
to_mec.send_blocking(ApplicationEvent::EnableAutomatic(button.is_active()))
{
event!(Level::ERROR, error = ?e, "Could not send message to the MEC");
}
}));
self.connection_buttons.camera_connection.connect_clicked(glib::clone!(@strong to_mec => move |_button| {
let span = span!(Level::TRACE, "camera_connection callback");
let _enter = span.enter();
match to_mec.try_send(ApplicationEvent::StartCameraSocket) {
match to_mec.try_send(ApplicationEvent::CameraConnectionPress) {
Ok(_) => {},
Err(async_channel::TrySendError::Closed(_)) => panic!("Coordinator MEC is closed. Unrecoverable error."),
Err(e) => event!(Level::ERROR, error = ?e, message = "There was an error sending to the MEC"),
@ -158,7 +161,7 @@ impl ExpanderMenu {
.expanded(true)
.label("Connections")
.build();
let camera_connection = Button::builder()
.label("Connect to Camera")
.margin_top(12)
@ -171,7 +174,7 @@ impl ExpanderMenu {
content_box.append(&camera_connection);
content_box.append(&tracker_enable_toggle);
ExpanderMenu {
top_level: expander,

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, ListBox};
use gtk::{gio, glib, prelude::*, AspectFrame, CssProvider, Label};
use gtk::{Application, ApplicationWindow};
use log::{error, info};
use tokio::runtime::Handle;
@ -15,8 +15,8 @@ use crate::config::AppConfig;
use crate::coordinator::{start_coordinator, ApplicationEvent, MoveEvent};
use crate::remote_sources::TrackerState;
mod settings_modal;
mod control_panel;
mod settings_modal;
use control_panel::ControlPanel;
@ -110,7 +110,16 @@ pub fn on_activate(app: &Application) {
pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Handle) {
let main_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
let left_box = ListBox::builder().width_request(200).build();
let left_box = gtk::Box::builder()
.width_request(300)
.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()
@ -172,9 +181,8 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
.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(&axis_label);
main_box.append(&left_box);
@ -186,7 +194,12 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
.obey_child(false)
.child(&overlay_box)
.build();
main_box.append(&aspect);
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();
@ -204,8 +217,8 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
glib::timeout_add_seconds_local(
1,
glib::clone!(@strong items, @strong id_label => move || {
#[cfg(feature = "tracker-state-debug")]
debug!("Getting lock on tracker state for checking identity boxes");
#[cfg(feature = "tracker-state-debug")]
debug!("Getting lock on tracker state for checking identity boxes");
// don't update the stringlist until after letting go of the tracker state
// due to async interweaving causing a mutex deadlock
@ -233,7 +246,7 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
glib::ControlFlow::Continue
}),
);
glib::spawn_future_local(
glib::clone!(@weak axis_label, @weak conn_status_label, @weak drawable => async move {
while let Ok(d) = gui_recv.recv().await {
@ -253,15 +266,15 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
},
GuiUpdate::SocketConnecting => {
control_panel.connection_buttons.camera_connection.set_sensitive(true);
control_panel.connection_buttons.camera_connection.set_label("Press to Cancel");
conn_status_label.set_label("Connected");
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");
conn_status_label.set_css_classes(&["LoadingConnection"]);
},
GuiUpdate::SocketDisconnected => {
control_panel.connection_buttons.camera_connection.set_sensitive(false);
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");

View file

@ -150,7 +150,7 @@ impl ConnectionsModal {
}
// FBI!!! OPEN UP!!!!
}
window.close();
info!("Please nicholas, add a non-crashing parse");