# VasCURA589 코드 분석 (com/laseroptek/raman) 이 문서는 `app/src/main/java/com/laseroptek/raman` 폴더를 기준으로 구조와 동작을 요약한 분석 문서입니다. 요청 경로의 `com/leaseroptek/raman`는 오타로 보이며, 실제 패키지는 `com/laseroptek/raman`입니다. ## 1) 폴더 개요 - 분석 대상 파일 수: **246개** - 상위 패키지 구성 - `const` (12): 프로토콜/테이블/상수 - `data` (57): 모델, DB/Serial/Preference 데이터 소스 - `di` (4): Hilt DI 모듈 - `navigation` (5): 라우트/네비게이션 그래프 - `repository` (3): 데이터 접근 추상화 계층 - `ui` (123): Compose UI + ViewModel - `utils` (40): 확장 함수, 보조 유틸, 커스텀 컴포넌트 ## 2) 아키텍처 요약 전체적으로 **Compose + Hilt + Room + DataStore + Serial(NDK)** 구조입니다. - Application - `VasCURA589App`: Hilt 진입점, Timber 로그 트리 설정 - Activity - `ui/MainActivity`: 전체 초기화 오케스트레이션, 풀스크린/시스템바 제어, APK 선택 이벤트 처리 - Presentation - `ui/screens/...`: Home, Info, Config, Lock, Engineer 화면 - 공용 상태는 `MainViewModel`이 중심으로 보유 - Domain/Data Access - `repository/*Repository`: Serial/Preference/DB 접근 래퍼 - Data Source - `data/source/serial/SerialPort`: native `serial_port` 라이브러리 로딩 + FD 기반 통신 - `data/source/db/*`: Room DB(`SerialLog`) 접근 - `PreferenceRepository`: DataStore 기반 앱 설정/테이블/카운트 저장 ## 3) 실행 흐름 (부팅~운영) 1. `MainActivity.onCreate()` 2. `initialize()`에서 `MainViewModel.performFullInitialization()` 실행 3. 초기화 완료 후 UI 활성화(`setInitialized(true)`) 4. 시리얼 시작 - `txPacketOnce()`: 초기 설정 패킷 송신 - `rxPacketLoop()`: 수신 루프 + 패킷 파싱/상태 반영 - `txPacketLoop()`: 주기적 heartbeat 성격 송신 핵심 포인트: - 초기화는 IO 스레드에서 병렬 로딩(`launch + joinAll`)로 처리 - `MainScreen`은 `isInitialized`를 기준으로 로딩 화면 -> 본 화면 전환 - 통신 타임아웃 감시(`monitorConnectionTimeout`)로 안정성 상태 추적 ## 4) 주요 패키지별 역할 ### `ui/screens/main` - `MainViewModel`: 앱의 사실상 중앙 상태 저장소 - 레이저 파라미터(펄스/플루언스/반복률), 카운터, 경고/에러, 온도, DCD, 에너지 디텍터 상태 관리 - 시리얼 TX/RX 패킷 생성/파싱 - Preference 로드/저장, DB 로그 적재/트리밍 - 핸드피스 타입 변경 시 에너지/Hz 테이블 재적용 ### `ui/screens/home` (30개) - 실제 시술/운영 메인 UI - 슬라이더 기반 파라미터 조절, 스탠바이/발진, 카운트/프리셋/DCD 제어 ### `ui/screens/info` (6개) - 모니터링/차트 화면 - `InfoViewModel`은 차트 라인 표시 상태(체크박스)만 관리 - 실데이터(온도, 램프카운트 등)는 `MainViewModel`에서 수신 ### `ui/screens/config` (7개) - 볼륨, 가이드빔, 언어/시간 설정, 엔지니어 모드 진입 지점 ### `ui/screens/engineer` (38개) - 서비스/캘리브레이션/로그/버전/임계치/시리얼번호 등 유지보수 기능 - Voltage Table/수명/온도 한계/Energy detect 기준값 조정 ### `ui/screens/lock` (3개) - PIN 기반 잠금/해제 플로우 ## 5) 시리얼 통신 구조 - 송신: `MainViewModel.txPacket()` - `READ_WRITE` + `CMD`로 패킷 조합 - WRITE 시 CS(checksum) 포함 - 수신: `rxPacketLoop()` - 버퍼 누적 후 `findCompletePackets()`로 STX(0x21)~ETX(0x0d) 패킷 분리 - `procRxPacket()`에서 CMD별 모델 변환 및 상태 업데이트 처리 예: - `CMD.LASER_STATUS`: 발진 상태 반영, 카운트 증가, 알림음 - `CMD.HAND_PIECE`: 타입 변경 감지 후 테이블/각도/상태 재초기화 - `CMD.TEMPERATURE`: 차트 큐 갱신 + 임계값 비교 - `CMD.ENERGY_DETECT`: 버전/마운트/측정 상태 처리 및 Good/Not Good 응답 ## 6) 저장 전략 - **DataStore (`PreferenceRepository`)** - 카운트, 볼륨, 가이드빔, 프리셋, Spray DCD, Voltage Table, 임계값, 시리얼 번호 리스트 등 - **Room (`RamanDatabase`, `SerialLogDao`)** - 주요 시리얼 패킷 로그 저장 - `trimLogs(limit)`로 로그 최대 개수 유지 ## 7) 눈에 띄는 구현 특성 - `MainViewModel`에 상태/로직이 집중된 구조(단일 허브) - 디버그 로그(Timber)와 패킷 헥사 덤프 활용 - 미리보기/테스트를 위한 fake serial repository 제공 - 패키지 네이밍이 일부 혼재: - 예: 파일 경로는 `data/source/db`인데 패키지 선언은 `data.datasource.db`인 클래스가 존재 ## 8) 빠른 참고 파일 - 앱 진입: `VasCURA589App.kt`, `ui/MainActivity.kt` - 내비게이션: `navigation/Routes.kt`, `navigation/graphs/MainNavGraph.kt` - 핵심 로직: `ui/screens/main/MainViewModel.kt` - 시리얼: `data/source/serial/SerialPort.kt`, `repository/SerialPortRepository.kt` - 설정 저장: `repository/PreferenceRepository.kt` - 로그 DB: `data/source/db/*`, `repository/DatabaseRepository.kt` ## 9) MainViewModel.kt 별도 분석 ### 개요 - 파일: `app/src/main/java/com/laseroptek/raman/ui/screens/main/MainViewModel.kt` - 규모: 약 **2,288 라인** - 역할: 앱 전체의 공용 상태 + 시리얼 프로토콜 처리 + 로컬 저장소 동기화를 한곳에서 담당하는 **중앙 오케스트레이터** ### 의존성 주입 구조 - `PreferenceRepository`: DataStore 기반 영속값 로드/저장 - `SerialPortRepository`: 시리얼 포트 open/write/close - `DatabaseRepository`: 시리얼 로그 조회/저장/트리밍 - `DispatcherProvider`: 코루틴 디스패처 추상화 - `Context`: 알림음 재생 및 시스템 리소스 접근 ### 상태(State) 도메인 분류 - UI/초기화 제어 - `isInitialized`, 팝업 노출 플래그, `apkUpdateEvent` - 레이저 파라미터 - `pulseAngle`, `fluenceAngle`, `repetitionAngle` - `energyTable`, `hzTable`, `voltageTable`, `fluenceList`, `repetitionList` - 프리셋/옵션 - `presetList`, `selectedPresetIndex` - `sprayDcdList`, `selectedSprayDcdIndex` - 카운터/수명 - `laserCount`, `lampCount`, `dcdCount`, `hpCount`, `lifeTime`, `opTimeHour` - 장비 상태 패킷 - `laserStatus`, `handPiece`, `warning`, `error`, `version` - `temperature`, `temperature_write`, `qSwitch`, `dcdGas`, `sprayDcd`, `oven`, `purgeBubble` - 에너지 디텍션 - `energyVersion`, `energyHandpiece`, `energyControl`, `energyMeasured`, `energyMeasuredWrite`, `energyDetectRefer2` - 모니터링/로그 - `chartDataQueue`, `serialLogList`, `isCommunicationStable` ### 핵심 메서드 흐름 - 초기화 - `performFullInitialization()` - DB 정리/온도 이력 로딩 후, 다수의 preference 로딩을 `launch + joinAll`로 병렬 수행 - 시리얼 송신 - `txPacketOnce()`: 버전/QSwitch/GuideBeam/DCD/Spray 기본값 송신 - `txPacketLoop()`: 주기적 명령(핸드피스, 샷카운트, 경고, 온도) 폴링 - `txPacket()`: CMD/READ_WRITE에 따라 실제 프로토콜 패킷 생성 - 시리얼 수신 - `rxPacketLoop()`: 포트 수신 collect - `findCompletePackets()`: 버퍼에서 STX~ETX 완성 패킷 분리 - `procRxPacket()`: CMD 별 디코딩/상태 반영/로그 저장 - `monitorConnectionTimeout()`: 응답 지연 watchdog ### 비즈니스 로직 포인트 - `CMD.HAND_PIECE` 수신 시 - 핸드피스 변경 여부 확인 후 레이저 상태/카운트/테이블/슬라이더 초기화 - handpiece type별 lifetime 카운트 누적 - `CMD.LASER_STATUS` 수신 시 - 레이저 ON 상태에서 샷/램프/HP/DCD 카운트 갱신 - 조건에 따라 알림음 재생 및 스탠바이 전환 처리 - `CMD.TEMPERATURE` 수신 시 - 차트 큐에 타임스탬프 데이터 적재 - 설정 임계값(`temperature_write`)과 비교해 경고/에러 조건 반영 - `CMD.ENERGY_DETECT` 수신 시 - measured 값과 refer2 기준 비교 후 Good(0x47)/Not Good(0x4E) 응답 송신 ### 파라미터 계산 로직 - `txLaserStatusEntry(laserStatus: Int)` - 현재 각도 -> step 변환 - step -> pulse/fluence/repetition 매핑 - `(pulse, fluence)`로 energy 조회 - `calculateInterpolatedC()`로 목표 전압 계산(선형 보간) - 최종 `LaserStatus` 패킷 송신 ### 저장소 동기화 패턴 - 상태 변경 직후 `save*ToPreference()` 호출하는 즉시 반영형 패턴 - 앱 시작 시 `load*FromPreference()`로 메모리 상태 복원 - 시리얼 로그는 특정 CMD만 DB에 적재하고, `trimSerialLog()`로 최대 개수 유지 ### 설계 관점 평가 - 장점 - 장비 프로토콜 처리, UI 상태, 저장소 동기화가 한 ViewModel에서 일관되게 연결됨 - 초기화 병렬화로 부팅 지연 완화 - 통신 watchdog/버퍼 파싱 등 실장비 대응 로직이 명확함 - 트레이드오프 - 단일 ViewModel 책임이 매우 큼(상태/함수 밀집) - 기능 분리(예: SerialHandler, PreferenceSync, LaserDomain) 여지가 큼 ### 유지보수 시 우선 확인 포인트 - 신규 CMD 추가 시 - `txPacket()` 인코딩 + `procRxPacket()` 디코딩을 함께 확장 - 파라미터 표 변경 시 - `loadFluenceTable()`, `loadHzTable()`, `calculateInterpolatedC()` 영향 확인 - 성능/안정성 이슈 시 - `txPacketLoop()` 주기, `RX_TIMEOUT_THRESHOLD`, DB 로그 적재량 점검