- Replace NodeID with RFID values in path display for better field mapping
- Add ComboBoxItem<T> class for {rfid} - [{node}] format in combo boxes
- Implement GetRfidByNodeId helper method for NodeID to RFID conversion
- Enhanced UpdatePathDebugInfo to show both RFID and NodeID information
- Improved path visualization with RFID-based route display
- Users can now easily match displayed paths with physical RFID tags
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
276 lines
8.8 KiB
C#
276 lines
8.8 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using AGVNavigationCore.Models;
|
||
|
||
namespace AGVNavigationCore.PathFinding
|
||
{
|
||
/// <summary>
|
||
/// AGV 경로 계산 결과 (방향성 및 명령어 포함)
|
||
/// </summary>
|
||
public class AGVPathResult
|
||
{
|
||
/// <summary>
|
||
/// 경로 찾기 성공 여부
|
||
/// </summary>
|
||
public bool Success { get; set; }
|
||
|
||
/// <summary>
|
||
/// 경로 노드 ID 목록 (시작 → 목적지 순서)
|
||
/// </summary>
|
||
public List<string> Path { get; set; }
|
||
|
||
/// <summary>
|
||
/// AGV 명령어 목록 (이동 방향 시퀀스)
|
||
/// </summary>
|
||
public List<AgvDirection> Commands { get; set; }
|
||
|
||
/// <summary>
|
||
/// 노드별 모터방향 정보 목록
|
||
/// </summary>
|
||
public List<NodeMotorInfo> NodeMotorInfos { get; set; }
|
||
|
||
/// <summary>
|
||
/// 총 거리
|
||
/// </summary>
|
||
public float TotalDistance { get; set; }
|
||
|
||
/// <summary>
|
||
/// 계산 소요 시간 (밀리초)
|
||
/// </summary>
|
||
public long CalculationTimeMs { get; set; }
|
||
|
||
/// <summary>
|
||
/// 예상 소요 시간 (초)
|
||
/// </summary>
|
||
public float EstimatedTimeSeconds { get; set; }
|
||
|
||
/// <summary>
|
||
/// 회전 횟수
|
||
/// </summary>
|
||
public int RotationCount { get; set; }
|
||
|
||
/// <summary>
|
||
/// 오류 메시지 (실패시)
|
||
/// </summary>
|
||
public string ErrorMessage { get; set; }
|
||
|
||
/// <summary>
|
||
/// 기본 생성자
|
||
/// </summary>
|
||
public AGVPathResult()
|
||
{
|
||
Success = false;
|
||
Path = new List<string>();
|
||
Commands = new List<AgvDirection>();
|
||
NodeMotorInfos = new List<NodeMotorInfo>();
|
||
TotalDistance = 0;
|
||
CalculationTimeMs = 0;
|
||
EstimatedTimeSeconds = 0;
|
||
RotationCount = 0;
|
||
ErrorMessage = string.Empty;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 성공 결과 생성
|
||
/// </summary>
|
||
/// <param name="path">경로</param>
|
||
/// <param name="commands">AGV 명령어 목록</param>
|
||
/// <param name="totalDistance">총 거리</param>
|
||
/// <param name="calculationTimeMs">계산 시간</param>
|
||
/// <returns>성공 결과</returns>
|
||
public static AGVPathResult CreateSuccess(List<string> path, List<AgvDirection> commands, float totalDistance, long calculationTimeMs)
|
||
{
|
||
var result = new AGVPathResult
|
||
{
|
||
Success = true,
|
||
Path = new List<string>(path),
|
||
Commands = new List<AgvDirection>(commands),
|
||
TotalDistance = totalDistance,
|
||
CalculationTimeMs = calculationTimeMs
|
||
};
|
||
|
||
result.CalculateMetrics();
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 성공 결과 생성 (노드별 모터방향 정보 포함)
|
||
/// </summary>
|
||
/// <param name="path">경로</param>
|
||
/// <param name="commands">AGV 명령어 목록</param>
|
||
/// <param name="nodeMotorInfos">노드별 모터방향 정보</param>
|
||
/// <param name="totalDistance">총 거리</param>
|
||
/// <param name="calculationTimeMs">계산 시간</param>
|
||
/// <returns>성공 결과</returns>
|
||
public static AGVPathResult CreateSuccess(List<string> path, List<AgvDirection> commands, List<NodeMotorInfo> nodeMotorInfos, float totalDistance, long calculationTimeMs)
|
||
{
|
||
var result = new AGVPathResult
|
||
{
|
||
Success = true,
|
||
Path = new List<string>(path),
|
||
Commands = new List<AgvDirection>(commands),
|
||
NodeMotorInfos = new List<NodeMotorInfo>(nodeMotorInfos),
|
||
TotalDistance = totalDistance,
|
||
CalculationTimeMs = calculationTimeMs
|
||
};
|
||
|
||
result.CalculateMetrics();
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 실패 결과 생성
|
||
/// </summary>
|
||
/// <param name="errorMessage">오류 메시지</param>
|
||
/// <param name="calculationTimeMs">계산 시간</param>
|
||
/// <returns>실패 결과</returns>
|
||
public static AGVPathResult CreateFailure(string errorMessage, long calculationTimeMs)
|
||
{
|
||
return new AGVPathResult
|
||
{
|
||
Success = false,
|
||
ErrorMessage = errorMessage,
|
||
CalculationTimeMs = calculationTimeMs
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 경로 메트릭 계산
|
||
/// </summary>
|
||
private void CalculateMetrics()
|
||
{
|
||
RotationCount = CountRotations();
|
||
EstimatedTimeSeconds = CalculateEstimatedTime();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 회전 횟수 계산
|
||
/// </summary>
|
||
private int CountRotations()
|
||
{
|
||
int count = 0;
|
||
foreach (var command in Commands)
|
||
{
|
||
if (command == AgvDirection.Left || command == AgvDirection.Right)
|
||
{
|
||
count++;
|
||
}
|
||
}
|
||
return count;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 예상 소요 시간 계산
|
||
/// </summary>
|
||
/// <param name="agvSpeed">AGV 속도 (픽셀/초, 기본값: 100)</param>
|
||
/// <param name="rotationTime">회전 시간 (초, 기본값: 3)</param>
|
||
/// <returns>예상 소요 시간 (초)</returns>
|
||
private float CalculateEstimatedTime(float agvSpeed = 100.0f, float rotationTime = 3.0f)
|
||
{
|
||
float moveTime = TotalDistance / agvSpeed;
|
||
float totalRotationTime = RotationCount * rotationTime;
|
||
return moveTime + totalRotationTime;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 명령어 요약 생성
|
||
/// </summary>
|
||
/// <returns>명령어 요약 문자열</returns>
|
||
public string GetCommandSummary()
|
||
{
|
||
if (!Success) return "실패";
|
||
|
||
var summary = new List<string>();
|
||
var currentCommand = AgvDirection.Stop;
|
||
var count = 0;
|
||
|
||
foreach (var command in Commands)
|
||
{
|
||
if (command == currentCommand)
|
||
{
|
||
count++;
|
||
}
|
||
else
|
||
{
|
||
if (count > 0)
|
||
{
|
||
summary.Add($"{GetCommandText(currentCommand)}×{count}");
|
||
}
|
||
currentCommand = command;
|
||
count = 1;
|
||
}
|
||
}
|
||
|
||
if (count > 0)
|
||
{
|
||
summary.Add($"{GetCommandText(currentCommand)}×{count}");
|
||
}
|
||
|
||
return string.Join(" → ", summary);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 명령어 텍스트 반환
|
||
/// </summary>
|
||
private string GetCommandText(AgvDirection command)
|
||
{
|
||
switch (command)
|
||
{
|
||
case AgvDirection.Forward: return "전진";
|
||
case AgvDirection.Backward: return "후진";
|
||
case AgvDirection.Left: return "좌회전";
|
||
case AgvDirection.Right: return "우회전";
|
||
case AgvDirection.Stop: return "정지";
|
||
default: return command.ToString();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 상세 경로 정보 반환
|
||
/// </summary>
|
||
/// <returns>상세 정보 문자열</returns>
|
||
public string GetDetailedInfo()
|
||
{
|
||
if (!Success)
|
||
{
|
||
return $"경로 계산 실패: {ErrorMessage} (계산시간: {CalculationTimeMs}ms)";
|
||
}
|
||
|
||
return $"경로: {Path.Count}개 노드, 거리: {TotalDistance:F1}px, " +
|
||
$"회전: {RotationCount}회, 예상시간: {EstimatedTimeSeconds:F1}초, " +
|
||
$"계산시간: {CalculationTimeMs}ms";
|
||
}
|
||
|
||
/// <summary>
|
||
/// PathResult로 변환 (호환성을 위해)
|
||
/// </summary>
|
||
/// <returns>PathResult 객체</returns>
|
||
public PathResult ToPathResult()
|
||
{
|
||
if (Success)
|
||
{
|
||
return PathResult.CreateSuccess(Path, TotalDistance, CalculationTimeMs, 0);
|
||
}
|
||
else
|
||
{
|
||
return PathResult.CreateFailure(ErrorMessage, CalculationTimeMs, 0);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 문자열 표현
|
||
/// </summary>
|
||
public override string ToString()
|
||
{
|
||
if (Success)
|
||
{
|
||
return $"Success: {Path.Count} nodes, {TotalDistance:F1}px, {RotationCount} rotations, {EstimatedTimeSeconds:F1}s";
|
||
}
|
||
else
|
||
{
|
||
return $"Failed: {ErrorMessage}";
|
||
}
|
||
}
|
||
}
|
||
} |