1use std::sync::{Arc, RwLock};
23
24use crate::error::Result;
25use crate::{HierarchyLevel, NodeId, HIVE_SERVICE_UUID};
26
27use super::characteristics::{
28 CharacteristicProperties, Command, CommandType, HiveCharacteristicUuids, NodeInfo, StatusData,
29 StatusFlags, SyncDataHeader, SyncDataOp, SyncState, SyncStateData,
30};
31
32pub type GattEventCallback = Box<dyn Fn(GattEvent) + Send + Sync>;
34
35#[derive(Debug, Clone)]
37pub enum GattEvent {
38 ClientConnected {
40 address: String,
42 },
43 ClientDisconnected {
45 address: String,
47 },
48 NotificationSubscribed {
50 characteristic: String,
52 },
53 NotificationUnsubscribed {
55 characteristic: String,
57 },
58 CommandReceived {
60 command: CommandType,
62 payload: Vec<u8>,
64 },
65 SyncDataReceived {
67 header: SyncDataHeader,
69 payload: Vec<u8>,
71 },
72 MtuChanged {
74 mtu: u16,
76 },
77}
78
79#[derive(Debug, Clone)]
81pub struct CharacteristicDescriptor {
82 pub uuid: uuid::Uuid,
84 pub name: &'static str,
86 pub properties: CharacteristicProperties,
88 pub encrypted: bool,
90}
91
92pub struct HiveCharacteristics;
94
95impl HiveCharacteristics {
96 pub fn node_info() -> CharacteristicDescriptor {
98 CharacteristicDescriptor {
99 uuid: HiveCharacteristicUuids::node_info(),
100 name: "Node Info",
101 properties: CharacteristicProperties::new(CharacteristicProperties::READ),
102 encrypted: true,
103 }
104 }
105
106 pub fn sync_state() -> CharacteristicDescriptor {
108 CharacteristicDescriptor {
109 uuid: HiveCharacteristicUuids::sync_state(),
110 name: "Sync State",
111 properties: CharacteristicProperties::new(
112 CharacteristicProperties::READ | CharacteristicProperties::NOTIFY,
113 ),
114 encrypted: true,
115 }
116 }
117
118 pub fn sync_data() -> CharacteristicDescriptor {
120 CharacteristicDescriptor {
121 uuid: HiveCharacteristicUuids::sync_data(),
122 name: "Sync Data",
123 properties: CharacteristicProperties::new(
124 CharacteristicProperties::WRITE | CharacteristicProperties::INDICATE,
125 ),
126 encrypted: true,
127 }
128 }
129
130 pub fn command() -> CharacteristicDescriptor {
132 CharacteristicDescriptor {
133 uuid: HiveCharacteristicUuids::command(),
134 name: "Command",
135 properties: CharacteristicProperties::new(CharacteristicProperties::WRITE),
136 encrypted: true,
137 }
138 }
139
140 pub fn status() -> CharacteristicDescriptor {
142 CharacteristicDescriptor {
143 uuid: HiveCharacteristicUuids::status(),
144 name: "Status",
145 properties: CharacteristicProperties::new(
146 CharacteristicProperties::READ | CharacteristicProperties::NOTIFY,
147 ),
148 encrypted: true,
149 }
150 }
151
152 pub fn all() -> Vec<CharacteristicDescriptor> {
154 vec![
155 Self::node_info(),
156 Self::sync_state(),
157 Self::sync_data(),
158 Self::command(),
159 Self::status(),
160 ]
161 }
162}
163
164struct ServiceState {
166 node_info: NodeInfo,
168 sync_state: SyncStateData,
170 status: StatusData,
172 connected_clients: Vec<String>,
174 sync_state_subscribers: Vec<String>,
176 status_subscribers: Vec<String>,
178 mtu: u16,
180}
181
182impl ServiceState {
183 fn new(node_id: NodeId, hierarchy_level: HierarchyLevel, capabilities: u16) -> Self {
184 Self {
185 node_info: NodeInfo::new(node_id, hierarchy_level, capabilities),
186 sync_state: SyncStateData::new(SyncState::Idle),
187 status: StatusData::new(),
188 connected_clients: Vec::new(),
189 sync_state_subscribers: Vec::new(),
190 status_subscribers: Vec::new(),
191 mtu: 23, }
193 }
194}
195
196pub struct HiveGattService {
200 pub uuid: uuid::Uuid,
202 state: Arc<RwLock<ServiceState>>,
204 #[allow(dead_code)]
206 event_callback: Option<GattEventCallback>,
207}
208
209impl HiveGattService {
210 pub fn new(node_id: NodeId, hierarchy_level: HierarchyLevel, capabilities: u16) -> Self {
212 Self {
213 uuid: HIVE_SERVICE_UUID,
214 state: Arc::new(RwLock::new(ServiceState::new(
215 node_id,
216 hierarchy_level,
217 capabilities,
218 ))),
219 event_callback: None,
220 }
221 }
222
223 pub fn set_event_callback(&mut self, callback: GattEventCallback) {
225 self.event_callback = Some(callback);
226 }
227
228 pub fn service_uuid(&self) -> uuid::Uuid {
230 self.uuid
231 }
232
233 pub fn characteristics(&self) -> Vec<CharacteristicDescriptor> {
235 HiveCharacteristics::all()
236 }
237
238 pub fn read_node_info(&self) -> Vec<u8> {
242 let state = self.state.read().unwrap();
243 state.node_info.encode().to_vec()
244 }
245
246 pub fn read_sync_state(&self) -> Vec<u8> {
248 let state = self.state.read().unwrap();
249 state.sync_state.encode().to_vec()
250 }
251
252 pub fn read_status(&self) -> Vec<u8> {
254 let state = self.state.read().unwrap();
255 state.status.encode().to_vec()
256 }
257
258 pub fn write_sync_data(&self, data: &[u8]) -> Result<Option<Vec<u8>>> {
262 let header = SyncDataHeader::decode(data).ok_or_else(|| {
263 crate::error::BleError::GattError("Invalid sync data header".to_string())
264 })?;
265
266 let payload = if data.len() > SyncDataHeader::SIZE {
267 data[SyncDataHeader::SIZE..].to_vec()
268 } else {
269 Vec::new()
270 };
271
272 match header.op {
274 SyncDataOp::Document => {
275 let mut state = self.state.write().unwrap();
277 state.sync_state.state = SyncState::Syncing;
278 state.status.flags =
279 StatusFlags::new(state.status.flags.flags() | StatusFlags::SYNCING);
280
281 let ack = SyncDataHeader::new(SyncDataOp::Ack, header.seq);
283 Ok(Some(ack.encode().to_vec()))
284 }
285 SyncDataOp::Vector => {
286 let ack = SyncDataHeader::new(SyncDataOp::Ack, header.seq);
288 Ok(Some(ack.encode().to_vec()))
289 }
290 SyncDataOp::End => {
291 let mut state = self.state.write().unwrap();
293 state.sync_state.state = SyncState::Complete;
294 state.sync_state.progress = 100;
295 state.status.flags =
296 StatusFlags::new(state.status.flags.flags() & !StatusFlags::SYNCING);
297
298 if let Some(ref callback) = self.event_callback {
300 callback(GattEvent::SyncDataReceived { header, payload });
301 }
302
303 Ok(None)
304 }
305 SyncDataOp::Ack => {
306 Ok(None)
308 }
309 }
310 }
311
312 pub fn write_command(&self, data: &[u8]) -> Result<()> {
314 let command = Command::decode(data)
315 .ok_or_else(|| crate::error::BleError::GattError("Invalid command data".to_string()))?;
316
317 match command.cmd_type {
318 CommandType::StartSync => {
319 let mut state = self.state.write().unwrap();
320 state.sync_state.state = SyncState::Syncing;
321 state.sync_state.progress = 0;
322 }
323 CommandType::StopSync => {
324 let mut state = self.state.write().unwrap();
325 state.sync_state.state = SyncState::Idle;
326 }
327 CommandType::RefreshInfo => {
328 }
330 CommandType::SetHierarchy => {
331 if !command.payload.is_empty() {
332 let mut state = self.state.write().unwrap();
333 state.node_info.hierarchy_level = HierarchyLevel::from(command.payload[0]);
334 }
335 }
336 CommandType::Ping => {
337 }
339 CommandType::Reset => {
340 let mut state = self.state.write().unwrap();
341 state.sync_state = SyncStateData::new(SyncState::Idle);
342 }
343 }
344
345 if let Some(ref callback) = self.event_callback {
347 callback(GattEvent::CommandReceived {
348 command: command.cmd_type,
349 payload: command.payload,
350 });
351 }
352
353 Ok(())
354 }
355
356 pub fn update_battery(&self, percent: u8) {
360 let mut state = self.state.write().unwrap();
361 state.node_info.battery_percent = percent.min(100);
362
363 if percent < 20 {
365 state.status.flags =
366 StatusFlags::new(state.status.flags.flags() | StatusFlags::LOW_BATTERY);
367 } else {
368 state.status.flags =
369 StatusFlags::new(state.status.flags.flags() & !StatusFlags::LOW_BATTERY);
370 }
371 }
372
373 pub fn update_hierarchy_level(&self, level: HierarchyLevel) {
375 let mut state = self.state.write().unwrap();
376 state.node_info.hierarchy_level = level;
377 }
378
379 pub fn update_sync_progress(&self, progress: u8, pending_docs: u16) {
381 let mut state = self.state.write().unwrap();
382 state.sync_state.progress = progress.min(100);
383 state.sync_state.pending_docs = pending_docs;
384
385 if progress >= 100 {
386 state.sync_state.state = SyncState::Complete;
387 }
388 }
389
390 pub fn update_parent_status(&self, connected: bool, rssi: Option<i8>) {
392 let mut state = self.state.write().unwrap();
393
394 if connected {
395 state.status.flags =
396 StatusFlags::new(state.status.flags.flags() | StatusFlags::CONNECTED);
397 state.status.parent_rssi = rssi.unwrap_or(0);
398 } else {
399 state.status.flags =
400 StatusFlags::new(state.status.flags.flags() & !StatusFlags::CONNECTED);
401 state.status.parent_rssi = 127; }
403 }
404
405 pub fn update_child_count(&self, count: u8) {
407 let mut state = self.state.write().unwrap();
408 state.status.child_count = count;
409 }
410
411 pub fn update_uptime(&self, minutes: u16) {
413 let mut state = self.state.write().unwrap();
414 state.status.uptime_minutes = minutes;
415 }
416
417 pub fn on_client_connected(&self, address: String) {
421 let mut state = self.state.write().unwrap();
422 if !state.connected_clients.contains(&address) {
423 state.connected_clients.push(address.clone());
424 }
425
426 if let Some(ref callback) = self.event_callback {
427 callback(GattEvent::ClientConnected { address });
428 }
429 }
430
431 pub fn on_client_disconnected(&self, address: &str) {
433 let mut state = self.state.write().unwrap();
434 state.connected_clients.retain(|a| a != address);
435 state.sync_state_subscribers.retain(|a| a != address);
436 state.status_subscribers.retain(|a| a != address);
437
438 if let Some(ref callback) = self.event_callback {
439 callback(GattEvent::ClientDisconnected {
440 address: address.to_string(),
441 });
442 }
443 }
444
445 pub fn on_subscribe(&self, address: String, characteristic: &str) {
447 let mut state = self.state.write().unwrap();
448
449 match characteristic {
450 "sync_state" => {
451 if !state.sync_state_subscribers.contains(&address) {
452 state.sync_state_subscribers.push(address);
453 }
454 }
455 "status" => {
456 if !state.status_subscribers.contains(&address) {
457 state.status_subscribers.push(address);
458 }
459 }
460 _ => {}
461 }
462
463 if let Some(ref callback) = self.event_callback {
464 callback(GattEvent::NotificationSubscribed {
465 characteristic: characteristic.to_string(),
466 });
467 }
468 }
469
470 pub fn on_mtu_changed(&self, mtu: u16) {
472 let mut state = self.state.write().unwrap();
473 state.mtu = mtu;
474
475 if let Some(ref callback) = self.event_callback {
476 callback(GattEvent::MtuChanged { mtu });
477 }
478 }
479
480 pub fn mtu(&self) -> u16 {
482 let state = self.state.read().unwrap();
483 state.mtu
484 }
485
486 pub fn connected_client_count(&self) -> usize {
488 let state = self.state.read().unwrap();
489 state.connected_clients.len()
490 }
491
492 pub fn sync_state_subscribers(&self) -> Vec<String> {
494 let state = self.state.read().unwrap();
495 state.sync_state_subscribers.clone()
496 }
497
498 pub fn status_subscribers(&self) -> Vec<String> {
500 let state = self.state.read().unwrap();
501 state.status_subscribers.clone()
502 }
503}
504
505#[cfg(test)]
506mod tests {
507 use super::*;
508 use crate::capabilities;
509
510 #[test]
511 fn test_gatt_service_creation() {
512 let service = HiveGattService::new(
513 NodeId::new(0x12345678),
514 HierarchyLevel::Squad,
515 capabilities::CAN_RELAY,
516 );
517
518 assert_eq!(service.service_uuid(), HIVE_SERVICE_UUID);
519 assert_eq!(service.characteristics().len(), 5);
520 }
521
522 #[test]
523 fn test_read_node_info() {
524 let service = HiveGattService::new(
525 NodeId::new(0x12345678),
526 HierarchyLevel::Squad,
527 capabilities::CAN_RELAY,
528 );
529
530 let data = service.read_node_info();
531 assert_eq!(data.len(), NodeInfo::ENCODED_SIZE);
532
533 let info = NodeInfo::decode(&data).unwrap();
534 assert_eq!(info.node_id, NodeId::new(0x12345678));
535 assert_eq!(info.hierarchy_level, HierarchyLevel::Squad);
536 }
537
538 #[test]
539 fn test_write_command() {
540 let service = HiveGattService::new(NodeId::new(0x12345678), HierarchyLevel::Platform, 0);
541
542 let cmd = Command::with_payload(CommandType::SetHierarchy, vec![2]); service.write_command(&cmd.encode()).unwrap();
545
546 let data = service.read_node_info();
547 let info = NodeInfo::decode(&data).unwrap();
548 assert_eq!(info.hierarchy_level, HierarchyLevel::Platoon);
549 }
550
551 #[test]
552 fn test_sync_data_flow() {
553 let service = HiveGattService::new(NodeId::new(0x12345678), HierarchyLevel::Platform, 0);
554
555 let cmd = Command::new(CommandType::StartSync);
557 service.write_command(&cmd.encode()).unwrap();
558
559 let state_data = service.read_sync_state();
561 let state = SyncStateData::decode(&state_data).unwrap();
562 assert_eq!(state.state, SyncState::Syncing);
563
564 let mut header = SyncDataHeader::new(SyncDataOp::Document, 1);
566 let mut data = header.encode().to_vec();
567 data.extend_from_slice(b"test document data");
568
569 let response = service.write_sync_data(&data).unwrap();
570 assert!(response.is_some()); header = SyncDataHeader::new(SyncDataOp::End, 2);
574 service.write_sync_data(&header.encode()).unwrap();
575
576 let state_data = service.read_sync_state();
578 let state = SyncStateData::decode(&state_data).unwrap();
579 assert_eq!(state.state, SyncState::Complete);
580 }
581
582 #[test]
583 fn test_battery_update() {
584 let service = HiveGattService::new(NodeId::new(0x12345678), HierarchyLevel::Platform, 0);
585
586 service.update_battery(15);
587
588 let data = service.read_node_info();
589 let info = NodeInfo::decode(&data).unwrap();
590 assert_eq!(info.battery_percent, 15);
591
592 let status_data = service.read_status();
593 let status = StatusData::decode(&status_data).unwrap();
594 assert!(status.flags.is_low_battery());
595 }
596
597 #[test]
598 fn test_client_connection() {
599 let service = HiveGattService::new(NodeId::new(0x12345678), HierarchyLevel::Platform, 0);
600
601 service.on_client_connected("AA:BB:CC:DD:EE:FF".to_string());
602 assert_eq!(service.connected_client_count(), 1);
603
604 service.on_client_disconnected("AA:BB:CC:DD:EE:FF");
605 assert_eq!(service.connected_client_count(), 0);
606 }
607
608 #[test]
609 fn test_mtu_negotiation() {
610 let service = HiveGattService::new(NodeId::new(0x12345678), HierarchyLevel::Platform, 0);
611
612 assert_eq!(service.mtu(), 23); service.on_mtu_changed(251);
615 assert_eq!(service.mtu(), 251);
616 }
617
618 #[test]
619 fn test_hive_characteristics() {
620 let chars = HiveCharacteristics::all();
621 assert_eq!(chars.len(), 5);
622
623 let node_info = HiveCharacteristics::node_info();
624 assert!(node_info.properties.can_read());
625 assert!(!node_info.properties.can_write());
626
627 let sync_data = HiveCharacteristics::sync_data();
628 assert!(sync_data.properties.can_write());
629 assert!(sync_data.properties.can_indicate());
630 }
631}