Initial commit: Dabit Time Manager project
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>
This commit is contained in:
88
network/packet_parser.py
Normal file
88
network/packet_parser.py
Normal file
@@ -0,0 +1,88 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user