From e1fa0d1620033da49657c772832bfb8b6d55bf28 Mon Sep 17 00:00:00 2001 From: jjangddu Date: Wed, 27 May 2026 09:41:59 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=8E=8C=EC=9B=A8=EC=96=B4=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=20=EC=95=B1=20=EB=82=B4=20=ED=91=9C=EC=8B=9C=20(Setti?= =?UTF-8?q?ngs=20+=20ClinicalHome)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 연결 시 자동으로 받던 mid?/rid: device_info를 UI에 노출. 이전에는 BLE 디버그 로그에만 기록되어 사용자가 접근 불가했음. BleManager: - firmwareVersion / hardwareVersion / serialNumber 3개 MutableState 추가 - rid: 응답 파싱 — "VBTHW0100 VBT26040001 VBTFW0111" 형식을 공백 split + prefix 매칭(VBTHW/VBTFW/VBT)으로 안전 추출 - disconnect 시 3 필드 모두 clear PiezoMonitoringView Settings overlay: - 기존 "Version" 행을 "App" + "Firmware" 두 행으로 분리 - FW 없으면 "—" 표시 (연결 대기 중) - dev 모드 한정 "Serial" 행 추가 (회사 자산 추적용) ClinicalHomeView BLE 연결 카드: - deviceName 아래 "FW: VBTFW0116" 한 줄 추가 - 검사자가 별도 클릭 없이 펌웨어 버전 즉시 확인 가능 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../medilightv2android/ble/BleManager.kt | 15 ++++++++ .../ui/views/clinical/ClinicalHomeView.kt | 7 ++++ .../views/monitoring/PiezoMonitoringView.kt | 38 ++++++++++++++++++- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/example/medilightv2android/ble/BleManager.kt b/app/src/main/java/com/example/medilightv2android/ble/BleManager.kt index 3958efb..836e54a 100644 --- a/app/src/main/java/com/example/medilightv2android/ble/BleManager.kt +++ b/app/src/main/java/com/example/medilightv2android/ble/BleManager.kt @@ -70,6 +70,10 @@ class BleManager private constructor(private val context: Context) { val isScanning = mutableStateOf(false) val isConnected = mutableStateOf(false) val connectedDeviceName = mutableStateOf("") + // mid? → rid: 응답에서 파싱. 연결 직후 자동 채워짐. + val firmwareVersion = mutableStateOf("") // "VBTFW0116" + val hardwareVersion = mutableStateOf("") // "VBTHW0100" + val serialNumber = mutableStateOf("") // "VBT26040001" val batteryLevel = mutableStateOf(0) val bluetoothEnabled = mutableStateOf(bluetoothAdapter?.isEnabled == true) val isServiceReady = mutableStateOf(false) @@ -755,6 +759,9 @@ class BleManager private constructor(private val context: Context) { isConnected.value = false isServiceReady.value = false connectedDeviceName.value = "" + firmwareVersion.value = "" + hardwareVersion.value = "" + serialNumber.value = "" batteryLevel.value = 0 val idx = discoveredDevices.indexOfFirst { it.address == gatt.device.address } @@ -971,6 +978,14 @@ class BleManager private constructor(private val context: Context) { val info = if (data.size > 4) String(data, 4, data.size - 4, Charsets.US_ASCII).trim() else "?" debugLogger.rx("rid", data.size, "device_info=$info") debugLogger.info("DEVICE_INFO $info") + // 형식: "VBTHW0100 VBT26040001 VBTFW0111" (HW, Serial, FW) + val parts = info.split(Regex("\\s+")).filter { it.isNotBlank() } + handler.post { + parts.firstOrNull { it.startsWith("VBTHW") }?.let { hardwareVersion.value = it } + parts.firstOrNull { it.startsWith("VBTFW") }?.let { firmwareVersion.value = it } + parts.firstOrNull { it.startsWith("VBT") && !it.startsWith("VBTHW") && !it.startsWith("VBTFW") } + ?.let { serialNumber.value = it } + } } "rls:" -> { val state = if (data.size >= 5) data[4].toInt() and 0xFF else -1 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 0946a72..9c9576f 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 @@ -103,6 +103,13 @@ fun ClinicalHomeView(appState: AppState) { Text("BLE Connected", fontWeight = FontWeight.Bold, fontSize = 14.sp) Text(bleManager.connectedDeviceName.value, fontSize = 11.sp, color = MlSecondaryText) + // 펌웨어 버전 (mid? → rid: 응답 파싱, 연결 직후 자동 채워짐) + val fwVersion by bleManager.firmwareVersion + if (fwVersion.isNotEmpty()) { + Text("FW: $fwVersion", + fontSize = 11.sp, color = MlSecondaryText, + fontWeight = FontWeight.SemiBold) + } } } else { Text("BLE Not Connected", fontWeight = FontWeight.Bold, fontSize = 14.sp, diff --git a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt index 630330f..edf1084 100644 --- a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt +++ b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt @@ -1580,12 +1580,12 @@ fun PiezoMonitoringView(appState: AppState) { HorizontalDivider(modifier = Modifier.padding(vertical = dividerPadding)) Spacer(modifier = Modifier.height(8.dp)) - // Version (BuildConfig.VERSION_NAME) — read-only + // App Version Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { - Text("Version", fontSize = 13.sp, color = MlSecondaryText) + Text("App", fontSize = 13.sp, color = MlSecondaryText) Spacer(modifier = Modifier.weight(1f)) Text( com.example.medilightv2android.BuildConfig.VERSION_NAME, @@ -1594,6 +1594,40 @@ fun PiezoMonitoringView(appState: AppState) { color = MlPrimary, ) } + // Firmware Version (mid? → rid: 응답에서 파싱, 연결 시 자동) + val fwVersion by bleManager.firmwareVersion + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Text("Firmware", fontSize = 13.sp, color = MlSecondaryText) + Spacer(modifier = Modifier.weight(1f)) + Text( + if (fwVersion.isNotEmpty()) fwVersion else "—", + fontSize = 13.sp, + fontWeight = FontWeight.SemiBold, + color = if (fwVersion.isNotEmpty()) MlPrimary else MlSecondaryText, + ) + } + // Serial Number (dev 모드에서만) + if (appState.isDevMode) { + val serial by bleManager.serialNumber + if (serial.isNotEmpty()) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Text("Serial", fontSize = 13.sp, color = MlSecondaryText) + Spacer(modifier = Modifier.weight(1f)) + Text( + serial, + fontSize = 11.sp, + fontWeight = FontWeight.SemiBold, + color = MlSecondaryText, + ) + } + } + } Spacer(modifier = Modifier.height(8.dp)) HorizontalDivider()