# 맵 로딩 양방향 연결 자동 설정 수정 ## 🔍 문제 현상 ### 원래 문제 ``` 맵 에디터에서 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()` 메서드가 양방향 연결을 **단방향으로 축약**했습니다. ```csharp // 기존 로직 (라인 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는 이미 명시적) ``` #### 코드 예시 ```csharp private static void EnsureBidirectionalConnections(List mapNodes) { // 1단계: 모든 명시적 연결 수집 var allConnections = new Dictionary>(); foreach (var node in mapNodes) { if (!allConnections.ContainsKey(node.NodeId)) allConnections[node.NodeId] = new HashSet(); 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(); // 이 노드를 연결하는 모든 노드 찾기 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`에 양방향 연결이 포함되어 있으므로: ```csharp public string GetNextNodeId(AgvDirection direction, List 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() 가능한 다음 노드 모두 찾을 수 있음 ✓ ``` --- ## 📋 체크리스트 - [x] `EnsureBidirectionalConnections()` 메서드 추가 - [x] `LoadMapFromFile()` 호출 순서 업데이트 - [x] 모든 연결이 양방향으로 보장됨 - [x] VirtualAGV.GetNextNodeId()에서 모든 가능한 다음 노드 찾을 수 있음 - [x] RFID 002 → 003 → Forward → 004 경로 가능 - [x] 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 로드하여 검증