package com.example.medilightv2android.models import java.text.SimpleDateFormat import java.util.Date import java.util.Locale /** * 임상 R&D 측정 — "한 측정 단위" 모델. * * 한 ClinicalSession = 한 라벨 = 한 폴더 = 한 측정 사이클. * 사용자가 BLE 연결 후 매번 자세/위치를 입력하고 측정 진행 → Back 시 폴더 finalize. * * 디바이스 식별자는 BLE 연결된 기기 이름(예: VBT26050202)을 자동으로 사용. * * 데이터 구조 (한 측정당): * Downloads/VesiScan_Sessions/{folderName}/ * ├─ meta.json * ├─ adc.csv * ├─ placements.jsonl (alignment) or events.jsonl (BV) * └─ ble.log */ enum class ClinicalPosture(val label: String) { SUPINE("Supine"), SITTING("Sitting"), STANDING("Standing") } /** 측정 대상 분류 — 회의 결정 (subject ID 자유 입력 대체). */ enum class SubjectType(val label: String) { PHANTOM("Phantom"), HUMAN("Human"), ETC("ETC") } /** * 측정 단계 — 회의 결정으로 BV 제거. 정렬모드 알고리즘 개발용 5단계만. * - ALIGN_0CM ~ ALIGN_4CM: 기기 아래 끝 기준 치골 위 0~4cm */ enum class ClinicalStep(val label: String, val description: String, val isAlignment: Boolean) { ALIGN_0CM("0 cm", "기기 아래 끝이 치골 바로 위", true), ALIGN_1CM("1 cm", "치골 위로 1 cm", true), ALIGN_2CM("2 cm", "치골 위로 2 cm", true), ALIGN_3CM("3 cm", "치골 위로 3 cm", true), ALIGN_4CM("4 cm", "치골 위로 4 cm", true), } /** * 한 ClinicalSession 의 데이터 수집 모드. * - ALIGNMENT : PlacementGuide(Sensor Alignment) 에서 V1 advisor 프레임 + 라벨 누적 * → labdb 업로드 (dataType="002") * - MEASUREMENT: ClinicalLive 에서 6ch 실시간 측정 누적 (기존 동작) * → labdb 업로드 (dataType="001") */ enum class SessionMode { ALIGNMENT, MEASUREMENT } data class ClinicalSession( val deviceName: String, // BLE 기기 이름 (예: "VBT26050202") val posture: ClinicalPosture, val step: ClinicalStep, val examiner: String = "", val notes: String = "", val subjectType: SubjectType = SubjectType.PHANTOM, val subject: String = "", val trueVolumeMl: Double? = null, // 외부 초음파 측정기 참값 (mL) val abdomenThicknessMm: Double? = null, // 복부 두께 측정기 참값 (mm) val mode: SessionMode = SessionMode.MEASUREMENT, // 신규 val alignmentAlgo: String = "", // demo-final: V1 only val startedAt: Long = System.currentTimeMillis(), var endedAt: Long? = null ) { private val folderFmt = SimpleDateFormat("yyyy-MM-dd_HHmmss", Locale.US) /** mode == ALIGNMENT 시 "_ALIGNMODE_" 접미사 (Live 6ch 세션과 폴더 분리). */ val folderName: String get() = buildString { append(subjectType.name) if (subject.isNotBlank()) { append('-') append(subject.replace(Regex("[^A-Za-z0-9_-]"), "-")) } append('_') append(deviceName); append('_') append(posture.name); append('_') append(step.name); append('_') if (mode == SessionMode.ALIGNMENT) append("ALIGNMODE_") append(folderFmt.format(Date(startedAt))) } /** clinical_label 컬럼/필드 값 (라벨링 자동) */ val label: String get() = "${deviceName}_${posture.name}_${step.name}" }