package com.medithings.vesiscan.managers import org.junit.Assume.assumeTrue import org.junit.Test import java.io.File /** * 진단용 — labdb 601 원시 파형을 앱과 **똑같은 판정**([AnchorGuide.measure])에 다시 넣어 * 시도별 nch · CH3 · 검출 trace 수를 찍는다. 요약(`positions`)은 같은 cm 의 **마지막 시도**만 * 남기므로(재측정은 최신값), 첫 측정 때 화면에서 본 CH3 가 실제로 몇 trace 였는지는 이걸로만 * 알 수 있다(2026-10-14, P9 sitting "1·2cm 에서 ch2·ch3 보였다" 질의). * * 입력: 환경변수 `LABDB_CYCLES_DIR` 의 `*.csv` (cycle,channel,s0.. · 헤더 없음 — * scratchpad/labdb_export_cycles.py 가 만든다). 파일명 `…__{cm}cm_{phase}_a{attempt}.csv`. * HW 프리셋은 `LABDB_HW_PRESET`(기본 R300). 환경변수가 없으면 건너뛴다 — CI 와 무관하다. */ class LabdbTraceDumpTest { private fun loadCycles(f: File): List> { val byCycle = sortedMapOf>() f.readLines().filter { it.isNotBlank() }.forEach { line -> val p = line.split(',') byCycle.getOrPut(p[0].toInt()) { sortedMapOf() }[p[1].toInt()] = DoubleArray(p.size - 2) { p[it + 2].toDouble() } } return byCycle.values.map { chans -> chans.toSortedMap().values.toList() } } @Test fun dumpPerAttemptTraces() { val dir = System.getenv("LABDB_CYCLES_DIR")?.let(::File) assumeTrue("LABDB_CYCLES_DIR 없음 — 진단 전용", dir != null && dir.isDirectory) val presetName = System.getenv("LABDB_HW_PRESET") ?: "R300" val saved = PiezoHW.activePreset PiezoHW.activePreset = PiezoHW.DevicePreset.valueOf(presetName) try { val re = Regex("""__(-?\d+)cm_([a-z_]+)_a(\d+)\.csv$""") println("TRACEDUMP preset=$presetName") println("TRACEDUMP file | cycles | traces | nch | ch3Mean | ch3Hit/traces | ch3Rate | capFrac | nchTraceMean") dir!!.listFiles { f -> f.name.endsWith(".csv") }!!.sortedBy { it.name }.forEach { f -> val m = re.find(f.name) ?: return@forEach val cm = m.groupValues[1].toInt() val cycles = loadCycles(f) val rec = AnchorGuide().measure(cycles, cm) println( "TRACEDUMP ${f.name} | ${cycles.size} | ${rec.nTrace} | ${rec.nch} | ${if (rec.ch3Mean) "O" else "X"} | " + "${rec.ch3Hit}/${rec.nTrace} | ${"%.2f".format(rec.ch3Rate)} | ${"%.3f".format(rec.capFrac)} | ${"%.2f".format(rec.nchTrace)}", ) } } finally { PiezoHW.activePreset = saved } } }