회의 결정 2건:
1. Step/Posture 유지
증상: 2cm 측정 후 돌아오면 Step이 0cm로 자동 reset되어 매번 다시 선택해야 함.
원인: ClinicalHomeView가 매번 ALIGN_0CM/SUPINE으로 초기화.
수정: ClinicalSessionStore.lastPosture / lastStep 추가, startMeasurement 시 저장.
ClinicalHomeView 초기값을 store의 last 값으로.
다른 입력값(examiner, notes, subject_type 등)과 동일한 패턴.
2. BV 단계 제거
회의 결정: Step에서 BV는 불필요.
ClinicalStep enum에서 BV 제거 — 5단계(ALIGN_0CM ~ ALIGN_4CM)만 유지.
다른 직접 참조 없어 안전 (LabeledSegmented가 enum.values()로 자동 반영).
step.isAlignment 필드는 유지 (JSON meta 호환) — 이제 모든 step이 true.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
회의 결정: 잘못 측정한 경우를 없앨 방법이 없었음. 두 시점 모두 제공.
ClinicalSessionStore:
- discardAndDeleteFolder(): 측정 중 즉시 폐기. endMeasurement 호출 안 하고
currentSession + cyclesBuffer 비우고 폴더 전체 삭제.
- deleteFinalizedFolder(): lastFinalizedFolder 로컬 삭제 + reset.
LabdbClient.deleteSession(testId):
DELETE /api/v1/sessions/{testId} — Cascade로 모든 records 함께 제거.
204/200/404 모두 성공 처리 (404 = 이미 없음, 멱등).
401 → 자격 clear + Reregister, 403 → PendingApproval, 429 → RateLimit.
LabdbUploader.deleteSessionForFolder(folder):
labdb_upload.json에 sessionId 있으면 그것을, 없으면 sanitizeTestId(폴더명).
미업로드 폴더에 대해서도 안전하게 호출 가능 (404 처리).
ClinicalLiveView:
- 기존 End Session 단일 버튼 → Discard + End Session 두 버튼 (50/50 Row)
- Discard: 빨간 outline. confirm dialog → 폴더 삭제 + ClinicalHome 복귀
- 사이클 진행 중에도 누를 수 있음 (잘못 측정 즉시 인지 시)
ClinicalHomeView:
- Last saved 카드에 Upload 버튼 옆 (또는 단독) Delete 버튼 추가
- confirm dialog → labdb 서버 DELETE 시도 → 로컬 폴더 삭제 → state reset
- 서버 실패해도 로컬은 진행, Toast로 결과 표시
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
회의 결정: 자유 텍스트 ID 입력 대신 측정 대상을 PHANTOM/HUMAN/ETC 세
카테고리로 명확히 구분. JSON/labdb 저장 방식도 함께 변경.
데이터 모델 (ClinicalSession.kt):
- SubjectType enum 추가: PHANTOM / HUMAN / ETC
- val subjectId: String 제거 → val subjectType: SubjectType
- 폴더명: "{subjectId}_{...}" → "{SUBJECT_TYPE}_{...}"
예: PHANTOM_VBT26050202_SUPINE_ALIGN_0CM_2026-06-08_15-30-00
JSON 변경 (meta.json + measurement.json):
- "subject_id": "S001" → "subject_type": "PHANTOM"
labdb payload (LabdbUploader):
- params.subject_id → params.subject_type (서버는 free JSON이라 코드 변경 불필요)
UI (ClinicalHomeView):
- Subject ID OutlinedTextField 제거
- LabeledSegmented "Subject Type" 3개 버튼 (Phantom/Human/ETC)
- 마지막 선택 기억 (ClinicalSessionStore.lastSubjectType)
ClinicalLive 상단 sub line: "S=S001 · TV=300mL · Ab=22mm"
→ "Phantom · TV=300mL · Ab=22mm"
호환성: 기존 측정 폴더는 그대로. 새 폴더부터 새 형식 적용.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cycle당 IMU 다수 샘플(FIFO 순)이 오는데, 첫 샘플을 쓰면 piezo 캡처
시점보다 ~240~300ms 전의 오래된 자세값이 labdb에 들어감. 마지막
샘플로 바꿔 piezo 측정 시점과 가장 가까운 자세값이 기록되도록 함.
imu_samples 전체는 그대로 보존.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
서버 로그에서 두 원인 확인:
[error] POST /devices/register invalid byte sequence for encoding UTF8: 0x00
[error] POST /upload/json unsupported Unicode escape sequence
PostgreSQL JSONB는 NULL escape를 거부 (HTTP 500). BLE rid: 응답에서
US-ASCII 디코딩 후 .trim()은 whitespace만 제거하고 NULL/제어문자는
남기므로, firmware/hw/serial 문자열에 padding NULL이 묻은 채 payload로 직렬화됨.
두 군데 방어:
1. BleManager.rid: 파싱에서 0x00~0x1F,0x7F를 공백으로 치환 후 trim
2. LabdbClient.sanitizeForJsonb 추가 — register/uploadJson 직전에
payload를 재귀적으로 순회하며 모든 string에서 C0 control + DEL 제거
(탭/개행/캐리지리턴은 보존)
원인 조사: 서버 SSH로 /var/log/labdb.log 직접 확인 +
server.js의 /upload/json 핸들러 코드 검토 결과 서버는 받은 sensor/channels
JSON을 그대로 JSONB 컬럼에 INSERT.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
INTERNAL_ERROR(500) 원인 좁히기 위해 LabdbClient에 logcat 추가:
- POST /devices/register 보내는 정확한 payload (TAG=LabdbClient)
- 모든 응답의 HTTP code + raw body (앞 500자)
- IOException 발생 시 스택 trace
logcat 필터: tag:LabdbClient
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
labdb 자료 구분 코드를 "000"(외부 일반 VesiScan) → "001"(내부 임상 R&D)로
변경. ClinicalSessionStore에 const DATA_TYPE을 두고, meta.json과
measurement.json의 meta 블록에 "data_type": "001"을 함께 기록 — 폴더만
보고도 내부 임상 데이터임을 즉시 식별 가능.
LabdbUploader는 measurement.json의 meta.data_type을 우선 사용하고
없으면 "001" fallback. 향후 dataType이 늘어나도 단일 진실 출처(meta)에서
파생되도록 분리.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
End Session 시 measurement.json을 https://labdb.medithings.net/api/v1/upload/json
에 자동 업로드. dataType="000" (VesiScan 6채널). cycle 1개 = record 1개.
testId는 폴더명 그대로 사용 (40자 초과 시 prefix+hash로 축약).
Files:
- LabdbCredentials: apiKey/deviceId를 EncryptedSharedPreferences에 안전 저장
- LabdbClient: register/checkStatus/uploadJson + 5가지 에러 분기
(PendingApproval / Reregister / RateLimit / ApiError / Network)
- LabdbUploader: measurement.json → payload 변환 + fire-and-forget 업로드
성공 시 labdb_upload.json, 실패 시 labdb_upload_error.json 마커 작성
ClinicalSessionStore.endMeasurement: cycle>0 + active 상태일 때만 자동 트리거.
ClinicalHomeView: labdb 상태 카드(Register/Refresh) + 직전 측정 업로드 상태
표시 + 수동 Upload 재시도 버튼.
오프라인 큐는 아직 없음 (Phase C는 후속).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ClinicalSessionStore에 MeasurementCycle 누적 버퍼 추가. End Session 시
6채널 piezo + 15 IMU 샘플을 단일 measurement.json으로 묶어 저장하여
분석 PC에서 한 줄(json.load) 로드 가능. 기존 CSV 로거는 유지.
ClinicalLiveView는 pendingPiezo 패턴으로 piezo(reb×6+raa)와
imu(rim)를 한 cycle로 묶어 addCycle() 호출.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
기존: 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>
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>
이전: 한 세션이 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>
회사 내 알고리즘 개발용 임상 시험(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>
## 측정 화면 (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>
[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>
- #1 배터리 최적화 페이지 이동 제거 (ForegroundService+WakeLock만)
- #2 Auto 첫 측정 대기 시 "Measuring... (n/5)" loading 표시
- #3 Placement 멘트 정리 — 방향만 남기기 (CH 언급 제거)
- #4 CH0~CH5 dot row 개발자 모드 전용
- #5 Spot 재시도 로직 (최대 6회, 유효 3개 목표, 300ms 간격)
- LR 좌우 조건 완화: ±8mm, symmetry≥0.5, lrDev≤0.35
- |len4-len5|≤10 → LATERAL 건너뛰고 바로 GREEN
- GREEN 미달 시 CV만 간당간당하면 GREEN 유지 ("Almost there...")
- CV 기본 임계 0.08→0.12, 디바운스 3초→1.5초
- BV 타원 피팅 outlier 반복 제거 (appshare #22)
- README v3 업데이트 (3모드 placement, BV 파이프라인, 상태표)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- BLE 연결 시 REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 다이얼로그 표시
- 이미 제외 상태면 스킵
- Manifest에 REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 권한 추가
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BleDebugLogger:
- 매 entry마다 즉시 Downloads/VesiScan_BLE_*.log에 append
- 새 BLE 연결 시 새 파일 생성 + 헤더 기록
- 기존 메모리 내 로그 + UI 표시는 동일 유지
- 화면 잠금/Doze/앱 종료에도 로그 보존
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Foreground Service (BleForegroundService.kt):
- BLE 연결 시 자동 시작, 해제 시 자동 중단
- PARTIAL_WAKE_LOCK (최대 4시간)
- Notification: "VesiScan - BLE 기기 연결 중..."
- connectedDevice foregroundServiceType
- Doze 모드에서도 BLE 통신 유지
- keepScreenOn도 유지 (추가 안전장치)
Detachment 개선:
- THR_MEAN/STD 100→30 (완전 탈착만 감지, 거리 약화 무시)
- 6채널 모두 수신됐을 때만 체크 (BLE 거리 문제와 구분)
- Placement: if(false) 제거 → 활성화 + "센서가 부착되지 않았습니다"
- Monitoring: 정상 복귀 시 LED OFF
Placement 가이드 모드:
- Simple (기본): 3+채널 → 바로 좌우 (기존)
- Boundary: 올라갔다 내려오기 패턴 (경계 감지 → 최적점 복귀)
- 설정 톱니바퀴에 Simple/Boundary 스위치
BLE 로그:
- 역순 표시 (최신이 위) — 스크롤 위치 유지
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
성능:
- 측정 콜백 분석(TGC/Otsu/BV)을 백그라운드 Thread로 이동
- UI 업데이트만 handler.post로 메인 스레드 복귀
- 메인 스레드 블로킹으로 인한 BLE 패킷 드롭 방지
- 분석 소요시간 로그 출력 (Analysis took Xms)
- Toast도 handler.post로 감싸서 Thread-safe
Placement:
- 6채널 그래프: 데이터 없는 채널도 "No data" 빈 칸 표시 (항상 6칸)
CSV:
- 일별 파일 → 세션별 파일 (YYYY-MM-DD_HHmmss.csv)
- BLE 연결 시 자동 newSession()
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
scan_id,timestamp,volume_ml,lr_ratio,threshold,dps,sg_filter,channel,s0,...
1,14:30:12,305.2,1.20,1150,1.771,ON,CH0,1823,...
매 측정 시점의 설정값이 ADC와 함께 기록되어
추후 분석 시 어떤 파라미터로 측정했는지 추적 가능
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- exportToDownloads(): Downloads/VesiScan_BLE_날짜.log 로 저장
- 실패 시 fallback으로 내부 저장소에 저장
- Toast로 저장 경로 안내
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
- Fix BLE API deprecation warnings with API 33+ version checks
- Move isServiceReady to onDescriptorWrite for reliable CCCD setup
- Fix battery polling: immediate query on connect, pause during NIRS streaming
- Add mcj retry mechanism for NIRS streaming reliability
- Add debug logging to BLE, NIRS parsing, and processing pipeline
- Add catheter count feature to Piezo monitoring (persistent count, add dialog, auto-decrement on catheterize)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>