diff --git a/app/src/main/java/com/example/medilightv2android/models/ClinicalSession.kt b/app/src/main/java/com/example/medilightv2android/models/ClinicalSession.kt index 5b1a2e7..f8d84c7 100644 --- a/app/src/main/java/com/example/medilightv2android/models/ClinicalSession.kt +++ b/app/src/main/java/com/example/medilightv2android/models/ClinicalSession.kt @@ -24,6 +24,11 @@ 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") +} + /** * 측정 단계. * - ALIGN_0CM ~ ALIGN_4CM: 정렬모드 알고리즘 개발용 (기기 아래 끝 기준 치골 위 0~4cm) @@ -44,8 +49,8 @@ data class ClinicalSession( val step: ClinicalStep, val examiner: String = "", val notes: String = "", - // 임상 시험 정보 (vesiscan_test Section 2-1 프로토콜과 매칭) - val subjectId: String = "", // 피험자 ID (예: "S001") + // 측정 대상 분류 (회의 결정: Phantom/Human/ETC 버튼 — subject ID 자유 입력 대체) + val subjectType: SubjectType = SubjectType.PHANTOM, val trueVolumeMl: Double? = null, // 외부 초음파 측정기 참값 (mL) val abdomenThicknessMm: Double? = null, // 복부 두께 측정기 참값 (mm) val startedAt: Long = System.currentTimeMillis(), @@ -53,13 +58,9 @@ data class ClinicalSession( ) { private val folderFmt = SimpleDateFormat("yyyy-MM-dd_HHmmss", Locale.US) - /** "S001_VBT26050202_SUPINE_ALIGN_0CM_2026-05-28_14-30-15" - * subjectId 비어있으면 기존 형식 유지 (deviceName_posture_step_timestamp). */ + /** "PHANTOM_VBT26050202_SUPINE_ALIGN_0CM_2026-05-28_14-30-15" */ val folderName: String - get() = buildString { - if (subjectId.isNotBlank()) { append(subjectId); append('_') } - append("${deviceName}_${posture.name}_${step.name}_${folderFmt.format(Date(startedAt))}") - } + get() = "${subjectType.name}_${deviceName}_${posture.name}_${step.name}_${folderFmt.format(Date(startedAt))}" /** clinical_label 컬럼/필드 값 (라벨링 자동) */ val label: String diff --git a/app/src/main/java/com/example/medilightv2android/services/ClinicalSessionStore.kt b/app/src/main/java/com/example/medilightv2android/services/ClinicalSessionStore.kt index 4f3dbcf..4c56882 100644 --- a/app/src/main/java/com/example/medilightv2android/services/ClinicalSessionStore.kt +++ b/app/src/main/java/com/example/medilightv2android/services/ClinicalSessionStore.kt @@ -51,7 +51,8 @@ object ClinicalSessionStore { /** 다음 측정 시 기본값으로 자동 채울 사용자 입력 — 같은 피험자/검사자 여러 측정 시 편의 */ var lastExaminer: String = "" var lastNotes: String = "" - var lastSubjectId: String = "" + var lastSubjectType: com.example.medilightv2android.models.SubjectType = + com.example.medilightv2android.models.SubjectType.PHANTOM var lastTrueVolumeMl: String = "" // 문자열로 보관 (입력 폼과 동일 형식) var lastAbdomenThicknessMm: String = "" @@ -62,7 +63,7 @@ object ClinicalSessionStore { currentSession = session lastExaminer = session.examiner lastNotes = session.notes - lastSubjectId = session.subjectId + lastSubjectType = session.subjectType lastTrueVolumeMl = session.trueVolumeMl?.toString() ?: "" lastAbdomenThicknessMm = session.abdomenThicknessMm?.toString() ?: "" cyclesBuffer.clear() @@ -107,8 +108,9 @@ object ClinicalSessionStore { put("examiner", s.examiner) put("notes", s.notes) put("label", s.label) + // 측정 대상 분류 (회의 결정: Phantom/Human/ETC) + put("subject_type", s.subjectType.name) // 임상 시험 참값 (외부 측정기, vesiscan_test 프로토콜) - if (s.subjectId.isNotBlank()) put("subject_id", s.subjectId) s.trueVolumeMl?.let { put("true_volume_ml", it) } s.abdomenThicknessMm?.let { put("abdomen_thickness_mm", it) } put("started_at", s.startedAt) @@ -179,7 +181,7 @@ object ClinicalSessionStore { // meta: writeMeta()와 동일 내용 포함 (단독 파일로 자족 가능) val meta = JSONObject().apply { put("data_type", DATA_TYPE) - put("subject_id", s.subjectId) + put("subject_type", s.subjectType.name) put("device_name", s.deviceName) put("posture", s.posture.name) put("step", s.step.name) diff --git a/app/src/main/java/com/example/medilightv2android/services/labdb/LabdbUploader.kt b/app/src/main/java/com/example/medilightv2android/services/labdb/LabdbUploader.kt index d5d4d67..45c239f 100644 --- a/app/src/main/java/com/example/medilightv2android/services/labdb/LabdbUploader.kt +++ b/app/src/main/java/com/example/medilightv2android/services/labdb/LabdbUploader.kt @@ -89,7 +89,7 @@ object LabdbUploader { put("step", meta.optString("step")) put("is_alignment", meta.optBoolean("is_alignment")) put("examiner", meta.optString("examiner")) - meta.optString("subject_id").takeIf { it.isNotBlank() }?.let { put("subject_id", it) } + meta.optString("subject_type").takeIf { it.isNotBlank() }?.let { put("subject_type", it) } if (meta.has("true_volume_ml")) put("true_volume_ml", meta.optDouble("true_volume_ml")) if (meta.has("abdomen_thickness_mm")) put("abdomen_thickness_mm", meta.optDouble("abdomen_thickness_mm")) meta.optString("firmware_version").takeIf { it.isNotBlank() }?.let { put("firmware_version", it) } diff --git a/app/src/main/java/com/example/medilightv2android/ui/views/clinical/ClinicalHomeView.kt b/app/src/main/java/com/example/medilightv2android/ui/views/clinical/ClinicalHomeView.kt index 976641b..7fb673b 100644 --- a/app/src/main/java/com/example/medilightv2android/ui/views/clinical/ClinicalHomeView.kt +++ b/app/src/main/java/com/example/medilightv2android/ui/views/clinical/ClinicalHomeView.kt @@ -63,8 +63,8 @@ fun ClinicalHomeView(appState: AppState) { var step by remember { mutableStateOf(ClinicalStep.ALIGN_0CM) } var examiner by remember { mutableStateOf(ClinicalSessionStore.lastExaminer) } var notes by remember { mutableStateOf(ClinicalSessionStore.lastNotes) } - // 임상 시험 정보 (vesiscan_test Section 2-1 프로토콜) - var subjectId by remember { mutableStateOf(ClinicalSessionStore.lastSubjectId) } + // 측정 대상 분류 (회의 결정: Phantom/Human/ETC 버튼) + var subjectType by remember { mutableStateOf(ClinicalSessionStore.lastSubjectType) } var trueVolText by remember { mutableStateOf(ClinicalSessionStore.lastTrueVolumeMl) } var abdomenText by remember { mutableStateOf(ClinicalSessionStore.lastAbdomenThicknessMm) } @@ -348,15 +348,14 @@ fun ClinicalHomeView(appState: AppState) { ) Spacer(modifier = Modifier.height(12.dp)) - // 임상 시험 참값 — vesiscan_test Section 2-1 프로토콜과 매칭 - OutlinedTextField( - value = subjectId, - onValueChange = { subjectId = it }, - label = { Text("Subject ID (예: S001)") }, - singleLine = true, - modifier = Modifier.fillMaxWidth() - ) - Spacer(modifier = Modifier.height(8.dp)) + // 측정 대상 분류 — Phantom/Human/ETC 버튼 (회의 결정) + LabeledSegmented( + label = "Subject Type", + options = com.example.medilightv2android.models.SubjectType.values().toList(), + selected = subjectType, + onSelect = { subjectType = it } + ) { it.label } + Spacer(modifier = Modifier.height(12.dp)) Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) { OutlinedTextField( value = trueVolText, @@ -419,7 +418,7 @@ fun ClinicalHomeView(appState: AppState) { step = step, examiner = examiner.trim(), notes = notes.trim(), - subjectId = subjectId.trim(), + subjectType = subjectType, trueVolumeMl = trueVolText.toDoubleOrNull(), abdomenThicknessMm = abdomenText.toDoubleOrNull(), ) diff --git a/app/src/main/java/com/example/medilightv2android/ui/views/clinical/ClinicalLiveView.kt b/app/src/main/java/com/example/medilightv2android/ui/views/clinical/ClinicalLiveView.kt index 7061a40..ee6d482 100644 --- a/app/src/main/java/com/example/medilightv2android/ui/views/clinical/ClinicalLiveView.kt +++ b/app/src/main/java/com/example/medilightv2android/ui/views/clinical/ClinicalLiveView.kt @@ -161,7 +161,7 @@ fun ClinicalLiveView(appState: AppState) { Column(modifier = Modifier.weight(1f)) { Text("Live · ${session.label}", fontSize = 14.sp, fontWeight = FontWeight.Bold) - val subj = if (session.subjectId.isNotBlank()) "S=${session.subjectId} · " else "" + val subj = "${session.subjectType.label} · " val tv = session.trueVolumeMl?.let { "TV=${it.toInt()}mL · " } ?: "" val ab = session.abdomenThicknessMm?.let { "Ab=${"%.0f".format(it)}mm" } ?: "" Text("$subj$tv$ab",