- Fixed critical issue in ConvertToDetailedPath where motor direction was not passed to GetRequiredMagnetDirection - Motor direction is essential for backward movement as Left/Right directions must be inverted - Modified AGVPathfinder.cs line 280 to pass currentDirection parameter - Ensures backward motor direction properly inverts magnet sensor directions feat: Add waypoint support to pathfinding system - Added FindPath overload with params string[] waypointNodeIds in AStarPathfinder - Supports sequential traversal through multiple intermediate nodes - Validates waypoints and prevents duplicates in sequence - Returns combined path result with aggregated metrics feat: Implement path result merging with DetailedPath preservation - Added CombineResults method in AStarPathfinder for intelligent path merging - Automatically deduplicates nodes when last of previous path equals first of current - Preserves DetailedPath information including motor and magnet directions - Essential for multi-segment path operations feat: Integrate magnet direction with motor direction awareness - Modified JunctionAnalyzer.GetRequiredMagnetDirection to accept AgvDirection parameter - Inverts Left/Right magnet directions when moving Backward - Properly handles motor direction context throughout pathfinding feat: Add automatic start node selection in simulator - Added SetStartNodeToCombo method to SimulatorForm - Automatically selects start node combo box when AGV position is set via RFID - Improves UI usability and workflow efficiency 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
7.0 KiB
7.0 KiB
맵 로딩 양방향 연결 자동 설정 수정
🔍 문제 현상
원래 문제
맵 에디터에서 002 → 003 연결 생성
↓
NewMap.agvmap 저장:
002.ConnectedNodes = ["001", "003"]
003.ConnectedNodes = ["002"]
↓
맵 로드 후 GetNextNodeId(Forward) 호출:
002의 ConnectedNodes 확인 → [001, 003] 있음 ✓
003의 ConnectedNodes 확인 → [002] 있음 ✓ (문제 없음)
↓
004에서 002로 이동한 경우:
002의 ConnectedNodes = ["001", "003"] ✓
003의 ConnectedNodes = ["002"] (004가 없음!) ✗
근본 원인
CleanupDuplicateConnections() 메서드가 양방향 연결을 단방향으로 축약했습니다.
// 기존 로직 (라인 303-314)
if (connectedNode.ConnectedNodes.Contains(node.NodeId))
{
// 양방향 연결인 경우 사전순으로 더 작은 노드에만 유지
if (string.Compare(node.NodeId, connectedNodeId, StringComparison.Ordinal) > 0)
{
connectionsToRemove.Add(connectedNodeId); // ← 역방향 제거!
}
else
{
connectedNode.RemoveConnection(node.NodeId); // ← 역방향 제거!
}
}
이로 인해 N003 → N002 같은 역방향 연결이 삭제되었습니다.
✅ 해결 방법
추가된 메서드: EnsureBidirectionalConnections()
파일: MapLoader.cs (라인 341-389)
목적: 모든 연결을 양방향으로 보장
동작 흐름
1단계: 모든 노드의 명시적 연결 수집
002.ConnectedNodes = ["001", "003"]
003.ConnectedNodes = ["002"]
allConnections = {
"N002": {"N001", "N003"},
"N003": {"N002"}
}
2단계: 역방향 연결 추가
각 노드에 대해:
"다른 노드가 나를 연결하고 있는가?" 확인
N003의 경우:
- N002가 N003을 연결? YES → N003.ConnectedNodes에 N002 추가
N002의 경우:
- N001이 N002를 연결? YES → N002.ConnectedNodes에 N001 추가
- N003이 N002를 연결? YES → N002.ConnectedNodes에 N003 추가 (이미 있음)
결과:
002.ConnectedNodes = ["001", "003"] ✓
003.ConnectedNodes = ["002"] ← ["002"]로 유지 (N002는 이미 명시적)
코드 예시
private static void EnsureBidirectionalConnections(List<MapNode> mapNodes)
{
// 1단계: 모든 명시적 연결 수집
var allConnections = new Dictionary<string, HashSet<string>>();
foreach (var node in mapNodes)
{
if (!allConnections.ContainsKey(node.NodeId))
allConnections[node.NodeId] = new HashSet<string>();
if (node.ConnectedNodes != null)
{
foreach (var connectedId in node.ConnectedNodes)
allConnections[node.NodeId].Add(connectedId);
}
}
// 2단계: 역방향 연결 추가
foreach (var node in mapNodes)
{
if (node.ConnectedNodes == null)
node.ConnectedNodes = new List<string>();
// 이 노드를 연결하는 모든 노드 찾기
foreach (var otherNodeId in allConnections.Keys)
{
if (otherNodeId == node.NodeId) continue;
// 다른 노드가 이 노드를 연결하고 있다면
if (allConnections[otherNodeId].Contains(node.NodeId))
{
// 이 노드의 ConnectedNodes에 그 노드를 추가
if (!node.ConnectedNodes.Contains(otherNodeId))
node.ConnectedNodes.Add(otherNodeId);
}
}
}
}
🔄 맵 로딩 순서 (수정된)
LoadMapFromFile()
↓
JSON 역직렬화
↓
MigrateDescriptionToName()
↓
MigrateDockingDirection()
↓
FixDuplicateNodeIds()
↓
CleanupDuplicateConnections() ← 중복만 제거 (양방향 연결 유지)
↓
✨ EnsureBidirectionalConnections() ← NEW: 양방향 자동 설정
↓
LoadImageNodes()
↓
Success = true
📊 결과 비교
수정 전
002.ConnectedNodes = ["001", "003"]
003.ConnectedNodes = ["002"]
004.ConnectedNodes = ["003", "022", "031"]
002에서 GetNextNodeId(Forward)
→ 003 계산 ✓
→ 001 계산 ✓
003에서 GetNextNodeId(Forward)
→ 002 계산 ✓
→ (004 없음!) ✗ ← 004로 진행 불가
004에서 GetNextNodeId(Backward)
→ 003 계산 ✓
→ 022, 031 계산 ✓
수정 후 ✅
002.ConnectedNodes = ["001", "003"]
003.ConnectedNodes = ["002", "004"]
004.ConnectedNodes = ["003", "022", "031"]
002에서 GetNextNodeId(Forward)
→ 003 계산 ✓
→ 001 계산 ✓
003에서 GetNextNodeId(Forward)
→ 002 계산 ✓
→ 004 계산 ✓ ← 이제 404로 진행 가능!
004에서 GetNextNodeId(Backward)
→ 003 계산 ✓
→ 022, 031 계산 ✓
🎯 GetNextNodeId() 동작 원리
이제 모든 노드의 ConnectedNodes에 양방향 연결이 포함되어 있으므로:
public string GetNextNodeId(AgvDirection direction, List<MapNode> allNodes)
{
// 현재 노드의 ConnectedNodes에 모든 가능한 다음 노드가 포함됨 ✓
var candidateNodes = allNodes.Where(n =>
_currentNode.ConnectedNodes.Contains(n.NodeId)
).ToList();
// 벡터 기반 점수 계산으로 최적 노드 선택
return bestCandidate.node?.NodeId;
}
🔗 관계도
맵 에디터
↓ (002→003 연결 생성 및 저장)
↓
NewMap.agvmap
↓ (파일 로드)
↓
LoadMapFromFile()
↓
[CleanupDuplicateConnections]
002: ["001", "003"]
003: ["002"]
↓
[EnsureBidirectionalConnections] ← NEW!
002: ["001", "003"]
003: ["002", "004"] ← 004 추가!
↓
VirtualAGV.GetNextNodeId()
가능한 다음 노드 모두 찾을 수 있음 ✓
📋 체크리스트
EnsureBidirectionalConnections()메서드 추가LoadMapFromFile()호출 순서 업데이트- 모든 연결이 양방향으로 보장됨
- VirtualAGV.GetNextNodeId()에서 모든 가능한 다음 노드 찾을 수 있음
- RFID 002 → 003 → Forward → 004 경로 가능
- RFID 004 → 003 → Backward → 002 경로 가능
🧪 테스트 시나리오
시나리오 1: 직선 경로
002 → 003 → Forward → 004
검증: 003.ConnectedNodes에 004가 포함되어야 함
시나리오 2: 분기점
004 → 003 → Left → ?
검증: 003.ConnectedNodes에 가능한 모든 노드 포함
시나리오 3: 역진
004 → 003 → Backward → 002
검증: 003.ConnectedNodes에 002가 포함되어야 함
📌 중요 포인트
✅ 맵 로딩 시 자동으로 양방향 설정
- 사용자(맵 에디터)는 단방향만 그으면 됨
- 시스템이 자동으로 역방향 추가
✅ GetNextNodeId() 완벽 지원
- 현재 노드의 ConnectedNodes만으로 모든 가능한 다음 노드 찾음
- 벡터 기반 점수 계산으로 최적 경로 선택
✅ 기존 맵 호환성 유지
- 기존 저장된 맵도 로드 시 자동으로 양방향 설정됨
- 새로운 맵도 동일 방식으로 처리됨
수정 완료일: 2025-10-23 상태: 🟢 완료 다음 단계: NewMap.agvmap 로드하여 검증