..
This commit is contained in:
@@ -151,11 +151,6 @@ namespace AGVNavigationCore.Models
|
||||
|
||||
#region Path Execution Methods
|
||||
|
||||
/// <summary>
|
||||
/// 경로 실행
|
||||
/// </summary>
|
||||
void ExecutePath(AGVPathResult path, List<MapNode> mapNodes);
|
||||
|
||||
/// <summary>
|
||||
/// 경로 정지
|
||||
/// </summary>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,17 +166,32 @@ 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여야 함
|
||||
{
|
||||
MakeDetailData(pathResult, ReverseDirection);
|
||||
MakeMagnetDirection(pathResult);
|
||||
return pathResult;
|
||||
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여야 함
|
||||
{
|
||||
MakeDetailData(pathResult, currentDirection);
|
||||
MakeMagnetDirection(pathResult);
|
||||
return pathResult;
|
||||
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])
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user