# 검사 판별 로직 — PatMax 결과 판독 + 모델 판별 + Pass/Fail 판정 import cv2 import numpy as np from db.sql_client import SQLClient from logic.products import model_display_label class Inspector: def __init__(self): self._pattern_cells: dict = {} def set_pattern_cells(self, cells: dict): """DB 기반 PatMax 셀 매핑 (refresh_wk_results 시 갱신).""" self._pattern_cells = cells or {} # ── Python PatMax 매칭 (주 경로) ─────────────────────────────────── # def match_image(self, image_bytes: bytes, matcher: "PatternMatcher") -> dict: """ FTP로 받은 이미지 바이트를 Python PatternMatcher로 매칭. 반환 형식은 read_patmax_results와 동일하여 identify_model에서 그대로 사용. """ if not image_bytes: return {} arr = np.frombuffer(image_bytes, dtype=np.uint8) img = cv2.imdecode(arr, cv2.IMREAD_UNCHANGED) if img is None: return {} all_scores = matcher.match_all(img) results = {} for pid, score in all_scores.items(): info = matcher.get_product_info(pid) if info is None: continue results[f"PY_{pid}"] = { "matched": score >= matcher.score_threshold, "score": score, "model": info, "raw": f"python_match={score:.1f}", } return results # ── Cognex GV 셀 방식 (fallback) ────────────────────────────────── # def read_patmax_results(self, insight) -> dict: """PatMax 결과 셀 조회 → #ERR이면 실패, 그 외 점수 파싱.""" results = {} for cell, model_info in self._pattern_cells.items(): try: insight._send(f"GV{cell}") code = insight._read_line() if code != "1": results[cell] = { "matched": False, "score": 0.0, "model": model_info, "raw": "" } continue value = insight._read_line() if "#ERR" in value or value.strip() == "": results[cell] = { "matched": False, "score": 0.0, "model": model_info, "raw": value } else: # "(736.1,742.0) -1.8 = 82.9" 형식에서 = 뒤 값 추출 try: score = float(value.split("=")[-1].strip()) except Exception: score = 0.0 results[cell] = { "matched": True, "score": score, "model": model_info, "raw": value } except Exception as e: print(f"[PatMax] {cell} 읽기 오류: {e}") results[cell] = { "matched": False, "score": 0.0, "model": model_info, "raw": "" } return results # ── 공통: 모델 판별 + 판정 ──────────────────────────────────────── # def identify_model(self, results: dict, allowed_model_ids: "list | None" = None, allowed_article_ids: "set | None" = None) -> dict: """매칭된 패턴 중 점수가 가장 높은 것을 선택해 허용 여부 판별.""" matched_patterns = [ (cell, info) for cell, info in results.items() if info["matched"] ] if not matched_patterns: return { "matched": False, "in_allowed": False, "model": None, "score": 0.0, "cognex_pass": False, "status": "인식 불가" } _best_cell, best_info = max(matched_patterns, key=lambda x: x[1]["score"]) model = best_info["model"] label = model_display_label(model) if allowed_article_ids is not None: in_allowed = ( SQLClient._norm_id(model.get("article_id")) in allowed_article_ids ) else: in_allowed = model["id"] in (allowed_model_ids or []) return { "matched": True, "in_allowed": in_allowed, "model": model, "score": best_info["score"], "cognex_pass": in_allowed, "status": ( f"{label} ({best_info['score']:.1f}점)" if in_allowed else f"작업 대상 외: {label}" ), } def judge(self, cognex_pass: bool, basler_pass: bool) -> str: return "PASS" if cognex_pass and basler_pass else "FAIL"