Files
ENIG/Cs_HMI/PathLogic/test_all_scenarios.py
ChiKyun Kim b53cff02bc feat: AGV 경로 탐색 알고리즘 완성 및 검증 시스템 구축
- agv_path_planner.py: JSON 형식 맵 파일 지원 추가
  * parse_map_json() 함수로 MapData.json/NewMap.agvmap 파싱
  * RFID 정규화 및 별칭 지원 (007/7 등)
  * TP(터닝포인트) 기반 방향전환 알고리즘 검증 완료

- universal_pathfinder.py: 범용 패턴 기반 경로 탐색
  * Q1-1, Q1-2, Q2-1, Q2-2 모든 시나리오 패턴 구현
  * UniversalPathFormatter 방향전환 표기 수정
  * 28개 테스트 케이스 중 20개 성공 (71.4%)

- test_all_scenarios.py: 전체 테스트 케이스 검증 스크립트
  * 4개 시나리오 × 7개 목표 = 28개 케이스 자동 검증
  * 사용자 제공 정답과 비교 분석

- show_map_info.py: 맵 구조 분석 도구
  * RFID 목록, 연결 정보, 갈림길 분석
  * 노드 타입별 분류 및 통계

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-22 08:41:57 +09:00

125 lines
5.7 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from universal_pathfinder import UniversalAGVPathfinder, UniversalPathFormatter
from agv_pathfinder import AGVMap, AgvDirection
def test_all_scenarios():
"""전체 28개 테스트 케이스 검증"""
print("="*80)
print("전체 28개 테스트 케이스 검증")
print("="*80)
# 맵 로드
agv_map = AGVMap()
agv_map.load_from_file(r"C:\Data\Source\(5613#) ENIG AGV\Source\Cs_HMI\Data\NewMap.agvmap")
pathfinder = UniversalAGVPathfinder(agv_map)
# 모든 테스트 케이스 정의 (사용자 제공 정답)
test_scenarios = [
{
"name": "Q1-1: 033→032(전진)",
"start": "032", "came_from": "033", "direction": AgvDirection.FORWARD,
"targets": {
"040": "032 ->(F) 031 ->(R) 032 -> 040",
"041": "032 ->(F) 040 ->(R) 032 -> 031 -> 041",
"008": "032 ->(B) 033 -> 034 -> 035 -> 036 -> 037 -> 005 -> 006 -> 007 -> 008",
"001": "032 ->(B) 033 -> 034 -> 035 -> 036 -> 037 -> 005 -> 004 -> 003 -> 002 -> 001",
"011": "032 ->(B) 033 -> 034 -> 035 -> 036 -> 037 -> 005 -> 004 -> 030 -> 009 -> 010 -> 011",
"019": "032 ->(B) 033 -> 034 -> 035 -> 036 -> 037 -> 005 -> 004 -> 012 -> 013 ->(F) -> 012 -> 016 -> 017 -> 018 -> 019",
"015": "032 ->(B) 033 -> 034 -> 035 -> 036 -> 037 -> 005 -> 004 -> 012 -> 016 ->(F) -> 012 -> 013 -> 014 -> 015"
}
},
{
"name": "Q1-2: 033→032(후진)",
"start": "032", "came_from": "033", "direction": AgvDirection.BACKWARD,
"targets": {
"040": "032 ->(F) 033 ->(R) 032 -> 040",
"041": "032 ->(R) 031 -> 041",
"008": "032 ->(F) 033 -> 034 -> 035 -> 036 -> 037 -> 005 -> 004 ->(B) -> 005 -> 006 -> 007 -> 008",
"001": "032 ->(F) 033 -> 034 -> 035 -> 036 -> 037 -> 005 -> 006 ->(B) -> 004 -> 003 -> 002 -> 001",
"011": "032 ->(F) 033 -> 034 -> 035 -> 036 -> 037 -> 005 -> 004 -> 003 ->(B) -> 004 -> 030 -> 009 -> 010 -> 011",
"019": "032 ->(F) 033 -> 034 -> 035 -> 036 -> 037 -> 005 -> 004 -> 012 -> 016 -> 017 -> 018 -> 019",
"015": "032 ->(F) 033 -> 034 -> 035 -> 036 -> 037 -> 005 -> 004 -> 012 -> 013 -> 014 -> 015"
}
},
{
"name": "Q2-1: 006→007(전진)",
"start": "007", "came_from": "006", "direction": AgvDirection.FORWARD,
"targets": {
"040": "007 ->(B) 006 -> 005 -> 037 -> 036 -> 035 -> 034 -> 033 -> 032 -> 040",
"041": "007 ->(B) 006 -> 005 -> 037 -> 036 -> 035 -> 034 -> 033 -> 032 -> 031 -> 041",
"008": "007 ->(F) 006 -> 005 -> 037 ->(B) 005 -> 006 -> 007 -> 008",
"001": "007 ->(B) 006 -> 005 -> 004 -> 003 -> 002 -> 001",
"011": "007 ->(B) 006 -> 005 -> 004 -> 030 -> 009 -> 010 -> 011",
"019": "007 ->(B) 006 -> 005 -> 004 -> 012 -> 013 ->(F) 012 -> 016 -> 017 -> 018 -> 019",
"015": "007 ->(B) 006 -> 005 -> 004 -> 012 -> 016 ->(F) 012 -> 013 -> 014 -> 015"
}
},
{
"name": "Q2-2: 006→007(후진)",
"start": "007", "came_from": "006", "direction": AgvDirection.BACKWARD,
"targets": {
"040": "007 ->(F) 006 -> 005 -> 004 ->(B) 005 -> 037 -> 036 -> 035 -> 034 -> 033 -> 032 -> 040",
"041": "007 ->(F) 006 -> 005 -> 004 ->(B) 005 -> 037 -> 036 -> 035 -> 034 -> 033 -> 032 -> 031 -> 041",
"008": "007 ->(B) 008",
"001": "007 ->(F) 006 -> 005 -> 004 -> 030 ->(B) 004 -> 003 -> 002 -> 001",
"011": "007 ->(F) 006 -> 005 -> 004 -> 003 ->(B) 004 -> 030 -> 009 -> 010 -> 011",
"019": "007 ->(F) 006 -> 005 -> 004 -> 012 -> 016 -> 017 -> 018 -> 019",
"015": "007 ->(F) 006 -> 005 -> 004 -> 012 -> 013 -> 014 -> 015"
}
}
]
total_tests = 0
total_success = 0
for scenario in test_scenarios:
print(f"\n{scenario['name']}")
print("-" * 60)
scenario_success = 0
scenario_total = len(scenario['targets'])
for target_rfid, expected_path in scenario['targets'].items():
total_tests += 1
print(f"\n목표 {target_rfid}:")
print(f" 정답: {expected_path}")
result = pathfinder.find_path(
start_rfid=scenario['start'],
target_rfid=target_rfid,
current_direction=scenario['direction'],
came_from_rfid=scenario['came_from']
)
if result.success:
actual_path = UniversalPathFormatter.format_path(result, agv_map)
print(f" 실제: {actual_path}")
if actual_path == expected_path:
print(" [SUCCESS]")
scenario_success += 1
total_success += 1
else:
print(" [FAILED] - Path mismatch")
else:
print(f" [FAILED]: {result.error_message}")
print(f"\n{scenario['name']} 결과: {scenario_success}/{scenario_total} 성공")
print(f"\n{'='*80}")
print(f"전체 결과: {total_success}/{total_tests} 성공 ({total_success/total_tests*100:.1f}%)")
print(f"{'='*80}")
if total_success == total_tests:
print("*** 모든 테스트 케이스 성공! 범용 알고리즘 완성! ***")
else:
print(f"*** {total_tests - total_success}개 케이스 실패. 추가 수정 필요. ***")
return total_success == total_tests
if __name__ == "__main__":
test_all_scenarios()