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;
}
}
}
}