From 1cf45df80c32ab8a3b385dcf76aabf1051d78cb9 Mon Sep 17 00:00:00 2001 From: jjangddu Date: Mon, 13 Jul 2026 15:28:43 +0900 Subject: [PATCH] =?UTF-8?q?docs:=205=EA=B0=9C=20md=20=EC=9D=BC=EA=B4=84=20?= =?UTF-8?q?sync=20(=EB=B2=84=EC=A0=84/=EB=82=A0=EC=A7=9C/package=20path/V2?= =?UTF-8?q?=206-stage/CH3=20flicker)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - USER_GUIDE.md * versionCode 24 → 26, App 1.0.0-design → 1.2.0-demo * Last Updated 2026-05-26 → 2026-07-13 * Recommended FW VBTFW0116 → VBTFW0120+ (mim FIFO 필수) * PinView.kt / AppState.kt link path com.example → com.medithings.vesiscan - docs/BLE_PROTOCOL_REFERENCE.md * 작성일 2026-06-05 → 2026-07-13, 검증 FW VBTFW0116 → VBTFW0120+ * package path 52건 com.example → com.medithings.vesiscan (모든 코드 링크 복구) - docs/ALGORITHM_COMPARISON.md * 3-Stage 를 "V1 (일반 진입)" 로 명시 * "V2 6-Stage Guide (AlignGuide4Stage)" 신규 섹션: phase 표 + CH3 flicker 3-Layer (majority / relaxed / soft-hint) + Python replay 검증 요약 * 헤더에 2026-07-13 update 표시 - docs/FLAVOR_DEMO_STABLE.md * example source path 표기 com/example/... → com/medithings/vesiscan/... - VesiScan_Android_Pipeline_Summary.md * 남아있던 com/example/ 경로 1건 정리 기능 변화 없음. 문서-코드 일관성 확보 (기존 링크가 리네임 이후 broken 상태였음). --- USER_GUIDE.md | 10 +-- VesiScan_Android_Pipeline_Summary.md | 2 +- docs/ALGORITHM_COMPARISON.md | 32 +++++++- docs/BLE_PROTOCOL_REFERENCE.md | 106 +++++++++++++-------------- docs/FLAVOR_DEMO_STABLE.md | 4 +- 5 files changed, 92 insertions(+), 62 deletions(-) diff --git a/USER_GUIDE.md b/USER_GUIDE.md index 06a8bf4..6aef3bf 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -1,8 +1,8 @@ # VesiScan-Basic User Guide -## App Version: 1.0.0-design (versionCode 24) -## Last Updated: 2026-05-26 -## Recommended Firmware: **VBTFW0116** (pending slot 1→8 확장, ADC drop 안정) +## App Version: 1.2.0-demo (versionCode 26) +## Last Updated: 2026-07-13 +## Recommended Firmware: **VBTFW0120+** (mim FIFO 지원 필수. VBTFW0121 은 `mls mode 0` handler 취약) --- @@ -15,7 +15,7 @@ 4. ~~Set a 4-digit PIN~~ — **현재 빌드는 PIN 데모 자동통과**: PinView 진입 시 즉시 verified 처리 (시연용) 5. You will arrive at the **Home** screen -> ⚠️ **DEMO 빌드 표시**: 현재 빌드는 PIN 잠금이 우회됩니다 ([PinView.kt:23-29](app/src/main/java/com/example/medilightv2android/ui/views/pin/PinView.kt#L23-L29)). 원본 UI는 `if (false) { ... }` 안에 보존되어 있어 배포 시 1줄 제거로 복구 가능. 배포 전 정리 체크리스트 참조. +> ⚠️ **DEMO 빌드 표시**: 현재 빌드는 PIN 잠금이 우회됩니다 ([PinView.kt:23-29](app/src/main/java/com/medithings/vesiscan/ui/views/pin/PinView.kt#L23-L29)). 원본 UI는 `if (false) { ... }` 안에 보존되어 있어 배포 시 1줄 제거로 복구 가능. 배포 전 정리 체크리스트 참조. ### Home Screen - 큰 **Bladdy 캐릭터** (3× 확대, 최대 660dp, 작은 화면에선 90% 캡) @@ -292,7 +292,7 @@ dividerPadding = if (isTablet) 12.dp else 0.dp - **Personalization → Placement → Monitoring** (정방향): Placement에서 Back = Personalization으로 복귀 - **Monitoring → Placement** (재정렬): Placement에서 Back = Monitoring으로 복귀 -코드: [AppState.kt](app/src/main/java/com/example/medilightv2android/AppState.kt) 의 `enterPlacementFromMonitoring()` / `backFromPlacementGuide()`. +코드: [AppState.kt](app/src/main/java/com/medithings/vesiscan/AppState.kt) 의 `enterPlacementFromMonitoring()` / `backFromPlacementGuide()`. ### startFromHome() 라우팅 fix diff --git a/VesiScan_Android_Pipeline_Summary.md b/VesiScan_Android_Pipeline_Summary.md index 5e79fbc..ddcfaa2 100644 --- a/VesiScan_Android_Pipeline_Summary.md +++ b/VesiScan_Android_Pipeline_Summary.md @@ -225,7 +225,7 @@ Central Link Layer ACK 속도가 향상됨 → 펌웨어 SoftDevice TX queue 포 이전: 시간 단독 `now - lastMaaSentMs < 800` 차단 → 펌웨어 응답이 1.2s 걸릴 때 `startMultiChannel(6)`이 응답 도중 호출되어 잔여 reb 패킷이 폐기되는 race 발생. -신규: [BleManager.kt:464-481](app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L464-L481) `canSendMaa()` +신규: [BleManager.kt:464-481](app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L464-L481) `canSendMaa()` ```kotlin private fun canSendMaa(caller: String): Boolean { val now = System.currentTimeMillis() diff --git a/docs/ALGORITHM_COMPARISON.md b/docs/ALGORITHM_COMPARISON.md index 4a6a6d2..7bb194d 100644 --- a/docs/ALGORITHM_COMPARISON.md +++ b/docs/ALGORITHM_COMPARISON.md @@ -1,5 +1,7 @@ # Method A / B / C Algorithm Comparison & Placement Test +_마지막 업데이트: 2026-07-13 — V2 6-stage / CH3 flicker 3-Layer 반영._ + ## Wall Detection Comparison ### Pipeline @@ -175,12 +177,40 @@ Screen exit → LED OFF **Boundary**: up → detect boundary (max drops) → come back → LATERAL **SWEEP** (CKLaw): navel → slide down → sagittalArgMaxCh → LATERAL → GREEN -### 3-Stage Guide +### 3-Stage Guide (V1 — 일반 사용자 진입, `computePlacementGuide`) **Stage 1 VERTICAL**: detect count + gradient + "Slide up/down ↑↓" **Stage 2 LATERAL**: |len4-len5| ≤ 10 → GREEN, else "Slide left/right ←→" **Stage 3 GREEN**: CV ≤ threshold AND |LR dev| ≤ 0.20 → "In position!" (7s hold) +### 6-Stage Guide (V2 — Clinical Alignment 세션, `AlignGuide4Stage`) + +`ClinicalHome → Sensor Alignment` 진입 시 자동 활성화. Method D chord 기반 +CV 로 정밀 vertical 조정 + CH3 flicker 대응 sliding-window majority. + +| # | Phase | 조건 | +|:---:|---|---| +| 1 | INITIAL_ACCUM | baseline 10 cycle 누적 | +| 2 | VERTICAL_CLIMB | CH3 sliding window majority — 최근 6 프레임 중 hit ≥ 3 | +| 3 | CH3_STABILIZE | 10 cycle 위치 유지, lost 판정도 sliding (window=4, majority=3) | +| 4 | CENTER_OPTIMIZE | CH0~CH3 chord CV ≤ 0.15 (3연속 hysteresis) | +| 5 | LR_BALANCE | \|u4−u5\| ≤ 8 samples, lost 채널 3연속 hysteresis | +| 6 | FINAL_CONFIRM | buf clear → 5 fresh 프레임 재확인 통과 → GREEN | + +**CH3 flicker 대응 3-Layer** (2026-07-06 이후, commit e808b5b / 9261bc7 / 4f8d536): +- **Layer 1 — Sliding-Window Majority**: 3 연속 hit 대체. 인체 flick 패턴 + (YYNNYY 등) 통과 가능. +- **Layer 2 — Stuck Detection + Relaxed Mode**: VERTICAL_CLIMB 20 프레임 (~5초) + 갇힘 + CH3 hit 저조 + CH0~CH2 all detected → CH3 없이 상단 3채널로 CENTER_OPT + 진입 허용. `AlignmentConstants.VERTICAL_STUCK_THRESHOLD`. +- **Layer 3 — UX Soft-Hint**: 12 프레임 (~3초) 반복 시 "↑ 위로" → + "CH3 신호 확인 중 · 위치 유지" 로 문구 자동 완화. + +**Python replay 검증** (`tools/align_analyze.py`, 2026-07-10~13): data123/ 인체 +4 세션 (Phantom + Human 0/1/3 CM). NEW vs LEGACY 병행 replay 결과 Human 0CM +(CH3 31.8%) 회귀 방지 + Human 1CM (77.3%) 조기 진입 확인, 팬텀 false-positive +없음. 상세 표: `tools/README.md`. + ### Measurement Modes (v6 업데이트) - **Auto Scan**: **min 600ms gap** (state-based throttle: `!isComplete` + 600ms), 10-sample trimmed mean (max/min removed, 8 avg) diff --git a/docs/BLE_PROTOCOL_REFERENCE.md b/docs/BLE_PROTOCOL_REFERENCE.md index a60ad3e..62b7ce2 100644 --- a/docs/BLE_PROTOCOL_REFERENCE.md +++ b/docs/BLE_PROTOCOL_REFERENCE.md @@ -1,8 +1,8 @@ # BLE Protocol Reference — VBT Device -VesiScan Basic Android 앱이 VBT 디바이스(VBTFW0116+ 펌웨어)와 주고받는 BLE 명령/응답 전체 정리. 코드 위치는 모두 `app/src/main/java/com/example/medilightv2android/ble/` 기준. +VesiScan Basic Android 앱이 VBT 디바이스(VBTFW0120+ 펌웨어)와 주고받는 BLE 명령/응답 전체 정리. 코드 위치는 모두 `app/src/main/java/com/medithings/vesiscan/ble/` 기준. -작성일 / 검증 FW: 2026-06-05 / **VBTFW0116** (reb 신구조 적용) +작성일 / 검증 FW: 2026-07-13 / **VBTFW0120+** (mim FIFO + mtb queue overrun 3-layer guard 반영) --- @@ -48,7 +48,7 @@ VesiScan Basic Android 앱이 VBT 디바이스(VBTFW0116+ 펌웨어)와 주고 ## 2. BLE 연결 Lifecycle ### 2.1 UUID (Nordic UART Service) -파일: [`BleManager.kt:45-48`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L45) +파일: [`BleManager.kt:45-48`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L45) | 역할 | UUID | |---|---| @@ -88,7 +88,7 @@ VesiScan Basic Android 앱이 VBT 디바이스(VBTFW0116+ 펌웨어)와 주고 - 두 협상 모두 디스커버리 + CCCD 활성화 이후에 진행. 너무 일찍 호출하면 reject. ### 2.4 자동 재연결 (silent) -파일: [`BleManager.kt:601` scheduleAutoReconnect](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L601) +파일: [`BleManager.kt:601` scheduleAutoReconnect](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L601) - 의도하지 않은 disconnect → `scheduleAutoReconnect()` 자동 호출 - 마지막 연결 디바이스 주소를 `SharedPreferences`에 저장 → scan + reconnect 시도 @@ -144,7 +144,7 @@ val isMtbBusy: Boolean ## 3. 명령 빌더 — CRC16-CCITT -파일: [`CRC16.kt`](../app/src/main/java/com/example/medilightv2android/ble/CRC16.kt) +파일: [`CRC16.kt`](../app/src/main/java/com/medithings/vesiscan/ble/CRC16.kt) ``` Polynomial: 0x1021 @@ -189,26 +189,26 @@ fun verify(data: ByteArray): Boolean | 명령 | 빌더 | Send 함수 | 응답 prefix | 용도 | |---|---|---|---|---| -| `msr?` | ASCII " " | `disconnectAndUnbond()` 내부 ([L276](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L276)) | — | 본딩 wipe + reboot | -| `mpa?` | BE [freq, cycles] | `sendPiezoPowerOn(freq=2, cycles=5)` ([L404](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L404)) | `rpa:` | Piezo power ON | -| `mps?` | ASCII " " | `sendPiezoStop()` ([L408](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L408)) | — | Piezo stop | -| `mec?` | BE [freq, delay, 140, cycles, 1, ch] | `sendBurst(...)` ([L412](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L412)) | `reb:` 단일 채널 | Single-channel burst | -| `maa?` | ASCII " " | `sendAllChannels()` / `sendChannelsOnly()` ([L416, L513](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L416)) | reb×6 + raa | 6채널 sweep (도넛/placement) | -| `mtb?` | ASCII " " | `sendMtb()` ([L430](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L430)) | reb×6 + raa + **rim** | 6채널 + IMU (★ Clinical) | -| `mbb?` | ASCII " " | `sendFullMeasurement()` ([L480](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L480)) | rbb + reb×6 + raa | 측정 헤더(batt+temp+IMU) + 6채널 | -| `mta?` | BE [0 or 1] | `sendNirsPowerOn()/Off()` ([L441/445](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L441)) | `rta:` | NIRS power | -| `mqq?` | ASCII " " | `sendMqqQuery()` ([L449](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L449)) | `rqq:` | NIRS sensor activate | -| `mag?` | ASCII " " | `sendMagQuery()` ([L453](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L453)) | `rag:` | NIRS gain calibration | -| `mcj?` | ASCII " " | `sendMcjQuery()` ([L457](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L457)) | `rcj:` | NIRS MCJ raw | -| `msn?` | BE [0] | `sendBatteryQuery()` ([L463](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L463)) | `rsn:` | 배터리 mV 조회 | -| `mid?` | ASCII " " | `sendDeviceInfoQuery()` ([L467](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L467)) | `rid:` | FW/HW/Serial 조회 | -| `mls?` | BE [state] | `sendLedMode(state)` ([L471](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L471)) | `rls:` | LED 모드 변경 | +| `msr?` | ASCII " " | `disconnectAndUnbond()` 내부 ([L276](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L276)) | — | 본딩 wipe + reboot | +| `mpa?` | BE [freq, cycles] | `sendPiezoPowerOn(freq=2, cycles=5)` ([L404](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L404)) | `rpa:` | Piezo power ON | +| `mps?` | ASCII " " | `sendPiezoStop()` ([L408](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L408)) | — | Piezo stop | +| `mec?` | BE [freq, delay, 140, cycles, 1, ch] | `sendBurst(...)` ([L412](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L412)) | `reb:` 단일 채널 | Single-channel burst | +| `maa?` | ASCII " " | `sendAllChannels()` / `sendChannelsOnly()` ([L416, L513](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L416)) | reb×6 + raa | 6채널 sweep (도넛/placement) | +| `mtb?` | ASCII " " | `sendMtb()` ([L430](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L430)) | reb×6 + raa + **rim** | 6채널 + IMU (★ Clinical) | +| `mbb?` | ASCII " " | `sendFullMeasurement()` ([L480](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L480)) | rbb + reb×6 + raa | 측정 헤더(batt+temp+IMU) + 6채널 | +| `mta?` | BE [0 or 1] | `sendNirsPowerOn()/Off()` ([L441/445](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L441)) | `rta:` | NIRS power | +| `mqq?` | ASCII " " | `sendMqqQuery()` ([L449](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L449)) | `rqq:` | NIRS sensor activate | +| `mag?` | ASCII " " | `sendMagQuery()` ([L453](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L453)) | `rag:` | NIRS gain calibration | +| `mcj?` | ASCII " " | `sendMcjQuery()` ([L457](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L457)) | `rcj:` | NIRS MCJ raw | +| `msn?` | BE [0] | `sendBatteryQuery()` ([L463](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L463)) | `rsn:` | 배터리 mV 조회 | +| `mid?` | ASCII " " | `sendDeviceInfoQuery()` ([L467](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L467)) | `rid:` | FW/HW/Serial 조회 | +| `mls?` | BE [state] | `sendLedMode(state)` ([L471](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L471)) | `rls:` | LED 모드 변경 | | ~~`msp?`~~ | ~~ASCII " "~~ | ~~`sendImuQuery()`~~ | `rsp:` | ⚠ **2026-07-08 완전 제거** — 신 firmware 는 `mim` 만 사용. `rsp:` 파서는 legacy 응답 대비 유지. | | `mim?` | ASCII " " | `sendImuFifoQuery()` | `rim:` (15 sample) | ★ IMU FIFO — piezo 무음, walking detector 시계열 확보용. `imuCollector.reset()` 을 먼저 호출하므로 mtb 진행 중 (isMtbBusy=true) 이면 caller 가 반드시 skip 해야 함 (mtb 의 rim 파괴 방지). | ### 4.1 maa / mtb / maa 송신 throttle (canSendMaa) -파일: [`BleManager.kt:494-509`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L494) +파일: [`BleManager.kt:494-509`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L494) `maa?`, `mtb?`, `sendChannelsOnly()`는 한 채널 그룹 응답이 280~420ms 소요되므로 다음 가드를 통과해야만 송신: @@ -241,28 +241,28 @@ fun sendMtb(): Boolean { `onCharacteristicChanged` → `processReceivedData(data)` → prefix 4글자 추출 → when 분기. -파일: [`BleManager.kt:986-1097`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L986) +파일: [`BleManager.kt:986-1097`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L986) | Prefix | 의미 | 패킷 길이 | 파싱 위치 | 콜백 | |---|---|---|---|---| -| `rta:` `rta!` | NIRS power ON ack | — | [L991](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L991) | `onNirsPowerOnReceived` | -| `rsh:` `rsh!` `rqq:` `rqq!` | Sensor activated | — | [L995](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L995) | `onNirsSensorActivated` | -| `rag:` `rag!` | NIRS gain calibration | data.size - 6 = N×2 | [L999](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L999) | `onNirsMagReceived` | -| `rcj:` `rcj!` | NIRS MCJ raw | — | [L1003](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1003) | `onNirsMcjReceived` | -| `rpa:` | Piezo ack | — | [L1007](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1007) | `onPiezoDataReceived` | -| `rer:` | Preliminary header | — | [L1011](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1011) | (로그만) | -| `reb:` | Piezo ADC raw (1채널) | **210B** (신구조) | [L1014](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1014) | `piezoCollector.addPacket` | -| `red:` | Piezo continuation | — | [L1022](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1022) | `piezoCollector.addPacket` | -| `ree:` | Single-ch end | 8B | [L1026](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1026) | `piezoCollector.addPacket` | -| `raa:` | **All-ch complete** marker | 8B | [L1026](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1026) | `piezoCollector.addPacket` → set finalize | -| `rsn:` | Battery (mV) | ≥6B | [L1031](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1031) | `batteryLevel.value` 갱신 | -| `rid:` | Device info (FW/HW/SN) | 가변 | [L1045](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1045) | `firmwareVersion/...value` 갱신 | -| `rls:` | LED state | ≥5B | [L1067](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1067) | (로그만) | -| `rxs:` | Cmd not supported (echo) | 가변 | [L1071](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1071) | (로그만) | -| `rbb:` | Full measurement header | 22B | [L1075](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1075) | `onMbbHeaderReceived` + collector | -| `rsp:` | IMU 단일 샘플 | ≥16B | [L1080](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1080) | `onImuReceived` | -| `rim:` | **IMU 15 samples** | 188B | [L1086](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1086) | `imuCollector.parseRim` | -| `???` | Unknown | — | [L1091](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1091) | hex 로그 + warn | +| `rta:` `rta!` | NIRS power ON ack | — | [L991](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L991) | `onNirsPowerOnReceived` | +| `rsh:` `rsh!` `rqq:` `rqq!` | Sensor activated | — | [L995](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L995) | `onNirsSensorActivated` | +| `rag:` `rag!` | NIRS gain calibration | data.size - 6 = N×2 | [L999](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L999) | `onNirsMagReceived` | +| `rcj:` `rcj!` | NIRS MCJ raw | — | [L1003](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1003) | `onNirsMcjReceived` | +| `rpa:` | Piezo ack | — | [L1007](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1007) | `onPiezoDataReceived` | +| `rer:` | Preliminary header | — | [L1011](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1011) | (로그만) | +| `reb:` | Piezo ADC raw (1채널) | **210B** (신구조) | [L1014](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1014) | `piezoCollector.addPacket` | +| `red:` | Piezo continuation | — | [L1022](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1022) | `piezoCollector.addPacket` | +| `ree:` | Single-ch end | 8B | [L1026](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1026) | `piezoCollector.addPacket` | +| `raa:` | **All-ch complete** marker | 8B | [L1026](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1026) | `piezoCollector.addPacket` → set finalize | +| `rsn:` | Battery (mV) | ≥6B | [L1031](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1031) | `batteryLevel.value` 갱신 | +| `rid:` | Device info (FW/HW/SN) | 가변 | [L1045](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1045) | `firmwareVersion/...value` 갱신 | +| `rls:` | LED state | ≥5B | [L1067](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1067) | (로그만) | +| `rxs:` | Cmd not supported (echo) | 가변 | [L1071](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1071) | (로그만) | +| `rbb:` | Full measurement header | 22B | [L1075](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1075) | `onMbbHeaderReceived` + collector | +| `rsp:` | IMU 단일 샘플 | ≥16B | [L1080](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1080) | `onImuReceived` | +| `rim:` | **IMU 15 samples** | 188B | [L1086](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1086) | `imuCollector.parseRim` | +| `???` | Unknown | — | [L1091](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1091) | hex 로그 + warn | ⚠️ prefix 매칭은 **`String(data, 0, 4, US_ASCII)`** 로 정확히 4글자 (`"reb:"`처럼 colon 포함). @@ -270,7 +270,7 @@ fun sendMtb(): Boolean { ## 6. Piezo 응답 상세 (reb / red / ree / raa) -파일: [`PiezoPacketCollector.kt`](../app/src/main/java/com/example/medilightv2android/ble/PiezoPacketCollector.kt) +파일: [`PiezoPacketCollector.kt`](../app/src/main/java/com/medithings/vesiscan/ble/PiezoPacketCollector.kt) ### 6.1 reb 신구조 (210B, FW 2026-06-02+) @@ -332,7 +332,7 @@ state: 펌웨어 상태 코드. 현재 클라이언트는 사용 안 함 — 마 ## 7. IMU 응답 상세 (rim) -파일: [`ImuPacketCollector.kt`](../app/src/main/java/com/example/medilightv2android/ble/ImuPacketCollector.kt) +파일: [`ImuPacketCollector.kt`](../app/src/main/java/com/medithings/vesiscan/ble/ImuPacketCollector.kt) ### 7.1 패킷 구조 (188B) ``` @@ -380,7 +380,7 @@ if (payloadBytes < expectedBytes) { ## 8. 디바이스 정보 (rid) -파일: [`BleManager.kt:1045-1066`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1045) +파일: [`BleManager.kt:1045-1066`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1045) ### 8.1 패킷 구조 ``` @@ -412,7 +412,7 @@ val sn = parts.firstOrNull { it.startsWith("VBT") && !it.startsWith("VBTHW") && ## 9. 배터리 (rsn) -파일: [`BleManager.kt:1031-1043`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1031) +파일: [`BleManager.kt:1031-1043`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1031) ### 9.1 패킷 구조 ``` @@ -442,7 +442,7 @@ if (pct <= batteryLevel.value || batteryLevel.value == 0) { - ⚠️ 충전 중 상승은 반영 안 됨 — 의도된 동작 ### 9.4 Polling 정책 (★ 2026-06-05 변경) -파일: [`BleManager.kt:517-565`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L517) +파일: [`BleManager.kt:517-565`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L517) ``` 1. 연결 직후 즉시 1회 msn? 송신 @@ -456,7 +456,7 @@ if (pct <= batteryLevel.value || batteryLevel.value == 0) { ## 10. 측정 헤더 (rbb) -파일: [`BleManager.kt:1075-1078`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L1075) +파일: [`BleManager.kt:1075-1078`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L1075) ``` [tag 4B "rbb:"][batt 2B][IMU 2B][temp 2B][crc 2B] = 22B @@ -485,7 +485,7 @@ if (pct <= batteryLevel.value || batteryLevel.value == 0) { `!` 변형은 펌웨어 측 에러/특수 상태. 처리 자체는 `:` 와 동일하지만 디버그 로그에 prefix 그대로 기록. -NIRS 자세한 파싱은 [`NirsManager`](../app/src/main/java/com/example/medilightv2android/managers/NirsManager.kt) 참고 (Vivamyo 화면 전용). +NIRS 자세한 파싱은 [`NirsManager`](../app/src/main/java/com/medithings/vesiscan/managers/NirsManager.kt) 참고 (Vivamyo 화면 전용). --- @@ -495,7 +495,7 @@ NIRS 자세한 파싱은 [`NirsManager`](../app/src/main/java/com/example/medili 표준 GATT 연결만으로 끝. 별도 페어링 명령 없음. CCCD enable 완료 시 service ready. ### 12.2 Unpair (`disconnectAndUnbond()`) -파일: [`BleManager.kt:262-310`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L262) +파일: [`BleManager.kt:262-310`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt#L262) ``` 1. isUserDisconnect = true ← auto-reconnect 차단 flag @@ -540,7 +540,7 @@ NIRS 자세한 파싱은 [`NirsManager`](../app/src/main/java/com/example/medili - `ImuCollector` — rim sample count ### 14.2 ble.log (텍스트 디버그 로그) -파일: [`BleDebugLogger.kt`](../app/src/main/java/com/example/medilightv2android/services/BleDebugLogger.kt) +파일: [`BleDebugLogger.kt`](../app/src/main/java/com/medithings/vesiscan/services/BleDebugLogger.kt) 각 세션 폴더에 `ble.log` 자동 작성: ``` @@ -610,10 +610,10 @@ T+600 App delay(600 - 290 = 310ms) 끝 → 다시 sendMtb() --- -**원본 소스**: `app/src/main/java/com/example/medilightv2android/ble/` -- [`BleManager.kt`](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt) -- [`CRC16.kt`](../app/src/main/java/com/example/medilightv2android/ble/CRC16.kt) -- [`PiezoPacketCollector.kt`](../app/src/main/java/com/example/medilightv2android/ble/PiezoPacketCollector.kt) -- [`ImuPacketCollector.kt`](../app/src/main/java/com/example/medilightv2android/ble/ImuPacketCollector.kt) +**원본 소스**: `app/src/main/java/com/medithings/vesiscan/ble/` +- [`BleManager.kt`](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt) +- [`CRC16.kt`](../app/src/main/java/com/medithings/vesiscan/ble/CRC16.kt) +- [`PiezoPacketCollector.kt`](../app/src/main/java/com/medithings/vesiscan/ble/PiezoPacketCollector.kt) +- [`ImuPacketCollector.kt`](../app/src/main/java/com/medithings/vesiscan/ble/ImuPacketCollector.kt) **관련 문서**: [iOS Porting Clinical Measurement](iOS_PORTING_CLINICAL_MEASUREMENT.md), [labdb.md](../labdb.md) diff --git a/docs/FLAVOR_DEMO_STABLE.md b/docs/FLAVOR_DEMO_STABLE.md index 2fb8929..4e2cab2 100644 --- a/docs/FLAVOR_DEMO_STABLE.md +++ b/docs/FLAVOR_DEMO_STABLE.md @@ -67,8 +67,8 @@ dev flavor 빌드 → src/main/* + src/dev/* **예시:** V4.1 wall detection 알고리즘을 2026-06-08 시점에 동결하려면: ``` -src/main/java/com/example/.../walldetect/V41Detector.kt ← 계속 변경됨 (dev에서 사용) -src/demo/java/com/example/.../walldetect/V41Detector.kt ← 2026-06-08 시점 복사본 (demo에서 사용) +src/main/java/com/medithings/vesiscan/.../walldetect/V41Detector.kt ← 계속 변경됨 (dev에서 사용) +src/demo/java/com/medithings/vesiscan/.../walldetect/V41Detector.kt ← 2026-06-08 시점 복사본 (demo에서 사용) ``` main의 V41Detector를 리팩토링/실험해도 demo 빌드는 항상 격리본을 컴파일.