|
|
|
@ -46,15 +46,17 @@ const CONTROL_POLL_INTERVAL: Duration = Duration::from_millis(50);
|
|
|
|
/// Реальный источник данных через btleplug.
|
|
|
|
/// Реальный источник данных через btleplug.
|
|
|
|
pub(crate) struct BleSource {
|
|
|
|
pub(crate) struct BleSource {
|
|
|
|
adapter: Adapter,
|
|
|
|
adapter: Adapter,
|
|
|
|
/// Last control result (set by the notification router, awaited by `request_control`).
|
|
|
|
/// Current cycle's control-result slot. Each `stream()` swaps in a fresh slot so a
|
|
|
|
control: Arc<Mutex<Option<ControlResult>>>,
|
|
|
|
/// still-winding-down router from the previous cycle writes to a discarded slot,
|
|
|
|
|
|
|
|
/// never the one the new cycle's `request_control` polls.
|
|
|
|
|
|
|
|
control: Mutex<Arc<Mutex<Option<ControlResult>>>>,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl BleSource {
|
|
|
|
impl BleSource {
|
|
|
|
pub(crate) fn new(adapter: Adapter) -> Self {
|
|
|
|
pub(crate) fn new(adapter: Adapter) -> Self {
|
|
|
|
Self {
|
|
|
|
Self {
|
|
|
|
adapter,
|
|
|
|
adapter,
|
|
|
|
control: Arc::new(Mutex::new(None)),
|
|
|
|
control: Mutex::new(Arc::new(Mutex::new(None))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -130,7 +132,10 @@ impl SensorSource for BleSource {
|
|
|
|
// This keeps the control-result flow concurrent with the telemetry consumer so that
|
|
|
|
// This keeps the control-result flow concurrent with the telemetry consumer so that
|
|
|
|
// `request_control` can await confirmation while telemetry is still being consumed.
|
|
|
|
// `request_control` can await confirmation while telemetry is still being consumed.
|
|
|
|
let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<Telemetry>();
|
|
|
|
let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<Telemetry>();
|
|
|
|
let control = self.control.clone();
|
|
|
|
// Swap in a fresh control slot for this cycle; the previous cycle's router holds
|
|
|
|
|
|
|
|
// the old slot and can only write there, so it cannot race this cycle's poll.
|
|
|
|
|
|
|
|
let control = Arc::new(Mutex::new(None));
|
|
|
|
|
|
|
|
*self.control.lock().unwrap() = control.clone();
|
|
|
|
let (cancel_tx, cancel_rx) = oneshot::channel::<()>();
|
|
|
|
let (cancel_tx, cancel_rx) = oneshot::channel::<()>();
|
|
|
|
|
|
|
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
tokio::spawn(async move {
|
|
|
|
@ -191,7 +196,8 @@ impl SensorSource for BleSource {
|
|
|
|
.ok_or_else(|| BleError::DeviceNotFound(cp_uuid.to_string()))?;
|
|
|
|
.ok_or_else(|| BleError::DeviceNotFound(cp_uuid.to_string()))?;
|
|
|
|
|
|
|
|
|
|
|
|
// Reset and send Request Control (opcode 0x00).
|
|
|
|
// Reset and send Request Control (opcode 0x00).
|
|
|
|
*self.control.lock().unwrap() = None;
|
|
|
|
let slot = self.control.lock().unwrap().clone();
|
|
|
|
|
|
|
|
*slot.lock().unwrap() = None;
|
|
|
|
let (_, payload) = encode_command(Command::FtmsRequestControl);
|
|
|
|
let (_, payload) = encode_command(Command::FtmsRequestControl);
|
|
|
|
peripheral
|
|
|
|
peripheral
|
|
|
|
.write(&ch, &payload, WriteType::WithoutResponse)
|
|
|
|
.write(&ch, &payload, WriteType::WithoutResponse)
|
|
|
|
@ -199,9 +205,9 @@ impl SensorSource for BleSource {
|
|
|
|
.map_err(BleError::WriteFailed)?;
|
|
|
|
.map_err(BleError::WriteFailed)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Wait for the Control Point Response (routed by the stream's notification task).
|
|
|
|
// Wait for the Control Point Response (routed by the stream's notification task).
|
|
|
|
let result = tokio::time::timeout(CONTROL_CONFIRM_TIMEOUT, async {
|
|
|
|
let result = tokio::time::timeout(CONTROL_CONFIRM_TIMEOUT, async move {
|
|
|
|
loop {
|
|
|
|
loop {
|
|
|
|
let current = *self.control.lock().unwrap();
|
|
|
|
let current = *slot.lock().unwrap();
|
|
|
|
if let Some(r) = current {
|
|
|
|
if let Some(r) = current {
|
|
|
|
return r;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|