using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Linq; using AGVNavigationCore.Models; using AGVNavigationCore.PathFinding; namespace AGVMapEditor.Models { /// /// AGV 전용 경로 계산기 (AGVNavigationCore 래퍼) /// AGVMapEditor와 AGVNavigationCore 간의 호환성 제공 /// RFID 기반 경로 계산을 우선 사용 /// public class PathCalculator { private AGVPathfinder _agvPathfinder; private AStarPathfinder _astarPathfinder; private RfidBasedPathfinder _rfidPathfinder; /// /// 생성자 /// public PathCalculator() { _agvPathfinder = new AGVPathfinder(); _astarPathfinder = new AStarPathfinder(); _rfidPathfinder = new RfidBasedPathfinder(); } /// /// 맵 노드 설정 /// /// 맵 노드 목록 public void SetMapNodes(List mapNodes) { _agvPathfinder.SetMapNodes(mapNodes); _astarPathfinder.SetMapNodes(mapNodes); } /// /// 맵 데이터 설정 /// /// 맵 노드 목록 public void SetMapData(List mapNodes) { _agvPathfinder.SetMapNodes(mapNodes); _astarPathfinder.SetMapNodes(mapNodes); // RfidPathfinder는 MapNode의 RFID 정보를 직접 사용 _rfidPathfinder.SetMapNodes(mapNodes); // 도킹 조건 검색용 내부 노드 목록 업데이트 UpdateInternalMapNodes(mapNodes); } /// /// AGV 경로 계산 /// /// 시작 노드 ID /// 목적지 노드 ID /// 목적지 도착 방향 /// AGV 경로 계산 결과 public AGVPathResult FindAGVPath(string startNodeId, string endNodeId, AgvDirection? targetDirection = null) { return _agvPathfinder.FindAGVPath(startNodeId, endNodeId, targetDirection); } /// /// AGV 경로 계산 (옵션 지정 가능) /// /// 시작 노드 ID /// 목적지 노드 ID /// 목적지 도착 방향 /// 경로 탐색 옵션 /// AGV 경로 계산 결과 public AGVPathResult FindAGVPath(string startNodeId, string endNodeId, AgvDirection? targetDirection, PathfindingOptions options) { return _agvPathfinder.FindAGVPath(startNodeId, endNodeId, targetDirection, options); } /// /// AGV 경로 계산 (현재 방향 및 PathfindingOptions 지원) /// /// 시작 노드 ID /// 목적지 노드 ID /// 현재 AGV 방향 /// 목적지 도착 방향 /// 경로 탐색 옵션 /// AGV 경로 계산 결과 public AGVPathResult FindAGVPath(string startNodeId, string endNodeId, AgvDirection? currentDirection, AgvDirection? targetDirection, PathfindingOptions options) { return _agvPathfinder.FindAGVPath(startNodeId, endNodeId, currentDirection, targetDirection, options); } /// /// 충전 스테이션으로의 경로 찾기 /// /// 시작 노드 ID /// AGV 경로 계산 결과 public AGVPathResult FindPathToChargingStation(string startNodeId) { return _agvPathfinder.FindPathToChargingStation(startNodeId); } /// /// 도킹 스테이션으로의 경로 찾기 /// /// 시작 노드 ID /// 장비 타입 /// AGV 경로 계산 결과 public AGVPathResult FindPathToDockingStation(string startNodeId, StationType stationType) { return _agvPathfinder.FindPathToDockingStation(startNodeId, stationType); } /// /// 여러 목적지 중 가장 가까운 노드로의 경로 찾기 /// /// 시작 노드 ID /// 목적지 후보 노드 ID 목록 /// 경로 계산 결과 public PathResult FindNearestPath(string startNodeId, List targetNodeIds) { return _astarPathfinder.FindNearestPath(startNodeId, targetNodeIds); } /// /// 두 노드가 연결되어 있는지 확인 /// /// 노드 1 ID /// 노드 2 ID /// 연결 여부 public bool AreNodesConnected(string nodeId1, string nodeId2) { return _astarPathfinder.AreNodesConnected(nodeId1, nodeId2); } /// /// 경로 유효성 검증 /// /// 검증할 경로 /// 유효성 검증 결과 public bool ValidatePath(List path) { return _agvPathfinder.ValidatePath(path); } /// /// 네비게이션 가능한 노드 목록 반환 /// /// 노드 ID 목록 public List GetNavigationNodes() { return _astarPathfinder.GetNavigationNodes(); } /// /// AGV 현재 방향 설정 /// /// 현재 방향 public void SetCurrentDirection(AgvDirection direction) { _agvPathfinder.CurrentDirection = direction; } /// /// 회전 비용 가중치 설정 /// /// 회전 비용 가중치 public void SetRotationCostWeight(float weight) { _agvPathfinder.RotationCostWeight = weight; } /// /// 휴리스틱 가중치 설정 /// /// 휴리스틱 가중치 public void SetHeuristicWeight(float weight) { _astarPathfinder.HeuristicWeight = weight; } /// /// 최대 탐색 노드 수 설정 /// /// 최대 탐색 노드 수 public void SetMaxSearchNodes(int maxNodes) { _astarPathfinder.MaxSearchNodes = maxNodes; } // ==================== RFID 기반 경로 계산 메서드들 ==================== /// /// RFID 기반 AGV 경로 계산 /// /// 시작 RFID /// 목적지 RFID /// 목적지 도착 방향 /// RFID 기반 AGV 경로 계산 결과 public RfidPathResult FindAGVPathByRfid(string startRfidId, string endRfidId, AgvDirection? targetDirection = null) { return _rfidPathfinder.FindAGVPath(startRfidId, endRfidId, targetDirection); } /// /// RFID 기반 충전소 경로 찾기 /// /// 시작 RFID /// RFID 기반 경로 계산 결과 public RfidPathResult FindPathToChargingStationByRfid(string startRfidId) { return _rfidPathfinder.FindPathToChargingStation(startRfidId); } /// /// RFID 기반 도킹 스테이션 경로 찾기 /// /// 시작 RFID /// 장비 타입 /// RFID 기반 경로 계산 결과 public RfidPathResult FindPathToDockingStationByRfid(string startRfidId, StationType stationType) { return _rfidPathfinder.FindPathToDockingStation(startRfidId, stationType); } /// /// 여러 RFID 목적지 중 가장 가까운 곳으로의 경로 찾기 /// /// 시작 RFID /// 목적지 후보 RFID 목록 /// RFID 기반 경로 계산 결과 public RfidPathResult FindNearestPathByRfid(string startRfidId, List targetRfidIds) { return _rfidPathfinder.FindNearestPath(startRfidId, targetRfidIds); } /// /// RFID 매핑 정보 조회 (MapNode 반환) /// /// RFID /// MapNode 또는 null public MapNode GetRfidMapping(string rfidId) { return _rfidPathfinder.GetRfidMapping(rfidId); } /// /// RFID로 NodeId 조회 /// /// RFID /// NodeId 또는 null public string GetNodeIdByRfid(string rfidId) { return _rfidPathfinder.GetNodeIdByRfid(rfidId); } /// /// NodeId로 RFID 조회 /// /// NodeId /// RFID 또는 null public string GetRfidByNodeId(string nodeId) { return _rfidPathfinder.GetRfidByNodeId(nodeId); } /// /// 활성화된 RFID 목록 반환 /// /// 활성화된 RFID 목록 public List GetActiveRfidList() { return _rfidPathfinder.GetActiveRfidList(); } /// /// RFID pathfinder의 AGV 현재 방향 설정 /// /// 현재 방향 public void SetRfidPathfinderCurrentDirection(AgvDirection direction) { _rfidPathfinder.CurrentDirection = direction; } /// /// RFID pathfinder의 회전 비용 가중치 설정 /// /// 회전 비용 가중치 public void SetRfidPathfinderRotationCostWeight(float weight) { _rfidPathfinder.RotationCostWeight = weight; } #region 도킹 조건 검색 기능 // 내부 노드 목록 저장 private List _mapNodes; /// /// 맵 노드 설정 (도킹 조건 검색용) /// private void UpdateInternalMapNodes(List mapNodes) { _mapNodes = mapNodes; } /// /// 도킹 방향 기반 노드 검색 /// /// 도킹 방향 /// 해당 도킹 방향의 노드 목록 public List GetNodesByDockingDirection(DockingDirection dockingDirection) { if (_mapNodes == null) return new List(); var result = new List(); foreach (var node in _mapNodes) { if (!node.IsActive) continue; var nodeDockingDirection = GetNodeDockingDirection(node); if (nodeDockingDirection == dockingDirection) { result.Add(node); } } return result; } /// /// 노드의 도킹 방향 결정 /// /// 노드 /// 도킹 방향 public DockingDirection GetNodeDockingDirection(MapNode node) { switch (node.Type) { case NodeType.Charging: return DockingDirection.Forward; // 충전기: 전진 도킹 case NodeType.Docking: return DockingDirection.Backward; // 장비: 후진 도킹 default: return DockingDirection.Forward; // 기본값: 전진 } } /// /// 도킹 방향과 장비 타입에 맞는 노드들로의 경로 검색 /// /// 시작 RFID /// 필요한 도킹 방향 /// 장비 타입 (선택사항) /// 경로 계산 결과 목록 (거리 순 정렬) public List FindPathsByDockingCondition(string startRfidId, DockingDirection dockingDirection, StationType? stationType = null) { var targetNodes = GetNodesByDockingDirection(dockingDirection); var results = new List(); // 장비 타입 필터링 (필요시) if (stationType.HasValue && dockingDirection == DockingDirection.Backward) { // 후진 도킹이면서 특정 장비 타입이 지정된 경우 // 이 부분은 추후 StationMapping 정보가 있을 때 구현 // 현재는 모든 도킹 노드를 대상으로 함 } foreach (var targetNode in targetNodes) { if (!targetNode.HasRfid()) continue; try { var pathResult = _rfidPathfinder.FindAGVPath(startRfidId, targetNode.RfidId); if (pathResult.Success) { results.Add(pathResult); } } catch (Exception ex) { // 개별 경로 계산 실패는 무시하고 계속 진행 System.Diagnostics.Debug.WriteLine($"Path calculation failed from {startRfidId} to {targetNode.RfidId}: {ex.Message}"); } } // 거리 순으로 정렬 return results.OrderBy(r => r.TotalDistance).ToList(); } /// /// 가장 가까운 충전기 경로 찾기 (전진 도킹) /// /// 시작 RFID /// 가장 가까운 충전기로의 경로 public RfidPathResult FindNearestChargingStationPath(string startRfidId) { var chargingPaths = FindPathsByDockingCondition(startRfidId, DockingDirection.Forward); var chargingNodes = chargingPaths.Where(p => p.Success).ToList(); return chargingNodes.FirstOrDefault() ?? new RfidPathResult { Success = false, ErrorMessage = "충전 가능한 충전기를 찾을 수 없습니다." }; } /// /// 가장 가까운 장비 도킹 경로 찾기 (후진 도킹) /// /// 시작 RFID /// 장비 타입 (선택사항) /// 가장 가까운 장비로의 경로 public RfidPathResult FindNearestEquipmentPath(string startRfidId, StationType? stationType = null) { var equipmentPaths = FindPathsByDockingCondition(startRfidId, DockingDirection.Backward, stationType); var equipmentNodes = equipmentPaths.Where(p => p.Success).ToList(); return equipmentNodes.FirstOrDefault() ?? new RfidPathResult { Success = false, ErrorMessage = $"도킹 가능한 장비를 찾을 수 없습니다. ({stationType?.ToString() ?? "모든 타입"})" }; } #endregion } }