feat: ADC 데이터 일별 CSV 저장 (Downloads/VesiScan_ADC/)
AdcCsvLogger: 측정마다 6채널 ADC를 CSV에 append - 파일: Downloads/VesiScan_ADC/YYYY-MM-DD.csv - 행: scan_id × 6채널 (1회 측정 = 6행) - 열: scan_id, timestamp, volume_ml, lr_ratio, channel, s0~s99 - BV 성공/실패 모두 ADC 저장 (volume_ml 빈칸이면 실패) - Python: pd.read_csv() → df[df.scan_id==1] 으로 1회 측정 추출 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
package com.example.medilightv2android.services
|
||||
|
||||
import android.os.Environment
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
object AdcCsvLogger {
|
||||
|
||||
private val dayFmt = SimpleDateFormat("yyyy-MM-dd", Locale.US)
|
||||
private val timeFmt = SimpleDateFormat("HH:mm:ss.SSS", Locale.US)
|
||||
private var scanCounter = 0
|
||||
|
||||
private fun getDir(): File {
|
||||
val dir = File(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||
"VesiScan_ADC"
|
||||
)
|
||||
if (!dir.exists()) dir.mkdirs()
|
||||
return dir
|
||||
}
|
||||
|
||||
private fun getTodayFile(): File {
|
||||
return File(getDir(), "${dayFmt.format(Date())}.csv")
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 isNew = !file.exists() || file.length() == 0L
|
||||
scanCounter++
|
||||
|
||||
val timestamp = timeFmt.format(Date())
|
||||
val volStr = volumeMl?.let { "%.1f".format(it) } ?: ""
|
||||
val lrStr = "%.2f".format(lrRatio)
|
||||
|
||||
java.io.FileWriter(file, !isNew).buffered().use { writer ->
|
||||
if (isNew) {
|
||||
val header = buildString {
|
||||
append("scan_id,timestamp,volume_ml,lr_ratio,channel")
|
||||
for (i in 0 until 100) append(",s$i")
|
||||
}
|
||||
writer.write(header)
|
||||
writer.newLine()
|
||||
}
|
||||
|
||||
for ((chIdx, buffer) in channels.withIndex()) {
|
||||
val row = buildString {
|
||||
append("$scanCounter,$timestamp,$volStr,$lrStr,CH$chIdx")
|
||||
for (sample in buffer) {
|
||||
append(",${sample.toInt()}")
|
||||
}
|
||||
// 100개 미만이면 빈 칸으로 채움
|
||||
for (i in buffer.size until 100) {
|
||||
append(",")
|
||||
}
|
||||
}
|
||||
writer.write(row)
|
||||
writer.newLine()
|
||||
}
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
// 로깅 실패는 앱 crash 방지
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user