- Add AGVMapEditor: Visual map editing with drag-and-drop node placement * RFID mapping separation (physical ID ↔ logical node mapping) * A* pathfinding algorithm with AGV directional constraints * JSON map data persistence with structured format * Interactive map canvas with zoom/pan functionality - Add AGVSimulator: Real-time AGV movement simulation * Virtual AGV with state machine (Idle, Moving, Rotating, Docking, Charging, Error) * Path execution and visualization from calculated routes * Real-time position tracking and battery simulation * Integration with map editor data format - Update solution structure and build configuration - Add comprehensive documentation in CLAUDE.md - Implement AGV-specific constraints (forward/backward docking, rotation limits) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
144 lines
4.0 KiB
C#
144 lines
4.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace AGVMapEditor.Models
|
|
{
|
|
/// <summary>
|
|
/// A* 알고리즘에서 사용되는 경로 노드
|
|
/// </summary>
|
|
public class PathNode : IComparable<PathNode>
|
|
{
|
|
/// <summary>
|
|
/// 맵 노드 ID
|
|
/// </summary>
|
|
public string NodeId { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// AGV의 현재 방향 (이 노드에 도달했을 때의 방향)
|
|
/// </summary>
|
|
public AgvDirection Direction { get; set; } = AgvDirection.Forward;
|
|
|
|
/// <summary>
|
|
/// 시작점에서 이 노드까지의 실제 비용 (G)
|
|
/// </summary>
|
|
public float GCost { get; set; } = float.MaxValue;
|
|
|
|
/// <summary>
|
|
/// 이 노드에서 목표까지의 추정 비용 (H)
|
|
/// </summary>
|
|
public float HCost { get; set; } = 0;
|
|
|
|
/// <summary>
|
|
/// 총 비용 (F = G + H)
|
|
/// </summary>
|
|
public float FCost => GCost + HCost;
|
|
|
|
/// <summary>
|
|
/// 이전 노드 (경로 추적용)
|
|
/// </summary>
|
|
public PathNode Parent { get; set; } = null;
|
|
|
|
/// <summary>
|
|
/// 회전 횟수 (방향 전환 비용 계산용)
|
|
/// </summary>
|
|
public int RotationCount { get; set; } = 0;
|
|
|
|
/// <summary>
|
|
/// 이 노드에 도달하기 위한 이동 명령 시퀀스
|
|
/// </summary>
|
|
public List<AgvDirection> MovementSequence { get; set; } = new List<AgvDirection>();
|
|
|
|
/// <summary>
|
|
/// 기본 생성자
|
|
/// </summary>
|
|
public PathNode()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// 매개변수 생성자
|
|
/// </summary>
|
|
/// <param name="nodeId">노드 ID</param>
|
|
/// <param name="direction">AGV 방향</param>
|
|
public PathNode(string nodeId, AgvDirection direction)
|
|
{
|
|
NodeId = nodeId;
|
|
Direction = direction;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 우선순위 큐를 위한 비교 (FCost 기준)
|
|
/// </summary>
|
|
public int CompareTo(PathNode other)
|
|
{
|
|
if (other == null) return 1;
|
|
|
|
int compare = FCost.CompareTo(other.FCost);
|
|
if (compare == 0)
|
|
{
|
|
// FCost가 같으면 HCost가 낮은 것을 우선
|
|
compare = HCost.CompareTo(other.HCost);
|
|
}
|
|
if (compare == 0)
|
|
{
|
|
// 그것도 같으면 회전 횟수가 적은 것을 우선
|
|
compare = RotationCount.CompareTo(other.RotationCount);
|
|
}
|
|
|
|
return compare;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 노드 상태 복사
|
|
/// </summary>
|
|
public PathNode Clone()
|
|
{
|
|
return new PathNode
|
|
{
|
|
NodeId = NodeId,
|
|
Direction = Direction,
|
|
GCost = GCost,
|
|
HCost = HCost,
|
|
Parent = Parent,
|
|
RotationCount = RotationCount,
|
|
MovementSequence = new List<AgvDirection>(MovementSequence)
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 고유 키 생성 (노드ID + 방향)
|
|
/// </summary>
|
|
public string GetKey()
|
|
{
|
|
return $"{NodeId}_{Direction}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// 문자열 표현
|
|
/// </summary>
|
|
public override string ToString()
|
|
{
|
|
return $"{NodeId}({Direction}) F:{FCost:F1} G:{GCost:F1} H:{HCost:F1} R:{RotationCount}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// 해시코드 (딕셔너리 키용)
|
|
/// </summary>
|
|
public override int GetHashCode()
|
|
{
|
|
return GetKey().GetHashCode();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 동등성 비교
|
|
/// </summary>
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (obj is PathNode other)
|
|
{
|
|
return NodeId == other.NodeId && Direction == other.Direction;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
} |