use std::collections::HashMap;
use std::sync::Arc;
use async_trait::async_trait;
use tokio::sync::RwLock;
use crate::config::{BleConfig, DiscoveryConfig};
use crate::error::{BleError, Result};
use crate::platform::{
BleAdapter, ConnectionCallback, ConnectionEvent, DisconnectReason, DiscoveredDevice,
DiscoveryCallback,
};
use crate::transport::BleConnection;
use crate::NodeId;
use super::advertiser::BleAdvertiser;
use super::connection::WinRtConnection;
use super::gatt_server::GattServer;
use super::watcher::BleWatcher;
struct AdapterState {
connections: HashMap<NodeId, WinRtConnection>,
address_to_node: HashMap<u64, NodeId>,
node_to_address: HashMap<NodeId, u64>,
}
impl Default for AdapterState {
fn default() -> Self {
Self {
connections: HashMap::new(),
address_to_node: HashMap::new(),
node_to_address: HashMap::new(),
}
}
}
pub struct WinRtBleAdapter {
watcher: Arc<RwLock<BleWatcher>>,
advertiser: Arc<RwLock<BleAdvertiser>>,
gatt_server: Arc<RwLock<Option<GattServer>>>,
config: RwLock<Option<BleConfig>>,
state: RwLock<AdapterState>,
discovery_callback: RwLock<Option<DiscoveryCallback>>,
connection_callback: RwLock<Option<ConnectionCallback>>,
initialized: bool,
}
impl WinRtBleAdapter {
pub fn new() -> Result<Self> {
let watcher = BleWatcher::new()?;
let advertiser = BleAdvertiser::new()?;
log::info!("WinRtBleAdapter created");
Ok(Self {
watcher: Arc::new(RwLock::new(watcher)),
advertiser: Arc::new(RwLock::new(advertiser)),
gatt_server: Arc::new(RwLock::new(None)),
config: RwLock::new(None),
state: RwLock::new(AdapterState::default()),
discovery_callback: RwLock::new(None),
connection_callback: RwLock::new(None),
initialized: false,
})
}
pub async fn register_node_address(&self, node_id: NodeId, address: u64) {
let mut state = self.state.write().await;
state.address_to_node.insert(address, node_id);
state.node_to_address.insert(node_id, address);
}
pub async fn get_node_address(&self, node_id: &NodeId) -> Option<u64> {
let state = self.state.read().await;
state.node_to_address.get(node_id).copied()
}
pub async fn get_address_node(&self, address: u64) -> Option<NodeId> {
let state = self.state.read().await;
state.address_to_node.get(&address).copied()
}
pub async fn process_discoveries(&self) -> Result<()> {
let watcher = self.watcher.read().await;
let peat_peripherals = watcher.get_peat_peripherals();
if let Some(ref callback) = *self.discovery_callback.read().await {
for peripheral in peat_peripherals {
if let Some(node_id) = peripheral.node_id {
self.register_node_address(node_id, peripheral.address)
.await;
}
let device: DiscoveredDevice = peripheral.into();
callback(device);
}
}
Ok(())
}
}
#[async_trait]
impl BleAdapter for WinRtBleAdapter {
async fn init(&mut self, config: &BleConfig) -> Result<()> {
*self.config.write().await = Some(config.clone());
let mut gatt_server = GattServer::new(config.node_id)?;
gatt_server.init().await?;
*self.gatt_server.write().await = Some(gatt_server);
self.initialized = true;
log::info!(
"WinRtBleAdapter initialized for node {:08X}",
config.node_id.as_u32()
);
Ok(())
}
async fn start(&self) -> Result<()> {
let config = self.config.read().await;
let config = config
.as_ref()
.ok_or_else(|| BleError::InvalidState("Adapter not initialized".to_string()))?;
if let Some(ref mut server) = *self.gatt_server.write().await {
server.start_advertising()?;
}
{
let mut advertiser = self.advertiser.write().await;
advertiser.start_advertising(config.node_id, &config.discovery)?;
}
{
let mut watcher = self.watcher.write().await;
watcher.start_scan(&config.discovery)?;
}
log::info!("WinRtBleAdapter started");
Ok(())
}
async fn stop(&self) -> Result<()> {
{
let mut watcher = self.watcher.write().await;
watcher.stop_scan()?;
}
{
let mut advertiser = self.advertiser.write().await;
advertiser.stop_advertising()?;
}
if let Some(ref mut server) = *self.gatt_server.write().await {
server.stop_advertising()?;
}
log::info!("WinRtBleAdapter stopped");
Ok(())
}
fn is_powered(&self) -> bool {
self.initialized
}
fn address(&self) -> Option<String> {
None
}
async fn start_scan(&self, config: &DiscoveryConfig) -> Result<()> {
let mut watcher = self.watcher.write().await;
watcher.start_scan(config)
}
async fn stop_scan(&self) -> Result<()> {
let mut watcher = self.watcher.write().await;
watcher.stop_scan()
}
async fn start_advertising(&self, config: &DiscoveryConfig) -> Result<()> {
let ble_config = self.config.read().await;
let ble_config = ble_config
.as_ref()
.ok_or_else(|| BleError::InvalidState("Adapter not initialized".to_string()))?;
let mut advertiser = self.advertiser.write().await;
advertiser.start_advertising(ble_config.node_id, config)
}
async fn stop_advertising(&self) -> Result<()> {
let mut advertiser = self.advertiser.write().await;
advertiser.stop_advertising()
}
fn set_discovery_callback(&mut self, callback: Option<DiscoveryCallback>) {
if let Ok(mut cb) = self.discovery_callback.try_write() {
*cb = callback;
}
}
async fn connect(&self, peer_id: &NodeId) -> Result<Box<dyn BleConnection>> {
let address = self
.get_node_address(peer_id)
.await
.ok_or_else(|| BleError::ConnectionFailed(format!("Unknown node ID: {}", peer_id)))?;
let mut connection = WinRtConnection::new(*peer_id, address);
connection.connect().await?;
{
let mut state = self.state.write().await;
state.connections.insert(*peer_id, connection.clone());
}
if let Some(ref cb) = *self.connection_callback.read().await {
cb(
*peer_id,
ConnectionEvent::Connected {
mtu: connection.mtu(),
phy: connection.phy(),
},
);
}
log::info!("Connected to peer {} at {:012X}", peer_id, address);
Ok(Box::new(connection))
}
async fn disconnect(&self, peer_id: &NodeId) -> Result<()> {
let connection = {
let mut state = self.state.write().await;
state.connections.remove(peer_id)
};
if let Some(mut conn) = connection {
conn.disconnect();
if let Some(ref cb) = *self.connection_callback.read().await {
cb(
*peer_id,
ConnectionEvent::Disconnected {
reason: DisconnectReason::LocalRequest,
},
);
}
log::info!("Disconnected from peer {}", peer_id);
}
Ok(())
}
fn get_connection(&self, peer_id: &NodeId) -> Option<Box<dyn BleConnection>> {
if let Ok(state) = self.state.try_read() {
state
.connections
.get(peer_id)
.map(|c| Box::new(c.clone()) as Box<dyn BleConnection>)
} else {
None
}
}
fn peer_count(&self) -> usize {
if let Ok(state) = self.state.try_read() {
state.connections.len()
} else {
0
}
}
fn connected_peers(&self) -> Vec<NodeId> {
if let Ok(state) = self.state.try_read() {
state.connections.keys().copied().collect()
} else {
Vec::new()
}
}
fn set_connection_callback(&mut self, callback: Option<ConnectionCallback>) {
if let Ok(mut cb) = self.connection_callback.try_write() {
*cb = callback;
}
}
async fn register_gatt_service(&self) -> Result<()> {
if let Some(ref mut server) = *self.gatt_server.write().await {
server.start_advertising()?;
}
Ok(())
}
async fn unregister_gatt_service(&self) -> Result<()> {
if let Some(ref mut server) = *self.gatt_server.write().await {
server.stop_advertising()?;
}
Ok(())
}
fn supports_coded_phy(&self) -> bool {
false
}
fn supports_extended_advertising(&self) -> bool {
true
}
fn max_mtu(&self) -> u16 {
512
}
fn max_connections(&self) -> u8 {
8
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_adapter_state_default() {
let state = AdapterState::default();
assert!(state.connections.is_empty());
assert!(state.address_to_node.is_empty());
}
}