hive_btle/gatt/
service.rs

1//! HIVE GATT Service Implementation
2//!
3//! Provides the GATT service structure and handlers for HIVE Protocol BLE communication.
4//!
5//! Note: This module requires the `std` feature for full functionality.
6
7use std::sync::{Arc, RwLock};
8
9use crate::error::Result;
10use crate::{HierarchyLevel, NodeId, HIVE_SERVICE_UUID};
11
12use super::characteristics::{
13    CharacteristicProperties, Command, CommandType, HiveCharacteristicUuids, NodeInfo, StatusData,
14    StatusFlags, SyncDataHeader, SyncDataOp, SyncState, SyncStateData,
15};
16
17/// GATT service event handler callback type
18pub type GattEventCallback = Box<dyn Fn(GattEvent) + Send + Sync>;
19
20/// Events emitted by the GATT service
21#[derive(Debug, Clone)]
22pub enum GattEvent {
23    /// Client connected
24    ClientConnected {
25        /// Client address
26        address: String,
27    },
28    /// Client disconnected
29    ClientDisconnected {
30        /// Client address
31        address: String,
32    },
33    /// Client subscribed to notifications
34    NotificationSubscribed {
35        /// Characteristic name
36        characteristic: String,
37    },
38    /// Client unsubscribed from notifications
39    NotificationUnsubscribed {
40        /// Characteristic name
41        characteristic: String,
42    },
43    /// Command received
44    CommandReceived {
45        /// Command that was received
46        command: CommandType,
47        /// Command payload
48        payload: Vec<u8>,
49    },
50    /// Sync data received
51    SyncDataReceived {
52        /// Sync data header
53        header: SyncDataHeader,
54        /// Sync data payload
55        payload: Vec<u8>,
56    },
57    /// MTU changed
58    MtuChanged {
59        /// New MTU value
60        mtu: u16,
61    },
62}
63
64/// GATT characteristic descriptor
65#[derive(Debug, Clone)]
66pub struct CharacteristicDescriptor {
67    /// Characteristic UUID
68    pub uuid: uuid::Uuid,
69    /// Human-readable name
70    pub name: &'static str,
71    /// Properties (read, write, notify, etc.)
72    pub properties: CharacteristicProperties,
73    /// Whether encryption is required
74    pub encrypted: bool,
75}
76
77/// Characteristic definitions for HIVE GATT service
78pub struct HiveCharacteristics;
79
80impl HiveCharacteristics {
81    /// Node Info characteristic descriptor
82    pub fn node_info() -> CharacteristicDescriptor {
83        CharacteristicDescriptor {
84            uuid: HiveCharacteristicUuids::node_info(),
85            name: "Node Info",
86            properties: CharacteristicProperties::new(CharacteristicProperties::READ),
87            encrypted: true,
88        }
89    }
90
91    /// Sync State characteristic descriptor
92    pub fn sync_state() -> CharacteristicDescriptor {
93        CharacteristicDescriptor {
94            uuid: HiveCharacteristicUuids::sync_state(),
95            name: "Sync State",
96            properties: CharacteristicProperties::new(
97                CharacteristicProperties::READ | CharacteristicProperties::NOTIFY,
98            ),
99            encrypted: true,
100        }
101    }
102
103    /// Sync Data characteristic descriptor
104    pub fn sync_data() -> CharacteristicDescriptor {
105        CharacteristicDescriptor {
106            uuid: HiveCharacteristicUuids::sync_data(),
107            name: "Sync Data",
108            properties: CharacteristicProperties::new(
109                CharacteristicProperties::WRITE | CharacteristicProperties::INDICATE,
110            ),
111            encrypted: true,
112        }
113    }
114
115    /// Command characteristic descriptor
116    pub fn command() -> CharacteristicDescriptor {
117        CharacteristicDescriptor {
118            uuid: HiveCharacteristicUuids::command(),
119            name: "Command",
120            properties: CharacteristicProperties::new(CharacteristicProperties::WRITE),
121            encrypted: true,
122        }
123    }
124
125    /// Status characteristic descriptor
126    pub fn status() -> CharacteristicDescriptor {
127        CharacteristicDescriptor {
128            uuid: HiveCharacteristicUuids::status(),
129            name: "Status",
130            properties: CharacteristicProperties::new(
131                CharacteristicProperties::READ | CharacteristicProperties::NOTIFY,
132            ),
133            encrypted: true,
134        }
135    }
136
137    /// Get all characteristic descriptors
138    pub fn all() -> Vec<CharacteristicDescriptor> {
139        vec![
140            Self::node_info(),
141            Self::sync_state(),
142            Self::sync_data(),
143            Self::command(),
144            Self::status(),
145        ]
146    }
147}
148
149/// Internal state for the GATT service
150struct ServiceState {
151    /// Node information
152    node_info: NodeInfo,
153    /// Current sync state
154    sync_state: SyncStateData,
155    /// Current status
156    status: StatusData,
157    /// Connected clients (addresses)
158    connected_clients: Vec<String>,
159    /// Clients subscribed to sync state notifications
160    sync_state_subscribers: Vec<String>,
161    /// Clients subscribed to status notifications
162    status_subscribers: Vec<String>,
163    /// Negotiated MTU
164    mtu: u16,
165}
166
167impl ServiceState {
168    fn new(node_id: NodeId, hierarchy_level: HierarchyLevel, capabilities: u16) -> Self {
169        Self {
170            node_info: NodeInfo::new(node_id, hierarchy_level, capabilities),
171            sync_state: SyncStateData::new(SyncState::Idle),
172            status: StatusData::new(),
173            connected_clients: Vec::new(),
174            sync_state_subscribers: Vec::new(),
175            status_subscribers: Vec::new(),
176            mtu: 23, // Default BLE MTU
177        }
178    }
179}
180
181/// HIVE GATT Service
182///
183/// Manages the GATT service lifecycle and provides handlers for characteristic operations.
184pub struct HiveGattService {
185    /// Service UUID
186    pub uuid: uuid::Uuid,
187    /// Internal state
188    state: Arc<RwLock<ServiceState>>,
189    /// Event callback
190    #[allow(dead_code)]
191    event_callback: Option<GattEventCallback>,
192}
193
194impl HiveGattService {
195    /// Create a new HIVE GATT service
196    pub fn new(node_id: NodeId, hierarchy_level: HierarchyLevel, capabilities: u16) -> Self {
197        Self {
198            uuid: HIVE_SERVICE_UUID,
199            state: Arc::new(RwLock::new(ServiceState::new(
200                node_id,
201                hierarchy_level,
202                capabilities,
203            ))),
204            event_callback: None,
205        }
206    }
207
208    /// Set event callback
209    pub fn set_event_callback(&mut self, callback: GattEventCallback) {
210        self.event_callback = Some(callback);
211    }
212
213    /// Get service UUID
214    pub fn service_uuid(&self) -> uuid::Uuid {
215        self.uuid
216    }
217
218    /// Get all characteristic descriptors
219    pub fn characteristics(&self) -> Vec<CharacteristicDescriptor> {
220        HiveCharacteristics::all()
221    }
222
223    // === Read Handlers ===
224
225    /// Handle Node Info read request
226    pub fn read_node_info(&self) -> Vec<u8> {
227        let state = self.state.read().unwrap();
228        state.node_info.encode().to_vec()
229    }
230
231    /// Handle Sync State read request
232    pub fn read_sync_state(&self) -> Vec<u8> {
233        let state = self.state.read().unwrap();
234        state.sync_state.encode().to_vec()
235    }
236
237    /// Handle Status read request
238    pub fn read_status(&self) -> Vec<u8> {
239        let state = self.state.read().unwrap();
240        state.status.encode().to_vec()
241    }
242
243    // === Write Handlers ===
244
245    /// Handle Sync Data write request
246    pub fn write_sync_data(&self, data: &[u8]) -> Result<Option<Vec<u8>>> {
247        let header = SyncDataHeader::decode(data).ok_or_else(|| {
248            crate::error::BleError::GattError("Invalid sync data header".to_string())
249        })?;
250
251        let payload = if data.len() > SyncDataHeader::SIZE {
252            data[SyncDataHeader::SIZE..].to_vec()
253        } else {
254            Vec::new()
255        };
256
257        // Process based on operation type
258        match header.op {
259            SyncDataOp::Document => {
260                // Update sync state to syncing
261                let mut state = self.state.write().unwrap();
262                state.sync_state.state = SyncState::Syncing;
263                state.status.flags =
264                    StatusFlags::new(state.status.flags.flags() | StatusFlags::SYNCING);
265
266                // Return acknowledgement
267                let ack = SyncDataHeader::new(SyncDataOp::Ack, header.seq);
268                Ok(Some(ack.encode().to_vec()))
269            }
270            SyncDataOp::Vector => {
271                // Sync vector update
272                let ack = SyncDataHeader::new(SyncDataOp::Ack, header.seq);
273                Ok(Some(ack.encode().to_vec()))
274            }
275            SyncDataOp::End => {
276                // Sync complete
277                let mut state = self.state.write().unwrap();
278                state.sync_state.state = SyncState::Complete;
279                state.sync_state.progress = 100;
280                state.status.flags =
281                    StatusFlags::new(state.status.flags.flags() & !StatusFlags::SYNCING);
282
283                // Emit event if callback set
284                if let Some(ref callback) = self.event_callback {
285                    callback(GattEvent::SyncDataReceived { header, payload });
286                }
287
288                Ok(None)
289            }
290            SyncDataOp::Ack => {
291                // Acknowledgement received (shouldn't happen on write)
292                Ok(None)
293            }
294        }
295    }
296
297    /// Handle Command write request
298    pub fn write_command(&self, data: &[u8]) -> Result<()> {
299        let command = Command::decode(data)
300            .ok_or_else(|| crate::error::BleError::GattError("Invalid command data".to_string()))?;
301
302        match command.cmd_type {
303            CommandType::StartSync => {
304                let mut state = self.state.write().unwrap();
305                state.sync_state.state = SyncState::Syncing;
306                state.sync_state.progress = 0;
307            }
308            CommandType::StopSync => {
309                let mut state = self.state.write().unwrap();
310                state.sync_state.state = SyncState::Idle;
311            }
312            CommandType::RefreshInfo => {
313                // Trigger info refresh (no-op for now)
314            }
315            CommandType::SetHierarchy => {
316                if !command.payload.is_empty() {
317                    let mut state = self.state.write().unwrap();
318                    state.node_info.hierarchy_level = HierarchyLevel::from(command.payload[0]);
319                }
320            }
321            CommandType::Ping => {
322                // Keepalive - no action needed
323            }
324            CommandType::Reset => {
325                let mut state = self.state.write().unwrap();
326                state.sync_state = SyncStateData::new(SyncState::Idle);
327            }
328        }
329
330        // Emit event if callback set
331        if let Some(ref callback) = self.event_callback {
332            callback(GattEvent::CommandReceived {
333                command: command.cmd_type,
334                payload: command.payload,
335            });
336        }
337
338        Ok(())
339    }
340
341    // === State Updates ===
342
343    /// Update battery percentage
344    pub fn update_battery(&self, percent: u8) {
345        let mut state = self.state.write().unwrap();
346        state.node_info.battery_percent = percent.min(100);
347
348        // Update low battery flag
349        if percent < 20 {
350            state.status.flags =
351                StatusFlags::new(state.status.flags.flags() | StatusFlags::LOW_BATTERY);
352        } else {
353            state.status.flags =
354                StatusFlags::new(state.status.flags.flags() & !StatusFlags::LOW_BATTERY);
355        }
356    }
357
358    /// Update hierarchy level
359    pub fn update_hierarchy_level(&self, level: HierarchyLevel) {
360        let mut state = self.state.write().unwrap();
361        state.node_info.hierarchy_level = level;
362    }
363
364    /// Update sync progress
365    pub fn update_sync_progress(&self, progress: u8, pending_docs: u16) {
366        let mut state = self.state.write().unwrap();
367        state.sync_state.progress = progress.min(100);
368        state.sync_state.pending_docs = pending_docs;
369
370        if progress >= 100 {
371            state.sync_state.state = SyncState::Complete;
372        }
373    }
374
375    /// Update parent connection status
376    pub fn update_parent_status(&self, connected: bool, rssi: Option<i8>) {
377        let mut state = self.state.write().unwrap();
378
379        if connected {
380            state.status.flags =
381                StatusFlags::new(state.status.flags.flags() | StatusFlags::CONNECTED);
382            state.status.parent_rssi = rssi.unwrap_or(0);
383        } else {
384            state.status.flags =
385                StatusFlags::new(state.status.flags.flags() & !StatusFlags::CONNECTED);
386            state.status.parent_rssi = 127; // No parent
387        }
388    }
389
390    /// Update child count
391    pub fn update_child_count(&self, count: u8) {
392        let mut state = self.state.write().unwrap();
393        state.status.child_count = count;
394    }
395
396    /// Update uptime
397    pub fn update_uptime(&self, minutes: u16) {
398        let mut state = self.state.write().unwrap();
399        state.status.uptime_minutes = minutes;
400    }
401
402    // === Connection Management ===
403
404    /// Handle client connection
405    pub fn on_client_connected(&self, address: String) {
406        let mut state = self.state.write().unwrap();
407        if !state.connected_clients.contains(&address) {
408            state.connected_clients.push(address.clone());
409        }
410
411        if let Some(ref callback) = self.event_callback {
412            callback(GattEvent::ClientConnected { address });
413        }
414    }
415
416    /// Handle client disconnection
417    pub fn on_client_disconnected(&self, address: &str) {
418        let mut state = self.state.write().unwrap();
419        state.connected_clients.retain(|a| a != address);
420        state.sync_state_subscribers.retain(|a| a != address);
421        state.status_subscribers.retain(|a| a != address);
422
423        if let Some(ref callback) = self.event_callback {
424            callback(GattEvent::ClientDisconnected {
425                address: address.to_string(),
426            });
427        }
428    }
429
430    /// Handle notification subscription
431    pub fn on_subscribe(&self, address: String, characteristic: &str) {
432        let mut state = self.state.write().unwrap();
433
434        match characteristic {
435            "sync_state" => {
436                if !state.sync_state_subscribers.contains(&address) {
437                    state.sync_state_subscribers.push(address);
438                }
439            }
440            "status" => {
441                if !state.status_subscribers.contains(&address) {
442                    state.status_subscribers.push(address);
443                }
444            }
445            _ => {}
446        }
447
448        if let Some(ref callback) = self.event_callback {
449            callback(GattEvent::NotificationSubscribed {
450                characteristic: characteristic.to_string(),
451            });
452        }
453    }
454
455    /// Handle MTU change
456    pub fn on_mtu_changed(&self, mtu: u16) {
457        let mut state = self.state.write().unwrap();
458        state.mtu = mtu;
459
460        if let Some(ref callback) = self.event_callback {
461            callback(GattEvent::MtuChanged { mtu });
462        }
463    }
464
465    /// Get current MTU
466    pub fn mtu(&self) -> u16 {
467        let state = self.state.read().unwrap();
468        state.mtu
469    }
470
471    /// Get connected client count
472    pub fn connected_client_count(&self) -> usize {
473        let state = self.state.read().unwrap();
474        state.connected_clients.len()
475    }
476
477    /// Get list of addresses subscribed to sync state
478    pub fn sync_state_subscribers(&self) -> Vec<String> {
479        let state = self.state.read().unwrap();
480        state.sync_state_subscribers.clone()
481    }
482
483    /// Get list of addresses subscribed to status
484    pub fn status_subscribers(&self) -> Vec<String> {
485        let state = self.state.read().unwrap();
486        state.status_subscribers.clone()
487    }
488}
489
490#[cfg(test)]
491mod tests {
492    use super::*;
493    use crate::capabilities;
494
495    #[test]
496    fn test_gatt_service_creation() {
497        let service = HiveGattService::new(
498            NodeId::new(0x12345678),
499            HierarchyLevel::Squad,
500            capabilities::CAN_RELAY,
501        );
502
503        assert_eq!(service.service_uuid(), HIVE_SERVICE_UUID);
504        assert_eq!(service.characteristics().len(), 5);
505    }
506
507    #[test]
508    fn test_read_node_info() {
509        let service = HiveGattService::new(
510            NodeId::new(0x12345678),
511            HierarchyLevel::Squad,
512            capabilities::CAN_RELAY,
513        );
514
515        let data = service.read_node_info();
516        assert_eq!(data.len(), NodeInfo::ENCODED_SIZE);
517
518        let info = NodeInfo::decode(&data).unwrap();
519        assert_eq!(info.node_id, NodeId::new(0x12345678));
520        assert_eq!(info.hierarchy_level, HierarchyLevel::Squad);
521    }
522
523    #[test]
524    fn test_write_command() {
525        let service = HiveGattService::new(NodeId::new(0x12345678), HierarchyLevel::Platform, 0);
526
527        // Set hierarchy command
528        let cmd = Command::with_payload(CommandType::SetHierarchy, vec![2]); // Platoon
529        service.write_command(&cmd.encode()).unwrap();
530
531        let data = service.read_node_info();
532        let info = NodeInfo::decode(&data).unwrap();
533        assert_eq!(info.hierarchy_level, HierarchyLevel::Platoon);
534    }
535
536    #[test]
537    fn test_sync_data_flow() {
538        let service = HiveGattService::new(NodeId::new(0x12345678), HierarchyLevel::Platform, 0);
539
540        // Start sync
541        let cmd = Command::new(CommandType::StartSync);
542        service.write_command(&cmd.encode()).unwrap();
543
544        // Check sync state
545        let state_data = service.read_sync_state();
546        let state = SyncStateData::decode(&state_data).unwrap();
547        assert_eq!(state.state, SyncState::Syncing);
548
549        // Send document
550        let mut header = SyncDataHeader::new(SyncDataOp::Document, 1);
551        let mut data = header.encode().to_vec();
552        data.extend_from_slice(b"test document data");
553
554        let response = service.write_sync_data(&data).unwrap();
555        assert!(response.is_some()); // Should get ACK
556
557        // End sync
558        header = SyncDataHeader::new(SyncDataOp::End, 2);
559        service.write_sync_data(&header.encode()).unwrap();
560
561        // Check sync complete
562        let state_data = service.read_sync_state();
563        let state = SyncStateData::decode(&state_data).unwrap();
564        assert_eq!(state.state, SyncState::Complete);
565    }
566
567    #[test]
568    fn test_battery_update() {
569        let service = HiveGattService::new(NodeId::new(0x12345678), HierarchyLevel::Platform, 0);
570
571        service.update_battery(15);
572
573        let data = service.read_node_info();
574        let info = NodeInfo::decode(&data).unwrap();
575        assert_eq!(info.battery_percent, 15);
576
577        let status_data = service.read_status();
578        let status = StatusData::decode(&status_data).unwrap();
579        assert!(status.flags.is_low_battery());
580    }
581
582    #[test]
583    fn test_client_connection() {
584        let service = HiveGattService::new(NodeId::new(0x12345678), HierarchyLevel::Platform, 0);
585
586        service.on_client_connected("AA:BB:CC:DD:EE:FF".to_string());
587        assert_eq!(service.connected_client_count(), 1);
588
589        service.on_client_disconnected("AA:BB:CC:DD:EE:FF");
590        assert_eq!(service.connected_client_count(), 0);
591    }
592
593    #[test]
594    fn test_mtu_negotiation() {
595        let service = HiveGattService::new(NodeId::new(0x12345678), HierarchyLevel::Platform, 0);
596
597        assert_eq!(service.mtu(), 23); // Default
598
599        service.on_mtu_changed(251);
600        assert_eq!(service.mtu(), 251);
601    }
602
603    #[test]
604    fn test_hive_characteristics() {
605        let chars = HiveCharacteristics::all();
606        assert_eq!(chars.len(), 5);
607
608        let node_info = HiveCharacteristics::node_info();
609        assert!(node_info.properties.can_read());
610        assert!(!node_info.properties.can_write());
611
612        let sync_data = HiveCharacteristics::sync_data();
613        assert!(sync_data.properties.can_write());
614        assert!(sync_data.properties.can_indicate());
615    }
616}