"""DIBD 응답 패킷 파싱 (가변 길이 지원)""" from models.controller import Controller from config import PROTOCOL_ENCODING def parse_dibd_packet(data: bytes) -> Controller | None: """DIBD 응답 패킷을 파싱하여 Controller 객체를 반환. 패킷 길이: - 이더넷 전용 모델: 174바이트 (DNS Name까지) - WiFi 지원 모델: 222바이트 (AP Mode까지) 패킷 구조 (0x88 응답, \r\n으로 필드 구분): 0-5: 'DIBD\\r\\n' 6-24: MAC (17자) + \\r\\n 25-41: Local IP (15자) + \\r\\n 42-58: Subnet Mask (15자) + \\r\\n 59-75: Gateway IP (15자) + \\r\\n 76-82: Port (5자) + \\r\\n 83-86: DHCP Mode (2자) + \\r\\n 87-103: Server IP (15자) + \\r\\n 104-110: Server Port (5자) + \\r\\n 111-114: Server Mode (2자) + \\r\\n 115-136: Name (20자) + \\r\\n 137-141: KeepAlive (3자) + \\r\\n 142-173: DNS Name (30자) + \\r\\n ← 174바이트 여기까지 174-195: AP SSID Name (20자) + \\r\\n ← 선택 196-217: AP SSID PW (20자) + \\r\\n ← 선택 218-221: AP Mode (2자) + \\r\\n ← 선택 """ try: text = data.decode(PROTOCOL_ENCODING, errors="replace") except Exception: return None # 최소 길이: KeepAlive 필드까지 = 142바이트 if len(text) < 142: return None if not text.startswith("DIBD"): return None ctrl = Controller() ctrl.mac = text[6:23].strip() ctrl.ip = _normalize_ip(text[25:40].strip()) ctrl.subnet = _normalize_ip(text[42:57].strip()) ctrl.gateway = _normalize_ip(text[59:74].strip()) ctrl.port = _safe_int(text[76:81].strip(), 5000) ctrl.dhcp_mode = _safe_int(text[83:85].strip(), 30) % 30 ctrl.server_ip = _normalize_ip(text[87:102].strip()) ctrl.server_port = _safe_int(text[104:109].strip(), 0) ctrl.server_mode = _safe_int(text[111:113].strip(), 30) % 30 ctrl.name = text[115:135].strip() ctrl.keep_alive = _safe_int(text[137:140].strip(), 0) # DNS Name (선택 - 174바이트 이상) if len(text) >= 172: ctrl.dns_name = text[142:172].strip() # AP SSID (선택 - 222바이트 패킷) if len(text) >= 194: ctrl.ap_ssid_name = text[174:194].strip() if len(text) >= 216: ctrl.ap_ssid_pw = text[196:216].strip() if len(text) >= 220: ctrl.ap_mode = _safe_int(text[218:220].strip(), 30) % 30 return ctrl def _normalize_ip(ip_str: str) -> str: """패딩된 IP(예: '192.168.000.074')를 일반형(예: '192.168.0.74')으로 변환.""" try: parts = ip_str.split(".") return ".".join(str(int(p)) for p in parts) except (ValueError, IndexError): return ip_str def _safe_int(s: str, default: int = 0) -> int: try: return int(s) except (ValueError, TypeError): return default