fix: 분석 백그라운드 Thread, 그래프 빈칸 표시, CSV 세션별 분리

성능:
- 측정 콜백 분석(TGC/Otsu/BV)을 백그라운드 Thread로 이동
- UI 업데이트만 handler.post로 메인 스레드 복귀
- 메인 스레드 블로킹으로 인한 BLE 패킷 드롭 방지
- 분석 소요시간 로그 출력 (Analysis took Xms)
- Toast도 handler.post로 감싸서 Thread-safe

Placement:
- 6채널 그래프: 데이터 없는 채널도 "No data" 빈 칸 표시 (항상 6칸)

CSV:
- 일별 파일 → 세션별 파일 (YYYY-MM-DD_HHmmss.csv)
- BLE 연결 시 자동 newSession()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-30 10:04:03 +09:00
parent 297d67b4d7
commit 1a42fe6a07
4 changed files with 82 additions and 66 deletions
@@ -6,19 +6,19 @@ import java.text.SimpleDateFormat
import java.util.*
/**
* ADC 데이터를 일별 CSV 파일로 저장.
* ADC 데이터를 세션별 CSV 파일로 저장.
*
* 파일 위치: Downloads/VesiScan_ADC/YYYY-MM-DD.csv
* 행: scan_id × 6채널 (측정 1회 = 6행)
* 열: scan_id, timestamp, volume_ml, lr_ratio, channel, sample_0~sample_99
* 파일 위치: Downloads/VesiScan_ADC/YYYY-MM-DD_HHmmss.csv
* newSession() 호출 시 새 파일로 전환 (BLE 연결 시 자동, 수동 호출 가능)
*/
object AdcCsvLogger {
private val dayFmt = SimpleDateFormat("yyyy-MM-dd", Locale.US)
private val sessionFmt = SimpleDateFormat("yyyy-MM-dd_HHmmss", Locale.US)
private val timeFmt = SimpleDateFormat("HH:mm:ss.SSS", Locale.US)
var lastScanId: Int = 0
private set
private var scanCounter = 0
private var sessionFile: File? = null
private fun getDir(): File {
val dir = File(
@@ -29,24 +29,24 @@ object AdcCsvLogger {
return dir
}
private fun getTodayFile(): File {
return File(getDir(), "${dayFmt.format(Date())}.csv")
fun newSession() {
scanCounter = 0
lastScanId = 0
sessionFile = File(getDir(), "${sessionFmt.format(Date())}.csv")
}
private fun getFile(): File {
if (sessionFile == null) newSession()
return sessionFile!!
}
/**
* 6채널 ADC 데이터를 CSV에 append.
*
* @param channels 6채널 ADC 버퍼 (List<List<UShort>>)
* @param volumeMl BV 추정 결과 (null이면 측정 실패)
* @param lrRatio lr_ratio 값
*/
fun log(
channels: List<List<UShort>>,
volumeMl: Double? = null,
lrRatio: Double = 1.0
) {
try {
val file = getTodayFile()
val file = getFile()
val isNew = !file.exists() || file.length() == 0L
scanCounter++
lastScanId = scanCounter
@@ -58,7 +58,7 @@ object AdcCsvLogger {
val thrStr = if (analyzer.useAdaptiveThreshold && analyzer.lastOtsuThreshold > 0)
"%.0f".format(analyzer.lastOtsuThreshold) else "%.0f".format(com.example.medilightv2android.managers.GreenZoneConstants.lowEchoAmp)
val dpsStr = "%.3f".format(com.example.medilightv2android.managers.PiezoHW.distancePerSample)
val sgStr = if (com.example.medilightv2android.managers.PiezoEchoAnalyzer.shared.useSgFilter) "ON" else "OFF"
val sgStr = if (analyzer.useSgFilter) "ON" else "OFF"
java.io.FileWriter(file, !isNew).buffered().use { writer ->
if (isNew) {
@@ -76,7 +76,6 @@ object AdcCsvLogger {
for (sample in buffer) {
append(",${sample.toInt()}")
}
// 100개 미만이면 빈 칸으로 채움
for (i in buffer.size until 100) {
append(",")
}
@@ -85,8 +84,6 @@ object AdcCsvLogger {
writer.newLine()
}
}
} catch (_: Exception) {
// 로깅 실패는 앱 crash 방지
}
} catch (_: Exception) {}
}
}