""" DAT10: 오프라인 단말 간 1차 P2P/USB 재고 데이터 교환 및 베이스라인 탐지 모델 - P2P 재고 상태 교환 및 단순 LWW(Last-Write-Wins) 동기화 - 룰 기반 베이스라인 재고 급감/안전재고 미달 탐지 """ from typing import Dict, List, Any import datetime class P2PInventoryBaselineSync: def __init__(self, node_id: str): self.node_id = node_id self.local_state: Dict[str, Dict[str, Any]] = {} # nsn_code -> {"stock": int, "lamport": int, "updated_at": str, "source_node": str} def update_local_inventory(self, nsn_code: str, stock: int, lamport: int) -> Dict[str, Any]: self.local_state[nsn_code] = { "stock": stock, "lamport": lamport, "updated_at": datetime.datetime.utcnow().isoformat() + "Z", "source_node": self.node_id } return self.local_state[nsn_code] def merge_p2p_payload(self, incoming_state: Dict[str, Dict[str, Any]]) -> Dict[str, Any]: """P2P/USB를 통해 수신된 피어 노드의 상태를 LWW 규칙으로 1차 병합""" merged_count = 0 conflicts = [] for nsn, remote_data in incoming_state.items(): if nsn not in self.local_state: self.local_state[nsn] = remote_data merged_count += 1 else: local_data = self.local_state[nsn] # Lamport Clock 비교 if remote_data["lamport"] > local_data["lamport"]: self.local_state[nsn] = remote_data merged_count += 1 elif remote_data["lamport"] == local_data["lamport"]: # 타임스탬프 동률 시 노드 ID 사전식 순서 if remote_data["source_node"] > local_data["source_node"]: self.local_state[nsn] = remote_data merged_count += 1 elif remote_data["stock"] != local_data["stock"]: conflicts.append({ "nsn_code": nsn, "local_stock": local_data["stock"], "remote_stock": remote_data["stock"], "lamport": local_data["lamport"], "peer_id": remote_data["source_node"] }) return { "merged_count": merged_count, "conflicts_count": len(conflicts), "conflicts": conflicts, "current_state": self.local_state } class BaselineRuleDetector: def __init__(self, safety_threshold: int = 10, drop_ratio_threshold: float = 0.5): self.safety_threshold = safety_threshold self.drop_ratio_threshold = drop_ratio_threshold def evaluate_inventory_health(self, inventory_states: Dict[str, Dict[str, Any]]) -> List[Dict[str, Any]]: alerts = [] for nsn, data in inventory_states.items(): stock = data.get("stock", 0) if stock < self.safety_threshold: alerts.append({ "nsn_code": nsn, "alert_type": "SAFETY_STOCK_VIOLATION", "severity": "WARNING", "current_stock": stock, "threshold": self.safety_threshold, "reason": f"잔여 재고({stock})가 안전 재고 기준({self.safety_threshold}) 미만입니다." }) return alerts