using System; using System.Collections.Generic; using System.Linq; namespace AGVMapEditor.Models { /// /// 경로 계산 결과 /// public class PathResult { /// /// 경로 계산 성공 여부 /// public bool Success { get; set; } = false; /// /// 경로상의 노드 ID 시퀀스 /// public List NodeSequence { get; set; } = new List(); /// /// AGV 이동 명령 시퀀스 /// public List MovementSequence { get; set; } = new List(); /// /// 총 이동 거리 (비용) /// public float TotalDistance { get; set; } = 0; /// /// 총 회전 횟수 /// public int TotalRotations { get; set; } = 0; /// /// 예상 소요 시간 (초) /// public float EstimatedTime { get; set; } = 0; /// /// 시작 노드 ID /// public string StartNodeId { get; set; } = string.Empty; /// /// 목표 노드 ID /// public string TargetNodeId { get; set; } = string.Empty; /// /// 시작시 AGV 방향 /// public AgvDirection StartDirection { get; set; } = AgvDirection.Forward; /// /// 도착시 AGV 방향 /// public AgvDirection EndDirection { get; set; } = AgvDirection.Forward; /// /// 경로 계산에 걸린 시간 (밀리초) /// public long CalculationTime { get; set; } = 0; /// /// 오류 메시지 (실패시) /// public string ErrorMessage { get; set; } = string.Empty; /// /// 경로상의 상세 정보 (디버깅용) /// public List DetailedPath { get; set; } = new List(); /// /// 회전이 발생하는 노드들 /// public List RotationNodes { get; set; } = new List(); /// /// 기본 생성자 /// public PathResult() { } /// /// 성공 결과 생성자 /// public PathResult(List path, string startNodeId, string targetNodeId, AgvDirection startDirection) { if (path == null || path.Count == 0) { Success = false; ErrorMessage = "빈 경로입니다."; return; } Success = true; StartNodeId = startNodeId; TargetNodeId = targetNodeId; StartDirection = startDirection; DetailedPath = new List(path); // 노드 시퀀스 구성 NodeSequence = path.Select(p => p.NodeId).ToList(); // 이동 명령 시퀀스 구성 MovementSequence = new List(); for (int i = 0; i < path.Count; i++) { MovementSequence.AddRange(path[i].MovementSequence); } // 통계 계산 if (path.Count > 0) { TotalDistance = path[path.Count - 1].GCost; EndDirection = path[path.Count - 1].Direction; } TotalRotations = MovementSequence.Count(cmd => cmd == AgvDirection.Left || cmd == AgvDirection.Right); // 회전 노드 추출 var previousDirection = startDirection; for (int i = 0; i < path.Count; i++) { if (path[i].Direction != previousDirection) { RotationNodes.Add(path[i].NodeId); } previousDirection = path[i].Direction; } // 예상 소요 시간 계산 (단순 추정) EstimatedTime = CalculateEstimatedTime(); } /// /// 실패 결과 생성자 /// public PathResult(string errorMessage) { Success = false; ErrorMessage = errorMessage; } /// /// 예상 소요 시간 계산 /// private float CalculateEstimatedTime() { // 기본 이동 속도 및 회전 시간 가정 const float MOVE_SPEED = 1.0f; // 단위/초 const float ROTATION_TIME = 2.0f; // 초/회전 float moveTime = TotalDistance / MOVE_SPEED; float rotationTime = TotalRotations * ROTATION_TIME; return moveTime + rotationTime; } /// /// 경로 요약 정보 /// public string GetSummary() { if (!Success) { return $"경로 계산 실패: {ErrorMessage}"; } return $"경로: {NodeSequence.Count}개 노드, " + $"거리: {TotalDistance:F1}, " + $"회전: {TotalRotations}회, " + $"예상시간: {EstimatedTime:F1}초"; } /// /// 상세 경로 정보 /// public List GetDetailedSteps() { var steps = new List(); if (!Success) { steps.Add($"경로 계산 실패: {ErrorMessage}"); return steps; } steps.Add($"시작: {StartNodeId} (방향: {StartDirection})"); for (int i = 0; i < DetailedPath.Count; i++) { var node = DetailedPath[i]; var step = $"{i + 1}. {node.NodeId}"; if (node.MovementSequence.Count > 0) { step += $" [명령: {string.Join(",", node.MovementSequence)}]"; } step += $" (F:{node.FCost:F1}, 방향:{node.Direction})"; steps.Add(step); } steps.Add($"도착: {TargetNodeId} (최종 방향: {EndDirection})"); return steps; } /// /// RFID 시퀀스 추출 (실제 AGV 제어용) /// public List GetRfidSequence(NodeResolver nodeResolver) { var rfidSequence = new List(); foreach (var nodeId in NodeSequence) { var rfidId = nodeResolver.GetRfidByNodeId(nodeId); if (!string.IsNullOrEmpty(rfidId)) { rfidSequence.Add(rfidId); } } return rfidSequence; } /// /// 경로 유효성 검증 /// public bool ValidatePath(List mapNodes) { if (!Success || NodeSequence.Count == 0) return false; // 모든 노드가 존재하는지 확인 foreach (var nodeId in NodeSequence) { if (!mapNodes.Any(n => n.NodeId == nodeId)) { ErrorMessage = $"존재하지 않는 노드: {nodeId}"; return false; } } // 연결성 확인 for (int i = 0; i < NodeSequence.Count - 1; i++) { var currentNode = mapNodes.FirstOrDefault(n => n.NodeId == NodeSequence[i]); var nextNodeId = NodeSequence[i + 1]; if (currentNode != null && !currentNode.ConnectedNodes.Contains(nextNodeId)) { ErrorMessage = $"연결되지 않은 노드: {currentNode.NodeId} → {nextNodeId}"; return false; } } return true; } /// /// JSON 직렬화를 위한 문자열 변환 /// public override string ToString() { return GetSummary(); } } }