use super::ble_client::BLEClientState;
use crate::{
ble,
utilities::{as_void_ptr, voidp_to_ref, ArcUnsafeCell, BleUuid, WeakUnsafeCell},
BLEAttribute, BLEError, BLERemoteCharacteristic, Signal,
};
use alloc::vec::Vec;
use core::ffi::c_void;
use esp_idf_svc::sys as esp_idf_sys;
pub struct BLERemoteServiceState {
client: WeakUnsafeCell<BLEClientState>,
pub(crate) uuid: BleUuid,
start_handle: u16,
pub(crate) end_handle: u16,
pub(crate) characteristics: Option<Vec<BLERemoteCharacteristic>>,
signal: Signal<u32>,
}
impl BLEAttribute for BLERemoteServiceState {
fn get_client(&self) -> Option<ArcUnsafeCell<BLEClientState>> {
self.client.upgrade()
}
}
#[derive(Clone)]
pub struct BLERemoteService {
pub(crate) state: ArcUnsafeCell<BLERemoteServiceState>,
}
impl BLERemoteService {
pub(crate) fn new(
client: WeakUnsafeCell<BLEClientState>,
service: &esp_idf_sys::ble_gatt_svc,
) -> Self {
Self {
state: ArcUnsafeCell::new(BLERemoteServiceState {
client,
uuid: BleUuid::from(service.uuid),
start_handle: service.start_handle,
end_handle: service.end_handle,
characteristics: None,
signal: Signal::new(),
}),
}
}
pub fn uuid(&self) -> BleUuid {
self.state.uuid
}
pub async fn get_characteristics(
&mut self,
) -> Result<core::slice::IterMut<'_, BLERemoteCharacteristic>, BLEError> {
if self.state.characteristics.is_none() {
self.state.characteristics = Some(Vec::new());
unsafe {
ble!(esp_idf_sys::ble_gattc_disc_all_chrs(
self.state.conn_handle(),
self.state.start_handle,
self.state.end_handle,
Some(Self::characteristic_disc_cb),
as_void_ptr(self),
))?;
}
ble!(self.state.signal.wait().await)?;
}
Ok(self.state.characteristics.as_mut().unwrap().iter_mut())
}
pub async fn get_characteristic(
&mut self,
uuid: BleUuid,
) -> Result<&mut BLERemoteCharacteristic, BLEError> {
let mut iter = self.get_characteristics().await?;
iter
.find(|x| x.uuid() == uuid)
.ok_or_else(|| BLEError::fail().unwrap_err())
}
extern "C" fn characteristic_disc_cb(
conn_handle: u16,
error: *const esp_idf_sys::ble_gatt_error,
chr: *const esp_idf_sys::ble_gatt_chr,
arg: *mut c_void,
) -> i32 {
let service = unsafe { voidp_to_ref::<Self>(arg) };
if service.state.conn_handle() != conn_handle {
return 0;
}
let error = unsafe { &*error };
let chr = unsafe { &*chr };
if error.status == 0 {
let chr = BLERemoteCharacteristic::new(ArcUnsafeCell::downgrade(&service.state), chr);
service.state.characteristics.as_mut().unwrap().push(chr);
return 0;
}
service.state.signal.signal(error.status as _);
error.status as _
}
}
impl core::fmt::Display for BLERemoteService {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "BLERemoteService[{}]", self.state.uuid)
}
}
impl core::fmt::Debug for BLERemoteService {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BLERemoteService")
.field("uuid", &self.state.uuid)
.field("start_handle", &self.state.start_handle)
.field("end_handle", &self.state.end_handle)
.field(
"characteristics",
&self
.state
.characteristics
.as_ref()
.map(|chars| chars.iter().map(|c| c.uuid()).collect::<Vec<_>>()),
)
.finish()
}
}