Commit Graph

16 Commits

Author SHA1 Message Date
dw.jang c7cddbf9c9 fix(clinical): 조건 완료 판정을 실행 단위 → 조합 단위 합산으로
화면에서 6조합 중 일부만 골라 돌릴 수 있게 되면서(다음 커밋) 판정이 깨진다. 종전에는
실행 하나만 보고 "6조합이 다 있나"를 따졌는데, 나눠 재면 어느 실행도 6개를 못 채워
**영원히 PARTIAL** 로 남는다. 그러면 "덜 됐다"는 표시가 의미를 잃는다.

같은 자세·충만도의 여러 실행을 조합 단위로 합산한다. 조합 하나가 여러 실행에 나오면
한 번이라도 계획을 채운 실행이 있으면 그 조합은 끝난 것으로 본다 — 20회 중 3회에서
끊긴 뒤 다시 20회를 채웠다면 완료다.

판정 전부를 progressFrom(manifests) 순수 함수로 떼어 시험으로 고정했다. 이 판정이
틀리면 간호사가 다 됐다고 믿고 방광을 비우는데 데이터는 못 쓰고, 그 단계는 그날 다시
못 잰다. 실기기 없이도 고정해 둬야 한다.

  나눠 재기 · 재측정 · 계획 미달 · planned 없는 옛 매니페스트 · 깨진 JSON ·
  조건 간 혼선 8건.

org.json 은 안드로이드 단위테스트에서 모든 메서드가 스텁이라(isReturnDefaultValues
로도 안 된다 — 반환값이 아니라 동작이 없다) 실제 구현을 테스트 의존성으로 넣었다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 15:50:29 +09:00
dw.jang eb2a0b19e7 feat(clinical): 연결 해제 버튼 · 프리셋 전환 시 dps 즉시 동기화
실리콘 R100/R200/R300 을 번갈아 쓰려면 한 기기를 끊고 다음을 잡아야 하는데
끊을 길이 없었다. 연결돼 있으면 버튼이 "연결 해제"로 바뀐다(병원 임상 모드 ·
정렬 화면 양쪽). disconnect() 가 자동 재연결도 취소하므로 방금 끊은 기기로
되돌아가지 않는다.

기기를 바꾸면 **정렬 결과를 지운다.** R100/R200/R300 은 빔 각도가 서로 달라
(0/-7/-14/-21 vs 5/-4/-12/-21 vs 0/-9/-18/-27) 같은 자리에서도 nch·cap_frac 이
달라진다 — 최적 부착 위치 자체가 다르므로 앞 기기의 cm 을 들고 가면 안 된다.

## dps 동기화 버그
`WdConfig.DPS_DEFAULT` 는 검출 함수들의 기본 인자인데, 이전 커밋에서 그 값을
`PiezoHW.distancePerSample` **getter 안에서만** 갱신하게 만들었다. 프리셋이 바뀐 뒤
아무도 그 프로퍼티를 읽지 않으면 옛 값이 남는다 — 실리콘으로 갈아 끼웠는데 검출은
abs 의 1.936 으로 도는 상황이 정확히 그것이다. activePreset setter 에서 동기화한다.

PresetDpsTest 신규: 프리셋 → dps·delay 표(v*=1.936/6.85, r*=1.897/7.651)와
WdConfig 동기화, 그리고 기기 이름 → 프리셋 매핑 6종을 검증한다.

testOptions.unitTests.isReturnDefaultValues=true — android.util.Log 스텁 때문에
순수 계산 시험이 죽던 것을 막는다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 11:38:24 +09:00
dw.jang 5f40896bee build: 앱 이름을 vesiscan-demo 로 · applicationId 충돌 제거
이 브랜치는 동결 시연판이다. 이제 앱이 셋으로 늘어 한 폰에 같이 깔리므로, 이름과
applicationId 를 1:1 로 못 박아 무엇이 무엇인지 헷갈리지 않게 한다.

  vesiscan-demo       com.medithings.vesiscan.demo        ← 이 브랜치
  vesiscan-user       com.medithings.vesiscan             ← vesiscan_pre_product
  vesiscan-caregiver  com.medithings.vesiscan.caregiver   ← vesiscan_pre_product

## 이름
"VesiScan-Basic Demo"(demo) · "VesiScan Dev"(dev) 를 각각 vesiscan-demo ·
vesiscan-demo-dev 로. 예전 이름은 **무슨 앱인지가 아니라 어떤 빌드인지**만 알려 줘서,
폰에 여러 개가 깔리면 구별할 수 없었다.

## applicationId — 이게 진짜 문제였다
base 가 `com.medithings.vesiscan` 이고 demo 플레이버가 `.demo` 를 붙이는 구조라,
**dev 플레이버는 접미가 없어 `com.medithings.vesiscan`** 이었다. 그 id 는 새 저장소의
환자 앱과 정확히 같다 — 이 브랜치에서 devDebug 를 한 번 빌드해 깔면 환자 앱이
말없이 덮어써진다. 이름을 아무리 잘 붙여도 막을 수 없는 종류의 사고다.

base 를 `com.medithings.vesiscan.demo` 로 옮기고(=demo 빌드의 id 는 그대로),
demo 는 접미 없음, dev 는 `.dev` 를 붙인다. 이제 어떤 플레이버를 빌드해도 다른 앱을
건드리지 않는다.

검증: aapt2 dump badging — package `com.medithings.vesiscan.demo` ·
label `vesiscan-demo` · versionName `2.0.0-demo`. assembleDemoDebug PASS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 10:31:01 +09:00
dw.jang 26dc53b4eb chore(cleanup): YOLO/CameraX/OCR 세트 완전 제거 (Category B · 사용자 요청)
배경: 소변컵 사진 촬영 → YOLO 검출 → MLKit OCR 눈금 인식 기능 폐기.
현재 미사용. Piezo 방광 측정 flow 만 실사용.

완전 제거 (4 파일 + 1 asset):
  · measure/YoloDetector.kt (130 L · ONNX YOLO)
  · measure/SimpleMeasureService.kt (287 L · MLKit OCR + 3-zone)
  · measure/CCPosition.kt (8 L)
  · ui/views/monitoring/UrineCameraScreen.kt (285 L · CameraX 프리뷰)
  · assets/urinecup_best.onnx (~10.6 MB · APK 크기 절감)

Gradle deps 삭제 (build.gradle.kts):
  · com.microsoft.onnxruntime:onnxruntime-android:1.17.0
  · com.google.mlkit:text-recognition:16.0.1
  · androidx.camera:camera-{core,camera2,lifecycle,view}:1.3.4

AndroidManifest.xml 삭제:
  · CAMERA permission
  · android.hardware.camera uses-feature

부분 편집:
  · AppState.kt        — URINE_CAMERA enum value 삭제
  · MainActivity.kt    — import + back-handler branch + Crossfade branch 삭제
  · PiezoMonitoringView.kt — showUrineCamera state · 카메라 카드 (16L) ·
                             LaunchedEffect(showUrineCamera) 삭제
  · strings.xml × 2    — 카메라 전용 문자열 15개 삭제
                        (camera_input 은 CatheterizeSheet 사용 중이라 유지)

측정용 VoidingRecord.kt 는 유지 (CatheterizeSheet · VoidingDiaryView 등에서 사용).
okhttp 는 speech/HfVolumeExtractor 에서 사용 중이라 유지.

합계: 약 780 LOC + 10.6 MB APK 크기 절감.
빌드: BUILD SUCCESSFUL 30s (첫 시도 통과).
2026-08-12 15:49:27 +09:00
dw.jang 395e908a01 chore(version): 2.0.0 (versionCode 200)
시작 화면 (HomeView) 에 표시되는 버전을 2.0.0 으로 갱신.
  · versionName: 1.1.1-design → 2.0.0
  · versionCode: 27 → 200 (2.0.0 semantic 매칭 · downgrade 방지)

demo flavor 는 -demo 접미사 그대로 → 실제 화면 표기: "2.0.0-demo".
빌드 통과 (:app:compileDemoDebugKotlin).
2026-08-10 17:36:26 +09:00
dw.jang 984fe2c939 feat(demo-final): cherry-pick — Firmware DFU + Battery dismiss + AppState fix (cloud-mvp 이식)
feature/cloud-mvp 에서 최근 만들어진 3가지 사용자용 기능을 demo-final 에도 적용.
demo-final 옛 폴더 구조 유지 (folder reorg 안 됨) · 각 파일 위치 수동 매핑.

이식 대상:

1. Firmware DFU (cloud-mvp 672e679):
   - firmware/ 폴더 신규 · MEDiDFU 6 파일 이식 (DfuManager 459 line 등)
   - assets/dfu_application_2+35.zip (243 KB · signed MCUboot)
   - libs.versions.toml: mcumgr = "2.5.0" (Kotlin 2.0 호환)
   - build.gradle.kts: mcumgr-ble + lifecycle-runtime-compose 추가
   - AppScreen.FIRMWARE_UPDATE enum 추가
   - MainActivity: 라우팅 + 뒤로가기 (→ HOME)
   - HomeView: "펌웨어 업데이트 (프로브 firmware)" TextButton 추가 (Dev clinical 버튼 밑)

2. BatteryWarningBanner tap-to-dismiss (cloud-mvp aa6cc1f):
   - clickable { dismissed = true } + 우측 X 아이콘
   - WARNING → CRITICAL 악화 시 자동 재표시
   - 재연결 시 dismissed 초기화

3. AppState.isDeviceConnected getter (cloud-mvp 9c82a50):
   - mutableStateOf → getter 로 변경 (BleManager.isConnected.value 실시간)
   - Compose 자동 재구성 · setter 는 no-op 로 호환 유지
   - 홈화면 "연결됨" · Start 후 "연결해제" 불일치 해결

주요 파일 매핑 (feature/cloud-mvp → demo-final):
- core/AppState.kt         → AppState.kt (root)
- core/MainActivity.kt     → MainActivity.kt (root)
- common/ui/components/BatteryWarningBanner.kt → ui/components/BatteryWarningBanner.kt
- firmware/**              → firmware/** (신규 · 동일)
- shell/ui/SettingsTabView.kt (진입점 원본) → HomeView 로 대체 (demo 는 SettingsTab 없음)

검증: BUILD SUCCESSFUL (demoDebug 1m 56s) · APK install PASS

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-03 13:29:18 +09:00
dw.jang bcbc7b440c refactor: 데모 브랜치 패키지 통일 com.example.medilightv2android → com.medithings.vesiscan
사용자앱 (feature/tab-navigation) 과 패키지 이름 일치. namespace + applicationId
둘 다 com.medithings.vesiscan 로 변경 (기존 데모 앱은 재설치 필요).

## 변경 범위
- Kotlin 148 파일: package + import 문 (714 occurrences)
- 디렉토리 이동: com/example/medilightv2android → com/medithings/vesiscan
  (main, test, androidTest 각각)
- app/build.gradle.kts: namespace, applicationId
- docs/FLAVOR_DEMO_STABLE.md: 참조 갱신
- V41DetectorCH4Test: BvDispatchResult.methodChosen → method (dto field name fix)

## 주의
- applicationId 가 바뀌므로 기존 데모 앱 (com.example.medilightv2android.demo) 은
  Android 관점에서 다른 앱으로 취급 — 재설치 시 PIN/설정 초기화됨.
- Fresh install 권장. 기존 앱 (com.example...) 은 별도로 uninstall 필요.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-02 14:30:43 +09:00
dw.jang 7be3f0b096 demo: PiezoMonitoring Settings overlay Disconnect 추가 + v1.1.1-design
다중 기기 페어링 작업과 짝 — known 기기로 빠른 스위치 시
완전 해제(Delete Pairing) 말고 소프트 끊기(Disconnect) 옵션
필요. demo-final 에는 통합 설정 탭이 없으므로 기존 overlay 의
Delete Pairing 위에 Disconnect 버튼을 같이 배치.

- Disconnect (회색): bond 유지, 다음 측정 빠른 재연결
- Delete Pairing (빨강): 기존 확인 다이얼로그 그대로

버전: 1.1.0-design (vc25) → 1.1.1-design (vc27)
  vc26은 master 의 v1.2.0 이라 충돌 피해 27 로 jump.
2026-06-11 15:05:08 +09:00
dw.jang 2801d2ae53 Build: productFlavors 추가 — demo(동결 안정판) / dev(개발 진행) + docs 정리
방광 모형 테스트가 100% 통과하는 시점을 demo flavor로 동결하고, 모든
신규 작업은 dev에서만 진행하기 위한 빌드 분기 셋업.

gradle (app/build.gradle.kts):
  - flavorDimensions: "channel"
  - demo: applicationIdSuffix=".demo", versionNameSuffix="-demo",
          BuildConfig.IS_DEMO=true, FLAVOR_LABEL="demo"
  - dev:  BuildConfig.IS_DEMO=false, FLAVOR_LABEL="dev"
  - 두 flavor 동시 설치 가능 (applicationId 분리)

source set:
  - src/demo/res/values/strings.xml — app_name "VesiScan Demo"
  - src/dev/res/values/strings.xml  — app_name "VesiScan Dev"
  - src/demo/java/.gitkeep + src/dev/java/.gitkeep — 격리본 둘 위치 안내

운영 가이드: docs/FLAVOR_DEMO_STABLE.md
  - Level 1 (BuildConfig 분기) vs Level 2 (source set 격리) 두 동결 방식
  - 새 안정판 동결 시 git tag + src/demo/ 복사 절차
  - 어떤 코드를 동결 후보로 둘지 (알고리즘/파라미터 권장, BLE/UI는 공유)
  - 트러블슈팅 + 첫 동결 시점 권장 절차

.gitignore: /docs/ 제거 — 팀/iOS 개발자가 접근 가능하도록
  - docs/BLE_PROTOCOL_REFERENCE.md
  - docs/iOS_PORTING_CLINICAL_MEASUREMENT.md
  - docs/FLAVOR_DEMO_STABLE.md 모두 신규 commit

검증:
  ./gradlew assembleDemoDebug + assembleDevDebug 둘 다 BUILD SUCCESSFUL
  → app/build/outputs/apk/demo/debug/app-demo-debug.apk
  → app/build/outputs/apk/dev/debug/app-dev-debug.apk

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-08 11:49:10 +09:00
dw.jang 76bb0942c0 chore: version bump 1.0.0-design → 1.1.0-design (versionCode 25)
v1.1 변경 요약 (1.0 이후):
- Clinical R&D Mode 신규 (Dev 한정)
- UI 3-phase 리디자인 (Critical/Important/Polish 12건)
- 측정 로그 자동 저장 (adc.csv / placements.jsonl / ble.log / meta.json)
- 외부 저장소 영구 보관 (Downloads/VesiScan_Sessions/, _Events/)
- Auto Scan interval 사용자 조절 가능 (600~5000ms)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:40:36 +09:00
dw.jang 11dd8fb58f chore: version 1.0.0-design (versionCode 24)
문서 변경 이력 (2026-05-12 이후 22 빌드 누적) 적용 완료 마커.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:34:51 +09:00
Charles KWON 833c110d2f feat: 측정 가로모드 대응 + Ant Offset + Scan Log + 다중 sanity check
## 측정 화면 (PiezoMonitoringView)
- BoxWithConstraints + donutFitSize: 가로/작은 화면에서도 donut+Bladdy+buttons
  모두 들어오도록 height-aware sizing (cap 120~360dp)
- nonDevSpacing: available height 기반 단계 (18/22/28/36) — 큰 화면 답답함 해소
- center 4 중 1개라도 missing → 즉시 realign prompt (이전: AutoMeasure 5회 누적)
- 6채널 중 3개 이상 missing 시 위치 이탈 알림 (모드 무관)
- 다이얼로그 문구: "Position Lost or Insufficient Gel"
- V41 결과 후처리 — walls를 PiezoEchoAnalyzer 기준으로 강제 (UI 차트와 일관)
  · ant = analyzer.result.ant
  · post = analyzer.result.lowEnd + 3
- CH3 secondary echo 잡힘 / CH4·CH5 lateral ant too-deep 문제 해결
- Bladdy 3-tap → Developer Mode toggle (측정 화면에서도)

## Placement 가이드 (PlacementGuideView)
- 가로 모드 대응: bodyW = min(w, h*0.7f) — 태블릿 가로에서 portrait 비율 유지
- Sensor 이미지를 Canvas drawImage 로 이동 — bladder oval 좌표계와 정확히 정렬
- Start Alignment 버튼: enabled = true 명시

## Settings overlay
- "Ant Offset" 숫자 입력 필드 (0~20mm, dev mode 전용)
  · BV 계산 시 ant 를 입력값만큼 probe 쪽으로 당김 (chord 증가 → BV 증가)
  · mm→samples 변환은 PiezoHW.distancePerSample
  · V41 SphereFit BV 는 offset≠0 일 때 우회 → 6ch estimate
- "Version" row 추가 (BuildConfig.VERSION_NAME)
- Save 버튼: 실제 UserStorage 영구 저장 + state 재전파
  · AppState.savePiezoSettingsNow() 추가
- 전체 overlay 에 verticalScroll + Card maxHeight 90% — dev 컨텐츠 길어도 Save 도달

## DPS 동기화 (긴급 수정)
설정의 DPS 변경이 V41Detector(Geometry/AnatomicalGate)에 반영 안 되던 문제:
- WdConfig.DPS_DEFAULT: const val → @Volatile @JvmField var
- PiezoHW.distancePerSample setter 가 WdConfig.DPS_DEFAULT 자동 동기화
- 시작 시점 1.968.also { WdConfig.DPS_DEFAULT = it } 로 두 값 일치

## 버전
- versionName: VBTAND0101 → 2026005-101
- versionCode: 2 → 3

## 신규 모듈
- services/ScanLogStore.kt — dev mode 진단 로그 (마지막 Single Scan summary)
  · WALLS CSV (ch,ant,post,urine), DATA CSV (raw ADC), antOffsetMm
- ui/components/ScanLogDialog.kt — 클립보드 복사 다이얼로그
  · xbench (CharlesKWONsLaw) MbbLogDialog 패턴 차용
  · SelectionContainer + monospace + Copy/Clear/Close
- 측정 화면 dev panel 에 "Scan Log (N)" 버튼 추가

## 데이터 모델
- PiezoSettings.antOffsetMm: Int = 0 필드 추가

## 단위 테스트
- V41DetectorCH4Test.kt — 2026-05-06 floating-signal 캡쳐 6채널 raw 를
  V41Detector.detect 에 통과시켜 결과 출력 (CH4 가짜 wall 진단용)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 18:01:33 +09:00
dw.jang 0e0e04b6f5 fix: cross-channel outlier 제거 + placement 강화 + UI 수정
- Cross-channel urine length outlier: median의 1.35x 초과 또는 0.5x 미만 → 제거
  (CH3 바닥반사 urine=50 vs 정상 ~35 → 480ml↔223ml 편차 해결)
- Placement CV: V0=0.07, V1/V2=0.10 (기기별 분기)
- Placement 기준 강화: LR dev 0.20, gradient 0.3, lenVariation 0.35
- "방향OK→무조건GREEN" 제거, CV 미달시 "Fine-tune position"
- GREEN CV가이드/Hold steady 제거, ch별 len 분석 방향 안내
- 가이드 텍스트 상자 항상 표시
- 센서 이미지 topview 교체 (240×150dp)
- 앱 버전 VBTAND0101
- Plot/Log allWalls 통일 (선택 Method 기준)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-08 15:36:38 +09:00
dw.jang 0e13f2bbb6 feat: consent screen, reminder system, medical metrics, sex field, design tokens, code quality
A. Reminder: ReminderService (AlarmManager chain), ReminderSettingsView,
   bell icon in Piezo top bar, reschedule on catheterization
B. Consent: ConsentView (3 mandatory items), onboarding→consent→registration flow,
   persistent consent storage, sex field in User + Registration Step 2
C. Diary: 24hUV, NUV, NPI, max capacity, day/night frequency metrics card
D. Design: DesignTokens.kt, 48dp touch targets, DonutChart semantics, pressScale modifier
E. Quality: BuildConfig.DEBUG log wrapping, DateFormatter caching, background I/O for diary store

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 14:26:02 +09:00
dw.jang c15fcfccca Integrate voiding diary into Piezo catheterization flow
Catheterize button now opens a voiding record sheet with 3 input modes:
- Manual: number TextField (1-1000ml)
- Voice: SpeechRecognizer + VolumeParser (Korean/English)
- Camera: placeholder for YOLO cup measurement (TODO)

On save: "배뇨일지에 등록되었습니다" toast, bladder level resets to 0

New modules (from Uridiary):
- measure/: SimpleMeasureService, YoloDetector, CCPosition, VoidingRecord
- speech/: SpeechRecognizerManager, VolumeParser, HfVolumeExtractor
- assets/urinecup_best.onnx: YOLO model for cup detection

VoidingRecordStore: local SharedPreferences+JSON storage for voiding records

Dependencies added: ONNX Runtime, ML Kit OCR, CameraX, OkHttp
Permissions added: CAMERA, RECORD_AUDIO, INTERNET

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 11:47:59 +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