feat: 방향전환 경로 검증 시스템 구현

- PathValidationResult 클래스를 Validation 폴더에 적절히 배치
- BacktrackingPattern 클래스로 A→B→A 패턴 상세 검출
- DirectionChangePlanner에서 되돌아가기 패턴 자동 검증
- CLAUDE.md에 AGVNavigationCore 프로젝트 구조 가이드 추가
- 빌드 시스템 오류 모두 해결

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ChiKyun Kim
2025-09-17 09:24:45 +09:00
parent 8d5ddbe008
commit cacd7fab1b
15 changed files with 789 additions and 237 deletions

View File

@@ -11,16 +11,17 @@ namespace AGVNavigationCore.Controls
public interface IAGV
{
string AgvId { get; }
Point CurrentPosition { get; }
AgvDirection CurrentDirection { get; }
AGVState CurrentState { get; }
Point CurrentPosition { get; set; }
AgvDirection CurrentDirection { get; set; }
AGVState CurrentState { get; set; }
float BatteryLevel { get; }
// 이동 경로 정보 추가
Point? TargetPosition { get; }
string CurrentNodeId { get; }
string TargetNodeId { get; }
DockingDirection DockingDirection { get; }
}

View File

@@ -35,7 +35,6 @@ namespace AGVNavigationCore.Controls
/// </summary>
public enum CanvasMode
{
ViewOnly, // 읽기 전용 (시뮬레이터, 모니터링)
Edit // 편집 가능 (맵 에디터)
}
@@ -52,7 +51,6 @@ namespace AGVNavigationCore.Controls
DeleteConnection, // 연결 삭제 모드
AddLabel, // 라벨 추가 모드
AddImage, // 이미지 추가 모드
SelectTarget // 목적지 선택 모드 (시뮬레이터 전용)
}
#endregion
@@ -60,7 +58,7 @@ namespace AGVNavigationCore.Controls
#region Fields
// 캔버스 모드
private CanvasMode _canvasMode = CanvasMode.ViewOnly;
private CanvasMode _canvasMode = CanvasMode.Edit;
private EditMode _editMode = EditMode.Select;
// 맵 데이터
@@ -121,7 +119,7 @@ namespace AGVNavigationCore.Controls
private Brush _gridBrush;
private Brush _agvBrush;
private Brush _pathBrush;
private Pen _connectionPen;
private Pen _gridPen;
private Pen _tempConnectionPen;
@@ -150,9 +148,6 @@ namespace AGVNavigationCore.Controls
public event EventHandler<IAGV> AGVSelected;
public event EventHandler<IAGV> AGVStateChanged;
// 시뮬레이터 이벤트
public event EventHandler<MapNode> TargetNodeSelected;
#endregion
#region Properties
@@ -180,7 +175,7 @@ namespace AGVNavigationCore.Controls
set
{
if (_canvasMode != CanvasMode.Edit) return;
_editMode = value;
if (_editMode != EditMode.Connect)
{
@@ -365,8 +360,8 @@ namespace AGVNavigationCore.Controls
private void InitializeCanvas()
{
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.DoubleBuffer |
ControlStyles.ResizeRedraw, true);
@@ -392,7 +387,7 @@ namespace AGVNavigationCore.Controls
_selectedNodeBrush = new SolidBrush(Color.Red);
_hoveredNodeBrush = new SolidBrush(Color.LightCyan);
_destinationNodeBrush = new SolidBrush(Color.Gold);
// AGV 및 경로 브러쉬
_agvBrush = new SolidBrush(Color.Red);
_pathBrush = new SolidBrush(Color.Purple);
@@ -403,7 +398,7 @@ namespace AGVNavigationCore.Controls
// 펜
_connectionPen = new Pen(Color.DarkBlue, CONNECTION_WIDTH);
_connectionPen.EndCap = LineCap.ArrowAnchor;
_gridPen = new Pen(Color.LightGray, 1);
_tempConnectionPen = new Pen(Color.Orange, 2) { DashStyle = DashStyle.Dash };
_selectedNodePen = new Pen(Color.Red, 3);
@@ -422,16 +417,8 @@ namespace AGVNavigationCore.Controls
private void UpdateModeUI()
{
// 모드에 따른 UI 업데이트
if (_canvasMode == CanvasMode.ViewOnly)
{
Cursor = Cursors.Default;
_contextMenu.Enabled = false;
}
else
{
_contextMenu.Enabled = true;
Cursor = GetCursorForMode(_editMode);
}
_contextMenu.Enabled = true;
Cursor = GetCursorForMode(_editMode);
}
#endregion
@@ -447,7 +434,7 @@ namespace AGVNavigationCore.Controls
_agvPositions[agvId] = position;
else
_agvPositions.Add(agvId, position);
Invalidate();
}
@@ -460,7 +447,7 @@ namespace AGVNavigationCore.Controls
_agvDirections[agvId] = direction;
else
_agvDirections.Add(agvId, direction);
Invalidate();
}
@@ -473,7 +460,7 @@ namespace AGVNavigationCore.Controls
_agvStates[agvId] = state;
else
_agvStates.Add(agvId, state);
Invalidate();
}
@@ -537,12 +524,12 @@ namespace AGVNavigationCore.Controls
private void UpdateDestinationNode()
{
_destinationNode = null;
if (_currentPath != null && _currentPath.Success && _currentPath.Path != null && _currentPath.Path.Count > 0)
{
// 경로의 마지막 노드가 목적지
string destinationNodeId = _currentPath.Path[_currentPath.Path.Count - 1];
// 노드 목록에서 해당 노드 찾기
_destinationNode = _nodes?.FirstOrDefault(n => n.NodeId == destinationNodeId);
}