1use 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
17pub type GattEventCallback = Box<dyn Fn(GattEvent) + Send + Sync>;
19
20#[derive(Debug, Clone)]
22pub enum GattEvent {
23 ClientConnected {
25 address: String,
27 },
28 ClientDisconnected {
30 address: String,
32 },
33 NotificationSubscribed {
35 characteristic: String,
37 },
38 NotificationUnsubscribed {
40 characteristic: String,
42 },
43 CommandReceived {
45 command: CommandType,
47 payload: Vec<u8>,
49 },
50 SyncDataReceived {
52 header: SyncDataHeader,
54 payload: Vec<u8>,
56 },
57 MtuChanged {
59 mtu: u16,
61 },
62}
63
64#[derive(Debug, Clone)]
66pub struct CharacteristicDescriptor {
67 pub uuid: uuid::Uuid,
69 pub name: &'static str,
71 pub properties: CharacteristicProperties,
73 pub encrypted: bool,
75}
76
77pub struct HiveCharacteristics;
79
80impl HiveCharacteristics {
81 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 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 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 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 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 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
149struct ServiceState {
151 node_info: NodeInfo,
153 sync_state: SyncStateData,
155 status: StatusData,
157 connected_clients: Vec<String>,
159 sync_state_subscribers: Vec<String>,
161 status_subscribers: Vec<String>,
163 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, }
178 }
179}
180
181pub struct HiveGattService {
185 pub uuid: uuid::Uuid,
187 state: Arc<RwLock<ServiceState>>,
189 #[allow(dead_code)]
191 event_callback: Option<GattEventCallback>,
192}
193
194impl HiveGattService {
195 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 pub fn set_event_callback(&mut self, callback: GattEventCallback) {
210 self.event_callback = Some(callback);
211 }
212
213 pub fn service_uuid(&self) -> uuid::Uuid {
215 self.uuid
216 }
217
218 pub fn characteristics(&self) -> Vec<CharacteristicDescriptor> {
220 HiveCharacteristics::all()
221 }
222
223 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 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 pub fn read_status(&self) -> Vec<u8> {
239 let state = self.state.read().unwrap();
240 state.status.encode().to_vec()
241 }
242
243 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 match header.op {
259 SyncDataOp::Document => {
260 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 let ack = SyncDataHeader::new(SyncDataOp::Ack, header.seq);
268 Ok(Some(ack.encode().to_vec()))
269 }
270 SyncDataOp::Vector => {
271 let ack = SyncDataHeader::new(SyncDataOp::Ack, header.seq);
273 Ok(Some(ack.encode().to_vec()))
274 }
275 SyncDataOp::End => {
276 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 if let Some(ref callback) = self.event_callback {
285 callback(GattEvent::SyncDataReceived { header, payload });
286 }
287
288 Ok(None)
289 }
290 SyncDataOp::Ack => {
291 Ok(None)
293 }
294 }
295 }
296
297 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 }
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 }
324 CommandType::Reset => {
325 let mut state = self.state.write().unwrap();
326 state.sync_state = SyncStateData::new(SyncState::Idle);
327 }
328 }
329
330 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 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 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 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 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 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; }
388 }
389
390 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 pub fn update_uptime(&self, minutes: u16) {
398 let mut state = self.state.write().unwrap();
399 state.status.uptime_minutes = minutes;
400 }
401
402 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 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 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 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 pub fn mtu(&self) -> u16 {
467 let state = self.state.read().unwrap();
468 state.mtu
469 }
470
471 pub fn connected_client_count(&self) -> usize {
473 let state = self.state.read().unwrap();
474 state.connected_clients.len()
475 }
476
477 pub fn sync_state_subscribers(&self) -> Vec<String> {
479 let state = self.state.read().unwrap();
480 state.sync_state_subscribers.clone()
481 }
482
483 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 let cmd = Command::with_payload(CommandType::SetHierarchy, vec![2]); 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 let cmd = Command::new(CommandType::StartSync);
542 service.write_command(&cmd.encode()).unwrap();
543
544 let state_data = service.read_sync_state();
546 let state = SyncStateData::decode(&state_data).unwrap();
547 assert_eq!(state.state, SyncState::Syncing);
548
549 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()); header = SyncDataHeader::new(SyncDataOp::End, 2);
559 service.write_sync_data(&header.encode()).unwrap();
560
561 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); 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}