Commit Graph

15 Commits

Author SHA1 Message Date
dw.jang 41ca909888 fix(alignment): 0/6 walls sanity check · 부착 안 됐을 때 "위로 올려" false positive 방지
증상 (사용자 보고 2026-08-05):
  Alignment 모드에서 센서를 부착하지 않았는데 "↑ 위로 올려" 힌트가 계속 노출.

원인:
  DetachmentDetection 임계 (mean AND std 둘 다 < 30) 를 노이즈로 통과하면
  alignment 알고리즘이 그대로 실행됨. AlignmentAdvice 는 CH3 미검출을
  "misaligned" 로 해석 → MOVE_UP 반환. "detached" 케이스와 구분 못함.

수정 (Option B: sanity check + Option C: 명확 문구):
  PlacementGuideView V2 branch 에서 v2Aligner.push 결과의
    detected.count { it } == 0 (6채널 모두 wall 미검출) 감지 시
  alignment advice 무시하고 placement_check_attachment 힌트로 override.

  strings.xml (KR/EN):
    placement_check_attachment
      · KR: "부착 상태 확인 필요 — 모든 채널에서 신호가 감지되지 않습니다.
             센서를 치골 위에 밀착시켜 주세요."
      · EN: "Attachment check needed — no signal from any channel.
             Ensure sensor is firmly placed above pubic bone."

효과:
  - 노이즈만 있고 실제 부착 없는 경우 "위로 올려" 반복 대신 명확한 부착 안내
  - alignment 알고리즘 자체는 유지 (0/6 만 특별 처리 · 1/6 이상은 정상 흐름)
  - DetachmentDetection 임계는 그대로 (실제 부착·미부착 경계 케이스 영향 없음)

검증: BUILD SUCCESSFUL 17s
2026-08-05 16:25:45 +09:00
dw.jang 4eb67e7450 feat(firmware): 새 DFU zip (1.0.0+5) 번들 + 버전 불일치 시 업데이트 배너
DFU 파일:
  - dfu_application_2+35.zip (구) 삭제
  - dfu_application_1.0.0+5.zip (신규 238KB) 추가

FirmwarePackage:
  - DEFAULT_ASSET = "dfu_application_1.0.0+5.zip"
  - BUNDLED_VERSION = "1.0.0+5" 상수 신규 (manifest.json version_MCUBOOT 와 일치)

BleManager:
  - isFirmwareOutdated: 신규 semver 포맷 ("1.0.0+5") 은 legacy VBTFW build-number
    비교 제외 (false positive 방지 · parseFirmwareBuild 가 tail 숫자 5 를 118 미만
    으로 오판정 안 하도록).
  - isFirmwareUpdateAvailable (신규): 런타임 fw 문자열이 BUNDLED_VERSION 을 포함
    하지 않으면 true. 사용자에게 "새 펌웨어 사용 가능" 배너 노출.

FirmwareWarningBanner:
  - update-available 파랑 배너 신규 (정보성 · 우선순위 outdated > timedOut > update).
  - onUpdateAvailableTap 콜백 · 파랑 배너 탭 시 FIRMWARE_UPDATE 화면 이동.
  - PiezoMonitoringView · PlacementGuideView 호출부에 콜백 전달.

strings.xml (KR/EN):
  - firmware_update_available_title/desc

동작 흐름:
  1. 앱 연결 → mid?/rid: 로 fw 수신 (예: "VBTFW0200")
  2. isFirmwareUpdateAvailable → true (fw !contains "1.0.0+5")
  3. 상단 파랑 배너 "새 펌웨어가 있습니다 · 기기: VBTFW0200 · 신규: 1.0.0+5" 노출
  4. 탭 → FIRMWARE_UPDATE 화면 → DFU 진행 → 성공 시 fw 갱신 후 배너 자동 사라짐

향후 FW 팀이 버전 스키마 확정 시 BUNDLED_VERSION 만 갱신하면 됨.
2026-08-04 17:24:59 +09:00
dw.jang a992a88b86 feat(ble): unbondSmart() · 삭제 시 자동 재연결 + 오프라인 경고 팝업 (cloud-mvp 1982399 이식) 2026-08-04 17:03:38 +09:00
dw.jang 96895e5c08 feat(ble): R100 견고성 3종 · startup 지연 대응 + hang 안내 + 재연결 제한
배경 (2026-08-04 · VBT0607R100 로그 분석):
  - 연결 직후 첫 msn/mid 명령이 9초간 무응답 → CMDQ timeout 3회 · 사용자 노출
  - mpa → mtb 발사 후 GATT status=8 (link supervision timeout) · 기기 hang
  - 이후 재연결 5회+ 실패 · 기기가 air 상에 안 잡힘 (advertising 죽음)
  - FW 사이드 문제 · 앱 층에서 UX 완화 가능한 부분 개선

변경:
1. INITIAL_TX_DELAY_MS = 2_000ms — CCCD write 후 첫 명령 2초 지연
   R100 boot 늦은 케이스에서 초기 timeout 반복 방지. Watchdog 은 즉시 시작.

2. GATT status 8/19/22 별도 처리
   - status=8  GATT_CONN_TIMEOUT (link supervision timeout · FW hang)
   - status=19 GATT_CONN_TERMINATE_PEER_USER
   - status=22 GATT_CONN_TERMINATE_LOCAL_HOST
   → connectionError = "DEVICE_UNRESPONSIVE:\$status" marker
   → UI 에서 "전원 15초 롱프레스로 재부팅 후 다시 연결하세요" 안내
   Bond conflict (status=5) 는 기존대로 별도 처리 유지.

3. MAX_RECONNECT_ATTEMPTS 10 → 5
   실패 후 connectionError = "RECONNECT_GAVE_UP" marker + onReconnectionFailed
   → UI 에서 "기기가 켜져 있는지 확인 후 다시 시도" 안내
   무한 시도 로그 스팸 방지.

4. mtb_response_reconnect 문구 개선
   "연결을 재시도합니다" → "재부팅이 필요할 수 있습니다 (전원 15초 롱프레스)"
   3회 consecutive mtb timeout 시 사용자에게 더 actionable 안내.

strings.xml (KR/EN):
   - ble_error_device_unresponsive (재부팅 안내)
   - ble_error_reconnect_gave_up (재연결 포기 안내)
   - mtb_response_reconnect 갱신

FW 팀 조사와 병행. 로그 (2026-08-04_134048) FW 팀 전달 예정.
2026-08-04 14:12:22 +09:00
dw.jang fda9a4d97f feat(ble): GATT status=5 (FW allowlist bond conflict) UX (cloud-mvp 10c45dc 이식)
FW 팀이 1:1 bond allowlist 정책 추가 예정. 다른 central 과 이미 bond 중인
기기에 붙으면 GATT_INSUFFICIENT_AUTHENTICATION (status=5) 로 거부.

BleManager:
  - bondConflictAddress: MutableStateOf<String?> 신규
  - status=5 감지 시 bondConflictAddress=addr + connectionError="BOND_CONFLICT:$addr"
  - CONNECTED 성공 시 clear

DeviceScanView (ui/views/connection/):
  - 에러 배너 확장 (Column 2단):
    · 안내 문구 (기기 15초 롱프레스로 본딩 삭제)
    · "이 폰의 본딩 정보 초기화" OutlinedButton (unbondAndRemoveAddress)
  - Toast 완료 안내

strings.xml (KR/EN): ble_error_bond_conflict / ble_action_reset_bond / ble_toast_bond_removed
2026-08-04 10:54:41 +09:00
dw.jang 990793170e fix(ble): mtb 응답 중 다른 명령 끼어들어 firmware freeze — 3-layer 근본 fix
실측 로그 (2026-07-08 11:18~11:19) 분석: mtb 응답 stream (reb×6 + raa
+ rim, 총 8개 패킷) 중간에 msn (battery) / mim (IMU) 이 TX 로 끼어들면
firmware GATT queue 가 꼬여 응답 실종 → 이어지는 mls mode 0 명령에서
완전 freeze (BLE 광고까지 중단, 재연결 10회 모두 실패).

핵심 원인:
- BleManager 의 batteryTimer / mim polling 이 시간 간격 기반이라 mtb
  응답 진행 중에도 무조건 TX 발사.
- 특히 sendImuFifoQuery 는 imuCollector.reset() 을 먼저 호출 → mtb 의
  rim (IMU) 이 파괴됨.

Layer 1 — BleManager gating
  - isMtbBusy 프로퍼티 추가 (piezoCollector.isMultiChannel && !isComplete)
  - batteryTimer / batteryRetryTimer: sendBatteryQuery 앞에 isMtbBusy skip
  - Watchdog silence heartbeat: sendImuQuery (msp) → sendImuFifoQuery
    (mim) 로 교체 + isMtbBusy skip
  - sendImuQuery 함수 자체 삭제 — msp 명령 코드에서 완전 제거
    (rsp 파서는 legacy 응답용 유지)

Layer 2 — PiezoMonitoringView mim polling gating
  - LaunchedEffect while 루프의 sendImuFifoQuery 앞에 isMtbBusy skip

Layer 3 — mtb 3초 timeout + UI 안내
  - sendMtb: 3초 timeout runnable, raa 응답 오면 취소
  - lastMtbTimeoutAt / consecutiveMtbTimeouts state 노출
  - PlacementGuideView 가 관찰 → 1~2회: "기기 응답 지연" 안내
    3회 연속: "재연결" 안내 + forceDisconnectAndReconnect 자동 호출
  - forceDisconnectAndReconnect 를 public 으로 승격

부가:
- disconnect 3곳 (GATT_ERROR / STATE_DISCONNECTED / watchdog forced)
  에서 mtbTimeoutRunnable 도 함께 정리.

Firmware VBTFW0121 의 mls mode 0 handler 취약점은 별도로 펌웨어 팀에
리포트 필요 (본 fix 는 큐 혼잡 방지로 근본적으로 회피).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-08 15:03:48 +09:00
dw.jang 2172304e50 fix(align): GREEN zone 조기 리셋 완화 + 문구 개선
증상:
1. Sensor Alignment 완료 GREEN 진입 후 일정 시간 (약 7초 hold 종료 +
   추가 몇 초) 지나면 UI 가 갑자기 "단계 0/3 / 정렬 시작" 으로
   초기화되어 처음부터 재시작해야 하는 불쾌한 UX.
2. GREEN 문구 "제 위치입니다!" 가 다소 어색.

원인:
PlacementGuideView V1 로직 (line 1098) 의 3-strike 규칙 —
`greenHeld` (7초 hold) 종료 후 `guideResult.isPass=false` 가 **3회
연속** 나오면 전체 리셋 (isLocked=false, waitingForStart=true,
scanCount=0, phase=VERTICAL, rTracker.reset() ...). 그런데 사용자가
GREEN 상태에서 버튼 안 누르고 그대로 기다리면 자연스러운 미세 자세
흔들림 (1~2초) 만으로 3회 실패 성립 → 완전 리셋.

수정:
1. `placement_in_position` "제 위치입니다!" → "최적의 위치입니다!"
   (영문: "In position!" → "Optimal position!").
2. 3-strike → 10-strike 완화. 10회 연속 실패 (약 5초+) 는 되어야
   진짜 이탈로 간주. 그 사이는 GREEN 유지 + "최적의 위치입니다" 표시.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-08 09:14:11 +09:00
dw.jang e808b5bc57 fix(align): CH3 flicker 무한 대기 방지 — sliding window majority + relaxed mode
인체 데이터 (data123/) 분석 결과 CH3 (-13.66° down-tilt) 검출률이
Phantom 95.2% → Human 31.8% 로 급락하며 YYNNYY flick 패턴 관측.
기존 "3연속 hit" 진입 조건이 무한 대기에 빠져 사용자에게
"↑ 위로 조금씩" 문구만 반복 노출되는 문제를 해결.

- VERTICAL_CLIMB 진입: 3연속 → 최근 6프레임 중 3회 (majority)
- CH3_STABILIZE / CENTER_OPTIMIZE lost 판정: 4프레임 중 3회 (majority)
- Stuck detection (20프레임 ≈ 5초 대기 시): 상단 3채널 relaxed mode
  진입 → CH3 없이도 정렬 완료 진행
- Soft hint (12프레임 ≈ 3초 후): "CH3 확인 중 · 위치 유지/미세 조정"
  으로 문구 완화 (사용자 답답함 완화)
- AdvisorState.relaxedMode 플래그 추가 (후속 저장 로직 대비)

Python replay (VBT26050202_0CM 22프레임) 로 검증:
- LEGACY: VERTICAL 무한 대기 → 정렬 실패
- NEW: soft-hint → threshold 도달 시 relaxed → 3프레임 만에 LR_BAL 도달

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-06 17:45:14 +09:00
dw.jang 6cff741370 feat(locale): DeviceScanView 한글화 — 기기 연결 페이지
이전 sweep 에서 누락됐던 두번째 '기기 연결' 화면 풀 한글화.

추가 strings: 25 신규 (각 언어)
- '기기 연결' 제목, 검색/탭 안내
- 페어링 필요 알림 (다이얼로그 + 에러 메시지)
- Bluetooth off / 권한 필요 경고
- 검색 중 / 기기 없음 / 트러블슈팅 4단계
- 저장된 기기 / 새 기기 섹션
- 권한 허용 / 검색 시작·중지 버튼
- 최근, 페어링 삭제, 신호 좋음/보통/약함

설계:
- signalLabel(rssi) → 영어 — dev/log 용 유지
- signalLabelRes(rssi) 신규 — UI 용 Locale-aware 라벨

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-29 11:31:50 +09:00
dw.jang 3b37c816fc fix(demo): 정렬모드 Skip top bar 우측 노출 + 개인화 페이지 한글화
1. PlacementGuide top bar 에 Skip TextButton 추가
   - 기존: scrollable Column 맨 아래 → 스크롤 안에 묻혀 안 보이던 문제
   - 변경: top bar 우측 (Back / Ready / Skip / [dev] Settings 순) 항상 가시
   - 동작: clinical alignment → endMeasurement + CLINICAL_HOME / 일반 → placementComplete

2. PiezoPersonalizationView (개인화 페이지) 한글화
   - 'Maximum Bladder Capacity' → '최대 방광 용량'
   - 'Voiding Reminder' → '배뇨 알림 기준'
   - 'Auto Scan Interval' → '자동 측정 주기' (기존 key 재사용)
   - 'Continue' → '계속' (기존 calibration_continue 재사용)
   - 'Back' (contentDescription) → '뒤로'

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-29 11:23:11 +09:00
dw.jang 59d4ac0e81 feat(locale): Phase 3 — 전수 sweep, 노인 사용자 기준 영어 잔여 텍스트 한글화
이전 Phase 2 에서 'already English-only' 라며 skip 했던 화면들 — 노인 사용자에게는 영어로
보이면 안 됨. 전체 다시 훑어 모든 user-visible 영어 hardcoded text 를 strings.xml 로 추출.

11 파일 수정:
- MainActivity (Exit dialog)
- HomeView (Smart Bladder Monitoring / Start / Connected / Clinical Mode / dev toast)
- OnboardingView (Welcome, Get Started 등)
- CalibrationView (모든 status + Retry/Skip/Continue)
- SensorSelectView (title, descriptions)
- ShuffledKeypad (Delete contentDescription)
- MeasurementHistoryView (title, tabs, empty state, Today/Yesterday)
- VivamyoMonitoringView (전체 status)
- PiezoMonitoringView (사용자 영역 — Auto/Single Scan, Voiding/Catheterization, 측정 카드, dialog 등)
- PlacementGuideView (V1 hints, Step indicator, Start/Skip/Complete Alignment, In position/Position lost)

추출 strings: ~170 (각 언어). strings.xml 194 → 365 라인.

skip 유지:
- ClinicalHomeView / ClinicalLiveView (R&D dev mode)
- PiezoMonitoring dev panel sliders (3-tap 활성화 dev 도구)
- PlacementGuide dev settings overlay + Echo Waveforms (isDevMode gated)
- BLE Debug Log / Scan Log dialog (dev 전용)
- 브랜드명 (VesiScan-Basic, Vivamyo, Bladdy)
- DEMO 배지, IMU debug 문자열, Log.d / debugLogger 출력

Korean coverage: ~95% (일반 사용자가 절대 보지 않는 dev surface 만 영어 유지).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-29 09:17:23 +09:00
dw.jang 2cf4f4db2f feat(locale): Phase 2 — 모든 사용자 노출 Korean 문자열 → strings.xml 추출 + 영문 번역
Sub-agent 작업 결과:
- values/strings.xml + values-ko/strings.xml 각각 71 → 194 줄 (약 85 신규 string)
- 17 파일 수정 (UI views 9개, components 3개, managers 1개, services 2개)

상세:
- ui/views/{home, consent, registration, reminder, monitoring (3), personalization, connection}
- components/{BatteryWarningBanner, FirmwareWarningBanner, LockableSlider}
- managers/AlignmentAdvisorV2 — Out/AdvisorState 에 msgKey + msgArgs 추가 (UI 가 context.getString 으로 localize)
- services/{ReminderService, BleForegroundService}

의도적으로 skip:
- ClinicalHomeView / ClinicalLiveView (R&D-only dev mode)
- PiezoMonitoring dev panel labels (dev 도구)
- AlignmentAdvice / 2-phase AlignGuide (UI 미사용)
- 이미 영어인 파일들 (Onboarding, Pin, SensorSelect, Calibration 등)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-26 17:27:05 +09:00
dw.jang cc00c25a66 feat: UI 일괄 수정 (9개 항목 중 1,2,3,4,9번)
1+2. Placement peak 감도: urineLen >= 10 필터 (짧은 false positive 방지)
3. Placement 센서 그림(SensorLayoutCanvas) 제거
4. 앱 이름: MediLight → VesiScan-Basic (홈화면 + strings.xml)
9. BLE 신호: Good+Excellent 통합 (rssi >= -65 → Green 4칸)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 11:30:45 +09:00
dwjang dc61e1d52e i18n: add Korean/English localization via strings.xml
Replace hardcoded Korean UI strings in Piezo, CatheterizeSheet,
VoidingDiary, and UrineCameraScreen with stringResource() calls.
Add values/strings.xml (English) and values-ko/strings.xml (Korean).
System language setting now controls app language.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 11:27:32 +09:00
dw.jang a8532917b4 Initial commit: VesiscanBasic Android app
Port of MedilightV2 iOS app to Android with Kotlin + Jetpack Compose.
Includes BLE connectivity, NIRS/Piezo sensor modes, Rive animations,
interactive measurement history charts, and foldable device support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:27:00 +09:00