diff --git a/Cargo.lock b/Cargo.lock index 7fa6bb4af..8bf7e0896 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1502,12 +1502,10 @@ dependencies = [ [[package]] name = "sbp-settings" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad2affc9a3b36ee5a8400cd478b86f99d8a95dafb0ff2d413bf3c17273becc79" +checksum = "dafc7b9002c270b4add37abc18dacd8503d2b931dc0c0484d3513f8e9b832fbc" dependencies = [ - "bindgen", - "cmake", "crossbeam-channel", "crossbeam-utils", "log", diff --git a/console_backend/Cargo.toml b/console_backend/Cargo.toml index 8d2c53a07..324a32eb5 100644 --- a/console_backend/Cargo.toml +++ b/console_backend/Cargo.toml @@ -42,7 +42,7 @@ minreq = { version = "2.4.2", features = ["https"] } regex = { version = "1.5.4" } rust-ini = "0.17.0" sbp = { version = "4.0.3", features = ["json", "link", "swiftnav"] } -sbp-settings = "0.4" +sbp-settings = "0.5.0" env_logger = { version = "0.9", optional = true } mimalloc = { version = "0.1", default-features = false } diff --git a/console_backend/src/lib.rs b/console_backend/src/lib.rs index 335e3c874..b235beb65 100644 --- a/console_backend/src/lib.rs +++ b/console_backend/src/lib.rs @@ -58,7 +58,7 @@ use crate::{ #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; -struct Tabs<'link> { +struct Tabs { pub main: Mutex, pub advanced_imu: Mutex, pub advanced_magnetometer: Mutex, @@ -73,10 +73,10 @@ struct Tabs<'link> { pub advanced_spectrum_analyzer: Mutex, pub status_bar: Mutex, pub update: Mutex, - pub settings: Option>, + pub settings: Option, } -impl<'link> Tabs<'link> { +impl Tabs { fn new( shared_state: shared_state::SharedState, client_sender: client_sender::BoxedClientSender, @@ -130,19 +130,13 @@ impl<'link> Tabs<'link> { shared_state: shared_state::SharedState, client_sender: client_sender::BoxedClientSender, msg_sender: types::MsgSender, - link: sbp::link::Link<'link, ()>, ) -> Self { let mut tabs = Self::new( shared_state.clone(), client_sender.clone(), msg_sender.clone(), ); - tabs.settings = Some(SettingsTab::new( - shared_state, - client_sender, - msg_sender, - link, - )); + tabs.settings = Some(SettingsTab::new(shared_state, client_sender, msg_sender)); tabs } } diff --git a/console_backend/src/process_messages.rs b/console_backend/src/process_messages.rs index 1a1ad143b..32f384f93 100644 --- a/console_backend/src/process_messages.rs +++ b/console_backend/src/process_messages.rs @@ -46,7 +46,6 @@ pub fn process_messages( shared_state.clone(), client_sender.clone(), msg_sender.clone(), - source.stateless_link(), ) } else { Tabs::new( @@ -84,6 +83,9 @@ pub fn process_messages( } for (message, _) in &mut messages { source.send_with_state(&tabs, &message); + if let Some(ref tab) = tabs.settings { + tab.handle_msg(message); + } log::logger().flush(); } if let Some(ref tab) = tabs.settings { diff --git a/console_backend/src/server.rs b/console_backend/src/server.rs index b556c52ac..953b2e34b 100644 --- a/console_backend/src/server.rs +++ b/console_backend/src/server.rs @@ -124,7 +124,7 @@ impl Server { setup_logging(client_send.clone(), shared_state.clone()); let opt = CliOptions::from_filtered_cli(); if let Some(ref path) = opt.settings_yaml { - sbp_settings::settings::load_from_path(path).expect("failed to load settings"); + sbp_settings::setting::load_from_path(path).expect("failed to load settings"); } let conn_manager = ConnectionManager::new(client_send.clone(), shared_state.clone()); // Handle CLI Opts. diff --git a/console_backend/src/settings_tab.rs b/console_backend/src/settings_tab.rs index 4864329bb..bdb6727b6 100644 --- a/console_backend/src/settings_tab.rs +++ b/console_backend/src/settings_tab.rs @@ -6,14 +6,16 @@ use std::time::Duration; use anyhow::anyhow; use capnp::message::Builder; +use crossbeam::channel::{self, Sender}; use ini::Ini; use lazy_static::lazy_static; -use log::{debug, error, warn}; +use log::{error, warn}; use parking_lot::Mutex; -use sbp::link::Link; +use sbp::link::LinkSource; use sbp::messages::piksi::MsgReset; use sbp::messages::settings::MsgSettingsSave; -use sbp_settings::{Client, Setting, SettingKind, SettingValue}; +use sbp::Sbp; +use sbp_settings::{Client, Context, Setting, SettingKind, SettingValue}; use crate::client_sender::BoxedClientSender; use crate::shared_state::{SettingsTabState, SharedState}; @@ -86,36 +88,56 @@ fn tick(settings_tab: &SettingsTab, settings_state: SettingsTabState) { } } -pub struct SettingsTab<'link> { +pub struct SettingsTab { shared_state: SharedState, - client_sender: Mutex, + client_sender: BoxedClientSender, msg_sender: MsgSender, settings: Mutex, - sbp_client: Client<'link>, + sbp_client: Mutex>, + send: Sender, } -impl Drop for SettingsTab<'_> { +impl Drop for SettingsTab { fn drop(&mut self) { self.shared_state.reset_settings_state(); } } -impl<'link> SettingsTab<'link> { +impl SettingsTab { pub fn new( shared_state: SharedState, client_sender: BoxedClientSender, msg_sender: MsgSender, - link: Link<'link, ()>, - ) -> SettingsTab<'link> { + ) -> Self { + let source = LinkSource::new(); + let link = source.link(); + let (send, recv) = channel::unbounded(); + + // this thread will join when `SettingsTab` is dropped, because the channel will disconnect + std::thread::spawn(move || { + for msg in recv.iter() { + source.send(&msg); + } + }); + SettingsTab { shared_state, - client_sender: Mutex::new(client_sender), + client_sender, msg_sender: msg_sender.clone(), settings: Mutex::new(Settings::new()), - sbp_client: Client::new(link, move |msg| msg_sender.send(msg).map_err(Into::into)), + sbp_client: Mutex::new(Client::with_link(link.clone(), move |msg| { + msg_sender.send(msg).map_err(Into::into) + })), + send, } } + pub fn handle_msg(&self, msg: Sbp) { + if self.send.send(msg).is_err() { + warn!("could not forward message to the settings tab"); + }; + } + pub fn stop(&self) { self.shared_state.reset_settings_state(); } @@ -149,9 +171,9 @@ impl<'link> SettingsTab<'link> { for (group, prop) in conf.iter() { for (name, value) in prop.iter() { if let Err(e) = self.write_setting(group.unwrap(), name, value) { - match e.downcast_ref::>() { - Some(sbp_settings::Error::Err( - sbp_settings::WriteSettingError::ReadOnly, + match e.downcast_ref::() { + Some(sbp_settings::Error::WriteError( + sbp_settings::error::WriteError::ReadOnly, )) => {} _ => { self.import_err(&e); @@ -171,7 +193,6 @@ impl<'link> SettingsTab<'link> { let mut import_response = msg.init_settings_import_response(); import_response.set_status("success"); self.client_sender - .lock() .send_data(serialize_capnproto_builder(builder)); } @@ -181,7 +202,6 @@ impl<'link> SettingsTab<'link> { let mut import_response = msg.init_settings_import_response(); import_response.set_status(&err.to_string()); self.client_sender - .lock() .send_data(serialize_capnproto_builder(builder)); } @@ -228,25 +248,22 @@ impl<'link> SettingsTab<'link> { } fn confirm_ins_change(&self) -> Result<()> { - let settings = self.settings.lock(); - let ins_mode = settings + let ins_mode = self + .settings + .lock() .get("ins", "output_mode")? .value - .as_ref() + .clone() .ok_or_else(|| anyhow!("setting not found"))?; - - let ins_on = ins_mode != &SettingValue::String("Disabled".to_string()); - + let ins_on = ins_mode != SettingValue::String("Disabled".to_string()); if ins_on { let recommended_settings = self.get_recommended_ins_setting_changes()?; for recommendation in recommended_settings { self.write_setting(&recommendation.0, &recommendation.1, &recommendation.3)?; } } - self.save()?; self.reset(false)?; - Ok(()) } @@ -256,13 +273,15 @@ impl<'link> SettingsTab<'link> { for setting in RECOMMENDED_INS_SETTINGS.iter() { let value = self .sbp_client - .read_setting(setting.0, setting.1) - .ok_or_else(|| anyhow!("setting not found"))??; - if value != setting.2 { + .lock() + .read_setting(setting.0, setting.1)? + .ok_or_else(|| anyhow!("setting not found"))? + .value; + if value.as_ref() != Some(&setting.2) { recommended_changes.push(( setting.0.to_string(), setting.1.to_string(), - value.to_string(), + value.map_or_else(String::new, |v| v.to_string()), setting.2.to_string(), )); } @@ -292,7 +311,6 @@ impl<'link> SettingsTab<'link> { } self.client_sender - .lock() .send_data(serialize_capnproto_builder(builder)); Ok(()) @@ -300,93 +318,53 @@ impl<'link> SettingsTab<'link> { fn write_setting(&self, group: &str, name: &str, value: &str) -> Result<()> { { - let mut settings = self.settings.lock(); - let setting = settings.get(group, name)?; - - if let Some(ref v) = setting.value { - if v.to_string() == value { - debug!("skipping write because setting has not changed"); + let settings = self.settings.lock(); + if let Ok(e) = settings.get(group, name) { + let current = e.value.as_ref().map(|v| v.to_string()); + if current.as_deref() == Some(value) { return Ok(()); } } - - self.sbp_client.write_setting(group, name, value)?; - if matches!( - setting.setting.kind, - SettingKind::Float | SettingKind::Double - ) { - let new_setting = self - .sbp_client - .read_setting(group, name) - .ok_or_else(|| anyhow!("settting not found"))??; - settings.set(group, name, new_setting)?; - } else { - settings.set(group, name, SettingValue::String(value.to_string()))?; - } } - + let setting = self.sbp_client.lock().write_setting(group, name, value)?; + if matches!( + setting.setting.kind, + SettingKind::Float | SettingKind::Double + ) { + let setting = self + .sbp_client + .lock() + .read_setting(group, name)? + .ok_or_else(|| anyhow!("setting not found"))?; + self.settings.lock().insert(setting); + } else { + self.settings.lock().insert(setting); + } if group == "ins" && name == "output_mode" { self.send_ins_change_response(value)?; } - self.send_table_data(); - Ok(()) } fn read_all_settings(&self) { const TIMEOUT: Duration = Duration::from_secs(15); - let (settings, errors) = self.sbp_client.read_all_timeout(TIMEOUT); + let (ctx, _handle) = Context::with_timeout(TIMEOUT); + let (settings, errors) = self.sbp_client.lock().read_all_ctx(ctx); for e in errors { warn!("{}", e); } - for setting in settings { - let mut settings = self.settings.lock(); - let current_setting = match settings.get_mut(&setting.group, &setting.name) { - Ok(setting) => setting, - Err(_) => { - warn!( - "No settings documentation entry or name: {} in group: {}", - setting.name, setting.group - ); - continue; + for entry in settings { + if let Some(ref value) = entry.value { + if FIRMWARE_VERSION_SETTING_KEY == entry.setting.name { + self.shared_state.set_firmware_version(value.to_string()); } - }; - if FIRMWARE_VERSION_SETTING_KEY == setting.name { - self.shared_state - .set_firmware_version(setting.clone().value); - } - if DGNSS_SOLUTION_MODE_SETTING_KEY == setting.name { - self.shared_state.set_dgnss_enabled(setting.clone().value); - } - - // update possible enum values with the possible values returned by the device - if !setting.fmt_type.is_empty() && current_setting.setting.kind == SettingKind::Enum { - let mut parts = setting.fmt_type.splitn(2, ':'); - let ty = parts.next(); - if !matches!(ty, Some("enum")) { - warn!( - "the type for setting {} in group {} was marked as enum but the device returned {:?}", - setting.name, setting.group, ty, - ); - continue; + if DGNSS_SOLUTION_MODE_SETTING_KEY == entry.setting.name { + self.shared_state.set_dgnss_enabled(value.to_string()); } - let possible_values = match parts.next() { - Some(possible_values) => possible_values, - None => { - warn!( - "setting {} in group {} was marked as enum but had no enumerated_possible_values", - setting.name, setting.group, - ); - continue; - } - }; - current_setting.setting.to_mut().enumerated_possible_values = - Some(possible_values.to_string()); } - - current_setting.value = Some(SettingValue::String(setting.value)); + self.settings.lock().insert(entry); } } @@ -477,7 +455,6 @@ impl<'link> SettingsTab<'link> { } } self.client_sender - .lock() .send_data(serialize_capnproto_builder(builder)); } } @@ -490,7 +467,7 @@ pub struct SaveRequest { } struct Settings { - inner: BTreeMap<&'static str, BTreeMap<&'static str, SettingsEntry>>, + inner: BTreeMap>, } impl Settings { @@ -501,8 +478,8 @@ impl Settings { inner: settings .into_iter() .fold(BTreeMap::new(), |mut settings, setting| { - (*settings.entry(&setting.group).or_default()) - .insert(&setting.name, SettingsEntry::new(setting)); + (*settings.entry(setting.group.clone()).or_default()) + .insert(setting.name.clone(), SettingsEntry::new(setting)); settings }), } @@ -534,35 +511,17 @@ impl Settings { .ok_or_else(|| anyhow!("unknown setting: group: {} name: {}", group, name)) } - fn get_mut<'a, 'b>( - &'a mut self, - group: &'b str, - name: &'b str, - ) -> Result<&'a mut SettingsEntry> { + fn insert(&mut self, entry: sbp_settings::Entry) { self.inner - .get_mut(group) - .map(|g| g.get_mut(name)) - .flatten() - .ok_or_else(|| anyhow!("unknown setting: group: {} name: {}", group, name)) - } - - fn set<'a, 'b>(&'a mut self, group: &'b str, name: &'b str, value: SettingValue) -> Result<()> { - let setting = self - .inner - .get_mut(group) - .map(|g| g.get_mut(name)) - .flatten() - .ok_or_else(|| anyhow!("unknown setting: group: {} name: {}", group, name))?; - setting.value = Some(value); - Ok(()) - } -} - -impl std::ops::Deref for Settings { - type Target = BTreeMap<&'static str, BTreeMap<&'static str, SettingsEntry>>; - - fn deref(&self) -> &Self::Target { - &self.inner + .entry(entry.setting.group.clone()) + .or_default() + .insert( + entry.setting.name.clone(), + SettingsEntry { + setting: entry.setting, + value: entry.value, + }, + ); } }