From 3080cd6b233e84813e2fb58928eb82874bbf930e Mon Sep 17 00:00:00 2001 From: jjangddu Date: Tue, 26 May 2026 12:33:26 +0900 Subject: [PATCH] =?UTF-8?q?CLINICAL:=20Subject=20ID=20/=20Device=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=EC=A0=9C=EA=B1=B0=20=E2=80=94=20BLE=20?= =?UTF-8?q?=EA=B8=B0=EA=B8=B0=20=EC=9D=B4=EB=A6=84=EC=9D=B4=20device=20?= =?UTF-8?q?=EC=8B=9D=EB=B3=84=EC=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사용자 요청: "subject ID 빼고, Device도 BLE 기기 이름따라 기기가 다른거라 따로 설정안해도 될듯." 변경: - ClinicalSession 모델: subjectId 필드 제거, ClinicalDevice enum 제거, device: ClinicalDevice → deviceName: String (BLE 연결된 기기 이름 자동 채움) - folderName: "{deviceName}_{posture}_{step}_{timestamp}" 예: "VBT26050202_SUPINE_ALIGN_0CM_2026-05-28_14-30-15" - label: "{deviceName}_{posture}_{step}" (예: "VBT26050202_SUPINE_ALIGN_0CM") - ClinicalSessionStore: lastSubjectId 제거 (Examiner/Notes만 기억) - ClinicalHomeView: * Subject ID TextField 제거 * Device segmented control 제거 * BLE 연결 카드에 deviceName 그대로 표시 (자동 사용) * Start 버튼 enabled = isConnected 만으로 충분 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../models/ClinicalSession.kt | 18 +++++------ .../services/ClinicalSessionStore.kt | 7 ++--- .../ui/views/clinical/ClinicalHomeView.kt | 31 +++++-------------- 3 files changed, 16 insertions(+), 40 deletions(-) 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 031d46c..eedcd8b 100644 --- a/app/src/main/java/com/example/medilightv2android/models/ClinicalSession.kt +++ b/app/src/main/java/com/example/medilightv2android/models/ClinicalSession.kt @@ -8,8 +8,9 @@ import java.util.Locale * 임상 R&D 측정 — "한 측정 단위" 모델. * * 한 ClinicalSession = 한 라벨 = 한 폴더 = 한 측정 사이클. - * 사용자가 BLE 연결 후 매번 라벨(Subject/Device/Posture/Step)을 입력하고 - * 측정을 진행하면, 측정 종료 시점에 폴더가 finalize됨. + * 사용자가 BLE 연결 후 매번 자세/위치를 입력하고 측정 진행 → Back 시 폴더 finalize. + * + * 디바이스 식별자는 BLE 연결된 기기 이름(예: VBT26050202)을 자동으로 사용. * * 데이터 구조 (한 측정당): * Downloads/VesiScan_Sessions/{folderName}/ @@ -19,10 +20,6 @@ import java.util.Locale * └─ ble.log */ -enum class ClinicalDevice(val label: String) { - A("Device A"), B("Device B"), C("Device C") -} - enum class ClinicalPosture(val label: String) { SUPINE("Supine"), SITTING("Sitting"), STANDING("Standing") } @@ -42,8 +39,7 @@ enum class ClinicalStep(val label: String, val isAlignment: Boolean) { } data class ClinicalSession( - val subjectId: String, // "S001" - val device: ClinicalDevice, + val deviceName: String, // BLE 기기 이름 (예: "VBT26050202") val posture: ClinicalPosture, val step: ClinicalStep, val examiner: String = "", @@ -53,11 +49,11 @@ data class ClinicalSession( ) { private val folderFmt = SimpleDateFormat("yyyy-MM-dd_HHmmss", Locale.US) - /** "S001_A_SUPINE_ALIGN_0CM_2026-05-28_14-30-15" */ + /** "VBT26050202_SUPINE_ALIGN_0CM_2026-05-28_14-30-15" */ val folderName: String - get() = "${subjectId}_${device.name}_${posture.name}_${step.name}_${folderFmt.format(Date(startedAt))}" + get() = "${deviceName}_${posture.name}_${step.name}_${folderFmt.format(Date(startedAt))}" /** clinical_label 컬럼/필드 값 (라벨링 자동) */ val label: String - get() = "${subjectId}_${device.name}_${posture.name}_${step.name}" + get() = "${deviceName}_${posture.name}_${step.name}" } 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 0e28dc8..062c4bc 100644 --- a/app/src/main/java/com/example/medilightv2android/services/ClinicalSessionStore.kt +++ b/app/src/main/java/com/example/medilightv2android/services/ClinicalSessionStore.kt @@ -34,14 +34,12 @@ object ClinicalSessionStore { var lastFinalizedFolder: File? = null private set - /** 다음 측정 시 기본값으로 자동 채울 사용자 입력 (subjectId/examiner/notes) */ - var lastSubjectId: String = "" + /** 다음 측정 시 기본값으로 자동 채울 사용자 입력 (examiner/notes) */ var lastExaminer: String = "" var lastNotes: String = "" fun startMeasurement(context: Context, session: ClinicalSession) { currentSession = session - lastSubjectId = session.subjectId lastExaminer = session.examiner lastNotes = session.notes currentLogDir()?.mkdirs() @@ -63,8 +61,7 @@ object ClinicalSessionStore { val dir = currentLogDir() ?: return dir.mkdirs() val meta = JSONObject().apply { - put("subject_id", s.subjectId) - put("device", s.device.name) + put("device_name", s.deviceName) put("posture", s.posture.name) put("step", s.step.name) put("is_alignment", s.step.isAlignment) 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 46ac9de..f274413 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 @@ -43,6 +43,7 @@ fun ClinicalHomeView(appState: AppState) { val context = LocalContext.current val bleManager = remember { BleManager.getInstance(context) } val isConnected by bleManager.isConnected + val deviceName = bleManager.connectedDeviceName.value // 진입 시 flag set, 화면 떠날 때 unset (DEVICE_SCAN/측정화면 라우팅용) DisposableEffect(Unit) { @@ -54,8 +55,6 @@ fun ClinicalHomeView(appState: AppState) { } // 라벨 입력 state — 마지막 입력값 기억 (Store에서 자동 채움) - var subjectId by remember { mutableStateOf(ClinicalSessionStore.lastSubjectId) } - var device by remember { mutableStateOf(ClinicalDevice.A) } var posture by remember { mutableStateOf(ClinicalPosture.SUPINE) } var step by remember { mutableStateOf(ClinicalStep.ALIGN_0CM) } var examiner by remember { mutableStateOf(ClinicalSessionStore.lastExaminer) } @@ -144,18 +143,6 @@ fun ClinicalHomeView(appState: AppState) { Text("Measurement Label", fontWeight = FontWeight.Bold, fontSize = 16.sp) Spacer(modifier = Modifier.height(8.dp)) - OutlinedTextField( - value = subjectId, - onValueChange = { subjectId = it }, - label = { Text("Subject ID (예: S001)") }, - singleLine = true, - modifier = Modifier.fillMaxWidth() - ) - Spacer(modifier = Modifier.height(12.dp)) - - LabeledSegmented("Device", ClinicalDevice.values().toList(), device, { device = it }) { it.label } - Spacer(modifier = Modifier.height(12.dp)) - LabeledSegmented("Posture", ClinicalPosture.values().toList(), posture, { posture = it }) { it.label } Spacer(modifier = Modifier.height(12.dp)) @@ -180,10 +167,10 @@ fun ClinicalHomeView(appState: AppState) { Spacer(modifier = Modifier.height(20.dp)) - // 라벨 preview - val previewLabel = if (subjectId.isNotBlank()) - "${subjectId}_${device.name}_${posture.name}_${step.name}" - else "(Subject ID 입력 필요)" + // 라벨 preview — BLE 기기 이름이 device 식별자 역할 + val previewLabel = if (isConnected) + "${deviceName}_${posture.name}_${step.name}" + else "(BLE 연결 후 활성화)" Text("Label: $previewLabel", fontSize = 12.sp, color = MlSecondaryText, fontWeight = FontWeight.SemiBold) @@ -195,8 +182,7 @@ fun ClinicalHomeView(appState: AppState) { ClinicalSessionStore.startMeasurement( context, ClinicalSession( - subjectId = subjectId.trim(), - device = device, + deviceName = deviceName, posture = posture, step = step, examiner = examiner.trim(), @@ -206,7 +192,7 @@ fun ClinicalHomeView(appState: AppState) { appState.currentScreen = if (step.isAlignment) AppScreen.PLACEMENT_GUIDE else AppScreen.PIEZO_MONITORING }, - enabled = isConnected && subjectId.isNotBlank(), + enabled = isConnected, modifier = Modifier.fillMaxWidth().height(56.dp), shape = RoundedCornerShape(14.dp), colors = ButtonDefaults.buttonColors(containerColor = MlPrimary) @@ -220,9 +206,6 @@ fun ClinicalHomeView(appState: AppState) { if (!isConnected) { Spacer(modifier = Modifier.height(4.dp)) Text("→ BLE 연결 후 활성화", fontSize = 11.sp, color = Color(0xFFFF9800)) - } else if (subjectId.isBlank()) { - Spacer(modifier = Modifier.height(4.dp)) - Text("→ Subject ID 입력 필요", fontSize = 11.sp, color = Color(0xFFFF9800)) } Spacer(modifier = Modifier.height(24.dp))