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
195#[async_trait]
200pub trait MeshTransport: Send + Sync {
201 async fn start(&self) -> Result<()>;
203
204 async fn stop(&self) -> Result<()>;
206
207 async fn connect(&self, peer_id: &NodeId) -> Result<Box<dyn BleConnection>>;
209
210 async fn disconnect(&self, peer_id: &NodeId) -> Result<()>;
212
213 fn get_connection(&self, peer_id: &NodeId) -> Option<Box<dyn BleConnection>>;
215
216 fn peer_count(&self) -> usize;
218
219 fn connected_peers(&self) -> Vec<NodeId>;
221
222 fn is_connected(&self, peer_id: &NodeId) -> bool {
224 self.get_connection(peer_id).is_some()
225 }
226
227 async fn send_to(&self, peer_id: &NodeId, data: &[u8]) -> Result<usize> {
234 let _ = (peer_id, data);
235 Err(crate::error::BleError::NotSupported(
236 "send_to not implemented".into(),
237 ))
238 }
239
240 fn capabilities(&self) -> &TransportCapabilities;
242}
243
244fn ble_uuid_from_u16(short: u16) -> uuid::Uuid {
248 uuid::Uuid::from_fields(
249 short as u32,
250 0x0000,
251 0x1000,
252 &[0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB],
253 )
254}
255
256#[async_trait]
257impl<A: BleAdapter + Send + Sync> MeshTransport for BluetoothLETransport<A> {
258 async fn start(&self) -> Result<()> {
259 self.adapter.start().await
261 }
262
263 async fn stop(&self) -> Result<()> {
264 self.adapter.stop().await
265 }
266
267 async fn connect(&self, peer_id: &NodeId) -> Result<Box<dyn BleConnection>> {
268 self.adapter.connect(peer_id).await
269 }
270
271 async fn disconnect(&self, peer_id: &NodeId) -> Result<()> {
272 self.adapter.disconnect(peer_id).await
273 }
274
275 fn get_connection(&self, peer_id: &NodeId) -> Option<Box<dyn BleConnection>> {
276 self.adapter.get_connection(peer_id)
277 }
278
279 fn peer_count(&self) -> usize {
280 self.adapter.peer_count()
281 }
282
283 fn connected_peers(&self) -> Vec<NodeId> {
284 self.adapter.connected_peers()
285 }
286
287 async fn send_to(&self, peer_id: &NodeId, data: &[u8]) -> Result<usize> {
288 use crate::sync::protocol::chunk_data;
289
290 let conn = self.get_connection(peer_id).ok_or_else(|| {
292 crate::error::BleError::ConnectionFailed(format!("No connection to {}", peer_id))
293 })?;
294 let mtu = conn.mtu() as usize;
295
296 let chunks = chunk_data(data, mtu, 0);
298
299 let char_uuid = ble_uuid_from_u16(crate::CHAR_SYNC_DATA_UUID);
301 for chunk in &chunks {
302 self.adapter
303 .write_to_peer(peer_id, char_uuid, &chunk.encode())
304 .await?;
305 }
306
307 Ok(data.len())
308 }
309
310 fn capabilities(&self) -> &TransportCapabilities {
311 &self.capabilities
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn test_capabilities_for_phy() {
321 let caps = TransportCapabilities::for_phy(BlePhy::LeCodedS8);
322 assert_eq!(caps.max_range_meters, 400);
323 assert_eq!(caps.max_bandwidth_bps, 125_000);
324 }
325
326 #[test]
327 fn test_capabilities_le2m() {
328 let caps = TransportCapabilities::for_phy(BlePhy::Le2M);
329 assert_eq!(caps.max_range_meters, 50);
330 assert_eq!(caps.max_bandwidth_bps, 500_000);
331 }
332
333 #[test]
334 fn test_ble_uuid_from_u16() {
335 let uuid = ble_uuid_from_u16(0x0003);
336 assert_eq!(uuid.to_string(), "00000003-0000-1000-8000-00805f9b34fb");
337 }
338
339 #[test]
340 fn test_send_to_default_returns_error() {
341 use crate::platform::StubAdapter;
343
344 let config = BleConfig::default();
345 let adapter = StubAdapter::default();
346 let transport = BluetoothLETransport::new(config, adapter);
347
348 let rt = tokio::runtime::Builder::new_current_thread()
350 .enable_all()
351 .build()
352 .unwrap();
353 let result = rt.block_on(transport.send_to(&NodeId::new(0x222), b"hello"));
354 assert!(result.is_err());
355 }
356
357 #[tokio::test]
358 async fn test_mock_send_to() {
359 use crate::platform::mock::{MockBleAdapter, MockNetwork};
360
361 let network = MockNetwork::new();
362 let mut adapter1 = MockBleAdapter::new(NodeId::new(0x111), network.clone());
363 let mut adapter2 = MockBleAdapter::new(NodeId::new(0x222), network.clone());
364
365 adapter1.init(&BleConfig::default()).await.unwrap();
366 adapter2.init(&BleConfig::default()).await.unwrap();
367 adapter2
368 .start_advertising(&crate::config::DiscoveryConfig::default())
369 .await
370 .unwrap();
371
372 let _conn = adapter1.connect(&NodeId::new(0x222)).await.unwrap();
374
375 let transport = BluetoothLETransport::new(BleConfig::default(), adapter1);
377 let result = transport.send_to(&NodeId::new(0x222), b"hello mesh").await;
378 assert!(result.is_ok());
379 assert_eq!(result.unwrap(), 10);
380
381 let packets = network.receive_data(&NodeId::new(0x222));
383 assert!(!packets.is_empty());
384 }
385
386 #[tokio::test]
387 async fn test_send_to_disconnected_peer() {
388 use crate::platform::mock::{MockBleAdapter, MockNetwork};
389
390 let network = MockNetwork::new();
391 let adapter = MockBleAdapter::new(NodeId::new(0x111), network);
392 let transport = BluetoothLETransport::new(BleConfig::default(), adapter);
393
394 let result = transport.send_to(&NodeId::new(0x999), b"hello").await;
396 assert!(result.is_err());
397 }
398}