This commit is contained in:
backuppc
2025-10-28 17:33:49 +09:00
parent e8b9fceb88
commit 24a14fbd48
5 changed files with 92 additions and 119 deletions

View File

@@ -151,11 +151,6 @@ namespace AGVNavigationCore.Models
#region Path Execution Methods
/// <summary>
/// 경로 실행
/// </summary>
void ExecutePath(AGVPathResult path, List<MapNode> mapNodes);
/// <summary>
/// 경로 정지
/// </summary>

View File

@@ -258,61 +258,7 @@ namespace AGVNavigationCore.Models
#region Public Methods -
/// <summary>
/// 경로 실행 시작
/// </summary>
/// <param name="path">실행할 경로</param>
/// <param name="mapNodes">맵 노드 목록</param>
public void ExecutePath(AGVPathResult path, List<MapNode> mapNodes)
{
if (path == null || !path.Success)
{
OnError("유효하지 않은 경로입니다.");
return;
}
_currentPath = path;
_remainingNodes = new List<string>(path.Path.Select(n => n.NodeId).ToList());
_currentNodeIndex = 0;
// 시작 노드와 목표 노드 설정
if (_remainingNodes.Count > 0)
{
var startNode = mapNodes.FirstOrDefault(n => n.NodeId == _remainingNodes[0]);
if (startNode != null)
{
_currentNode = startNode;
// 목표 노드 설정 (경로의 마지막 노드)
if (_remainingNodes.Count > 1)
{
var targetNodeId = _remainingNodes[_remainingNodes.Count - 1];
var targetNode = mapNodes.FirstOrDefault(n => n.NodeId == targetNodeId);
// 목표 노드의 타입에 따라 도킹 방향 결정
if (targetNode != null)
{
_dockingDirection = GetDockingDirection(targetNode.Type);
_prevNode = targetNode;
}
}
StartMovement();
}
else
{
OnError($"시작 노드를 찾을 수 없습니다: {_remainingNodes[0]}");
}
}
}
/// <summary>
/// 간단한 경로 실행 (경로 객체 없이 노드만)
/// </summary>
public void StartPath(AGVPathResult path, List<MapNode> mapNodes)
{
ExecutePath(path, mapNodes);
}
/// <summary>
/// 경로 정지
@@ -428,14 +374,15 @@ namespace AGVNavigationCore.Models
{
_prevPosition = _currentPosition; // 이전 위치
_prevNode = _currentNode;
}
//모터방향이 다르다면 적용한다
if (_currentDirection != motorDirection)
{
_prevDirection = _currentDirection;
}
////모터방향이 다르다면 적용한다
//if (_currentDirection != motorDirection)
//{
// _prevDirection = motorDirection;
//}
// 새로운 위치 설정
_currentPosition = node.Position;
_currentDirection = motorDirection;

View File

@@ -128,8 +128,14 @@ namespace AGVNavigationCore.PathFinding.Planning
//정방향/역방향 이동 시 다음 노드 확인
// 경로 계획 단계에서는 마그넷 방향이 미리 알려지지 않으므로 Straight로 기본값 사용
var nextNodeForward = DirectionalHelper.GetNextNodeByDirection(startNode, prevNode, prevDirection, currentDirection, MagnetDirection.Straight, _mapNodes);
var nextNodeBackward = DirectionalHelper.GetNextNodeByDirection(startNode, prevNode, prevDirection, ReverseDirection, MagnetDirection.Straight, _mapNodes);
// ✅ 현재 방향 유지: prevDirection = currentDirection (방향 일관성)
var nextNodeForward = DirectionalHelper.GetNextNodeByDirection(
startNode, prevNode, currentDirection, currentDirection, MagnetDirection.Straight, _mapNodes);
// ✅ 방향 전환: prevDirection = currentDirection, direction = ReverseDirection
var nextNodeBackward = DirectionalHelper.GetNextNodeByDirection(
startNode, prevNode, currentDirection, ReverseDirection, MagnetDirection.Straight, _mapNodes);
//2.AGV방향과 목적지에 설정된 방향이 일치하면 그대로 진행하면된다.(목적지에 방향이 없는 경우에도 그대로 진행)
@@ -137,7 +143,7 @@ namespace AGVNavigationCore.PathFinding.Planning
(targetNode.DockDirection == DockingDirection.Forward && currentDirection == AgvDirection.Forward) ||
(targetNode.DockDirection == DockingDirection.Backward && currentDirection == AgvDirection.Backward))
{
if (nextNodeForward.NodeId == pathResult.Path[1].NodeId) //예측경로와 다음진행방향 경로가 일치하면 해당 방향이 맞다
if ((nextNodeForward?.NodeId ?? string.Empty) == pathResult.Path[1].NodeId) //예측경로와 다음진행방향 경로가 일치하면 해당 방향이 맞다
{
MakeDetailData(pathResult, currentDirection);
MakeMagnetDirection(pathResult);
@@ -160,19 +166,34 @@ namespace AGVNavigationCore.PathFinding.Planning
//뒤로 이동시 경로상의 처음 만나는 노드가 같다면 그 방향으로 이동하면 된다.
if (nextNodeBackward != null && pathResult.Path.Count > 1 && nextNodeBackward.NodeId == pathResult.Path[1].NodeId && targetNode.DockDirection == DockingDirection.Backward)
// ⚠️ 단, 현재 방향과 목적지 도킹 방향이 일치해야 함!
if (nextNodeBackward != null && pathResult.Path.Count > 1 &&
nextNodeBackward.NodeId == pathResult.Path[1].NodeId) // ✅ 추가: 현재도 Backward여야 함
{
if (targetNode.DockDirection == DockingDirection.Forward && ReverseDirection == AgvDirection.Forward ||
targetNode.DockDirection == DockingDirection.Backward && ReverseDirection == AgvDirection.Backward)
{
MakeDetailData(pathResult, ReverseDirection);
MakeMagnetDirection(pathResult);
return pathResult;
}
if (nextNodeForward != null && pathResult.Path.Count > 1 && nextNodeForward.NodeId == pathResult.Path[1].NodeId && targetNode.DockDirection == DockingDirection.Forward)
}
if (nextNodeForward != null && pathResult.Path.Count > 1 &&
nextNodeForward.NodeId == pathResult.Path[1].NodeId &&
targetNode.DockDirection == DockingDirection.Forward &&
currentDirection == AgvDirection.Forward) // ✅ 추가: 현재도 Forward여야 함
{
if (targetNode.DockDirection == DockingDirection.Forward && currentDirection == AgvDirection.Forward ||
targetNode.DockDirection == DockingDirection.Backward && currentDirection == AgvDirection.Backward)
{
MakeDetailData(pathResult, currentDirection);
MakeMagnetDirection(pathResult);
return pathResult;
}
}
//if(nextNodeForward.NodeId == pathResult.Path[1])
//{
// MakeDetailData(pathResult, currentDirection);
@@ -201,15 +222,16 @@ namespace AGVNavigationCore.PathFinding.Planning
path1.PrevNode = prevNode;
path1.PrevDirection = prevDirection;
//다음좌표를 보고 방향인지 방향인지 체크한다.
//다음좌표를 보고 교차로가 진행방향인지 반대방향인지 체크한다.(!모터의 정/역방향을 말하는것이 아님)
bool ReverseCheck = false;
if (path1.Path.Count > 1 && nextNodeForward != null && nextNodeForward.NodeId.Equals(path1.Path[1].NodeId))
{
ReverseCheck = false; //현재 진행 방향으로 이동해야한다
MakeDetailData(path1, currentDirection); // path1의 상세 경로 정보 채우기 (모터 방향 설정)
}
else if (path1.Path.Count > 1 && nextNodeBackward != null && nextNodeBackward.NodeId.Equals(path1.Path[1].NodeId))
{
ReverseCheck = true;
ReverseCheck = true; //현재 방향의 반대방향으로 이동해야한다
MakeDetailData(path1, ReverseDirection); // path1의 상세 경로 정보 채우기 (모터 방향 설정)
}
else return AGVPathResult.CreateFailure("교차로까지 계산된 경로에 현재 위치정보로 추측을 할 수 없습니다", 0, 0);
@@ -224,8 +246,17 @@ namespace AGVNavigationCore.PathFinding.Planning
path2.PrevNode = prevNode;
path2.PrevDirection = prevDirection;
if (ReverseCheck) MakeDetailData(path2, currentDirection);
else MakeDetailData(path2, ReverseDirection);
//2번paths느최종 목적지 이므로 목적지와 도킹방향 확인해서 결정한다
if ((path2.Path.Last().DockDirection == DockingDirection.Forward && ReverseDirection == AgvDirection.Forward) ||
(path2.Path.Last().DockDirection == DockingDirection.Backward && ReverseDirection == AgvDirection.Backward))
{
MakeDetailData(path2, ReverseDirection);
}
else if ((path2.Path.Last().DockDirection == DockingDirection.Forward && currentDirection == AgvDirection.Forward) ||
(path2.Path.Last().DockDirection == DockingDirection.Backward && currentDirection == AgvDirection.Backward))
{
MakeDetailData(path2, currentDirection);
}
//3.방향전환을 위환 대체 노드찾기
var tempNode = _basicPathfinder.FindAlternateNodeForDirectionChange(JunctionInPath.NodeId,
@@ -236,25 +267,30 @@ namespace AGVNavigationCore.PathFinding.Planning
if (tempNode == null)
return AGVPathResult.CreateFailure("방향 전환을 위한 대체 노드를 찾을 수 없습니다.", 0, 0);
// path1 (시작 → 교차로)
var combinedResult = path1;
// 교차로 → 대체노드 경로 계산
var pathToTemp = _basicPathfinder.FindPath(JunctionInPath.NodeId, tempNode.NodeId);
pathToTemp.PrevNode = JunctionInPath;
pathToTemp.PrevDirection = (ReverseCheck ? ReverseDirection : currentDirection);
if (!pathToTemp.Success)
return AGVPathResult.CreateFailure("교차로에서 대체 노드까지의 경로를 찾을 수 없습니다.", 0, 0);
if (ReverseCheck) MakeDetailData(pathToTemp, ReverseDirection);
else MakeDetailData(pathToTemp, currentDirection);
//교차로찍고 원래방향으로 돌어가야한다.
if (pathToTemp.DetailedPath.Count > 1)
pathToTemp.DetailedPath[pathToTemp.DetailedPath.Count - 1].MotorDirection = ReverseDirection;
pathToTemp.DetailedPath[pathToTemp.DetailedPath.Count - 1].MotorDirection = currentDirection;
// path1 + pathToTemp 합치기
combinedResult = _basicPathfinder.CombineResults(combinedResult, pathToTemp);
// 대체노드 → 교차로 경로 계산 (역방향)
var pathFromTemp = _basicPathfinder.FindPath(tempNode.NodeId, JunctionInPath.NodeId);
pathFromTemp.PrevNode = JunctionInPath;
pathFromTemp.PrevDirection = (ReverseCheck ? ReverseDirection : currentDirection);
if (!pathFromTemp.Success)
return AGVPathResult.CreateFailure("대체 노드에서 교차로까지의 경로를 찾을 수 없습니다.", 0, 0);
@@ -264,6 +300,37 @@ namespace AGVNavigationCore.PathFinding.Planning
// (path1 + pathToTemp) + pathFromTemp 합치기
combinedResult = _basicPathfinder.CombineResults(combinedResult, pathFromTemp);
//대체노드에서 최종 목적지를 다시 확인한다.
if ((currentDirection == AgvDirection.Forward && targetNode.DockDirection != DockingDirection.Forward) ||
(currentDirection == AgvDirection.Backward && targetNode.DockDirection != DockingDirection.Backward))
{
//목적지와 방향이 맞지 않다. 그러므로 대체노드를 추가로 더 찾아야한다.
var tempNode2 = _basicPathfinder.FindAlternateNodeForDirectionChange(JunctionInPath.NodeId,
combinedResult.Path[combinedResult.Path.Count - 2].NodeId,
path2.Path[1].NodeId);
var pathToTemp2 = _basicPathfinder.FindPath(JunctionInPath.NodeId, tempNode2.NodeId);
if (ReverseCheck) MakeDetailData(pathToTemp2, currentDirection);
else MakeDetailData(pathToTemp2, ReverseDirection);
combinedResult = _basicPathfinder.CombineResults(combinedResult, pathToTemp2);
//교차로찍고 원래방향으로 돌어가야한다.
if (combinedResult.DetailedPath.Count > 1)
{
if (ReverseCheck)
combinedResult.DetailedPath[combinedResult.DetailedPath.Count - 1].MotorDirection = ReverseDirection;
else
combinedResult.DetailedPath[combinedResult.DetailedPath.Count - 1].MotorDirection = currentDirection;
}
var pathToTemp3 = _basicPathfinder.FindPath(tempNode2.NodeId, JunctionInPath.NodeId);
if (ReverseCheck) MakeDetailData(pathToTemp3, ReverseDirection);
else MakeDetailData(pathToTemp3, currentDirection);
combinedResult = _basicPathfinder.CombineResults(combinedResult, pathToTemp3);
}
// (path1 + pathToTemp + pathFromTemp) + path2 합치기
combinedResult = _basicPathfinder.CombineResults(combinedResult, path2);

View File

@@ -84,7 +84,6 @@ namespace AGVSimulator.Forms
this._simulationStatusLabel = new System.Windows.Forms.Label();
this._pathGroup = new System.Windows.Forms.GroupBox();
this._clearPathButton = new System.Windows.Forms.Button();
this._startPathButton = new System.Windows.Forms.Button();
this._calculatePathButton = new System.Windows.Forms.Button();
this._targetCalcButton = new System.Windows.Forms.Button();
this._avoidRotationCheckBox = new System.Windows.Forms.CheckBox();
@@ -450,7 +449,6 @@ namespace AGVSimulator.Forms
// _pathGroup
//
this._pathGroup.Controls.Add(this._clearPathButton);
this._pathGroup.Controls.Add(this._startPathButton);
this._pathGroup.Controls.Add(this._calculatePathButton);
this._pathGroup.Controls.Add(this._targetCalcButton);
this._pathGroup.Controls.Add(this._avoidRotationCheckBox);
@@ -476,16 +474,6 @@ namespace AGVSimulator.Forms
this._clearPathButton.UseVisualStyleBackColor = true;
this._clearPathButton.Click += new System.EventHandler(this.OnClearPath_Click);
//
// _startPathButton
//
this._startPathButton.Location = new System.Drawing.Point(80, 177);
this._startPathButton.Name = "_startPathButton";
this._startPathButton.Size = new System.Drawing.Size(65, 25);
this._startPathButton.TabIndex = 5;
this._startPathButton.Text = "경로 시작";
this._startPathButton.UseVisualStyleBackColor = true;
this._startPathButton.Click += new System.EventHandler(this.OnStartPath_Click);
//
// _calculatePathButton
//
this._calculatePathButton.Location = new System.Drawing.Point(10, 177);
@@ -816,7 +804,6 @@ namespace AGVSimulator.Forms
private System.Windows.Forms.Label targetNodeLabel;
private System.Windows.Forms.ComboBox _targetNodeCombo;
private System.Windows.Forms.Button _calculatePathButton;
private System.Windows.Forms.Button _startPathButton;
private System.Windows.Forms.Button _clearPathButton;
private System.Windows.Forms.Button _targetCalcButton;
private System.Windows.Forms.CheckBox _avoidRotationCheckBox;

View File

@@ -342,25 +342,6 @@ namespace AGVSimulator.Forms
}
}
private void OnStartPath_Click(object sender, EventArgs e)
{
var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
if (selectedAGV == null)
{
MessageBox.Show("AGV를 선택해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
if (_simulatorCanvas.CurrentPath == null || !_simulatorCanvas.CurrentPath.Success)
{
MessageBox.Show("먼저 경로를 계산해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
selectedAGV.StartPath(_simulatorCanvas.CurrentPath, _mapNodes);
_statusLabel.Text = $"{selectedAGV.AgvId} 경로 시작";
}
private void OnClearPath_Click(object sender, EventArgs e)
{
_simulatorCanvas.CurrentPath = null;
@@ -804,10 +785,6 @@ namespace AGVSimulator.Forms
_stopSimulationButton.Enabled = _simulationState.IsRunning;
_removeAgvButton.Enabled = _agvListCombo.SelectedItem != null;
_startPathButton.Enabled = _agvListCombo.SelectedItem != null &&
_simulatorCanvas.CurrentPath != null &&
_simulatorCanvas.CurrentPath.Success;
_calculatePathButton.Enabled = _startNodeCombo.SelectedItem != null &&
_targetNodeCombo.SelectedItem != null;