From a17c6dac69aa1073fa041a91b2da653da5cf4f66 Mon Sep 17 00:00:00 2001 From: jjangddu Date: Tue, 23 Jun 2026 15:04:06 +0900 Subject: [PATCH] =?UTF-8?q?fix(posture):=20IMU=20polling=20=EC=9D=84=20msp?= =?UTF-8?q?=20=E2=86=92=20mtb=20=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(alignmen?= =?UTF-8?q?t=20=EB=AA=85=EB=A0=B9=20=EC=9A=B0=ED=9A=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 증상: msp 명령이 펌웨어에서 rsp: 응답 안 보내는 듯 — adb logcat 에 rsp/POSTURE 로그 한 줄도 안 보임. msp 자체가 작동 안 함. 수정: msp 대신 mtb (alignment 모드, rim: 응답 보장) 로 polling. PiezoPacketCollector: var skipNextComplete: Boolean = false 추가 raa: complete 시 skipNextComplete 가 true 면 onComplete invoke 건너뛰고 flag reset. 자동 1회 use. PiezoMonitoringView LaunchedEffect: - msp(5초) → mtb(10초) 로 전환 - mtb 전에 piezoCollector.skipNextComplete = true 설정 → user 의 측정 callback 가 posture poll 때문에 spurious fire 되는 거 차단 - sendMtb() throttled 면 (canSendMaa fail) flag rollback - 측정 중 (isMeasuring/isAutoMeasuring/isSpotInProgress) 일 땐 skip → 사용자 측정과 충돌 방지 trade-off: BLE 트래픽 ↑ (10초마다 mtb = piezo 6ch + IMU). msp 보다 무거움. 하지만 msp 미작동이라 다른 선택지 없음. Co-Authored-By: Claude Opus 4.7 --- .../ble/PiezoPacketCollector.kt | 10 +++++++++- .../views/monitoring/PiezoMonitoringView.kt | 20 +++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/example/medilightv2android/ble/PiezoPacketCollector.kt b/app/src/main/java/com/example/medilightv2android/ble/PiezoPacketCollector.kt index d713b32..1f3fe8d 100644 --- a/app/src/main/java/com/example/medilightv2android/ble/PiezoPacketCollector.kt +++ b/app/src/main/java/com/example/medilightv2android/ble/PiezoPacketCollector.kt @@ -43,6 +43,9 @@ class PiezoPacketCollector { // Callbacks var onComplete: ((PiezoEchoResult) -> Unit)? = null var onMultiChannelComplete: ((List) -> Unit)? = null + /** true 면 다음 onMultiChannelComplete 1회 skip (자동 reset). + * posture polling 으로 mtb 보낼 때 측정 callback 가 spurious fire 되는 거 차단용. */ + var skipNextComplete: Boolean = false var onLog: ((String) -> Unit)? = null private var recursionDepth = 0 @@ -228,7 +231,12 @@ class PiezoPacketCollector { else -> { val validChannels = channelResults.filterNotNull() onLog?.invoke("raa: session=$currentSetSession complete — ${validChannels.size} channels") - onMultiChannelComplete?.invoke(validChannels) + if (skipNextComplete) { + skipNextComplete = false + onLog?.invoke("raa: skip onComplete (posture poll)") + } else { + onMultiChannelComplete?.invoke(validChannels) + } } } // 다음 set을 위해 reset (isMultiChannel/onComplete 등 유지) 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 c54118c..b3985c5 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 @@ -591,7 +591,10 @@ fun PiezoMonitoringView(appState: AppState) { } } - // IMU polling + 자세 분류 (Phase 1) — 5초 주기로 msp 명령 → rsp: 응답 받아 분류. + // IMU polling + 자세 분류 (Phase 1) — 10초 주기로 mtb 명령 → rim: 응답 받아 분류. + // msp 가 펌웨어에서 rsp: 응답 안 보내는 듯해 mtb (alignment) 로 우회. + // mtb 는 piezo measurement 도 트리거하므로 piezoCollector.skipNextComplete 로 + // user 의 측정 callback 가 spurious fire 되는 거 차단. LaunchedEffect(isConnected, isDemoMode) { Log.d("ImuDbg", "LaunchedEffect start isConnected=$isConnected isDemoMode=$isDemoMode") if (isDemoMode) { @@ -609,13 +612,18 @@ fun PiezoMonitoringView(appState: AppState) { Log.d("ImuDbg", "imuCollector log: $msg") } while (isActive) { - if (isConnected) { - Log.d("ImuDbg", "→ sendImuQuery (msp)") - bleManager.sendImuQuery() + if (isConnected && !isMeasuring && !isAutoMeasuring && !isSpotInProgress) { + Log.d("ImuDbg", "→ sendMtb (posture poll)") + bleManager.piezoCollector.skipNextComplete = true + val sent = bleManager.sendMtb() + if (!sent) { + bleManager.piezoCollector.skipNextComplete = false // throttled, no response coming + Log.d("ImuDbg", "mtb throttled — skip this cycle") + } } else { - Log.d("ImuDbg", "skip (not connected)") + Log.d("ImuDbg", "skip (connected=$isConnected measuring=$isMeasuring auto=$isAutoMeasuring spot=$isSpotInProgress)") } - kotlinx.coroutines.delay(5000) + kotlinx.coroutines.delay(10000) } }