From 54bca199af8ddde6bad0d81b78bd8255b9edeffe Mon Sep 17 00:00:00 2001 From: jjangddu Date: Fri, 8 May 2026 11:59:13 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20maa=20=EC=9D=B4=EC=A4=91=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1=20=EB=B0=A9=EC=A7=80=20(BV=5FFAIL=20=EA=B7=BC?= =?UTF-8?q?=EB=B3=B8=20=EC=9B=90=EC=9D=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - isMeasuring=false를 handler.post로 메인 스레드 갱신 (Thread 내 설정 → Compose state 지연 → 다음 measure() 통과 → maa 이중 전송) - measure() 진입 시 isMeasuring 가드 추가 (중복 호출 방어) - 채널 수신 로그 추가 (RECEIVED N channels) - MTU 247 negotiation 추가 (reb: 208B 분할 전송 최소화) - Spot 6ch 필터 추가 (allChannelsReceived) - Auto Stop 시 값 유지 (재계산 안 함) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../medilightv2android/ble/BleManager.kt | 12 +++++++- .../views/monitoring/PiezoMonitoringView.kt | 28 +++++++++---------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/example/medilightv2android/ble/BleManager.kt b/app/src/main/java/com/example/medilightv2android/ble/BleManager.kt index 95103c1..c83267e 100644 --- a/app/src/main/java/com/example/medilightv2android/ble/BleManager.kt +++ b/app/src/main/java/com/example/medilightv2android/ble/BleManager.kt @@ -701,7 +701,11 @@ class BleManager private constructor(private val context: Context) { .apply() } catch (_: Exception) {} - gatt.discoverServices() + // MTU 247 요청 (reb: 208B 패킷 드롭 방지) + // onMtuChanged 콜백에서 discoverServices 호출 + if (!gatt.requestMtu(247)) { + gatt.discoverServices() // MTU 요청 실패 시 바로 진행 + } onConnectionStateChanged?.invoke(true) } BluetoothProfile.STATE_DISCONNECTED -> { @@ -732,6 +736,12 @@ class BleManager private constructor(private val context: Context) { } } + override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) { + logd { "MTU changed: $mtu (status=$status)" } + handler.post { debugLogger.info("MTU=$mtu") } + gatt.discoverServices() + } + override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { logd { "onServicesDiscovered: status=$status" } if (status != BluetoothGatt.GATT_SUCCESS) { diff --git a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt index cc1eb0b..6a5ceaf 100644 --- a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt +++ b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt @@ -106,6 +106,10 @@ fun PiezoMonitoringView(appState: AppState) { } fun measure() { + if (isMeasuring) { + Log.w("PiezoMonitor", "measure() called while already measuring — SKIPPED") + return + } isMeasuring = true measureDoneSignal = kotlinx.coroutines.CompletableDeferred() @@ -122,6 +126,7 @@ fun PiezoMonitoringView(appState: AppState) { Log.d("PiezoMonitor", "Sending measurement command...") bleManager.piezoCollector.onMultiChannelComplete = { channels -> + Log.d("PiezoMonitor", "RECEIVED ${channels.size} channels: ${channels.map { "CH${it.channel}(${it.buffer.size})" }}") // 분석을 백그라운드에서 수행 후 UI 업데이트만 메인으로 Thread { val analysisStart = System.currentTimeMillis() @@ -311,7 +316,7 @@ fun PiezoMonitoringView(appState: AppState) { Log.d("PiezoMonitor", "AUTO trimmedMean=${"%.1f".format(displayVol)}ml from ${autoVolumeWindow.map { "%.0f".format(it) }}") } } else if (isSpotInProgress) { - if (finalRawVol > 0) spotVolumes.add(finalRawVol) + if (finalRawVol > 0 && allChannelsReceived) spotVolumes.add(finalRawVol) } else { lastVolumeMl = finalRawVol maxVolumeMl = maxOf(maxVolumeMl, finalRawVol) @@ -324,9 +329,11 @@ fun PiezoMonitoringView(appState: AppState) { val level = (pct * BladderLevel.MAX_LEVEL).toInt().coerceIn(1, BladderLevel.MAX_LEVEL) appState.bladderLevel = appState.bladderLevel.copy(currentLevel = level) } - isMeasuring = false - measureRetryCount = 0 - try { measureDoneSignal.complete(Unit) } catch (_: Exception) {} + handler.post { + isMeasuring = false + measureRetryCount = 0 + try { measureDoneSignal.complete(Unit) } catch (_: Exception) {} + } } }.start() } @@ -379,17 +386,8 @@ fun PiezoMonitoringView(appState: AppState) { fun stopAutoMeasure() { isAutoMeasuring = false autoTickCount = 0 - // 종료 시 최종 trimmed mean 반영 - if (autoVolumeWindow.size >= 3) { - val finalVol = trimmedMean(autoVolumeWindow.toList()) - lastVolumeMl = finalVol - maxVolumeMl = maxOf(maxVolumeMl, finalVol) - val maxVol = appState.piezoSettings.maxVolume.toDouble() - val pct = if (maxVol > 0) (finalVol / maxVol).coerceAtMost(1.0) else 0.0 - val level = (pct * BladderLevel.MAX_LEVEL).toInt().coerceIn(1, BladderLevel.MAX_LEVEL) - appState.bladderLevel = appState.bladderLevel.copy(currentLevel = level) - Log.d("PiezoMonitor", "AUTO STOP finalTrimmedMean=${"%.1f".format(finalVol)}ml from ${autoVolumeWindow.size} samples") - } + // Stop 시 현재 표시값 유지 (재계산 안 함) + Log.d("PiezoMonitor", "AUTO STOP — keeping displayed vol=${"%.1f".format(lastVolumeMl)}ml") autoVolumeWindow.clear() }