Files
ENIG/Cs_HMI/AGVMapEditor/Models/PathNode.cs
ChiKyun Kim 7567602479 feat: Add AGV Map Editor and Simulator tools
- 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>
2025-09-10 17:39:23 +09:00

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