From 253312b4b003a2b361e11b88937752ad4edefb79 Mon Sep 17 00:00:00 2001 From: jjangddu Date: Tue, 4 Aug 2026 16:18:07 +0900 Subject: [PATCH] =?UTF-8?q?fix(logs):=20=C2=A7C+E=20=C2=B7=20purge=20dead?= =?UTF-8?q?=20code=20=ED=99=9C=EC=84=B1=ED=99=94=20+=20record()=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9=20=EA=B4=80=EB=AC=B8=20(cloud-mvp=20d8425d0?= =?UTF-8?q?=20=EC=9D=B4=EC=8B=9D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/medithings/vesiscan/MainActivity.kt | 12 +++++ .../vesiscan/services/AdcCsvLogger.kt | 17 +++++++ .../vesiscan/services/ClinicalSessionStore.kt | 19 ++++++++ .../services/MeasurementLogService.kt | 47 +++++++++++++++++++ 4 files changed, 95 insertions(+) diff --git a/app/src/main/java/com/medithings/vesiscan/MainActivity.kt b/app/src/main/java/com/medithings/vesiscan/MainActivity.kt index 7e1f825..7923dc7 100644 --- a/app/src/main/java/com/medithings/vesiscan/MainActivity.kt +++ b/app/src/main/java/com/medithings/vesiscan/MainActivity.kt @@ -63,6 +63,18 @@ class MainActivity : ComponentActivity() { com.medithings.vesiscan.telemetry.AlarmAggregator.start() com.medithings.vesiscan.telemetry.NotificationSubscriber.start(this) + // 2026-08-04: LOG_STORAGE_ANALYSIS §C fix — 앱 시작 시 오래된 로그 자동 정리. + // 실패해도 필수기능 영향 없음 (silent · UC-05). + try { + val app = applicationContext + com.medithings.vesiscan.services.MeasurementLogService.getInstance(app) + .purgeOldLogs(keepDays = 365) + com.medithings.vesiscan.services.BleDebugLogger.getInstance(app) + .purgeOldLogs(keepDays = 7) + com.medithings.vesiscan.services.AdcCsvLogger.purgeOldSessions(keepDays = 30) + com.medithings.vesiscan.services.ClinicalSessionStore.purgeOldSessions(keepDays = 90) + } catch (_: Exception) { } + enableEdgeToEdge() window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) setContent { diff --git a/app/src/main/java/com/medithings/vesiscan/services/AdcCsvLogger.kt b/app/src/main/java/com/medithings/vesiscan/services/AdcCsvLogger.kt index 37e39fb..4889034 100644 --- a/app/src/main/java/com/medithings/vesiscan/services/AdcCsvLogger.kt +++ b/app/src/main/java/com/medithings/vesiscan/services/AdcCsvLogger.kt @@ -121,4 +121,21 @@ object AdcCsvLogger { } } catch (_: Exception) {} } + + /** + * VesiScan_ADC/ 폴더의 keepDays 일 이전 CSV 삭제 (§C purge). + * 2026-08-04: LOG_STORAGE_ANALYSIS Priority 3 — 시간당 40MB 누적 방지. + */ + fun purgeOldSessions(keepDays: Int = 30) { + try { + val calendar = Calendar.getInstance() + calendar.add(Calendar.DAY_OF_YEAR, -keepDays) + val cutoff = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(calendar.time) + val dir = getDir() + dir.listFiles { f -> f.extension == "csv" }?.forEach { file -> + val prefix = file.nameWithoutExtension.take(10) + if (prefix < cutoff) file.delete() + } + } catch (_: Exception) { } + } } diff --git a/app/src/main/java/com/medithings/vesiscan/services/ClinicalSessionStore.kt b/app/src/main/java/com/medithings/vesiscan/services/ClinicalSessionStore.kt index e5cbf4a..15d7e52 100644 --- a/app/src/main/java/com/medithings/vesiscan/services/ClinicalSessionStore.kt +++ b/app/src/main/java/com/medithings/vesiscan/services/ClinicalSessionStore.kt @@ -384,4 +384,23 @@ object ClinicalSessionStore { lastFinalizedFolder = null return dir } + + /** + * VesiScan_Sessions/ 폴더의 keepDays 일 이전 세션 삭제 (§C purge). + * 2026-08-04: LOG_STORAGE_ANALYSIS Priority 3 — 시간당 최대 40MB 누적 방지. + * lastModified 기준. currentSession 진행 중 폴더 제외. + */ + fun purgeOldSessions(keepDays: Int = 90) { + runCatching { + val cutoffMs = System.currentTimeMillis() - keepDays * 24L * 3600L * 1000L + val activeFolder = currentLogDir() + val downloads = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + val root = File(downloads, "VesiScan_Sessions") + if (!root.exists() || !root.isDirectory) return@runCatching + root.listFiles { f -> f.isDirectory }?.forEach { folder -> + if (folder == activeFolder) return@forEach + if (folder.lastModified() < cutoffMs) folder.deleteRecursively() + } + } + } } diff --git a/app/src/main/java/com/medithings/vesiscan/services/MeasurementLogService.kt b/app/src/main/java/com/medithings/vesiscan/services/MeasurementLogService.kt index cadc6ad..a6b39ff 100644 --- a/app/src/main/java/com/medithings/vesiscan/services/MeasurementLogService.kt +++ b/app/src/main/java/com/medithings/vesiscan/services/MeasurementLogService.kt @@ -53,6 +53,38 @@ class MeasurementLogService private constructor(private val context: Context) { // -- Log Events -- + /** + * 통합 관문 (2026-08-04 · LOG_STORAGE_ANALYSIS §E fix). + * 호출부는 이 함수 하나만 호출하면 4개 sink 자동 fan-out. + * 기존 개별 함수는 그대로 유지 (backwards compat). + */ + fun record(outcome: MeasurementOutcome): Int { + val scanId = outcome.scanId + ?: com.medithings.vesiscan.services.AdcCsvLogger.nextScanId() + runCatching { + com.medithings.vesiscan.ble.BleManager.current?.debugLogger?.measurementResult( + volumeMl = outcome.volumeMl, + lrRatio = outcome.lrRatio, + validChannels = outcome.validChannels, + urineLines = outcome.urineLines, + otsuThreshold = outcome.otsuThreshold, + scanId = scanId, + ) + } + runCatching { + logMeasurement(outcome.volumeMl, outcome.channels, outcome.rawADC, scanId = scanId) + } + runCatching { + com.medithings.vesiscan.services.AdcCsvLogger.log( + outcome.rawADC, + outcome.volumeMl, + outcome.lrRatio, + scanId = scanId, + ) + } + return scanId + } + /** * Log a spot measurement result. */ @@ -247,3 +279,18 @@ data class ChannelLogEntry( val urineLen: Int? = null, val score: Double? = null ) + +/** + * 단일 측정 사이클의 모든 sink 에 fan-out 될 결과 (2026-08-04 · §E 통합 관문 입력). + * scanId = null 이면 record() 가 자동 발급. + */ +data class MeasurementOutcome( + val volumeMl: Double?, + val lrRatio: Double = 1.0, + val rawADC: List>, + val channels: List, + val validChannels: Int = 0, + val urineLines: List = emptyList(), + val otsuThreshold: Double = 0.0, + val scanId: Int? = null, +)