fix: maa 이중 전송 방지 (BV_FAIL 근본 원인)
- 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) <noreply@anthropic.com>
This commit is contained in:
@@ -701,7 +701,11 @@ class BleManager private constructor(private val context: Context) {
|
|||||||
.apply()
|
.apply()
|
||||||
} catch (_: Exception) {}
|
} catch (_: Exception) {}
|
||||||
|
|
||||||
gatt.discoverServices()
|
// MTU 247 요청 (reb: 208B 패킷 드롭 방지)
|
||||||
|
// onMtuChanged 콜백에서 discoverServices 호출
|
||||||
|
if (!gatt.requestMtu(247)) {
|
||||||
|
gatt.discoverServices() // MTU 요청 실패 시 바로 진행
|
||||||
|
}
|
||||||
onConnectionStateChanged?.invoke(true)
|
onConnectionStateChanged?.invoke(true)
|
||||||
}
|
}
|
||||||
BluetoothProfile.STATE_DISCONNECTED -> {
|
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) {
|
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
|
||||||
logd { "onServicesDiscovered: status=$status" }
|
logd { "onServicesDiscovered: status=$status" }
|
||||||
if (status != BluetoothGatt.GATT_SUCCESS) {
|
if (status != BluetoothGatt.GATT_SUCCESS) {
|
||||||
|
|||||||
+13
-15
@@ -106,6 +106,10 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun measure() {
|
fun measure() {
|
||||||
|
if (isMeasuring) {
|
||||||
|
Log.w("PiezoMonitor", "measure() called while already measuring — SKIPPED")
|
||||||
|
return
|
||||||
|
}
|
||||||
isMeasuring = true
|
isMeasuring = true
|
||||||
measureDoneSignal = kotlinx.coroutines.CompletableDeferred()
|
measureDoneSignal = kotlinx.coroutines.CompletableDeferred()
|
||||||
|
|
||||||
@@ -122,6 +126,7 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
Log.d("PiezoMonitor", "Sending measurement command...")
|
Log.d("PiezoMonitor", "Sending measurement command...")
|
||||||
|
|
||||||
bleManager.piezoCollector.onMultiChannelComplete = { channels ->
|
bleManager.piezoCollector.onMultiChannelComplete = { channels ->
|
||||||
|
Log.d("PiezoMonitor", "RECEIVED ${channels.size} channels: ${channels.map { "CH${it.channel}(${it.buffer.size})" }}")
|
||||||
// 분석을 백그라운드에서 수행 후 UI 업데이트만 메인으로
|
// 분석을 백그라운드에서 수행 후 UI 업데이트만 메인으로
|
||||||
Thread {
|
Thread {
|
||||||
val analysisStart = System.currentTimeMillis()
|
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) }}")
|
Log.d("PiezoMonitor", "AUTO trimmedMean=${"%.1f".format(displayVol)}ml from ${autoVolumeWindow.map { "%.0f".format(it) }}")
|
||||||
}
|
}
|
||||||
} else if (isSpotInProgress) {
|
} else if (isSpotInProgress) {
|
||||||
if (finalRawVol > 0) spotVolumes.add(finalRawVol)
|
if (finalRawVol > 0 && allChannelsReceived) spotVolumes.add(finalRawVol)
|
||||||
} else {
|
} else {
|
||||||
lastVolumeMl = finalRawVol
|
lastVolumeMl = finalRawVol
|
||||||
maxVolumeMl = maxOf(maxVolumeMl, 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)
|
val level = (pct * BladderLevel.MAX_LEVEL).toInt().coerceIn(1, BladderLevel.MAX_LEVEL)
|
||||||
appState.bladderLevel = appState.bladderLevel.copy(currentLevel = level)
|
appState.bladderLevel = appState.bladderLevel.copy(currentLevel = level)
|
||||||
}
|
}
|
||||||
isMeasuring = false
|
handler.post {
|
||||||
measureRetryCount = 0
|
isMeasuring = false
|
||||||
try { measureDoneSignal.complete(Unit) } catch (_: Exception) {}
|
measureRetryCount = 0
|
||||||
|
try { measureDoneSignal.complete(Unit) } catch (_: Exception) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
@@ -379,17 +386,8 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
fun stopAutoMeasure() {
|
fun stopAutoMeasure() {
|
||||||
isAutoMeasuring = false
|
isAutoMeasuring = false
|
||||||
autoTickCount = 0
|
autoTickCount = 0
|
||||||
// 종료 시 최종 trimmed mean 반영
|
// Stop 시 현재 표시값 유지 (재계산 안 함)
|
||||||
if (autoVolumeWindow.size >= 3) {
|
Log.d("PiezoMonitor", "AUTO STOP — keeping displayed vol=${"%.1f".format(lastVolumeMl)}ml")
|
||||||
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")
|
|
||||||
}
|
|
||||||
autoVolumeWindow.clear()
|
autoVolumeWindow.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user