=== 리플렉터 검사 시스템 실제 구현 현황 ===
분석 일시: 2026-05-07
전체 진행률: 27/35 (77%)

판정 기준
  ✅ 완료     : 실제 동작 코드, placeholder 없음
  🔶 부분구현 : 일부는 실제 동작, 일부 print()/pass placeholder 존재
  ❌ 미구현   : 코드 없음 또는 전부 pass/print placeholder
  🔲 블로킹   : 코드 외부 의존성(장비 설정/데이터)으로 구현 불가

────────────────────────────────────────────────────────────

[환경설정] 6/7

  ✅  1. 코그넥스 IP/포트 연결
      근거: settings_page.py L.520~567 _on_cognex_connect()에서
            InSightCamera().connect(ip, port) 실제 호출.
            insight.py L.22~51 소켓 연결, FTP 세션 수립 실제 구현.

  🔶  2. 이미지 설정 팝업 (GV/SV 셀 명령)
      근거: gui/dialogs/image_settings_dialog.py 자체는 완성.
            _gv() L.126~137 GV{cell} 실제 전송,
            _sv() L.172~182 SV{cell} 실제 전송.
            BUT settings_page.py에 이 다이얼로그를 여는 버튼 없음.
            코드는 있으나 UI에서 접근 불가.

  ✅  3. Basler 카메라 설정 (ExposureTime/Gain)
      근거: settings_page.py L.278~301 _on_basler_apply()에서
            camera.ExposureTime.SetValue(float(exposure)),
            camera.Gain.SetValue(float(gain)) 실제 호출.

  🔶  4. MySQL DB 연결 버튼
      근거: settings_page.py L.414
            btn.clicked.connect(lambda: print("[설정] DB 연결")) → print() placeholder.
            MySQLClient.connect() 자체(db/mysql_client.py L.9~15)는
            pymysql.connect() 실제 구현됨.
            연결 테스트(_test_db() L.805~822)도 실제 pymysql.connect() 호출.
            단, 설정 탭 "연결" 버튼이 placeholder.

  ✅  5. AI 모델 로드 (YOLO)
      근거: settings_page.py L.694~720 _do_load_model()에서
            Detector().load_model() 실제 호출.
            detector.py L.19 YOLO(model_path) 지연 로딩 실제 구현.
            L.769~774 앱 시작 시 config.json 경로로 자동 로드.

  ✅  6. 설정 저장/불러오기 (config.json)
      근거: settings_page.py L.588~608 _load_config()/_save_config()
            json.load/json.dump 실제 구현.
            L.646~688 _on_save_all()/_on_load_all() 실제 구현.

  ✅  7. 전체 연결 테스트 버튼
      근거: settings_page.py L.780~844.
            코그넥스: socket.connect() 실제 호출 (L.786~792).
            Basler: pylon.TlFactory.EnumerateDevices() 실제 호출 (L.794~803).
            DB: pymysql.connect() 실제 호출 (L.805~822).

────────────────────────────────────────────────────────────

[제품 등록] 5/6

  ✅  8. 제품 목록 표시 (QListWidget)
      근거: register_page.py L.232~241 _populate_list()에서
            REFLECTOR_LIST 10개를 QListWidget에 표시. 실제 구현.

  ❌  9. MES DB 불러오기
      근거: register_page.py L.72~79 "MES 불러오기" 버튼 setEnabled(False).
            db/mysql_client.py L.26~28 get_reflector_list() → pass placeholder.
            연결도 없고 쿼리도 없음.

  ✅ 10. Type L/R 시각적 표시 (화살표)
      근거: register_page.py L.259~264 _on_select()에서
            RH이면 "→" (파란색), LH이면 "←" (주황색) 실제 표시.

  ✅ 11. 캡처 기능 (trigger_and_get_image)
      근거: register_page.py L.289 self._insight.trigger_and_get_image() 실제 호출.
            insight.py L.99~103 trigger_and_get_image(): SE8 트리거 → sleep(1.0)
            → get_image() 실제 구현.

  ✅ 12. PatMax 패턴 등록
      근거: register_page.py L.322~326 self._matcher.train(img, rid, info, roi=self._roi)
            실제 호출.
            logic/pattern_matcher.py: ORB 특징점 자동 검출 + Canny 엣지 NCC fallback
            실제 구현. (Cognex Explorer PatMax 대신 Python ORB로 대체)

  ✅ 13. 저장 기능 (products.json)
      근거: register_page.py L.335~344 _write_saved()에서 json.dump 실제 저장.
            이미지 BMP도 cv2.imwrite() 실제 저장 (L.332~333).

────────────────────────────────────────────────────────────

[검사] 7/9

  ✅ 14. 그룹 A/B 최대 4종 제한
      근거: inspect_page.py L.493~505 _on_group_changed()에서
            len(checked) > GroupManager.MAX_PER_GROUP(=4) 이면 체크 해제.
            group_manager.py L.11 MAX_PER_GROUP = 4.

  ✅ 15. 그룹 A/B 수동 전환
      근거: inspect_page.py L.507~511 _on_switch()에서
            self._groups.switch_group() 실제 호출.
            group_manager.py L.22~25 switch_group() A↔B 전환 실제 구현.

  ✅ 16. 코그넥스 트리거 + 이미지 표시
      근거: inspect_page.py L.117~144 InspectWorker._cognex_work()에서
            software_trigger() 실제 호출, get_image() FTP 수신 실제 호출.
            L.133 cognex_image_ready.emit(raw) → _display_cognex_image() 실제 표시.
            (QTimer 방식 아닌 QThread 파이프라인으로 구현)

  ✅ 17. PatMax 결과 읽기 + 모델 판별
      근거: inspect_page.py L.136 read_patmax_results() + L.138 match_image() 병합.
            inspector.py L.49~84 GV A27/A77/A127/A177 읽기, #ERR 판별 실제 구현.
            inspector.py L.88~117 identify_model() 최고점수 선택 실제 구현.

  ✅ 18. Basler 이미지 표시
      근거: inspect_page.py L.158~172 self._basler.capture() 실제 호출.
            basler.py L.36~46 GrabOne(5000) 실제 구현.
            inspect_page.py L.644~681 _display_basler_image() 실제 표시.

  ✅ 19. YOLOv8 추론 + 불량 박스
      근거: inspect_page.py L.165~169 self.detector.detect(frame) 실제 호출.
            detector.py L.32~58 self._model(image, verbose=False) 실제 추론.
            inspect_page.py L.651~663 바운딩 박스 오버레이 실제 구현.
            (AI 모델 파일 로드 시 동작)

  ✅ 20. Pass/Fail AND 판별
      근거: inspect_page.py L.199 self._inspector.judge(cognex_pass, basler_pass).
            inspector.py L.119~120 "PASS" if cognex_pass and basler_pass else "FAIL".

  ❌ 21. PLC 불량 신호 전송
      근거: inspect_page.py 전체 어디에도 plc_client.send_signal() 호출 없음.
            plc_client.py L.20~22 send_signal() → pass placeholder.
            settings_page.py L.462 PLC 연결 버튼도 print() placeholder.

  ✅ 22. 양품/불량 카운터 집계
      근거: inspect_page.py L.588~596 _on_result()에서
            total/pass/fail/unknown 실시간 카운터 업데이트 실제 구현.

────────────────────────────────────────────────────────────

[재학습] 6/6

  ✅ 23. 이미지 폴더 선택 + 목록 표시
      근거: retrain_page.py L.885~901 _on_select_folder()에서
            QFileDialog.getExistingDirectory() 실제 호출, 목록 QListWidget 표시.

  ✅ 24. 불량 클래스 선택 토글 (4개)
      근거: retrain_page.py L.948~958 _on_class_select()에서
            버튼 스타일 전환, canvas.set_class() 실제 구현.
            선택된 박스 클래스도 즉시 변경됨.

  ✅ 25. 바운딩 박스 라벨링 + YOLO .txt 저장
      근거: retrain_page.py LabelingCanvas L.274~401 마우스 드래그 박스 실제 구현
            (new_box/move/resize/pan 4가지 모드, 8핸들 리사이즈, Ctrl+Z 실행취소).
            L.982~997 _on_label_save() YOLO format .txt 실제 저장.
            L.256~269 get_yolo_labels() cx/cy/nw/nh 정규화 실제 구현.

  ✅ 26. YOLOv8 학습 실행 (QThread)
      근거: retrain_page.py L.1006~1046 _on_train_start()에서
            TrainWorker(...).start() 실제 호출.
            trainer.py L.101~162 YOLO("yolov8n.pt").train() 실제 구현.
            trainer.py L.55~97 prepare_dataset() train/val split 실제 구현.

  ✅ 27. 학습 로그 실시간 표시
      근거: retrain_page.py L.1059~1063 _on_log() → QTextEdit.append() 실제 구현.
            trainer.py L.14~41 _StdoutCapture: sys.stdout 가로채기로
            YOLOv8 Epoch 로그 실시간 캡처 실제 구현.

  ✅ 28. 모델 저장 (best.pt 복사)
      근거: retrain_page.py L.1084~1109 _on_model_save()에서
            shutil.copy(src, dest) 실제 구현.
            trainer.py L.151~156 학습 후 자동 best.pt 복사 실제 구현.
            config.json ai.model_path도 자동 갱신.

────────────────────────────────────────────────────────────

[공통/기타] 5/7

  ✅ 29. 하단 상태바 업데이트
      근거: main_window.py L.280~304 update_connection_status()에서
            코그넥스/Basler/DB 도트 색상 및 텍스트 실시간 갱신.
            L.174~178 시그널 연결로 연결 상태 변경 시 자동 업데이트.

  ❌ 30. PLC 통신 모듈
      근거: plc_client.py L.9~25
            connect(), disconnect(), send_signal(), read_signal() 전부 pass placeholder.
            주석에 "통신 방식 미확정: Modbus TCP / MC프로토콜 / OPC-UA 중 선택 후 구현"

  ❌ 31. MySQL DB 쿼리 메서드
      근거: db/mysql_client.py L.26~35
            get_reflector_list() → pass,
            save_reflector() → pass,
            save_inspection_result() → pass.
            connect() 자체는 pymysql.connect() 실제 구현이나
            모든 쿼리 메서드가 placeholder.

  ✅ 32. PyInstaller 패키징 (build.py)
      근거: build.py 존재. PyInstaller.__main__.run() 실제 호출.
            --onefile --windowed, hidden-import 목록,
            pypylon/ultralytics collect-all 포함 실제 구현.

  ✅ 33. AI 모델 + 검사 연동
      근거: inspect_page.py L.165 self.detector.detect(frame) 실제 호출.
            L.546~549 update_detector() → self._worker.detector 업데이트 실제 구현.
            main_window.py L.106 Detector() 생성, L.273~275 update_detector() 연동.

  🔲 34. 트리거 모드 수동 변경
      근거: 코드 외부 작업 — Cognex In-Sight Explorer에서
            job 파일의 트리거 모드를 "소프트웨어 트리거"로 변경 필요.
            소프트웨어 트리거 명령(SE8)은 insight.py에 구현되어 있으나
            카메라 job 설정이 외부 의존성.

  🔲 35. AI 학습 데이터 수집
      근거: trainer.py L.55~97 prepare_dataset()은 이미지+라벨 쌍 처리 가능.
            retrain_page.py 라벨링 도구도 완성.
            실제 불량 이미지 데이터 수집은 운영 중 진행 필요 (외부 의존성).

────────────────────────────────────────────────────────────

요약

  ✅ 완료     27개 (77%)
  🔶 부분구현  2개 (6%)  → #2 이미지설정 팝업 버튼 없음, #4 DB 연결버튼 placeholder
  ❌ 미구현    4개 (11%) → #9 MES 불러오기, #21 PLC 신호전송, #30 PLC 모듈, #31 DB 쿼리
  🔲 블로킹    2개 (6%)  → #34 트리거모드 설정, #35 AI 학습 데이터

잔여 작업 우선순위 제안
  1순위 (빠른 완성 가능)
    - #2  : settings_page.py에 ImageSettingsDialog 열기 버튼 추가 (1~2줄)
    - #4  : settings_page.py DB 연결 버튼에 MySQLClient.connect() 실제 호출 연결
  2순위 (통신 방식 결정 후)
    - #21 : PLC 신호 전송 — 방식 결정(Modbus TCP 권장) 후 plc_client.py 구현
    - #30 : PLC 모듈 전체 구현
  3순위 (DB 서버 준비 후)
    - #31 : DB 쿼리 메서드 구현 (get_reflector_list, save_inspection_result)
    - #9  : MES DB 불러오기 연결
