agv 노드 정보 정리 세분화

This commit is contained in:
backuppc
2025-12-09 13:18:22 +09:00
parent 455e18f427
commit 9db031c305
28 changed files with 992 additions and 1476 deletions

View File

@@ -416,10 +416,10 @@ namespace AGVNavigationCore.Controls
case NodeType.Normal:
ghostBrush = new SolidBrush(Color.FromArgb(120, 100, 149, 237)); // 반투명 파란색
break;
case NodeType.Rotation:
ghostBrush = new SolidBrush(Color.FromArgb(120, 255, 165, 0)); // 반투명 주황색
break;
case NodeType.Docking:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
ghostBrush = new SolidBrush(Color.FromArgb(120, 50, 205, 50)); // 반투명 초록색
break;
case NodeType.Charging:
@@ -439,7 +439,10 @@ namespace AGVNavigationCore.Controls
case NodeType.Image:
DrawImageGhost(g);
break;
case NodeType.Docking:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
DrawPentagonGhost(g, ghostBrush);
break;
case NodeType.Charging:
@@ -646,7 +649,10 @@ namespace AGVNavigationCore.Controls
switch (node.Type)
{
case NodeType.Docking:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
DrawPentagonNodeShape(g, node, brush);
break;
case NodeType.Charging:
@@ -723,6 +729,13 @@ namespace AGVNavigationCore.Controls
{
DrawDuplicateRfidMarker(g, node);
}
// CanCross 가능 노드 표시 (교차지점으로 사용 가능)
if (node.DisableCross==true)
{
var crossRect = new Rectangle(rect.X - 3, rect.Y - 3, rect.Width + 6, rect.Height + 6);
g.DrawEllipse(new Pen(Color.DeepSkyBlue, 3), crossRect);
}
}
private void DrawPentagonNodeShape(Graphics g, MapNode node, Brush brush)
@@ -826,6 +839,21 @@ namespace AGVNavigationCore.Controls
{
DrawDuplicateRfidMarker(g, node);
}
// CanCross 가능 노드 표시 (교차지점으로 사용 가능)
if (node.DisableCross==false)
{
var crossPoints = new Point[5];
for (int i = 0; i < 5; i++)
{
var angle = (Math.PI * 2 * i / 5) - Math.PI / 2;
crossPoints[i] = new Point(
(int)(center.X + (radius + 4) * Math.Cos(angle)),
(int)(center.Y + (radius + 4) * Math.Sin(angle))
);
}
g.DrawPolygon(new Pen(Color.Gold, 3), crossPoints);
}
}
private void DrawTriangleNodeShape(Graphics g, MapNode node, Brush brush)
@@ -929,6 +957,21 @@ namespace AGVNavigationCore.Controls
{
DrawDuplicateRfidMarker(g, node);
}
// CanCross 가능 노드 표시 (교차지점으로 사용 가능)
if (node.DisableCross==false)
{
var crossPoints = new Point[3];
for (int i = 0; i < 3; i++)
{
var angle = (Math.PI * 2 * i / 3) - Math.PI / 2;
crossPoints[i] = new Point(
(int)(center.X + (radius + 4) * Math.Cos(angle)),
(int)(center.Y + (radius + 4) * Math.Sin(angle))
);
}
g.DrawPolygon(new Pen(Color.Gold, 3), crossPoints);
}
}
private void DrawNodeLabel(Graphics g, MapNode node)

View File

@@ -108,8 +108,10 @@ namespace AGVNavigationCore.Controls
switch (hitNode.Type)
{
case NodeType.Normal:
case NodeType.Rotation:
case NodeType.Docking:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
case NodeType.Charging:
HandleNormalNodeDoubleClick(hitNode);
break;
@@ -429,7 +431,10 @@ namespace AGVNavigationCore.Controls
{
switch (node.Type)
{
case NodeType.Docking:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
return IsPointInPentagon(point, node);
case NodeType.Charging:
return IsPointInTriangle(point, node);

View File

@@ -11,10 +11,20 @@ namespace AGVNavigationCore.Models
{
/// <summary>일반 경로 노드</summary>
Normal,
/// <summary>회전 가능 지점</summary>
Rotation,
/// <summary>도킹 스테이션</summary>
Docking,
/// <summary>로더</summary>
Loader,
/// <summary>
/// 언로더
/// </summary>
UnLoader,
/// <summary>
/// 클리너
/// </summary>
Clearner,
/// <summary>
/// 버퍼
/// </summary>
Buffer,
/// <summary>충전 스테이션</summary>
Charging,
/// <summary>라벨 (UI 요소)</summary>
@@ -58,6 +68,10 @@ namespace AGVNavigationCore.Models
/// </summary>
public enum StationType
{
/// <summary>
/// 일반노드
/// </summary>
Node,
/// <summary>로더</summary>
Loader,
/// <summary>클리너</summary>
@@ -70,6 +84,24 @@ namespace AGVNavigationCore.Models
Charger
}
/// <summary>
/// AGV턴상태
/// </summary>
public enum AGVTurn
{
None=0,
/// <summary>
/// left turn 90"
/// </summary>
L90,
/// <summary>
/// right turn 90"
/// </summary>
R90
}
/// <summary>
/// 모터 명령 열거형 (실제 AGV 제어용)
/// </summary>

View File

@@ -226,7 +226,10 @@ namespace AGVNavigationCore.Models
case NodeType.Charging:
node.DockDirection = DockingDirection.Forward;
break;
case NodeType.Docking:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
node.DockDirection = DockingDirection.Backward;
break;
default:

View File

@@ -34,6 +34,19 @@ namespace AGVNavigationCore.Models
/// </summary>
public NodeType Type { get; set; } = NodeType.Normal;
public bool CanDocking
{
get
{
if (Type == NodeType.Buffer) return true;
if (Type == NodeType.Loader) return true;
if (Type == NodeType.UnLoader) return true;
if (Type == NodeType.Clearner) return true;
if (Type == NodeType.Charging) return true;
return false;
}
}
/// <summary>
/// 도킹 방향 (도킹/충전 노드인 경우에만 Forward/Backward, 일반 노드는 DontCare)
/// </summary>
@@ -53,18 +66,38 @@ namespace AGVNavigationCore.Models
/// <summary>
/// 회전 가능 여부 (180도 회전 가능한 지점)
/// </summary>
public bool CanRotate { get; set; } = false;
public bool CanTurnLeft { get; set; } = true;
/// <summary>
/// 회전 가능 여부 (180도 회전 가능한 지점)
/// </summary>
public bool CanTurnRight { get; set; } = true;
/// <summary>
/// 교차로로 이용가능한지
/// </summary>
public bool DisableCross
{
get
{
if (Type != NodeType.Normal) return true;
return _disablecross;
}
set { _disablecross = value; }
}
private bool _disablecross = false;
/// <summary>
/// 장비 ID (도킹/충전 스테이션인 경우)
/// 예: "LOADER1", "CLEANER1", "BUFFER1", "CHARGER1"
/// </summary>
public string StationId { get; set; } = string.Empty;
public string NodeAlias { get; set; } = string.Empty;
/// <summary>
/// 장비 타입 (도킹/충전 스테이션인 경우)
/// </summary>
public StationType? StationType { get; set; } = null;
// public StationType? StationType { get; set; } = null;
/// <summary>
/// 노드 생성 일자
@@ -224,7 +257,7 @@ namespace AGVNavigationCore.Models
Type = type;
CreatedDate = DateTime.Now;
ModifiedDate = DateTime.Now;
// 타입별 기본 색상 설정
SetDefaultColorByType(type);
}
@@ -240,10 +273,10 @@ namespace AGVNavigationCore.Models
case NodeType.Normal:
DisplayColor = Color.Blue;
break;
case NodeType.Rotation:
DisplayColor = Color.Orange;
break;
case NodeType.Docking:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
case NodeType.Loader:
DisplayColor = Color.Green;
break;
case NodeType.Charging:
@@ -283,21 +316,20 @@ namespace AGVNavigationCore.Models
}
}
/// <summary>
/// 도킹 스테이션 설정
/// </summary>
/// <param name="stationId">장비 ID</param>
/// <param name="stationType">장비 타입</param>
/// <param name="dockDirection">도킹 방향</param>
public void SetDockingStation(string stationId, StationType stationType, DockingDirection dockDirection)
{
Type = NodeType.Docking;
StationId = stationId;
StationType = stationType;
DockDirection = dockDirection;
SetDefaultColorByType(NodeType.Docking);
ModifiedDate = DateTime.Now;
}
///// <summary>
///// 도킹 스테이션 설정
///// </summary>
///// <param name="stationId">장비 ID</param>
///// <param name="stationType">장비 타입</param>
///// <param name="dockDirection">도킹 방향</param>
//public void SetDockingStation(string stationId, StationType stationType, DockingDirection dockDirection)
//{
// Type = NodeType.Docking;
// NodeAlias = stationId;
// DockDirection = dockDirection;
// SetDefaultColorByType(NodeType.Docking);
// ModifiedDate = DateTime.Now;
//}
/// <summary>
/// 충전 스테이션 설정
@@ -306,8 +338,7 @@ namespace AGVNavigationCore.Models
public void SetChargingStation(string stationId)
{
Type = NodeType.Charging;
StationId = stationId;
StationType = Models.StationType.Charger;
NodeAlias = stationId;
DockDirection = DockingDirection.Forward; // 충전기는 항상 전진 도킹
SetDefaultColorByType(NodeType.Charging);
ModifiedDate = DateTime.Now;
@@ -329,17 +360,17 @@ namespace AGVNavigationCore.Models
get
{
var displayText = NodeId;
if (!string.IsNullOrEmpty(Name))
{
displayText += $" - {Name}";
}
if (!string.IsNullOrEmpty(RfidId))
{
displayText += $" - [{RfidId}]";
}
return displayText;
}
}
@@ -358,9 +389,12 @@ namespace AGVNavigationCore.Models
Type = Type,
DockDirection = DockDirection,
ConnectedNodes = new List<string>(ConnectedNodes),
CanRotate = CanRotate,
StationId = StationId,
StationType = StationType,
CanTurnLeft = CanTurnLeft,
CanTurnRight = CanTurnRight,
DisableCross = DisableCross,
NodeAlias = NodeAlias,
CreatedDate = CreatedDate,
ModifiedDate = ModifiedDate,
IsActive = IsActive,
@@ -475,7 +509,7 @@ namespace AGVNavigationCore.Models
public Size GetDisplaySize()
{
if (Type != NodeType.Image || LoadedImage == null) return Size.Empty;
return new Size(
(int)(LoadedImage.Width * Scale.Width),
(int)(LoadedImage.Height * Scale.Height)

View File

@@ -61,12 +61,12 @@ namespace AGVNavigationCore.Models
private int _currentNodeIndex;
private MapNode _currentNode;
private MapNode _prevNode;
private AGVTurn _turn;
// 이동 관련
private DateTime _lastUpdateTime;
private Point _moveStartPosition;
private Point _moveTargetPosition;
private float _moveProgress;
// 도킹 관련
private DockingDirection _dockingDirection;
@@ -89,7 +89,6 @@ namespace AGVNavigationCore.Models
public AgvDirection PrevDirection => _prevDirection;
/// <summary>
/// AGV ID
/// </summary>
@@ -158,6 +157,11 @@ namespace AGVNavigationCore.Models
/// </summary>
public MapNode PrevNode => _prevNode;
/// <summary>
/// Turn 상태값
/// </summary>
public AGVTurn Turn { get; set; }
/// <summary>
/// 도킹 방향
/// </summary>
@@ -527,10 +531,10 @@ namespace AGVNavigationCore.Models
_prevPosition = targetPosition;
_moveStartPosition = _currentPosition;
_moveTargetPosition = targetPosition;
_moveProgress = 0;
SetState(AGVState.Moving);
_isMoving = true;
Turn = AGVTurn.None;
}
/// <summary>
@@ -620,7 +624,6 @@ namespace AGVNavigationCore.Models
if (item != null)
{
//item.IsPass = true;
//이전노드는 모두 지나친걸로 한다
CurrentPath.DetailedPath.Where(t => t.seq < item.seq).ToList().ForEach(t => t.IsPass = true);
}
@@ -865,7 +868,10 @@ namespace AGVNavigationCore.Models
{
case NodeType.Charging:
return DockingDirection.Forward;
case NodeType.Docking:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
return DockingDirection.Backward;
default:
return DockingDirection.Forward;
@@ -880,8 +886,6 @@ namespace AGVNavigationCore.Models
#endregion
#region Cleanup
/// <summary>

View File

@@ -101,7 +101,7 @@ namespace AGVNavigationCore.PathFinding.Core
/// <param name="startNodeId">시작 노드 ID</param>
/// <param name="endNodeId">목적지 노드 ID</param>
/// <returns>경로 계산 결과</returns>
public AGVPathResult FindPath(string startNodeId, string endNodeId)
public AGVPathResult FindPathAStar(string startNodeId, string endNodeId)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
@@ -126,7 +126,6 @@ namespace AGVNavigationCore.PathFinding.Core
var startNode = _nodeMap[startNodeId];
var endNode = _nodeMap[endNodeId];
var openSet = new List<PathNode>();
var closedSet = new HashSet<string>();
var exploredCount = 0;
@@ -199,7 +198,7 @@ namespace AGVNavigationCore.PathFinding.Core
// 경유지가 없으면 기본 FindPath 호출
if (waypointNodeIds == null || waypointNodeIds.Length == 0)
{
return FindPath(startNodeId, endNodeId);
return FindPathAStar(startNodeId, endNodeId);
}
// 경유지 유효성 검증
@@ -220,7 +219,7 @@ namespace AGVNavigationCore.PathFinding.Core
// 경유지가 없으면 기본 경로 계산
if (validWaypoints.Count == 0)
{
return FindPath(startNodeId, endNodeId);
return FindPathAStar(startNodeId, endNodeId);
}
// 첫 번째 경유지가 시작노드와 같은지 검사
@@ -267,7 +266,7 @@ namespace AGVNavigationCore.PathFinding.Core
string waypoint = validWaypoints[i];
// 현재 위치에서 경유지까지의 경로 계산
var segmentResult = FindPath(currentStart, waypoint);
var segmentResult = FindPathAStar(currentStart, waypoint);
if (!segmentResult.Success)
{
@@ -295,7 +294,7 @@ namespace AGVNavigationCore.PathFinding.Core
}
// 2단계: 마지막 경유지에서 최종 목적지까지의 경로 계산
var finalSegmentResult = FindPath(currentStart, endNodeId);
var finalSegmentResult = FindPathAStar(currentStart, endNodeId);
if (!finalSegmentResult.Success)
{
@@ -442,7 +441,7 @@ namespace AGVNavigationCore.PathFinding.Core
AGVPathResult bestResult = null;
foreach (var targetId in targetNodeIds)
{
var result = FindPath(startNodeId, targetId);
var result = FindPathAStar(startNodeId, targetId);
if (result.Success && (bestResult == null || result.TotalDistance < bestResult.TotalDistance))
{
bestResult = result;

View File

@@ -22,8 +22,6 @@ namespace AGVNavigationCore.PathFinding.Planning
private readonly JunctionAnalyzer _junctionAnalyzer;
private readonly DirectionChangePlanner _directionChangePlanner;
public AGVPathfinder(List<MapNode> mapNodes)
{
_mapNodes = mapNodes ?? new List<MapNode>();
@@ -49,8 +47,9 @@ namespace AGVNavigationCore.PathFinding.Planning
n.IsActive &&
n.IsNavigationNode() &&
n.ConnectedNodes != null &&
n.DisableCross == false &&
n.ConnectedNodes.Count >= 3 &&
n.ConnectedMapNodes.Where(t => t.Type == NodeType.Docking || t.Type == NodeType.Charging).Any() == false &&
n.ConnectedMapNodes.Where(t => t.CanDocking).Any() == false &&
n.NodeId != startNode.NodeId
).ToList();
@@ -99,9 +98,10 @@ namespace AGVNavigationCore.PathFinding.Planning
if (pathNode != null &&
pathNode.IsActive &&
pathNode.IsNavigationNode() &&
pathNode.DisableCross == false &&
pathNode.ConnectedNodes != null &&
pathNode.ConnectedNodes.Count >= 3 &&
pathNode.ConnectedMapNodes.Where(t => t.Type == NodeType.Docking || t.Type == NodeType.Charging).Any() == false)
pathNode.ConnectedMapNodes.Where(t => t.CanDocking).Any() == false)
{
if (pathNode.NodeId.Equals(StartNode.NodeId) == false)
return pathNode;
@@ -122,12 +122,12 @@ namespace AGVNavigationCore.PathFinding.Planning
if (prevNode == null)
return AGVPathResult.CreateFailure("이전위치 노드가 null입니다.", 0, 0);
if (startNode.NodeId == targetNode.NodeId && targetNode.DockDirection.MatchAGVDirection(prevDirection))
return AGVPathResult.CreateSuccess(new List<MapNode> { startNode,startNode }, new List<AgvDirection>(), 0, 0);
return AGVPathResult.CreateSuccess(new List<MapNode> { startNode, startNode }, new List<AgvDirection>(), 0, 0);
var ReverseDirection = (currentDirection == AgvDirection.Forward ? AgvDirection.Backward : AgvDirection.Forward);
//1.목적지까지의 최단거리 경로를 찾는다.
var pathResult = _basicPathfinder.FindPath(startNode.NodeId, targetNode.NodeId);
var pathResult = _basicPathfinder.FindPathAStar(startNode.NodeId, targetNode.NodeId);
pathResult.PrevNode = prevNode;
pathResult.PrevDirection = prevDirection;
if (!pathResult.Success || pathResult.Path == null || pathResult.Path.Count == 0)
@@ -221,7 +221,7 @@ namespace AGVNavigationCore.PathFinding.Planning
//진행방향으로 이동했을때 나오는 노드를 사용한다.
if (nextNodeForward != null)
{
var Path0 = _basicPathfinder.FindPath(startNode.NodeId, nextNodeForward.NodeId);
var Path0 = _basicPathfinder.FindPathAStar(startNode.NodeId, nextNodeForward.NodeId);
Path0.PrevNode = prevNode;
Path0.PrevDirection = prevDirection;
MakeDetailData(Path0, prevDirection);
@@ -267,7 +267,7 @@ namespace AGVNavigationCore.PathFinding.Planning
//경유지를 포함하여 경로를 다시 계산한다.
//1.시작위치 - 교차로(여기까지는 현재 방향으로 그대로 이동을 한다)
var path1 = _basicPathfinder.FindPath(startNode.NodeId, JunctionInPath.NodeId);
var path1 = _basicPathfinder.FindPathAStar(startNode.NodeId, JunctionInPath.NodeId);
path1.PrevNode = prevNode;
path1.PrevDirection = prevDirection;
@@ -295,7 +295,7 @@ namespace AGVNavigationCore.PathFinding.Planning
//2.교차로 - 종료위치
var path2 = _basicPathfinder.FindPath(JunctionInPath.NodeId, targetNode.NodeId);
var path2 = _basicPathfinder.FindPathAStar(JunctionInPath.NodeId, targetNode.NodeId);
path2.PrevNode = prevNode;
path2.PrevDirection = prevDirection;
@@ -332,7 +332,7 @@ namespace AGVNavigationCore.PathFinding.Planning
//if (tempNode != null)
{
// 교차로 → 대체노드 경로 계산
var pathToTemp = _basicPathfinder.FindPath(JunctionInPath.NodeId, tempNode.NodeId);
var pathToTemp = _basicPathfinder.FindPathAStar(JunctionInPath.NodeId, tempNode.NodeId);
pathToTemp.PrevNode = JunctionInPath;
pathToTemp.PrevDirection = (ReverseCheck ? ReverseDirection : currentDirection);
if (!pathToTemp.Success)
@@ -348,7 +348,7 @@ namespace AGVNavigationCore.PathFinding.Planning
combinedResult = _basicPathfinder.CombineResults(combinedResult, pathToTemp);
// 대체노드 → 교차로 경로 계산 (역방향)
var pathFromTemp = _basicPathfinder.FindPath(tempNode.NodeId, JunctionInPath.NodeId);
var pathFromTemp = _basicPathfinder.FindPathAStar(tempNode.NodeId, JunctionInPath.NodeId);
pathFromTemp.PrevNode = JunctionInPath;
pathFromTemp.PrevDirection = (ReverseCheck ? ReverseDirection : currentDirection);
if (!pathFromTemp.Success)
@@ -362,7 +362,7 @@ namespace AGVNavigationCore.PathFinding.Planning
//현재까지 노드에서 목적지까지의 방향이 일치하면 그대로 사용한다.
bool temp3ok = false;
var TempCheck3 = _basicPathfinder.FindPath(combinedResult.Path.Last().NodeId, targetNode.NodeId);
var TempCheck3 = _basicPathfinder.FindPathAStar(combinedResult.Path.Last().NodeId, targetNode.NodeId);
if (TempCheck3.Path.First().NodeId.Equals(combinedResult.Path.Last().NodeId))
{
if (targetNode.DockDirection == DockingDirection.Forward && combinedResult.DetailedPath.Last().MotorDirection == AgvDirection.Forward)
@@ -385,7 +385,7 @@ namespace AGVNavigationCore.PathFinding.Planning
combinedResult.Path[combinedResult.Path.Count - 2].NodeId,
path2.Path[1].NodeId);
var pathToTemp2 = _basicPathfinder.FindPath(JunctionInPath.NodeId, tempNode2.NodeId);
var pathToTemp2 = _basicPathfinder.FindPathAStar(JunctionInPath.NodeId, tempNode2.NodeId);
if (ReverseCheck) MakeDetailData(pathToTemp2, currentDirection);
else MakeDetailData(pathToTemp2, ReverseDirection);
@@ -400,7 +400,7 @@ namespace AGVNavigationCore.PathFinding.Planning
combinedResult.DetailedPath[combinedResult.DetailedPath.Count - 1].MotorDirection = currentDirection;
}
var pathToTemp3 = _basicPathfinder.FindPath(tempNode2.NodeId, JunctionInPath.NodeId);
var pathToTemp3 = _basicPathfinder.FindPathAStar(tempNode2.NodeId, JunctionInPath.NodeId);
if (ReverseCheck) MakeDetailData(pathToTemp3, ReverseDirection);
else MakeDetailData(pathToTemp3, currentDirection);
@@ -438,7 +438,7 @@ namespace AGVNavigationCore.PathFinding.Planning
string nextNodeId = (i + 1 < path1.Path.Count) ? path1.Path[i + 1].NodeId : null;
// 노드 정보 생성 (현재 방향 유지)
var nodeInfo = new NodeMotorInfo(i+1,
var nodeInfo = new NodeMotorInfo(i + 1,
nodeId, RfidId,
currentDirection,
nextNodeId,

View File

@@ -73,7 +73,7 @@ namespace AGVNavigationCore.PathFinding.Planning
// 방향이 같으면 직접 경로 계산
if (currentDirection == requiredDirection)
{
var directPath = _pathfinder.FindPath(startNodeId, targetNodeId);
var directPath = _pathfinder.FindPathAStar(startNodeId, targetNodeId);
if (directPath.Success)
{
return DirectionChangePlan.CreateSuccess(
@@ -85,7 +85,7 @@ namespace AGVNavigationCore.PathFinding.Planning
}
// 방향 전환이 필요한 경우 - 먼저 간단한 직접 경로 확인
var directPath2 = _pathfinder.FindPath(startNodeId, targetNodeId);
var directPath2 = _pathfinder.FindPathAStar(startNodeId, targetNodeId);
if (directPath2.Success)
{
// 직접 경로에 갈림길이 포함된 경우 그 갈림길에서 방향 전환
@@ -160,7 +160,7 @@ namespace AGVNavigationCore.PathFinding.Planning
}
// 2. 직진 경로상의 갈림길들도 검색 (단, 되돌아가기 방지)
var directPath = _pathfinder.FindPath(startNodeId, targetNodeId);
var directPath = _pathfinder.FindPathAStar(startNodeId, targetNodeId);
if (directPath.Success)
{
foreach (var node in directPath.Path.Skip(2)) // 시작점과 다음 노드는 제외
@@ -261,7 +261,7 @@ namespace AGVNavigationCore.PathFinding.Planning
foreach (var junction in junctions)
{
var path = _pathfinder.FindPath(startNodeId, junction);
var path = _pathfinder.FindPathAStar(startNodeId, junction);
double distance = path.Success ? path.TotalDistance : double.MaxValue;
distances.Add((junction, distance));
}
@@ -313,12 +313,12 @@ namespace AGVNavigationCore.PathFinding.Planning
var fullPath = new List<MapNode>();
// 1. 시작점에서 갈림길까지의 경로
var toJunctionPath = _pathfinder.FindPath(startNodeId, junctionNodeId);
var toJunctionPath = _pathfinder.FindPathAStar(startNodeId, junctionNodeId);
if (!toJunctionPath.Success)
return fullPath;
// 2. 인근 갈림길을 통한 우회인지, 직진 경로상 갈림길인지 판단
var directPath = _pathfinder.FindPath(startNodeId, targetNodeId);
var directPath = _pathfinder.FindPathAStar(startNodeId, targetNodeId);
bool isNearbyDetour = !directPath.Success || !directPath.Path.Any(n => n.NodeId == junctionNodeId);
if (isNearbyDetour)
@@ -341,7 +341,7 @@ namespace AGVNavigationCore.PathFinding.Planning
var fullPath = new List<MapNode>();
// 1. 시작점에서 갈림길까지 직진 (현재 방향 유지)
var toJunctionPath = _pathfinder.FindPath(startNodeId, junctionNodeId);
var toJunctionPath = _pathfinder.FindPathAStar(startNodeId, junctionNodeId);
if (!toJunctionPath.Success)
return fullPath;
@@ -349,7 +349,7 @@ namespace AGVNavigationCore.PathFinding.Planning
// 2. 갈림길에서 방향 전환 후 목적지로
// 이때 마그넷 센서를 이용해 목적지 방향으로 진입
var fromJunctionPath = _pathfinder.FindPath(junctionNodeId, targetNodeId);
var fromJunctionPath = _pathfinder.FindPathAStar(junctionNodeId, targetNodeId);
if (fromJunctionPath.Success && fromJunctionPath.Path.Count > 1)
{
fullPath.AddRange(fromJunctionPath.Path.Skip(1)); // 중복 노드 제거
@@ -366,7 +366,7 @@ namespace AGVNavigationCore.PathFinding.Planning
var fullPath = new List<MapNode>();
// 1. 시작점에서 갈림길까지의 경로
var toJunctionPath = _pathfinder.FindPath(startNodeId, junctionNodeId);
var toJunctionPath = _pathfinder.FindPathAStar(startNodeId, junctionNodeId);
if (!toJunctionPath.Success)
return fullPath;
@@ -387,7 +387,7 @@ namespace AGVNavigationCore.PathFinding.Planning
// 3. 갈림길에서 목표점까지의 경로
string lastNode = fullPath.LastOrDefault()?.NodeId ?? junctionNodeId;
var fromJunctionPath = _pathfinder.FindPath(lastNode, targetNodeId);
var fromJunctionPath = _pathfinder.FindPathAStar(lastNode, targetNodeId);
if (fromJunctionPath.Success && fromJunctionPath.Path.Count > 1)
{
fullPath.AddRange(fromJunctionPath.Path.Skip(1));
@@ -609,7 +609,7 @@ namespace AGVNavigationCore.PathFinding.Planning
private bool CanReachTargetViaJunction(string junctionNodeId, string targetNodeId)
{
// 갈림길에서 목적지까지의 경로가 존재하는지 확인
var pathToTarget = _pathfinder.FindPath(junctionNodeId, targetNodeId);
var pathToTarget = _pathfinder.FindPathAStar(junctionNodeId, targetNodeId);
return pathToTarget.Success;
}

View File

@@ -153,7 +153,7 @@ namespace AGVNavigationCore.Utils
Console.WriteLine($"이름: {node.Name}");
Console.WriteLine($"위치: {node.Position}");
Console.WriteLine($"타입: {GetNodeTypeName(node.Type)}");
Console.WriteLine($"회전 가능: {node.CanRotate}");
Console.WriteLine($"TurnLeft/Right/교차로 : {(node.CanTurnLeft ? "O":"X")}/{(node.CanTurnRight ? "O" : "X")}/{(node.DisableCross ? "X" : "O")}");
Console.WriteLine($"활성: {node.IsActive}");
Console.WriteLine($"연결된 노드:");