From fa9687df216c79b2cd7cf879998843d2a695f678 Mon Sep 17 00:00:00 2001 From: Yuriy Larin Date: Fri, 2 Feb 2024 19:41:05 +0200 Subject: [PATCH] moved, refactored --- Cargo.toml | 15 ++++--- brainbit/Cargo.toml | 14 +++---- brainbit/src/bbit.rs | 9 ++++ brainbit/src/bbit/control.rs | 8 +--- brainbit/src/bbit/device.rs | 32 ++++++++------- brainbit/src/bbit/errors.rs | 29 +++++++++++++ brainbit/src/bbit/{mod.rs => internals.rs} | 43 +++----------------- brainbit/src/bbit/responses.rs | 22 +++++----- brainbit/src/bbit/traits.rs | 29 +++++++++++++ brainbit/src/bbit/{eeg_uuids.rs => uuids.rs} | 0 brainbit/src/lib.rs | 30 +------------- examples/battery_level/Cargo.toml | 3 +- examples/battery_level/src/main.rs | 13 +++--- examples/connect/Cargo.toml | 2 +- examples/connect/src/main.rs | 4 +- handler/Cargo.toml | 3 +- handler/src/main_handler.rs | 10 +++-- mainapp/Cargo.toml | 2 +- mainapp/src/main.rs | 4 +- 19 files changed, 144 insertions(+), 128 deletions(-) create mode 100644 brainbit/src/bbit.rs create mode 100644 brainbit/src/bbit/errors.rs rename brainbit/src/bbit/{mod.rs => internals.rs} (52%) create mode 100644 brainbit/src/bbit/traits.rs rename brainbit/src/bbit/{eeg_uuids.rs => uuids.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index df5a15b..77083cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,14 +16,19 @@ edition = "2021" repository = "https://github.com/blandger/mielophone" [workspace.dependencies] -btleplug = { version = "0.11.0", features = ["serde"] } +btleplug = { version = "0.11.5", features = ["serde"] } #btleplug = { path = "../btleplug-blandger" } -tokio = { version = "1.28.2", features = ["full"] } -tracing = "0.1.37" -tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +tokio = { version = "1.36.0", features = ["full"] } +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +async-trait = "0.1.77" +futures = "0.3.30" +tokio-stream = { version = "0.1.14", features = ["sync"] } +uuid = "1.7.0" +thiserror = "1.0.56" color-eyre = "0.6.2" -chrono = "0.4.25" +chrono = "0.4.33" #[build] #rustflags = ["-C", "link-args=-gz"] #zip debug data \ No newline at end of file diff --git a/brainbit/Cargo.toml b/brainbit/Cargo.toml index d57ae16..1c59f6e 100644 --- a/brainbit/Cargo.toml +++ b/brainbit/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "lib" +name = "brainbit" version = "0.1.0" authors.workspace = true edition.workspace = true @@ -8,13 +8,13 @@ description = "Library wrapper above BLE to support BrainBit device only" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-trait = "0.1.68" -futures = "0.3" btleplug = { workspace = true, features = ["serde"] } -thiserror = "1.0.40" +thiserror.workspace = true tokio = { workspace = true, features = ["rt", "sync"] } -tokio-stream = { version = "0.1.11", features = ["sync"] } -uuid = "1.3.3" +tokio-stream.workspace = true +uuid.workspace = true tracing.workspace = true -tracing-subscriber = { workspace = true, features = ["env-filter"] } \ No newline at end of file +tracing-subscriber = { workspace = true, features = ["env-filter"] } +async-trait.workspace = true +futures.workspace = true \ No newline at end of file diff --git a/brainbit/src/bbit.rs b/brainbit/src/bbit.rs new file mode 100644 index 0000000..5205f70 --- /dev/null +++ b/brainbit/src/bbit.rs @@ -0,0 +1,9 @@ +pub(crate) mod control; +pub mod device; +pub mod errors; +pub(crate) mod internals; +pub mod resist; +pub mod responses; +pub(crate) mod sealed; +pub mod traits; +pub mod uuids; diff --git a/brainbit/src/bbit/control.rs b/brainbit/src/bbit/control.rs index 7c88ad8..9ab505c 100644 --- a/brainbit/src/bbit/control.rs +++ b/brainbit/src/bbit/control.rs @@ -1,5 +1,5 @@ use crate::bbit::device::BBitResult; -use crate::bbit::eeg_uuids::WRITE_COMMAN_UUID; +use crate::bbit::uuids::WRITE_COMMAN_UUID; use crate::{find_characteristic, Error}; use btleplug::api::{Characteristic, Peripheral as _, WriteType}; use btleplug::platform::Peripheral; @@ -50,10 +50,6 @@ impl ControlPoint { .await .map_err(Error::BleError) } - - /* unsafe fn get_enum_as_u8_slice(p: &T) -> &[u8] { - from_raw_parts((p as *const T) as *const u8, ::core::mem::size_of::()) - }*/ } #[derive(Clone, Debug, PartialEq, Eq)] @@ -154,7 +150,7 @@ impl TryFrom<&[u8]> for ControlPointCommand { #[cfg(test)] mod tests { use super::*; - use crate::bbit::ADS1294ChannelInput; + use crate::bbit::internals::ADS1294ChannelInput; #[test] fn test_resist_command_layout() { diff --git a/brainbit/src/bbit/device.rs b/brainbit/src/bbit/device.rs index 3f1885b..5349184 100644 --- a/brainbit/src/bbit/device.rs +++ b/brainbit/src/bbit/device.rs @@ -1,26 +1,28 @@ -use crate::bbit::control::{ControlCommandType, ControlPoint, ControlPointCommand}; -use crate::bbit::eeg_uuids::{ - EventType, NotifyStream, NotifyUuid, FIRMWARE_REVISION_STRING_UUID, - HARDWARE_REVISION_STRING_UUID, MODEL_NUMBER_STRING_UUID, NSS2_SERVICE_UUID, - SERIAL_NUMBER_STRING_UUID, -}; -use crate::bbit::responses::{DeviceInfo, DeviceStatusData}; -use crate::bbit::sealed::{Bluetooth, Configure, Connected, EventLoop, Level}; -use crate::{find_characteristic, Error}; +use std::collections::BTreeSet; +use std::sync::{Arc, OnceLock}; +use std::time::Duration; -use crate::bbit::{ADS1294ChannelInput, ChannelType, EventHandler, MeasurementType}; use btleplug::{ api::{Central, Characteristic, Manager as _, Peripheral as _, ScanFilter}, platform::{Manager, Peripheral}, }; use futures::stream::StreamExt; -use std::collections::BTreeSet; -use std::sync::{Arc, OnceLock}; -use std::time::Duration; use tokio::sync::{mpsc, oneshot, watch}; use tracing::instrument; use uuid::Uuid; +use crate::bbit::control::{ControlCommandType, ControlPoint, ControlPointCommand}; +use crate::bbit::internals::{ADS1294ChannelInput, ChannelType, MeasurementType}; +use crate::bbit::responses::{DeviceInfo, DeviceStatusData}; +use crate::bbit::sealed::{Bluetooth, Configure, Connected, EventLoop, Level}; +use crate::bbit::traits::EventHandler; +use crate::bbit::uuids::{ + EventType, NotifyStream, NotifyUuid, FIRMWARE_REVISION_STRING_UUID, + HARDWARE_REVISION_STRING_UUID, MODEL_NUMBER_STRING_UUID, NSS2_SERVICE_UUID, + SERIAL_NUMBER_STRING_UUID, +}; +use crate::{find_characteristic, Error}; + pub type BBitResult = Result; /// Structure to contain EEG data and interval. @@ -106,8 +108,8 @@ impl BBitSensor { /// ```rust,no_run /// # #[tokio::main] /// # async fn main() { - /// use lib::Error; - /// use lib::bbit::device::BBitSensor; + /// use brainbit::bbit::device::BBitSensor; + /// use brainbit::bbit::errors::Error; /// /// let mut bbit = BBitSensor::new().await.unwrap() /// // default handling that is applied to BleSensor::block_connect diff --git a/brainbit/src/bbit/errors.rs b/brainbit/src/bbit/errors.rs new file mode 100644 index 0000000..f82e66e --- /dev/null +++ b/brainbit/src/bbit/errors.rs @@ -0,0 +1,29 @@ +use thiserror::Error; + +/// Error type for general brainbit errors and internal btleplug Ble errors +#[derive(Debug, Error)] +pub enum Error { + /// Bluetooth adapter is not found on attempt to scan it + #[error("No BLE adaptor")] + NoBleAdaptor, + /// Could not connect to a device by filter + #[error("No BLE device")] + NoDevice, + /// Device looks as it's not connected, but command was called + #[error("Not connected")] + NotConnected, + /// UUID device's characteristic is missing + #[error("Characteristic not found")] + CharacteristicNotFound, + /// EEG Data packets received from device is not parsed + #[error("Invalid '{0}'")] + InvalidData(String), + /// The command did not return a response + #[error("No command response")] + NoControlPointResponse, + /// An error occurred in the underlying BLE library. + #[error("BLE error: {0}")] + BleError(#[from] btleplug::Error), + #[error("Error generated by event handler")] + HandlerError(#[from] Box), +} diff --git a/brainbit/src/bbit/mod.rs b/brainbit/src/bbit/internals.rs similarity index 52% rename from brainbit/src/bbit/mod.rs rename to brainbit/src/bbit/internals.rs index d91aded..7c02112 100644 --- a/brainbit/src/bbit/mod.rs +++ b/brainbit/src/bbit/internals.rs @@ -1,40 +1,5 @@ -use crate::bbit::device::{BBitResult, CommandData}; -use crate::bbit::responses::DeviceStatusData; -use crate::Error; -use async_trait::async_trait; - -pub(crate) mod control; -pub mod device; -pub mod eeg_uuids; -pub mod resist; -pub mod responses; -pub(crate) mod sealed; - -/// Base trait for handling events coming from a BrainBit device. -#[async_trait] -pub trait EventHandler { - /// Dispatched when a internal device status update is received. - /// - /// Contains the status, cmd error, battery level. - async fn device_status_update(&self, _status: DeviceStatusData) {} - - /// Dispatched when an eeg data is received. - /// - /// Contains information about the O1, O2, T3, T4 + interval. - async fn eeg_update(&mut self, _eeg_data: Vec) {} - - /// Dispatched when measurement data is received over the PMD data UUID. - /// - /// Contains data in a [`CommandData`]. - async fn send_command(&self, _command_data: CommandData) {} - - /// Checked at start of each event loop. - /// - /// Returns [`false`] if the event loop should be terminated and close connection. - async fn should_continue(&self) -> bool { - true - } -} +use crate::bbit::device::BBitResult; +use crate::bbit::errors::Error; /// List of measurement types you can request. #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -57,6 +22,7 @@ pub enum ChannelType { /// Channel 3, o2 (occipital lobe = o, right O2 = 3, } + impl ChannelType { pub fn new(channel_number: u8) -> BBitResult { match channel_number { @@ -70,6 +36,7 @@ impl ChannelType { } } } + impl Into for ChannelType { fn into(self) -> u8 { self as u8 @@ -78,7 +45,7 @@ impl Into for ChannelType { /// Internal constants to assign for Resistance commands #[derive(Debug, Copy, Clone)] -enum ADS1294ChannelInput { +pub enum ADS1294ChannelInput { PowerDownGain6 = 0x00, PowerDownGain3 = 0x91, PowerUpGain1 = 0x48, diff --git a/brainbit/src/bbit/responses.rs b/brainbit/src/bbit/responses.rs index 1ec7fbe..67dd075 100644 --- a/brainbit/src/bbit/responses.rs +++ b/brainbit/src/bbit/responses.rs @@ -58,7 +58,7 @@ pub struct DeviceStatusData { /// NNS service state pub status_nss2: Nss2Status, // Error code. It's reset when new command is received - pub cmd_error: CommandResultError, + pub cmd_error: CommandExecutionState, /// Battery level in percents (%) is stored by lower seven bits, 8-th bit keeps 'charging flag' pub battery_level: u8, // 87 is a max value = 100% charge /// Firmware version @@ -68,7 +68,7 @@ impl Default for DeviceStatusData { fn default() -> Self { Self { status_nss2: Nss2Status::Initial, - cmd_error: CommandResultError::NoError, + cmd_error: CommandExecutionState::Ok, battery_level: 0, firmware_version: 0, } @@ -96,7 +96,7 @@ impl TryFrom> for DeviceStatusData { return Err("device status Vec length"); } let status_nss2 = Nss2Status::try_from(value[0])?; - let cmd_error = CommandResultError::try_from(value[1])?; + let cmd_error = CommandExecutionState::try_from(value[1])?; let battery_level = value[2]; let firmware_version = value[3]; @@ -157,22 +157,22 @@ impl Into for Nss2Status { /// A main GATT NSS2 service sending, executing command result type #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum CommandResultError { +pub enum CommandExecutionState { /// No error after command - NoError, + Ok, /// Command had an incorrect length - ErrorLength, + CommandLengthError, /// Error on changing device mode, changing working mode is not possible - ErrorSwitchMode, + SwitchModeError, } -impl TryFrom for CommandResultError { +impl TryFrom for CommandExecutionState { type Error = &'static str; fn try_from(value: u8) -> Result { match value { - 0x0 => Ok(Self::NoError), - 0x1 => Ok(Self::ErrorLength), - 0x2 => Ok(Self::ErrorSwitchMode), + 0x0 => Ok(Self::Ok), + 0x1 => Ok(Self::CommandLengthError), + 0x2 => Ok(Self::SwitchModeError), _ => Err("BBit command execution result error"), } } diff --git a/brainbit/src/bbit/traits.rs b/brainbit/src/bbit/traits.rs new file mode 100644 index 0000000..69c8182 --- /dev/null +++ b/brainbit/src/bbit/traits.rs @@ -0,0 +1,29 @@ +use crate::bbit::device::CommandData; +use crate::bbit::responses::DeviceStatusData; +use async_trait::async_trait; + +/// Base trait for handling events coming from a BrainBit device. +#[async_trait] +pub trait EventHandler { + /// Dispatched when a internal device status update is received. + /// + /// Contains the status, cmd error, battery level. + async fn device_status_update(&self, _status: DeviceStatusData) {} + + /// Dispatched when an eeg data is received. + /// + /// Contains information about the O1, O2, T3, T4 + interval. + async fn eeg_update(&mut self, _eeg_data: Vec) {} + + /// Dispatched when measurement data is received over the PMD data UUID. + /// + /// Contains data in a [`CommandData`]. + async fn send_command(&self, _command_data: CommandData) {} + + /// Checked at start of each event loop. + /// + /// Returns [`false`] if the event loop should be terminated and close connection. + async fn should_continue(&self) -> bool { + true + } +} diff --git a/brainbit/src/bbit/eeg_uuids.rs b/brainbit/src/bbit/uuids.rs similarity index 100% rename from brainbit/src/bbit/eeg_uuids.rs rename to brainbit/src/bbit/uuids.rs diff --git a/brainbit/src/lib.rs b/brainbit/src/lib.rs index 7b2c66d..56dbbad 100644 --- a/brainbit/src/lib.rs +++ b/brainbit/src/lib.rs @@ -1,40 +1,12 @@ pub mod bbit; use crate::bbit::device::BBitResult; -pub use async_trait::async_trait; +use bbit::errors::Error; use btleplug::api::{Characteristic, Peripheral as _}; use btleplug::platform::Peripheral; use thiserror::Error; use uuid::Uuid; -/// Error type for general brainbit errors and internal btleplug Ble errors -#[derive(Debug, Error)] -pub enum Error { - /// Bluetooth adapter is not found on attempt to scan it - #[error("No BLE adaptor")] - NoBleAdaptor, - /// Could not connect to a device by filter - #[error("No BLE device")] - NoDevice, - /// Device looks as it's not connected, but command was called - #[error("Not connected")] - NotConnected, - /// UUID device's characteristic is missing - #[error("Characteristic not found")] - CharacteristicNotFound, - /// EEG Data packets received from device is not parsed - #[error("Invalid '{0}'")] - InvalidData(String), - /// The command did not return a response - #[error("No command response")] - NoControlPointResponse, - /// An error occurred in the underlying BLE library. - #[error("BLE error: {0}")] - BleError(#[from] btleplug::Error), - #[error("Error generated by event handler")] - HandlerError(#[from] Box), -} - /// Private helper to find characteristics from a [`Uuid`]. async fn find_characteristic(device: &Peripheral, uuid: Uuid) -> BBitResult { device diff --git a/examples/battery_level/Cargo.toml b/examples/battery_level/Cargo.toml index 30c74f4..eee9bbe 100644 --- a/examples/battery_level/Cargo.toml +++ b/examples/battery_level/Cargo.toml @@ -8,10 +8,11 @@ description = "Subscribe on BBit device status changes including battery level" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lib = { version = "0.1.0", path = "../../brainbit" } +brainbit = { version = "0.1.0", path = "../../brainbit" } tokio.workspace = true tracing.workspace = true tracing-subscriber.workspace = true +async-trait.workspace = true color-eyre.workspace = true chrono.workspace = true \ No newline at end of file diff --git a/examples/battery_level/src/main.rs b/examples/battery_level/src/main.rs index bdcd111..a328d42 100644 --- a/examples/battery_level/src/main.rs +++ b/examples/battery_level/src/main.rs @@ -1,17 +1,20 @@ -use lib::bbit::device::BBitSensor; -use lib::bbit::eeg_uuids::{EventType, PERIPHERAL_NAME_MATCH_FILTER}; -use lib::bbit::responses::DeviceStatusData; -use lib::bbit::EventHandler; use std::error::Error; use std::{ io::{self, Write}, sync::atomic::{AtomicUsize, Ordering}, time::Duration, }; + +use async_trait::async_trait; use tokio::sync::oneshot; use tracing::instrument; use tracing_subscriber::{fmt, prelude::*, EnvFilter}; +use brainbit::bbit::device::BBitSensor; +use brainbit::bbit::responses::DeviceStatusData; +use brainbit::bbit::traits::EventHandler; +use brainbit::bbit::uuids::{EventType, PERIPHERAL_NAME_MATCH_FILTER}; + #[tokio::main] #[instrument] async fn main() -> Result<(), Box> { @@ -55,7 +58,7 @@ impl Handler { } } -#[lib::async_trait] +#[async_trait] impl EventHandler for Handler { #[instrument(skip(self))] async fn device_status_update(&self, status_data: DeviceStatusData) { diff --git a/examples/connect/Cargo.toml b/examples/connect/Cargo.toml index b65beb0..a1a38ea 100644 --- a/examples/connect/Cargo.toml +++ b/examples/connect/Cargo.toml @@ -8,7 +8,7 @@ description = "Connect to BBit device example" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lib = { version = "0.1.0", path = "../../brainbit" } +brainbit = { version = "0.1.0", path = "../../brainbit" } tokio.workspace = true tracing.workspace = true tracing-subscriber.workspace = true diff --git a/examples/connect/src/main.rs b/examples/connect/src/main.rs index 64c7024..afd2e1b 100644 --- a/examples/connect/src/main.rs +++ b/examples/connect/src/main.rs @@ -1,5 +1,5 @@ -use lib::bbit::device::BBitSensor; -use lib::bbit::eeg_uuids::PERIPHERAL_NAME_MATCH_FILTER; +use brainbit::bbit::device::BBitSensor; +use brainbit::bbit::uuids::PERIPHERAL_NAME_MATCH_FILTER; use std::error::Error; use tracing_subscriber::{fmt, prelude::*, EnvFilter}; diff --git a/handler/Cargo.toml b/handler/Cargo.toml index 61b2528..366a352 100644 --- a/handler/Cargo.toml +++ b/handler/Cargo.toml @@ -9,8 +9,9 @@ description = "Data handler code, calculations, resistance check" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lib = { path = "../brainbit" } +brainbit = { path = "../brainbit" } tracing.workspace = true tracing-subscriber.workspace = true color-eyre.workspace = true chrono.workspace = true +async-trait.workspace = true diff --git a/handler/src/main_handler.rs b/handler/src/main_handler.rs index fea6812..32f9a7a 100644 --- a/handler/src/main_handler.rs +++ b/handler/src/main_handler.rs @@ -1,13 +1,15 @@ use chrono::Utc; -use lib::bbit::resist::ResistState; -use lib::bbit::responses::{DeviceStatusData, Nss2Status}; -use lib::bbit::EventHandler; use std::fs::File; use std::io::Write; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use tracing::instrument; +use async_trait::async_trait; +use brainbit::bbit::resist::ResistState; +use brainbit::bbit::responses::{DeviceStatusData, Nss2Status}; +use brainbit::bbit::traits::EventHandler; + const SKIP_FIRST_RESIST_RECORDS_NUMBER: u8 = 20; const STORE_RESIST_RECORDS_NUMBER: u8 = 20; @@ -22,7 +24,7 @@ pub struct BBitHandler { resist_results: Mutex, } -#[lib::async_trait] +#[async_trait] impl EventHandler for BBitHandler { #[instrument(skip(self))] async fn device_status_update(&self, status_data: DeviceStatusData) { diff --git a/mainapp/Cargo.toml b/mainapp/Cargo.toml index 8e1a639..b3c636f 100644 --- a/mainapp/Cargo.toml +++ b/mainapp/Cargo.toml @@ -8,7 +8,7 @@ description = "Console client app" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lib = { path = "../brainbit" } +brainbit = { path = "../brainbit" } handler = { path = "../handler" } tokio.workspace = true diff --git a/mainapp/src/main.rs b/mainapp/src/main.rs index bbd176b..d80abba 100644 --- a/mainapp/src/main.rs +++ b/mainapp/src/main.rs @@ -9,8 +9,8 @@ use tokio::sync::oneshot; use tracing::instrument; use tracing_subscriber::{fmt, prelude::*, EnvFilter}; -use lib::bbit::device::BBitSensor; -use lib::bbit::eeg_uuids::{EventType, PERIPHERAL_NAME_MATCH_FILTER}; +use brainbit::bbit::device::BBitSensor; +use brainbit::bbit::uuids::{EventType, PERIPHERAL_NAME_MATCH_FILTER}; #[tokio::main] #[instrument]