Skip to content

Commit

Permalink
More refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
ztroop committed Mar 23, 2024
1 parent 7d46e4d commit 61e3522
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 117 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
- name: Build
uses: actions-rs/cargo@v1
with:
use-cross: true
use-cross: false
command: build
args: --verbose --release --target ${{ matrix.target }}

Expand Down
76 changes: 76 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};

use ratatui::widgets::TableState;
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};

use crate::{
scan::{bluetooth_scan, get_characteristics},
structs::{Characteristic, DeviceInfo},
};

pub enum DeviceData {
DeviceInfo(DeviceInfo),
#[allow(dead_code)]
Characteristics(Vec<Characteristic>),
Error(String),
}

pub struct App {
pub rx: UnboundedReceiver<DeviceData>,
pub tx: UnboundedSender<DeviceData>,
pub loading_status: Arc<AtomicBool>,
pub pause_status: Arc<AtomicBool>,
pub table_state: TableState,
pub devices: Vec<DeviceInfo>,
pub inspect_view: bool,
pub inspect_overlay_scroll: usize,
pub selected_characteristics: Vec<Characteristic>,
pub frame_count: usize,
pub is_loading: bool,
pub error_view: bool,
pub error_message: String,
}

impl App {
pub fn new() -> Self {
let (tx, rx) = mpsc::unbounded_channel();
Self {
tx,
rx,
loading_status: Arc::new(AtomicBool::default()),
pause_status: Arc::new(AtomicBool::default()),
table_state: TableState::default(),
devices: Vec::new(),
inspect_view: false,
inspect_overlay_scroll: 0,
selected_characteristics: Vec::new(),
frame_count: 0,
is_loading: false,
error_view: false,
error_message: String::new(),
}
}

pub async fn scan(&mut self) {
let pause_signal_clone = Arc::clone(&self.pause_status);
let tx_clone = self.tx.clone();
tokio::spawn(async move { bluetooth_scan(tx_clone, pause_signal_clone).await });
}

pub async fn connect(&mut self) {
let selected_device = self
.devices
.get(self.table_state.selected().unwrap_or(0))
.unwrap();

self.pause_status.store(true, Ordering::SeqCst);

let device = Arc::new(selected_device.clone());
let tx_clone = self.tx.clone();

tokio::spawn(async move { get_characteristics(tx_clone, device).await });
}
}
25 changes: 5 additions & 20 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,9 @@ use crossterm::{
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use ratatui::{backend::CrosstermBackend, Terminal};
use scan::bluetooth_scan;
use std::{
error::Error,
io,
sync::{atomic::AtomicBool, Arc},
};
use tokio::sync::mpsc;
use std::{error::Error, io};

mod app;
mod company_codes;
mod scan;
mod structs;
Expand All @@ -30,19 +25,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;

let (tx, rx) = mpsc::channel(100);
let pause_signal = Arc::new(AtomicBool::new(false));
let pause_signal_clone = Arc::clone(&pause_signal);

tokio::spawn(async move {
if let Err(e) = bluetooth_scan(tx, pause_signal_clone).await {
eprintln!("Error during Bluetooth scan: {}", e);
}
});

if let Err(e) = viewer(&mut terminal, rx, pause_signal).await {
eprintln!("Error running application: {}", e);
}
let mut app = app::App::new();
app.scan().await;
viewer(&mut terminal, &mut app).await?;

disable_raw_mode()?;
execute!(
Expand Down
88 changes: 52 additions & 36 deletions src/scan.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::app::DeviceData;
use crate::structs::{Characteristic, DeviceInfo};
use btleplug::api::{
Central, CentralEvent, Manager as _, Peripheral, PeripheralProperties, ScanFilter,
};
use btleplug::platform::Manager;
use futures::StreamExt;
use std::error::Error;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
Expand All @@ -13,18 +13,16 @@ use tokio::time::timeout;

/// Scans for Bluetooth devices and sends the information to the provided `mpsc::Sender`.
/// The scan can be paused by setting the `pause_signal` to `true`.
pub async fn bluetooth_scan(
tx: mpsc::Sender<Vec<DeviceInfo>>,
pause_signal: Arc<AtomicBool>,
) -> Result<(), Box<dyn Error>> {
let manager = Manager::new().await?;
let adapters = manager.adapters().await?;
let central = adapters.into_iter().next().ok_or("No adapters found")?;
pub async fn bluetooth_scan(tx: mpsc::UnboundedSender<DeviceData>, pause_signal: Arc<AtomicBool>) {
let manager = Manager::new().await.unwrap();
let adapters = manager.adapters().await.unwrap();
let central = adapters.into_iter().next().expect("No adapters found");

central.start_scan(ScanFilter::default()).await?;
let mut events = central.events().await?;

let mut devices_info = Vec::new();
central
.start_scan(ScanFilter::default())
.await
.expect("Scanning failure");
let mut events = central.events().await.unwrap();

while let Some(event) = events.next().await {
// Check the pause signal before processing the event
Expand All @@ -36,11 +34,12 @@ pub async fn bluetooth_scan(
if let Ok(device) = central.peripheral(&id).await {
let properties = device
.properties()
.await?
.await
.unwrap()
.unwrap_or(PeripheralProperties::default());

// Add the new device's information to the accumulated list
devices_info.push(DeviceInfo::new(
let device = DeviceInfo::new(
device.id().to_string(),
properties.local_name,
properties.tx_power_level,
Expand All @@ -50,38 +49,55 @@ pub async fn bluetooth_scan(
properties.services,
properties.service_data,
device.clone(),
));
);

// Send a clone of the accumulated device information so far
tx.send(devices_info.clone()).await?;
let _ = tx.send(DeviceData::DeviceInfo(device));
}
}
}

Ok(())
}

/// Gets the characteristics of a Bluetooth device and returns them as a `Vec<Characteristic>`.
/// The device is identified by its address or UUID.
pub async fn get_characteristics(
peripheral: &btleplug::platform::Peripheral,
) -> Result<Vec<Characteristic>, Box<dyn Error>> {
tx: mpsc::UnboundedSender<DeviceData>,
peripheral: Arc<DeviceInfo>,
) {
let duration = Duration::from_secs(10);
timeout(duration, peripheral.connect()).await??;

let characteristics = peripheral.characteristics();
let mut result = Vec::new();
for characteristic in characteristics {
result.push(Characteristic {
uuid: characteristic.uuid,
properties: characteristic.properties,
descriptors: characteristic
.descriptors
.into_iter()
.map(|d| d.uuid)
.collect(),
service: characteristic.service_uuid,
});
match &peripheral.device {
Some(device) => match timeout(duration, device.connect()).await {
Ok(Ok(_)) => {
if let Some(device) = &peripheral.device {
let characteristics = device.characteristics();
let mut result = Vec::new();
for characteristic in characteristics {
result.push(Characteristic {
uuid: characteristic.uuid,
properties: characteristic.properties,
descriptors: characteristic
.descriptors
.into_iter()
.map(|d| d.uuid)
.collect(),
service: characteristic.service_uuid,
});
}
let _ = tx.send(DeviceData::Characteristics(result));
}
}
Ok(Err(e)) => {
tx.send(DeviceData::Error(format!("Connection error: {}", e)))
.unwrap();
}
Err(_) => {
tx.send(DeviceData::Error("Connection timed out".to_string()))
.unwrap();
}
},
None => {
tx.send(DeviceData::Error("Device not found".to_string()))
.unwrap();
}
}
Ok(result)
}
13 changes: 0 additions & 13 deletions src/structs.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,8 @@
use std::collections::HashMap;

use btleplug::api::CharPropFlags;
use ratatui::widgets::TableState;
use uuid::Uuid;

pub struct App {
pub table_state: TableState,
pub devices: Vec<DeviceInfo>,
pub inspect_view: bool,
pub inspect_overlay_scroll: usize,
pub selected_characteristics: Vec<Characteristic>,
pub frame_count: usize,
pub is_loading: bool,
pub error_view: bool,
pub error_message: String,
}

/// A struct to hold the information of a Bluetooth device.
#[derive(Clone, Default)]
pub struct DeviceInfo {
Expand Down
Loading

0 comments on commit 61e3522

Please sign in to comment.