using System; using System.Collections.Generic; using System.Drawing; using AGVNavigationCore.Models; using AGVNavigationCore.PathFinding.Planning; namespace AGVNavigationCore.Utils { /// /// AGV 방향 기반 다음 노드 계산기 /// VirtualAGV 또는 실제 AGV 시스템에서 현재 방향을 알 때, 다음 목적지 노드를 결정 /// public class AGVDirectionCalculator { private DirectionalPathfinder _pathfinder; public AGVDirectionCalculator(DirectionalPathfinder.DirectionWeights weights = null) { _pathfinder = new DirectionalPathfinder(weights); } /// /// 이전 RFID 위치 + 현재 위치 + 현재 방향을 기반으로 다음 노드 ID를 반환 /// /// 사용 예시: /// - 001에서 002로 이동 후 GetNextNodeId(001_pos, 002_node, 002_pos, Forward) → 003 /// - 003에서 004로 이동 후, Left 선택 → 030 /// - 004에서 003으로 이동(Backward) 후, GetNextNodeId(..., Backward) → 002 /// /// 이전 RFID 감지 위치 /// 현재 RFID 노드 /// 현재 RFID 감지 위치 /// 이동 방향 /// 맵의 모든 노드 /// 다음 노드 ID (실패 시 null) public string GetNextNodeId( Point previousRfidPos, MapNode currentNode, Point currentRfidPos, AgvDirection direction, List allNodes) { // 유효성 검사 if (previousRfidPos == Point.Empty) { throw new ArgumentException("previousRfidPos는 빈 값일 수 없습니다. 최소 2개의 위치 히스토리가 필요합니다."); } if (currentNode == null) { throw new ArgumentNullException(nameof(currentNode), "currentNode는 null일 수 없습니다."); } if (allNodes == null || allNodes.Count == 0) { throw new ArgumentException("allNodes는 비어있을 수 없습니다."); } return _pathfinder.GetNextNodeId( previousRfidPos, currentNode, currentRfidPos, direction, allNodes ); } /// /// 현재 모터 상태를 기반으로 실제 선택된 방향을 분석 /// VirtualAGV의 현재/이전 상태로부터 선택된 방향을 역추적 /// public AgvDirection AnalyzeSelectedDirection( Point previousPos, Point currentPos, MapNode selectedNextNode, List connectedNodes) { if (previousPos == Point.Empty || currentPos == Point.Empty || selectedNextNode == null) { return AgvDirection.Forward; } // 이동 벡터 var movementVector = new PointF( currentPos.X - previousPos.X, currentPos.Y - previousPos.Y ); // 다음 노드 벡터 var nextVector = new PointF( selectedNextNode.Position.X - currentPos.X, selectedNextNode.Position.Y - currentPos.Y ); // 내적 계산 (유사도) float dotProduct = (movementVector.X * nextVector.X) + (movementVector.Y * nextVector.Y); // 외적 계산 (좌우 판별) float crossProduct = (movementVector.X * nextVector.Y) - (movementVector.Y * nextVector.X); // 진행 방향 판별 if (dotProduct > 0) // 같은 방향으로 진행 { if (Math.Abs(crossProduct) < 0.1f) // 거의 직진 { return AgvDirection.Forward; } else if (crossProduct > 0) // 좌측으로 회전 { return AgvDirection.Left; } else // 우측으로 회전 { return AgvDirection.Right; } } else // 반대 방향으로 진행 (후진) { return AgvDirection.Backward; } } } }