Files
ENIG/Cs_HMI/AGVLogic/MAP_LOADING_BIDIRECTIONAL_FIX.md
backuppc d932b8d332 fix: Add motor direction parameter to magnet direction calculation in pathfinding
- 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>
2025-10-24 15:46:16 +09:00

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 로드하여 검증