From e1786a320e9883a8f2a25f936d4a64238e857ee0 Mon Sep 17 00:00:00 2001 From: John-Michael Burke <43353147+john-michaelburke@users.noreply.github.com> Date: Fri, 17 Sep 2021 10:33:18 -0700 Subject: [PATCH] Headless runner for console[CPP-318]. (#123) * Headless runner for console. * Update console_backend/Cargo.toml Co-authored-by: Jason Mobarak * Log messages. Co-authored-by: Jason Mobarak --- Cargo.lock | 11 +- Makefile.toml | 12 + console_backend/Cargo.toml | 8 + console_backend/benches/cpu_benches.rs | 3 +- console_backend/src/advanced_ins_tab.rs | 3 +- .../src/advanced_magnetometer_tab.rs | 3 +- .../src/advanced_spectrum_analyzer_tab.rs | 3 +- console_backend/src/baseline_tab.rs | 4 +- console_backend/src/bin/headless-console.rs | 62 ++ console_backend/src/cli_options.rs | 52 +- console_backend/src/connection.rs | 1 + console_backend/src/errors.rs | 7 +- console_backend/src/fusion_status_flags.rs | 4 +- console_backend/src/lib.rs | 4 +- console_backend/src/log_panel.rs | 17 +- console_backend/src/main_tab.rs | 12 +- console_backend/src/observation_tab.rs | 3 +- console_backend/src/process_messages.rs | 6 +- console_backend/src/server.rs | 353 +------- console_backend/src/server_recv_thread.rs | 310 +++++++ console_backend/src/settings_tab.rs | 3 +- console_backend/src/shared_state.rs | 794 +++++++++++++++++ console_backend/src/solution_tab.rs | 5 +- console_backend/src/solution_velocity_tab.rs | 3 +- console_backend/src/status_bar.rs | 3 +- console_backend/src/tracking_signals_tab.rs | 14 +- console_backend/src/types.rs | 802 +----------------- console_backend/src/update_tab.rs | 3 +- console_backend/src/utils.rs | 2 +- console_backend/tests/mem_benches.rs | 3 +- 30 files changed, 1344 insertions(+), 1166 deletions(-) create mode 100644 console_backend/src/bin/headless-console.rs create mode 100644 console_backend/src/server_recv_thread.rs create mode 100644 console_backend/src/shared_state.rs diff --git a/Cargo.lock b/Cargo.lock index a4e176532..b988c6d43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -411,6 +411,7 @@ dependencies = [ "serial_test", "serialport", "slotmap", + "snap", "strum", "strum_macros", "sysinfo", @@ -853,9 +854,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.101" +version = "0.2.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" +checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103" [[package]] name = "libloading" @@ -1744,6 +1745,12 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +[[package]] +name = "snap" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45456094d1983e2ee2a18fdfebce3189fa451699d0502cb8e3b49dba5ba41451" + [[package]] name = "spin" version = "0.5.2" diff --git a/Makefile.toml b/Makefile.toml index 97eb4deaf..44c2f21e3 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -152,6 +152,18 @@ dependencies = [ ] run_task = "start-console" +[tasks.headless-run] +command = "cargo" +args = [ + "run", + "--bin", + "headless-console", + "--features", + "headless-console", + "--no-default-features", + "${@}", +] + [tasks.store-version] script_runner = "@shell" script = ''' diff --git a/console_backend/Cargo.toml b/console_backend/Cargo.toml index d4331456e..8dabcabec 100644 --- a/console_backend/Cargo.toml +++ b/console_backend/Cargo.toml @@ -40,6 +40,7 @@ minreq = { version = "2.4.2", features = ["https"] } regex = { version = "1.5.4" } semver = { version = "1" } rust-ini = "0.17.0" +snap = { version = "1", optional = true } [dependencies.sbp] git = "https://github.com/swift-nav/libsbp.git" @@ -80,8 +81,15 @@ path = "src/bin/fft_monitor.rs" bench = false required-features = ["fft"] +[[bin]] +name = "headless-console" +path = "src/bin/headless-console.rs" +bench = false +required-features = ["headless-console"] + [features] default = ["pyo3"] benches = [] tests = [] fft = ["serde-pickle"] +headless-console = ["snap"] diff --git a/console_backend/benches/cpu_benches.rs b/console_backend/benches/cpu_benches.rs index c7c73718b..2316b10e4 100644 --- a/console_backend/benches/cpu_benches.rs +++ b/console_backend/benches/cpu_benches.rs @@ -14,7 +14,8 @@ extern crate console_backend; use console_backend::{ connection::Connection, process_messages, - types::{ClientSender, RealtimeDelay, SharedState}, + shared_state::SharedState, + types::{ClientSender, RealtimeDelay}, }; const BENCH_FILEPATH: &str = "./tests/data/piksi-relay.sbp"; diff --git a/console_backend/src/advanced_ins_tab.rs b/console_backend/src/advanced_ins_tab.rs index acc08526a..dab334efa 100644 --- a/console_backend/src/advanced_ins_tab.rs +++ b/console_backend/src/advanced_ins_tab.rs @@ -6,7 +6,8 @@ use capnp::message::Builder; use crate::constants::*; use crate::errors::GET_MUT_OBJECT_FAILURE; use crate::fusion_status_flags::FusionStatusFlags; -use crate::types::{CapnProtoSender, Deque, SharedState}; +use crate::shared_state::SharedState; +use crate::types::{CapnProtoSender, Deque}; use crate::utils::serialize_capnproto_builder; /// AdvancedInsTab struct. diff --git a/console_backend/src/advanced_magnetometer_tab.rs b/console_backend/src/advanced_magnetometer_tab.rs index 98e585a5e..f71f6b637 100644 --- a/console_backend/src/advanced_magnetometer_tab.rs +++ b/console_backend/src/advanced_magnetometer_tab.rs @@ -4,7 +4,8 @@ use capnp::message::Builder; use crate::constants::{MAGNETOMETER_Y_AXIS_PADDING_MULTIPLIER, NUM_POINTS}; use crate::errors::GET_MUT_OBJECT_FAILURE; -use crate::types::{CapnProtoSender, Deque, SharedState}; +use crate::shared_state::SharedState; +use crate::types::{CapnProtoSender, Deque}; use crate::utils::serialize_capnproto_builder; /// AdvancedMagnetometerTab struct. diff --git a/console_backend/src/advanced_spectrum_analyzer_tab.rs b/console_backend/src/advanced_spectrum_analyzer_tab.rs index d6074e53a..656f4bc74 100644 --- a/console_backend/src/advanced_spectrum_analyzer_tab.rs +++ b/console_backend/src/advanced_spectrum_analyzer_tab.rs @@ -3,7 +3,8 @@ use log::error; use crate::constants::{AMPLITUDES, CHANNELS, FREQUENCIES}; use crate::errors::SHARED_STATE_LOCK_MUTEX_FAILURE; use crate::fft_monitor::FftMonitor; -use crate::types::{CapnProtoSender, SharedState, Specan}; +use crate::shared_state::SharedState; +use crate::types::{CapnProtoSender, Specan}; use crate::utils::serialize_capnproto_builder; use capnp::message::Builder; diff --git a/console_backend/src/baseline_tab.rs b/console_backend/src/baseline_tab.rs index 84101b07c..a74b4cfa4 100644 --- a/console_backend/src/baseline_tab.rs +++ b/console_backend/src/baseline_tab.rs @@ -12,9 +12,9 @@ use crate::constants::*; use crate::date_conv::*; use crate::output::BaselineLog; use crate::piksi_tools_constants::EMPTY_STR; +use crate::shared_state::SharedState; use crate::types::{ - BaselineNED, CapnProtoSender, Deque, GnssModes, GpsTime, MsgSender, Result, SharedState, - UtcDateTime, + BaselineNED, CapnProtoSender, Deque, GnssModes, GpsTime, MsgSender, Result, UtcDateTime, }; use crate::utils::*; diff --git a/console_backend/src/bin/headless-console.rs b/console_backend/src/bin/headless-console.rs new file mode 100644 index 000000000..1fd23649b --- /dev/null +++ b/console_backend/src/bin/headless-console.rs @@ -0,0 +1,62 @@ +use anyhow::Result; +use chrono::prelude::*; +use console_backend::{ + cli_options::{handle_cli, CliOptions}, + connection::ConnectionState, + log_panel::setup_logging, + server_recv_thread::server_recv_thread, + shared_state::SharedState, + types::ClientSender, + utils::{refresh_loggingbar, refresh_navbar}, +}; +use crossbeam::channel; +use std::fs::File; +use std::io::{prelude::*, BufWriter}; + +fn main() -> Result<()> { + let opt = CliOptions::from_filtered_cli(); + if opt.input.is_none() { + eprintln!( + r#" +Running in headless mode only command line options work. +Help: +./headless-console --help +Usage: +./headless-console tcp piksi-relay-bb9f2b10e53143f4a816a11884e679cf.ce.swiftnav.com --port=55555 +"# + ); + return Ok(()); + } + let (client_send_, client_recv) = channel::unbounded::>(); + let (_server_send, server_recv) = channel::unbounded::>(); + let client_send = ClientSender::new(client_send_); + setup_logging(client_send.clone(), true); + let shared_state = SharedState::new(); + let connection_state = ConnectionState::new(client_send.clone(), shared_state.clone()); + handle_cli(opt, &connection_state, shared_state.clone()); + refresh_navbar(&mut client_send.clone(), shared_state.clone()); + refresh_loggingbar(&mut client_send.clone(), shared_state.clone()); + server_recv_thread(connection_state, client_send, server_recv, shared_state); + + let local: DateTime = Local::now(); + let mut filename = format!("headless-console-{}.data", local); + filename.retain(|c| !c.is_whitespace()); + let file_out = File::create(filename)?; + let buf_out = BufWriter::new(file_out); + let mut msg_count: usize = 0; + let mut file_out = snap::write::FrameEncoder::new(buf_out); + while let Ok(msg) = client_recv.recv() { + match file_out.write_all(&msg) { + Ok(_) => { + msg_count += 1; + if msg_count % 100 == 0 { + println!("Messages received: {}", msg_count); + } + } + Err(err) => { + eprintln!("{}", err); + } + } + } + Ok(()) +} diff --git a/console_backend/src/cli_options.rs b/console_backend/src/cli_options.rs index f7428d87b..738134663 100644 --- a/console_backend/src/cli_options.rs +++ b/console_backend/src/cli_options.rs @@ -7,11 +7,14 @@ use std::{ use strum::VariantNames; use crate::constants::{AVAILABLE_BAUDRATES, AVAILABLE_REFRESH_RATES}; +use crate::errors::{CONVERT_TO_STR_FAILURE, SHARED_STATE_LOCK_MUTEX_FAILURE}; use crate::log_panel::LogLevel; -use crate::types::FlowControl; +use crate::output::CsvLogging; +use crate::shared_state::SharedState; +use crate::types::{FlowControl, RealtimeDelay}; use crate::{ common_constants::{SbpLogging, Tabs}, - connection::Connection, + connection::{Connection, ConnectionState}, }; #[derive(Debug)] @@ -233,3 +236,48 @@ fn is_baudrate(br: &str) -> Result<(), String> { AVAILABLE_BAUDRATES )) } + +/// Start connections based on CLI options. +/// +/// # Parameters +/// - `opt`: CLI Options to start specific connection type. +/// - `connection_state`: The Server state to start a specific connection. +/// - `client_send`: Client Sender channel for communication from backend to frontend. +/// - `shared_state`: The shared state for validating another connection is not already running. +pub fn handle_cli(opt: CliOptions, connection_state: &ConnectionState, shared_state: SharedState) { + if let Some(opt_input) = opt.input { + match opt_input { + Input::Tcp { host, port } => { + connection_state.connect_to_host(host, port); + } + Input::File { file_in } => { + let filename = file_in.display().to_string(); + connection_state.connect_to_file(filename, RealtimeDelay::On, opt.exit_after); + } + Input::Serial { + serialport, + baudrate, + flow_control, + } => { + let serialport = serialport.display().to_string(); + connection_state.connect_to_serial(serialport, baudrate, flow_control); + } + } + } + if let Some(folder) = opt.dirname { + shared_state.set_logging_directory(PathBuf::from(folder)); + } + let log_level = if let Some(log_level_) = opt.log_level { + (*log_level_).clone() + } else { + LogLevel::INFO + }; + shared_state.set_log_level(log_level); + let mut shared_data = shared_state.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).logging_bar.csv_logging = CsvLogging::from(opt.csv_log); + if let Some(sbp_log) = opt.sbp_log { + (*shared_data).logging_bar.sbp_logging = + SbpLogging::from_str(&sbp_log.to_string()).expect(CONVERT_TO_STR_FAILURE); + } + log::logger().flush(); +} diff --git a/console_backend/src/connection.rs b/console_backend/src/connection.rs index 982ca9c11..e156e8e62 100644 --- a/console_backend/src/connection.rs +++ b/console_backend/src/connection.rs @@ -1,6 +1,7 @@ use crate::constants::*; use crate::errors::*; use crate::process_messages::process_messages; +use crate::shared_state::SharedState; use crate::types::*; use anyhow::anyhow; use crossbeam::channel::{unbounded, Receiver, Sender}; diff --git a/console_backend/src/errors.rs b/console_backend/src/errors.rs index e61be5136..b7651a8d9 100644 --- a/console_backend/src/errors.rs +++ b/console_backend/src/errors.rs @@ -1,11 +1,10 @@ pub(crate) const HEARTBEAT_LOCK_MUTEX_FAILURE: &str = "unable to lock heartbeat mutex"; -pub(crate) const SHARED_STATE_LOCK_MUTEX_FAILURE: &str = "unable to lock shared_state mutex"; +pub const SHARED_STATE_LOCK_MUTEX_FAILURE: &str = "unable to lock shared_state mutex"; pub(crate) const UPDATE_STATUS_LOCK_MUTEX_FAILURE: &str = "unable to lock update status mutex"; pub(crate) const CAP_N_PROTO_SERIALIZATION_FAILURE: &str = "unable to serialize capnproto message"; #[allow(dead_code)] -pub(crate) const CAP_N_PROTO_DESERIALIZATION_FAILURE: &str = - "unable to deserialize capnproto message"; -pub(crate) const CONVERT_TO_STR_FAILURE: &str = "error converting to str"; +pub const CAP_N_PROTO_DESERIALIZATION_FAILURE: &str = "unable to deserialize capnproto message"; +pub const CONVERT_TO_STR_FAILURE: &str = "error converting to str"; pub(crate) const GET_MUT_OBJECT_FAILURE: &str = "error trying to get mut object"; pub(crate) const UNABLE_TO_STOP_TIMER_THREAD_FAILURE: &str = "unable to kill running timer thread"; pub(crate) const UNABLE_TO_SEND_INS_UPDATE_FAILURE: &str = "unable to send an ins status update"; diff --git a/console_backend/src/fusion_status_flags.rs b/console_backend/src/fusion_status_flags.rs index f9d01baa0..02da07ab4 100644 --- a/console_backend/src/fusion_status_flags.rs +++ b/console_backend/src/fusion_status_flags.rs @@ -14,8 +14,8 @@ use crate::errors::{ THREAD_JOIN_FAILURE, UNABLE_TO_SEND_INS_UPDATE_FAILURE, UNABLE_TO_STOP_TIMER_THREAD_FAILURE, UPDATE_STATUS_LOCK_MUTEX_FAILURE, }; -use crate::types::ArcBool; -use crate::types::{CapnProtoSender, SharedState}; +use crate::shared_state::SharedState; +use crate::types::{ArcBool, CapnProtoSender}; use crate::utils::serialize_capnproto_builder; const STATUS_PERIOD: f64 = 1.0; diff --git a/console_backend/src/lib.rs b/console_backend/src/lib.rs index ff3e630c3..d8f38bc5e 100644 --- a/console_backend/src/lib.rs +++ b/console_backend/src/lib.rs @@ -25,7 +25,9 @@ pub mod process_messages; #[cfg(not(test))] #[cfg(all(not(feature = "benches"), not(feature = "tests"), feature = "pyo3"))] pub mod server; +pub mod server_recv_thread; pub mod settings_tab; +pub mod shared_state; pub mod solution_tab; pub mod solution_velocity_tab; pub mod status_bar; @@ -62,7 +64,7 @@ struct Tabs<'link, S: types::CapnProtoSender> { impl<'link, S: types::CapnProtoSender> Tabs<'link, S> { fn new( - shared_state: types::SharedState, + shared_state: shared_state::SharedState, client_sender: S, msg_sender: types::MsgSender, link: sbp::link::Link<'link, ()>, diff --git a/console_backend/src/log_panel.rs b/console_backend/src/log_panel.rs index 8a40c8282..51b694de2 100644 --- a/console_backend/src/log_panel.rs +++ b/console_backend/src/log_panel.rs @@ -6,7 +6,7 @@ use capnp::message::Builder; use crate::common_constants as cc; use crate::constants::LOG_WRITER_BUFFER_MESSAGE_COUNT; use crate::errors::CONSOLE_LOG_JSON_TO_STRING_FAILURE; -use crate::types::*; +use crate::types::{CapnProtoSender, ClientSender}; use crate::utils::serialize_capnproto_builder; use async_logger::Writer; @@ -96,8 +96,8 @@ pub fn handle_log_msg(msg: MsgLog) { } } -pub fn setup_logging(client_sender: ClientSender) { - let log_panel = LogPanelWriter::new(client_sender); +pub fn setup_logging(client_sender: ClientSender, debug: bool) { + let log_panel = LogPanelWriter::new(client_sender, debug); let logger = Logger::builder() .buf_size(LOG_WRITER_BUFFER_MESSAGE_COUNT) .formatter(splitable_log_formatter) @@ -111,11 +111,15 @@ pub fn setup_logging(client_sender: ClientSender) { #[derive(Debug)] pub struct LogPanelWriter { pub client_sender: S, + pub debug: bool, } impl LogPanelWriter { - pub fn new(client_sender: S) -> LogPanelWriter { - LogPanelWriter { client_sender } + pub fn new(client_sender: S, debug: bool) -> LogPanelWriter { + LogPanelWriter { + client_sender, + debug, + } } } @@ -132,6 +136,9 @@ impl Writer> for LogPanelWriter { let mut entries = log_update.init_entries(slice.len() as u32); for (idx, item) in slice.iter().enumerate() { + if self.debug { + eprintln!("{}", item); + } let mut entry = entries.reborrow().get(idx as u32); entry.set_line(&**item); diff --git a/console_backend/src/main_tab.rs b/console_backend/src/main_tab.rs index 7daed7b02..bd40be2d7 100644 --- a/console_backend/src/main_tab.rs +++ b/console_backend/src/main_tab.rs @@ -5,9 +5,13 @@ use log::{debug, error}; use sbp::{messages::SBP, time::GpsTime}; use crate::common_constants::SbpLogging; -use crate::constants::*; -use crate::output::*; -use crate::types::*; +use crate::constants::{ + BASELINE_TIME_STR_FILEPATH, POS_LLH_TIME_STR_FILEPATH, SBP_FILEPATH, SBP_JSON_FILEPATH, + VEL_TIME_STR_FILEPATH, +}; +use crate::output::{CsvLogging, SbpLogger}; +use crate::shared_state::{create_directory, SharedState}; +use crate::types::CapnProtoSender; use crate::utils::refresh_loggingbar; pub struct MainTab { @@ -191,7 +195,7 @@ mod tests { use super::*; use crate::baseline_tab::BaselineTab; use crate::solution_tab::SolutionTab; - use crate::types::{PosLLH, VelNED}; + use crate::types::{BaselineNED, MsgSender, PosLLH, TestSender, VelNED}; use crate::utils::{mm_to_m, ms_to_sec}; use glob::glob; use sbp::messages::navigation::{MsgBaselineNED, MsgPosLLH, MsgVelNED}; diff --git a/console_backend/src/observation_tab.rs b/console_backend/src/observation_tab.rs index 453d748b1..2afbb19a0 100644 --- a/console_backend/src/observation_tab.rs +++ b/console_backend/src/observation_tab.rs @@ -3,7 +3,8 @@ use capnp::message::Builder; use log::warn; use std::collections::{BTreeMap, HashMap}; -use crate::types::*; +use crate::shared_state::SharedState; +use crate::types::{CapnProtoSender, ObservationMsg, SignalCodes}; use crate::utils::{compute_doppler, sec_to_ns}; use crate::utils::serialize_capnproto_builder; diff --git a/console_backend/src/process_messages.rs b/console_backend/src/process_messages.rs index 0a5511359..2b90a87df 100644 --- a/console_backend/src/process_messages.rs +++ b/console_backend/src/process_messages.rs @@ -24,7 +24,11 @@ use crate::connection::Connection; use crate::constants::PAUSE_LOOP_SLEEP_DURATION_MS; use crate::errors::UNABLE_TO_CLONE_UPDATE_SHARED; use crate::log_panel::handle_log_msg; -use crate::types::*; +use crate::shared_state::SharedState; +use crate::types::{ + BaselineNED, CapnProtoSender, Dops, GpsTime, MsgSender, ObservationMsg, PosLLH, RealtimeDelay, + Result, Specan, VelNED, +}; use crate::update_tab; use crate::utils::{close_frontend, refresh_navbar}; use crate::Tabs; diff --git a/console_backend/src/server.rs b/console_backend/src/server.rs index 7d2f37741..c261d4856 100644 --- a/console_backend/src/server.rs +++ b/console_backend/src/server.rs @@ -1,27 +1,17 @@ -use capnp::serialize; use crossbeam::channel; -use log::{error, info}; use pyo3::exceptions; use pyo3::prelude::*; use pyo3::types::PyBytes; -use std::{ - io::{BufReader, Cursor}, - path::PathBuf, - str::FromStr, - thread, time, -}; +use std::time; use crate::cli_options::*; use crate::connection::ConnectionState; -use crate::console_backend_capnp as m; -use crate::errors::*; -use crate::log_panel::{setup_logging, LogLevel}; -use crate::output::{CsvLogging, SbpLogging}; -use crate::settings_tab; -use crate::types::{ClientSender, FlowControl, RealtimeDelay, SharedState}; -use crate::update_tab::UpdateTabUpdate; +use crate::log_panel::setup_logging; +use crate::server_recv_thread::server_recv_thread; +use crate::shared_state::SharedState; +use crate::types::ClientSender; use crate::utils::{refresh_loggingbar, refresh_navbar}; /// The backend server @@ -70,335 +60,6 @@ impl ServerEndpoint { } } -/// Start connections based on CLI options. -/// -/// # Parameters -/// - `opt`: CLI Options to start specific connection type. -/// - `connection_state`: The Server state to start a specific connection. -/// - `client_send`: Client Sender channel for communication from backend to frontend. -/// - `shared_state`: The shared state for validating another connection is not already running. -fn handle_cli(opt: CliOptions, connection_state: &ConnectionState, shared_state: SharedState) { - if let Some(opt_input) = opt.input { - match opt_input { - Input::Tcp { host, port } => { - connection_state.connect_to_host(host, port); - } - Input::File { file_in } => { - let filename = file_in.display().to_string(); - connection_state.connect_to_file(filename, RealtimeDelay::On, opt.exit_after); - } - Input::Serial { - serialport, - baudrate, - flow_control, - } => { - let serialport = serialport.display().to_string(); - connection_state.connect_to_serial(serialport, baudrate, flow_control); - } - } - } - if let Some(folder) = opt.dirname { - shared_state.set_logging_directory(PathBuf::from(folder)); - } - let log_level = if let Some(log_level_) = opt.log_level { - (*log_level_).clone() - } else { - LogLevel::INFO - }; - shared_state.set_log_level(log_level); - let mut shared_data = shared_state.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).logging_bar.csv_logging = CsvLogging::from(opt.csv_log); - if let Some(sbp_log) = opt.sbp_log { - (*shared_data).logging_bar.sbp_logging = - SbpLogging::from_str(&sbp_log.to_string()).expect(CONVERT_TO_STR_FAILURE); - } - log::logger().flush(); -} - -fn backend_recv_thread( - connection_state: ConnectionState, - client_send: ClientSender, - server_recv: channel::Receiver>, - shared_state: SharedState, -) { - thread::spawn(move || { - let client_send_clone = client_send.clone(); - loop { - log::logger().flush(); - let buf = server_recv.recv(); - if let Ok(buf) = buf { - let mut buf_reader = BufReader::new(Cursor::new(buf)); - let message_reader = serialize::read_message( - &mut buf_reader, - ::capnp::message::ReaderOptions::new(), - ) - .unwrap(); - let message = message_reader - .get_root::() - .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - let message = match message.which() { - Ok(msg) => msg, - Err(e) => { - error!("error reading message: {}", e); - continue; - } - }; - let shared_state_clone = shared_state.clone(); - match message { - m::message::SerialRefreshRequest(Ok(_)) => { - refresh_navbar(&mut client_send_clone.clone(), shared_state_clone); - } - m::message::DisconnectRequest(Ok(_)) => { - connection_state.disconnect(client_send_clone.clone()); - } - m::message::FileRequest(Ok(req)) => { - let filename = req - .get_filename() - .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - let filename = filename.to_string(); - connection_state.connect_to_file( - filename, - RealtimeDelay::On, - /*close_when_done*/ false, - ); - } - m::message::PauseRequest(Ok(_)) => { - if shared_state_clone.is_paused() { - shared_state_clone.set_paused(false); - } else { - shared_state_clone.set_paused(true); - } - } - m::message::TcpRequest(Ok(req)) => { - let host = req.get_host().expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - let port = req.get_port(); - connection_state.connect_to_host(host.to_string(), port); - } - m::message::SerialRequest(Ok(req)) => { - let device = req.get_device().expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - let device = device.to_string(); - let baudrate = req.get_baudrate(); - let flow = req.get_flow_control().unwrap(); - let flow = FlowControl::from_str(flow).unwrap(); - connection_state.connect_to_serial(device, baudrate, flow); - } - m::message::TrackingSignalsStatusFront(Ok(cv_in)) => { - let check_visibility = cv_in - .get_tracking_signals_check_visibility() - .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - let check_visibility: Vec = check_visibility - .iter() - .map(|x| String::from(x.unwrap())) - .collect(); - let shared_state_clone = shared_state.clone(); - { - let mut shared_data = shared_state_clone - .lock() - .expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).tracking_tab.signals_tab.check_visibility = - check_visibility; - } - } - m::message::LoggingBarFront(Ok(cv_in)) => { - let directory = cv_in - .get_directory() - .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - shared_state.set_logging_directory(PathBuf::from(directory)); - let shared_state_clone = shared_state.clone(); - let mut shared_data = shared_state_clone - .lock() - .expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).logging_bar.csv_logging = - CsvLogging::from(cv_in.get_csv_logging()); - let sbp_logging = cv_in - .get_sbp_logging() - .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - (*shared_data).logging_bar.sbp_logging = - SbpLogging::from_str(sbp_logging).expect(CONVERT_TO_STR_FAILURE); - } - m::message::LogLevelFront(Ok(cv_in)) => { - let shared_state_clone = shared_state.clone(); - let log_level = cv_in - .get_log_level() - .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - let log_level = - LogLevel::from_str(log_level).expect(CONVERT_TO_STR_FAILURE); - info!("Log Level: {}", log_level); - shared_state_clone.set_log_level(log_level); - refresh_navbar(&mut client_send.clone(), shared_state.clone()); - } - m::message::SolutionVelocityStatusFront(Ok(cv_in)) => { - let unit = cv_in - .get_solution_velocity_unit() - .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - let shared_state_clone = shared_state.clone(); - { - let mut shared_data = shared_state_clone - .lock() - .expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).solution_tab.velocity_tab.unit = unit.to_string(); - } - } - m::message::SolutionPositionStatusUnitFront(Ok(cv_in)) => { - let shared_state_clone = shared_state.clone(); - let mut shared_data = shared_state_clone - .lock() - .expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - let unit = cv_in - .get_solution_position_unit() - .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - (*shared_data).solution_tab.position_tab.unit = unit.to_string(); - } - m::message::SolutionPositionStatusButtonFront(Ok(cv_in)) => { - let shared_state_clone = shared_state.clone(); - let mut shared_data = shared_state_clone - .lock() - .expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).solution_tab.position_tab.clear = - cv_in.get_solution_position_clear(); - (*shared_data).solution_tab.position_tab.pause = - cv_in.get_solution_position_pause(); - } - m::message::BaselinePlotStatusButtonFront(Ok(cv_in)) => { - let shared_state_clone = shared_state.clone(); - let mut shared_data = shared_state_clone - .lock() - .expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).baseline_tab.clear = cv_in.get_clear(); - (*shared_data).baseline_tab.pause = cv_in.get_pause(); - (*shared_data).baseline_tab.reset = cv_in.get_reset_filters(); - } - m::message::AdvancedSpectrumAnalyzerStatusFront(Ok(cv_in)) => { - let shared_state_clone = shared_state.clone(); - let mut shared_data = shared_state_clone - .lock() - .expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).advanced_spectrum_analyzer_tab.channel_idx = - cv_in.get_channel(); - } - m::message::UpdateTabStatusFront(Ok(cv_in)) => { - if let Some(update_tab_sender) = shared_state.update_tab_sender() { - let download_latest_firmware = cv_in.get_download_latest_firmware(); - let update_firmware = cv_in.get_update_firmware(); - let send_file_to_device = cv_in.get_send_file_to_device(); - let firmware_directory = match cv_in.get_download_directory().which() { - Ok(m::update_tab_status_front::download_directory::Directory( - Ok(directory), - )) => Some(PathBuf::from(directory)), - Err(e) => { - error!("{}", e); - None - } - _ => None, - }; - let firmware_local_filepath = - match cv_in.get_update_local_filepath().which() { - Ok( - m::update_tab_status_front::update_local_filepath::Filepath( - Ok(filepath), - ), - ) => Some(PathBuf::from(filepath)), - Err(e) => { - error!("{}", e); - None - } - _ => None, - }; - let firmware_local_filename = - match cv_in.get_update_local_filename().which() { - Ok( - m::update_tab_status_front::update_local_filename::Filepath( - Ok(filepath), - ), - ) => Some(PathBuf::from(filepath)), - Err(e) => { - error!("{}", e); - None - } - _ => None, - }; - let fileio_local_filepath = - match cv_in.get_fileio_local_filepath().which() { - Ok( - m::update_tab_status_front::fileio_local_filepath::Filepath( - Ok(filepath), - ), - ) => Some(PathBuf::from(filepath)), - Err(e) => { - error!("{}", e); - None - } - _ => None, - }; - let fileio_destination_filepath = match cv_in.get_fileio_destination_filepath().which() { - Ok( - m::update_tab_status_front::fileio_destination_filepath::Filepath( - Ok(filepath), - ), - ) => { - Some(PathBuf::from(filepath)) - } - Err(e) => { - error!("{}", e); - None - } - _ => None, - }; - update_tab_sender - .send(Some(UpdateTabUpdate { - download_latest_firmware, - update_firmware, - send_file_to_device, - firmware_directory, - firmware_local_filepath, - firmware_local_filename, - fileio_local_filepath, - fileio_destination_filepath, - })) - .unwrap(); - } - } - m::message::SettingsRefreshRequest(Ok(_)) => { - shared_state_clone.set_settings_refresh(true); - } - m::message::SettingsResetRequest(Ok(_)) => { - shared_state_clone.set_settings_reset(true); - } - m::message::SettingsSaveRequest(Ok(_)) => { - shared_state_clone.set_settings_save(true); - } - m::message::SettingsExportRequest(Ok(path)) => { - let path = path.get_path().expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - shared_state_clone.set_export_settings(Some(PathBuf::from(path))); - } - m::message::SettingsImportRequest(Ok(path)) => { - let path = path.get_path().expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - shared_state_clone.set_import_settings(Some(PathBuf::from(path))); - } - m::message::SettingsWriteRequest(Ok(req)) => { - let group = req.get_group().expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - let name = req.get_name().expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - let value = req.get_value().expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - let req = settings_tab::SaveRequest { - group: group.to_string(), - name: name.to_string(), - value: value.to_string(), - }; - shared_state_clone.set_write_setting(Some(req)); - } - _ => { - error!("unknown message from front-end"); - } - } - } else { - break; - } - } - eprintln!("client recv loop shutdown"); - client_send_clone.connected.set(false); - }); -} - #[pymethods] impl Server { #[new] @@ -448,7 +109,7 @@ impl Server { let server_endpoint = ServerEndpoint { server_send: Some(server_send), }; - setup_logging(client_send.clone()); + setup_logging(client_send.clone(), false); let opt = CliOptions::from_filtered_cli(); let shared_state = SharedState::new(); @@ -457,7 +118,7 @@ impl Server { handle_cli(opt, &connection_state, shared_state.clone()); refresh_navbar(&mut client_send.clone(), shared_state.clone()); refresh_loggingbar(&mut client_send.clone(), shared_state.clone()); - backend_recv_thread(connection_state, client_send, server_recv, shared_state); + server_recv_thread(connection_state, client_send, server_recv, shared_state); Ok(server_endpoint) } } diff --git a/console_backend/src/server_recv_thread.rs b/console_backend/src/server_recv_thread.rs new file mode 100644 index 000000000..e86dc2e4a --- /dev/null +++ b/console_backend/src/server_recv_thread.rs @@ -0,0 +1,310 @@ +use crate::common_constants::SbpLogging; +use crate::connection::ConnectionState; +use crate::console_backend_capnp as m; +use crate::errors::{ + CAP_N_PROTO_DESERIALIZATION_FAILURE, CONVERT_TO_STR_FAILURE, SHARED_STATE_LOCK_MUTEX_FAILURE, +}; +use crate::log_panel::LogLevel; +use crate::output::CsvLogging; +use crate::settings_tab; +use crate::shared_state::SharedState; +use crate::types::{ClientSender, FlowControl, RealtimeDelay}; +use crate::update_tab::UpdateTabUpdate; +use crate::utils::refresh_navbar; +use capnp::serialize; +use chrono::{DateTime, Utc}; +use crossbeam::channel; +use log::{error, info}; +use std::{ + io::{BufReader, Cursor}, + path::PathBuf, + str::FromStr, + thread, +}; +pub type Error = anyhow::Error; +pub type Result = anyhow::Result; +pub type UtcDateTime = DateTime; + +pub fn server_recv_thread( + connection_state: ConnectionState, + client_send: ClientSender, + server_recv: channel::Receiver>, + shared_state: SharedState, +) { + thread::spawn(move || { + let client_send_clone = client_send.clone(); + loop { + log::logger().flush(); + let buf = server_recv.recv(); + if let Ok(buf) = buf { + let mut buf_reader = BufReader::new(Cursor::new(buf)); + let message_reader = serialize::read_message( + &mut buf_reader, + ::capnp::message::ReaderOptions::new(), + ) + .unwrap(); + let message = message_reader + .get_root::() + .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); + let message = match message.which() { + Ok(msg) => msg, + Err(e) => { + error!("error reading message: {}", e); + continue; + } + }; + let shared_state_clone = shared_state.clone(); + match message { + m::message::SerialRefreshRequest(Ok(_)) => { + refresh_navbar(&mut client_send_clone.clone(), shared_state_clone); + } + m::message::DisconnectRequest(Ok(_)) => { + connection_state.disconnect(client_send_clone.clone()); + } + m::message::FileRequest(Ok(req)) => { + let filename = req + .get_filename() + .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); + let filename = filename.to_string(); + connection_state.connect_to_file( + filename, + RealtimeDelay::On, + /*close_when_done*/ false, + ); + } + m::message::PauseRequest(Ok(_)) => { + if shared_state_clone.is_paused() { + shared_state_clone.set_paused(false); + } else { + shared_state_clone.set_paused(true); + } + } + m::message::TcpRequest(Ok(req)) => { + let host = req.get_host().expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); + let port = req.get_port(); + connection_state.connect_to_host(host.to_string(), port); + } + m::message::SerialRequest(Ok(req)) => { + let device = req.get_device().expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); + let device = device.to_string(); + let baudrate = req.get_baudrate(); + let flow = req.get_flow_control().unwrap(); + let flow = FlowControl::from_str(flow).unwrap(); + connection_state.connect_to_serial(device, baudrate, flow); + } + m::message::TrackingSignalsStatusFront(Ok(cv_in)) => { + let check_visibility = cv_in + .get_tracking_signals_check_visibility() + .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); + let check_visibility: Vec = check_visibility + .iter() + .map(|x| String::from(x.unwrap())) + .collect(); + let shared_state_clone = shared_state.clone(); + { + let mut shared_data = shared_state_clone + .lock() + .expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).tracking_tab.signals_tab.check_visibility = + check_visibility; + } + } + m::message::LoggingBarFront(Ok(cv_in)) => { + let directory = cv_in + .get_directory() + .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); + shared_state.set_logging_directory(PathBuf::from(directory)); + let shared_state_clone = shared_state.clone(); + let mut shared_data = shared_state_clone + .lock() + .expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).logging_bar.csv_logging = + CsvLogging::from(cv_in.get_csv_logging()); + let sbp_logging = cv_in + .get_sbp_logging() + .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); + (*shared_data).logging_bar.sbp_logging = + SbpLogging::from_str(sbp_logging).expect(CONVERT_TO_STR_FAILURE); + } + m::message::LogLevelFront(Ok(cv_in)) => { + let shared_state_clone = shared_state.clone(); + let log_level = cv_in + .get_log_level() + .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); + let log_level = + LogLevel::from_str(log_level).expect(CONVERT_TO_STR_FAILURE); + info!("Log Level: {}", log_level); + shared_state_clone.set_log_level(log_level); + refresh_navbar(&mut client_send.clone(), shared_state.clone()); + } + m::message::SolutionVelocityStatusFront(Ok(cv_in)) => { + let unit = cv_in + .get_solution_velocity_unit() + .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); + let shared_state_clone = shared_state.clone(); + { + let mut shared_data = shared_state_clone + .lock() + .expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).solution_tab.velocity_tab.unit = unit.to_string(); + } + } + m::message::SolutionPositionStatusUnitFront(Ok(cv_in)) => { + let shared_state_clone = shared_state.clone(); + let mut shared_data = shared_state_clone + .lock() + .expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + let unit = cv_in + .get_solution_position_unit() + .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); + (*shared_data).solution_tab.position_tab.unit = unit.to_string(); + } + m::message::SolutionPositionStatusButtonFront(Ok(cv_in)) => { + let shared_state_clone = shared_state.clone(); + let mut shared_data = shared_state_clone + .lock() + .expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).solution_tab.position_tab.clear = + cv_in.get_solution_position_clear(); + (*shared_data).solution_tab.position_tab.pause = + cv_in.get_solution_position_pause(); + } + m::message::BaselinePlotStatusButtonFront(Ok(cv_in)) => { + let shared_state_clone = shared_state.clone(); + let mut shared_data = shared_state_clone + .lock() + .expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).baseline_tab.clear = cv_in.get_clear(); + (*shared_data).baseline_tab.pause = cv_in.get_pause(); + (*shared_data).baseline_tab.reset = cv_in.get_reset_filters(); + } + m::message::AdvancedSpectrumAnalyzerStatusFront(Ok(cv_in)) => { + let shared_state_clone = shared_state.clone(); + let mut shared_data = shared_state_clone + .lock() + .expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).advanced_spectrum_analyzer_tab.channel_idx = + cv_in.get_channel(); + } + m::message::UpdateTabStatusFront(Ok(cv_in)) => { + if let Some(update_tab_sender) = shared_state.update_tab_sender() { + let download_latest_firmware = cv_in.get_download_latest_firmware(); + let update_firmware = cv_in.get_update_firmware(); + let send_file_to_device = cv_in.get_send_file_to_device(); + let firmware_directory = match cv_in.get_download_directory().which() { + Ok(m::update_tab_status_front::download_directory::Directory( + Ok(directory), + )) => Some(PathBuf::from(directory)), + Err(e) => { + error!("{}", e); + None + } + _ => None, + }; + let firmware_local_filepath = + match cv_in.get_update_local_filepath().which() { + Ok( + m::update_tab_status_front::update_local_filepath::Filepath( + Ok(filepath), + ), + ) => Some(PathBuf::from(filepath)), + Err(e) => { + error!("{}", e); + None + } + _ => None, + }; + let firmware_local_filename = + match cv_in.get_update_local_filename().which() { + Ok( + m::update_tab_status_front::update_local_filename::Filepath( + Ok(filepath), + ), + ) => Some(PathBuf::from(filepath)), + Err(e) => { + error!("{}", e); + None + } + _ => None, + }; + let fileio_local_filepath = + match cv_in.get_fileio_local_filepath().which() { + Ok( + m::update_tab_status_front::fileio_local_filepath::Filepath( + Ok(filepath), + ), + ) => Some(PathBuf::from(filepath)), + Err(e) => { + error!("{}", e); + None + } + _ => None, + }; + let fileio_destination_filepath = match cv_in.get_fileio_destination_filepath().which() { + Ok( + m::update_tab_status_front::fileio_destination_filepath::Filepath( + Ok(filepath), + ), + ) => { + Some(PathBuf::from(filepath)) + } + Err(e) => { + error!("{}", e); + None + } + _ => None, + }; + update_tab_sender + .send(Some(UpdateTabUpdate { + download_latest_firmware, + update_firmware, + send_file_to_device, + firmware_directory, + firmware_local_filepath, + firmware_local_filename, + fileio_local_filepath, + fileio_destination_filepath, + })) + .unwrap(); + } + } + m::message::SettingsRefreshRequest(Ok(_)) => { + shared_state_clone.set_settings_refresh(true); + } + m::message::SettingsResetRequest(Ok(_)) => { + shared_state_clone.set_settings_reset(true); + } + m::message::SettingsSaveRequest(Ok(_)) => { + shared_state_clone.set_settings_save(true); + } + m::message::SettingsExportRequest(Ok(path)) => { + let path = path.get_path().expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); + shared_state_clone.set_export_settings(Some(PathBuf::from(path))); + } + m::message::SettingsImportRequest(Ok(path)) => { + let path = path.get_path().expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); + shared_state_clone.set_import_settings(Some(PathBuf::from(path))); + } + m::message::SettingsWriteRequest(Ok(req)) => { + let group = req.get_group().expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); + let name = req.get_name().expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); + let value = req.get_value().expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); + let req = settings_tab::SaveRequest { + group: group.to_string(), + name: name.to_string(), + value: value.to_string(), + }; + shared_state_clone.set_write_setting(Some(req)); + } + _ => { + error!("unknown message from front-end"); + } + } + } else { + break; + } + } + eprintln!("client recv loop shutdown"); + client_send_clone.connected.set(false); + }); +} diff --git a/console_backend/src/settings_tab.rs b/console_backend/src/settings_tab.rs index f97f79d31..65464a438 100644 --- a/console_backend/src/settings_tab.rs +++ b/console_backend/src/settings_tab.rs @@ -13,7 +13,8 @@ use sbp::link::Link; use sbp::messages::piksi::MsgReset; use sbp::messages::settings::MsgSettingsSave; -use crate::types::{CapnProtoSender, Error, MsgSender, Result, SharedState}; +use crate::shared_state::SharedState; +use crate::types::{CapnProtoSender, Error, MsgSender, Result}; use crate::utils::*; pub struct SettingsTab<'link, S> { diff --git a/console_backend/src/shared_state.rs b/console_backend/src/shared_state.rs new file mode 100644 index 000000000..06125b196 --- /dev/null +++ b/console_backend/src/shared_state.rs @@ -0,0 +1,794 @@ +use crate::common_constants::{self as cc, SbpLogging}; +use crate::constants::{ + APPLICATION_NAME, APPLICATION_ORGANIZATION, APPLICATION_QUALIFIER, CONNECTION_HISTORY_FILENAME, + DEFAULT_LOG_DIRECTORY, DEGREES, MAX_CONNECTION_HISTORY, MPS, +}; +use crate::errors::{CONVERT_TO_STR_FAILURE, SHARED_STATE_LOCK_MUTEX_FAILURE}; +use crate::log_panel::LogLevel; +use crate::output::{CsvLogging, CsvSerializer}; +use crate::piksi_tools_constants::*; +use crate::settings_tab; +use crate::types::CapnProtoSender; +use crate::update_tab::UpdateTabUpdate; +use crate::utils::set_connected_frontend; +use anyhow::{Context, Result as AHResult}; +use chrono::{DateTime, Utc}; +use crossbeam::channel::Sender; +use directories::{ProjectDirs, UserDirs}; +use indexmap::set::IndexSet; +use lazy_static::lazy_static; +use log::error; +use serde::{Deserialize, Serialize}; +use std::path::Path; +use std::{ + cmp::{Eq, PartialEq}, + fmt::Debug, + fs, + hash::Hash, + ops::Deref, + path::PathBuf, + sync::{Arc, Mutex}, + time::Instant, +}; +pub type Error = anyhow::Error; +pub type Result = anyhow::Result; +pub type UtcDateTime = DateTime; + +#[derive(Debug)] +pub struct SharedState(Arc>); + +impl SharedState { + pub fn new() -> SharedState { + SharedState(Arc::new(Mutex::new(SharedStateInner::default()))) + } + pub fn is_running(&self) -> bool { + let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).running + } + pub fn set_running(&self, set_to: bool, mut client_send: S) + where + S: CapnProtoSender, + { + if set_to { + set_connected_frontend(cc::ApplicationStates::CONNECTED, &mut client_send); + } else { + set_connected_frontend(cc::ApplicationStates::DISCONNECTED, &mut client_send); + self.set_current_connection(EMPTY_STR.to_string()); + } + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).running = set_to; + } + pub fn is_server_running(&self) -> bool { + let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).server_running + } + pub fn stop_server_running(&self) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).running = false; + (*shared_data).server_running = false; + } + pub fn is_paused(&self) -> bool { + let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).paused + } + pub fn set_paused(&self, set_to: bool) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).paused = set_to; + } + pub fn debug(&self) -> bool { + let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).debug + } + pub fn set_debug(&self, set_to: bool) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).debug = set_to; + } + pub fn current_connection(&self) -> String { + let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).status_bar.current_connection.clone() + } + pub fn set_current_connection(&self, current_connection: String) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).status_bar.current_connection = current_connection; + } + pub fn logging_directory(&self) -> PathBuf { + let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + let mut folders = (*shared_data).connection_history.folders(); + if let Some(folder) = folders.pop() { + PathBuf::from(folder) + } else { + LOG_DIRECTORY.path() + } + } + pub fn set_log_level(&self, log_level: LogLevel) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).log_panel.log_level = log_level.clone(); + log::set_max_level(log_level.level_filter()); + } + pub fn log_level(&self) -> LogLevel { + let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).log_panel.log_level.clone() + } + pub fn sbp_logging(&self) -> SbpLogging { + let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).logging_bar.sbp_logging.clone() + } + pub fn csv_logging(&self) -> CsvLogging { + let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).logging_bar.csv_logging.clone() + } + pub fn set_logging_directory(&self, directory: PathBuf) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + let directory = if directory.starts_with("~/") { + if let Ok(dir) = directory.strip_prefix("~/") { + user_directory().join(dir) + } else { + directory + } + } else { + directory + }; + (*shared_data).logging_bar.logging_directory = directory; + } + pub fn set_sbp_logging(&self, logging: SbpLogging) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).logging_bar.sbp_logging = logging; + } + pub fn set_csv_logging(&self, logging: CsvLogging) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).logging_bar.csv_logging = logging; + } + pub fn folder_history(&self) -> IndexSet { + let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).connection_history.folders() + } + pub fn file_history(&self) -> IndexSet { + let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).connection_history.files() + } + pub fn address_history(&self) -> IndexSet
{ + let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).connection_history.addresses() + } + pub fn update_folder_history(&self, folder: PathBuf) { + let folder = String::from(folder.to_str().expect(CONVERT_TO_STR_FAILURE)); + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).connection_history.record_folder(folder); + } + pub fn update_file_history(&self, filename: String) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).connection_history.record_file(filename); + } + pub fn update_tcp_history(&self, host: String, port: u16) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).connection_history.record_address(host, port); + } + pub fn start_vel_log(&self, path: &Path) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).solution_tab.velocity_tab.log_file = match CsvSerializer::new(path) { + Ok(vel_csv) => Some(vel_csv), + Err(e) => { + error!("issue creating file, {:?}, error, {}", path, e); + None + } + } + } + pub fn end_vel_log(&self) -> Result<()> { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + if let Some(ref mut log) = (*shared_data).solution_tab.velocity_tab.log_file { + log.flush()?; + } + (*shared_data).solution_tab.velocity_tab.log_file = None; + Ok(()) + } + pub fn start_pos_log(&self, path: &Path) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).solution_tab.position_tab.log_file = match CsvSerializer::new(path) { + Ok(vel_csv) => Some(vel_csv), + Err(e) => { + error!("issue creating file, {:?}, error, {}", path, e); + None + } + } + } + pub fn end_pos_log(&self) -> Result<()> { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + if let Some(ref mut log) = (*shared_data).solution_tab.position_tab.log_file { + log.flush()?; + } + (*shared_data).solution_tab.position_tab.log_file = None; + Ok(()) + } + pub fn start_baseline_log(&self, path: &Path) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).baseline_tab.log_file = match CsvSerializer::new(path) { + Ok(vel_csv) => Some(vel_csv), + Err(e) => { + error!("issue creating file, {:?}, error, {}", path, e); + None + } + } + } + pub fn end_baseline_log(&self) -> Result<()> { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + if let Some(ref mut log) = (*shared_data).baseline_tab.log_file { + log.flush()?; + } + (*shared_data).baseline_tab.log_file = None; + Ok(()) + } + pub fn update_tab_sender(&self) -> Option>> { + let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).update_tab_sender.clone() + } + pub fn set_update_tab_sender(&self, sender: Sender>) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + (*shared_data).update_tab_sender = Some(sender); + } + pub fn settings_needs_update(&self) -> bool { + self.lock() + .expect(SHARED_STATE_LOCK_MUTEX_FAILURE) + .settings_tab + .needs_update() + } + pub fn take_settings_state(&self) -> SettingsTabState { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + shared_data.settings_tab.take() + } + pub fn set_settings_refresh(&self, set_to: bool) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + shared_data.settings_tab.refresh = set_to; + } + pub fn set_settings_save(&self, set_to: bool) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + shared_data.settings_tab.save = set_to; + } + pub fn set_settings_reset(&self, set_to: bool) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + shared_data.settings_tab.reset = set_to; + } + pub fn set_export_settings(&self, path: Option) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + shared_data.settings_tab.export = path; + } + pub fn set_import_settings(&self, path: Option) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + shared_data.settings_tab.import = path; + } + pub fn set_write_setting(&self, setting: Option) { + let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); + shared_data.settings_tab.write = setting; + } +} + +impl Deref for SharedState { + type Target = Mutex; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for SharedState { + fn default() -> Self { + Self::new() + } +} + +impl Clone for SharedState { + fn clone(&self) -> Self { + SharedState { + 0: Arc::clone(&self.0), + } + } +} + +#[derive(Debug)] +pub struct SharedStateInner { + pub(crate) status_bar: StatusBarState, + pub(crate) logging_bar: LoggingBarState, + pub(crate) log_panel: LogPanelState, + pub(crate) tracking_tab: TrackingTabState, + pub(crate) paused: bool, + pub(crate) connection_history: ConnectionHistory, + pub(crate) running: bool, + pub(crate) debug: bool, + pub(crate) server_running: bool, + pub(crate) solution_tab: SolutionTabState, + pub(crate) baseline_tab: BaselineTabState, + pub(crate) advanced_spectrum_analyzer_tab: AdvancedSpectrumAnalyzerTabState, + pub(crate) update_tab_sender: Option>>, + pub(crate) settings_tab: SettingsTabState, +} +impl SharedStateInner { + pub fn new() -> SharedStateInner { + let connection_history = ConnectionHistory::new(); + let log_directory = connection_history.folders().pop(); + SharedStateInner { + status_bar: StatusBarState::new(), + logging_bar: LoggingBarState::new(log_directory), + log_panel: LogPanelState::new(), + tracking_tab: TrackingTabState::new(), + paused: false, + debug: false, + connection_history, + running: false, + server_running: true, + solution_tab: SolutionTabState::new(), + baseline_tab: BaselineTabState::new(), + advanced_spectrum_analyzer_tab: AdvancedSpectrumAnalyzerTabState::new(), + update_tab_sender: None, + settings_tab: SettingsTabState::new(), + } + } +} +impl Default for SharedStateInner { + fn default() -> Self { + SharedStateInner::new() + } +} + +#[derive(Debug)] +pub struct StatusBarState { + pub current_connection: String, +} + +impl StatusBarState { + fn new() -> StatusBarState { + StatusBarState { + current_connection: String::from(""), + } + } +} + +#[derive(Debug)] +pub struct LoggingBarState { + pub sbp_logging: SbpLogging, + pub csv_logging: CsvLogging, + pub logging_directory: PathBuf, +} + +impl LoggingBarState { + fn new(log_directory: Option) -> LoggingBarState { + let logging_directory = if let Some(dir) = log_directory { + PathBuf::from(dir) + } else { + LOG_DIRECTORY.path() + }; + LoggingBarState { + sbp_logging: SbpLogging::OFF, + csv_logging: CsvLogging::OFF, + logging_directory, + } + } +} + +#[derive(Debug)] +pub struct LogPanelState { + pub log_level: LogLevel, +} + +impl LogPanelState { + fn new() -> LogPanelState { + LogPanelState { + log_level: LogLevel::INFO, + } + } +} + +#[derive(Debug)] +pub struct TrackingTabState { + pub signals_tab: TrackingSignalsTabState, +} + +impl TrackingTabState { + fn new() -> TrackingTabState { + TrackingTabState { + signals_tab: TrackingSignalsTabState::new(), + } + } +} + +#[derive(Debug)] +pub struct TrackingSignalsTabState { + pub check_visibility: Vec, +} + +impl TrackingSignalsTabState { + fn new() -> TrackingSignalsTabState { + TrackingSignalsTabState { + check_visibility: vec![], + } + } +} + +#[derive(Debug)] +pub struct BaselineTabState { + pub(crate) clear: bool, + pub(crate) pause: bool, + pub(crate) reset: bool, + pub(crate) log_file: Option, +} + +impl BaselineTabState { + fn new() -> BaselineTabState { + BaselineTabState { + clear: false, + pause: false, + reset: false, + log_file: None, + } + } +} + +#[derive(Debug)] +pub struct SolutionTabState { + pub position_tab: SolutionPositionTabState, + pub velocity_tab: SolutionVelocityTabState, +} + +impl SolutionTabState { + fn new() -> SolutionTabState { + SolutionTabState { + position_tab: SolutionPositionTabState::new(), + velocity_tab: SolutionVelocityTabState::new(), + } + } +} + +#[derive(Debug)] +pub struct SolutionPositionTabState { + pub clear: bool, + pub ins_status_flags: u32, + pub last_ins_status_receipt_time: Instant, + pub last_odo_update_time: Instant, + pub pause: bool, + pub unit: String, + pub log_file: Option, +} + +impl SolutionPositionTabState { + fn new() -> SolutionPositionTabState { + SolutionPositionTabState { + clear: false, + ins_status_flags: 0, + last_ins_status_receipt_time: Instant::now(), + last_odo_update_time: Instant::now(), + pause: false, + unit: String::from(DEGREES), + log_file: None, + } + } +} + +#[derive(Debug)] +pub struct SolutionVelocityTabState { + pub unit: String, + pub log_file: Option, +} + +impl SolutionVelocityTabState { + fn new() -> SolutionVelocityTabState { + SolutionVelocityTabState { + unit: String::from(MPS), + log_file: None, + } + } +} + +#[derive(Debug)] +pub struct AdvancedSpectrumAnalyzerTabState { + pub channel_idx: u16, +} + +impl AdvancedSpectrumAnalyzerTabState { + fn new() -> AdvancedSpectrumAnalyzerTabState { + AdvancedSpectrumAnalyzerTabState { channel_idx: 0 } + } +} + +#[derive(Debug, Default)] +pub struct SettingsTabState { + pub refresh: bool, + pub reset: bool, + pub save: bool, + pub export: Option, + pub import: Option, + pub write: Option, +} + +impl SettingsTabState { + fn new() -> Self { + Default::default() + } + + fn take(&mut self) -> SettingsTabState { + std::mem::take(self) + } + + fn needs_update(&self) -> bool { + self.refresh + || self.reset + || self.save + || self.export.is_some() + || self.import.is_some() + || self.write.is_some() + } +} + +// Navbar Types. + +/// Directory struct for storing informating and helpers pertaining to project directory. +/// +/// Taken from swift-cli/swift/src/types.rs. +/// impl taken from swift-cli/swift/src/lib.rs. +#[derive(Debug)] +pub struct Directory { + path: PathBuf, +} +lazy_static! { + pub static ref DATA_DIRECTORY: Directory = Directory::new_data_directory(); +} +lazy_static! { + pub static ref LOG_DIRECTORY: Directory = Directory::new_log_directory(); +} +impl Directory { + pub fn new_data_directory() -> Directory { + Directory { + path: create_data_dir().unwrap(), + } + } + + pub fn new_log_directory() -> Directory { + Directory { + path: user_directory().join(DEFAULT_LOG_DIRECTORY), + } + } + /// Return a clone to the private path PathBuf. + pub fn path(&self) -> PathBuf { + self.path.clone() + } +} +impl Default for Directory { + fn default() -> Self { + Directory::new_data_directory() + } +} + +/// Deduce data directory path and create folder. +/// +/// Taken from swift-cli/swift/src/lib.rs. +/// # Returns +/// - `Ok`: The PathBuf for the data directory path. +/// - `Err`: Issue deducing path or creating the data directory. +fn create_data_dir() -> AHResult { + let proj_dirs = ProjectDirs::from( + APPLICATION_QUALIFIER, + APPLICATION_ORGANIZATION, + APPLICATION_NAME, + ) + .context("could not discover local project directory")?; + let path: PathBuf = ProjectDirs::data_local_dir(&proj_dirs).into(); + fs::create_dir_all(path.clone())?; + Ok(path) +} + +/// Create directory. +/// +/// Taken from swift-cli/swift/src/lib.rs. +/// # Returns +/// - `Ok`: The PathBuf for the data directory path. +/// - `Err`: Issue deducing path or creating the data directory. +pub fn create_directory(directory: PathBuf) -> Result { + fs::create_dir_all(&directory)?; + Ok(directory) +} + +/// Get user directory. +/// +/// # Returns +/// - The PathBuf for the user directory path. Otherwise empty pathbuf. +pub fn user_directory() -> PathBuf { + if let Some(user_dirs) = UserDirs::new() { + PathBuf::from(user_dirs.home_dir()) + } else { + PathBuf::from("") + } +} + +#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub struct Address { + pub host: String, + pub port: u16, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct ConnectionHistory { + addresses: IndexSet
, + files: IndexSet, + folders: IndexSet, +} + +impl Default for ConnectionHistory { + fn default() -> Self { + ConnectionHistory::new() + } +} + +impl ConnectionHistory { + /// Attempts to create a new ConnectionHistory from expected filepath otherwise empty. + pub fn new() -> ConnectionHistory { + let filename = DATA_DIRECTORY.path().join(CONNECTION_HISTORY_FILENAME); + if let Ok(file) = fs::File::open(&filename) { + if let Ok(conn_yaml) = serde_yaml::from_reader(file) { + return conn_yaml; + } + } + let mut folders = IndexSet::new(); + if let Ok(default_path) = LOG_DIRECTORY.path().into_os_string().into_string() { + folders.insert(default_path); + } + ConnectionHistory { + addresses: IndexSet::new(), + files: IndexSet::new(), + folders, + } + } + /// Return the filename of the saved connection history file. + fn filename(&self) -> PathBuf { + DATA_DIRECTORY.path().join(CONNECTION_HISTORY_FILENAME) + } + /// Returns a clone of the private addresses vec. + pub fn addresses(&self) -> IndexSet
{ + self.addresses.clone() + } + /// Returns a clone of the private files vec. + pub fn files(&self) -> IndexSet { + self.files.clone() + } + /// Returns a clone of the private folders vec. + pub fn folders(&self) -> IndexSet { + self.folders.clone() + } + /// Attempt to add a new host and port if not the most recent entries. + /// + /// # Parameters + /// - `host`: The TCP host to add to the history. + /// - `port`: The TCP port to add to the history. + pub fn record_address(&mut self, host: String, port: u16) { + let address = Address { host, port }; + self.addresses.shift_remove(&address); + self.addresses.insert(address); + let diff = i32::max(0, self.addresses.len() as i32 - MAX_CONNECTION_HISTORY); + self.addresses = self.addresses.split_off(diff as usize); + + if let Err(e) = self.save() { + error!("Unable to save connection history, {}.", e); + } + } + /// Attempt to add a new filepath if not the most recent entry. + /// + /// # Parameters + /// - `filename`: The path to the file to add to history. + pub fn record_file(&mut self, filename: String) { + self.files.shift_remove(&filename); + self.files.insert(filename); + let diff = i32::max(0, self.files.len() as i32 - MAX_CONNECTION_HISTORY); + self.files = self.files.split_off(diff as usize); + + if let Err(e) = self.save() { + error!("Unable to save connection history, {}.", e); + } + } + /// Attempt to add a new folder if not the most recent entry. + /// + /// # Parameters + /// - `folder`: The path to the folder to add to history. + pub fn record_folder(&mut self, folder: String) { + self.folders.shift_remove(&folder); + self.folders.insert(folder); + let diff = i32::max(0, self.folders.len() as i32 - MAX_CONNECTION_HISTORY); + self.folders = self.folders.split_off(diff as usize); + + if let Err(e) = self.save() { + error!("Unable to save connection history, {}.", e); + } + } + + /// Save the history to the expected filepath. + fn save(&self) -> Result<()> { + serde_yaml::to_writer(fs::File::create(&self.filename())?, self)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_common::{backup_file, data_directories, filename, restore_backup_file}; + use serial_test::serial; + + #[test] + fn create_data_dir_test() { + create_data_dir().unwrap(); + let user_dirs = UserDirs::new().unwrap(); + let home_dir = user_dirs.home_dir(); + #[cfg(target_os = "linux")] + { + assert!(home_dir.join(data_directories::LINUX).exists()); + } + + #[cfg(target_os = "macos")] + { + assert!(home_dir.join(data_directories::MACOS).exists()); + } + #[cfg(target_os = "windows")] + { + assert!(home_dir.join(data_directories::WINDOWS).exists()); + } + } + + #[test] + #[serial] + fn connection_history_save_test() { + let bfilename = filename(); + backup_file(bfilename.clone()); + let conn_history = ConnectionHistory::new(); + conn_history.save().unwrap(); + assert!(bfilename.exists()); + restore_backup_file(bfilename); + } + + #[test] + #[serial] + fn connection_history_additions_test() { + let bfilename = filename(); + backup_file(bfilename.clone()); + + let mut conn_history = ConnectionHistory::new(); + let host1 = String::from("host1"); + let host2 = String::from("host2"); + let port = 100; + + conn_history.record_address(host1.clone(), port); + let addresses = conn_history.addresses(); + let addresses_len = addresses.len(); + let first_addy = addresses.first().unwrap(); + assert_eq!(host1, first_addy.host); + assert_eq!(port, first_addy.port); + + conn_history.record_address(host2.clone(), port); + let addresses = conn_history.addresses(); + let first_addy = addresses.first().unwrap(); + let second_addy = addresses.get_index(1).unwrap(); + assert_eq!(host1, first_addy.host); + assert_eq!(port, first_addy.port); + assert_eq!(host2, second_addy.host); + assert_eq!(port, second_addy.port); + assert_eq!(addresses_len + 1, addresses.len()); + + conn_history.record_address(host1.clone(), port); + let addresses = conn_history.addresses(); + let first_addy = addresses.first().unwrap(); + let second_addy = addresses.get_index(1).unwrap(); + assert_eq!(host2, first_addy.host); + assert_eq!(port, first_addy.port); + assert_eq!(host1, second_addy.host); + assert_eq!(port, second_addy.port); + assert_eq!(addresses_len + 1, addresses.len()); + + let filename1 = String::from("filename1"); + let filename2 = String::from("filename2"); + conn_history.record_file(filename1.clone()); + conn_history.record_file(filename2.clone()); + conn_history.record_file(filename1.clone()); + let files = conn_history.files(); + assert_eq!(filename1, files[1]); + assert_eq!(filename2, files[0]); + + for ele in 0..MAX_CONNECTION_HISTORY { + conn_history.record_file(ele.to_string()); + } + assert_eq!(conn_history.files().len(), MAX_CONNECTION_HISTORY as usize); + restore_backup_file(bfilename); + } +} diff --git a/console_backend/src/solution_tab.rs b/console_backend/src/solution_tab.rs index 653114d2e..cd7677a10 100644 --- a/console_backend/src/solution_tab.rs +++ b/console_backend/src/solution_tab.rs @@ -11,9 +11,8 @@ use crate::constants::*; use crate::date_conv::*; use crate::output::{PosLLHLog, VelLog}; use crate::piksi_tools_constants::EMPTY_STR; -use crate::types::{ - CapnProtoSender, Deque, Dops, GnssModes, GpsTime, PosLLH, SharedState, UtcDateTime, VelNED, -}; +use crate::shared_state::SharedState; +use crate::types::{CapnProtoSender, Deque, Dops, GnssModes, GpsTime, PosLLH, UtcDateTime, VelNED}; use crate::utils::*; /// SolutionTab struct. diff --git a/console_backend/src/solution_velocity_tab.rs b/console_backend/src/solution_velocity_tab.rs index 9019012fe..49d57ca52 100644 --- a/console_backend/src/solution_velocity_tab.rs +++ b/console_backend/src/solution_velocity_tab.rs @@ -6,7 +6,8 @@ use sbp::messages::navigation::MsgVelNED; use capnp::message::Builder; use crate::constants::{HORIZONTAL_COLOR, NUM_POINTS, VERTICAL_COLOR}; -use crate::types::{CapnProtoSender, Deque, SharedState, VelocityUnits}; +use crate::shared_state::SharedState; +use crate::types::{CapnProtoSender, Deque, VelocityUnits}; use crate::utils::serialize_capnproto_builder; /// SolutionVelocityTab struct. /// diff --git a/console_backend/src/status_bar.rs b/console_backend/src/status_bar.rs index 19a16ab63..0044601a9 100644 --- a/console_backend/src/status_bar.rs +++ b/console_backend/src/status_bar.rs @@ -17,7 +17,8 @@ use crate::errors::*; use crate::piksi_tools_constants::{ ins_error_dict, ins_mode_dict, ins_type_dict, rtk_mode_dict, DR_MODE, EMPTY_STR, RTK_MODES, }; -use crate::types::{ArcBool, BaselineNED, CapnProtoSender, GnssModes, PosLLH, SharedState}; +use crate::shared_state::SharedState; +use crate::types::{ArcBool, BaselineNED, CapnProtoSender, GnssModes, PosLLH}; use crate::utils::{bytes_to_kb, decisec_to_sec, serialize_capnproto_builder}; #[derive(Debug, Clone)] diff --git a/console_backend/src/tracking_signals_tab.rs b/console_backend/src/tracking_signals_tab.rs index 59724e130..882bb45b8 100644 --- a/console_backend/src/tracking_signals_tab.rs +++ b/console_backend/src/tracking_signals_tab.rs @@ -8,9 +8,16 @@ use capnp::message::Builder; use log::warn; -use crate::constants::*; -use crate::piksi_tools_constants::*; -use crate::types::*; +use crate::constants::{ + CHART_XMIN_OFFSET_NO_TRACKING, CHART_XMIN_OFFSET_TRACKING, GLO_FCN_OFFSET, GLO_SLOT_SAT_MAX, + NUM_POINTS, SHOW_LEGEND, TRACKING_UPDATE_PERIOD, TRK_RATE, +}; +use crate::piksi_tools_constants::{ + BDS2_B1_STR, BDS2_B2_STR, GAL_E1B_STR, GAL_E7I_STR, GLO_L1OF_STR, GLO_L2OF_STR, GPS_L1CA_STR, + GPS_L2CM_STR, QZS_L1CA_STR, QZS_L2CM_STR, SBAS_L1_STR, +}; +use crate::shared_state::SharedState; +use crate::types::{CapnProtoSender, Cn0Age, Cn0Dict, Deque, ObservationMsg, SignalCodes}; use crate::utils::{serialize_capnproto_builder, signal_key_color, signal_key_label}; use sbp::messages::tracking::{MeasurementState, TrackingChannelState}; @@ -426,6 +433,7 @@ mod tests { use std::thread::sleep; use super::*; + use crate::types::TestSender; use sbp::messages::{ gnss::{CarrierPhase, GPSTime, GnssSignal}, observation::{Doppler, MsgObs, ObservationHeader, PackedObsContent}, diff --git a/console_backend/src/types.rs b/console_backend/src/types.rs index ea7534606..5758f3765 100644 --- a/console_backend/src/types.rs +++ b/console_backend/src/types.rs @@ -1,20 +1,25 @@ -use crate::common_constants::{self as cc, SbpLogging}; -use crate::constants::*; -use crate::errors::*; -use crate::log_panel::LogLevel; -use crate::output::{CsvLogging, CsvSerializer}; -use crate::piksi_tools_constants::*; -use crate::settings_tab; -use crate::update_tab::UpdateTabUpdate; -use crate::utils::{mm_to_m, ms_to_sec, set_connected_frontend}; - -use anyhow::{Context, Result as AHResult}; +use crate::constants::{ + DGNSS, DGNSS_COLOR, DGNSS_LABEL, DR, DR_COLOR, DR_LABEL, FIXED, FIXED_COLOR, FIXED_LABEL, + FLOAT, FLOAT_COLOR, FLOAT_LABEL, FLOW_CONTROL_HARDWARE, FLOW_CONTROL_NONE, + FLOW_CONTROL_SOFTWARE, KPH, MPH, MPS, MPS2KPH, MPS2MPH, NO_FIX, NO_FIX_COLOR, NO_FIX_LABEL, + RTK, SBAS, SBAS_COLOR, SBAS_LABEL, SPP, SPP_COLOR, SPP_LABEL, +}; +use crate::piksi_tools_constants::{ + BDS2_B1_STR, BDS2_B2_STR, BDS3_B1CI_STR, BDS3_B1CQ_STR, BDS3_B1CX_STR, BDS3_B3I_STR, + BDS3_B3Q_STR, BDS3_B3X_STR, BDS3_B5I_STR, BDS3_B5Q_STR, BDS3_B5X_STR, BDS3_B7I_STR, + BDS3_B7Q_STR, BDS3_B7X_STR, CODE_NOT_AVAILABLE, GAL_AUX_STR, GAL_E1B_STR, GAL_E1C_STR, + GAL_E1X_STR, GAL_E5I_STR, GAL_E5Q_STR, GAL_E5X_STR, GAL_E6B_STR, GAL_E6C_STR, GAL_E6X_STR, + GAL_E7I_STR, GAL_E7Q_STR, GAL_E7X_STR, GAL_E8I_STR, GAL_E8Q_STR, GAL_E8X_STR, GLO_L1OF_STR, + GLO_L1P_STR, GLO_L2OF_STR, GLO_L2P_STR, GPS_AUX_STR, GPS_L1CA_STR, GPS_L1P_STR, GPS_L2CL_STR, + GPS_L2CM_STR, GPS_L2CX_STR, GPS_L2P_STR, GPS_L5I_STR, GPS_L5Q_STR, GPS_L5X_STR, QZS_AUX_STR, + QZS_L1CA_STR, QZS_L2CL_STR, QZS_L2CM_STR, QZS_L2CX_STR, QZS_L5I_STR, QZS_L5Q_STR, QZS_L5X_STR, + SBAS_AUX_STR, SBAS_L1_STR, SBAS_L5I_STR, SBAS_L5Q_STR, SBAS_L5X_STR, +}; + +use crate::utils::{mm_to_m, ms_to_sec}; +use anyhow::Context; use chrono::{DateTime, Utc}; -use crossbeam::channel::{self, Sender}; -use directories::{ProjectDirs, UserDirs}; -use indexmap::set::IndexSet; -use lazy_static::lazy_static; -use log::error; +use crossbeam::channel; use ordered_float::OrderedFloat; use sbp::codec::dencode::{FramedWrite, IterSinkExt}; use sbp::codec::sbp::SbpEncoder; @@ -32,25 +37,20 @@ use sbp::messages::{ SBP, }; use sbp::messages::{ConcreteMessage, SBPMessage}; -use serde::{Deserialize, Serialize}; use serialport::FlowControl as SPFlowControl; use std::io; -use std::path::Path; use std::{ cmp::{Eq, PartialEq}, collections::HashMap, fmt, fmt::Debug, - fs, hash::Hash, ops::Deref, - path::PathBuf, str::FromStr, sync::{ atomic::{AtomicBool, Ordering::*}, Arc, Mutex, }, - time::Instant, }; pub type Error = anyhow::Error; pub type Result = anyhow::Result; @@ -189,487 +189,6 @@ impl Clone for ArcBool { } } -#[derive(Debug)] -pub struct SharedState(Arc>); - -impl SharedState { - pub fn new() -> SharedState { - SharedState(Arc::new(Mutex::new(SharedStateInner::default()))) - } - pub fn is_running(&self) -> bool { - let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).running - } - pub fn set_running(&self, set_to: bool, mut client_send: S) - where - S: CapnProtoSender, - { - if set_to { - set_connected_frontend(cc::ApplicationStates::CONNECTED, &mut client_send); - } else { - set_connected_frontend(cc::ApplicationStates::DISCONNECTED, &mut client_send); - self.set_current_connection(EMPTY_STR.to_string()); - } - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).running = set_to; - } - pub fn is_server_running(&self) -> bool { - let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).server_running - } - pub fn stop_server_running(&self) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).running = false; - (*shared_data).server_running = false; - } - pub fn is_paused(&self) -> bool { - let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).paused - } - pub fn set_paused(&self, set_to: bool) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).paused = set_to; - } - pub fn debug(&self) -> bool { - let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).debug - } - pub fn set_debug(&self, set_to: bool) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).debug = set_to; - } - pub fn current_connection(&self) -> String { - let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).status_bar.current_connection.clone() - } - pub fn set_current_connection(&self, current_connection: String) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).status_bar.current_connection = current_connection; - } - pub fn logging_directory(&self) -> PathBuf { - let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - let mut folders = (*shared_data).connection_history.folders(); - if let Some(folder) = folders.pop() { - PathBuf::from(folder) - } else { - LOG_DIRECTORY.path() - } - } - pub fn set_log_level(&self, log_level: LogLevel) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).log_panel.log_level = log_level.clone(); - log::set_max_level(log_level.level_filter()); - } - pub fn log_level(&self) -> LogLevel { - let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).log_panel.log_level.clone() - } - pub fn sbp_logging(&self) -> SbpLogging { - let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).logging_bar.sbp_logging.clone() - } - pub fn csv_logging(&self) -> CsvLogging { - let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).logging_bar.csv_logging.clone() - } - pub fn set_logging_directory(&self, directory: PathBuf) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - let directory = if directory.starts_with("~/") { - if let Ok(dir) = directory.strip_prefix("~/") { - user_directory().join(dir) - } else { - directory - } - } else { - directory - }; - (*shared_data).logging_bar.logging_directory = directory; - } - pub fn set_sbp_logging(&self, logging: SbpLogging) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).logging_bar.sbp_logging = logging; - } - pub fn set_csv_logging(&self, logging: CsvLogging) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).logging_bar.csv_logging = logging; - } - pub fn folder_history(&self) -> IndexSet { - let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).connection_history.folders() - } - pub fn file_history(&self) -> IndexSet { - let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).connection_history.files() - } - pub fn address_history(&self) -> IndexSet
{ - let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).connection_history.addresses() - } - pub fn update_folder_history(&self, folder: PathBuf) { - let folder = String::from(folder.to_str().expect(CONVERT_TO_STR_FAILURE)); - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).connection_history.record_folder(folder); - } - pub fn update_file_history(&self, filename: String) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).connection_history.record_file(filename); - } - pub fn update_tcp_history(&self, host: String, port: u16) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).connection_history.record_address(host, port); - } - pub fn start_vel_log(&self, path: &Path) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).solution_tab.velocity_tab.log_file = match CsvSerializer::new(path) { - Ok(vel_csv) => Some(vel_csv), - Err(e) => { - error!("issue creating file, {:?}, error, {}", path, e); - None - } - } - } - pub fn end_vel_log(&self) -> Result<()> { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - if let Some(ref mut log) = (*shared_data).solution_tab.velocity_tab.log_file { - log.flush()?; - } - (*shared_data).solution_tab.velocity_tab.log_file = None; - Ok(()) - } - pub fn start_pos_log(&self, path: &Path) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).solution_tab.position_tab.log_file = match CsvSerializer::new(path) { - Ok(vel_csv) => Some(vel_csv), - Err(e) => { - error!("issue creating file, {:?}, error, {}", path, e); - None - } - } - } - pub fn end_pos_log(&self) -> Result<()> { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - if let Some(ref mut log) = (*shared_data).solution_tab.position_tab.log_file { - log.flush()?; - } - (*shared_data).solution_tab.position_tab.log_file = None; - Ok(()) - } - pub fn start_baseline_log(&self, path: &Path) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).baseline_tab.log_file = match CsvSerializer::new(path) { - Ok(vel_csv) => Some(vel_csv), - Err(e) => { - error!("issue creating file, {:?}, error, {}", path, e); - None - } - } - } - pub fn end_baseline_log(&self) -> Result<()> { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - if let Some(ref mut log) = (*shared_data).baseline_tab.log_file { - log.flush()?; - } - (*shared_data).baseline_tab.log_file = None; - Ok(()) - } - pub fn update_tab_sender(&self) -> Option>> { - let shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).update_tab_sender.clone() - } - pub fn set_update_tab_sender(&self, sender: Sender>) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - (*shared_data).update_tab_sender = Some(sender); - } - pub fn settings_needs_update(&self) -> bool { - self.lock() - .expect(SHARED_STATE_LOCK_MUTEX_FAILURE) - .settings_tab - .needs_update() - } - pub fn take_settings_state(&self) -> SettingsTabState { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - shared_data.settings_tab.take() - } - pub fn set_settings_refresh(&self, set_to: bool) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - shared_data.settings_tab.refresh = set_to; - } - pub fn set_settings_save(&self, set_to: bool) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - shared_data.settings_tab.save = set_to; - } - pub fn set_settings_reset(&self, set_to: bool) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - shared_data.settings_tab.reset = set_to; - } - pub fn set_export_settings(&self, path: Option) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - shared_data.settings_tab.export = path; - } - pub fn set_import_settings(&self, path: Option) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - shared_data.settings_tab.import = path; - } - pub fn set_write_setting(&self, setting: Option) { - let mut shared_data = self.lock().expect(SHARED_STATE_LOCK_MUTEX_FAILURE); - shared_data.settings_tab.write = setting; - } -} - -impl Deref for SharedState { - type Target = Mutex; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl Default for SharedState { - fn default() -> Self { - Self::new() - } -} - -impl Clone for SharedState { - fn clone(&self) -> Self { - SharedState { - 0: Arc::clone(&self.0), - } - } -} - -#[derive(Debug)] -pub struct SharedStateInner { - pub(crate) status_bar: StatusBarState, - pub(crate) logging_bar: LoggingBarState, - pub(crate) log_panel: LogPanelState, - pub(crate) tracking_tab: TrackingTabState, - pub(crate) paused: bool, - pub(crate) connection_history: ConnectionHistory, - pub(crate) running: bool, - pub(crate) debug: bool, - pub(crate) server_running: bool, - pub(crate) solution_tab: SolutionTabState, - pub(crate) baseline_tab: BaselineTabState, - pub(crate) advanced_spectrum_analyzer_tab: AdvancedSpectrumAnalyzerTabState, - pub(crate) update_tab_sender: Option>>, - pub(crate) settings_tab: SettingsTabState, -} -impl SharedStateInner { - pub fn new() -> SharedStateInner { - let connection_history = ConnectionHistory::new(); - let log_directory = connection_history.folders().pop(); - SharedStateInner { - status_bar: StatusBarState::new(), - logging_bar: LoggingBarState::new(log_directory), - log_panel: LogPanelState::new(), - tracking_tab: TrackingTabState::new(), - paused: false, - debug: false, - connection_history, - running: false, - server_running: true, - solution_tab: SolutionTabState::new(), - baseline_tab: BaselineTabState::new(), - advanced_spectrum_analyzer_tab: AdvancedSpectrumAnalyzerTabState::new(), - update_tab_sender: None, - settings_tab: SettingsTabState::new(), - } - } -} -impl Default for SharedStateInner { - fn default() -> Self { - SharedStateInner::new() - } -} - -#[derive(Debug)] -pub struct StatusBarState { - pub current_connection: String, -} - -impl StatusBarState { - fn new() -> StatusBarState { - StatusBarState { - current_connection: String::from(""), - } - } -} - -#[derive(Debug)] -pub struct LoggingBarState { - pub sbp_logging: SbpLogging, - pub csv_logging: CsvLogging, - pub logging_directory: PathBuf, -} - -impl LoggingBarState { - fn new(log_directory: Option) -> LoggingBarState { - let logging_directory = if let Some(dir) = log_directory { - PathBuf::from(dir) - } else { - LOG_DIRECTORY.path() - }; - LoggingBarState { - sbp_logging: SbpLogging::OFF, - csv_logging: CsvLogging::OFF, - logging_directory, - } - } -} - -#[derive(Debug)] -pub struct LogPanelState { - pub log_level: LogLevel, -} - -impl LogPanelState { - fn new() -> LogPanelState { - LogPanelState { - log_level: LogLevel::INFO, - } - } -} - -#[derive(Debug)] -pub struct TrackingTabState { - pub signals_tab: TrackingSignalsTabState, -} - -impl TrackingTabState { - fn new() -> TrackingTabState { - TrackingTabState { - signals_tab: TrackingSignalsTabState::new(), - } - } -} - -#[derive(Debug)] -pub struct TrackingSignalsTabState { - pub check_visibility: Vec, -} - -impl TrackingSignalsTabState { - fn new() -> TrackingSignalsTabState { - TrackingSignalsTabState { - check_visibility: vec![], - } - } -} - -#[derive(Debug)] -pub struct BaselineTabState { - pub(crate) clear: bool, - pub(crate) pause: bool, - pub(crate) reset: bool, - pub(crate) log_file: Option, -} - -impl BaselineTabState { - fn new() -> BaselineTabState { - BaselineTabState { - clear: false, - pause: false, - reset: false, - log_file: None, - } - } -} - -#[derive(Debug)] -pub struct SolutionTabState { - pub position_tab: SolutionPositionTabState, - pub velocity_tab: SolutionVelocityTabState, -} - -impl SolutionTabState { - fn new() -> SolutionTabState { - SolutionTabState { - position_tab: SolutionPositionTabState::new(), - velocity_tab: SolutionVelocityTabState::new(), - } - } -} - -#[derive(Debug)] -pub struct SolutionPositionTabState { - pub clear: bool, - pub ins_status_flags: u32, - pub last_ins_status_receipt_time: Instant, - pub last_odo_update_time: Instant, - pub pause: bool, - pub unit: String, - pub log_file: Option, -} - -impl SolutionPositionTabState { - fn new() -> SolutionPositionTabState { - SolutionPositionTabState { - clear: false, - ins_status_flags: 0, - last_ins_status_receipt_time: Instant::now(), - last_odo_update_time: Instant::now(), - pause: false, - unit: String::from(DEGREES), - log_file: None, - } - } -} - -#[derive(Debug)] -pub struct SolutionVelocityTabState { - pub unit: String, - pub log_file: Option, -} - -impl SolutionVelocityTabState { - fn new() -> SolutionVelocityTabState { - SolutionVelocityTabState { - unit: String::from(MPS), - log_file: None, - } - } -} - -#[derive(Debug)] -pub struct AdvancedSpectrumAnalyzerTabState { - pub channel_idx: u16, -} - -impl AdvancedSpectrumAnalyzerTabState { - fn new() -> AdvancedSpectrumAnalyzerTabState { - AdvancedSpectrumAnalyzerTabState { channel_idx: 0 } - } -} - -#[derive(Debug, Default)] -pub struct SettingsTabState { - pub refresh: bool, - pub reset: bool, - pub save: bool, - pub export: Option, - pub import: Option, - pub write: Option, -} - -impl SettingsTabState { - fn new() -> Self { - Default::default() - } - - fn take(&mut self) -> SettingsTabState { - std::mem::take(self) - } - - fn needs_update(&self) -> bool { - self.refresh - || self.reset - || self.save - || self.export.is_some() - || self.import.is_some() - || self.write.is_some() - } -} - // Main Tab Types. #[derive(Clone, Copy, Debug, PartialEq)] pub enum RealtimeDelay { @@ -677,192 +196,6 @@ pub enum RealtimeDelay { Off, } -// Navbar Types. - -/// Directory struct for storing informating and helpers pertaining to project directory. -/// -/// Taken from swift-cli/swift/src/types.rs. -/// impl taken from swift-cli/swift/src/lib.rs. -#[derive(Debug)] -pub struct Directory { - path: PathBuf, -} -lazy_static! { - pub static ref DATA_DIRECTORY: Directory = Directory::new_data_directory(); -} -lazy_static! { - pub static ref LOG_DIRECTORY: Directory = Directory::new_log_directory(); -} -impl Directory { - pub fn new_data_directory() -> Directory { - Directory { - path: create_data_dir().unwrap(), - } - } - - pub fn new_log_directory() -> Directory { - Directory { - path: user_directory().join(DEFAULT_LOG_DIRECTORY), - } - } - /// Return a clone to the private path PathBuf. - pub fn path(&self) -> PathBuf { - self.path.clone() - } -} -impl Default for Directory { - fn default() -> Self { - Directory::new_data_directory() - } -} - -/// Deduce data directory path and create folder. -/// -/// Taken from swift-cli/swift/src/lib.rs. -/// # Returns -/// - `Ok`: The PathBuf for the data directory path. -/// - `Err`: Issue deducing path or creating the data directory. -fn create_data_dir() -> AHResult { - let proj_dirs = ProjectDirs::from( - APPLICATION_QUALIFIER, - APPLICATION_ORGANIZATION, - APPLICATION_NAME, - ) - .context("could not discover local project directory")?; - let path: PathBuf = ProjectDirs::data_local_dir(&proj_dirs).into(); - fs::create_dir_all(path.clone())?; - Ok(path) -} - -/// Create directory. -/// -/// Taken from swift-cli/swift/src/lib.rs. -/// # Returns -/// - `Ok`: The PathBuf for the data directory path. -/// - `Err`: Issue deducing path or creating the data directory. -pub fn create_directory(directory: PathBuf) -> Result { - fs::create_dir_all(&directory)?; - Ok(directory) -} - -/// Get user directory. -/// -/// # Returns -/// - The PathBuf for the user directory path. Otherwise empty pathbuf. -pub fn user_directory() -> PathBuf { - if let Some(user_dirs) = UserDirs::new() { - PathBuf::from(user_dirs.home_dir()) - } else { - PathBuf::from("") - } -} - -#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct Address { - pub host: String, - pub port: u16, -} - -#[derive(Debug, PartialEq, Serialize, Deserialize)] -pub struct ConnectionHistory { - addresses: IndexSet
, - files: IndexSet, - folders: IndexSet, -} - -impl Default for ConnectionHistory { - fn default() -> Self { - ConnectionHistory::new() - } -} - -impl ConnectionHistory { - /// Attempts to create a new ConnectionHistory from expected filepath otherwise empty. - pub fn new() -> ConnectionHistory { - let filename = DATA_DIRECTORY.path().join(CONNECTION_HISTORY_FILENAME); - if let Ok(file) = fs::File::open(&filename) { - if let Ok(conn_yaml) = serde_yaml::from_reader(file) { - return conn_yaml; - } - } - let mut folders = IndexSet::new(); - if let Ok(default_path) = LOG_DIRECTORY.path().into_os_string().into_string() { - folders.insert(default_path); - } - ConnectionHistory { - addresses: IndexSet::new(), - files: IndexSet::new(), - folders, - } - } - /// Return the filename of the saved connection history file. - fn filename(&self) -> PathBuf { - DATA_DIRECTORY.path().join(CONNECTION_HISTORY_FILENAME) - } - /// Returns a clone of the private addresses vec. - pub fn addresses(&self) -> IndexSet
{ - self.addresses.clone() - } - /// Returns a clone of the private files vec. - pub fn files(&self) -> IndexSet { - self.files.clone() - } - /// Returns a clone of the private folders vec. - pub fn folders(&self) -> IndexSet { - self.folders.clone() - } - /// Attempt to add a new host and port if not the most recent entries. - /// - /// # Parameters - /// - `host`: The TCP host to add to the history. - /// - `port`: The TCP port to add to the history. - pub fn record_address(&mut self, host: String, port: u16) { - let address = Address { host, port }; - self.addresses.shift_remove(&address); - self.addresses.insert(address); - let diff = i32::max(0, self.addresses.len() as i32 - MAX_CONNECTION_HISTORY); - self.addresses = self.addresses.split_off(diff as usize); - - if let Err(e) = self.save() { - error!("Unable to save connection history, {}.", e); - } - } - /// Attempt to add a new filepath if not the most recent entry. - /// - /// # Parameters - /// - `filename`: The path to the file to add to history. - pub fn record_file(&mut self, filename: String) { - self.files.shift_remove(&filename); - self.files.insert(filename); - let diff = i32::max(0, self.files.len() as i32 - MAX_CONNECTION_HISTORY); - self.files = self.files.split_off(diff as usize); - - if let Err(e) = self.save() { - error!("Unable to save connection history, {}.", e); - } - } - /// Attempt to add a new folder if not the most recent entry. - /// - /// # Parameters - /// - `folder`: The path to the folder to add to history. - pub fn record_folder(&mut self, folder: String) { - self.folders.shift_remove(&folder); - self.folders.insert(folder); - let diff = i32::max(0, self.folders.len() as i32 - MAX_CONNECTION_HISTORY); - self.folders = self.folders.split_off(diff as usize); - - if let Err(e) = self.save() { - error!("Unable to save connection history, {}.", e); - } - } - - /// Save the history to the expected filepath. - fn save(&self) -> Result<()> { - serde_yaml::to_writer(fs::File::create(&self.filename())?, self)?; - Ok(()) - } -} - // Enum wrapping around various Vel NED Message types. #[derive(Debug, Clone)] pub struct FlowControl(SPFlowControl); @@ -1305,6 +638,7 @@ impl fmt::Display for SignalCodes { SignalCodes::CodeQzsL5I => QZS_L5I_STR, SignalCodes::CodeQzsL5Q => QZS_L5Q_STR, SignalCodes::CodeQzsL5X => QZS_L5X_STR, + SignalCodes::CodeAuxQzs => QZS_AUX_STR, _ => CODE_NOT_AVAILABLE, }; write!(f, "{}", sat_code_str) @@ -2063,95 +1397,3 @@ impl std::str::FromStr for VelocityUnits { } } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::test_common::{backup_file, data_directories, filename, restore_backup_file}; - use serial_test::serial; - - #[test] - fn create_data_dir_test() { - create_data_dir().unwrap(); - let user_dirs = UserDirs::new().unwrap(); - let home_dir = user_dirs.home_dir(); - #[cfg(target_os = "linux")] - { - assert!(home_dir.join(data_directories::LINUX).exists()); - } - - #[cfg(target_os = "macos")] - { - assert!(home_dir.join(data_directories::MACOS).exists()); - } - #[cfg(target_os = "windows")] - { - assert!(home_dir.join(data_directories::WINDOWS).exists()); - } - } - - #[test] - #[serial] - fn connection_history_save_test() { - let bfilename = filename(); - backup_file(bfilename.clone()); - let conn_history = ConnectionHistory::new(); - conn_history.save().unwrap(); - assert!(bfilename.exists()); - restore_backup_file(bfilename); - } - - #[test] - #[serial] - fn connection_history_additions_test() { - let bfilename = filename(); - backup_file(bfilename.clone()); - - let mut conn_history = ConnectionHistory::new(); - let host1 = String::from("host1"); - let host2 = String::from("host2"); - let port = 100; - - conn_history.record_address(host1.clone(), port); - let addresses = conn_history.addresses(); - let addresses_len = addresses.len(); - let first_addy = addresses.first().unwrap(); - assert_eq!(host1, first_addy.host); - assert_eq!(port, first_addy.port); - - conn_history.record_address(host2.clone(), port); - let addresses = conn_history.addresses(); - let first_addy = addresses.first().unwrap(); - let second_addy = addresses.get_index(1).unwrap(); - assert_eq!(host1, first_addy.host); - assert_eq!(port, first_addy.port); - assert_eq!(host2, second_addy.host); - assert_eq!(port, second_addy.port); - assert_eq!(addresses_len + 1, addresses.len()); - - conn_history.record_address(host1.clone(), port); - let addresses = conn_history.addresses(); - let first_addy = addresses.first().unwrap(); - let second_addy = addresses.get_index(1).unwrap(); - assert_eq!(host2, first_addy.host); - assert_eq!(port, first_addy.port); - assert_eq!(host1, second_addy.host); - assert_eq!(port, second_addy.port); - assert_eq!(addresses_len + 1, addresses.len()); - - let filename1 = String::from("filename1"); - let filename2 = String::from("filename2"); - conn_history.record_file(filename1.clone()); - conn_history.record_file(filename2.clone()); - conn_history.record_file(filename1.clone()); - let files = conn_history.files(); - assert_eq!(filename1, files[1]); - assert_eq!(filename2, files[0]); - - for ele in 0..MAX_CONNECTION_HISTORY { - conn_history.record_file(ele.to_string()); - } - assert_eq!(conn_history.files().len(), MAX_CONNECTION_HISTORY as usize); - restore_backup_file(bfilename); - } -} diff --git a/console_backend/src/update_tab.rs b/console_backend/src/update_tab.rs index c1f9c00f4..d6a2ff22a 100644 --- a/console_backend/src/update_tab.rs +++ b/console_backend/src/update_tab.rs @@ -25,7 +25,8 @@ use crate::errors::{ THREAD_JOIN_FAILURE, }; use crate::fileio::{new_sequence, Fileio}; -use crate::types::{CapnProtoSender, MsgSender, SharedState, LOG_DIRECTORY}; +use crate::shared_state::{SharedState, LOG_DIRECTORY}; +use crate::types::{CapnProtoSender, MsgSender}; use crate::update_downloader::UpdateDownloader; use crate::utils::{compare_semvers, serialize_capnproto_builder}; diff --git a/console_backend/src/utils.rs b/console_backend/src/utils.rs index 7781c1abc..31a65768c 100644 --- a/console_backend/src/utils.rs +++ b/console_backend/src/utils.rs @@ -11,7 +11,7 @@ use serialport::available_ports; use crate::constants::*; use crate::errors::*; use crate::types::{CapnProtoSender, SignalCodes}; -use crate::{common_constants as cc, types::SharedState}; +use crate::{common_constants as cc, shared_state::SharedState}; /// Compare to semvar strings and return true if the later_version is greater than the early version. /// diff --git a/console_backend/tests/mem_benches.rs b/console_backend/tests/mem_benches.rs index b9c771fc7..9d8c07d72 100644 --- a/console_backend/tests/mem_benches.rs +++ b/console_backend/tests/mem_benches.rs @@ -14,7 +14,8 @@ mod mem_bench_impl { use console_backend::{ connection::Connection, process_messages, - types::{ClientSender, RealtimeDelay, SharedState}, + shared_state::SharedState, + types::{ClientSender, RealtimeDelay}, }; const BENCH_FILEPATH: &str = "./tests/data/piksi-relay-1min.sbp";