Skip to content

Commit

Permalink
feat: handle adapter events in lib and backend
Browse files Browse the repository at this point in the history
  • Loading branch information
gmallios committed Mar 16, 2024
1 parent 800e791 commit 6dd3867
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 24 deletions.
29 changes: 20 additions & 9 deletions manager-app/src/async_bridge/bridge.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::sync::Arc;

use log::debug;
use tauri::AppHandle;
use tokio::sync::{mpsc, Mutex};

Expand All @@ -13,28 +14,38 @@ use super::{BridgeCommand, BridgeResponse, NewStateResponse};

struct CommandLoopState<B: BLEConnectionManager> {
manager: DeviceManager<B>,
app_handle: AppHandle,
devices: Vec<Arc<SoundcoreBLEDevice<B::Connection>>>,
}

impl<B: BLEConnectionManager> CommandLoopState<B> {
fn new(manager: DeviceManager<B>, app_handle: AppHandle) -> Self {
fn new(manager: DeviceManager<B>) -> Self {
Self {
manager,
app_handle,
devices: Vec::new(),
}
}
}

pub async fn async_bridge(
mut input_rx: mpsc::Receiver<BridgeCommand>,
output_tx: mpsc::Sender<BridgeResponse>,
app_handle: AppHandle,
) {
let command_loop = tokio::spawn(async move {
let manager = create_device_manager().await;
let command_loop_state = Arc::new(Mutex::new(CommandLoopState::new(manager, app_handle)));
tokio::spawn(async move {
let manager =
create_device_manager().await;

// Adapter events
let mut manager_event_channel = manager.get_event_channel().await.unwrap();
let adapter_tx = output_tx.clone();
tokio::task::spawn(async move {
while let Some(event) = manager_event_channel.recv().await {
adapter_tx
.send(BridgeResponse::AdapterEvent(event))
.await
.expect("Failed to send adapter event");
}
});

// Main command loop
let command_loop_state = Arc::new(Mutex::new(CommandLoopState::new(manager)));
loop {
while let Some(command) = input_rx.recv().await {
let command_loop_state = command_loop_state.clone();
Expand Down
2 changes: 2 additions & 0 deletions manager-app/src/async_bridge/response.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use serde::Serialize;

use soundcore_lib::api::SoundcoreDeviceState;
use soundcore_lib::ble::BLEAdapterEvent;
use soundcore_lib::btaddr::BluetoothAdrr;
use soundcore_lib::device_manager::DiscoveredDevice;
use typeshare::typeshare;
Expand All @@ -13,6 +14,7 @@ pub enum BridgeResponse {
ConnectionEstablished(BluetoothAdrr),
NewState(NewStateResponse),
Disconnected(BluetoothAdrr),
AdapterEvent(BLEAdapterEvent),
Error(String),
}

Expand Down
9 changes: 3 additions & 6 deletions manager-app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,18 @@ async fn main() {
#[cfg(target_os = "macos")]
server::launch_server();

// Bring up bridge

// builder()
// .filter(None, log::LevelFilter::Debug)
// .filter_module("h2", log::LevelFilter::Off)
// .filter_module("hyper", log::LevelFilter::Off)
// .filter_module("tower", log::LevelFilter::Off)
// .init();
let (input_tx, input_rx) = channel(1);
let (output_tx, mut output_rx) = channel(1);
let (input_tx, input_rx) = channel(255);
let (output_tx, mut output_rx) = channel(255);

tauri::Builder::default()
.setup(|app| {
let bridge_app_handle = app.handle();
tokio::spawn(async_bridge(input_rx, output_tx, bridge_app_handle));
tokio::spawn(async_bridge(input_rx, output_tx));

let app_handle = app.handle();
tokio::spawn(async move {
Expand Down
12 changes: 8 additions & 4 deletions soundcore-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ log = { workspace = true }
env_logger = { workspace = true }
thiserror = { workspace = true }
serde = { workspace = true, features = ["derive", "rc"] }
tokio = { workspace = true, features = ["time", "macros", "rt-multi-thread", "sync"] }
tokio = { workspace = true, features = [
"time",
"macros",
"rt-multi-thread",
"sync",
] }
async-trait = { workspace = true }
futures = { workspace = true }
strum = { version = "0.26", features = ["derive"] }
Expand All @@ -34,7 +39,6 @@ btleplug = { version = "0.11", features = ["serde"], optional = true }
test_data = { path = "../test_data" }



[target.'cfg(target_os = "windows")'.dependencies]
windows = { version = "0.52", features = [
"Storage_Streams",
Expand All @@ -44,5 +48,5 @@ windows = { version = "0.52", features = [
"Devices_Enumeration",
"Devices_Bluetooth",
"Devices_Bluetooth_GenericAttributeProfile",
"Devices_Bluetooth_Advertisement"
], optional = true }
"Devices_Bluetooth_Advertisement",
], optional = true }
6 changes: 6 additions & 0 deletions soundcore-lib/src/ble/btleplug/btaddr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ impl From<BluetoothAdrr> for PeripheralId {
}
}

impl From<PeripheralId> for BluetoothAdrr {
fn from(val: PeripheralId) -> Self {
BluetoothAdrr::from_str(&val.to_string()).unwrap()
}
}

impl From<WriteType> for btleplug::api::WriteType {
fn from(value: WriteType) -> Self {
match value {
Expand Down
55 changes: 50 additions & 5 deletions soundcore-lib/src/ble/btleplug/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@ use std::sync::{Arc, Weak};
use std::time::Duration;

use async_trait::async_trait;
use btleplug::api::{Central as _, CentralEvent};
use btleplug::{
api::Manager as _,
platform::{Adapter, Manager},
};
use futures::StreamExt;
use log::warn;
use tokio::sync::RwLock;
use weak_table::weak_value_hash_map::Entry;
use weak_table::WeakValueHashMap;

use crate::ble::{BLEConnectionFactory, BLEConnectionUuidSet, BLEDeviceDescriptor, BLEConnectionManager, BLEDeviceScanner};
use crate::ble::btleplug::connection::BtlePlugConnection;
use crate::ble::{
BLEConnectionFactory, BLEConnectionManager, BLEConnectionUuidSet, BLEDeviceDescriptor,
BLEAdapterEvent, BLEDeviceScanner,

Check failure on line 19 in soundcore-lib/src/ble/btleplug/manager.rs

View workflow job for this annotation

GitHub Actions / Check

unresolved import `crate::ble::BLEAdapterEvent`

Check failure on line 19 in soundcore-lib/src/ble/btleplug/manager.rs

View workflow job for this annotation

GitHub Actions / Tests with stable Rust

unresolved import `crate::ble::BLEAdapterEvent`
};
use crate::btaddr::BluetoothAdrr;
use crate::error::SoundcoreLibResult;

Expand Down Expand Up @@ -72,10 +78,13 @@ impl BLEConnectionManager for BtlePlugBLEManager {
descriptor: BLEDeviceDescriptor,
uuid_set: Option<BLEConnectionUuidSet>,
) -> SoundcoreLibResult<Arc<Self::Connection>> {
match self.open_connections.write().await.entry(descriptor.addr.to_owned()) {
Entry::Occupied(e) => {
Ok(e.get().to_owned())
}
match self
.open_connections
.write()
.await
.entry(descriptor.addr.to_owned())
{
Entry::Occupied(e) => Ok(e.get().to_owned()),
Entry::Vacant(e) => {
let connection = self
.connection_factory
Expand All @@ -87,4 +96,40 @@ impl BLEConnectionManager for BtlePlugBLEManager {
}
}
}

async fn adapter_events(

Check failure on line 100 in soundcore-lib/src/ble/btleplug/manager.rs

View workflow job for this annotation

GitHub Actions / Check

method `adapter_events` is not a member of trait `BLEConnectionManager`

Check failure on line 100 in soundcore-lib/src/ble/btleplug/manager.rs

View workflow job for this annotation

GitHub Actions / Tests with stable Rust

method `adapter_events` is not a member of trait `BLEConnectionManager`
&self,
) -> SoundcoreLibResult<tokio::sync::mpsc::Receiver<BLEAdapterEvent>> {
let (tx, rx) = tokio::sync::mpsc::channel::<BLEAdapterEvent>(255);

for adapter in self.adapters.clone() {
let tx_clone = tx.clone();
let mut adapter_events = adapter.events().await.unwrap();
tokio::spawn(async move {
while let Some(evt) = adapter_events.next().await {
let event: Option<BLEAdapterEvent> = evt.try_into().ok();
if let Some(event) = event {
tx_clone.send(event).await.unwrap();
}
}
});
}
Ok(rx)
}
}


impl TryInto<BLEAdapterEvent> for CentralEvent {
type Error = ();

fn try_into(self) -> Result<BLEAdapterEvent, Self::Error> {
match self {
CentralEvent::DeviceDisconnected(id) => Ok(BLEAdapterEvent::DeviceDisconnected(id.into())),
CentralEvent::DeviceConnected(id) => Ok(BLEAdapterEvent::DeviceConnected(id.into())),
_ => {
warn!("Unhandled CentralEvent: {:?}", self);
Err(())
}
}
}
}
6 changes: 6 additions & 0 deletions soundcore-lib/src/device_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::collections::HashMap;
use std::{sync::Arc, time::Duration};

use crate::ble::btleplug::manager::BtlePlugBLEManager;
use crate::ble::BLEAdapterEvent;

Check failure on line 6 in soundcore-lib/src/device_manager.rs

View workflow job for this annotation

GitHub Actions / Check

unresolved import `crate::ble::BLEAdapterEvent`

Check failure on line 6 in soundcore-lib/src/device_manager.rs

View workflow job for this annotation

GitHub Actions / Tests with stable Rust

unresolved import `crate::ble::BLEAdapterEvent`
#[cfg(any(test, feature = "mock-ble"))]
use crate::mocks::*;
use crate::{
Expand Down Expand Up @@ -79,6 +80,10 @@ where
.collect::<Vec<_>>())
}

pub async fn get_event_channel(&self) -> SoundcoreLibResult<tokio::sync::mpsc::Receiver<BLEAdapterEvent>> {
self.ble_manager.adapter_events().await

Check failure on line 84 in soundcore-lib/src/device_manager.rs

View workflow job for this annotation

GitHub Actions / Check

no method named `adapter_events` found for type parameter `B` in the current scope

Check failure on line 84 in soundcore-lib/src/device_manager.rs

View workflow job for this annotation

GitHub Actions / Tests with stable Rust

no method named `adapter_events` found for type parameter `B` in the current scope
}

fn map_descriptor_to_discovered_device(descriptor: &BLEDeviceDescriptor) -> DiscoveredDevice {
DiscoveredDevice {
descriptor: descriptor.to_owned(),
Expand All @@ -98,6 +103,7 @@ where
None => discovered_device,
}
}

}

/// A discovered BLE device. The DiscoveredDevice can be upgraded to a SoundcoreBLEDevice.
Expand Down
4 changes: 4 additions & 0 deletions soundcore-lib/src/mocks/connection_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ impl BLEConnectionManager for MockBLEConnectionManager {
let conn = MockBLEConnection::new_with_empty_channel();
Ok(Arc::new(conn))
}

async fn adapter_events(&self) -> SoundcoreLibResult<tokio::sync::mpsc::Receiver<crate::ble::BLEAdapterEvent>> {
unimplemented!()
}
}

0 comments on commit 6dd3867

Please sign in to comment.