refactor: PathFinding 폴더 구조 개선 및 도킹 에러 기능 추가
- PathFinding 폴더를 Core, Validation, Planning, Analysis로 세분화 - 네임스페이스 정리 및 using 문 업데이트 - UnifiedAGVCanvas에 SetDockingError 메서드 추가 - 도킹 검증 시스템 인프라 구축 - DockingValidator 유틸리티 클래스 추가 - 빌드 오류 수정 및 안정성 개선 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,309 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AGVNavigationCore.Models;
|
||||
|
||||
namespace AGVMapEditor.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// RFID 값을 논리적 노드로 변환하는 클래스
|
||||
/// 실제 AGV 시스템에서 RFID 리더가 읽은 값을 맵 노드 정보로 변환
|
||||
/// </summary>
|
||||
public class NodeResolver
|
||||
{
|
||||
private List<RfidMapping> _rfidMappings;
|
||||
private List<MapNode> _mapNodes;
|
||||
|
||||
/// <summary>
|
||||
/// 기본 생성자
|
||||
/// </summary>
|
||||
public NodeResolver()
|
||||
{
|
||||
_rfidMappings = new List<RfidMapping>();
|
||||
_mapNodes = new List<MapNode>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 매개변수 생성자
|
||||
/// </summary>
|
||||
/// <param name="rfidMappings">RFID 매핑 목록</param>
|
||||
/// <param name="mapNodes">맵 노드 목록</param>
|
||||
public NodeResolver(List<RfidMapping> rfidMappings, List<MapNode> mapNodes)
|
||||
{
|
||||
_rfidMappings = rfidMappings ?? new List<RfidMapping>();
|
||||
_mapNodes = mapNodes ?? new List<MapNode>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RFID 값으로 맵 노드 검색
|
||||
/// </summary>
|
||||
/// <param name="rfidValue">RFID 리더에서 읽은 값</param>
|
||||
/// <returns>해당하는 맵 노드, 없으면 null</returns>
|
||||
public MapNode GetNodeByRfid(string rfidValue)
|
||||
{
|
||||
if (string.IsNullOrEmpty(rfidValue))
|
||||
return null;
|
||||
|
||||
// 1. RFID 매핑에서 논리적 노드 ID 찾기
|
||||
var mapping = _rfidMappings.FirstOrDefault(m =>
|
||||
m.RfidId.Equals(rfidValue, StringComparison.OrdinalIgnoreCase) && m.IsActive);
|
||||
|
||||
if (mapping == null)
|
||||
return null;
|
||||
|
||||
// 2. 논리적 노드 ID로 실제 맵 노드 찾기
|
||||
var mapNode = _mapNodes.FirstOrDefault(n =>
|
||||
n.NodeId.Equals(mapping.LogicalNodeId, StringComparison.OrdinalIgnoreCase) && n.IsActive);
|
||||
|
||||
return mapNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 논리적 노드 ID로 맵 노드 검색
|
||||
/// </summary>
|
||||
/// <param name="nodeId">논리적 노드 ID</param>
|
||||
/// <returns>해당하는 맵 노드, 없으면 null</returns>
|
||||
public MapNode GetNodeById(string nodeId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(nodeId))
|
||||
return null;
|
||||
|
||||
return _mapNodes.FirstOrDefault(n =>
|
||||
n.NodeId.Equals(nodeId, StringComparison.OrdinalIgnoreCase) && n.IsActive);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 맵 노드로 연결된 RFID 값 검색
|
||||
/// </summary>
|
||||
/// <param name="nodeId">논리적 노드 ID</param>
|
||||
/// <returns>연결된 RFID 값, 없으면 null</returns>
|
||||
public string GetRfidByNodeId(string nodeId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(nodeId))
|
||||
return null;
|
||||
|
||||
var mapping = _rfidMappings.FirstOrDefault(m =>
|
||||
m.LogicalNodeId.Equals(nodeId, StringComparison.OrdinalIgnoreCase) && m.IsActive);
|
||||
|
||||
return mapping?.RfidId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RFID 매핑 정보 검색
|
||||
/// </summary>
|
||||
/// <param name="rfidValue">RFID 값</param>
|
||||
/// <returns>매핑 정보, 없으면 null</returns>
|
||||
public RfidMapping GetRfidMapping(string rfidValue)
|
||||
{
|
||||
if (string.IsNullOrEmpty(rfidValue))
|
||||
return null;
|
||||
|
||||
return _rfidMappings.FirstOrDefault(m =>
|
||||
m.RfidId.Equals(rfidValue, StringComparison.OrdinalIgnoreCase) && m.IsActive);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 새로운 RFID 매핑 추가
|
||||
/// </summary>
|
||||
/// <param name="rfidId">RFID 값</param>
|
||||
/// <param name="nodeId">논리적 노드 ID</param>
|
||||
/// <param name="description">설명</param>
|
||||
/// <returns>추가 성공 여부</returns>
|
||||
public bool AddRfidMapping(string rfidId, string nodeId, string description = "")
|
||||
{
|
||||
if (string.IsNullOrEmpty(rfidId) || string.IsNullOrEmpty(nodeId))
|
||||
return false;
|
||||
|
||||
// 중복 RFID 체크
|
||||
if (_rfidMappings.Any(m => m.RfidId.Equals(rfidId, StringComparison.OrdinalIgnoreCase)))
|
||||
return false;
|
||||
|
||||
// 해당 노드 존재 체크
|
||||
if (!_mapNodes.Any(n => n.NodeId.Equals(nodeId, StringComparison.OrdinalIgnoreCase)))
|
||||
return false;
|
||||
|
||||
var mapping = new RfidMapping(rfidId, nodeId, description);
|
||||
_rfidMappings.Add(mapping);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RFID 매핑 제거
|
||||
/// </summary>
|
||||
/// <param name="rfidId">제거할 RFID 값</param>
|
||||
/// <returns>제거 성공 여부</returns>
|
||||
public bool RemoveRfidMapping(string rfidId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(rfidId))
|
||||
return false;
|
||||
|
||||
var mapping = _rfidMappings.FirstOrDefault(m =>
|
||||
m.RfidId.Equals(rfidId, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (mapping != null)
|
||||
{
|
||||
_rfidMappings.Remove(mapping);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 맵 노드 추가
|
||||
/// </summary>
|
||||
/// <param name="node">추가할 맵 노드</param>
|
||||
/// <returns>추가 성공 여부</returns>
|
||||
public bool AddMapNode(MapNode node)
|
||||
{
|
||||
if (node == null || string.IsNullOrEmpty(node.NodeId))
|
||||
return false;
|
||||
|
||||
// 중복 노드 ID 체크
|
||||
if (_mapNodes.Any(n => n.NodeId.Equals(node.NodeId, StringComparison.OrdinalIgnoreCase)))
|
||||
return false;
|
||||
|
||||
_mapNodes.Add(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 맵 노드 제거
|
||||
/// </summary>
|
||||
/// <param name="nodeId">제거할 노드 ID</param>
|
||||
/// <returns>제거 성공 여부</returns>
|
||||
public bool RemoveMapNode(string nodeId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(nodeId))
|
||||
return false;
|
||||
|
||||
var node = _mapNodes.FirstOrDefault(n =>
|
||||
n.NodeId.Equals(nodeId, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (node != null)
|
||||
{
|
||||
// 연관된 RFID 매핑도 함께 제거
|
||||
var associatedMappings = _rfidMappings.Where(m =>
|
||||
m.LogicalNodeId.Equals(nodeId, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
foreach (var mapping in associatedMappings)
|
||||
{
|
||||
_rfidMappings.Remove(mapping);
|
||||
}
|
||||
|
||||
// 다른 노드의 연결 정보에서도 제거
|
||||
foreach (var otherNode in _mapNodes.Where(n => n.ConnectedNodes.Contains(nodeId)))
|
||||
{
|
||||
otherNode.RemoveConnection(nodeId);
|
||||
}
|
||||
|
||||
_mapNodes.Remove(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 특정 타입의 노드들 검색
|
||||
/// </summary>
|
||||
/// <param name="nodeType">노드 타입</param>
|
||||
/// <returns>해당 타입의 노드 목록</returns>
|
||||
public List<MapNode> GetNodesByType(NodeType nodeType)
|
||||
{
|
||||
return _mapNodes.Where(n => n.Type == nodeType && n.IsActive).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 장비 ID로 노드 검색
|
||||
/// </summary>
|
||||
/// <param name="stationId">장비 ID</param>
|
||||
/// <returns>해당 장비의 노드, 없으면 null</returns>
|
||||
public MapNode GetNodeByStationId(string stationId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(stationId))
|
||||
return null;
|
||||
|
||||
return _mapNodes.FirstOrDefault(n =>
|
||||
n.StationId.Equals(stationId, StringComparison.OrdinalIgnoreCase) && n.IsActive);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 매핑되지 않은 노드들 검색 (RFID가 연결되지 않은 노드)
|
||||
/// </summary>
|
||||
/// <returns>매핑되지 않은 노드 목록</returns>
|
||||
public List<MapNode> GetUnmappedNodes()
|
||||
{
|
||||
var mappedNodeIds = _rfidMappings.Where(m => m.IsActive)
|
||||
.Select(m => m.LogicalNodeId)
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
return _mapNodes.Where(n => n.IsActive && !mappedNodeIds.Contains(n.NodeId)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 사용되지 않는 RFID 매핑들 검색 (노드가 삭제된 매핑)
|
||||
/// </summary>
|
||||
/// <returns>사용되지 않는 매핑 목록</returns>
|
||||
public List<RfidMapping> GetOrphanedMappings()
|
||||
{
|
||||
var activeNodeIds = _mapNodes.Where(n => n.IsActive)
|
||||
.Select(n => n.NodeId)
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
return _rfidMappings.Where(m => m.IsActive && !activeNodeIds.Contains(m.LogicalNodeId)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 데이터 초기화
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
_rfidMappings.Clear();
|
||||
_mapNodes.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 데이터 유효성 검증
|
||||
/// </summary>
|
||||
/// <returns>검증 결과 메시지 목록</returns>
|
||||
public List<string> ValidateData()
|
||||
{
|
||||
var errors = new List<string>();
|
||||
|
||||
// 중복 RFID 체크
|
||||
var duplicateRfids = _rfidMappings.GroupBy(m => m.RfidId.ToLower())
|
||||
.Where(g => g.Count() > 1)
|
||||
.Select(g => g.Key);
|
||||
foreach (var rfid in duplicateRfids)
|
||||
{
|
||||
errors.Add($"중복된 RFID: {rfid}");
|
||||
}
|
||||
|
||||
// 중복 노드 ID 체크
|
||||
var duplicateNodeIds = _mapNodes.GroupBy(n => n.NodeId.ToLower())
|
||||
.Where(g => g.Count() > 1)
|
||||
.Select(g => g.Key);
|
||||
foreach (var nodeId in duplicateNodeIds)
|
||||
{
|
||||
errors.Add($"중복된 노드 ID: {nodeId}");
|
||||
}
|
||||
|
||||
// 고아 매핑 체크
|
||||
var orphanedMappings = GetOrphanedMappings();
|
||||
foreach (var mapping in orphanedMappings)
|
||||
{
|
||||
errors.Add($"존재하지 않는 노드를 참조하는 RFID 매핑: {mapping.RfidId} → {mapping.LogicalNodeId}");
|
||||
}
|
||||
|
||||
// 매핑되지 않은 노드 경고 (에러는 아님)
|
||||
var unmappedNodes = GetUnmappedNodes();
|
||||
foreach (var node in unmappedNodes)
|
||||
{
|
||||
errors.Add($"RFID가 매핑되지 않은 노드: {node.NodeId} ({node.Name})");
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,432 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using AGVNavigationCore.Models;
|
||||
using AGVNavigationCore.PathFinding;
|
||||
|
||||
namespace AGVMapEditor.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// AGV 전용 경로 계산기 (AGVNavigationCore 래퍼)
|
||||
/// AGVMapEditor와 AGVNavigationCore 간의 호환성 제공
|
||||
/// RFID 기반 경로 계산을 우선 사용
|
||||
/// </summary>
|
||||
public class PathCalculator
|
||||
{
|
||||
private AGVPathfinder _agvPathfinder;
|
||||
private AStarPathfinder _astarPathfinder;
|
||||
private RfidBasedPathfinder _rfidPathfinder;
|
||||
|
||||
/// <summary>
|
||||
/// 생성자
|
||||
/// </summary>
|
||||
public PathCalculator()
|
||||
{
|
||||
_agvPathfinder = new AGVPathfinder();
|
||||
_astarPathfinder = new AStarPathfinder();
|
||||
_rfidPathfinder = new RfidBasedPathfinder();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 맵 노드 설정
|
||||
/// </summary>
|
||||
/// <param name="mapNodes">맵 노드 목록</param>
|
||||
public void SetMapNodes(List<MapNode> mapNodes)
|
||||
{
|
||||
_agvPathfinder.SetMapNodes(mapNodes);
|
||||
_astarPathfinder.SetMapNodes(mapNodes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 맵 데이터 설정
|
||||
/// </summary>
|
||||
/// <param name="mapNodes">맵 노드 목록</param>
|
||||
public void SetMapData(List<MapNode> mapNodes)
|
||||
{
|
||||
_agvPathfinder.SetMapNodes(mapNodes);
|
||||
_astarPathfinder.SetMapNodes(mapNodes);
|
||||
// RfidPathfinder는 MapNode의 RFID 정보를 직접 사용
|
||||
_rfidPathfinder.SetMapNodes(mapNodes);
|
||||
// 도킹 조건 검색용 내부 노드 목록 업데이트
|
||||
UpdateInternalMapNodes(mapNodes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AGV 경로 계산
|
||||
/// </summary>
|
||||
/// <param name="startNodeId">시작 노드 ID</param>
|
||||
/// <param name="endNodeId">목적지 노드 ID</param>
|
||||
/// <param name="targetDirection">목적지 도착 방향</param>
|
||||
/// <returns>AGV 경로 계산 결과</returns>
|
||||
public AGVPathResult FindAGVPath(string startNodeId, string endNodeId, AgvDirection? targetDirection = null)
|
||||
{
|
||||
return _agvPathfinder.FindAGVPath(startNodeId, endNodeId, targetDirection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AGV 경로 계산 (옵션 지정 가능)
|
||||
/// </summary>
|
||||
/// <param name="startNodeId">시작 노드 ID</param>
|
||||
/// <param name="endNodeId">목적지 노드 ID</param>
|
||||
/// <param name="targetDirection">목적지 도착 방향</param>
|
||||
/// <param name="options">경로 탐색 옵션</param>
|
||||
/// <returns>AGV 경로 계산 결과</returns>
|
||||
public AGVPathResult FindAGVPath(string startNodeId, string endNodeId, AgvDirection? targetDirection, PathfindingOptions options)
|
||||
{
|
||||
return _agvPathfinder.FindAGVPath(startNodeId, endNodeId, targetDirection, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AGV 경로 계산 (현재 방향 및 PathfindingOptions 지원)
|
||||
/// </summary>
|
||||
/// <param name="startNodeId">시작 노드 ID</param>
|
||||
/// <param name="endNodeId">목적지 노드 ID</param>
|
||||
/// <param name="currentDirection">현재 AGV 방향</param>
|
||||
/// <param name="targetDirection">목적지 도착 방향</param>
|
||||
/// <param name="options">경로 탐색 옵션</param>
|
||||
/// <returns>AGV 경로 계산 결과</returns>
|
||||
public AGVPathResult FindAGVPath(string startNodeId, string endNodeId, AgvDirection? currentDirection, AgvDirection? targetDirection, PathfindingOptions options)
|
||||
{
|
||||
return _agvPathfinder.FindAGVPath(startNodeId, endNodeId, currentDirection, targetDirection, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 충전 스테이션으로의 경로 찾기
|
||||
/// </summary>
|
||||
/// <param name="startNodeId">시작 노드 ID</param>
|
||||
/// <returns>AGV 경로 계산 결과</returns>
|
||||
public AGVPathResult FindPathToChargingStation(string startNodeId)
|
||||
{
|
||||
return _agvPathfinder.FindPathToChargingStation(startNodeId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 도킹 스테이션으로의 경로 찾기
|
||||
/// </summary>
|
||||
/// <param name="startNodeId">시작 노드 ID</param>
|
||||
/// <param name="stationType">장비 타입</param>
|
||||
/// <returns>AGV 경로 계산 결과</returns>
|
||||
public AGVPathResult FindPathToDockingStation(string startNodeId, StationType stationType)
|
||||
{
|
||||
return _agvPathfinder.FindPathToDockingStation(startNodeId, stationType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 여러 목적지 중 가장 가까운 노드로의 경로 찾기
|
||||
/// </summary>
|
||||
/// <param name="startNodeId">시작 노드 ID</param>
|
||||
/// <param name="targetNodeIds">목적지 후보 노드 ID 목록</param>
|
||||
/// <returns>경로 계산 결과</returns>
|
||||
public PathResult FindNearestPath(string startNodeId, List<string> targetNodeIds)
|
||||
{
|
||||
return _astarPathfinder.FindNearestPath(startNodeId, targetNodeIds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 두 노드가 연결되어 있는지 확인
|
||||
/// </summary>
|
||||
/// <param name="nodeId1">노드 1 ID</param>
|
||||
/// <param name="nodeId2">노드 2 ID</param>
|
||||
/// <returns>연결 여부</returns>
|
||||
public bool AreNodesConnected(string nodeId1, string nodeId2)
|
||||
{
|
||||
return _astarPathfinder.AreNodesConnected(nodeId1, nodeId2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 경로 유효성 검증
|
||||
/// </summary>
|
||||
/// <param name="path">검증할 경로</param>
|
||||
/// <returns>유효성 검증 결과</returns>
|
||||
public bool ValidatePath(List<string> path)
|
||||
{
|
||||
return _agvPathfinder.ValidatePath(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 네비게이션 가능한 노드 목록 반환
|
||||
/// </summary>
|
||||
/// <returns>노드 ID 목록</returns>
|
||||
public List<string> GetNavigationNodes()
|
||||
{
|
||||
return _astarPathfinder.GetNavigationNodes();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AGV 현재 방향 설정
|
||||
/// </summary>
|
||||
/// <param name="direction">현재 방향</param>
|
||||
public void SetCurrentDirection(AgvDirection direction)
|
||||
{
|
||||
_agvPathfinder.CurrentDirection = direction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 회전 비용 가중치 설정
|
||||
/// </summary>
|
||||
/// <param name="weight">회전 비용 가중치</param>
|
||||
public void SetRotationCostWeight(float weight)
|
||||
{
|
||||
_agvPathfinder.RotationCostWeight = weight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 휴리스틱 가중치 설정
|
||||
/// </summary>
|
||||
/// <param name="weight">휴리스틱 가중치</param>
|
||||
public void SetHeuristicWeight(float weight)
|
||||
{
|
||||
_astarPathfinder.HeuristicWeight = weight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 최대 탐색 노드 수 설정
|
||||
/// </summary>
|
||||
/// <param name="maxNodes">최대 탐색 노드 수</param>
|
||||
public void SetMaxSearchNodes(int maxNodes)
|
||||
{
|
||||
_astarPathfinder.MaxSearchNodes = maxNodes;
|
||||
}
|
||||
|
||||
// ==================== RFID 기반 경로 계산 메서드들 ====================
|
||||
|
||||
/// <summary>
|
||||
/// RFID 기반 AGV 경로 계산
|
||||
/// </summary>
|
||||
/// <param name="startRfidId">시작 RFID</param>
|
||||
/// <param name="endRfidId">목적지 RFID</param>
|
||||
/// <param name="targetDirection">목적지 도착 방향</param>
|
||||
/// <returns>RFID 기반 AGV 경로 계산 결과</returns>
|
||||
public RfidPathResult FindAGVPathByRfid(string startRfidId, string endRfidId, AgvDirection? targetDirection = null)
|
||||
{
|
||||
return _rfidPathfinder.FindAGVPath(startRfidId, endRfidId, targetDirection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RFID 기반 충전소 경로 찾기
|
||||
/// </summary>
|
||||
/// <param name="startRfidId">시작 RFID</param>
|
||||
/// <returns>RFID 기반 경로 계산 결과</returns>
|
||||
public RfidPathResult FindPathToChargingStationByRfid(string startRfidId)
|
||||
{
|
||||
return _rfidPathfinder.FindPathToChargingStation(startRfidId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RFID 기반 도킹 스테이션 경로 찾기
|
||||
/// </summary>
|
||||
/// <param name="startRfidId">시작 RFID</param>
|
||||
/// <param name="stationType">장비 타입</param>
|
||||
/// <returns>RFID 기반 경로 계산 결과</returns>
|
||||
public RfidPathResult FindPathToDockingStationByRfid(string startRfidId, StationType stationType)
|
||||
{
|
||||
return _rfidPathfinder.FindPathToDockingStation(startRfidId, stationType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 여러 RFID 목적지 중 가장 가까운 곳으로의 경로 찾기
|
||||
/// </summary>
|
||||
/// <param name="startRfidId">시작 RFID</param>
|
||||
/// <param name="targetRfidIds">목적지 후보 RFID 목록</param>
|
||||
/// <returns>RFID 기반 경로 계산 결과</returns>
|
||||
public RfidPathResult FindNearestPathByRfid(string startRfidId, List<string> targetRfidIds)
|
||||
{
|
||||
return _rfidPathfinder.FindNearestPath(startRfidId, targetRfidIds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RFID 매핑 정보 조회 (MapNode 반환)
|
||||
/// </summary>
|
||||
/// <param name="rfidId">RFID</param>
|
||||
/// <returns>MapNode 또는 null</returns>
|
||||
public MapNode GetRfidMapping(string rfidId)
|
||||
{
|
||||
return _rfidPathfinder.GetRfidMapping(rfidId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RFID로 NodeId 조회
|
||||
/// </summary>
|
||||
/// <param name="rfidId">RFID</param>
|
||||
/// <returns>NodeId 또는 null</returns>
|
||||
public string GetNodeIdByRfid(string rfidId)
|
||||
{
|
||||
return _rfidPathfinder.GetNodeIdByRfid(rfidId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NodeId로 RFID 조회
|
||||
/// </summary>
|
||||
/// <param name="nodeId">NodeId</param>
|
||||
/// <returns>RFID 또는 null</returns>
|
||||
public string GetRfidByNodeId(string nodeId)
|
||||
{
|
||||
return _rfidPathfinder.GetRfidByNodeId(nodeId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 활성화된 RFID 목록 반환
|
||||
/// </summary>
|
||||
/// <returns>활성화된 RFID 목록</returns>
|
||||
public List<string> GetActiveRfidList()
|
||||
{
|
||||
return _rfidPathfinder.GetActiveRfidList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RFID pathfinder의 AGV 현재 방향 설정
|
||||
/// </summary>
|
||||
/// <param name="direction">현재 방향</param>
|
||||
public void SetRfidPathfinderCurrentDirection(AgvDirection direction)
|
||||
{
|
||||
_rfidPathfinder.CurrentDirection = direction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RFID pathfinder의 회전 비용 가중치 설정
|
||||
/// </summary>
|
||||
/// <param name="weight">회전 비용 가중치</param>
|
||||
public void SetRfidPathfinderRotationCostWeight(float weight)
|
||||
{
|
||||
_rfidPathfinder.RotationCostWeight = weight;
|
||||
}
|
||||
|
||||
#region 도킹 조건 검색 기능
|
||||
|
||||
// 내부 노드 목록 저장
|
||||
private List<MapNode> _mapNodes;
|
||||
|
||||
/// <summary>
|
||||
/// 맵 노드 설정 (도킹 조건 검색용)
|
||||
/// </summary>
|
||||
private void UpdateInternalMapNodes(List<MapNode> mapNodes)
|
||||
{
|
||||
_mapNodes = mapNodes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 도킹 방향 기반 노드 검색
|
||||
/// </summary>
|
||||
/// <param name="dockingDirection">도킹 방향</param>
|
||||
/// <returns>해당 도킹 방향의 노드 목록</returns>
|
||||
public List<MapNode> GetNodesByDockingDirection(DockingDirection dockingDirection)
|
||||
{
|
||||
if (_mapNodes == null) return new List<MapNode>();
|
||||
|
||||
var result = new List<MapNode>();
|
||||
|
||||
foreach (var node in _mapNodes)
|
||||
{
|
||||
if (!node.IsActive) continue;
|
||||
|
||||
var nodeDockingDirection = GetNodeDockingDirection(node);
|
||||
if (nodeDockingDirection == dockingDirection)
|
||||
{
|
||||
result.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 노드의 도킹 방향 결정
|
||||
/// </summary>
|
||||
/// <param name="node">노드</param>
|
||||
/// <returns>도킹 방향</returns>
|
||||
public DockingDirection GetNodeDockingDirection(MapNode node)
|
||||
{
|
||||
switch (node.Type)
|
||||
{
|
||||
case NodeType.Charging:
|
||||
return DockingDirection.Forward; // 충전기: 전진 도킹
|
||||
case NodeType.Docking:
|
||||
return DockingDirection.Backward; // 장비: 후진 도킹
|
||||
default:
|
||||
return DockingDirection.Forward; // 기본값: 전진
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 도킹 방향과 장비 타입에 맞는 노드들로의 경로 검색
|
||||
/// </summary>
|
||||
/// <param name="startRfidId">시작 RFID</param>
|
||||
/// <param name="dockingDirection">필요한 도킹 방향</param>
|
||||
/// <param name="stationType">장비 타입 (선택사항)</param>
|
||||
/// <returns>경로 계산 결과 목록 (거리 순 정렬)</returns>
|
||||
public List<RfidPathResult> FindPathsByDockingCondition(string startRfidId, DockingDirection dockingDirection, StationType? stationType = null)
|
||||
{
|
||||
var targetNodes = GetNodesByDockingDirection(dockingDirection);
|
||||
var results = new List<RfidPathResult>();
|
||||
|
||||
// 장비 타입 필터링 (필요시)
|
||||
if (stationType.HasValue && dockingDirection == DockingDirection.Backward)
|
||||
{
|
||||
// 후진 도킹이면서 특정 장비 타입이 지정된 경우
|
||||
// 이 부분은 추후 StationMapping 정보가 있을 때 구현
|
||||
// 현재는 모든 도킹 노드를 대상으로 함
|
||||
}
|
||||
|
||||
foreach (var targetNode in targetNodes)
|
||||
{
|
||||
if (!targetNode.HasRfid()) continue;
|
||||
|
||||
try
|
||||
{
|
||||
var pathResult = _rfidPathfinder.FindAGVPath(startRfidId, targetNode.RfidId);
|
||||
if (pathResult.Success)
|
||||
{
|
||||
results.Add(pathResult);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 개별 경로 계산 실패는 무시하고 계속 진행
|
||||
System.Diagnostics.Debug.WriteLine($"Path calculation failed from {startRfidId} to {targetNode.RfidId}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 거리 순으로 정렬
|
||||
return results.OrderBy(r => r.TotalDistance).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 가장 가까운 충전기 경로 찾기 (전진 도킹)
|
||||
/// </summary>
|
||||
/// <param name="startRfidId">시작 RFID</param>
|
||||
/// <returns>가장 가까운 충전기로의 경로</returns>
|
||||
public RfidPathResult FindNearestChargingStationPath(string startRfidId)
|
||||
{
|
||||
var chargingPaths = FindPathsByDockingCondition(startRfidId, DockingDirection.Forward);
|
||||
var chargingNodes = chargingPaths.Where(p => p.Success).ToList();
|
||||
|
||||
return chargingNodes.FirstOrDefault() ?? new RfidPathResult
|
||||
{
|
||||
Success = false,
|
||||
ErrorMessage = "충전 가능한 충전기를 찾을 수 없습니다."
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 가장 가까운 장비 도킹 경로 찾기 (후진 도킹)
|
||||
/// </summary>
|
||||
/// <param name="startRfidId">시작 RFID</param>
|
||||
/// <param name="stationType">장비 타입 (선택사항)</param>
|
||||
/// <returns>가장 가까운 장비로의 경로</returns>
|
||||
public RfidPathResult FindNearestEquipmentPath(string startRfidId, StationType? stationType = null)
|
||||
{
|
||||
var equipmentPaths = FindPathsByDockingCondition(startRfidId, DockingDirection.Backward, stationType);
|
||||
var equipmentNodes = equipmentPaths.Where(p => p.Success).ToList();
|
||||
|
||||
return equipmentNodes.FirstOrDefault() ?? new RfidPathResult
|
||||
{
|
||||
Success = false,
|
||||
ErrorMessage = $"도킹 가능한 장비를 찾을 수 없습니다. ({stationType?.ToString() ?? "모든 타입"})"
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user