using System; using System.Collections.Generic; using System.Linq; using AGVNavigationCore.Models; namespace AGVMapEditor.Models { /// /// RFID 값을 논리적 노드로 변환하는 클래스 /// 실제 AGV 시스템에서 RFID 리더가 읽은 값을 맵 노드 정보로 변환 /// public class NodeResolver { private List _rfidMappings; private List _mapNodes; /// /// 기본 생성자 /// public NodeResolver() { _rfidMappings = new List(); _mapNodes = new List(); } /// /// 매개변수 생성자 /// /// RFID 매핑 목록 /// 맵 노드 목록 public NodeResolver(List rfidMappings, List mapNodes) { _rfidMappings = rfidMappings ?? new List(); _mapNodes = mapNodes ?? new List(); } /// /// RFID 값으로 맵 노드 검색 /// /// RFID 리더에서 읽은 값 /// 해당하는 맵 노드, 없으면 null public MapNode GetNodeByRfid(string rfidValue) { if (string.IsNullOrEmpty(rfidValue)) return null; // 1. RFID 매핑에서 논리적 노드 ID 찾기 var mapping = _rfidMappings.FirstOrDefault(m => m.RfidId.Equals(rfidValue, StringComparison.OrdinalIgnoreCase) && m.IsActive); if (mapping == null) return null; // 2. 논리적 노드 ID로 실제 맵 노드 찾기 var mapNode = _mapNodes.FirstOrDefault(n => n.NodeId.Equals(mapping.LogicalNodeId, StringComparison.OrdinalIgnoreCase) && n.IsActive); return mapNode; } /// /// 논리적 노드 ID로 맵 노드 검색 /// /// 논리적 노드 ID /// 해당하는 맵 노드, 없으면 null public MapNode GetNodeById(string nodeId) { if (string.IsNullOrEmpty(nodeId)) return null; return _mapNodes.FirstOrDefault(n => n.NodeId.Equals(nodeId, StringComparison.OrdinalIgnoreCase) && n.IsActive); } /// /// 맵 노드로 연결된 RFID 값 검색 /// /// 논리적 노드 ID /// 연결된 RFID 값, 없으면 null public string GetRfidByNodeId(string nodeId) { if (string.IsNullOrEmpty(nodeId)) return null; var mapping = _rfidMappings.FirstOrDefault(m => m.LogicalNodeId.Equals(nodeId, StringComparison.OrdinalIgnoreCase) && m.IsActive); return mapping?.RfidId; } /// /// RFID 매핑 정보 검색 /// /// RFID 값 /// 매핑 정보, 없으면 null public RfidMapping GetRfidMapping(string rfidValue) { if (string.IsNullOrEmpty(rfidValue)) return null; return _rfidMappings.FirstOrDefault(m => m.RfidId.Equals(rfidValue, StringComparison.OrdinalIgnoreCase) && m.IsActive); } /// /// 새로운 RFID 매핑 추가 /// /// RFID 값 /// 논리적 노드 ID /// 설명 /// 추가 성공 여부 public bool AddRfidMapping(string rfidId, string nodeId, string description = "") { if (string.IsNullOrEmpty(rfidId) || string.IsNullOrEmpty(nodeId)) return false; // 중복 RFID 체크 if (_rfidMappings.Any(m => m.RfidId.Equals(rfidId, StringComparison.OrdinalIgnoreCase))) return false; // 해당 노드 존재 체크 if (!_mapNodes.Any(n => n.NodeId.Equals(nodeId, StringComparison.OrdinalIgnoreCase))) return false; var mapping = new RfidMapping(rfidId, nodeId, description); _rfidMappings.Add(mapping); return true; } /// /// RFID 매핑 제거 /// /// 제거할 RFID 값 /// 제거 성공 여부 public bool RemoveRfidMapping(string rfidId) { if (string.IsNullOrEmpty(rfidId)) return false; var mapping = _rfidMappings.FirstOrDefault(m => m.RfidId.Equals(rfidId, StringComparison.OrdinalIgnoreCase)); if (mapping != null) { _rfidMappings.Remove(mapping); return true; } return false; } /// /// 맵 노드 추가 /// /// 추가할 맵 노드 /// 추가 성공 여부 public bool AddMapNode(MapNode node) { if (node == null || string.IsNullOrEmpty(node.NodeId)) return false; // 중복 노드 ID 체크 if (_mapNodes.Any(n => n.NodeId.Equals(node.NodeId, StringComparison.OrdinalIgnoreCase))) return false; _mapNodes.Add(node); return true; } /// /// 맵 노드 제거 /// /// 제거할 노드 ID /// 제거 성공 여부 public bool RemoveMapNode(string nodeId) { if (string.IsNullOrEmpty(nodeId)) return false; var node = _mapNodes.FirstOrDefault(n => n.NodeId.Equals(nodeId, StringComparison.OrdinalIgnoreCase)); if (node != null) { // 연관된 RFID 매핑도 함께 제거 var associatedMappings = _rfidMappings.Where(m => m.LogicalNodeId.Equals(nodeId, StringComparison.OrdinalIgnoreCase)).ToList(); foreach (var mapping in associatedMappings) { _rfidMappings.Remove(mapping); } // 다른 노드의 연결 정보에서도 제거 foreach (var otherNode in _mapNodes.Where(n => n.ConnectedNodes.Contains(nodeId))) { otherNode.RemoveConnection(nodeId); } _mapNodes.Remove(node); return true; } return false; } /// /// 특정 타입의 노드들 검색 /// /// 노드 타입 /// 해당 타입의 노드 목록 public List GetNodesByType(NodeType nodeType) { return _mapNodes.Where(n => n.Type == nodeType && n.IsActive).ToList(); } /// /// 장비 ID로 노드 검색 /// /// 장비 ID /// 해당 장비의 노드, 없으면 null public MapNode GetNodeByStationId(string stationId) { if (string.IsNullOrEmpty(stationId)) return null; return _mapNodes.FirstOrDefault(n => n.StationId.Equals(stationId, StringComparison.OrdinalIgnoreCase) && n.IsActive); } /// /// 매핑되지 않은 노드들 검색 (RFID가 연결되지 않은 노드) /// /// 매핑되지 않은 노드 목록 public List GetUnmappedNodes() { var mappedNodeIds = _rfidMappings.Where(m => m.IsActive) .Select(m => m.LogicalNodeId) .ToHashSet(StringComparer.OrdinalIgnoreCase); return _mapNodes.Where(n => n.IsActive && !mappedNodeIds.Contains(n.NodeId)).ToList(); } /// /// 사용되지 않는 RFID 매핑들 검색 (노드가 삭제된 매핑) /// /// 사용되지 않는 매핑 목록 public List GetOrphanedMappings() { var activeNodeIds = _mapNodes.Where(n => n.IsActive) .Select(n => n.NodeId) .ToHashSet(StringComparer.OrdinalIgnoreCase); return _rfidMappings.Where(m => m.IsActive && !activeNodeIds.Contains(m.LogicalNodeId)).ToList(); } /// /// 데이터 초기화 /// public void Clear() { _rfidMappings.Clear(); _mapNodes.Clear(); } /// /// 데이터 유효성 검증 /// /// 검증 결과 메시지 목록 public List ValidateData() { var errors = new List(); // 중복 RFID 체크 var duplicateRfids = _rfidMappings.GroupBy(m => m.RfidId.ToLower()) .Where(g => g.Count() > 1) .Select(g => g.Key); foreach (var rfid in duplicateRfids) { errors.Add($"중복된 RFID: {rfid}"); } // 중복 노드 ID 체크 var duplicateNodeIds = _mapNodes.GroupBy(n => n.NodeId.ToLower()) .Where(g => g.Count() > 1) .Select(g => g.Key); foreach (var nodeId in duplicateNodeIds) { errors.Add($"중복된 노드 ID: {nodeId}"); } // 고아 매핑 체크 var orphanedMappings = GetOrphanedMappings(); foreach (var mapping in orphanedMappings) { errors.Add($"존재하지 않는 노드를 참조하는 RFID 매핑: {mapping.RfidId} → {mapping.LogicalNodeId}"); } // 매핑되지 않은 노드 경고 (에러는 아님) var unmappedNodes = GetUnmappedNodes(); foreach (var node in unmappedNodes) { errors.Add($"RFID가 매핑되지 않은 노드: {node.NodeId} ({node.Name})"); } return errors; } } }