From 432127a667211155933d8604fe7d69f49874a337 Mon Sep 17 00:00:00 2001 From: jjangddu Date: Mon, 3 Aug 2026 16:58:06 +0900 Subject: [PATCH] =?UTF-8?q?feat(state):=20Phase=20A=20=C2=B7=20=EC=8B=A4?= =?UTF-8?q?=EC=B8=A1=20=EA=B2=BD=EB=A1=9C=20=EC=95=8C=EB=9E=8C/=EC=9D=B4?= =?UTF-8?q?=EB=A0=A5=20=ED=86=B5=ED=95=A9=20(cloud-mvp=20deb7b17=20?= =?UTF-8?q?=EC=9D=B4=EC=8B=9D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guardian 공유 이전 선행 작업 (GUARDIAN-01 §5 Phase A). 이전 버그: 데모 경로 (incrementLevel) 만 NotificationService · HistoryStore 호출. 실측 (BLE) 경로는 bladderLevel = ...copy(currentLevel = level) 로 직접 대입 → 알람 미발생 · 이력 미저장. 수정: - AppState.updateLevelFromMeasurement(volumeMl, recordHistory) 신규 함수 · bladderLevel 계산·갱신·storage 동기화 · MeasurementRecord 저장 (recordHistory=true 시) · Urgency 변화 시 sendUrgencyNotification (중복 억제는 기존 로직 재사용) - AppState.incrementLevel(): 데모 1단계 증가를 신규 함수로 위임 - PiezoMonitoringView 3곳 (L578/L1345/L1550) 직접 대입 → 신규 함수 호출 · auto-scan 갱신: recordHistory=false (폭탄 방지) · SPOT trimmed 실측: recordHistory=true · dev-mode tap: recordHistory=true Co-Authored-By: Claude Opus 4.7 --- .../java/com/medithings/vesiscan/AppState.kt | 46 ++++++++++++++----- .../views/monitoring/PiezoMonitoringView.kt | 18 +++----- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/medithings/vesiscan/AppState.kt b/app/src/main/java/com/medithings/vesiscan/AppState.kt index ef5fcbe..63cd97f 100644 --- a/app/src/main/java/com/medithings/vesiscan/AppState.kt +++ b/app/src/main/java/com/medithings/vesiscan/AppState.kt @@ -203,24 +203,46 @@ class AppState(private val context: Context) { } fun incrementLevel() { + // 데모 · 1단계 증가 — 신규 함수로 위임 (cloud-mvp deb7b17 이식). + val currentVolume = estimatedVolume + val stepVolume = piezoSettings.maxVolume.toDouble() / BladderLevel.MAX_LEVEL + updateLevelFromMeasurement(currentVolume + stepVolume, recordHistory = true) + } + + /** + * Phase A · 실측 경로 통합 (GUARDIAN-01, 2026-08-03 · cloud-mvp deb7b17 이식). + * + * 이전: PiezoMonitoringView 3곳 bladderLevel 직접 대입 → + * NotificationService · MeasurementHistoryStore 우회 → 실사용 시 알람 미발생. + * 이제: 실측/데모 경로 모두 이 함수로 일원화. 알람 · 이력 자동. + * + * @param volumeMl 실측 부피 (mL) + * @param recordHistory 이력 저장 여부 (기본 true · auto-scan 갱신 시 false 로 폭탄 방지) + */ + fun updateLevelFromMeasurement(volumeMl: Double, recordHistory: Boolean = true) { val previousUrgency = bladderLevel.urgency - val newLevel = bladderLevel.copy() - newLevel.increment() - bladderLevel = newLevel + + val maxVol = piezoSettings.maxVolume.toDouble() + val pct = if (maxVol > 0) (volumeMl / maxVol).coerceAtMost(1.0) else 0.0 + val level = (pct * BladderLevel.MAX_LEVEL).toInt().coerceIn(0, BladderLevel.MAX_LEVEL) + + bladderLevel = bladderLevel.copy(currentLevel = level) storage.bladderLevel = bladderLevel.currentLevel - val record = MeasurementRecord( - eventType = MeasurementEventType.MEASUREMENT, - level = bladderLevel.currentLevel, - estimatedVolume = estimatedVolume, - urgencyLevel = bladderLevel.urgency, - fillPercentage = fillPercentage - ) - historyStore.addRecord(record) + if (recordHistory) { + val record = MeasurementRecord( + eventType = MeasurementEventType.MEASUREMENT, + level = bladderLevel.currentLevel, + estimatedVolume = volumeMl.toInt(), + urgencyLevel = bladderLevel.urgency, + fillPercentage = fillPercentage, + ) + historyStore.addRecord(record) + } val newUrgency = bladderLevel.urgency if (newUrgency != previousUrgency) { - notificationService.sendUrgencyNotification(newUrgency, estimatedVolume) + notificationService.sendUrgencyNotification(newUrgency, volumeMl.toInt()) } } 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 35ee000..f885b92 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 @@ -572,10 +572,8 @@ fun PiezoMonitoringView(appState: AppState) { // lastVolumeMl 기반으로 두면 auto scan trimmed mean이 max보다 낮을 때 색깔만 초록색으로 // 떨어지고 표시값(309)은 그대로 유지되는 불일치 발생. if (maxVolumeMl > 0 && !isSpotInProgress) { - val maxVol = appState.piezoSettings.maxVolume.toDouble() - val pct = if (maxVol > 0) (maxVolumeMl / 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) + // Phase A (GUARDIAN-01 · cloud-mvp deb7b17): 통합 함수 · auto-scan 은 이력 저장 안 함. + appState.updateLevelFromMeasurement(maxVolumeMl, recordHistory = false) } if (autoSucceeded) autoFailCount = 0 @@ -1335,14 +1333,14 @@ fun PiezoMonitoringView(appState: AppState) { .clip(RoundedCornerShape(20.dp)) .background(tint.copy(alpha = 0.15f)) .then(if (appState.isDevMode) Modifier.clickable { + // Dev-mode 강제 볼륨 랜덤 (테스트). val maxVol = appState.piezoSettings.maxVolume val dummy = (60..(maxVol - 50).coerceAtLeast(70)).random().toDouble() lastVolumeMl = dummy maxVolumeMl = maxOf(maxVolumeMl, dummy) displayMaxVolumeMl = maxVolumeMl - val pct = (dummy / maxVol.toDouble()).coerceAtMost(1.0) - val lvl = (pct * BladderLevel.MAX_LEVEL).toInt().coerceIn(0, BladderLevel.MAX_LEVEL) - appState.bladderLevel = appState.bladderLevel.copy(currentLevel = lvl) + // Phase A (GUARDIAN-01): 통합 함수 사용 · 알람/이력 발동. + appState.updateLevelFromMeasurement(dummy, recordHistory = true) } else Modifier) .padding(horizontal = 20.dp, vertical = 8.dp) ) { @@ -1544,10 +1542,8 @@ fun PiezoMonitoringView(appState: AppState) { maxVolumeMl = maxOf(maxVolumeMl, trimmedResult) displayMaxVolumeMl = maxVolumeMl // Spot은 즉시 갱신 Log.d("PiezoMonitor", "SPOT trimmedMean=${"%.1f".format(trimmedResult)}ml from ${finalSpotVolumes.map { "%.0f".format(it) }}") - val maxVol = appState.piezoSettings.maxVolume.toDouble() - val pct = if (maxVol > 0) (trimmedResult / 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) + // Phase A (GUARDIAN-01): 실측 SPOT · 이력 저장 + 알람 자동. + appState.updateLevelFromMeasurement(trimmedResult, recordHistory = true) } if (appState.isDevMode) { ScanLogStore.appendScanSummary(