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

@@ -95,7 +95,7 @@ namespace AGVSimulator.Forms
_simulatorCanvas.Dock = DockStyle.Fill;
// 목적지 선택 이벤트 구독
_simulatorCanvas.TargetNodeSelected += OnTargetNodeSelected;
_simulatorCanvas.NodeSelected += OnTargetNodeSelected;
_canvasPanel.Controls.Add(_simulatorCanvas);
}
@@ -302,20 +302,23 @@ namespace AGVSimulator.Forms
var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
var currentDirection = selectedAGV?.CurrentDirection ?? AgvDirection.Forward;
// 고급 경로 계획 사용 (단일 경로 계산 방식)
var advancedResult = _advancedPathfinder.FindPath(startNode.NodeId, targetNode.NodeId, currentDirection);
// 고급 경로 계획 사용 (노드 객체 직접 전달)
var advancedResult = _advancedPathfinder.FindPath(startNode, targetNode, currentDirection);
if (advancedResult.Success)
{
// 고급 경로 결과를 AGVPathResult 형태로 변환 (도킹 검증 포함)
var agvResult = ConvertToAGVPathResult(advancedResult, currentDirection);
// 도킹 검증이 없는 경우 추가 검증 수행
if (advancedResult.DockingValidation == null || !advancedResult.DockingValidation.IsValidationRequired)
{
advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _mapNodes, currentDirection);
}
_simulatorCanvas.CurrentPath = agvResult;
_simulatorCanvas.CurrentPath = advancedResult;
_pathLengthLabel.Text = $"경로 길이: {advancedResult.TotalDistance:F1}";
_statusLabel.Text = $"경로 계산 완료 ({advancedResult.CalculationTimeMs}ms)";
// 도킹 검증 결과 확인 및 UI 표시
CheckAndDisplayDockingValidation(agvResult);
CheckAndDisplayDockingValidation(advancedResult);
// 고급 경로 디버깅 정보 표시
UpdateAdvancedPathDebugInfo(advancedResult);
@@ -364,7 +367,6 @@ namespace AGVSimulator.Forms
_isTargetCalcMode = false;
_targetCalcButton.Text = "타겟계산";
_targetCalcButton.BackColor = SystemColors.Control;
_simulatorCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.Select;
_statusLabel.Text = "타겟계산 모드 해제";
}
else
@@ -373,7 +375,6 @@ namespace AGVSimulator.Forms
_isTargetCalcMode = true;
_targetCalcButton.Text = "계산 취소";
_targetCalcButton.BackColor = Color.LightGreen;
_simulatorCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.SelectTarget;
_statusLabel.Text = "목적지 노드를 클릭하세요 (자동으로 경로 계산됨)";
}
}
@@ -390,6 +391,7 @@ namespace AGVSimulator.Forms
//_targetCalcButton.Text = "타겟계산";
//_targetCalcButton.BackColor = SystemColors.Control;
//_simulatorCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.Select;
if (selectedNode == null) return;
// 목적지를 선택된 노드로 설정
SetTargetNodeInCombo(selectedNode.NodeId);
@@ -985,32 +987,6 @@ namespace AGVSimulator.Forms
}
}
/// <summary>
/// 고급 경로 결과를 기존 AGVPathResult 형태로 변환 (도킹 검증 포함)
/// </summary>
private AGVPathResult ConvertToAGVPathResult(AGVPathResult advancedResult, AgvDirection? currentDirection = null)
{
var agvResult = new AGVPathResult();
agvResult.Success = advancedResult.Success;
agvResult.Path = advancedResult.GetSimplePath();
agvResult.NodeMotorInfos = advancedResult.DetailedPath;
agvResult.TotalDistance = advancedResult.TotalDistance;
agvResult.CalculationTimeMs = advancedResult.CalculationTimeMs;
agvResult.ExploredNodes = advancedResult.ExploredNodeCount;
agvResult.ErrorMessage = advancedResult.ErrorMessage;
// 도킹 검증 수행 (AdvancedPathResult에서 이미 수행되었다면 그 결과 사용)
if (advancedResult.DockingValidation != null && advancedResult.DockingValidation.IsValidationRequired)
{
agvResult.DockingValidation = advancedResult.DockingValidation;
}
else if (agvResult.Success && _mapNodes != null && currentDirection.HasValue)
{
agvResult.DockingValidation = DockingValidator.ValidateDockingDirection(agvResult, _mapNodes, currentDirection.Value);
}
return agvResult;
}
/// <summary>