1#[cfg(not(feature = "std"))]
23use alloc::{boxed::Box, vec::Vec};
24
25use async_trait::async_trait;
26use core::time::Duration;
27
28use crate::config::{BleConfig, BlePhy};
29use crate::error::Result;
30use crate::platform::BleAdapter;
31use crate::NodeId;
32
33#[derive(Debug, Clone)]
38pub struct TransportCapabilities {
39 pub max_bandwidth_bps: u64,
41 pub typical_latency_ms: u32,
43 pub max_range_meters: u32,
45 pub bidirectional: bool,
47 pub reliable: bool,
49 pub battery_impact: u8,
51 pub supports_broadcast: bool,
53 pub requires_pairing: bool,
55 pub max_message_size: usize,
57}
58
59impl TransportCapabilities {
60 pub fn bluetooth_le() -> Self {
62 Self {
63 max_bandwidth_bps: 250_000, typical_latency_ms: 30,
65 max_range_meters: 100,
66 bidirectional: true,
67 reliable: true,
68 battery_impact: 15,
69 supports_broadcast: true,
70 requires_pairing: false,
71 max_message_size: 512,
72 }
73 }
74
75 pub fn bluetooth_le_coded() -> Self {
77 Self {
78 max_bandwidth_bps: 125_000, typical_latency_ms: 100,
80 max_range_meters: 400,
81 bidirectional: true,
82 reliable: true,
83 battery_impact: 20, supports_broadcast: true,
85 requires_pairing: false,
86 max_message_size: 512,
87 }
88 }
89
90 pub fn for_phy(phy: BlePhy) -> Self {
92 match phy {
93 BlePhy::Le1M => Self::bluetooth_le(),
94 BlePhy::Le2M => Self {
95 max_bandwidth_bps: 500_000,
96 typical_latency_ms: 20,
97 max_range_meters: 50,
98 ..Self::bluetooth_le()
99 },
100 BlePhy::LeCodedS2 => Self {
101 max_bandwidth_bps: 250_000,
102 typical_latency_ms: 50,
103 max_range_meters: 200,
104 ..Self::bluetooth_le()
105 },
106 BlePhy::LeCodedS8 => Self::bluetooth_le_coded(),
107 }
108 }
109}
110
111impl Default for TransportCapabilities {
112 fn default() -> Self {
113 Self::bluetooth_le()
114 }
115}
116
117pub trait BleConnection: Send + Sync {
121 fn peer_id(&self) -> &NodeId;
123
124 fn is_alive(&self) -> bool;
126
127 fn mtu(&self) -> u16;
129
130 fn phy(&self) -> BlePhy;
132
133 fn rssi(&self) -> Option<i8>;
135
136 fn connected_duration(&self) -> Duration;
138}
139
140pub struct BluetoothLETransport<A: BleAdapter> {
160 config: BleConfig,
162 adapter: A,
164 capabilities: TransportCapabilities,
166}
167
168impl<A: BleAdapter> BluetoothLETransport<A> {
169 pub fn new(config: BleConfig, adapter: A) -> Self {
171 let capabilities = TransportCapabilities::for_phy(config.phy.preferred_phy);
172 Self {
173 config,
174 adapter,
175 capabilities,
176 }
177 }
178
179 pub fn config(&self) -> &BleConfig {
181 &self.config
182 }
183
184 pub fn capabilities(&self) -> &TransportCapabilities {
186 &self.capabilities
187 }
188
189 pub fn node_id(&self) -> &NodeId {
191 &self.config.node_id
192 }
193
194 pub fn peer_link_info(&self, peer_id: &NodeId) -> Option<crate::peer::BlePeerLinkInfo> {
202 self.adapter.peer_link_info(peer_id)
203 }
204}
205
206#[async_trait]
211pub trait MeshTransport: Send + Sync {
212 async fn start(&self) -> Result<()>;
214
215 async fn stop(&self) -> Result<()>;
217
218 async fn connect(&self, peer_id: &NodeId) -> Result<Box<dyn BleConnection>>;
220
221 async fn disconnect(&self, peer_id: &NodeId) -> Result<()>;
223
224 fn get_connection(&self, peer_id: &NodeId) -> Option<Box<dyn BleConnection>>;
226
227 fn peer_count(&self) -> usize;
229
230 fn connected_peers(&self) -> Vec<NodeId>;
232
233 fn is_connected(&self, peer_id: &NodeId) -> bool {
235 self.get_connection(peer_id).is_some()
236 }
237
238 async fn send_to(&self, peer_id: &NodeId, data: &[u8]) -> Result<usize> {
245 let _ = (peer_id, data);
246 Err(crate::error::BleError::NotSupported(
247 "send_to not implemented".into(),
248 ))
249 }
250
251 fn capabilities(&self) -> &TransportCapabilities;
253}
254
255fn ble_uuid_from_u16(short: u16) -> uuid::Uuid {
259 uuid::Uuid::from_fields(
260 short as u32,
261 0x0000,
262 0x1000,
263 &[0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB],
264 )
265}
266
267#[async_trait]
268impl<A: BleAdapter + Send + Sync> MeshTransport for BluetoothLETransport<A> {
269 async fn start(&self) -> Result<()> {
270 self.adapter.start().await
272 }
273
274 async fn stop(&self) -> Result<()> {
275 self.adapter.stop().await
276 }
277
278 async fn connect(&self, peer_id: &NodeId) -> Result<Box<dyn BleConnection>> {
279 self.adapter.connect(peer_id).await
280 }
281
282 async fn disconnect(&self, peer_id: &NodeId) -> Result<()> {
283 self.adapter.disconnect(peer_id).await
284 }
285
286 fn get_connection(&self, peer_id: &NodeId) -> Option<Box<dyn BleConnection>> {
287 self.adapter.get_connection(peer_id)
288 }
289
290 fn peer_count(&self) -> usize {
291 self.adapter.peer_count()
292 }
293
294 fn connected_peers(&self) -> Vec<NodeId> {
295 self.adapter.connected_peers()
296 }
297
298 async fn send_to(&self, peer_id: &NodeId, data: &[u8]) -> Result<usize> {
299 use crate::sync::protocol::chunk_data;
300
301 let conn = self.get_connection(peer_id).ok_or_else(|| {
303 crate::error::BleError::ConnectionFailed(format!("No connection to {}", peer_id))
304 })?;
305 let mtu = conn.mtu() as usize;
306
307 let chunks = chunk_data(data, mtu, 0);
309
310 let char_uuid = ble_uuid_from_u16(crate::CHAR_SYNC_DATA_UUID);
312 for chunk in &chunks {
313 self.adapter
314 .write_to_peer(peer_id, char_uuid, &chunk.encode())
315 .await?;
316 }
317
318 Ok(data.len())
319 }
320
321 fn capabilities(&self) -> &TransportCapabilities {
322 &self.capabilities
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 #[test]
331 fn test_capabilities_for_phy() {
332 let caps = TransportCapabilities::for_phy(BlePhy::LeCodedS8);
333 assert_eq!(caps.max_range_meters, 400);
334 assert_eq!(caps.max_bandwidth_bps, 125_000);
335 }
336
337 #[test]
338 fn test_capabilities_le2m() {
339 let caps = TransportCapabilities::for_phy(BlePhy::Le2M);
340 assert_eq!(caps.max_range_meters, 50);
341 assert_eq!(caps.max_bandwidth_bps, 500_000);
342 }
343
344 #[test]
345 fn test_ble_uuid_from_u16() {
346 let uuid = ble_uuid_from_u16(0x0003);
347 assert_eq!(uuid.to_string(), "00000003-0000-1000-8000-00805f9b34fb");
348 }
349
350 #[test]
351 fn test_send_to_default_returns_error() {
352 use crate::platform::StubAdapter;
354
355 let config = BleConfig::default();
356 let adapter = StubAdapter::default();
357 let transport = BluetoothLETransport::new(config, adapter);
358
359 let rt = tokio::runtime::Builder::new_current_thread()
361 .enable_all()
362 .build()
363 .unwrap();
364 let result = rt.block_on(transport.send_to(&NodeId::new(0x222), b"hello"));
365 assert!(result.is_err());
366 }
367
368 #[tokio::test]
369 async fn test_mock_send_to() {
370 use crate::platform::mock::{MockBleAdapter, MockNetwork};
371
372 let network = MockNetwork::new();
373 let mut adapter1 = MockBleAdapter::new(NodeId::new(0x111), network.clone());
374 let mut adapter2 = MockBleAdapter::new(NodeId::new(0x222), network.clone());
375
376 adapter1.init(&BleConfig::default()).await.unwrap();
377 adapter2.init(&BleConfig::default()).await.unwrap();
378 adapter2
379 .start_advertising(&crate::config::DiscoveryConfig::default())
380 .await
381 .unwrap();
382
383 let _conn = adapter1.connect(&NodeId::new(0x222)).await.unwrap();
385
386 let transport = BluetoothLETransport::new(BleConfig::default(), adapter1);
388 let result = transport.send_to(&NodeId::new(0x222), b"hello mesh").await;
389 assert!(result.is_ok());
390 assert_eq!(result.unwrap(), 10);
391
392 let packets = network.receive_data(&NodeId::new(0x222));
394 assert!(!packets.is_empty());
395 }
396
397 #[tokio::test]
398 async fn test_send_to_disconnected_peer() {
399 use crate::platform::mock::{MockBleAdapter, MockNetwork};
400
401 let network = MockNetwork::new();
402 let adapter = MockBleAdapter::new(NodeId::new(0x111), network);
403 let transport = BluetoothLETransport::new(BleConfig::default(), adapter);
404
405 let result = transport.send_to(&NodeId::new(0x999), b"hello").await;
407 assert!(result.is_err());
408 }
409}