use std::sync::{Arc, Mutex};
use windows::core::GUID;
use windows::Devices::Bluetooth::GenericAttributeProfile::{
GattCharacteristicProperties, GattLocalCharacteristic, GattLocalCharacteristicParameters,
GattLocalService, GattProtectionLevel, GattServiceProvider,
GattServiceProviderAdvertisementStatus, GattServiceProviderAdvertisingParameters,
};
use windows::Foundation::TypedEventHandler;
use crate::error::{BleError, Result};
use crate::NodeId;
const PEAT_SERVICE_UUID: GUID = GUID::from_values(
0xf47ac10b,
0x58cc,
0x4372,
[0xa5, 0x67, 0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79],
);
const CHAR_NODE_INFO_UUID: GUID = GUID::from_values(
0xf47ac10b,
0x58cc,
0x4372,
[0xa5, 0x67, 0x0e, 0x02, 0xb2, 0xc3, 0xd0, 0x01],
);
const CHAR_SYNC_STATE_UUID: GUID = GUID::from_values(
0xf47ac10b,
0x58cc,
0x4372,
[0xa5, 0x67, 0x0e, 0x02, 0xb2, 0xc3, 0xd0, 0x02],
);
const CHAR_SYNC_DATA_UUID: GUID = GUID::from_values(
0xf47ac10b,
0x58cc,
0x4372,
[0xa5, 0x67, 0x0e, 0x02, 0xb2, 0xc3, 0xd0, 0x03],
);
struct GattServerState {
node_id: NodeId,
document_data: Vec<u8>,
write_callback: Option<Box<dyn Fn(Vec<u8>) + Send + Sync>>,
}
pub struct GattServer {
provider: Option<GattServiceProvider>,
service: Option<GattLocalService>,
node_info_char: Option<GattLocalCharacteristic>,
sync_state_char: Option<GattLocalCharacteristic>,
sync_data_char: Option<GattLocalCharacteristic>,
state: Arc<Mutex<GattServerState>>,
is_advertising: bool,
}
impl GattServer {
pub fn new(node_id: NodeId) -> Result<Self> {
Ok(Self {
provider: None,
service: None,
node_info_char: None,
sync_state_char: None,
sync_data_char: None,
state: Arc::new(Mutex::new(GattServerState {
node_id,
document_data: Vec::new(),
write_callback: None,
})),
is_advertising: false,
})
}
pub fn init_sync(&mut self) -> Result<()> {
let create_op = GattServiceProvider::CreateAsync(PEAT_SERVICE_UUID).map_err(|e| {
BleError::GattError(format!("Failed to create service provider: {}", e))
})?;
let provider_result = create_op
.get()
.map_err(|e| BleError::GattError(format!("Service provider creation failed: {}", e)))?;
let provider = provider_result
.ServiceProvider()
.map_err(|e| BleError::GattError(format!("Failed to get provider: {}", e)))?;
let service = provider
.Service()
.map_err(|e| BleError::GattError(format!("Failed to get service: {}", e)))?;
let node_info_char = self.create_characteristic_sync(
&service,
CHAR_NODE_INFO_UUID,
GattCharacteristicProperties::Read,
)?;
let sync_state_char = self.create_characteristic_sync(
&service,
CHAR_SYNC_STATE_UUID,
GattCharacteristicProperties::Read | GattCharacteristicProperties::Notify,
)?;
let sync_data_char = self.create_characteristic_sync(
&service,
CHAR_SYNC_DATA_UUID,
GattCharacteristicProperties::Read
| GattCharacteristicProperties::Write
| GattCharacteristicProperties::Notify,
)?;
self.setup_write_handler(&sync_data_char)?;
self.provider = Some(provider);
self.service = Some(service);
self.node_info_char = Some(node_info_char);
self.sync_state_char = Some(sync_state_char);
self.sync_data_char = Some(sync_data_char);
log::info!("GATT server initialized");
Ok(())
}
pub async fn init(&mut self) -> Result<()> {
self.init_sync()
}
fn create_characteristic_sync(
&self,
service: &GattLocalService,
uuid: GUID,
properties: GattCharacteristicProperties,
) -> Result<GattLocalCharacteristic> {
let params = GattLocalCharacteristicParameters::new()
.map_err(|e| BleError::GattError(format!("Failed to create params: {}", e)))?;
params
.SetCharacteristicProperties(properties)
.map_err(|e| BleError::GattError(format!("Failed to set properties: {}", e)))?;
params
.SetReadProtectionLevel(GattProtectionLevel::Plain)
.map_err(|e| BleError::GattError(format!("Failed to set read protection: {}", e)))?;
params
.SetWriteProtectionLevel(GattProtectionLevel::Plain)
.map_err(|e| BleError::GattError(format!("Failed to set write protection: {}", e)))?;
let create_op = service
.CreateCharacteristicAsync(uuid, ¶ms)
.map_err(|e| BleError::GattError(format!("Failed to create characteristic: {}", e)))?;
let char_result = create_op
.get()
.map_err(|e| BleError::GattError(format!("Characteristic creation failed: {}", e)))?;
let char = char_result
.Characteristic()
.map_err(|e| BleError::GattError(format!("Failed to get characteristic: {}", e)))?;
Ok(char)
}
fn setup_write_handler(&self, char: &GattLocalCharacteristic) -> Result<()> {
let _state = self.state.clone();
let handler = TypedEventHandler::new(
move |_char,
args: &Option<
windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteRequestedEventArgs,
>| {
if let Some(args) = args {
if let Ok(deferral) = args.GetDeferral() {
log::debug!("Received write request on sync data characteristic");
deferral.Complete().ok();
}
}
Ok(())
},
);
char.WriteRequested(&handler)
.map_err(|e| BleError::GattError(format!("Failed to set write handler: {}", e)))?;
Ok(())
}
pub fn start_advertising(&mut self) -> Result<()> {
if self.is_advertising {
return Ok(());
}
let provider = self
.provider
.as_ref()
.ok_or_else(|| BleError::InvalidState("Server not initialized".to_string()))?;
let params = GattServiceProviderAdvertisingParameters::new().map_err(|e| {
BleError::GattError(format!("Failed to create advertising params: {}", e))
})?;
params
.SetIsDiscoverable(true)
.map_err(|e| BleError::GattError(format!("Failed to set discoverable: {}", e)))?;
params
.SetIsConnectable(true)
.map_err(|e| BleError::GattError(format!("Failed to set connectable: {}", e)))?;
provider
.StartAdvertisingWithParameters(¶ms)
.map_err(|e| BleError::GattError(format!("Failed to start advertising: {}", e)))?;
self.is_advertising = true;
log::info!("GATT server advertising started");
Ok(())
}
pub fn stop_advertising(&mut self) -> Result<()> {
if !self.is_advertising {
return Ok(());
}
if let Some(provider) = &self.provider {
provider
.StopAdvertising()
.map_err(|e| BleError::GattError(format!("Failed to stop advertising: {}", e)))?;
}
self.is_advertising = false;
log::info!("GATT server advertising stopped");
Ok(())
}
pub fn set_document_data(&self, data: Vec<u8>) {
if let Ok(mut state) = self.state.lock() {
state.document_data = data;
}
}
pub fn set_write_callback(&self, callback: Box<dyn Fn(Vec<u8>) + Send + Sync>) {
if let Ok(mut state) = self.state.lock() {
state.write_callback = Some(callback);
}
}
pub fn advertising_status(&self) -> Result<GattServiceProviderAdvertisementStatus> {
let provider = self
.provider
.as_ref()
.ok_or_else(|| BleError::InvalidState("Server not initialized".to_string()))?;
provider
.AdvertisementStatus()
.map_err(|e| BleError::GattError(format!("Failed to get status: {}", e)))
}
pub fn is_advertising(&self) -> bool {
self.is_advertising
}
}
impl Drop for GattServer {
fn drop(&mut self) {
let _ = self.stop_advertising();
}
}