fix: Add motor direction parameter to magnet direction calculation in pathfinding

- Fixed critical issue in ConvertToDetailedPath where motor direction was not passed to GetRequiredMagnetDirection
- Motor direction is essential for backward movement as Left/Right directions must be inverted
- Modified AGVPathfinder.cs line 280 to pass currentDirection parameter
- Ensures backward motor direction properly inverts magnet sensor directions

feat: Add waypoint support to pathfinding system

- Added FindPath overload with params string[] waypointNodeIds in AStarPathfinder
- Supports sequential traversal through multiple intermediate nodes
- Validates waypoints and prevents duplicates in sequence
- Returns combined path result with aggregated metrics

feat: Implement path result merging with DetailedPath preservation

- Added CombineResults method in AStarPathfinder for intelligent path merging
- Automatically deduplicates nodes when last of previous path equals first of current
- Preserves DetailedPath information including motor and magnet directions
- Essential for multi-segment path operations

feat: Integrate magnet direction with motor direction awareness

- Modified JunctionAnalyzer.GetRequiredMagnetDirection to accept AgvDirection parameter
- Inverts Left/Right magnet directions when moving Backward
- Properly handles motor direction context throughout pathfinding

feat: Add automatic start node selection in simulator

- Added SetStartNodeToCombo method to SimulatorForm
- Automatically selects start node combo box when AGV position is set via RFID
- Improves UI usability and workflow efficiency

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
backuppc
2025-10-24 15:46:16 +09:00
parent 3ddecf63ed
commit d932b8d332
47 changed files with 7473 additions and 1088 deletions

View File

@@ -93,6 +93,7 @@ namespace AGVSimulator.Forms
this._startNodeCombo = new System.Windows.Forms.ComboBox();
this.startNodeLabel = new System.Windows.Forms.Label();
this._agvControlGroup = new System.Windows.Forms.GroupBox();
this.btNextNode = new System.Windows.Forms.Button();
this._setPositionButton = new System.Windows.Forms.Button();
this._rfidTextBox = new System.Windows.Forms.TextBox();
this._rfidLabel = new System.Windows.Forms.Label();
@@ -108,7 +109,7 @@ namespace AGVSimulator.Forms
this._agvInfoTitleLabel = new System.Windows.Forms.Label();
this._liftDirectionLabel = new System.Windows.Forms.Label();
this._motorDirectionLabel = new System.Windows.Forms.Label();
this._pathDebugLabel = new System.Windows.Forms.Label();
this._pathDebugLabel = new System.Windows.Forms.TextBox();
this._menuStrip.SuspendLayout();
this._toolStrip.SuspendLayout();
this._statusStrip.SuspendLayout();
@@ -495,7 +496,6 @@ namespace AGVSimulator.Forms
this._calculatePathButton.UseVisualStyleBackColor = true;
this._calculatePathButton.Click += new System.EventHandler(this.OnCalculatePath_Click);
//
//
// _targetCalcButton
//
this._targetCalcButton.Location = new System.Drawing.Point(10, 148);
@@ -552,6 +552,7 @@ namespace AGVSimulator.Forms
//
// _agvControlGroup
//
this._agvControlGroup.Controls.Add(this.btNextNode);
this._agvControlGroup.Controls.Add(this._setPositionButton);
this._agvControlGroup.Controls.Add(this._rfidTextBox);
this._agvControlGroup.Controls.Add(this._rfidLabel);
@@ -570,11 +571,21 @@ namespace AGVSimulator.Forms
this._agvControlGroup.TabStop = false;
this._agvControlGroup.Text = "AGV 제어";
//
// btNextNode
//
this.btNextNode.Location = new System.Drawing.Point(160, 183);
this.btNextNode.Name = "btNextNode";
this.btNextNode.Size = new System.Drawing.Size(60, 21);
this.btNextNode.TabIndex = 10;
this.btNextNode.Text = "다음";
this.btNextNode.UseVisualStyleBackColor = true;
this.btNextNode.Click += new System.EventHandler(this.btNextNode_Click);
//
// _setPositionButton
//
this._setPositionButton.Location = new System.Drawing.Point(160, 138);
this._setPositionButton.Name = "_setPositionButton";
this._setPositionButton.Size = new System.Drawing.Size(60, 67);
this._setPositionButton.Size = new System.Drawing.Size(60, 39);
this._setPositionButton.TabIndex = 7;
this._setPositionButton.Text = "위치설정";
this._setPositionButton.UseVisualStyleBackColor = true;
@@ -667,30 +678,30 @@ namespace AGVSimulator.Forms
// _canvasPanel
//
this._canvasPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this._canvasPanel.Location = new System.Drawing.Point(0, 109);
this._canvasPanel.Location = new System.Drawing.Point(0, 129);
this._canvasPanel.Name = "_canvasPanel";
this._canvasPanel.Size = new System.Drawing.Size(967, 669);
this._canvasPanel.Size = new System.Drawing.Size(967, 649);
this._canvasPanel.TabIndex = 4;
//
// _agvInfoPanel
//
this._agvInfoPanel.BackColor = System.Drawing.Color.LightBlue;
this._agvInfoPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._agvInfoPanel.Controls.Add(this._pathDebugLabel);
this._agvInfoPanel.Controls.Add(this._agvInfoTitleLabel);
this._agvInfoPanel.Controls.Add(this._liftDirectionLabel);
this._agvInfoPanel.Controls.Add(this._motorDirectionLabel);
this._agvInfoPanel.Controls.Add(this._pathDebugLabel);
this._agvInfoPanel.Dock = System.Windows.Forms.DockStyle.Top;
this._agvInfoPanel.Location = new System.Drawing.Point(0, 49);
this._agvInfoPanel.Name = "_agvInfoPanel";
this._agvInfoPanel.Size = new System.Drawing.Size(967, 60);
this._agvInfoPanel.Size = new System.Drawing.Size(967, 80);
this._agvInfoPanel.TabIndex = 5;
//
// _agvInfoTitleLabel
//
this._agvInfoTitleLabel.AutoSize = true;
this._agvInfoTitleLabel.Font = new System.Drawing.Font("맑은 고딕", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this._agvInfoTitleLabel.Location = new System.Drawing.Point(10, 12);
this._agvInfoTitleLabel.Location = new System.Drawing.Point(10, 8);
this._agvInfoTitleLabel.Name = "_agvInfoTitleLabel";
this._agvInfoTitleLabel.Size = new System.Drawing.Size(91, 15);
this._agvInfoTitleLabel.TabIndex = 0;
@@ -700,7 +711,7 @@ namespace AGVSimulator.Forms
//
this._liftDirectionLabel.AutoSize = true;
this._liftDirectionLabel.Font = new System.Drawing.Font("맑은 고딕", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this._liftDirectionLabel.Location = new System.Drawing.Point(120, 12);
this._liftDirectionLabel.Location = new System.Drawing.Point(120, 8);
this._liftDirectionLabel.Name = "_liftDirectionLabel";
this._liftDirectionLabel.Size = new System.Drawing.Size(83, 15);
this._liftDirectionLabel.TabIndex = 1;
@@ -710,7 +721,7 @@ namespace AGVSimulator.Forms
//
this._motorDirectionLabel.AutoSize = true;
this._motorDirectionLabel.Font = new System.Drawing.Font("맑은 고딕", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this._motorDirectionLabel.Location = new System.Drawing.Point(250, 12);
this._motorDirectionLabel.Location = new System.Drawing.Point(250, 8);
this._motorDirectionLabel.Name = "_motorDirectionLabel";
this._motorDirectionLabel.Size = new System.Drawing.Size(71, 15);
this._motorDirectionLabel.TabIndex = 2;
@@ -718,13 +729,14 @@ namespace AGVSimulator.Forms
//
// _pathDebugLabel
//
this._pathDebugLabel.AutoSize = true;
this._pathDebugLabel.Font = new System.Drawing.Font("맑은 고딕", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this._pathDebugLabel.ForeColor = System.Drawing.Color.DarkBlue;
this._pathDebugLabel.BackColor = System.Drawing.Color.LightBlue;
this._pathDebugLabel.BorderStyle = System.Windows.Forms.BorderStyle.None;
this._pathDebugLabel.Font = new System.Drawing.Font("굴림", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this._pathDebugLabel.Location = new System.Drawing.Point(10, 30);
this._pathDebugLabel.Multiline = true;
this._pathDebugLabel.Name = "_pathDebugLabel";
this._pathDebugLabel.Size = new System.Drawing.Size(114, 15);
this._pathDebugLabel.TabIndex = 3;
this._pathDebugLabel.Size = new System.Drawing.Size(947, 43);
this._pathDebugLabel.TabIndex = 4;
this._pathDebugLabel.Text = "경로: 설정되지 않음";
//
// SimulatorForm
@@ -828,6 +840,7 @@ namespace AGVSimulator.Forms
private System.Windows.Forms.Label _liftDirectionLabel;
private System.Windows.Forms.Label _motorDirectionLabel;
private System.Windows.Forms.Label _agvInfoTitleLabel;
private System.Windows.Forms.Label _pathDebugLabel;
private System.Windows.Forms.Button btNextNode;
private System.Windows.Forms.TextBox _pathDebugLabel;
}
}

View File

@@ -303,28 +303,17 @@ namespace AGVSimulator.Forms
var currentDirection = selectedAGV?.CurrentDirection ?? AgvDirection.Forward;
// AGV의 이전 위치에서 가장 가까운 노드 찾기
MapNode prevNode = startNode; // 기본값으로 시작 노드 사용
if (selectedAGV != null && _mapNodes != null && _mapNodes.Count > 0)
{
// AGV 현재 위치에서 가장 가까운 노드 찾기
var agvPos = selectedAGV.CurrentPosition;
prevNode = _mapNodes.OrderBy(n =>
Math.Sqrt(Math.Pow(n.Position.X - agvPos.X, 2) +
Math.Pow(n.Position.Y - agvPos.Y, 2))).FirstOrDefault();
if (prevNode == null)
prevNode = startNode;
}
var prevNode = selectedAGV?.PrevNode;
// 고급 경로 계획 사용 (노드 객체 직접 전달)
var advancedResult = _advancedPathfinder.FindPath(startNode, targetNode, prevNode, currentDirection);
var advancedResult = _advancedPathfinder.FindPath_test(startNode, targetNode, prevNode, currentDirection);
if (advancedResult.Success)
{
// 도킹 검증이 없는 경우 추가 검증 수행
if (advancedResult.DockingValidation == null || !advancedResult.DockingValidation.IsValidationRequired)
{
advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _mapNodes, currentDirection);
advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _mapNodes);
}
_simulatorCanvas.CurrentPath = advancedResult;
@@ -582,45 +571,77 @@ namespace AGVSimulator.Forms
var selectedDirectionItem = _directionCombo.SelectedItem as DirectionItem;
var selectedDirection = selectedDirectionItem?.Direction ?? AgvDirection.Forward;
// 콘솔 출력 (상세한 리프트 방향 계산 과정)
//이전위치와 동일한지 체크한다.
if(selectedAGV.CurrentNodeId == targetNode.NodeId && selectedAGV.CurrentDirection == selectedDirection)
{
Program.WriteLine($"이전 노드위치와 모터의 방향이 동일하여 현재 위치 변경이 취소됩니다(NODE:{targetNode.NodeId},RFID:{targetNode.RfidId},DIR:{selectedDirection})");
return;
}
// 콘솔 출력 (상세한 리프트 방향 계산 과정)
Program.WriteLine($"[AGV-{selectedAGV.AgvId}] 위치 설정:");
Program.WriteLine($" RFID: {rfidId} → 노드: {targetNode.NodeId}");
Program.WriteLine($" 새로운 위치: ({targetNode.Position.X}, {targetNode.Position.Y})");
Program.WriteLine($" 모터 방향: {selectedDirectionItem?.DisplayText ?? ""} ({selectedDirection})");
Program.WriteLine($" 위치: ({targetNode.Position.X}, {targetNode.Position.Y})");
Program.WriteLine($" 방향: {selectedDirectionItem?.DisplayText ?? ""} ({selectedDirection})");
// SetPosition 호출 전 상태
var oldTargetPos = selectedAGV.TargetPosition;
var oldCurrentPos = selectedAGV.CurrentPosition;
Program.WriteLine($" [BEFORE] 현재 CurrentPosition: ({oldCurrentPos.X}, {oldCurrentPos.Y})");
Program.WriteLine($" [BEFORE] 이전 TargetPosition: {(oldTargetPos.HasValue ? $"({oldTargetPos.Value.X}, {oldTargetPos.Value.Y})" : "None")}");
var PrevNodeID = selectedAGV.CurrentNodeId;
var PrevDir = selectedAGV.CurrentDirection;
var PrevPosition = selectedAGV.CurrentPosition;
Program.WriteLine($" [BEFORE] Node:{PrevNodeID}, Dir:{PrevDir},Pos X:{PrevPosition.X},{PrevPosition.Y}");
// AGV 위치 및 방향 설정
_simulatorCanvas.SetAGVPosition(selectedAGV.AgvId, targetNode.Position);
_simulatorCanvas.SetAGVPosition(selectedAGV.AgvId, targetNode, selectedDirection);
_simulatorCanvas.UpdateAGVDirection(selectedAGV.AgvId, selectedDirection);
// VirtualAGV 객체의 위치와 방향 업데이트
selectedAGV.SetPosition(targetNode, targetNode.Position, selectedDirection); // 이전 위치 기억하도록
selectedAGV.SetPosition(targetNode, selectedDirection); // 이전 위치 기억하도록
// SetPosition 호출 후 상태 확인 및 리프트 계산
var newTargetPos = selectedAGV.TargetPosition;
var newPrevPos = selectedAGV.PrevPosition;
var newCurrentPos = selectedAGV.CurrentPosition;
Program.WriteLine($" [AFTER] 새로운 CurrentPosition: ({newCurrentPos.X}, {newCurrentPos.Y})");
Program.WriteLine($" [AFTER] 새로운 TargetPosition: {(newTargetPos.HasValue ? $"({newTargetPos.Value.X}, {newTargetPos.Value.Y})" : "None")}");
Program.WriteLine($" [AFTER] 새로운 PrevPosition: {(newPrevPos.HasValue ? $"({newPrevPos.Value.X}, {newPrevPos.Value.Y})" : "None")}");
// 리프트 방향 계산 과정 상세 출력
Program.WriteLine($" [LIFT] 리프트 방향 계산:");
CalculateLiftDirectionDetailed(selectedAGV);
Program.WriteLine("");
_statusLabel.Text = $"{selectedAGV.AgvId} 위치를 RFID '{rfidId}' (노드: {targetNode.NodeId}), 방향: {selectedDirectionItem?.DisplayText ?? ""}로 설정했습니다.";
_rfidTextBox.Text = ""; // 입력 필드 초기화
// 시뮬레이터 캔버스의 해당 노드로 이동
_simulatorCanvas.PanToNode(targetNode.NodeId);
// 시작 노드 콤보박스를 현재 위치로 자동 선택
SetStartNodeToCombo(targetNode.NodeId);
}
/// <summary>
/// 시작 노드 콤보박스에 노드를 설정
/// </summary>
private void SetStartNodeToCombo(string nodeId)
{
try
{
for (int i = 0; i < _startNodeCombo.Items.Count; i++)
{
var item = _startNodeCombo.Items[i].ToString();
if (item.Contains($"[{nodeId}]"))
{
_startNodeCombo.SelectedIndex = i;
Program.WriteLine($"[SYSTEM] 시작 노드를 '{nodeId}'로 자동 선택했습니다.");
break;
}
}
}
catch (Exception ex)
{
Program.WriteLine($"[ERROR] 시작 노드 자동 선택 실패: {ex.Message}");
}
}
private string GetAvailableRfidList()
@@ -833,22 +854,22 @@ namespace AGVSimulator.Forms
private void CalculateLiftDirectionDetailed(VirtualAGV agv)
{
var currentPos = agv.CurrentPosition;
var targetPos = agv.TargetPosition;
var prevPos = agv.PrevPosition;
var dockingDirection = agv.DockingDirection;
Program.WriteLine($" 입력값: CurrentPos=({currentPos.X}, {currentPos.Y})");
Program.WriteLine($" 입력값: TargetPos={(!targetPos.HasValue ? "None" : $"({targetPos.Value.X}, {targetPos.Value.Y})")}");
Program.WriteLine($" 입력값: prevPos={(!prevPos.HasValue ? "None" : $"({prevPos.Value.X}, {prevPos.Value.Y})")}");
Program.WriteLine($" 입력값: DockingDirection={dockingDirection}");
if (!targetPos.HasValue || targetPos.Value == currentPos)
if (!prevPos.HasValue || prevPos.Value == currentPos)
{
Program.WriteLine($" 결과: 방향을 알 수 없음 (TargetPos 없음 또는 같은 위치)");
Program.WriteLine($" 결과: 방향을 알 수 없음 (이전 위치값 없음 또는 같은 위치)");
return;
}
// 이동 방향 계산 (이전 → 현재 = TargetPos → CurrentPos)
var dx = currentPos.X - targetPos.Value.X;
var dy = currentPos.Y - targetPos.Value.Y;
var dx = currentPos.X - prevPos.Value.X;
var dy = currentPos.Y - prevPos.Value.Y;
Program.WriteLine($" 이동 벡터: dx={dx}, dy={dy}");
if (Math.Abs(dx) < 1 && Math.Abs(dy) < 1)
@@ -859,7 +880,7 @@ namespace AGVSimulator.Forms
// 경로 예측 기반 LiftCalculator를 사용하여 리프트 방향 계산
var liftInfo = AGVNavigationCore.Utils.LiftCalculator.CalculateLiftInfoWithPathPrediction(
currentPos, targetPos.Value, agv.CurrentDirection, _mapNodes);
currentPos, prevPos.Value, agv.CurrentDirection, _mapNodes);
// 이동 각도 계산 (표시용)
var moveAngleRad = Math.Atan2(dy, dx);
@@ -883,7 +904,7 @@ namespace AGVSimulator.Forms
private string CalculateLiftDirection(VirtualAGV agv)
{
var currentPos = agv.CurrentPosition;
var targetPos = agv.TargetPosition;
var targetPos = agv.PrevPosition;
var dockingDirection = agv.DockingDirection;
if (!targetPos.HasValue || targetPos.Value == currentPos)
@@ -1063,14 +1084,15 @@ namespace AGVSimulator.Forms
{
var motorInfo = advancedResult.DetailedPath[i];
var rfidId = GetRfidByNodeId(motorInfo.NodeId);
string motorSymbol = motorInfo.MotorDirection == AgvDirection.Forward ? "[전진]" : "[후진]";
string motorSymbol = motorInfo.MotorDirection == AgvDirection.Forward ? "[F]" : "[B]";
// 마그넷 방향 표시
if (motorInfo.MagnetDirection != MagnetDirection.Straight)
{
string magnetSymbol = motorInfo.MagnetDirection == MagnetDirection.Left ? "[]" : "[]";
string magnetSymbol = motorInfo.MagnetDirection == MagnetDirection.Left ? "[L]" : "[R]";
motorSymbol += magnetSymbol;
}
else motorSymbol += "[S]";
// 특수 동작 표시
if (motorInfo.RequiresSpecialAction)
@@ -1084,10 +1106,10 @@ namespace AGVSimulator.Forms
string pathString = string.Join(" → ", pathWithDetails);
// UI에 표시 (길이 제한)
if (pathString.Length > 100)
{
pathString = pathString.Substring(0, 97) + "...";
}
//if (pathString.Length > 100)
//{
// pathString = pathString.Substring(0, 97) + "...";
//}
// 통계 정보
var forwardCount = advancedResult.DetailedPath.Count(m => m.MotorDirection == AgvDirection.Forward);
@@ -1101,89 +1123,7 @@ namespace AGVSimulator.Forms
_pathDebugLabel.Text = $"고급경로: {pathString} (총 {advancedResult.DetailedPath.Count}개 노드, {advancedResult.TotalDistance:F1}px, {stats})";
}
/// <summary>
/// 경로 디버깅 정보 업데이트 (RFID 값 표시, 모터방향 정보 포함)
/// </summary>
private void UpdatePathDebugInfo(AGVPathResult agvResult)
{
if (agvResult == null || !agvResult.Success)
{
_pathDebugLabel.Text = "경로: 설정되지 않음";
return;
}
// 노드 ID를 RFID로 변환한 경로 생성
var pathWithRfid = agvResult.Path.Select(nodeId => GetRfidByNodeId(nodeId)).ToList();
// 콘솔 디버그 정보 출력 (RFID 기준)
Program.WriteLine($"[DEBUG] 경로 계산 완료:");
Program.WriteLine($" 전체 경로 (RFID): [{string.Join(" ", pathWithRfid)}]");
Program.WriteLine($" 전체 경로 (NodeID): [{string.Join(" ", agvResult.Path)}]");
Program.WriteLine($" 경로 노드 수: {agvResult.Path.Count}");
if (agvResult.NodeMotorInfos != null)
{
Program.WriteLine($" 모터정보 수: {agvResult.NodeMotorInfos.Count}");
for (int i = 0; i < agvResult.NodeMotorInfos.Count; i++)
{
var info = agvResult.NodeMotorInfos[i];
var rfidId = GetRfidByNodeId(info.NodeId);
var nextRfidId = info.NextNodeId != null ? GetRfidByNodeId(info.NextNodeId) : "END";
var flags = new List<string>();
if (info.CanRotate) flags.Add("회전가능");
if (info.IsDirectionChangePoint) flags.Add("방향전환");
if (info.RequiresSpecialAction) flags.Add($"특수동작:{info.SpecialActionDescription}");
var flagsStr = flags.Count > 0 ? $" [{string.Join(", ", flags)}]" : "";
Program.WriteLine($" {i}: {rfidId}({info.NodeId}) → {info.MotorDirection} → {nextRfidId}{flagsStr}");
}
}
// 모터방향 정보가 있다면 이를 포함하여 경로 문자열 구성 (RFID 기준)
string pathString;
if (agvResult.NodeMotorInfos != null && agvResult.NodeMotorInfos.Count > 0)
{
// RFID별 모터방향 정보를 포함한 경로 문자열 생성
var pathWithMotorInfo = new List<string>();
for (int i = 0; i < agvResult.NodeMotorInfos.Count; i++)
{
var motorInfo = agvResult.NodeMotorInfos[i];
var rfidId = GetRfidByNodeId(motorInfo.NodeId);
string motorSymbol = motorInfo.MotorDirection == AgvDirection.Forward ? "[전진]" : "[후진]";
// 특수 동작 표시 추가
if (motorInfo.RequiresSpecialAction)
motorSymbol += "[🔄]";
else if (motorInfo.IsDirectionChangePoint && motorInfo.CanRotate)
motorSymbol += "[↻]";
pathWithMotorInfo.Add($"{rfidId}{motorSymbol}");
}
pathString = string.Join(" → ", pathWithMotorInfo);
}
else
{
// 기본 경로 정보만 표시 (RFID 기준)
pathString = string.Join(" → ", pathWithRfid);
}
// UI에 표시 (길이 제한)
if (pathString.Length > 120)
{
pathString = pathString.Substring(0, 117) + "...";
}
// 모터방향 통계 추가
string motorStats = "";
if (agvResult.NodeMotorInfos != null && agvResult.NodeMotorInfos.Count > 0)
{
var forwardCount = agvResult.NodeMotorInfos.Count(m => m.MotorDirection == AgvDirection.Forward);
var backwardCount = agvResult.NodeMotorInfos.Count(m => m.MotorDirection == AgvDirection.Backward);
motorStats = $", 전진: {forwardCount}, 후진: {backwardCount}";
}
_pathDebugLabel.Text = $"경로: {pathString} (총 {agvResult.Path.Count}개 노드, {agvResult.TotalDistance:F1}px{motorStats})";
}
private void OnReloadMap_Click(object sender, EventArgs e)
{
@@ -1294,6 +1234,27 @@ namespace AGVSimulator.Forms
_statusLabel.Text = "초기화 완료";
}
private void btNextNode_Click(object sender, EventArgs e)
{
//get next node
// 선택된 AGV 확인
var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
if (selectedAGV == null)
{
MessageBox.Show("먼저 AGV를 선택해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
// 선택된 방향 확인
var selectedDirectionItem = _directionCombo.SelectedItem as DirectionItem;
var selectedDirection = selectedDirectionItem?.Direction ?? AgvDirection.Forward;
var nextNode = selectedAGV.GetNextNodeId(selectedDirection, this._mapNodes);
MessageBox.Show($"Node:{nextNode.NodeId},RFID:{nextNode.RfidId}");
}
}
/// <summary>