diff --git a/console_backend/src/cli_options.rs b/console_backend/src/cli_options.rs index 12819645c..e6e371e31 100644 --- a/console_backend/src/cli_options.rs +++ b/console_backend/src/cli_options.rs @@ -7,12 +7,34 @@ use std::{ use strum::VariantNames; use crate::constants::{AVAILABLE_BAUDRATES, AVAILABLE_REFRESH_RATES}; +use crate::log_panel::LogLevel; use crate::types::FlowControl; use crate::{ common_constants::{SbpLogging, Tabs}, types::Connection, }; +#[derive(Debug)] +pub struct CliLogLevel(LogLevel); + +impl Deref for CliLogLevel { + type Target = LogLevel; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl FromStr for CliLogLevel { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + Ok(CliLogLevel(LogLevel::from_str(s).map_err(|_| { + format!("Must choose from available tabs {:?}", LogLevel::VARIANTS) + })?)) + } +} + #[derive(Debug)] pub struct CliTabs(Tabs); @@ -73,6 +95,10 @@ pub struct CliOptions { #[clap(long = "sbp-log")] pub sbp_log: Option, + /// Set Console Log Level Filter. Default: INFO. + #[clap(long = "log-level")] + pub log_level: Option, + /// Set log directory. #[clap(long = "log-dirname")] pub dirname: Option, diff --git a/console_backend/src/common_constants.rs b/console_backend/src/common_constants.rs index bbfa6879e..e821bce5a 100644 --- a/console_backend/src/common_constants.rs +++ b/console_backend/src/common_constants.rs @@ -1,6 +1,21 @@ -// cargo-deps: strum,strum_macros +//! ```cargo +//! [package] +//! edition = "2018" +//! [dependencies] +//! strum = "*" +//! strum_macros = "*" +//! ``` +#![allow(clippy::collapsible_else_if)] +#![allow(clippy::double_parens)] // https://github.com/adsharma/py2many/issues/17 +#![allow(clippy::map_identity)] +#![allow(clippy::needless_return)] +#![allow(clippy::print_literal)] +#![allow(clippy::ptr_arg)] +#![allow(clippy::redundant_static_lifetimes)] // https://github.com/adsharma/py2many/issues/266 +#![allow(clippy::unnecessary_cast)] #![allow(clippy::upper_case_acronyms)] +#![allow(clippy::useless_vec)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(non_upper_case_globals)] @@ -178,6 +193,12 @@ pub enum Keys { NHC, #[strum(serialize = "ZEROVEL")] ZEROVEL, + #[strum(serialize = "YMIN")] + YMIN, + #[strum(serialize = "YMAX")] + YMAX, + #[strum(serialize = "LOG_LEVEL")] + LOG_LEVEL, } #[derive(Clone, Debug, Display, EnumString, EnumVariantNames, Eq, Hash, PartialEq)] diff --git a/console_backend/src/log_panel.rs b/console_backend/src/log_panel.rs index c84d190eb..a8858503d 100644 --- a/console_backend/src/log_panel.rs +++ b/console_backend/src/log_panel.rs @@ -2,12 +2,25 @@ use sbp::messages::logging::MsgLog; use capnp::message::Builder; +use crate::common_constants as cc; use crate::types::*; use crate::utils::serialize_capnproto_builder; use async_logger::Writer; use chrono::Local; -use log::{debug, error, info, warn, Record}; +use log::{debug, error, info, warn, LevelFilter, Record}; + +pub type LogLevel = cc::LogLevel; +impl LogLevel { + pub fn level_filter(&self) -> LevelFilter { + match self { + cc::LogLevel::DEBUG => LevelFilter::Debug, + cc::LogLevel::INFO => LevelFilter::Info, + cc::LogLevel::NOTICE | cc::LogLevel::WARNING => LevelFilter::Warn, + cc::LogLevel::ERROR => LevelFilter::Error, + } + } +} // Custom formatting of `log::Record` to account for SbpLog values pub fn splitable_log_formatter(record: &Record) -> String { diff --git a/console_backend/src/server.rs b/console_backend/src/server.rs index 1087e9ced..17fe20ad1 100644 --- a/console_backend/src/server.rs +++ b/console_backend/src/server.rs @@ -16,11 +16,10 @@ use std::{ }; use crate::cli_options::*; -use crate::common_constants::LogLevel; use crate::console_backend_capnp as m; use crate::constants::LOG_WRITER_BUFFER_MESSAGE_COUNT; use crate::errors::*; -use crate::log_panel::{splitable_log_formatter, LogPanelWriter}; +use crate::log_panel::{splitable_log_formatter, LogLevel, LogPanelWriter}; use crate::output::{CsvLogging, SbpLogging}; use crate::types::{ClientSender, FlowControl, ServerState, SharedState}; use crate::utils::{refresh_loggingbar, refresh_navbar}; @@ -112,6 +111,12 @@ fn handle_cli( 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 { @@ -161,8 +166,7 @@ impl Server { }; let shared_state = SharedState::new(); let server_state = ServerState::new(); - refresh_navbar(&mut client_send.clone(), shared_state.clone()); - refresh_loggingbar(&mut client_send.clone(), shared_state.clone()); + let logger = Logger::builder() .buf_size(LOG_WRITER_BUFFER_MESSAGE_COUNT) .formatter(splitable_log_formatter) @@ -171,7 +175,6 @@ impl Server { .unwrap(); log::set_boxed_logger(Box::new(logger)).expect("Failed to set logger"); - log::set_max_level(log::LevelFilter::Info); // Handle CLI Opts. handle_cli( @@ -180,6 +183,8 @@ impl Server { client_send.clone(), shared_state.clone(), ); + refresh_navbar(&mut client_send.clone(), shared_state.clone()); + refresh_loggingbar(&mut client_send.clone(), shared_state.clone()); thread::spawn(move || loop { let buf = server_recv.recv(); if let Ok(buf) = buf { @@ -316,14 +321,13 @@ impl Server { } m::message::LogLevelFront(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 log_level = cv_in .get_log_level() .expect(CAP_N_PROTO_DESERIALIZATION_FAILURE); - (*shared_data).log_panel.log_level = + let log_level = LogLevel::from_str(log_level).expect(CONVERT_TO_STR_FAILURE); + 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 diff --git a/console_backend/src/types.rs b/console_backend/src/types.rs index f8bfc708b..d3abf9e50 100644 --- a/console_backend/src/types.rs +++ b/console_backend/src/types.rs @@ -1,7 +1,8 @@ -use crate::common_constants::{self as cc, LogLevel, SbpLogging}; +use crate::common_constants::{self as cc, SbpLogging}; use crate::constants::*; use crate::errors::*; use crate::formatters::*; +use crate::log_panel::LogLevel; use crate::output::CsvLogging; use crate::piksi_tools_constants::*; use crate::process_messages::process_messages; @@ -355,6 +356,15 @@ impl SharedState { 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() @@ -508,7 +518,7 @@ pub struct LogPanelState { impl LogPanelState { fn new() -> LogPanelState { LogPanelState { - log_level: LogLevel::DEBUG, + log_level: LogLevel::INFO, } } } diff --git a/console_backend/src/utils.rs b/console_backend/src/utils.rs index 70fe6db66..a817c3db2 100644 --- a/console_backend/src/utils.rs +++ b/console_backend/src/utils.rs @@ -113,6 +113,8 @@ pub fn refresh_navbar(client_send: &mut P, shared_state: Share prevous_files.set(i as u32, filename); } + nav_bar_status.set_log_level(&shared_state.log_level().to_string()); + client_send.send_data(serialize_capnproto_builder(builder)); } diff --git a/poetry.lock b/poetry.lock index af58db563..afe220926 100644 --- a/poetry.lock +++ b/poetry.lock @@ -305,7 +305,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "py2many" -version = "0.1.0" +version = "0.2.1" description = "Python to CLike language transpiler." category = "dev" optional = false @@ -314,9 +314,9 @@ develop = false [package.source] type = "git" -url = "https://github.com/john-michaelburke/py2many" -reference = "console" -resolved_reference = "8b7574ad46579405ebed8af386028518d9a692db" +url = "https://github.com/adsharma/py2many.git" +reference = "main" +resolved_reference = "1f2a1f32c7917d51c080a7eafea03b01a169edc7" [[package]] name = "pycapnp" @@ -504,7 +504,7 @@ python-versions = "*" [metadata] lock-version = "1.1" python-versions = ">=3.9,<3.10" -content-hash = "5aa8b9ba23d1a45e3914a28189cc664ac67f6500be4c469e873661886a0ff08f" +content-hash = "def2e7676002d0fa998b096d2bb236fa2faabb2740d9b091f273f44ac7730785" [metadata.files] altgraph = [ diff --git a/pyproject.toml b/pyproject.toml index ef24986a3..9f05ff5d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ Cython = "^0.29.21" nuitka = "<=0.6.12.4" psutil = "5.8.0" qt5-applications = [{ version = "^5.15.2" }] -py2many = { git = "https://github.com/john-michaelburke/py2many", rev = "console" } +py2many = { git = "https://github.com/adsharma/py2many.git", rev = "main" } [build-system] requires = [ diff --git a/requirements-dev.txt b/requirements-dev.txt index b1cc5da76..28f9b419a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -25,7 +25,7 @@ pefile==2021.5.24; sys_platform == "win32" and python_version >= "3.5" and pytho pkgconfig==1.5.4; python_version >= "3.3" and python_version < "4.0" pluggy==0.13.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" psutil==5.8.0; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") -py2many @ git+https://github.com/john-michaelburke/py2many@console ; python_version >= "3.8" +py2many @ git+https://github.com/adsharma/py2many.git@main ; python_version >= "3.8" py==1.10.0; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" pycapnp==1.1.0 pyinstaller-hooks-contrib==2021.1; python_version >= "3.5" diff --git a/resources/Constants/Constants.qml b/resources/Constants/Constants.qml index 465b63d5a..accd73976 100644 --- a/resources/Constants/Constants.qml +++ b/resources/Constants/Constants.qml @@ -53,8 +53,8 @@ QtObject { } navBar: QtObject { - readonly property int connectionDropdownWidth: 70 - readonly property int serialSelectionDropdownWidth: 90 + readonly property int connectionDropdownWidth: 100 + readonly property int serialSelectionDropdownWidth: 100 readonly property int dropdownHeight: 40 readonly property int buttonHeight: 40 readonly property int buttonSvgHeight: 15 @@ -62,12 +62,12 @@ QtObject { readonly property int navBarMargin: 10 readonly property int plotRefreshRateDropdownWidth: 50 readonly property int serialDeviceBaudRateDropdownWidth: 90 - readonly property int serialDeviceFlowControlDropdownWidth: 100 + readonly property int serialDeviceFlowControlDropdownWidth: 130 readonly property int serialDeviceRefreshWidth: 30 readonly property int connectButtonWidth: 30 readonly property int connectionPauseWidth: 30 readonly property int folderButtonWidth: 30 - readonly property int logLevelButtonWidth: 90 + readonly property int logLevelButtonWidth: 110 readonly property color placeholderTextColor: "#CDC9C9" readonly property int padding: 0 readonly property string connectButtonPath: "images/fontawesome/power-off-solid.svg" diff --git a/resources/LoggingBar.qml b/resources/LoggingBar.qml index 4904b94b5..dce6416ee 100644 --- a/resources/LoggingBar.qml +++ b/resources/LoggingBar.qml @@ -36,7 +36,7 @@ Rectangle { ToolTip.text: !checked ? "On" : "Off" checkable: true visible: Globals.showCsvLog - onClicked: data_model.logging_bar([csvLoggingButton.checked, sbpLoggingButton.currentText, logLevelButton.currentText], folderPathBar.editText) + onClicked: data_model.logging_bar([csvLoggingButton.checked, sbpLoggingButton.currentText], folderPathBar.editText) } ComboBox { @@ -47,7 +47,7 @@ Rectangle { model: sbp_logging_labels ToolTip.visible: hovered ToolTip.text: "SBP Log" - onActivated: data_model.logging_bar([csvLoggingButton.checked, sbpLoggingButton.currentText, logLevelButton.currentText], folderPathBar.editText) + onActivated: data_model.logging_bar([csvLoggingButton.checked, sbpLoggingButton.currentText], folderPathBar.editText) background: Rectangle { border.width: 3 @@ -68,7 +68,7 @@ Rectangle { var text = folderPathBar.currentText; folderPathBar.currentIndex = -1; folderPathBar.editText = text; - data_model.logging_bar([csvLoggingButton.checked, sbpLoggingButton.currentText, logLevelButton.currentText], folderPathBar.editText); + data_model.logging_bar([csvLoggingButton.checked, sbpLoggingButton.currentText], folderPathBar.editText); } Text { diff --git a/resources/NavBar.qml b/resources/NavBar.qml index 57fbf8103..77897a9bb 100644 --- a/resources/NavBar.qml +++ b/resources/NavBar.qml @@ -57,11 +57,13 @@ Rectangle { serialDeviceRefresh.visible = true; serialDeviceBaudRate.visible = true; serialDeviceFlowControl.visible = true; + serialDeviceFill.visible = true; } else { serialDevice.visible = false; serialDeviceRefresh.visible = false; serialDeviceBaudRate.visible = false; serialDeviceFlowControl.visible = false; + serialDeviceFill.visible = false; } } @@ -164,6 +166,13 @@ Rectangle { } + Item { + id: serialDeviceFill + + visible: false + Layout.fillWidth: true + } + Row { id: tcpUrlBarPortBar @@ -354,6 +363,7 @@ Rectangle { previous_ports = navBarData.previous_ports; previous_files = navBarData.previous_files; connectButton.checked = navBarData.connected; + logLevelButton.currentIndex = log_level_labels.indexOf(navBarData.log_level); } } diff --git a/src/main/python/constants.py b/src/main/python/constants.py index a643361f2..38ccf7c8c 100644 --- a/src/main/python/constants.py +++ b/src/main/python/constants.py @@ -91,6 +91,7 @@ class Keys(str, Enum): ZEROVEL = "ZEROVEL" YMIN = "YMIN" YMAX = "YMAX" + LOG_LEVEL = "LOG_LEVEL" class ApplicationStates(str, Enum): diff --git a/src/main/python/main.py b/src/main/python/main.py index 14761c54b..f309d25a0 100644 --- a/src/main/python/main.py +++ b/src/main/python/main.py @@ -260,6 +260,7 @@ def receive_messages(app_, backend, messages): NAV_BAR[Keys.PREVIOUS_HOSTS][:] = m.navBarStatus.previousHosts NAV_BAR[Keys.PREVIOUS_PORTS][:] = m.navBarStatus.previousPorts NAV_BAR[Keys.PREVIOUS_FILES][:] = m.navBarStatus.previousFiles + NAV_BAR[Keys.LOG_LEVEL] = m.navBarStatus.logLevel elif m.which == Message.Union.LoggingBarStatus: LOGGING_BAR[Keys.PREVIOUS_FOLDERS][:] = m.loggingBarStatus.previousFolders LOGGING_BAR[Keys.CSV_LOGGING] = m.loggingBarStatus.csvLogging @@ -405,7 +406,6 @@ def logging_bar(self, buttons, directory) -> None: m.loggingBarFront = m.init(Message.Union.LoggingBarFront) m.loggingBarFront.csvLogging = buttons[0] m.loggingBarFront.sbpLogging = buttons[1] - m.loggingBarFront.logLevel = buttons[2] m.loggingBarFront.directory = directory buffer = m.to_bytes() self.endpoint.send_message(buffer) diff --git a/src/main/python/nav_bar.py b/src/main/python/nav_bar.py index f8b0c10a6..63b6719c5 100644 --- a/src/main/python/nav_bar.py +++ b/src/main/python/nav_bar.py @@ -17,6 +17,7 @@ Keys.PREVIOUS_PORTS: [], Keys.PREVIOUS_FILES: [], Keys.LOG_LEVEL_LABELS: [LogLevel.ERROR, LogLevel.WARNING, LogLevel.NOTICE, LogLevel.INFO, LogLevel.DEBUG], + Keys.LOG_LEVEL: LogLevel.INFO, } @@ -31,6 +32,7 @@ class NavBarData(QObject): # pylint: disable=too-many-instance-attributes _previous_ports: List[str] = [] _previous_files: List[str] = [] _log_level_labels: List[str] = [] + _log_level: str def get_log_level_labels(self) -> List[str]: return self._log_level_labels @@ -40,6 +42,14 @@ def set_log_level_labels(self, log_level_labels: List[str]) -> None: log_level_labels = Property(QTKeys.QVARIANTLIST, get_log_level_labels, set_log_level_labels) # type: ignore + def get_log_level(self) -> str: + return self._log_level + + def set_log_level(self, log_level: str) -> None: + self._log_level = log_level + + log_level = Property(str, get_log_level, set_log_level) + def get_available_ports(self) -> List[str]: return self._available_ports @@ -127,4 +137,5 @@ def fill_data(self, cp: NavBarData) -> NavBarData: # pylint:disable=no-self-use cp.set_previous_ports(NAV_BAR[Keys.PREVIOUS_PORTS]) cp.set_previous_files(NAV_BAR[Keys.PREVIOUS_FILES]) cp.set_log_level_labels(NAV_BAR[Keys.LOG_LEVEL_LABELS]) + cp.set_log_level(NAV_BAR[Keys.LOG_LEVEL]) return cp diff --git a/src/main/resources/base/console_backend.capnp b/src/main/resources/base/console_backend.capnp index 805c87839..872de8a88 100644 --- a/src/main/resources/base/console_backend.capnp +++ b/src/main/resources/base/console_backend.capnp @@ -75,6 +75,7 @@ struct NavBarStatus { availableRefreshRates @4 : List(UInt8); previousPorts @5: List(UInt16); previousFiles @6: List(Text); + logLevel @7: Text; } struct StatusBarStatus {