Python-based time management application with UDP discovery, TCP protocol communication, time sync, and drift monitoring. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
89 lines
2.9 KiB
Python
89 lines
2.9 KiB
Python
"""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
|