using System; using System.Collections.Generic; using System.Linq; namespace PathLogic.Models { /// /// 맵 데이터를 관리하는 클래스 /// 기존 AGV 맵 파일 형식과 호환 /// public class MapData { /// /// 맵의 모든 노드 목록 /// public List Nodes { get; set; } = new List(); /// /// 맵 생성 일자 /// public DateTime CreatedDate { get; set; } = DateTime.Now; /// /// 맵 버전 /// public string Version { get; set; } = "1.0"; /// /// 기본 생성자 /// public MapData() { } /// /// 노드 ID로 노드 찾기 /// /// 노드 ID /// 해당 노드, 없으면 null public MapNode GetNodeById(string nodeId) { return Nodes.FirstOrDefault(n => n.NodeId == nodeId); } /// /// RFID ID로 노드 찾기 /// /// RFID ID /// 해당 노드, 없으면 null public MapNode GetNodeByRfidId(string rfidId) { return Nodes.FirstOrDefault(n => n.RfidId == rfidId); } /// /// 네비게이션 가능한 노드만 반환 /// /// 네비게이션 가능한 노드 목록 public List GetNavigationNodes() { return Nodes.Where(n => n.IsNavigationNode()).ToList(); } /// /// 특정 타입의 노드들 반환 /// /// 노드 타입 /// 해당 타입의 노드 목록 public List GetNodesByType(NodeType nodeType) { return Nodes.Where(n => n.Type == nodeType).ToList(); } /// /// 도킹 스테이션 노드들 반환 /// /// 도킹 스테이션 노드 목록 public List GetDockingStations() { return Nodes.Where(n => n.Type == NodeType.Docking || n.Type == NodeType.Charging).ToList(); } /// /// 충전 스테이션 노드들 반환 /// /// 충전 스테이션 노드 목록 public List GetChargingStations() { return Nodes.Where(n => n.Type == NodeType.Charging).ToList(); } /// /// 회전 가능한 노드들 반환 /// /// 회전 가능한 노드 목록 public List GetRotationNodes() { return Nodes.Where(n => n.CanPerformRotation()).ToList(); } /// /// 노드 추가 /// /// 추가할 노드 /// 추가 성공 여부 public bool AddNode(MapNode node) { if (node == null || GetNodeById(node.NodeId) != null) return false; Nodes.Add(node); return true; } /// /// 노드 제거 /// /// 제거할 노드 ID /// 제거 성공 여부 public bool RemoveNode(string nodeId) { var node = GetNodeById(nodeId); if (node == null) return false; // 다른 노드들의 연결에서도 제거 foreach (var otherNode in Nodes) { otherNode.ConnectedNodes.Remove(nodeId); } return Nodes.Remove(node); } /// /// 두 노드 간 연결 추가 /// /// 시작 노드 ID /// 도착 노드 ID /// 양방향 연결 여부 /// 연결 성공 여부 public bool AddConnection(string fromNodeId, string toNodeId, bool bidirectional = true) { var fromNode = GetNodeById(fromNodeId); var toNode = GetNodeById(toNodeId); if (fromNode == null || toNode == null) return false; // 단방향 연결 if (!fromNode.ConnectedNodes.Contains(toNodeId)) { fromNode.ConnectedNodes.Add(toNodeId); fromNode.ModifiedDate = DateTime.Now; } // 양방향 연결 if (bidirectional && !toNode.ConnectedNodes.Contains(fromNodeId)) { toNode.ConnectedNodes.Add(fromNodeId); toNode.ModifiedDate = DateTime.Now; } return true; } /// /// 두 노드 간 연결 제거 /// /// 시작 노드 ID /// 도착 노드 ID /// 양방향 제거 여부 /// 제거 성공 여부 public bool RemoveConnection(string fromNodeId, string toNodeId, bool bidirectional = true) { var fromNode = GetNodeById(fromNodeId); var toNode = GetNodeById(toNodeId); if (fromNode == null || toNode == null) return false; bool removed = false; // 단방향 제거 if (fromNode.ConnectedNodes.Remove(toNodeId)) { fromNode.ModifiedDate = DateTime.Now; removed = true; } // 양방향 제거 if (bidirectional && toNode.ConnectedNodes.Remove(fromNodeId)) { toNode.ModifiedDate = DateTime.Now; removed = true; } return removed; } /// /// 두 노드가 연결되어 있는지 확인 /// /// 시작 노드 ID /// 도착 노드 ID /// 연결 여부 public bool AreConnected(string fromNodeId, string toNodeId) { var fromNode = GetNodeById(fromNodeId); return fromNode?.IsConnectedTo(toNodeId) ?? false; } /// /// 특정 노드의 이웃 노드들 반환 /// /// 노드 ID /// 이웃 노드 목록 public List GetNeighbors(string nodeId) { var node = GetNodeById(nodeId); if (node == null) return new List(); var neighbors = new List(); foreach (var connectedId in node.ConnectedNodes) { var neighbor = GetNodeById(connectedId); if (neighbor != null && neighbor.IsNavigationNode()) { neighbors.Add(neighbor); } } return neighbors; } /// /// 맵 데이터 유효성 검증 /// /// 검증 결과 메시지 public List ValidateMap() { var issues = new List(); // 노드 ID 중복 검사 var nodeIds = Nodes.Select(n => n.NodeId).ToList(); var duplicateIds = nodeIds.GroupBy(id => id) .Where(g => g.Count() > 1) .Select(g => g.Key); foreach (var duplicateId in duplicateIds) { issues.Add($"중복된 노드 ID: {duplicateId}"); } // RFID ID 중복 검사 var rfidIds = Nodes.Where(n => n.HasRfid()) .Select(n => n.RfidId) .ToList(); var duplicateRfids = rfidIds.GroupBy(id => id) .Where(g => g.Count() > 1) .Select(g => g.Key); foreach (var duplicateRfid in duplicateRfids) { issues.Add($"중복된 RFID ID: {duplicateRfid}"); } // 잘못된 연결 검사 foreach (var node in Nodes) { foreach (var connectedId in node.ConnectedNodes) { if (GetNodeById(connectedId) == null) { issues.Add($"노드 {node.NodeId}가 존재하지 않는 노드 {connectedId}에 연결됨"); } } } // 고립된 네비게이션 노드 검사 var navigationNodes = GetNavigationNodes(); foreach (var node in navigationNodes) { if (node.ConnectedNodes.Count == 0) { issues.Add($"고립된 노드: {node.NodeId}"); } } return issues; } /// /// 맵 통계 정보 반환 /// /// 맵 통계 public MapStatistics GetStatistics() { var stats = new MapStatistics(); var navigationNodes = GetNavigationNodes(); stats.TotalNodes = Nodes.Count; stats.NavigationNodes = navigationNodes.Count; stats.DockingStations = GetNodesByType(NodeType.Docking).Count; stats.ChargingStations = GetNodesByType(NodeType.Charging).Count; stats.RotationNodes = GetRotationNodes().Count; stats.LabelNodes = GetNodesByType(NodeType.Label).Count; stats.ImageNodes = GetNodesByType(NodeType.Image).Count; // 연결 수 계산 stats.TotalConnections = navigationNodes.Sum(n => n.ConnectedNodes.Count) / 2; // 양방향이므로 2로 나눔 // RFID 할당된 노드 수 stats.NodesWithRfid = Nodes.Count(n => n.HasRfid()); return stats; } /// /// 맵 데이터 복사 /// /// 복사된 맵 데이터 public MapData Clone() { var clonedMap = new MapData { CreatedDate = CreatedDate, Version = Version }; foreach (var node in Nodes) { clonedMap.Nodes.Add(node.Clone()); } return clonedMap; } } /// /// 맵 통계 정보 클래스 /// public class MapStatistics { public int TotalNodes { get; set; } public int NavigationNodes { get; set; } public int DockingStations { get; set; } public int ChargingStations { get; set; } public int RotationNodes { get; set; } public int LabelNodes { get; set; } public int ImageNodes { get; set; } public int TotalConnections { get; set; } public int NodesWithRfid { get; set; } public override string ToString() { return $"총 노드: {TotalNodes}, 네비게이션: {NavigationNodes}, " + $"도킹: {DockingStations}, 충전: {ChargingStations}, " + $"회전: {RotationNodes}, 연결: {TotalConnections}, " + $"RFID 할당: {NodesWithRfid}"; } } }