feat: 펌웨어 버전을 모든 로그 파일에 직접 노출
기존: meta.json에만 기록 (파일 단독으로 보면 펌웨어 확인 불가) 변경: adc.csv 컬럼 / placements.jsonl·events.jsonl 필드 / ble.log 헤더 블록 모두 노출 검사자/분석자가 각 파일 단독으로 열어도 어느 펌웨어 데이터인지 즉시 확인 가능. 변경: - BleManager.current: companion에 instance accessor 추가 (logger 같은 context-free 컴포넌트에서 BLE state 읽기용) - BleManager rid: 핸들러에서 debugLogger.deviceInfo(fw, hw, sn) 호출 (Headers 블록 기록) - AdcCsvLogger: CSV 헤더에 firmware_version 컬럼 추가, 모든 행에 값 채움 scan_id,timestamp,clinical_label,firmware_version,volume_ml,lr_ratio,... - MeasurementLogService.logMeasurement / logPlacement: JSON에 firmware_version 필드 추가 - BleDebugLogger.deviceInfo(): 새 메서드. rid: 응답 받은 후 ble.log에 다음 블록 기록: ─── Device Info ─── Firmware: VBTFW0116 Hardware: VBTHW0100 Serial: VBT26040001 ─────────────────── BLE 미연결 / mid 응답 안 옴 시엔 필드/값 비어있음 (정상). 응답 받자마자 자동 갱신. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,9 @@ class BleManager private constructor(private val context: Context) {
|
|||||||
instance ?: BleManager(context.applicationContext).also { instance = it }
|
instance ?: BleManager(context.applicationContext).also { instance = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Logger 같은 context-free 컴포넌트에서 BLE state 읽기용. instance 생성 후에만 non-null. */
|
||||||
|
val current: BleManager? get() = instance
|
||||||
|
|
||||||
val SERVICE_UUID: UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
|
val SERVICE_UUID: UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
|
||||||
val TX_CHAR_UUID: UUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E")
|
val TX_CHAR_UUID: UUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E")
|
||||||
val RX_CHAR_UUID: UUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E")
|
val RX_CHAR_UUID: UUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E")
|
||||||
@@ -980,12 +983,16 @@ class BleManager private constructor(private val context: Context) {
|
|||||||
debugLogger.info("DEVICE_INFO $info")
|
debugLogger.info("DEVICE_INFO $info")
|
||||||
// 형식: "VBTHW0100 VBT26040001 VBTFW0111" (HW, Serial, FW)
|
// 형식: "VBTHW0100 VBT26040001 VBTFW0111" (HW, Serial, FW)
|
||||||
val parts = info.split(Regex("\\s+")).filter { it.isNotBlank() }
|
val parts = info.split(Regex("\\s+")).filter { it.isNotBlank() }
|
||||||
|
val hw = parts.firstOrNull { it.startsWith("VBTHW") } ?: ""
|
||||||
|
val fw = parts.firstOrNull { it.startsWith("VBTFW") } ?: ""
|
||||||
|
val sn = parts.firstOrNull { it.startsWith("VBT") && !it.startsWith("VBTHW") && !it.startsWith("VBTFW") } ?: ""
|
||||||
handler.post {
|
handler.post {
|
||||||
parts.firstOrNull { it.startsWith("VBTHW") }?.let { hardwareVersion.value = it }
|
if (hw.isNotEmpty()) hardwareVersion.value = hw
|
||||||
parts.firstOrNull { it.startsWith("VBTFW") }?.let { firmwareVersion.value = it }
|
if (fw.isNotEmpty()) firmwareVersion.value = fw
|
||||||
parts.firstOrNull { it.startsWith("VBT") && !it.startsWith("VBTHW") && !it.startsWith("VBTFW") }
|
if (sn.isNotEmpty()) serialNumber.value = sn
|
||||||
?.let { serialNumber.value = it }
|
|
||||||
}
|
}
|
||||||
|
// ble.log에 헤더 블록 형태로 (텍스트 에디터로 열었을 때 즉시 보임)
|
||||||
|
debugLogger.deviceInfo(fw, hw, sn)
|
||||||
}
|
}
|
||||||
"rls:" -> {
|
"rls:" -> {
|
||||||
val state = if (data.size >= 5) data[4].toInt() and 0xFF else -1
|
val state = if (data.size >= 5) data[4].toInt() and 0xFF else -1
|
||||||
|
|||||||
@@ -70,11 +70,13 @@ object AdcCsvLogger {
|
|||||||
val sgStr = if (analyzer.useSgFilter) "ON" else "OFF"
|
val sgStr = if (analyzer.useSgFilter) "ON" else "OFF"
|
||||||
|
|
||||||
val clinicalLabel = ClinicalSessionStore.currentLabel() // 일반 모드면 ""
|
val clinicalLabel = ClinicalSessionStore.currentLabel() // 일반 모드면 ""
|
||||||
|
// BLE 펌웨어 버전 (연결 안 됐으면 "")
|
||||||
|
val fwVersion = com.example.medilightv2android.ble.BleManager.current?.firmwareVersion?.value ?: ""
|
||||||
|
|
||||||
java.io.FileWriter(file, !isNew).buffered().use { writer ->
|
java.io.FileWriter(file, !isNew).buffered().use { writer ->
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
val header = buildString {
|
val header = buildString {
|
||||||
append("scan_id,timestamp,clinical_label,volume_ml,lr_ratio,threshold,dps,sg_filter,channel")
|
append("scan_id,timestamp,clinical_label,firmware_version,volume_ml,lr_ratio,threshold,dps,sg_filter,channel")
|
||||||
for (i in 0 until 100) append(",s$i")
|
for (i in 0 until 100) append(",s$i")
|
||||||
}
|
}
|
||||||
writer.write(header)
|
writer.write(header)
|
||||||
@@ -83,7 +85,7 @@ object AdcCsvLogger {
|
|||||||
|
|
||||||
for ((chIdx, buffer) in channels.withIndex()) {
|
for ((chIdx, buffer) in channels.withIndex()) {
|
||||||
val row = buildString {
|
val row = buildString {
|
||||||
append("$scanCounter,$timestamp,$clinicalLabel,$volStr,$lrStr,$thrStr,$dpsStr,$sgStr,CH$chIdx")
|
append("$scanCounter,$timestamp,$clinicalLabel,$fwVersion,$volStr,$lrStr,$thrStr,$dpsStr,$sgStr,CH$chIdx")
|
||||||
for (sample in buffer) {
|
for (sample in buffer) {
|
||||||
append(",${sample.toInt()}")
|
append(",${sample.toInt()}")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,6 +130,15 @@ class BleDebugLogger private constructor(private val context: Context) {
|
|||||||
add(Entry(System.currentTimeMillis(), Level.RSSI, "RSSI=$value dBm", rssi = value))
|
add(Entry(System.currentTimeMillis(), Level.RSSI, "RSSI=$value dBm", rssi = value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** mid?/rid: 응답 받은 후 호출 — 펌웨어 정보를 ble.log에 보기 좋게 헤더 형태로 기록 */
|
||||||
|
fun deviceInfo(fw: String, hw: String, sn: String) {
|
||||||
|
appendToFile("─── Device Info ───")
|
||||||
|
if (fw.isNotEmpty()) appendToFile("Firmware: $fw")
|
||||||
|
if (hw.isNotEmpty()) appendToFile("Hardware: $hw")
|
||||||
|
if (sn.isNotEmpty()) appendToFile("Serial: $sn")
|
||||||
|
appendToFile("───────────────────")
|
||||||
|
}
|
||||||
|
|
||||||
fun connected(name: String, address: String) {
|
fun connected(name: String, address: String) {
|
||||||
deviceName = name
|
deviceName = name
|
||||||
sessionStart = System.currentTimeMillis()
|
sessionStart = System.currentTimeMillis()
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ class MeasurementLogService private constructor(private val context: Context) {
|
|||||||
put("app_version_code", appVersionCode)
|
put("app_version_code", appVersionCode)
|
||||||
val clinical = ClinicalSessionStore.currentLabel()
|
val clinical = ClinicalSessionStore.currentLabel()
|
||||||
if (clinical.isNotEmpty()) put("clinical_label", clinical)
|
if (clinical.isNotEmpty()) put("clinical_label", clinical)
|
||||||
|
// BLE 펌웨어 버전 (연결 시 mid?/rid: 응답에서 받음)
|
||||||
|
val fw = com.example.medilightv2android.ble.BleManager.current?.firmwareVersion?.value
|
||||||
|
if (!fw.isNullOrEmpty()) put("firmware_version", fw)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (volumeMl != null) {
|
if (volumeMl != null) {
|
||||||
@@ -132,6 +135,9 @@ class MeasurementLogService private constructor(private val context: Context) {
|
|||||||
if (score >= 0) put("score", score)
|
if (score >= 0) put("score", score)
|
||||||
val clinical = ClinicalSessionStore.currentLabel()
|
val clinical = ClinicalSessionStore.currentLabel()
|
||||||
if (clinical.isNotEmpty()) put("clinical_label", clinical)
|
if (clinical.isNotEmpty()) put("clinical_label", clinical)
|
||||||
|
// BLE 펌웨어 버전
|
||||||
|
val fw = com.example.medilightv2android.ble.BleManager.current?.firmwareVersion?.value
|
||||||
|
if (!fw.isNullOrEmpty()) put("firmware_version", fw)
|
||||||
}
|
}
|
||||||
appendLog(entry, "placements.jsonl")
|
appendLog(entry, "placements.jsonl")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user