diff --git a/app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt b/app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt index 43f57ec..90cbaca 100644 --- a/app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt +++ b/app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt @@ -75,7 +75,11 @@ class BleManager private constructor(private val context: Context) { private var reconnectTimer: Runnable? = null @Volatile private var lastRxTimestamp: Long = 0 private var watchdogTimer: Runnable? = null - private val watchdogTimeoutMs: Long = 15000 + // 2026-07-07: 15 → 25초. 실측 로그 (2026-07-07 10:43~10:44) 에서 재연결 후 첫 RX 가 + // 14초 지연 후 도착하는 케이스 확인. Peripheral / OS BLE 스택의 좀비 회복 시간을 + // 허용하기 위해 timeout 여유 확보. 15초로는 회복 직전에 forced reconnect 발생 → + // 무한 재연결 루프. 25초면 대부분 회복 케이스 커버 가능. + private val watchdogTimeoutMs: Long = 25000 // 2026-07-07 fix: disconnect 시 취소 위해 Runnable 참조 유지. // fwFallback: onDescriptorWrite 성공 후 4초 mfv? fallback. // cccdRetry: onDescriptorWrite 실패 후 500ms retry. diff --git a/app/src/main/java/com/medithings/vesiscan/ui/views/monitoring/PiezoMonitoringView.kt b/app/src/main/java/com/medithings/vesiscan/ui/views/monitoring/PiezoMonitoringView.kt index ed9d9fc..2c3fd03 100644 --- a/app/src/main/java/com/medithings/vesiscan/ui/views/monitoring/PiezoMonitoringView.kt +++ b/app/src/main/java/com/medithings/vesiscan/ui/views/monitoring/PiezoMonitoringView.kt @@ -738,14 +738,20 @@ fun PiezoMonitoringView(appState: AppState) { // 실패해도 UI 알림 없음 — BleManager가 다음 주기에 또 재시도하거나 idle. } bleManager.onConnectionStateChanged = { connected -> + // 2026-07-07 fix: startBatteryPolling 을 여기서 호출하지 않음. + // 이전 fix 로 onConnectionStateChanged(true) 가 CCCD 완료 시점 (=BleManager + // 자체가 line 1059 에서 startBatteryPolling 실행하는 시점) 으로 이동했음. + // 콜백에서 또 호출하면 첫 msn battery TX 가 2번 나가는 duplicate 발생. + // BleManager 가 항상 담당하므로 여기서는 아무것도 안 함. (또는 UI 관련 side effect + // 만 처리하도록 향후 확장.) if (connected) { - bleManager.startBatteryPolling() + // 필요 시 UI 관련 반응만. Battery polling / TX 는 BleManager 소관. } } // 진입 시 이미 연결되어 있으면 한 번 polling 재시작 (안전망). - // 이전 화면(Clinical Unpair → 새 연결 등)에서 batteryLevel=0으로 reset된 채로 // 도넛차트 진입 시 onDescriptorWrite/onConnectionStateChanged 둘 다 이미 끝나 있어 - // 콜백이 트리거 안 됨. 진입 시점에 강제 1회 polling으로 보강. + // 콜백이 트리거 안 됨. 진입 시점에 강제 1회 polling으로 보강 — 이건 duplicate 아님 + // (BleManager 는 이미 polling 중이지만 stopBatteryPolling→restart 로 idempotent). if (bleManager.isConnected.value) { bleManager.startBatteryPolling() }