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:
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
@@ -46,7 +46,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Models\SimulatorConfig.cs" />
|
||||
<Compile Include="Models\VirtualAGV.cs" />
|
||||
<Compile Include="Models\SimulationState.cs" />
|
||||
<Compile Include="Forms\SimulatorForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -1,561 +0,0 @@
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Drawing;
|
||||
//using System.Linq;
|
||||
//using AGVMapEditor.Models;
|
||||
//using AGVNavigationCore.Models;
|
||||
//using AGVNavigationCore.PathFinding;
|
||||
//using AGVNavigationCore.PathFinding.Core;
|
||||
//using AGVNavigationCore.Controls;
|
||||
|
||||
//namespace AGVSimulator.Models
|
||||
//{
|
||||
|
||||
// /// <summary>
|
||||
// /// 가상 AGV 클래스
|
||||
// /// 실제 AGV의 동작을 시뮬레이션
|
||||
// /// </summary>
|
||||
// public class VirtualAGV : IAGV
|
||||
// {
|
||||
// #region Events
|
||||
|
||||
// /// <summary>
|
||||
// /// AGV 상태 변경 이벤트
|
||||
// /// </summary>
|
||||
// public event EventHandler<AGVState> StateChanged;
|
||||
|
||||
// /// <summary>
|
||||
// /// 위치 변경 이벤트
|
||||
// /// </summary>
|
||||
// public event EventHandler<(Point, AgvDirection, MapNode)> PositionChanged;
|
||||
|
||||
// /// <summary>
|
||||
// /// RFID 감지 이벤트
|
||||
// /// </summary>
|
||||
// public event EventHandler<string> RfidDetected;
|
||||
|
||||
// /// <summary>
|
||||
// /// 경로 완료 이벤트
|
||||
// /// </summary>
|
||||
// public event EventHandler<AGVPathResult> PathCompleted;
|
||||
|
||||
// /// <summary>
|
||||
// /// 오류 발생 이벤트
|
||||
// /// </summary>
|
||||
// public event EventHandler<string> ErrorOccurred;
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Fields
|
||||
|
||||
// private string _agvId;
|
||||
// private Point _currentPosition;
|
||||
// private Point _targetPosition;
|
||||
// private string _targetId;
|
||||
// private string _currentId;
|
||||
// private AgvDirection _currentDirection;
|
||||
// private AgvDirection _targetDirection;
|
||||
// private AGVState _currentState;
|
||||
// private float _currentSpeed;
|
||||
|
||||
// // 경로 관련
|
||||
// private AGVPathResult _currentPath;
|
||||
// private List<string> _remainingNodes;
|
||||
// private int _currentNodeIndex;
|
||||
// private MapNode _currentNode;
|
||||
// private MapNode _targetNode;
|
||||
|
||||
// // 이동 관련
|
||||
// private System.Windows.Forms.Timer _moveTimer;
|
||||
// private DateTime _lastMoveTime;
|
||||
// private Point _moveStartPosition;
|
||||
// private Point _moveTargetPosition;
|
||||
// private float _moveProgress;
|
||||
|
||||
// // 도킹 관련
|
||||
// private DockingDirection _dockingDirection;
|
||||
|
||||
// // 시뮬레이션 설정
|
||||
// private readonly float _moveSpeed = 50.0f; // 픽셀/초
|
||||
// private readonly float _rotationSpeed = 90.0f; // 도/초
|
||||
// private readonly int _updateInterval = 50; // ms
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Properties
|
||||
|
||||
// /// <summary>
|
||||
// /// AGV ID
|
||||
// /// </summary>
|
||||
// public string AgvId => _agvId;
|
||||
|
||||
// /// <summary>
|
||||
// /// 현재 위치
|
||||
// /// </summary>
|
||||
// public Point CurrentPosition
|
||||
// {
|
||||
// get => _currentPosition;
|
||||
// set => _currentPosition = value;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 현재 방향
|
||||
// /// 모터의 동작 방향
|
||||
// /// </summary>
|
||||
// public AgvDirection CurrentDirection
|
||||
// {
|
||||
// get => _currentDirection;
|
||||
// set => _currentDirection = value;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 현재 상태
|
||||
// /// </summary>
|
||||
// public AGVState CurrentState
|
||||
// {
|
||||
// get => _currentState;
|
||||
// set => _currentState = value;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 현재 속도
|
||||
// /// </summary>
|
||||
// public float CurrentSpeed => _currentSpeed;
|
||||
|
||||
// /// <summary>
|
||||
// /// 현재 경로
|
||||
// /// </summary>
|
||||
// public AGVPathResult CurrentPath => _currentPath;
|
||||
|
||||
// /// <summary>
|
||||
// /// 현재 노드 ID
|
||||
// /// </summary>
|
||||
// public string CurrentNodeId => _currentNode.NodeId;
|
||||
|
||||
// /// <summary>
|
||||
// /// 목표 위치
|
||||
// /// </summary>
|
||||
// public Point? TargetPosition => _targetPosition;
|
||||
|
||||
// /// <summary>
|
||||
// /// 배터리 레벨 (시뮬레이션)
|
||||
// /// </summary>
|
||||
// public float BatteryLevel { get; set; } = 100.0f;
|
||||
|
||||
// /// <summary>
|
||||
// /// 목표 노드 ID
|
||||
// /// </summary>
|
||||
// public string TargetNodeId => _targetNode.NodeId;
|
||||
|
||||
// /// <summary>
|
||||
// /// 도킹 방향
|
||||
// /// </summary>
|
||||
// public DockingDirection DockingDirection => _dockingDirection;
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Constructor
|
||||
|
||||
// /// <summary>
|
||||
// /// 생성자
|
||||
// /// </summary>
|
||||
// /// <param name="agvId">AGV ID</param>
|
||||
// /// <param name="startPosition">시작 위치</param>
|
||||
// /// <param name="startDirection">시작 방향</param>
|
||||
// public VirtualAGV(string agvId, Point startPosition, AgvDirection startDirection = AgvDirection.Forward)
|
||||
// {
|
||||
// _agvId = agvId;
|
||||
// _currentPosition = startPosition;
|
||||
// _currentDirection = startDirection;
|
||||
// _currentState = AGVState.Idle;
|
||||
// _currentSpeed = 0;
|
||||
// _dockingDirection = DockingDirection.Forward; // 기본값: 전진 도킹
|
||||
// _currentNode = null; // = string.Empty;
|
||||
// _targetNode = null;// string.Empty;
|
||||
|
||||
// InitializeTimer();
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Initialization
|
||||
|
||||
// private void InitializeTimer()
|
||||
// {
|
||||
// _moveTimer = new System.Windows.Forms.Timer();
|
||||
// _moveTimer.Interval = _updateInterval;
|
||||
// _moveTimer.Tick += OnMoveTimer_Tick;
|
||||
// _lastMoveTime = DateTime.Now;
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Public Methods
|
||||
|
||||
// /// <summary>
|
||||
// /// 경로 실행 시작
|
||||
// /// </summary>
|
||||
// /// <param name="path">실행할 경로</param>
|
||||
// /// <param name="mapNodes">맵 노드 목록</param>
|
||||
// public void StartPath(AGVPathResult path, List<MapNode> mapNodes)
|
||||
// {
|
||||
// if (path == null || !path.Success)
|
||||
// {
|
||||
// OnError("유효하지 않은 경로입니다.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// _currentPath = path;
|
||||
// _remainingNodes = new List<string>(path.Path);
|
||||
// _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);
|
||||
// }
|
||||
// }
|
||||
|
||||
// StartMovement();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// OnError($"시작 노드를 찾을 수 없습니다: {_remainingNodes[0]}");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 경로 정지
|
||||
// /// </summary>
|
||||
// public void StopPath()
|
||||
// {
|
||||
// _moveTimer.Stop();
|
||||
// _currentPath = null;
|
||||
// _remainingNodes?.Clear();
|
||||
// SetState(AGVState.Idle);
|
||||
// _currentSpeed = 0;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 긴급 정지
|
||||
// /// </summary>
|
||||
// public void EmergencyStop()
|
||||
// {
|
||||
// StopPath();
|
||||
// OnError("긴급 정지가 실행되었습니다.");
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 수동 이동 (테스트용)
|
||||
// /// </summary>
|
||||
// /// <param name="targetPosition">목표 위치</param>
|
||||
// public void MoveTo(Point targetPosition)
|
||||
// {
|
||||
// _targetPosition = targetPosition;
|
||||
// _moveStartPosition = _currentPosition;
|
||||
// _moveTargetPosition = targetPosition;
|
||||
// _moveProgress = 0;
|
||||
|
||||
// SetState(AGVState.Moving);
|
||||
// _moveTimer.Start();
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 수동 회전 (테스트용)
|
||||
// /// </summary>
|
||||
// /// <param name="direction">회전 방향</param>
|
||||
// public void Rotate(AgvDirection direction)
|
||||
// {
|
||||
// if (_currentState != AGVState.Idle)
|
||||
// return;
|
||||
|
||||
// SetState(AGVState.Rotating);
|
||||
|
||||
// // 시뮬레이션: 즉시 방향 변경 (실제로는 시간이 걸림)
|
||||
// _currentDirection = direction;
|
||||
|
||||
// System.Threading.Thread.Sleep(500); // 회전 시간 시뮬레이션
|
||||
// SetState(AGVState.Idle);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
// /// <summary>
|
||||
// /// AGV 위치 직접 설정 (시뮬레이터용)
|
||||
// /// TargetPosition을 이전 위치로 저장하여 리프트 방향 계산이 가능하도록 함
|
||||
// /// </summary>
|
||||
// /// <param name="newPosition">새로운 위치</param>
|
||||
// /// <param name="motorDirection">모터이동방향</param>
|
||||
// public void SetPosition(MapNode node, Point newPosition, AgvDirection motorDirection)
|
||||
// {
|
||||
// // 현재 위치를 이전 위치로 저장 (리프트 방향 계산용)
|
||||
// if (_currentPosition != Point.Empty)
|
||||
// {
|
||||
// _targetPosition = _currentPosition; // 이전 위치 (previousPos 역할)
|
||||
// _targetDirection = _currentDirection;
|
||||
// _targetNode = node;
|
||||
// }
|
||||
|
||||
// // 새로운 위치 설정
|
||||
// _currentPosition = newPosition;
|
||||
// _currentDirection = motorDirection;
|
||||
// _currentNode = node;
|
||||
|
||||
// // 위치 변경 이벤트 발생
|
||||
// PositionChanged?.Invoke(this, (_currentPosition, _currentDirection, _currentNode));
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 충전 시작 (시뮬레이션)
|
||||
// /// </summary>
|
||||
// public void StartCharging()
|
||||
// {
|
||||
// if (_currentState == AGVState.Idle)
|
||||
// {
|
||||
// SetState(AGVState.Charging);
|
||||
// // 충전 시뮬레이션 시작
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 충전 종료
|
||||
// /// </summary>
|
||||
// public void StopCharging()
|
||||
// {
|
||||
// if (_currentState == AGVState.Charging)
|
||||
// {
|
||||
// SetState(AGVState.Idle);
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// AGV 정보 조회
|
||||
// /// </summary>
|
||||
// public string GetStatus()
|
||||
// {
|
||||
// return $"AGV[{_agvId}] 위치:({_currentPosition.X},{_currentPosition.Y}) " +
|
||||
// $"방향:{_currentDirection} 상태:{_currentState} " +
|
||||
// $"속도:{_currentSpeed:F1} 배터리:{BatteryLevel:F1}%";
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 현재 RFID 시뮬레이션 (현재 위치 기준)
|
||||
// /// </summary>
|
||||
// public string SimulateRfidReading(List<MapNode> mapNodes)
|
||||
// {
|
||||
// // 현재 위치에서 가장 가까운 노드 찾기
|
||||
// var closestNode = FindClosestNode(_currentPosition, mapNodes);
|
||||
// if (closestNode == null)
|
||||
// return null;
|
||||
|
||||
// // 해당 노드의 RFID 정보 반환 (MapNode에 RFID 정보 포함)
|
||||
// return closestNode.HasRfid() ? closestNode.RfidId : null;
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Private Methods
|
||||
|
||||
// private void StartMovement()
|
||||
// {
|
||||
// SetState(AGVState.Moving);
|
||||
// _moveTimer.Start();
|
||||
// _lastMoveTime = DateTime.Now;
|
||||
// }
|
||||
|
||||
// private void OnMoveTimer_Tick(object sender, EventArgs e)
|
||||
// {
|
||||
// var now = DateTime.Now;
|
||||
// var deltaTime = (float)(now - _lastMoveTime).TotalSeconds;
|
||||
// _lastMoveTime = now;
|
||||
|
||||
// UpdateMovement(deltaTime);
|
||||
// UpdateBattery(deltaTime);
|
||||
|
||||
// // 위치 변경 이벤트 발생
|
||||
// PositionChanged?.Invoke(this, (_currentPosition, _currentDirection, _currentNode));
|
||||
// }
|
||||
|
||||
// private void UpdateMovement(float deltaTime)
|
||||
// {
|
||||
// if (_currentState != AGVState.Moving)
|
||||
// return;
|
||||
|
||||
// // 목표 위치까지의 거리 계산
|
||||
// var distance = CalculateDistance(_currentPosition, _moveTargetPosition);
|
||||
|
||||
// if (distance < 5.0f) // 도달 임계값
|
||||
// {
|
||||
// // 목표 도달
|
||||
// _currentPosition = _moveTargetPosition;
|
||||
// _currentSpeed = 0;
|
||||
|
||||
// // 다음 노드로 이동
|
||||
// ProcessNextNode();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // 계속 이동
|
||||
// var moveDistance = _moveSpeed * deltaTime;
|
||||
// var direction = new PointF(
|
||||
// _moveTargetPosition.X - _currentPosition.X,
|
||||
// _moveTargetPosition.Y - _currentPosition.Y
|
||||
// );
|
||||
|
||||
// // 정규화
|
||||
// var length = (float)Math.Sqrt(direction.X * direction.X + direction.Y * direction.Y);
|
||||
// if (length > 0)
|
||||
// {
|
||||
// direction.X /= length;
|
||||
// direction.Y /= length;
|
||||
// }
|
||||
|
||||
// // 새 위치 계산
|
||||
// _currentPosition = new Point(
|
||||
// (int)(_currentPosition.X + direction.X * moveDistance),
|
||||
// (int)(_currentPosition.Y + direction.Y * moveDistance)
|
||||
// );
|
||||
|
||||
// _currentSpeed = _moveSpeed;
|
||||
// }
|
||||
// }
|
||||
|
||||
// private void UpdateBattery(float deltaTime)
|
||||
// {
|
||||
// // 배터리 소모 시뮬레이션
|
||||
// if (_currentState == AGVState.Moving)
|
||||
// {
|
||||
// BatteryLevel -= 0.1f * deltaTime; // 이동시 소모
|
||||
// }
|
||||
// else if (_currentState == AGVState.Charging)
|
||||
// {
|
||||
// BatteryLevel += 5.0f * deltaTime; // 충전
|
||||
// BatteryLevel = Math.Min(100.0f, BatteryLevel);
|
||||
// }
|
||||
|
||||
// BatteryLevel = Math.Max(0, BatteryLevel);
|
||||
|
||||
// // 배터리 부족 경고
|
||||
// if (BatteryLevel < 20.0f && _currentState != AGVState.Charging)
|
||||
// {
|
||||
// OnError($"배터리 부족: {BatteryLevel:F1}%");
|
||||
// }
|
||||
// }
|
||||
|
||||
// private void ProcessNextNode()
|
||||
// {
|
||||
// if (_remainingNodes == null || _currentNodeIndex >= _remainingNodes.Count - 1)
|
||||
// {
|
||||
// // 경로 완료
|
||||
// _moveTimer.Stop();
|
||||
// SetState(AGVState.Idle);
|
||||
// PathCompleted?.Invoke(this, _currentPath);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // 다음 노드로 이동
|
||||
// _currentNodeIndex++;
|
||||
// var nextNodeId = _remainingNodes[_currentNodeIndex];
|
||||
|
||||
// // RFID 감지 시뮬레이션
|
||||
// RfidDetected?.Invoke(this, $"RFID_{nextNodeId}");
|
||||
// //_currentNodeId = nextNodeId;
|
||||
|
||||
// // 다음 목표 위치 설정 (실제로는 맵에서 좌표 가져와야 함)
|
||||
// // 여기서는 간단히 현재 위치에서 랜덤 오프셋으로 설정
|
||||
// var random = new Random();
|
||||
// _moveTargetPosition = new Point(
|
||||
// _currentPosition.X + random.Next(-100, 100),
|
||||
// _currentPosition.Y + random.Next(-100, 100)
|
||||
// );
|
||||
// }
|
||||
|
||||
// private MapNode FindClosestNode(Point position, List<MapNode> mapNodes)
|
||||
// {
|
||||
// if (mapNodes == null || mapNodes.Count == 0)
|
||||
// return null;
|
||||
|
||||
// MapNode closestNode = null;
|
||||
// float closestDistance = float.MaxValue;
|
||||
|
||||
// foreach (var node in mapNodes)
|
||||
// {
|
||||
// var distance = CalculateDistance(position, node.Position);
|
||||
// if (distance < closestDistance)
|
||||
// {
|
||||
// closestDistance = distance;
|
||||
// closestNode = node;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 일정 거리 내에 있는 노드만 반환
|
||||
// return closestDistance < 50.0f ? closestNode : null;
|
||||
// }
|
||||
|
||||
// private float CalculateDistance(Point from, Point to)
|
||||
// {
|
||||
// var dx = to.X - from.X;
|
||||
// var dy = to.Y - from.Y;
|
||||
// return (float)Math.Sqrt(dx * dx + dy * dy);
|
||||
// }
|
||||
|
||||
// private void SetState(AGVState newState)
|
||||
// {
|
||||
// if (_currentState != newState)
|
||||
// {
|
||||
// _currentState = newState;
|
||||
// StateChanged?.Invoke(this, newState);
|
||||
// }
|
||||
// }
|
||||
|
||||
// private DockingDirection GetDockingDirection(NodeType nodeType)
|
||||
// {
|
||||
// switch (nodeType)
|
||||
// {
|
||||
// case NodeType.Charging:
|
||||
// return DockingDirection.Forward; // 충전기: 전진 도킹
|
||||
// case NodeType.Docking:
|
||||
// return DockingDirection.Backward; // 장비 (로더, 클리너, 오프로더, 버퍼): 후진 도킹
|
||||
// default:
|
||||
// return DockingDirection.Forward; // 기본값: 전진
|
||||
// }
|
||||
// }
|
||||
|
||||
// private void OnError(string message)
|
||||
// {
|
||||
// SetState(AGVState.Error);
|
||||
// ErrorOccurred?.Invoke(this, message);
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Cleanup
|
||||
|
||||
// /// <summary>
|
||||
// /// 리소스 정리
|
||||
// /// </summary>
|
||||
// public void Dispose()
|
||||
// {
|
||||
// _moveTimer?.Stop();
|
||||
// _moveTimer?.Dispose();
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
// }
|
||||
//}
|
||||
Reference in New Issue
Block a user