docs: v7 (2026-07-10) 반영 — msp 제거, 6-stage alignment, mtb queue fix, labdb 자동 재시도
이번 세션 대량 변경 사항을 5개 문서에 반영. 각 문서마다 stale 이던
섹션을 갱신하거나 신규 섹션 추가.
USER_GUIDE.md
- Sensor Alignment 를 V1 (3-stage) / V2 (6-stage) 로 재구성
- V2 6-stage 표 + relaxed mode / soft hint 설명
- GREEN 진입 5초 hold + 10-strike 리셋 완화 명시
- "최적의 위치입니다!" 문구 반영
docs/BLE_PROTOCOL_REFERENCE.md
- msp 명령 취소선 처리 + mim 신규 명령 문서화
- Watchdog timeout 25초 연장 명시 (2.5)
- §2.6 신규: firmware VBTFW0121 mls mode 0 freeze 취약점 +
앱 측 3-layer 회피 (isMtbBusy / mtb 3초 timeout / 자동 재연결)
VesiScan_Android_Pipeline_Summary.md
- v7 (2026-07-10) 섹션 신규 추가 — BLE / Alignment / UI / labdb /
tools 5개 카테고리로 변경 사항 정리
- 권장 펌웨어 표기 VBTFW0116 → VBTFW0120+ 로 갱신
labdb.md
- §12b 신규: 앱 측 Auto Retry Policy — endMeasurement 자동 업로드
조건 완화, LabdbAutoRetry object, UI 배너, 재시도 안전성
tools/README.md
- labdb_upload.py 섹션 신규 — 사용법 / 폴더 구조 / 재실행 안전성 /
buildPayload 로직 / 활용 예 정리
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+39
-26
@@ -74,40 +74,53 @@ This step ensures the sensor is positioned correctly over your bladder for accur
|
||||
- Apply ultrasound gel between the sensor and your skin
|
||||
- Tap the **Start Alignment** button (large orange button)
|
||||
|
||||
### Step 1/3: Vertical Alignment
|
||||
### 알고리즘 V1 (일반 사용자, 3-stage) / V2 (Clinical Alignment 세션, 6-stage)
|
||||
|
||||
The app will begin scanning and provide direction:
|
||||
- **"Slide up ↑"** — slide the device upward toward your navel
|
||||
- **"Slide down ↓"** — slide the device downward toward your pubic bone
|
||||
- **"Slide up slightly ↑"** / **"Slide down slightly ↓"** — make small adjustments
|
||||
- Move the sensor **slowly, 1-2mm at a time**
|
||||
- When the vertical position is correct, the app will show **Step 2/3**
|
||||
일반 진입 (모니터링 → Settings → 재측정 등) 은 **V1 (3-stage)**, Clinical
|
||||
Home 의 **Sensor Alignment** 버튼으로 진입한 세션은 자동으로 **V2 (6-stage)**
|
||||
로 활성화됩니다. Step indicator ("단계 N/M") 로 구분 가능.
|
||||
|
||||
> The hint text will show animated dots (e.g., "Slide up ↑.", "Slide up ↑..", "Slide up ↑...") to indicate the system is actively scanning.
|
||||
### V1: 3-stage (일반 진입)
|
||||
|
||||
### Step 2/3: Lateral Alignment
|
||||
**Step 1/3: Vertical Alignment**
|
||||
- **"Slide up ↑"** / **"Slide down ↓"** / **"Slide up slightly ↑"** 등의 방향 안내
|
||||
- 1~2mm 씩 천천히 이동, 위치가 잡히면 Step 2/3
|
||||
|
||||
1. The app will first show **"Checking..."** for a few seconds
|
||||
2. Then it will guide you:
|
||||
- **"Slide left ←"** — slide the device to the left
|
||||
- **"Slide right →"** — slide the device to the right
|
||||
3. Adjust until both lateral sensors (CH4 and CH5) detect the bladder equally
|
||||
4. When balanced, the app will show **Step 3/3**
|
||||
**Step 2/3: Lateral Alignment**
|
||||
- **"Checking..."** → **"Slide left ←"** / **"Slide right →"** — CH4/CH5 균형
|
||||
- 균형 잡히면 Step 3/3
|
||||
|
||||
### Step 3/3: Final Check (Green Zone)
|
||||
**Step 3/3: Final Check (Green Zone)**
|
||||
- CV + LR deviation 확인 → **"최적의 위치입니다!"** (2026-07-07 문구 개선)
|
||||
- 배경 초록 + **5초 hold** (2026-07-07: 7초 → 5초 유지, 조기 unlock 방지 강화)
|
||||
- **Start Scanning** 활성 → 측정 화면으로
|
||||
|
||||
1. The app checks overall alignment quality (CV + LR deviation)
|
||||
2. If everything is optimal, the hint will show **"In position!"**
|
||||
3. The screen background turns green and holds this state for **7 seconds** to confirm stability
|
||||
4. The **Start Scanning** button (green) becomes active
|
||||
5. Tap **Start Scanning** to proceed to the measurement screen
|
||||
### V2: 6-stage (Clinical Alignment 세션 전용)
|
||||
|
||||
방광 하부/후벽 각도 오차를 실시간 보정하는 정밀 정렬 알고리즘.
|
||||
|
||||
| 단계 | 화면 문구 예 | 조건 |
|
||||
|:---:|---|---|
|
||||
| 1/6 INITIAL_ACCUM | "잠시 그대로 두세요 (n/10)" | baseline 10 cycle 누적 |
|
||||
| 2/6 VERTICAL_CLIMB | "↑ 위로 조금씩 올리세요 (hit N/6)" | CH3 sliding window majority — 최근 6 프레임 중 3회 hit |
|
||||
| 3/6 CH3_STABILIZE | "✓ ch3 검출 — 위치 유지 (n/10)" | CH3 안정 확인, 단발 flick 무시 (window=4, majority=3) |
|
||||
| 4/6 CENTER_OPTIMIZE | "↑/↓ 조금 (CV=0.xx)" | CH0~CH3 chord CV 임계 0.15 |
|
||||
| 5/6 LR_BALANCE | "← / → \|Δ\|=n" | CH4/CH5 균형 |
|
||||
| 6/6 FINAL_CONFIRM | "✓ 최적의 위치입니다!" | 5 프레임 재확인 통과 → GREEN |
|
||||
|
||||
**추가 안전장치**:
|
||||
- **Relaxed mode**: VERTICAL_CLIMB 에 20 프레임 (~5초) 이상 갇히면 CH3 없이도
|
||||
상단 3채널 (CH0~CH2) 로 CENTER_OPT 진입. CH3 flicker 심한 자세에서 무한 대기 방지.
|
||||
- **Soft hint**: 12 프레임 (~3초) 이상 반복되면 "↑ 위로" → "CH3 신호 확인 중 · 위치 유지"
|
||||
로 문구 자동 완화.
|
||||
- **6단계 완료 후 GREEN 5초 hold**: 사용자가 "측정 시작" 버튼 누를 시간 확보.
|
||||
|
||||
### If Alignment is Lost
|
||||
- If the sensor shifts significantly after reaching "In position!":
|
||||
- The app allows up to **3 consecutive failures** before exiting GREEN
|
||||
- After 3 failures: **"Position lost. Restart above pubic bone."**
|
||||
- The scanning will stop automatically
|
||||
- Re-place the sensor and tap **Start Alignment** again
|
||||
- Green Zone 유지 중 실패해도 즉시 리셋 X — **10 consecutive failures** (약 5초+)
|
||||
까지 관대하게 유지 (2026-07-07 완화, 이전 3-strike → 10-strike). 이 사이는
|
||||
GREEN 유지 + "최적의 위치입니다!" 표시.
|
||||
- 10회 연속 실패: **"위치를 놓쳤습니다. 치골 위에서 다시 시작해주세요"** →
|
||||
자동 재시작 대기.
|
||||
|
||||
### If the Sensor is Detached
|
||||
- If the sensor loses contact with skin:
|
||||
|
||||
@@ -2,8 +2,73 @@
|
||||
|
||||
## 작성일: 2026-04-23
|
||||
## 작성자: dwjang
|
||||
## 마지막 업데이트: 2026-05-26 (v6) — 1.0.0-design (versionCode 24)
|
||||
## 권장 펌웨어: **VBTFW0116** (pending slot 1→8 확장으로 ADC drop 안정)
|
||||
## 마지막 업데이트: 2026-07-10 (v7) — 1.2.0-demo (versionCode 26)
|
||||
## 권장 펌웨어: **VBTFW0120+** (mim FIFO 지원 필수. VBTFW0121 은 `mls mode 0` handler 취약)
|
||||
|
||||
### v7 주요 변경 요약 (2026-07-06 ~ 2026-07-10)
|
||||
|
||||
**BLE / Firmware 대응**
|
||||
- **`msp` 완전 제거 → `mim` (FIFO 15 sample) 로 통일** (커밋 34ae4d7/deae8a7/9907931).
|
||||
신 firmware 는 mim 만 사용. `rsp:` 파서는 legacy 응답 대비 유지.
|
||||
- **mtb queue overrun freeze 3-layer fix** (커밋 9907931). 실측 로그에서 mtb 응답
|
||||
stream 중 `msn`/`mim` 이 끼어들어 firmware GATT queue 꼬임 → `mls mode 0`
|
||||
에서 완전 freeze 확인. 상세: [BLE_PROTOCOL_REFERENCE.md](docs/BLE_PROTOCOL_REFERENCE.md) §2.6.
|
||||
- `isMtbBusy` 프로퍼티 + battery/mim polling 시 skip
|
||||
- mtb 3초 timeout + `consecutiveMtbTimeouts` state
|
||||
- 3회 연속 실패 시 자동 `forceDisconnectAndReconnect()`
|
||||
- **Watchdog timeout 15초 → 25초 연장** (커밋 0383a21). 재연결 후 첫 RX 가 14초
|
||||
지연 도착하는 케이스 허용.
|
||||
- **좀비 세션 방지**: `onConnectionStateChanged` 콜백을 CCCD write 완료 시점으로
|
||||
이동 (BleManager). `fwFallbackTimer` / `cccdRetryTimer` / `mtbTimeoutRunnable`
|
||||
disconnect 시 취소.
|
||||
- **Auto scan 명령 `maa` → `mtb` 로 전환** (커밋 29b6ca5). maa 는 IMU 응답
|
||||
없음 (posture 갱신 X). mtb 는 reb×6 + raa + rim 이라 auto scan 중에도 posture
|
||||
실시간 업데이트.
|
||||
- **lastBleRxAt 갱신 확대** (커밋 3a41864). `processReceivedData` 진입점에서
|
||||
모든 유효 응답에 대해 갱신 → "기기 응답 없음" false-positive 배너 해소.
|
||||
|
||||
**Alignment (V2 6-stage)**
|
||||
- **CH3 flicker 무한 대기 방지** (커밋 e808b5b). VERTICAL_CLIMB 진입 조건을
|
||||
"3연속 hit" → "최근 6프레임 중 3회 hit" sliding window majority 로 완화.
|
||||
20 프레임 대기 시 상단 3채널 relaxed mode 진입 (CH3 없이도 정렬 완료).
|
||||
12 프레임 반복 시 문구 자동 완화 ("↑ 위로" → "CH3 확인 중").
|
||||
- **6단계 완료 후 "측정 시작" 버튼 활성화 fix** (커밋 49aab7b). 기존
|
||||
`phase == LR_BALANCE` 조건이 실제 완료 phase (`FINAL_CONFIRM`) 를 못 잡아
|
||||
버튼 disable 되던 버그. `state=="commit" && phase==FINAL_CONFIRM` 로 수정.
|
||||
- **GREEN 진입 후 5초 hold** (커밋 159116d). V2 에도 V1 처럼 hold 로직 추가.
|
||||
순간 flick 으로 GREEN 즉시 풀리는 UX 문제 해소.
|
||||
- **GREEN zone 조기 리셋 완화** (커밋 7484077). V1 의 3-strike → **10-strike**
|
||||
(약 5초+). 미세 자세 흔들림으로 "0/3 정렬시작" 리셋되던 버그 완화.
|
||||
- **문구 개선**: "제 위치입니다!" → **"최적의 위치입니다!"** / "In position!"
|
||||
→ "Optimal position!" (커밋 7484077).
|
||||
|
||||
**UI / UX**
|
||||
- **Posture 라벨**: "서있음" → **"일어남"** (`piezo_posture_upright`, 한글).
|
||||
영문 "Upright" 유지.
|
||||
- **도넛차트 posture chip stuck 버그** (커밋 149e511/3abc024): `imuCollector.onComplete`
|
||||
콜백이 DisposableEffect race 로 즉시 null 되던 문제. 세 화면
|
||||
(PiezoMonitoring / PlacementGuide / ClinicalLive) 의 DisposableEffect 에서
|
||||
콜백 null 정리 제거. stale 콜백은 다음 화면 진입 시 자기 콜백으로 덮어씀.
|
||||
|
||||
**Clinical 세션 / labdb**
|
||||
- **labdb 자동 업로드 강화** (커밋 db6e7e1/93b8650/8c17407):
|
||||
- `endMeasurement` 자동 업로드 조건 완화 (`isRegistered` 만 체크, `lastStatus == "active"` 조건 제거)
|
||||
- 신규 `LabdbAutoRetry` object — ClinicalHome 진입 시 미업로드 폴더 자동 재시도
|
||||
- 상단 배너 UI (진행 중 / 완료 결과)
|
||||
- **KnownDeviceStore clinical flow 제한 제거** (커밋 b822b49). 일반 모드에서도
|
||||
연결한 기기 자동 저장 + DeviceScan 상단에 표시.
|
||||
|
||||
**Python tools**
|
||||
- **`tools/labdb_upload.py` 신규** — LabdbUploader.kt 의 `buildPayload` 로직을
|
||||
Python 으로 이식. 외부 저장 세션 폴더 일괄 업로드용.
|
||||
자세한 사용법: [tools/README.md](tools/README.md).
|
||||
|
||||
**참고**
|
||||
- 이번 세션에서 발견되었으나 아직 미조치: PiezoMonitoringView (2900L) /
|
||||
PlacementGuideView (2200L) 대형 파일 분해, Design Token 중앙화, ViewModel
|
||||
도입 등 아키텍처 개선 사항은 별도 로드맵으로 진행 예정.
|
||||
|
||||
### v6 주요 변경 요약 (2026-05-12 ~ 2026-05-26)
|
||||
|
||||
### v6 주요 변경 요약 (2026-05-12 ~ 2026-05-26)
|
||||
|
||||
|
||||
@@ -96,10 +96,49 @@ VesiScan Basic Android 앱이 VBT 디바이스(VBTFW0116+ 펌웨어)와 주고
|
||||
- 측정 중이었으면 측정 일시정지, 복구 시 사용자가 다시 시작
|
||||
|
||||
### 2.5 Watchdog (좀비 감지)
|
||||
파일: [`BleManager.kt:528+` watchdogJob](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L528)
|
||||
파일: [`BleManager.kt` watchdogJob](../app/src/main/java/com/medithings/vesiscan/ble/BleManager.kt)
|
||||
|
||||
- 별도 스레드로 30초 주기 RX 마지막 수신 시간 확인
|
||||
- N초 이상 무응답 + isConnected.value = true 라면 GATT 강제 reset → 재연결
|
||||
- 별도 스레드가 5초 tick 으로 RX 마지막 수신 시각 확인.
|
||||
- **2026-07-07**: watchdog timeout **15 → 25초** 연장. 실측 로그에서 재연결
|
||||
후 첫 RX 가 14초 지연 후 도착하는 케이스 확인 — peripheral / OS BLE
|
||||
스택의 좀비 회복 시간 허용. 15초로는 회복 직전에 forced reconnect 발동
|
||||
→ 무한 재연결 루프 문제.
|
||||
- Silence 10초~timeout 사이면 **heartbeat 발동**: 2026-07-08 부터 `msp` 대신
|
||||
`sendImuFifoQuery()` (mim). `isMtbBusy` 시 skip.
|
||||
- 정리는 UI thread 로 위임 (`handler.post`) — watchdog thread 에서 직접
|
||||
GATT close 하면 UI thread 의 sendRaw 와 race.
|
||||
|
||||
### 2.6 ⚠ 알려진 firmware freeze (VBTFW0121) — mtb queue overrun
|
||||
|
||||
**증상**: `mtb` 응답 stream (reb×6 + raa + rim) 진행 중에 다른 명령
|
||||
(`msn`, `mim`) 이 TX 로 끼어들면 firmware GATT queue 가 꼬여 이후 명령
|
||||
응답이 실종. 특히 그 상태에서 `mls mode 0` 이 결정타가 되어 BLE
|
||||
advertising 까지 죽는 **완전 freeze** 실측 확인 (2026-07-07 11:26 세션 로그).
|
||||
|
||||
**앱 측 3-layer 회피** (2026-07-08 커밋 9907931):
|
||||
|
||||
**Layer 1 — BleManager gating (`isMtbBusy`)**
|
||||
```kotlin
|
||||
val isMtbBusy: Boolean
|
||||
get() = piezoCollector.isMultiChannel && !piezoCollector.isComplete
|
||||
```
|
||||
- `batteryTimer` / `batteryRetryTimer`: `sendBatteryQuery` 앞에 skip
|
||||
- Watchdog silence heartbeat: `sendImuFifoQuery` 앞에 skip
|
||||
- 상위 (View 레이어) 도 `mim` 폴링 앞에서 `isMtbBusy` 체크
|
||||
|
||||
**Layer 2 — mtb 3초 timeout**
|
||||
- `sendMtb()` 후 3초 timeout runnable, `raa` 응답 오면 취소.
|
||||
- Timeout 시 `consecutiveMtbTimeouts` 증가 + `piezoCollector.reset()` /
|
||||
`imuCollector.reset()` 해제.
|
||||
|
||||
**Layer 3 — UI 안내 + 자동 재연결**
|
||||
- `PlacementGuideView` 가 `consecutiveMtbTimeouts` 관찰.
|
||||
- 1~2회: `"기기 응답 지연"` 배너
|
||||
- **3회 연속: `forceDisconnectAndReconnect()` 자동 호출** — 사용자가 앱을
|
||||
방치해도 자동 복구.
|
||||
|
||||
**Firmware 팀 리포트 대상**: VBTFW0121 의 `mls mode 0` handler 가 큐
|
||||
스트레스 상태에서 취약. 앱 fix 로 회피는 되지만 근본은 firmware.
|
||||
|
||||
---
|
||||
|
||||
@@ -164,7 +203,8 @@ fun verify(data: ByteArray): Boolean
|
||||
| `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 모드 변경 |
|
||||
| `msp?` | ASCII " " | `sendImuQuery()` ([L475](../app/src/main/java/com/example/medilightv2android/ble/BleManager.kt#L475)) | `rsp:` | IMU 단일 샘플 조회 |
|
||||
| ~~`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)
|
||||
|
||||
|
||||
@@ -1512,6 +1512,128 @@ async function upload(key, session) {
|
||||
|
||||
|
||||
|
||||
\## 12b. 앱 측 Auto Retry Policy (Android app, 2026-07-09)
|
||||
|
||||
|
||||
|
||||
Android 앱 (VesiScan-Basic demo-final / tab-navigation / cloud-mvp) 은 매
|
||||
|
||||
session 마다 사용자가 Upload 버튼을 누르지 않아도 되도록 **자동 업로드 +
|
||||
|
||||
재시도 큐** 를 내장합니다.
|
||||
|
||||
|
||||
|
||||
\### 12b.1 endMeasurement 자동 업로드
|
||||
|
||||
|
||||
|
||||
Clinical 세션 종료 시 (`ClinicalSessionStore.endMeasurement()`) `apiKey` 가
|
||||
|
||||
등록되어 있으면 (`LabdbCredentials.isRegistered`) 무조건 `LabdbUploader.
|
||||
|
||||
uploadAsync(context, folder)` 를 호출. **fire-and-forget** 이므로 응답 대기
|
||||
|
||||
없이 다음 화면으로 진행.
|
||||
|
||||
|
||||
|
||||
\- 이전 정책: `lastStatus == "active"` 조건 필요 → 상태 갱신 안 된 첫 진입 시
|
||||
|
||||
자동 업로드 skip 되던 문제.
|
||||
|
||||
\- 2026-07-09 이후: `isRegistered` 만 체크. 서버가 401/403 을 반환하면 상위
|
||||
|
||||
로직에서 status 재조회.
|
||||
|
||||
|
||||
|
||||
\### 12b.2 LabdbAutoRetry object (`app/src/main/java/.../labdb/LabdbAutoRetry.kt`)
|
||||
|
||||
|
||||
|
||||
Clinical Home 진입 시 (`LaunchedEffect(Unit)`) `retryPending(context)` 자동
|
||||
|
||||
호출. 로직:
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
1. LabdbCredentials.isRegistered 확인 → 없으면 no-op
|
||||
|
||||
2. ClinicalSessionStore.recentFinalizedFolders (tab-nav/cloud-mvp) 또는
|
||||
|
||||
listOfNotNull(lastFinalizedFolder) (demo-final) 스캔
|
||||
|
||||
3. labdb_upload.json 마커 없는 폴더만 pending
|
||||
|
||||
4. 순차 (병렬 X) LabdbUploader.uploadBlocking 실행
|
||||
|
||||
5. 결과: labdb_upload.json (성공) / labdb_upload_error.json (실패)
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
\### 12b.3 UI 배너
|
||||
|
||||
|
||||
|
||||
Clinical Home 상단:
|
||||
|
||||
\- 진행 중 (`pendingCount > 0` 또는 `uploadingName != null`):
|
||||
|
||||
파란 배너 "자동 업로드 진행 중 · 남은 세션 N · <folderName>"
|
||||
|
||||
\- 완료 (`lastResultMessage != null`):
|
||||
|
||||
초록/주황 배너 "N uploaded, M failed" + 확인 버튼
|
||||
|
||||
|
||||
|
||||
\### 12b.4 State 노출
|
||||
|
||||
|
||||
|
||||
```kotlin
|
||||
|
||||
object LabdbAutoRetry {
|
||||
|
||||
val pendingCount: MutableIntState // UI 배너 카운트
|
||||
|
||||
val uploadingName: MutableState<String?> // 현재 업로드 중 폴더
|
||||
|
||||
val lastResultMessage: MutableState<String?> // 완료 후 결과 요약
|
||||
|
||||
fun retryPending(context: Context) // 트리거
|
||||
|
||||
fun dismissResult() // 배너 확인
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
\### 12b.5 재시도 안전성
|
||||
|
||||
|
||||
|
||||
\- 이미 업로드된 폴더 (`labdb_upload.json` 있음) 는 skip
|
||||
|
||||
\- 실패 폴더는 `labdb_upload_error.json` 만 남기고 다음 진입 시 재시도
|
||||
|
||||
\- 네트워크 순간 이슈 → 다음 진입에서 자동 복구
|
||||
|
||||
\- 앱 종료 후 재실행 → `ensureHistoryLoaded` 로 recent 폴더 복원 → 재시도
|
||||
|
||||
|
||||
|
||||
\---
|
||||
|
||||
|
||||
|
||||
\## 13. 지원
|
||||
|
||||
|
||||
|
||||
@@ -75,3 +75,98 @@ scan_id, timestamp, ..., volume_ml, lr_ratio, ..., channel, s0..s99
|
||||
2. **알고리즘 변경 검증**: 새 fix 후 같은 CSV 로 `--ablation` 돌려 회귀 여부 확인
|
||||
3. **Device-to-device variance**: 두 device 의 같은 phantom 측정 → CV 비교
|
||||
4. **lr_ratio 패턴**: human cohort 의 `--plot` 으로 lr 분포 시각화
|
||||
|
||||
---
|
||||
|
||||
## labdb_upload.py — 세션 폴더 일괄 업로드
|
||||
|
||||
앱이 저장한 세션 폴더 (`measurement_<name>.json` 포함) 를 labdb REST API
|
||||
로 일괄 업로드. 앱 안의 `LabdbUploader.kt` (buildPayload) 로직을 그대로
|
||||
Python 으로 옮긴 것으로, 폰이 없는 상황에서 데스크톱에 백업한 데이터를
|
||||
바로 서버로 넣을 때 사용.
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
# 별도 의존성 없음 (표준 라이브러리만 사용)
|
||||
export LABDB_API_KEY=xbk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
apiKey 는 labdb admin 콘솔 또는 backend 담당자로부터 발급.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
# 기본 — 폴더 안의 모든 세션 업로드
|
||||
python tools/labdb_upload.py "C:/Users/장동우/Desktop/newdata"
|
||||
|
||||
# apiKey 를 명령줄로 전달
|
||||
python tools/labdb_upload.py ./sessions --api-key xbk_live_xxx...
|
||||
|
||||
# dry-run — 전송하지 않고 payload 검증만
|
||||
python tools/labdb_upload.py ./sessions --dry-run
|
||||
|
||||
# 이미 업로드된 폴더도 재전송 (labdb_upload.json 마커 무시)
|
||||
python tools/labdb_upload.py ./sessions --force
|
||||
|
||||
# meta.data_type 없을 때 사용할 fallback (기본 "001")
|
||||
python tools/labdb_upload.py ./sessions --data-type 002
|
||||
```
|
||||
|
||||
### 폴더 구조 요구사항
|
||||
|
||||
각 세션 폴더는 앱이 만든 표준 형식:
|
||||
|
||||
```
|
||||
<folder>/
|
||||
meta_<folder>.json
|
||||
measurement_<folder>.json ← 이걸 payload 로 변환
|
||||
adc.csv
|
||||
imu.csv
|
||||
ble.log
|
||||
labdb_upload.json ← 성공 시 생성 (sessionId, inserted 개수 등)
|
||||
labdb_upload_error.json ← 실패 시 생성 (다음 실행 시 참고)
|
||||
```
|
||||
|
||||
`measurement.json` 또는 `measurement_<folder>.json` 이 있으면 대상. 없으면
|
||||
"measurement json not found" 로 실패.
|
||||
|
||||
### 재실행 안전성
|
||||
|
||||
- 이미 업로드된 폴더 (`labdb_upload.json` 있음) 는 자동 skip
|
||||
- `--force` 로 강제 재전송 가능
|
||||
- 실패한 폴더는 다음 실행 시 자동 재시도
|
||||
|
||||
### 출력 예시
|
||||
|
||||
```
|
||||
[OK ] HUMAN-kai_VBT26040302_SITTING_ALIGN_0CM_2026-07-08_110903: uploaded: 130/130 (dup=0)
|
||||
[OK ] HUMAN-kai_VBT26040302_SITTING_ALIGN_1CM_2026-07-08_111049: uploaded: 3/3 (dup=0)
|
||||
[FAIL] HUMAN-kai_VBT26050202_SITTING_ALIGN_2CM_2026-07-08_161928: upload fail: HTTP 401
|
||||
[OK ] ... (skip already uploaded)
|
||||
|
||||
summary: 19 uploaded, 0 skipped, 1 failed (total 20)
|
||||
```
|
||||
|
||||
### buildPayload 로직 (LabdbUploader.kt 와 동일)
|
||||
|
||||
- **testId**: 폴더명 sanitize (`^[A-Za-z0-9_-]{1,40}$`). 40자 초과 시 앞
|
||||
31자 + `_` + 8자 SHA-1 prefix 로 축약 (uniqueness 유지).
|
||||
- **dataType**: `meta.data_type` 우선, 없으면 `--data-type` fallback.
|
||||
- **records[]**: `measurement.json` 의 각 cycle → labdb record 로 변환:
|
||||
- `sensor.imu`: 마지막 (가장 최근) IMU sample
|
||||
- `sensor.imu_samples`: 전체 시계열
|
||||
- `channels[0..5]`: peak / peakIdx / data 배열
|
||||
- alignment session 은 align_phase / align_score / align_hint 등 부가 필드.
|
||||
- **params**: posture / step / is_alignment / examiner / firmware / hardware / subject 등
|
||||
meta 요약.
|
||||
|
||||
### 활용 예
|
||||
|
||||
1. **개발용 백업 재전송**: 폰이 없는 상황에서 데스크톱에 백업한 세션 폴더를
|
||||
labdb 로 일괄 업로드.
|
||||
2. **자동 재시도 실패 복구**: 앱의 `LabdbAutoRetry` 도 실패한 케이스 (예:
|
||||
apiKey 만료) → apiKey 갱신 후 스크립트로 일괄 재전송.
|
||||
3. **다른 폰 → labdb 이관**: A 폰에서 측정 → `Downloads/VesiScan_Sessions/`
|
||||
폴더 → PC 로 복사 → 스크립트로 서버 반영.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user