Commit Graph

223 Commits

Author SHA1 Message Date
dw.jang 38fa93c8b2 feat: 임상 Live 6채널 시각화 화면 (ClinicalLiveView)
vesiscan_test Section 2 프로토콜 패턴을 안드로이드 앱 안에서 직접 구현.
실제 몸에 센서 붙인 채 6채널 실시간 그래프 보면서 측정 + 저장 가능.

신규 화면 ClinicalLiveView:
- 600ms 간격 mtb? 자동 송신 (Piezo 6ch + IMU 동시)
- 2×3 grid 라이브 차트 (raw ADC waveform, CH0~CH5 색상별)
  · y축 0~4095 (12-bit ADC) 고정 + 가로 grid 4분할
  · 각 채널 peak 값 표시
- IMU 카드 — ImuAnalysis 기반 자세 + tilt + shake (흔들림 시 빨강 경고)
- 상단: 세션 라벨 + S=Subject TV=TrueVol Ab=Abdomen 한 줄 + #cycle ✓captured
- 컨트롤: Pause/Resume · Capture (수동 저장) · Auto Capture 토글 · End Session

ClinicalSession 모델 확장:
- subjectId / trueVolumeMl / abdomenThicknessMm 필드 추가
- folderName 에 subjectId prefix (예: "S001_VBT26050202_SUPINE_ALIGN_0CM_...")
- meta.json 에 subject_id / true_volume_ml / abdomen_thickness_mm 자동 기록

ClinicalSessionStore:
- lastSubjectId / lastTrueVolumeMl / lastAbdomenThicknessMm 기억 (다음 측정 자동 채움)

ClinicalHomeView 입력 폼 확장:
- Subject ID 텍스트필드
- True Volume (mL) + Abdomen (mm) 2-컬럼 숫자 입력
- Start Live Measurement 버튼 → CLINICAL_LIVE 화면으로 직행
  (이전: alignment/BV step에 따라 PlacementGuide/PiezoMonitoring 분기)

AppState/MainActivity:
- AppScreen.CLINICAL_LIVE 추가
- MainActivity Back: LIVE 시 endMeasurement + ClinicalHome 복귀

데이터 흐름:
  Start Live → ClinicalLiveView 진입 → mtb? 600ms loop
    → piezoCollector → 6채널 buffer (자동 또는 수동 capture)
    → AdcCsvLogger.log → adc.csv 누적
    → imuCollector → 15 IMU samples
    → ImuCsvLogger.log → imu.csv 누적
  End/Back → endMeasurement → meta.json finalize → ClinicalHome 복귀

장점:
- 검사자가 실제 시연 중 6채널 신호 품질 즉시 확인
- Auto Capture로 매 사이클 자동 저장 (Pause로 일시 멈춤)
- subject_id 기반 폴더명으로 피험자별 grouping 자연스럽게

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 11:17:55 +09:00
dw.jang ec3be6fa5d feat: Sensor Alignment에 IMU 통합 (mtb? + rim: 정식 처리)
PPT 'VesiScan-Basic IMU FW 진행사항_260521' 사양 기반 구현.
실측 1회 (2026-05-27 12:24, 평평한 책상)로 endian/필드 순서/시간 순서 확정 후 정식 적용.

확정 사양 (실측):
- rim: 패킷 188B = [tag 4B "rim:"] [num 2B BE] [data 180B = 15 samples × 12B] [crc 2B]
- 1 sample = 12B: Ax 2B + Ay 2B + Az 2B + Gx 2B + Gy 2B + Gz 2B (모두 BE int16)
- Accel ±4g sensitivity: /8192 g/LSB
- Gyro ±500 dps sensitivity: /65.536 dps/LSB
- 샘플 순서: samples[0] = oldest (FIFO 순)

신규 파일:
- ble/ImuPacketCollector.kt — rim: 파서. ImuSample data class + 15-sample list 콜백
- services/ImuCsvLogger.kt — Clinical 모드 한정 imu.csv 자동 저장
  컬럼: scan_id, timestamp, clinical_label, firmware_version, sample_idx, ax_g, ay_g, az_g, gx_dps, gy_dps, gz_dps
- managers/ImuAnalysis.kt — 자세 추정/Tilt/흔들림 RMS 유틸 (간이, 임상 보정 필요)

수정:
- BleManager: imuCollector 필드 + sendMtb() 정식 명령
  (canSendMaa() 게이트 maa와 공유. piezo + IMU 동시 캡처)
- BleManager: rim: 핸들러 정식화 (imuCollector.parseRim 위임)
- PlacementGuideView: sendAllChannels → sendMtb 교체, imuCollector.onComplete 등록
  Clinical 모드일 때 ImuCsvLogger.log() 자동 호출
  Dev 모드 한정 상단 칩에 "IMU: Supine · tilt=N° · shake=N.Ndps" 표시

데이터 흐름:
  performScan → sendMtb? → 응답:
    1) reb × 6 (Piezo, 기존 처리)
    2) raa (Piezo end, 기존 onMultiChannelComplete 트리거)
    3) rim (IMU, 신규 imuCollector.onComplete → lastImuSamples 갱신 + imu.csv)

분석:
- ImuAnalysis.estimatePosture: Accel 평균으로 Supine/Sitting/Standing/Tilted/Moving 판정
- shakeRmsDps > 30 → "Moving" (자세 판정 보류)
- tiltDegrees: z축 vs 중력 각도

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:19:20 +09:00
dw.jang 020a45ba92 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>
2026-05-27 10:19:18 +09:00
dw.jang de3177e5e0 feat: ClinicalSession meta.json에 firmware/hardware/serial 자동 기록
R&D 분석 시 펌웨어 버전별 데이터 grouping 가능 (VBTFW0111 vs VBTFW0116 등).

writeMeta()에서 BleManager 인스턴스의 firmwareVersion/hardwareVersion/serialNumber
state를 읽어 meta.json에 기록:
- "firmware_version": "VBTFW0116"
- "hardware_version": "VBTHW0100"
- "serial_number": "VBT26040001"

값이 비어있으면(연결 안 됨/응답 안 옴) 필드 생략. 측정 종료 시점(endMeasurement)에
한 번 더 writeMeta 호출되므로 측정 중 BLE 응답 받으면 자동 갱신됨.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 10:09:22 +09:00
dw.jang e1fa0d1620 feat: 펌웨어 버전 앱 내 표시 (Settings + ClinicalHome)
연결 시 자동으로 받던 mid?/rid: device_info를 UI에 노출. 이전에는 BLE 디버그 로그에만
기록되어 사용자가 접근 불가했음.

BleManager:
- firmwareVersion / hardwareVersion / serialNumber 3개 MutableState 추가
- rid: 응답 파싱 — "VBTHW0100   VBT26040001 VBTFW0111" 형식을
  공백 split + prefix 매칭(VBTHW/VBTFW/VBT)으로 안전 추출
- disconnect 시 3 필드 모두 clear

PiezoMonitoringView Settings overlay:
- 기존 "Version" 행을 "App" + "Firmware" 두 행으로 분리
- FW 없으면 "—" 표시 (연결 대기 중)
- dev 모드 한정 "Serial" 행 추가 (회사 자산 추적용)

ClinicalHomeView BLE 연결 카드:
- deviceName 아래 "FW: VBTFW0116" 한 줄 추가
- 검사자가 별도 클릭 없이 펌웨어 버전 즉시 확인 가능

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 09:41:59 +09:00
dw.jang 3b8fc9ca5d UI: Sensor Alignment 방향 화살표 4종 복원 (사용자 요청)
빌드 111(e0f92eb)에서 의도적으로 제거했던 4방향 화살표(↑↓←→)를
사용자 요청으로 복원. directionIcon에 따라 센서/타원 바깥에 그려짐.
Hint 텍스트와 함께 시각적 방향 안내 제공.

색상 #FF5722 · arrowLen 70f · sw 9f (Charles 833c110 원본 사양 그대로)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:51:40 +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 652a123aec revert: PlacementGuide Skip 버튼 복원
Phase 3 #12에서 Dev 모드 한정으로 격리했던 Skip 버튼을 원본대로 복원.
일반 사용에서도 필요한 기능이었음. 임상 안전 우려는 별도 방식으로 해결 필요.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:40:36 +09:00
dw.jang 83f63c62ca UX Phase 3: Polish — 라벨/안전/실루엣 정리
스크린샷에서 발견된 디테일 4건 fix.

9. Status badge 임상 모드 분기
   - 일반 모드: urgency.message ("Enjoy your day!" 등) 그대로
   - 임상 모드: "Supine · 0 cm" 같은 측정 라벨 표시
   - 임상 시연 시 캐주얼 메시지가 부적절했던 문제 해결
   - 검사자가 현재 어느 condition인지 즉시 인지 가능

10. 카테터 카운트에 "left" 라벨 추가
    - 이전: 아이콘 + 숫자만 (의미 모호 — 잔여? 추가? 누적?)
    - 새로: 아이콘 + 숫자 + "left" 텍스트 (10×fontScale, 0.7 alpha)
    - 한눈에 "잔여 카테터" 의미 전달

11. 토르소 실루엣 모래시계 완화
    - chestW 0.42 → 0.40 (어깨 살짝 축소)
    - waistW 0.32 → 0.36 (허리 덜 잘록 — 다리처럼 보이던 원인)
    - hipW 0.46 → 0.42 (골반 살짝 축소)
    - 결과: 더 자연스러운 토르소 형태

12. Skip 버튼 Dev 모드 한정
    - 이전: 항상 표시 (임상에선 정렬 우회 위험)
    - 새로: appState.isDevMode일 때만 표시 + "Skip (Dev)" 라벨 + 크기 ↓
    - 폰트 15→13sp, alpha 0.6 — 의도된 dev 도구임을 시각적으로 명시

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:40:36 +09:00
dw.jang 52bd58ac87 UX Phase 2: Important — 위계/일관성 회복
스크린샷에서 발견된 시각 위계/정보 비대칭 4건 fix.

5. Voiding 버튼 fill 격상 (가장 자주 누르는 액션을 primary로)
   - containerColor: MlPrimary.copy(alpha=0.1f) → MlPrimary 솔리드
   - text color: MlPrimary → White
   - Auto Scan(주황) / Single Scan(청록)과 동급 강도로 일관성 회복
   - 환자가 가장 자주 누르는 액션이 옅게 디자인되어 있던 문제 해결

6. InfoCard 우측 라벨 명시 ("Session Max")
   - 이전: title="X mL / Y mL" + value="65%" (라벨 없음, 의미 모호)
   - 새로: title="Session Max", value="X" + unit="mL"
   - 좌측 "Current"와 같은 톤. fill % 정보는 도넛이 이미 시각화 중이라 카드는 raw 숫자만
   - 좌측 title도 "Current Measurement" → "Current"로 단순화

7. Alignment Hint 박스 톤다운 (3중 강조 해소)
   - fontSize 28sp → 20sp
   - padding horizontal 20→16dp, vertical 18→12dp
   - 배경 alpha 1.0 → 0.7
   - corner 16→14dp
   - "pubic bone" 빨강 강조는 유지 (의도된 1점 강조)

8. Step 1/3 도트 indicator 추가 (텍스트 한 줄 → 시각 progress)
   - 이전: 13sp 회색 텍스트 한 줄 (정보 위계 너무 약함)
   - 새로: 14sp Bold MlPrimary + 3-dot indicator
     ○ 완료된 step: MlPrimary 0.6 alpha, 8dp
     ● 현재 step: MlPrimary 솔리드, 12dp (강조)
     ○ 미완료 step: 회색 0.3 alpha, 8dp

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:39:46 +09:00
dw.jang 59267866b4 UX Phase 1: Critical — 폰트/버튼/타이틀 비례 회복
스크린샷에서 발견된 시각 위계 깨짐 4건 일괄 fix.

1. Monitoring "0 mL" 폰트 축소 (도넛 비례 회복)
   - volFontSize: screenWidth × 0.18 cap 96 → screenWidth × 0.12 cap 60
   - Bladdy↔숫자 spacer 63dp → 36dp (수직 균형 회복)
   - 효과: 도넛 안에 Bladdy + 숫자가 적절한 비율로 들어감

2. Voiding 버튼 텍스트 단순화
   - "Voiding/" + "Catheterization" (2줄, 좁은 폭에서 3줄로 깨짐)
     → "Voiding" 단일 라벨 1줄
   - 폰트도 (13×fs) → (14×fs)로 살짝 키워 가독성 확보

3. PlacementGuide 헤더 타이틀 중복 제거
   - 헤더의 작은 "Sensor Alignment" 20sp 제거
   - 본문 큰 타이틀과 중복되어 시각적 잡음이었음

4. PlacementGuide 큰 타이틀 38sp → 28sp
   - ExtraBold → Bold로 weight도 한 단계 낮춤
   - 화면 1/8 차지하던 과한 강조 해소, Hint 박스와의 위계 균형 회복

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:39:46 +09:00
dw.jang f7a036ec34 CLINICAL: Step에 부가 설명 추가 (치골 위로 N cm)
ClinicalStep enum에 description 필드 추가하고 ClinicalHomeView에서
선택된 Step의 설명을 segmented control 아래에 표시.

설명:
- ALIGN_0CM: 기기 아래 끝이 치골 바로 위
- ALIGN_1CM ~ 4CM: 치골 위로 N cm
- BV: Cradle 부착 후 본측정

UI 표시 위치: Step 라디오 그룹 바로 아래 한 줄, MlPrimary 색상 + 화살표(→).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:38:41 +09:00
dw.jang d0977d98ac CLINICAL: Alignment 화면에서도 임상 모드 시 adc.csv 자동 저장
문제: PlacementGuideView(Sensor Alignment, 정렬모드 0~4cm)에서는 AdcCsvLogger.log()를
호출하지 않아 adc.csv가 안 생성됨. R&D 정렬모드 알고리즘 개발은 raw ADC가 핵심인데
placements.jsonl + ble.log만 남고 ADC가 누락되던 상황.

원인: AdcCsvLogger.log()는 PiezoMonitoringView (BV 측정)에서만 호출됨. PlacementGuide는
미호출. 일반 사용에서 1초마다 도는 placement scan마다 ADC를 저장하면 파일이 폭주하므로
의도적으로 빠져 있었던 것.

변경:
- PlacementGuideView의 onMultiChannelComplete 콜백 끝에 임상 모드 한정 조건부 추가:
  ClinicalSessionStore.currentSession != null 이면 raw ADC를 adc.csv에 저장
- 일반 사용 흐름은 영향 없음 (currentSession == null이므로 기존 동작 그대로)
- 임상 모드에선 한 alignment 측정(예: 30초)당 ~30-50개 scan이 adc.csv에 누적됨

부차적 fix:
- MainActivity.kt:87 typo 수정 (androidx.com+-pose → androidx.compose)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:38:41 +09:00
dw.jang 3080cd6b23 CLINICAL: Subject ID / Device 입력 제거 — BLE 기기 이름이 device 식별자
사용자 요청: "subject ID 빼고, Device도 BLE 기기 이름따라 기기가 다른거라
따로 설정안해도 될듯."

변경:
- ClinicalSession 모델: subjectId 필드 제거, ClinicalDevice enum 제거,
  device: ClinicalDevice → deviceName: String (BLE 연결된 기기 이름 자동 채움)
- folderName: "{deviceName}_{posture}_{step}_{timestamp}"
  예: "VBT26050202_SUPINE_ALIGN_0CM_2026-05-28_14-30-15"
- label: "{deviceName}_{posture}_{step}" (예: "VBT26050202_SUPINE_ALIGN_0CM")
- ClinicalSessionStore: lastSubjectId 제거 (Examiner/Notes만 기억)
- ClinicalHomeView:
  * Subject ID TextField 제거
  * Device segmented control 제거
  * BLE 연결 카드에 deviceName 그대로 표시 (자동 사용)
  * Start 버튼 enabled = isConnected 만으로 충분

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:38:40 +09:00
dw.jang ab25b3c65d CLINICAL: 흐름 재설계 — 한 측정 = 한 라벨 = 한 폴더
이전: 한 세션이 9 conditions × 6 steps를 모두 묶고 마지막에 zip
새로: 한 측정마다 라벨 입력 → 측정 → 자동 finalize → 다음 측정 라벨

사용자 요청: "session 들어가면 일단 BLE 연결을 하고, 연결 이후에 내가 어떤 자세로
어느 위치 측정할건지를 직접 적고 테스트를 완료한 뒤에 로그가 저장되도록"

변경:
- ClinicalSession 모델: 단일 측정 단위로 재정의 (subjectId/device/posture/step
  /examiner/notes/startedAt/endedAt). folderName에 라벨 전체 + timestamp 포함:
  "S001_A_SUPINE_ALIGN_0CM_2026-05-28_14-30-15"
- ClinicalSessionStore: 단순화. zip 제거. startMeasurement/endMeasurement.
  inClinicalFlow boolean 추가 (DeviceScan/Back 라우팅용).
  lastSubjectId/Examiner/Notes 기억 (다음 측정 시 자동 채움).
  lastFinalizedFolder (UI에 "Saved: ..." 표시용).
- 3 로거: stepLogDir() → currentLogDir() 단순화. 한 폴더에 모든 파일.
- ClinicalHomeView 전면 재작성:
  * BLE 연결 상태 카드 (미연결 시 Connect 버튼 → DeviceScan)
  * 직전 측정 결과 표시 (lastFinalizedFolder)
  * 라벨 입력 폼 (Subject + Device/Posture/Step segmented control + Examiner + Notes)
  * 라벨 preview 실시간
  * Start Measurement 버튼 (BLE 연결 + Subject ID 있을 때만 활성)
- AppState.deviceConnected: 임상 흐름이면 ClinicalHome 복귀 (Personalization 우회)
- MainActivity Back handler:
  * DEVICE_SCAN Back → inClinicalFlow면 ClinicalHome
  * PLACEMENT_GUIDE/PIEZO_MONITORING Back → 활성 세션이면 endMeasurement +
    ClinicalHome 복귀 (폴더 finalize)
  * CLINICAL_HOME Back → inClinicalFlow 해제 + HOME

데이터 구조 (한 측정당):
  Downloads/VesiScan_Sessions/{subjectId}_{device}_{posture}_{step}_{timestamp}/
    ├─ meta.json   (라벨 + 앱/펌웨어/디바이스 버전 + 시작/종료 시각)
    ├─ adc.csv
    ├─ placements.jsonl (alignment) or events.jsonl (BV)
    └─ ble.log

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:38:40 +09:00
dw.jang 6e2dba749b CLINICAL: Dev 모드 한정 임상 R&D 측정 세션 메뉴 추가
회사 내 알고리즘 개발용 임상 시험(3 devices × 3 postures × 6 steps = 54 measurements
per subject)을 위한 데이터 라벨링/라우팅/zip 자동화. Dev 모드 켠 경우에만 노출.

신규 파일:
- models/ClinicalSession.kt — ClinicalDevice/Posture/Step 모델 + 완료 추적
- services/ClinicalSessionStore.kt — 전역 세션 store + 폴더 라우팅 + meta.json + zip
- ui/views/clinical/ClinicalHomeView.kt — 세션 메타 입력 다이얼로그 + 9-conditions
  그리드 + 6 steps + Mark Complete + End Session(zip) 화면

로거 라우팅 (세션 존재 시 자동 분기):
- AdcCsvLogger: sessionRoot/{device}_{posture}/{step}/adc.csv +
  CSV에 clinical_label 컬럼 추가 (S001_A_SUPINE_ALIGN_0CM 형태)
- MeasurementLogService: 내부 filesDir → Downloads/VesiScan_Events/ 영구 이동.
  세션 시 step 폴더의 events.jsonl/placements.jsonl로 분기.
  BuildConfig 대신 PackageInfo로 versionName/versionCode 정확 주입.
  보관 90일 → 365일 (R&D 장기 보관)
- BleDebugLogger: step 변경 감지해서 ble.log 파일 자동 전환

UI/라우팅:
- AppScreen.CLINICAL_HOME + MainActivity Crossfade/BackHandler 분기
- HomeView에 dev 모드 한정 보라색 "Clinical Mode (Dev)" 버튼
- 측정 화면(PlacementGuide/PiezoMonitoring) Back 시 임상 세션 활성 → ClinicalHome 복귀

Placement 구조화:
- PlacementGuideView가 logService.logPlacement() 호출 시 urineLens/cv/phase/score
  추가 인자 전달 → placements.jsonl에 구조화 기록

데이터 구조 (한 세션당):
  Downloads/VesiScan_Sessions/{S001_2026-05-28_14:30}/
    ├─ meta.json
    ├─ A_SUPINE/align_0cm/{adc.csv, placements.jsonl, ble.log}
    ├─ A_SUPINE/align_1cm/...
    ├─ ...
    ├─ A_SUPINE/bv/{adc.csv, events.jsonl, ble.log}
    ├─ A_SITTING/... (총 9 conditions × 6 steps)
    └─ session.zip (End Session 시 자동 생성)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:38:40 +09:00
dw.jang 63bb8586ec SETTINGS: Auto Scan Interval 슬라이더 추가 (600~5000ms, 200ms 단위)
기존 hardcoded 600ms를 사용자가 Settings에서 조절 가능하도록 변경.

추가:
- PiezoSettings.autoScanIntervalMs (default 600ms) 필드
- AppState.savePiezoSettings() 헬퍼 — storage에 piezoSettings 영속화
- Settings 패널에 슬라이더 (Catheter Threshold 다음, Bladder Capacity와 동일 톤)
  - 범위 600~5000ms, 200ms 단위로 snap (roundToInt + 200 배수)
  - 현재값 mL 표시 패턴과 동일하게 "X ms" 우측 표시
- Settings Save 버튼이 실제로 영속화하도록 savePiezoSettings() 호출 추가
  (이전에는 showSettings=false만 호출 — maxVolume 등도 메모리만 변경되던 버그 동시 fix)

동작:
- PiezoMonitor Auto loop의 remaining = targetIntervalMs - elapsed
- BleManager throttle(600ms)이 하한이라 600 미만 설정해도 막힘 — 의미 있는 최소가 600
- 5000ms로 설정 시 거의 정적 모니터링 (배터리/CPU 절약)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:38:40 +09:00
dw.jang 31e9b8acbb UI: 설정 패널 폰/태블릿 자동 분기 — fontScale 활용
태블릿 시연용으로 키운 28sp가 폰에서 곱창나는 문제 해결.
이미 코드에 정의된 fontScale = (screenWidth / 360f).coerceIn(1.0, 1.8)를
활용해 자동 분기.

- 폰(360dp): 14sp × 1.0 = 14sp (원본)
- 태블릿(648dp+): 14sp × 1.8 = 25.2sp (시연 시인성)
- HorizontalDivider padding: 태블릿(≥600dp)에서만 vertical=12dp

변경:
- showSettings 블록에 isTablet/labelSize/rowSpacing/dividerPadding 변수 도입
- 9개 라벨 + 일부 값 fontSize = 14.sp → labelSize
- 9개 HorizontalDivider에 dividerPadding 적용
- Column spacedBy 6.dp → rowSpacing (폰 6dp, 태블릿 ~10dp)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:36:52 +09:00
dw.jang e716763cc4 UI revert: PiezoMonitoring 설정 패널 라벨 28sp → 14sp 복원
문서 빌드 117 묘사를 그대로 적용한 28sp가 실제 화면에서 너무 커서
Bladder Capacity / Catheter Threshold 라벨이 줄바꿈되고 divider padding
28dp가 카드를 과도하게 늘리는 곱창 결과 발생. 원본 14sp + divider padding
없음으로 일괄 되돌림.

- 9개 라벨 fontSize 28sp → 14sp
- 9개 HorizontalDivider modifier 제거 (padding 28dp 삭제)
- Column verticalArrangement.spacedBy 9.dp → 6.dp

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:35:50 +09:00
dw.jang e370d9c702 PERF: 측정 사이클 간격 1500/1000ms → 600ms 단축
MTU 247 + CONN_PRIORITY HIGH 적용 후 한 사이클 응답이 ~280~420ms로 단축됨
(이전 ~1.2~1.4s 대비 약 4배). 응답 시간 대비 과도하게 보수적이었던
throttle/loop delay를 일괄 단축.

변경:
- BleManager.canSendMaa() 시간 게이트 1500ms → 600ms (응답 평균 + 안전 마진 ~200ms)
- BleManager.canSendMaa() FORCE 게이트 5000ms → 3000ms (응답 빨라진 만큼 deadlock 감지도 빠르게)
- PiezoMonitor Auto loop delay 1500ms → 600ms
- PlacementGuide continuous scan loop delay 1000ms → 600ms

state-based gate(!isComplete)가 안전망 역할을 하므로 응답이 늦어지면
자동으로 차단되어 데이터 유실 위험은 그대로. 정상 경우엔 사이클당 ~0.6~0.7s로
실시간 모니터링 반응성 2배 향상.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:34:51 +09:00
dw.jang c1fac51085 BLE: 연결 직후 CONNECTION_PRIORITY_HIGH 요청
VBTFW0116 펌웨어가 pending slot 1→8로 확장해 TX queue 포화 시
패킷 보관/재전송으로 ADC 유실을 줄였음. 안드로이드 측에서는 Central의
Link Layer ACK 속도를 끌어올려 펌웨어 TX queue 포화 빈도 자체를 낮추는
방향이 가장 효과적.

CCCD write 완료(서비스 ready 시점)에 gatt.requestConnectionPriority(HIGH)
호출. peripheral이 수락하면 connection interval이 ~15ms로 협상되어
한 사이클 응답 시간 단축 가능. 거부되어도 패널티 없음.

실제 적용 여부는 sniffer로만 확인 가능. logcat의 "CONN_PRIORITY HIGH
requested (ok=...)" 로 호출 성공 여부만 확인 가능.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:34:51 +09:00
dw.jang cac3712b8c BLE: maa throttle을 state-based 게이트로 강화
이전 응답이 완료되기 전에 새 maa가 송신되면 piezoCollector.startMultiChannel(6)이
호출되며 collector가 reset → 큐에 떠다니던 reb 패킷이 폐기되어 BV_FAIL/BV_SKIP가
발생하던 문제 대응.

변경:
- 시간 단독 게이트(800ms) → state + 시간 복합 게이트
- (A) piezoCollector.isMultiChannel && !isComplete 이면 차단
       단 5초 넘으면 강제 통과(영영 deadlock 방지)
- (B) 최소 간격 800ms → 1500ms (펌웨어 응답 시간 ~1.2~1.4s 반영)
- sendAllChannels/sendChannelsOnly가 공유하는 canSendMaa() 헬퍼로 일원화

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:34:51 +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
dw.jang d507b1f447 UI: PiezoMonitoring 누적 업데이트 (108-122)
- 빌드 108: "Void/" → "Voiding/", Toast "Recorded" → 48sp Dialog 오버레이 (1.5초 자동)
- 빌드 109: Donut 캡 제거, 너비×0.85 적응형 (dev 모드는 0.65)
- 빌드 110: useScrollLayout = isDevMode && showDevPanels — 패널 표시될 때만 스크롤 레이아웃
- 빌드 111+115: Volume Text 24→72sp (3× ExtraBold, letterSpacing -1), Bladdy 0.85→0.483, 흰 bg 0.85→0.78, scroll 40/55→46/63dp
- 빌드 113: Voiding 시 Auto Scan 자동 stop + displayMaxVolumeMl/window/timer 정규화
- 빌드 114: Status 배지 dev 모드 한정 탭 → 60~maxVol-50 더미 측정값 랜덤 주입
- 빌드 117: 설정 패널 9개 라벨 14sp→28sp, 행간 spacedBy 6→9dp, HorizontalDivider 9개 padding(vertical=28dp)
- 빌드 122: 측정 상단 좌측 Home 아이콘 추가 (Placement 옆), Placement 진입은 enterPlacementFromMonitoring() 경유

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:34:14 +09:00
dw.jang 898fb77605 UI: PlacementGuide(Sensor Alignment) 리디자인
빌드 107-121 누적 적용:
- 3-stop vertical gradient 배경 (MlTeal 14% / 흰 / MlPrimary 10%) — GREEN 진입 시 단색 톤으로 덮임
- 큰 "Sensor Alignment" 타이틀 38sp ExtraBold 검정 (Maximum Bladder Capacity 톤 통일)
- Hint 텍스트 "pubic bone" 인라인 빨강 ExtraBold 강조 (AnnotatedString)
- Canvas 방향 화살표 4종 제거 — hint 텍스트로만 방향 안내
- Canvas 치골 아치 라인+halo+endpoint 제거 — pubic bone 강조는 hint 인라인 1곳에만
- 실루엣 chestY 16% UP shift (0.06 → 0.09, 비례 유지)
- Body 상단 Spacer 8dp

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:30:50 +09:00
dw.jang 3e1a875b5b UI: PiezoPersonalizationView 전면 리디자인 — 평행 2-카드
빌드 107-119 적용:
- Bladdy 히어로 BoxWithConstraints 동적 사이징, 카드 영역 수직 중앙 정렬
- Card 1 "Maximum Bladder Capacity" 34sp ExtraBold + 500/mL (64+24sp) + 56dp 솔리드 StepperButton + 슬라이더
- Card 2 "Catheter Threshold" 34sp ExtraBold + Level/7/10 3분할(24+64+24sp) + StepperButton + Level 1~10 슬라이더
- 부제 모두 제거, 섹션 간격 64dp
- Slider 값 부동소수점 절삭 버그 fix: it.toInt() → it.roundToInt()

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:28:59 +09:00
dw.jang 975be77126 UI: HomeView Bladdy 220 → 660dp (3×) + BoxWithConstraints 캡
빌드 114 적용: Home의 Bladdy 히어로를 3배 확대하고 BoxWithConstraints로
가용 영역의 90% 캡을 적용해 작은 화면에서 overflow가 나지 않도록 함.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:28:59 +09:00
dw.jang cb8500f6ce ROUTING: Placement 진입 경로 분기 (Personalization vs Monitoring)
빌드 106 적용: placementFromMonitoring 플래그 + enterPlacementFromMonitoring() /
backFromPlacementGuide() 헬퍼 추가. Personalization → Placement → Monitoring 정방향과
Monitoring → Placement 재진입 경로에서 Back 동작이 올바르게 분기되도록 일괄 정리.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:28:59 +09:00
dw.jang 34bb446e0f DEV: 측정 모드 직진입 버튼 + Start 라우팅 fix
빌드 104-105 적용:
- DeviceScanView "No devices found" 화면에 dev 모드 한정 "Enter Measurement Mode" 버튼 추가 (BLE 없이 디자인 작업용)
- AppState.startFromHome() 라우팅 fix — 페어링 후 Personalization → Back → HOME → Start 시 Monitoring 직행 버그를 PIEZO_PERSONALIZATION 부터 시작하도록 변경

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:28:59 +09:00
dw.jang 38c5a3f606 DEMO: PinView 자동통과 + UI 본체 비표시
빌드 101-103 적용: PIN 화면 진입 시 LaunchedEffect로 즉시 appState.pinVerified()
호출(PIN 저장 안 함). 화면 본체는 if(false) {...}로 비표시 — 빈 MlBackground Box만 노출.
원본 UI는 보존하여 데모 후 깨끗한 롤백 가능.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:28:58 +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 ef38aa6eaf UI: 앱 아이콘 교체 + Placement 힌트 텍스트 정리
- 앱 아이콘: 방광이.png로 교체 (webp→png, adaptive-icon 제거)
- Placement: "Centering" → "Checking", "LR balanced. Final check" → "Final check"
- 모든 방향 힌트에 화살표(↑↓←→) 추가 — phaseProtected 시 아이콘 추론 보장
- 디바운스 중에도 directionIcon은 최신 guideResult로 갱신 (화살표 누락 수정)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-11 17:21:10 +09:00
dw.jang 2cfa56a06d UI: 페어링 다이얼로그 + 태블릿 폰트스케일 + Placement 힌트/버튼 확대
- DeviceScanView: 페어링 안내 다이얼로그 추가 (bonding 완료 대기 중 표시)
- PiezoMonitoringView: fontScale(screenWidth/360) 적용, 도넛 비율 조정,
  버튼 높이/텍스트 확대, InfoCard 폰트스케일, Void/Catheterization 텍스트
- PlacementGuideView: 힌트 카드 28sp + 패딩 확대, Start Alignment/
  Start Scanning 버튼 96dp + 28sp, 센서 BiasAlignment 0.55

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-11 16:25:01 +09:00
dw.jang ddd2f79380 UI text: revised 문서 반영 — 전체 텍스트 통일
[Placement]
- Alignment Complete → Start Scanning
- sensor → VesiScan (초기 안내)
- Raise/Lower the sensor → Slide up/down
- Move left/right → Slide left/right
- Fine-tune → Optimizing, Checking LR → Centering
- Optimal position! → In position!
- Position lost/detached → Restart above pubic bone.
- Past optimum → Too far, Lost signal → Signal lost

[Donut]
- Void/Catheterize → Voided/Catheterized
- Auto → Auto Scan, Stop → Stop Scan, Spot → Single Scan
- Voiding recorded → Recorded
- 한글 다이얼로그 → 영어 (Disconnect and remove pairing.)
- Sensor Position Issue → Position lost, Go to Alignment → Realign

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-11 13:58:27 +09:00
dw.jang 75b10c2f77 UI: 힌트 위치 이동 + 버튼 텍스트 + Alignment 비활성 + Bladder Capacity
#1 Placement 힌트 메시지: 몸 아래 → Step 아래/몸 위로 이동
#2 버튼 텍스트: Catheterize→Catheterization, Auto→Automatic, 글자 크기 UP
#3 Alignment Complete: GREEN(Optimal)일 때만 활성 (Skip과 차별화)
#4 Max Volume → Bladder Capacity (설정+Personalization 모두)
- Bladdy 도넛 내 위치 3% 위로 보정

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-11 11:51:27 +09:00
dw.jang fab018f19e UI: 도넛차트 비율 기반 레이아웃 (태블릿 대응)
고정 dp → 화면 너비 비율:
- 도넛: 85% (dev: 65%), 선 두께: 도넛×8%
- Bladdy: 도넛×55% (dev: 30%)
- 볼륨 폰트: 화면×6%
- 패딩: 화면×5%, 간격: 화면×8%
- 버튼: 화면×15% (50~70dp clamp)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-11 11:02:51 +09:00
dw.jang 6287f21a05 fix: 센서 비율 기반 + LED 깜빡임 + 디바운스 + gated 체크
[Placement]
- 센서 이미지 비율 기반 (너비 35%, BiasAlignment 0.44) — 태블릿 대응
- LED 깜빡임 수정: greenHeld/failCount<3 동안 LED 5 안 보냄
- 디바운스: phaseProtected 중 lastHintChangeMs 안 건드림 (화살표 안 나오던 버그)
- Step 2 전환 힌트 항상 "Checking left/right balance..."
- GREEN에서 ch4/ch5 개별 탈락 시 방향 안내 (lrDev NaN 처리)

[도넛차트]
- Fill 카드: 윗줄 65%, 아랫줄 325/500 mL
- ml → mL 통일
- Spot 완료 시 displayMaxVolumeMl 즉시 갱신
- Auto 윈도우 < 5일 때 Current Measurement "—"

[기타]
- V41 gated 체크 + 최소 len≥3 (ungated 피크 안 보임)
- CCCD 실패(status 133) 500ms 후 1회 재시도
- 기기 스캔 사전순 정렬
- 힌트 점 순환 (./.../...)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-11 10:07:15 +09:00
dw.jang a7133a13ae feat: UI 대량 개선 + Placement 안정성 + BV 수정
[Placement]
- Start 버튼 필수 (자동 스캔 안 함)
- 화면 진입 시 전체 상태 리셋 (Step 2 직행 방지)
- Step 1 완료 조건: ch0~ch3 4개 전부 감지 필수
- Step 2 전환 시 "Checking left/right balance..." 3스캔 보호
- Step 전환 힌트 강제 유지 (phaseHoldHint)
- GREEN hold 7초 + 3회 연속 실패 후 이탈
- GREEN 이탈 시 스캔 중지 + Start 재시작 대기
- Detach 시 스캔 중지 + Start 버튼 표시
- 힌트 점 순환 (Raise.. / Raise... 업데이트 표시)
- 텍스트: "Raise the sensor up" 등 자연어
- 초기/재시작 시 화살표 제거
- Step 2 스킵 제거 (항상 3단계 순서)
- GREEN에서 ch4/ch5 개별 탈락 시 방향 안내
- gradient dead zone에 4채널 필수

[도넛차트]
- Max Volume 5초 간격 갱신
- Current Measurement / Fill 카드 (Fill = 65% + 325/500 mL)
- Auto 윈도우 < 5일 때 Current Measurement "—"
- Spot 완료 시 displayMaxVolumeMl 즉시 갱신
- ml → mL 통일
- Sensor Detached 상단 문구 제거
- 카테터 기본값 15개

[기능]
- Auto 2ch 미수신 5회 연속 → Realign 팝업
- Spot 간격 800ms→100ms (throttle이 보장)
- Auto Stop 버튼 깜빡임 해결
- Plot fallback 제거 (allWalls null이면 피크 안 보임)
- Pairing 미완료 시 다음 화면 차단
- 설정 패널 Save/Cancel 버튼
- 뒤로가기 앱 종료 확인

[코드 품질]
- Thread → coroutine (measureScope + Dispatchers.Default)
- mutableStateOf 메인 스레드 보장
- measureDoneSignal var→val mutableStateOf
- @Volatile (BleManager, GreenZoneConstants, PiezoHW)
- maa throttle deadlock 방지 (Boolean 반환)
- channelLogs allWalls 기준 통일

README v4 업데이트

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-09 20:20:34 +09:00
dw.jang b97d3d8052 feat: 10건 UI 피드백 + Auto fail 팝업 + 텍스트 정리
#1 Sensor Detached 상단 문구 삭제 (urgency 메시지 항상 유지)
#2 Max Volume 5초 간격 갱신 (displayMaxVolumeMl)
#3 Fill/Level → Current Measurement/Fill
#4 detach 시 팝업→Placement 이동 (showRealignPrompt)
#5 Auto 2ch 이상 미수신 연속 5회 → 팝업 (바로 이동 아님)
#6 팝업 텍스트 "Sensor Position Issue" + 안내 문구
#7 Step2 진입 시 "Checking left/right balance..."
#8 detach 후 waitingForStart=true → Start 버튼 표시
#9 초기/재시작 안내 시 화살표 제거 (directionIcon="")
#10 Raise/Lower 텍스트 → "Raise the sensor up" 등 자연어

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-09 17:21:11 +09:00
dw.jang 8f2eb15af3 feat: Placement Start flow + Auto fail redirect + Spot 800ms
#1 Placement: 초기 "Place sensor, then press Start" 안내
   Start Alignment 버튼으로 스캔 시작 (자동 시작 안 함)
#2 GREEN 이탈 시 스캔 중지 + "Position lost, press Start" 재시작 대기
#3 Auto: 3회 연속 실패(채널 미수신/BV 불가) → Auto 중지 +
   "Sensor Alignment Required" 다이얼로그 → Go to Alignment
#4 Spot 간격 1500ms → 800ms (총 ~5초로 단축)

기타:
- Auto Stop 깜빡거림 해결 (enabled에서 isMeasuring 제거)
- 미부착 시 상단 배지 "Sensor detached" 빨간 표시
- 카테터 수 상단 바 표시 (탭→추가 팝업)
- Void 버튼 → 즉시 기록+감소 (팝업 없이)
- ch4/ch5 하나라도 빠지면 GREEN 해제 (lrPass 기본 false)
- GREEN hold 5초 + 이탈 시 Start over
- Placement Post Max 슬라이더 추가 (개발자 설정)
- 설정 패널 Save/Cancel 버튼

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-09 14:46:04 +09:00
dw.jang 38ab74369e UI: 8건 피드백 반영 + Spot 봉인 + 페어링 체크
[Placement]
#1 센서 위치 타원 중앙 (offset y=65dp)
#5 skin 색 항상 동일 (greenZone 분기 제거)
#6 화살표 센서 밖으로 (sensorHalfH + 20f 간격)
#8 GREEN 체크마크 제거
센서 크기 192x120dp (80%)

[Donut]
#2 상단 버튼 1.5배 (48/56dp)
#3 배터리 아이콘 Canvas 그리기 (충전량 바)
#4 도넛-카드 간격 2배 (32/40dp)
#7 Bladdy 정중앙 (matchParentSize + Arrangement.Center)

[기능]
- Spot 중 Auto/Void 버튼 봉인 (enabled=false)
- BLE 페어링(bonding) 완료 확인 후 다음 화면 (30초 대기)
- R-peak gate Gradient 모드 비활성화
- GREEN 디버그 로그 (cv/lrPass/rPeakOk)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-08 17:27:34 +09:00
dw.jang 72ccbdf102 fix: Thread→coroutine, PlacementGuideView 메인스레드 보장
- #2: PiezoMonitoringView raw Thread → measureScope.launch + Dispatchers.Default
  withContext(Main)으로 state 변경 보장, composable 떠나면 자동 취소
- #22: PlacementGuideView onMultiChannelComplete 콜백 → handler.post 감싸기
  BLE Binder 스레드에서 Compose state 직접 변경 → 메인 스레드 보장
- nested handler.post 제거, throttle 시 직접 state 리셋

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-08 16:30:19 +09:00
dw.jang 758930796f fix: critical code review — thread safety, state management
CRITICAL fixes:
- #1: lastMultiChannelResult 변경을 handler.post로 (메인 스레드 보장)
- #3: measureDoneSignal var=remember → val mutableStateOf (recomposition 안전)
- #34,35: BleManager bluetoothGatt/tx/rxCharacteristic/lastRxTimestamp @Volatile
- #66: GreenZoneConstants 모든 mutable var @Volatile
- #56: PiezoHW.distancePerSample @Volatile
- nested handler.post 제거 (불필요한 프레임 지연)
- maa throttle deadlock 방지 (Boolean 반환 + state 리셋)
- Plot/Log 불일치 수정 (channelLogs를 allWalls 기준으로 통일)
- Placement 센서 위치/화살표 조정, gradient dead zone, CV relaxation
- 개발자 모드 CV 표시, 가이드 상자 항상 표시

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-08 16:25:06 +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 6dd0ec5058 UI: 피드백 반영 — Placement/Donut/뒤로가기/토스트
[Placement]
- 복부 높이: 500dp→weight(1f) 유동, 스크롤 제거
- 화살표 크기: 50→70, head 20→28, stroke 7→9
- 6/6, 60% 비개발자 모드에서 제거
- Step indicator를 이미지 위로 이동

[Donut]
- Bladdy 크기: 80/110dp→120/150dp (비개발자)
- 도넛 크기: 260/320dp→300/360dp (비개발자)
- 볼륨 텍스트 배경 제거 (투명)
- 원 내부 흰색 반투명 배경 추가
- measuring 메시지 제거 (Auto 대기 중 표시 안 함)
- 배터리 옆 초록 connection dot 제거
- Volume 카드 제거, Fill/Level만 유지 (maxVolumeMl 기준)
- "빠른 저장"→"Quick Save" 영어 통일
- 측정 부족/미감지 토스트 제거

[기타]
- HOME 뒤로가기 → "Exit App?" 확인 다이얼로그

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-08 12:36:41 +09:00
dw.jang 414ce940a2 fix: maa 1초 throttle — BLE 계층에서 이중 전송 물리 차단
handler.post로 isMeasuring 갱신해도 Compose state 레이스 해결 안 됨.
BleManager에서 lastMaaSentMs 타임스탬프로 1초 이내 재전송 무조건 차단.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-08 12:27:58 +09:00
dw.jang 54bca199af fix: maa 이중 전송 방지 (BV_FAIL 근본 원인)
- isMeasuring=false를 handler.post로 메인 스레드 갱신
  (Thread 내 설정 → Compose state 지연 → 다음 measure() 통과 → maa 이중 전송)
- measure() 진입 시 isMeasuring 가드 추가 (중복 호출 방어)
- 채널 수신 로그 추가 (RECEIVED N channels)
- MTU 247 negotiation 추가 (reb: 208B 분할 전송 최소화)
- Spot 6ch 필터 추가 (allChannelsReceived)
- Auto Stop 시 값 유지 (재계산 안 함)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-08 11:59:13 +09:00
dw.jang c819d615d0 fix: LR 방향 수정 + Almost there 제거 + postMaxIdx 슬라이더
- 좌우 방향 전면 수정: ch4(왼쪽)길다→←, ch5(오른쪽)길다→→
  ch4만 감지→← (기기 오른쪽 치우침), ch5만→→
- Almost there 제거: 방향OK+CV미달 → 그냥 GREEN 통과
- postMaxIdx 런타임 슬라이더 (개발자 설정, 기본 80, 범위 50-95)
  Method A/B 공통 적용 — 바닥반사 peak 무시

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-08 09:41:44 +09:00
dw.jang b1ca5bfd66 fix: Auto Stop 최종 trimmedMean 반영 + Placement 채널소실 멘트
- Auto Stop 시 윈도우 전체 trimmed mean 계산 후 표시
- LATERAL/GREEN 중 center 채널 소실 → "Start over from above the pubic bone"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 18:04:24 +09:00
dw.jang e5b252b314 feat: Detection/BV 분리 설정 + Spot 5회 trimmedMean + UI 여유
- Detection(A/B/C)과 BV(Frustum/V41) 독립 설정 (개발자 모드)
  기본값: Detection=C, BV=V41
- Spot: 3회 median → 5회 1.5초간격 trimmed mean (Auto와 동일)
  최대 8회 재시도, 최소 유효 3개
- 일반 모드 도넛 320dp, 간격 28dp, 버튼 62dp (화면 여유)
- Placement mm 표시 제거 — 방향만 안내

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 17:28:04 +09:00