refactor: Consolidate RFID mapping and add bidirectional pathfinding

Major improvements to AGV navigation system:

• Consolidated RFID management into MapNode, removing duplicate RfidMapping class
• Enhanced MapNode with RFID metadata fields (RfidStatus, RfidDescription)
• Added automatic bidirectional connection generation in pathfinding algorithms
• Updated all components to use unified MapNode-based RFID system
• Added command line argument support for AGVMapEditor auto-loading files
• Fixed pathfinding failures by ensuring proper node connectivity

Technical changes:
- Removed RfidMapping class and dependencies across all projects
- Updated AStarPathfinder with EnsureBidirectionalConnections() method
- Modified MapLoader to use AssignAutoRfidIds() for RFID automation
- Enhanced UnifiedAGVCanvas, SimulatorForm, and MainForm for MapNode integration
- Improved data consistency and reduced memory footprint

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ChiKyun Kim
2025-09-11 16:41:52 +09:00
parent 7567602479
commit de0e39e030
50 changed files with 9578 additions and 1854 deletions

View File

@@ -4,7 +4,7 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B2C3D4E5-F6G7-8901-BCDE-F23456789012}</ProjectGuid>
<ProjectGuid>{B2C3D4E5-0000-0000-0000-000000000000}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>AGVSimulator</RootNamespace>
<AssemblyName>AGVSimulator</AssemblyName>
@@ -42,14 +42,9 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Models\SimulatorConfig.cs" />
<Compile Include="Models\VirtualAGV.cs" />
<Compile Include="Models\SimulationState.cs" />
<Compile Include="Controls\SimulatorCanvas.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Controls\SimulatorCanvas.Designer.cs">
<DependentUpon>SimulatorCanvas.cs</DependentUpon>
</Compile>
<Compile Include="Forms\SimulatorForm.cs">
<SubType>Form</SubType>
</Compile>
@@ -60,17 +55,19 @@
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Controls\SimulatorCanvas.resx">
<DependentUpon>SimulatorCanvas.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\SimulatorForm.resx">
<DependentUpon>SimulatorForm.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="build.bat" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AGVNavigationCore\AGVNavigationCore.csproj">
<Project>{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}</Project>
<Name>AGVNavigationCore</Name>
</ProjectReference>
<ProjectReference Include="..\AGVMapEditor\AGVMapEditor.csproj">
<Project>{a1b2c3d4-e5f6-7890-abcd-ef1234567890}</Project>
<Name>AGVMapEditor</Name>

View File

@@ -4,6 +4,8 @@ using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using AGVMapEditor.Models;
using AGVNavigationCore.Models;
using AGVNavigationCore.PathFinding;
using AGVSimulator.Models;
namespace AGVSimulator.Controls
@@ -388,13 +390,13 @@ namespace AGVSimulator.Controls
private void DrawPath(Graphics g)
{
if (_currentPath?.NodeSequence == null || _currentPath.NodeSequence.Count < 2)
if (_currentPath?.Path == null || _currentPath.Path.Count < 2)
return;
for (int i = 0; i < _currentPath.NodeSequence.Count - 1; i++)
for (int i = 0; i < _currentPath.Path.Count - 1; i++)
{
var currentNodeId = _currentPath.NodeSequence[i];
var nextNodeId = _currentPath.NodeSequence[i + 1];
var currentNodeId = _currentPath.Path[i];
var nextNodeId = _currentPath.Path[i + 1];
var currentNode = _mapNodes?.FirstOrDefault(n => n.NodeId == currentNodeId);
var nextNode = _mapNodes?.FirstOrDefault(n => n.NodeId == nextNodeId);
@@ -583,11 +585,11 @@ namespace AGVSimulator.Controls
if (_currentPath != null && _currentPath.Success)
{
y += 10;
g.DrawString($"경로: {_currentPath.NodeSequence.Count}개 노드", font, brush, new PointF(10, y));
g.DrawString($"경로: {_currentPath.Path.Count}개 노드", font, brush, new PointF(10, y));
y += 15;
g.DrawString($"거리: {_currentPath.TotalDistance:F1}", font, brush, new PointF(10, y));
y += 15;
g.DrawString($"계산시간: {_currentPath.CalculationTime}ms", font, brush, new PointF(10, y));
g.DrawString($"계산시간: {_currentPath.CalculationTimeMs}ms", font, brush, new PointF(10, y));
}
font.Dispose();

View File

@@ -45,20 +45,666 @@ namespace AGVSimulator.Forms
/// </summary>
private void InitializeComponent()
{
this._menuStrip = new System.Windows.Forms.MenuStrip();
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.openMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.reloadMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
this.launchMapEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator();
this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.simulationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.startSimulationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.stopSimulationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.resetToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.viewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.fitToMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.resetZoomToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this._toolStrip = new System.Windows.Forms.ToolStrip();
this.openMapToolStripButton = new System.Windows.Forms.ToolStripButton();
this.reloadMapToolStripButton = new System.Windows.Forms.ToolStripButton();
this.launchMapEditorToolStripButton = new System.Windows.Forms.ToolStripButton();
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
this.startSimulationToolStripButton = new System.Windows.Forms.ToolStripButton();
this.stopSimulationToolStripButton = new System.Windows.Forms.ToolStripButton();
this.resetToolStripButton = new System.Windows.Forms.ToolStripButton();
this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
this.fitToMapToolStripButton = new System.Windows.Forms.ToolStripButton();
this.resetZoomToolStripButton = new System.Windows.Forms.ToolStripButton();
this._statusStrip = new System.Windows.Forms.StatusStrip();
this._statusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this._coordLabel = new System.Windows.Forms.ToolStripStatusLabel();
this._controlPanel = new System.Windows.Forms.Panel();
this._statusGroup = new System.Windows.Forms.GroupBox();
this._pathLengthLabel = new System.Windows.Forms.Label();
this._agvCountLabel = new System.Windows.Forms.Label();
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._targetNodeCombo = new System.Windows.Forms.ComboBox();
this.targetNodeLabel = new System.Windows.Forms.Label();
this._startNodeCombo = new System.Windows.Forms.ComboBox();
this.startNodeLabel = new System.Windows.Forms.Label();
this._agvControlGroup = new System.Windows.Forms.GroupBox();
this._setPositionButton = new System.Windows.Forms.Button();
this._rfidTextBox = new System.Windows.Forms.TextBox();
this._rfidLabel = new System.Windows.Forms.Label();
this._stopSimulationButton = new System.Windows.Forms.Button();
this._startSimulationButton = new System.Windows.Forms.Button();
this._removeAgvButton = new System.Windows.Forms.Button();
this._addAgvButton = new System.Windows.Forms.Button();
this._agvListCombo = new System.Windows.Forms.ComboBox();
this._canvasPanel = new System.Windows.Forms.Panel();
this.btAllReset = new System.Windows.Forms.ToolStripButton();
this._menuStrip.SuspendLayout();
this._toolStrip.SuspendLayout();
this._statusStrip.SuspendLayout();
this._controlPanel.SuspendLayout();
this._statusGroup.SuspendLayout();
this._pathGroup.SuspendLayout();
this._agvControlGroup.SuspendLayout();
this.SuspendLayout();
//
// _menuStrip
//
this._menuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileToolStripMenuItem,
this.simulationToolStripMenuItem,
this.viewToolStripMenuItem,
this.helpToolStripMenuItem});
this._menuStrip.Location = new System.Drawing.Point(0, 0);
this._menuStrip.Name = "_menuStrip";
this._menuStrip.Size = new System.Drawing.Size(1200, 24);
this._menuStrip.TabIndex = 0;
this._menuStrip.Text = "menuStrip";
//
// fileToolStripMenuItem
//
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.openMapToolStripMenuItem,
this.reloadMapToolStripMenuItem,
this.toolStripSeparator1,
this.launchMapEditorToolStripMenuItem,
this.toolStripSeparator4,
this.exitToolStripMenuItem});
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
this.fileToolStripMenuItem.Size = new System.Drawing.Size(57, 20);
this.fileToolStripMenuItem.Text = "파일(&F)";
//
// openMapToolStripMenuItem
//
this.openMapToolStripMenuItem.Name = "openMapToolStripMenuItem";
this.openMapToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O)));
this.openMapToolStripMenuItem.Size = new System.Drawing.Size(183, 22);
this.openMapToolStripMenuItem.Text = "맵 열기(&O)...";
this.openMapToolStripMenuItem.Click += new System.EventHandler(this.OnOpenMap_Click);
//
// reloadMapToolStripMenuItem
//
this.reloadMapToolStripMenuItem.Name = "reloadMapToolStripMenuItem";
this.reloadMapToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.R)));
this.reloadMapToolStripMenuItem.Size = new System.Drawing.Size(183, 22);
this.reloadMapToolStripMenuItem.Text = "맵 다시열기(&R)";
this.reloadMapToolStripMenuItem.Click += new System.EventHandler(this.OnReloadMap_Click);
//
// toolStripSeparator1
//
this.toolStripSeparator1.Name = "toolStripSeparator1";
this.toolStripSeparator1.Size = new System.Drawing.Size(180, 6);
//
// launchMapEditorToolStripMenuItem
//
this.launchMapEditorToolStripMenuItem.Name = "launchMapEditorToolStripMenuItem";
this.launchMapEditorToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.M)));
this.launchMapEditorToolStripMenuItem.Size = new System.Drawing.Size(183, 22);
this.launchMapEditorToolStripMenuItem.Text = "MapEditor 실행(&M)";
this.launchMapEditorToolStripMenuItem.Click += new System.EventHandler(this.OnLaunchMapEditor_Click);
//
// toolStripSeparator4
//
this.toolStripSeparator4.Name = "toolStripSeparator4";
this.toolStripSeparator4.Size = new System.Drawing.Size(180, 6);
//
// exitToolStripMenuItem
//
this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
this.exitToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Alt | System.Windows.Forms.Keys.F4)));
this.exitToolStripMenuItem.Size = new System.Drawing.Size(183, 22);
this.exitToolStripMenuItem.Text = "종료(&X)";
this.exitToolStripMenuItem.Click += new System.EventHandler(this.OnExit_Click);
//
// simulationToolStripMenuItem
//
this.simulationToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.startSimulationToolStripMenuItem,
this.stopSimulationToolStripMenuItem,
this.resetToolStripMenuItem});
this.simulationToolStripMenuItem.Name = "simulationToolStripMenuItem";
this.simulationToolStripMenuItem.Size = new System.Drawing.Size(94, 20);
this.simulationToolStripMenuItem.Text = "시뮬레이션(&S)";
//
// startSimulationToolStripMenuItem
//
this.startSimulationToolStripMenuItem.Name = "startSimulationToolStripMenuItem";
this.startSimulationToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F5;
this.startSimulationToolStripMenuItem.Size = new System.Drawing.Size(145, 22);
this.startSimulationToolStripMenuItem.Text = "시작(&S)";
this.startSimulationToolStripMenuItem.Click += new System.EventHandler(this.OnStartSimulation_Click);
//
// stopSimulationToolStripMenuItem
//
this.stopSimulationToolStripMenuItem.Name = "stopSimulationToolStripMenuItem";
this.stopSimulationToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F6;
this.stopSimulationToolStripMenuItem.Size = new System.Drawing.Size(145, 22);
this.stopSimulationToolStripMenuItem.Text = "정지(&T)";
this.stopSimulationToolStripMenuItem.Click += new System.EventHandler(this.OnStopSimulation_Click);
//
// resetToolStripMenuItem
//
this.resetToolStripMenuItem.Name = "resetToolStripMenuItem";
this.resetToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F7;
this.resetToolStripMenuItem.Size = new System.Drawing.Size(145, 22);
this.resetToolStripMenuItem.Text = "초기화(&R)";
this.resetToolStripMenuItem.Click += new System.EventHandler(this.OnReset_Click);
//
// viewToolStripMenuItem
//
this.viewToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fitToMapToolStripMenuItem,
this.resetZoomToolStripMenuItem});
this.viewToolStripMenuItem.Name = "viewToolStripMenuItem";
this.viewToolStripMenuItem.Size = new System.Drawing.Size(59, 20);
this.viewToolStripMenuItem.Text = "보기(&V)";
//
// fitToMapToolStripMenuItem
//
this.fitToMapToolStripMenuItem.Name = "fitToMapToolStripMenuItem";
this.fitToMapToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.F)));
this.fitToMapToolStripMenuItem.Size = new System.Drawing.Size(182, 22);
this.fitToMapToolStripMenuItem.Text = "맵 맞춤(&F)";
this.fitToMapToolStripMenuItem.Click += new System.EventHandler(this.OnFitToMap_Click);
//
// resetZoomToolStripMenuItem
//
this.resetZoomToolStripMenuItem.Name = "resetZoomToolStripMenuItem";
this.resetZoomToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.D0)));
this.resetZoomToolStripMenuItem.Size = new System.Drawing.Size(182, 22);
this.resetZoomToolStripMenuItem.Text = "줌 초기화(&Z)";
this.resetZoomToolStripMenuItem.Click += new System.EventHandler(this.OnResetZoom_Click);
//
// helpToolStripMenuItem
//
this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.aboutToolStripMenuItem});
this.helpToolStripMenuItem.Name = "helpToolStripMenuItem";
this.helpToolStripMenuItem.Size = new System.Drawing.Size(72, 20);
this.helpToolStripMenuItem.Text = "도움말(&H)";
//
// aboutToolStripMenuItem
//
this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem";
this.aboutToolStripMenuItem.Size = new System.Drawing.Size(123, 22);
this.aboutToolStripMenuItem.Text = "정보(&A)...";
this.aboutToolStripMenuItem.Click += new System.EventHandler(this.OnAbout_Click);
//
// _toolStrip
//
this._toolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.openMapToolStripButton,
this.reloadMapToolStripButton,
this.launchMapEditorToolStripButton,
this.toolStripSeparator2,
this.startSimulationToolStripButton,
this.stopSimulationToolStripButton,
this.resetToolStripButton,
this.btAllReset,
this.toolStripSeparator3,
this.fitToMapToolStripButton,
this.resetZoomToolStripButton});
this._toolStrip.Location = new System.Drawing.Point(0, 24);
this._toolStrip.Name = "_toolStrip";
this._toolStrip.Size = new System.Drawing.Size(1200, 25);
this._toolStrip.TabIndex = 1;
this._toolStrip.Text = "toolStrip";
//
// openMapToolStripButton
//
this.openMapToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.openMapToolStripButton.Name = "openMapToolStripButton";
this.openMapToolStripButton.Size = new System.Drawing.Size(51, 22);
this.openMapToolStripButton.Text = "맵 열기";
this.openMapToolStripButton.ToolTipText = "맵 파일을 엽니다";
this.openMapToolStripButton.Click += new System.EventHandler(this.OnOpenMap_Click);
//
// reloadMapToolStripButton
//
this.reloadMapToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.reloadMapToolStripButton.Name = "reloadMapToolStripButton";
this.reloadMapToolStripButton.Size = new System.Drawing.Size(63, 22);
this.reloadMapToolStripButton.Text = "다시열기";
this.reloadMapToolStripButton.ToolTipText = "현재 맵을 다시 로드합니다";
this.reloadMapToolStripButton.Click += new System.EventHandler(this.OnReloadMap_Click);
//
// launchMapEditorToolStripButton
//
this.launchMapEditorToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.launchMapEditorToolStripButton.Name = "launchMapEditorToolStripButton";
this.launchMapEditorToolStripButton.Size = new System.Drawing.Size(71, 22);
this.launchMapEditorToolStripButton.Text = "MapEditor";
this.launchMapEditorToolStripButton.ToolTipText = "MapEditor를 실행합니다";
this.launchMapEditorToolStripButton.Click += new System.EventHandler(this.OnLaunchMapEditor_Click);
//
// toolStripSeparator2
//
this.toolStripSeparator2.Name = "toolStripSeparator2";
this.toolStripSeparator2.Size = new System.Drawing.Size(6, 25);
//
// startSimulationToolStripButton
//
this.startSimulationToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.startSimulationToolStripButton.Name = "startSimulationToolStripButton";
this.startSimulationToolStripButton.Size = new System.Drawing.Size(99, 22);
this.startSimulationToolStripButton.Text = "시뮬레이션 시작";
this.startSimulationToolStripButton.ToolTipText = "시뮬레이션을 시작합니다";
this.startSimulationToolStripButton.Click += new System.EventHandler(this.OnStartSimulation_Click);
//
// stopSimulationToolStripButton
//
this.stopSimulationToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.stopSimulationToolStripButton.Name = "stopSimulationToolStripButton";
this.stopSimulationToolStripButton.Size = new System.Drawing.Size(99, 22);
this.stopSimulationToolStripButton.Text = "시뮬레이션 정지";
this.stopSimulationToolStripButton.ToolTipText = "시뮬레이션을 정지합니다";
this.stopSimulationToolStripButton.Click += new System.EventHandler(this.OnStopSimulation_Click);
//
// resetToolStripButton
//
this.resetToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.resetToolStripButton.Name = "resetToolStripButton";
this.resetToolStripButton.Size = new System.Drawing.Size(47, 22);
this.resetToolStripButton.Text = "초기화";
this.resetToolStripButton.ToolTipText = "시뮬레이션을 초기화합니다";
this.resetToolStripButton.Click += new System.EventHandler(this.OnReset_Click);
//
// toolStripSeparator3
//
this.toolStripSeparator3.Name = "toolStripSeparator3";
this.toolStripSeparator3.Size = new System.Drawing.Size(6, 25);
//
// fitToMapToolStripButton
//
this.fitToMapToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.fitToMapToolStripButton.Name = "fitToMapToolStripButton";
this.fitToMapToolStripButton.Size = new System.Drawing.Size(51, 22);
this.fitToMapToolStripButton.Text = "맵 맞춤";
this.fitToMapToolStripButton.ToolTipText = "맵 전체를 화면에 맞춥니다";
this.fitToMapToolStripButton.Click += new System.EventHandler(this.OnFitToMap_Click);
//
// resetZoomToolStripButton
//
this.resetZoomToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.resetZoomToolStripButton.Name = "resetZoomToolStripButton";
this.resetZoomToolStripButton.Size = new System.Drawing.Size(63, 22);
this.resetZoomToolStripButton.Text = "줌 초기화";
this.resetZoomToolStripButton.ToolTipText = "줌을 초기화합니다";
this.resetZoomToolStripButton.Click += new System.EventHandler(this.OnResetZoom_Click);
//
// _statusStrip
//
this._statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this._statusLabel,
this._coordLabel});
this._statusStrip.Location = new System.Drawing.Point(0, 778);
this._statusStrip.Name = "_statusStrip";
this._statusStrip.Size = new System.Drawing.Size(1200, 22);
this._statusStrip.TabIndex = 2;
this._statusStrip.Text = "statusStrip";
//
// _statusLabel
//
this._statusLabel.Name = "_statusLabel";
this._statusLabel.Size = new System.Drawing.Size(31, 17);
this._statusLabel.Text = "준비";
//
// _coordLabel
//
this._coordLabel.Name = "_coordLabel";
this._coordLabel.Size = new System.Drawing.Size(0, 17);
//
// _controlPanel
//
this._controlPanel.BackColor = System.Drawing.SystemColors.Control;
this._controlPanel.Controls.Add(this._statusGroup);
this._controlPanel.Controls.Add(this._pathGroup);
this._controlPanel.Controls.Add(this._agvControlGroup);
this._controlPanel.Dock = System.Windows.Forms.DockStyle.Right;
this._controlPanel.Location = new System.Drawing.Point(950, 49);
this._controlPanel.Name = "_controlPanel";
this._controlPanel.Size = new System.Drawing.Size(250, 729);
this._controlPanel.TabIndex = 3;
//
// _statusGroup
//
this._statusGroup.Controls.Add(this._pathLengthLabel);
this._statusGroup.Controls.Add(this._agvCountLabel);
this._statusGroup.Controls.Add(this._simulationStatusLabel);
this._statusGroup.Location = new System.Drawing.Point(10, 356);
this._statusGroup.Name = "_statusGroup";
this._statusGroup.Size = new System.Drawing.Size(230, 100);
this._statusGroup.TabIndex = 3;
this._statusGroup.TabStop = false;
this._statusGroup.Text = "상태 정보";
//
// _pathLengthLabel
//
this._pathLengthLabel.AutoSize = true;
this._pathLengthLabel.Location = new System.Drawing.Point(10, 65);
this._pathLengthLabel.Name = "_pathLengthLabel";
this._pathLengthLabel.Size = new System.Drawing.Size(71, 12);
this._pathLengthLabel.TabIndex = 2;
this._pathLengthLabel.Text = "경로 길이: -";
//
// _agvCountLabel
//
this._agvCountLabel.AutoSize = true;
this._agvCountLabel.Location = new System.Drawing.Point(10, 45);
this._agvCountLabel.Name = "_agvCountLabel";
this._agvCountLabel.Size = new System.Drawing.Size(60, 12);
this._agvCountLabel.TabIndex = 1;
this._agvCountLabel.Text = "AGV 수: 0";
//
// _simulationStatusLabel
//
this._simulationStatusLabel.AutoSize = true;
this._simulationStatusLabel.Location = new System.Drawing.Point(10, 25);
this._simulationStatusLabel.Name = "_simulationStatusLabel";
this._simulationStatusLabel.Size = new System.Drawing.Size(97, 12);
this._simulationStatusLabel.TabIndex = 0;
this._simulationStatusLabel.Text = "시뮬레이션: 정지";
//
// _pathGroup
//
this._pathGroup.Controls.Add(this._clearPathButton);
this._pathGroup.Controls.Add(this._startPathButton);
this._pathGroup.Controls.Add(this._calculatePathButton);
this._pathGroup.Controls.Add(this._targetNodeCombo);
this._pathGroup.Controls.Add(this.targetNodeLabel);
this._pathGroup.Controls.Add(this._startNodeCombo);
this._pathGroup.Controls.Add(this.startNodeLabel);
this._pathGroup.Location = new System.Drawing.Point(10, 200);
this._pathGroup.Name = "_pathGroup";
this._pathGroup.Size = new System.Drawing.Size(230, 150);
this._pathGroup.TabIndex = 1;
this._pathGroup.TabStop = false;
this._pathGroup.Text = "경로 제어";
//
// _clearPathButton
//
this._clearPathButton.Location = new System.Drawing.Point(150, 120);
this._clearPathButton.Name = "_clearPathButton";
this._clearPathButton.Size = new System.Drawing.Size(70, 25);
this._clearPathButton.TabIndex = 6;
this._clearPathButton.Text = "경로 지우기";
this._clearPathButton.UseVisualStyleBackColor = true;
this._clearPathButton.Click += new System.EventHandler(this.OnClearPath_Click);
//
// _startPathButton
//
this._startPathButton.Location = new System.Drawing.Point(80, 120);
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, 120);
this._calculatePathButton.Name = "_calculatePathButton";
this._calculatePathButton.Size = new System.Drawing.Size(65, 25);
this._calculatePathButton.TabIndex = 4;
this._calculatePathButton.Text = "경로 계산";
this._calculatePathButton.UseVisualStyleBackColor = true;
this._calculatePathButton.Click += new System.EventHandler(this.OnCalculatePath_Click);
//
// _targetNodeCombo
//
this._targetNodeCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this._targetNodeCombo.Location = new System.Drawing.Point(10, 95);
this._targetNodeCombo.Name = "_targetNodeCombo";
this._targetNodeCombo.Size = new System.Drawing.Size(210, 20);
this._targetNodeCombo.TabIndex = 3;
//
// targetNodeLabel
//
this.targetNodeLabel.AutoSize = true;
this.targetNodeLabel.Location = new System.Drawing.Point(10, 75);
this.targetNodeLabel.Name = "targetNodeLabel";
this.targetNodeLabel.Size = new System.Drawing.Size(63, 12);
this.targetNodeLabel.TabIndex = 2;
this.targetNodeLabel.Text = "목표 RFID:";
//
// _startNodeCombo
//
this._startNodeCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this._startNodeCombo.Location = new System.Drawing.Point(10, 45);
this._startNodeCombo.Name = "_startNodeCombo";
this._startNodeCombo.Size = new System.Drawing.Size(210, 20);
this._startNodeCombo.TabIndex = 1;
//
// startNodeLabel
//
this.startNodeLabel.AutoSize = true;
this.startNodeLabel.Location = new System.Drawing.Point(10, 25);
this.startNodeLabel.Name = "startNodeLabel";
this.startNodeLabel.Size = new System.Drawing.Size(63, 12);
this.startNodeLabel.TabIndex = 0;
this.startNodeLabel.Text = "시작 RFID:";
//
// _agvControlGroup
//
this._agvControlGroup.Controls.Add(this._setPositionButton);
this._agvControlGroup.Controls.Add(this._rfidTextBox);
this._agvControlGroup.Controls.Add(this._rfidLabel);
this._agvControlGroup.Controls.Add(this._stopSimulationButton);
this._agvControlGroup.Controls.Add(this._startSimulationButton);
this._agvControlGroup.Controls.Add(this._removeAgvButton);
this._agvControlGroup.Controls.Add(this._addAgvButton);
this._agvControlGroup.Controls.Add(this._agvListCombo);
this._agvControlGroup.Location = new System.Drawing.Point(10, 10);
this._agvControlGroup.Name = "_agvControlGroup";
this._agvControlGroup.Size = new System.Drawing.Size(230, 180);
this._agvControlGroup.TabIndex = 0;
this._agvControlGroup.TabStop = false;
this._agvControlGroup.Text = "AGV 제어";
//
// _setPositionButton
//
this._setPositionButton.Location = new System.Drawing.Point(160, 138);
this._setPositionButton.Name = "_setPositionButton";
this._setPositionButton.Size = new System.Drawing.Size(60, 25);
this._setPositionButton.TabIndex = 7;
this._setPositionButton.Text = "위치설정";
this._setPositionButton.UseVisualStyleBackColor = true;
this._setPositionButton.Click += new System.EventHandler(this.OnSetPosition_Click);
//
// _rfidTextBox
//
this._rfidTextBox.Location = new System.Drawing.Point(10, 140);
this._rfidTextBox.Name = "_rfidTextBox";
this._rfidTextBox.Size = new System.Drawing.Size(140, 21);
this._rfidTextBox.TabIndex = 6;
this._rfidTextBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.OnRfidTextBox_KeyPress);
//
// _rfidLabel
//
this._rfidLabel.AutoSize = true;
this._rfidLabel.Location = new System.Drawing.Point(10, 120);
this._rfidLabel.Name = "_rfidLabel";
this._rfidLabel.Size = new System.Drawing.Size(87, 12);
this._rfidLabel.TabIndex = 5;
this._rfidLabel.Text = "RFID 현재위치:";
//
// _stopSimulationButton
//
this._stopSimulationButton.Location = new System.Drawing.Point(120, 85);
this._stopSimulationButton.Name = "_stopSimulationButton";
this._stopSimulationButton.Size = new System.Drawing.Size(100, 25);
this._stopSimulationButton.TabIndex = 4;
this._stopSimulationButton.Text = "시뮬레이션 정지";
this._stopSimulationButton.UseVisualStyleBackColor = true;
this._stopSimulationButton.Click += new System.EventHandler(this.OnStopSimulation_Click);
//
// _startSimulationButton
//
this._startSimulationButton.Location = new System.Drawing.Point(10, 85);
this._startSimulationButton.Name = "_startSimulationButton";
this._startSimulationButton.Size = new System.Drawing.Size(100, 25);
this._startSimulationButton.TabIndex = 3;
this._startSimulationButton.Text = "시뮬레이션 시작";
this._startSimulationButton.UseVisualStyleBackColor = true;
this._startSimulationButton.Click += new System.EventHandler(this.OnStartSimulation_Click);
//
// _removeAgvButton
//
this._removeAgvButton.Location = new System.Drawing.Point(120, 55);
this._removeAgvButton.Name = "_removeAgvButton";
this._removeAgvButton.Size = new System.Drawing.Size(100, 25);
this._removeAgvButton.TabIndex = 2;
this._removeAgvButton.Text = "AGV 제거";
this._removeAgvButton.UseVisualStyleBackColor = true;
this._removeAgvButton.Click += new System.EventHandler(this.OnRemoveAGV_Click);
//
// _addAgvButton
//
this._addAgvButton.Location = new System.Drawing.Point(10, 55);
this._addAgvButton.Name = "_addAgvButton";
this._addAgvButton.Size = new System.Drawing.Size(100, 25);
this._addAgvButton.TabIndex = 1;
this._addAgvButton.Text = "AGV 추가";
this._addAgvButton.UseVisualStyleBackColor = true;
this._addAgvButton.Click += new System.EventHandler(this.OnAddAGV_Click);
//
// _agvListCombo
//
this._agvListCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this._agvListCombo.Location = new System.Drawing.Point(10, 25);
this._agvListCombo.Name = "_agvListCombo";
this._agvListCombo.Size = new System.Drawing.Size(210, 20);
this._agvListCombo.TabIndex = 0;
this._agvListCombo.SelectedIndexChanged += new System.EventHandler(this.OnAGVList_SelectedIndexChanged);
//
// _canvasPanel
//
this._canvasPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this._canvasPanel.Location = new System.Drawing.Point(0, 49);
this._canvasPanel.Name = "_canvasPanel";
this._canvasPanel.Size = new System.Drawing.Size(950, 729);
this._canvasPanel.TabIndex = 4;
//
// btAllReset
//
this.btAllReset.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.btAllReset.Name = "btAllReset";
this.btAllReset.Size = new System.Drawing.Size(71, 22);
this.btAllReset.Text = "전체초기화";
this.btAllReset.ToolTipText = "시뮬레이션을 초기화합니다";
this.btAllReset.Click += new System.EventHandler(this.btAllReset_Click);
//
// SimulatorForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1200, 800);
this.Controls.Add(this._canvasPanel);
this.Controls.Add(this._controlPanel);
this.Controls.Add(this._statusStrip);
this.Controls.Add(this._toolStrip);
this.Controls.Add(this._menuStrip);
this.MainMenuStrip = this._menuStrip;
this.Name = "SimulatorForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "AGV 시뮬레이터";
this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
this._menuStrip.ResumeLayout(false);
this._menuStrip.PerformLayout();
this._toolStrip.ResumeLayout(false);
this._toolStrip.PerformLayout();
this._statusStrip.ResumeLayout(false);
this._statusStrip.PerformLayout();
this._controlPanel.ResumeLayout(false);
this._statusGroup.ResumeLayout(false);
this._statusGroup.PerformLayout();
this._pathGroup.ResumeLayout(false);
this._pathGroup.PerformLayout();
this._agvControlGroup.ResumeLayout(false);
this._agvControlGroup.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.MenuStrip _menuStrip;
private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem openMapToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem simulationToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem startSimulationToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem stopSimulationToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem resetToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem viewToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem fitToMapToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem resetZoomToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem helpToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem;
private System.Windows.Forms.ToolStrip _toolStrip;
private System.Windows.Forms.ToolStripButton openMapToolStripButton;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
private System.Windows.Forms.ToolStripButton startSimulationToolStripButton;
private System.Windows.Forms.ToolStripButton stopSimulationToolStripButton;
private System.Windows.Forms.ToolStripButton resetToolStripButton;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator3;
private System.Windows.Forms.ToolStripButton fitToMapToolStripButton;
private System.Windows.Forms.ToolStripButton resetZoomToolStripButton;
private System.Windows.Forms.StatusStrip _statusStrip;
private System.Windows.Forms.ToolStripStatusLabel _statusLabel;
private System.Windows.Forms.ToolStripStatusLabel _coordLabel;
private System.Windows.Forms.Panel _controlPanel;
private System.Windows.Forms.GroupBox _agvControlGroup;
private System.Windows.Forms.ComboBox _agvListCombo;
private System.Windows.Forms.Button _addAgvButton;
private System.Windows.Forms.Button _removeAgvButton;
private System.Windows.Forms.Button _startSimulationButton;
private System.Windows.Forms.Button _stopSimulationButton;
private System.Windows.Forms.GroupBox _pathGroup;
private System.Windows.Forms.Label startNodeLabel;
private System.Windows.Forms.ComboBox _startNodeCombo;
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.GroupBox _statusGroup;
private System.Windows.Forms.Label _simulationStatusLabel;
private System.Windows.Forms.Label _agvCountLabel;
private System.Windows.Forms.Label _pathLengthLabel;
private System.Windows.Forms.Panel _canvasPanel;
private System.Windows.Forms.Label _rfidLabel;
private System.Windows.Forms.TextBox _rfidTextBox;
private System.Windows.Forms.Button _setPositionButton;
private System.Windows.Forms.ToolStripButton btAllReset;
private System.Windows.Forms.ToolStripMenuItem reloadMapToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem launchMapEditorToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator4;
private System.Windows.Forms.ToolStripButton reloadMapToolStripButton;
private System.Windows.Forms.ToolStripButton launchMapEditorToolStripButton;
}
}

View File

@@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using AGVMapEditor.Models;
using AGVSimulator.Controls;
using AGVNavigationCore.Models;
using AGVNavigationCore.Controls;
using AGVSimulator.Models;
using Newtonsoft.Json;
@@ -18,50 +20,17 @@ namespace AGVSimulator.Forms
{
#region Fields
private SimulatorCanvas _simulatorCanvas;
private UnifiedAGVCanvas _simulatorCanvas;
private List<MapNode> _mapNodes;
private List<RfidMapping> _rfidMappings;
private NodeResolver _nodeResolver;
private PathCalculator _pathCalculator;
private List<VirtualAGV> _agvList;
private SimulationState _simulationState;
private Timer _simulationTimer;
private SimulatorConfig _config;
private string _currentMapFilePath;
// UI Controls
private MenuStrip _menuStrip;
private ToolStrip _toolStrip;
private StatusStrip _statusStrip;
private Panel _controlPanel;
private Panel _canvasPanel;
// Control Panel Controls
private GroupBox _agvControlGroup;
private ComboBox _agvListCombo;
private Button _addAgvButton;
private Button _removeAgvButton;
private Button _startSimulationButton;
private Button _stopSimulationButton;
private Button _resetButton;
private GroupBox _pathGroup;
private ComboBox _startNodeCombo;
private ComboBox _targetNodeCombo;
private Button _calculatePathButton;
private Button _startPathButton;
private Button _clearPathButton;
private GroupBox _viewGroup;
private Button _fitToMapButton;
private Button _resetZoomButton;
private GroupBox _statusGroup;
private Label _simulationStatusLabel;
private Label _agvCountLabel;
private Label _pathLengthLabel;
// Status Labels
private ToolStripStatusLabel _statusLabel;
private ToolStripStatusLabel _coordLabel;
// UI Controls - Designer에서 생성됨
#endregion
@@ -88,27 +57,18 @@ namespace AGVSimulator.Forms
private void InitializeForm()
{
// 설정
Text = "AGV 시뮬레이터";
Size = new Size(1200, 800);
StartPosition = FormStartPosition.CenterScreen;
// 설정 로드
_config = SimulatorConfig.Load();
// 데이터 초기화
_mapNodes = new List<MapNode>();
_rfidMappings = new List<RfidMapping>();
_agvList = new List<VirtualAGV>();
_simulationState = new SimulationState();
_currentMapFilePath = string.Empty;
// UI 컨트롤 생성
CreateMenuStrip();
CreateToolStrip();
CreateStatusStrip();
CreateControlPanel();
// 시뮬레이터 캔버스 생성 (중앙 패널에만)
CreateSimulatorCanvas();
// 레이아웃 설정
SetupLayout();
// 타이머 초기화
_simulationTimer = new Timer();
_simulationTimer.Interval = 100; // 100ms 간격
@@ -118,260 +78,21 @@ namespace AGVSimulator.Forms
UpdateUI();
}
private void CreateMenuStrip()
{
_menuStrip = new MenuStrip();
// 파일 메뉴
var fileMenu = new ToolStripMenuItem("파일(&F)");
fileMenu.DropDownItems.Add(new ToolStripMenuItem("맵 열기(&O)...", null, OnOpenMap_Click) { ShortcutKeys = Keys.Control | Keys.O });
fileMenu.DropDownItems.Add(new ToolStripSeparator());
fileMenu.DropDownItems.Add(new ToolStripMenuItem("종료(&X)", null, OnExit_Click) { ShortcutKeys = Keys.Alt | Keys.F4 });
// 시뮬레이션 메뉴
var simMenu = new ToolStripMenuItem("시뮬레이션(&S)");
simMenu.DropDownItems.Add(new ToolStripMenuItem("시작(&S)", null, OnStartSimulation_Click) { ShortcutKeys = Keys.F5 });
simMenu.DropDownItems.Add(new ToolStripMenuItem("정지(&T)", null, OnStopSimulation_Click) { ShortcutKeys = Keys.F6 });
simMenu.DropDownItems.Add(new ToolStripMenuItem("초기화(&R)", null, OnReset_Click) { ShortcutKeys = Keys.F7 });
// 보기 메뉴
var viewMenu = new ToolStripMenuItem("보기(&V)");
viewMenu.DropDownItems.Add(new ToolStripMenuItem("맵 맞춤(&F)", null, OnFitToMap_Click) { ShortcutKeys = Keys.Control | Keys.F });
viewMenu.DropDownItems.Add(new ToolStripMenuItem("줌 초기화(&Z)", null, OnResetZoom_Click) { ShortcutKeys = Keys.Control | Keys.D0 });
// 도움말 메뉴
var helpMenu = new ToolStripMenuItem("도움말(&H)");
helpMenu.DropDownItems.Add(new ToolStripMenuItem("정보(&A)...", null, OnAbout_Click));
_menuStrip.Items.AddRange(new ToolStripItem[] { fileMenu, simMenu, viewMenu, helpMenu });
Controls.Add(_menuStrip);
MainMenuStrip = _menuStrip;
}
private void CreateToolStrip()
{
_toolStrip = new ToolStrip();
_toolStrip.Items.Add(new ToolStripButton("맵 열기", null, OnOpenMap_Click) { ToolTipText = "맵 파일을 엽니다" });
_toolStrip.Items.Add(new ToolStripSeparator());
_toolStrip.Items.Add(new ToolStripButton("시뮬레이션 시작", null, OnStartSimulation_Click) { ToolTipText = "시뮬레이션을 시작합니다" });
_toolStrip.Items.Add(new ToolStripButton("시뮬레이션 정지", null, OnStopSimulation_Click) { ToolTipText = "시뮬레이션을 정지합니다" });
_toolStrip.Items.Add(new ToolStripButton("초기화", null, OnReset_Click) { ToolTipText = "시뮬레이션을 초기화합니다" });
_toolStrip.Items.Add(new ToolStripSeparator());
_toolStrip.Items.Add(new ToolStripButton("맵 맞춤", null, OnFitToMap_Click) { ToolTipText = "맵 전체를 화면에 맞춥니다" });
_toolStrip.Items.Add(new ToolStripButton("줌 초기화", null, OnResetZoom_Click) { ToolTipText = "줌을 초기화합니다" });
Controls.Add(_toolStrip);
}
private void CreateStatusStrip()
{
_statusStrip = new StatusStrip();
_statusLabel = new ToolStripStatusLabel("준비");
_coordLabel = new ToolStripStatusLabel();
_statusStrip.Items.AddRange(new ToolStripItem[] { _statusLabel, _coordLabel });
Controls.Add(_statusStrip);
}
private void CreateControlPanel()
{
_controlPanel = new Panel();
_controlPanel.Width = 250;
_controlPanel.Dock = DockStyle.Right;
_controlPanel.BackColor = SystemColors.Control;
// AGV 제어 그룹
CreateAGVControlGroup();
// 경로 제어 그룹
CreatePathControlGroup();
// 뷰 제어 그룹
CreateViewControlGroup();
// 상태 그룹
CreateStatusGroup();
Controls.Add(_controlPanel);
}
private void CreateAGVControlGroup()
{
_agvControlGroup = new GroupBox();
_agvControlGroup.Text = "AGV 제어";
_agvControlGroup.Location = new Point(10, 10);
_agvControlGroup.Size = new Size(230, 120);
_agvListCombo = new ComboBox();
_agvListCombo.DropDownStyle = ComboBoxStyle.DropDownList;
_agvListCombo.Location = new Point(10, 25);
_agvListCombo.Size = new Size(210, 21);
_agvListCombo.SelectedIndexChanged += OnAGVList_SelectedIndexChanged;
_addAgvButton = new Button();
_addAgvButton.Text = "AGV 추가";
_addAgvButton.Location = new Point(10, 55);
_addAgvButton.Size = new Size(100, 25);
_addAgvButton.Click += OnAddAGV_Click;
_removeAgvButton = new Button();
_removeAgvButton.Text = "AGV 제거";
_removeAgvButton.Location = new Point(120, 55);
_removeAgvButton.Size = new Size(100, 25);
_removeAgvButton.Click += OnRemoveAGV_Click;
_startSimulationButton = new Button();
_startSimulationButton.Text = "시뮬레이션 시작";
_startSimulationButton.Location = new Point(10, 85);
_startSimulationButton.Size = new Size(100, 25);
_startSimulationButton.Click += OnStartSimulation_Click;
_stopSimulationButton = new Button();
_stopSimulationButton.Text = "시뮬레이션 정지";
_stopSimulationButton.Location = new Point(120, 85);
_stopSimulationButton.Size = new Size(100, 25);
_stopSimulationButton.Click += OnStopSimulation_Click;
_agvControlGroup.Controls.AddRange(new Control[] {
_agvListCombo, _addAgvButton, _removeAgvButton, _startSimulationButton, _stopSimulationButton
});
_controlPanel.Controls.Add(_agvControlGroup);
}
private void CreatePathControlGroup()
{
_pathGroup = new GroupBox();
_pathGroup.Text = "경로 제어";
_pathGroup.Location = new Point(10, 140);
_pathGroup.Size = new Size(230, 150);
var startLabel = new Label();
startLabel.Text = "시작 노드:";
startLabel.Location = new Point(10, 25);
startLabel.Size = new Size(70, 15);
_startNodeCombo = new ComboBox();
_startNodeCombo.DropDownStyle = ComboBoxStyle.DropDownList;
_startNodeCombo.Location = new Point(10, 45);
_startNodeCombo.Size = new Size(210, 21);
var targetLabel = new Label();
targetLabel.Text = "목표 노드:";
targetLabel.Location = new Point(10, 75);
targetLabel.Size = new Size(70, 15);
_targetNodeCombo = new ComboBox();
_targetNodeCombo.DropDownStyle = ComboBoxStyle.DropDownList;
_targetNodeCombo.Location = new Point(10, 95);
_targetNodeCombo.Size = new Size(210, 21);
_calculatePathButton = new Button();
_calculatePathButton.Text = "경로 계산";
_calculatePathButton.Location = new Point(10, 120);
_calculatePathButton.Size = new Size(65, 25);
_calculatePathButton.Click += OnCalculatePath_Click;
_startPathButton = new Button();
_startPathButton.Text = "경로 시작";
_startPathButton.Location = new Point(80, 120);
_startPathButton.Size = new Size(65, 25);
_startPathButton.Click += OnStartPath_Click;
_clearPathButton = new Button();
_clearPathButton.Text = "경로 지우기";
_clearPathButton.Location = new Point(150, 120);
_clearPathButton.Size = new Size(70, 25);
_clearPathButton.Click += OnClearPath_Click;
_pathGroup.Controls.AddRange(new Control[] {
startLabel, _startNodeCombo, targetLabel, _targetNodeCombo,
_calculatePathButton, _startPathButton, _clearPathButton
});
_controlPanel.Controls.Add(_pathGroup);
}
private void CreateViewControlGroup()
{
_viewGroup = new GroupBox();
_viewGroup.Text = "화면 제어";
_viewGroup.Location = new Point(10, 300);
_viewGroup.Size = new Size(230, 60);
_fitToMapButton = new Button();
_fitToMapButton.Text = "맵 맞춤";
_fitToMapButton.Location = new Point(10, 25);
_fitToMapButton.Size = new Size(100, 25);
_fitToMapButton.Click += OnFitToMap_Click;
_resetZoomButton = new Button();
_resetZoomButton.Text = "줌 초기화";
_resetZoomButton.Location = new Point(120, 25);
_resetZoomButton.Size = new Size(100, 25);
_resetZoomButton.Click += OnResetZoom_Click;
_resetButton = new Button();
_resetButton.Text = "전체 초기화";
_resetButton.Location = new Point(65, 55);
_resetButton.Size = new Size(100, 25);
_resetButton.Click += OnReset_Click;
_viewGroup.Controls.AddRange(new Control[] { _fitToMapButton, _resetZoomButton });
_controlPanel.Controls.Add(_viewGroup);
}
private void CreateStatusGroup()
{
_statusGroup = new GroupBox();
_statusGroup.Text = "상태 정보";
_statusGroup.Location = new Point(10, 370);
_statusGroup.Size = new Size(230, 100);
_simulationStatusLabel = new Label();
_simulationStatusLabel.Text = "시뮬레이션: 정지";
_simulationStatusLabel.Location = new Point(10, 25);
_simulationStatusLabel.Size = new Size(210, 15);
_agvCountLabel = new Label();
_agvCountLabel.Text = "AGV 수: 0";
_agvCountLabel.Location = new Point(10, 45);
_agvCountLabel.Size = new Size(210, 15);
_pathLengthLabel = new Label();
_pathLengthLabel.Text = "경로 길이: -";
_pathLengthLabel.Location = new Point(10, 65);
_pathLengthLabel.Size = new Size(210, 15);
_statusGroup.Controls.AddRange(new Control[] {
_simulationStatusLabel, _agvCountLabel, _pathLengthLabel
});
_controlPanel.Controls.Add(_statusGroup);
}
private void CreateSimulatorCanvas()
{
_canvasPanel = new Panel();
_canvasPanel.Dock = DockStyle.Fill;
_simulatorCanvas = new SimulatorCanvas();
_simulatorCanvas = new UnifiedAGVCanvas();
_simulatorCanvas.Dock = DockStyle.Fill;
_simulatorCanvas.Mode = UnifiedAGVCanvas.CanvasMode.ViewOnly;
_canvasPanel.Controls.Add(_simulatorCanvas);
Controls.Add(_canvasPanel);
}
private void SetupLayout()
{
// Z-Order 설정
// Z-Order 설정 - 모든 컨트롤이 디자이너에 구현되어 자동 관리됨
_canvasPanel.BringToFront();
_controlPanel.BringToFront();
_toolStrip.BringToFront();
_menuStrip.BringToFront();
}
#endregion
@@ -382,7 +103,7 @@ namespace AGVSimulator.Forms
{
using (var openDialog = new OpenFileDialog())
{
openDialog.Filter = "맵 파일 (*.json)|*.json|모든 파일 (*.*)|*.*";
openDialog.Filter = "AGV Map Files (*.agvmap)|*.agvmap|모든 파일 (*.*)|*.*";
openDialog.Title = "맵 파일 열기";
if (openDialog.ShowDialog() == DialogResult.OK)
@@ -430,30 +151,12 @@ namespace AGVSimulator.Forms
private void OnReset_Click(object sender, EventArgs e)
{
// 시뮬레이션 정지
if (_simulationState.IsRunning)
{
OnStopSimulation_Click(sender, e);
}
// AGV 초기화
_simulatorCanvas.ClearAGVs();
_agvList.Clear();
// 경로 초기화
_simulatorCanvas.CurrentPath = null;
// UI 업데이트
UpdateAGVComboBox();
UpdateNodeComboBoxes();
UpdateUI();
_statusLabel.Text = "초기화 완료";
}
private void OnFitToMap_Click(object sender, EventArgs e)
{
_simulatorCanvas.FitToMap();
_simulatorCanvas.FitToNodes();
}
private void OnResetZoom_Click(object sender, EventArgs e)
@@ -480,7 +183,7 @@ namespace AGVSimulator.Forms
var newAGV = new VirtualAGV(agvId, startPosition);
_agvList.Add(newAGV);
_simulatorCanvas.AddAGV(newAGV);
_simulatorCanvas.AGVList = new List<IAGV>(_agvList.Cast<IAGV>());
UpdateAGVComboBox();
UpdateUI();
@@ -496,8 +199,8 @@ namespace AGVSimulator.Forms
var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
if (selectedAGV != null)
{
_simulatorCanvas.RemoveAGV(selectedAGV.AgvId);
_agvList.Remove(selectedAGV);
_simulatorCanvas.AGVList = new List<IAGV>(_agvList.Cast<IAGV>());
UpdateAGVComboBox();
UpdateUI();
@@ -515,7 +218,7 @@ namespace AGVSimulator.Forms
{
if (_startNodeCombo.SelectedItem == null || _targetNodeCombo.SelectedItem == null)
{
MessageBox.Show("시작 노드와 목표 노드를 선택해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
MessageBox.Show("시작 RFID와 목표 RFID를 선택해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
@@ -524,20 +227,21 @@ namespace AGVSimulator.Forms
if (_pathCalculator == null)
{
_pathCalculator = new PathCalculator(_mapNodes, _nodeResolver);
_pathCalculator = new PathCalculator();
_pathCalculator.SetMapData(_mapNodes);
}
var result = _pathCalculator.CalculatePath(startNode.NodeId, targetNode.NodeId, AgvDirection.Forward);
var agvResult = _pathCalculator.FindAGVPath(startNode.NodeId, targetNode.NodeId);
if (result.Success)
if (agvResult.Success)
{
_simulatorCanvas.CurrentPath = result;
_pathLengthLabel.Text = $"경로 길이: {result.TotalDistance:F1}";
_statusLabel.Text = $"경로 계산 완료 ({result.CalculationTime}ms)";
_simulatorCanvas.CurrentPath = agvResult.ToPathResult();
_pathLengthLabel.Text = $"경로 길이: {agvResult.TotalDistance:F1}";
_statusLabel.Text = $"경로 계산 완료 ({agvResult.CalculationTimeMs}ms)";
}
else
{
MessageBox.Show($"경로를 찾을 수 없습니다:\n{result.ErrorMessage}", "경로 계산 실패",
MessageBox.Show($"경로를 찾을 수 없습니다:\n{agvResult.ErrorMessage}", "경로 계산 실패",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
@@ -568,6 +272,20 @@ namespace AGVSimulator.Forms
_statusLabel.Text = "경로 지움";
}
private void OnSetPosition_Click(object sender, EventArgs e)
{
SetAGVPositionByRfid();
}
private void OnRfidTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Enter)
{
SetAGVPositionByRfid();
e.Handled = true;
}
}
private void OnSimulationTimer_Tick(object sender, EventArgs e)
{
// 시뮬레이션 업데이트는 각 AGV의 내부 타이머에서 처리됨
@@ -578,51 +296,103 @@ namespace AGVSimulator.Forms
#region Private Methods
private void SetAGVPositionByRfid()
{
// 선택된 AGV 확인
var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
if (selectedAGV == null)
{
MessageBox.Show("먼저 AGV를 선택해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
// RFID 값 확인
var rfidId = _rfidTextBox.Text.Trim();
if (string.IsNullOrEmpty(rfidId))
{
MessageBox.Show("RFID 값을 입력해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
// RFID에 해당하는 노드 직접 찾기
var targetNode = _mapNodes?.FirstOrDefault(n => n.RfidId.Equals(rfidId, StringComparison.OrdinalIgnoreCase));
if (targetNode == null)
{
MessageBox.Show($"RFID '{rfidId}'에 해당하는 노드를 찾을 수 없습니다.\n\n사용 가능한 RFID 목록:\n{GetAvailableRfidList()}",
"RFID 찾기 실패", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
// AGV 위치 설정
_simulatorCanvas.SetAGVPosition(selectedAGV.AgvId, targetNode.Position);
_statusLabel.Text = $"{selectedAGV.AgvId} 위치를 RFID '{rfidId}' (노드: {targetNode.NodeId})로 설정했습니다.";
_rfidTextBox.Text = ""; // 입력 필드 초기화
// 시뮬레이터 캔버스의 해당 노드로 이동
_simulatorCanvas.PanToNode(targetNode.NodeId);
}
private string GetAvailableRfidList()
{
if (_mapNodes == null || _mapNodes.Count == 0)
return "매핑된 RFID가 없습니다.";
var nodesWithRfid = _mapNodes.Where(n => n.HasRfid()).ToList();
if (nodesWithRfid.Count == 0)
return "RFID가 할당된 노드가 없습니다.";
// 처음 10개의 RFID만 표시
var rfidList = nodesWithRfid.Take(10).Select(n => $"- {n.RfidId} → {n.NodeId}");
var result = string.Join("\n", rfidList);
if (nodesWithRfid.Count > 10)
result += $"\n... 외 {nodesWithRfid.Count - 10}개";
return result;
}
private void LoadMapFile(string filePath)
{
try
{
var json = File.ReadAllText(filePath);
var result = MapLoader.LoadMapFromFile(filePath);
// 구조체로 직접 역직렬화
var mapData = JsonConvert.DeserializeObject<MapFileData>(json);
if (mapData != null)
if (result.Success)
{
_mapNodes = mapData.MapNodes ?? new List<MapNode>();
_rfidMappings = mapData.RfidMappings ?? new List<RfidMapping>();
_mapNodes = result.Nodes;
_currentMapFilePath = filePath;
// RFID가 없는 노드들에 자동 할당
MapLoader.AssignAutoRfidIds(_mapNodes);
// 시뮬레이터 캔버스에 맵 설정
_simulatorCanvas.Nodes = _mapNodes;
// 설정에 마지막 맵 파일 경로 저장
_config.LastMapFilePath = filePath;
if (_config.AutoSave)
{
_config.Save();
}
// UI 업데이트
UpdateNodeComboBoxes();
UpdateUI();
// 맵에 맞춤
_simulatorCanvas.FitToNodes();
}
else
{
_mapNodes = new List<MapNode>();
_rfidMappings = new List<RfidMapping>();
throw new InvalidOperationException($"맵 파일 로드 실패: {result.ErrorMessage}");
}
// NodeResolver 초기화
_nodeResolver = new NodeResolver(_rfidMappings, _mapNodes);
// 시뮬레이터 캔버스에 맵 설정
_simulatorCanvas.MapNodes = _mapNodes;
// UI 업데이트
UpdateNodeComboBoxes();
UpdateUI();
// 맵에 맞춤
_simulatorCanvas.FitToMap();
}
catch (Exception ex)
{
throw new InvalidOperationException($"맵 파일 로드 실패: {ex.Message}", ex);
}
}
// 맵 파일 데이터 구조체
private class MapFileData
{
public List<MapNode> MapNodes { get; set; }
public List<RfidMapping> RfidMappings { get; set; }
}
private void UpdateNodeComboBoxes()
{
@@ -633,13 +403,16 @@ namespace AGVSimulator.Forms
{
foreach (var node in _mapNodes)
{
_startNodeCombo.Items.Add(node);
_targetNodeCombo.Items.Add(node);
if (node.IsActive && node.HasRfid())
{
_startNodeCombo.Items.Add(node);
_targetNodeCombo.Items.Add(node);
}
}
}
_startNodeCombo.DisplayMember = "NodeId";
_targetNodeCombo.DisplayMember = "NodeId";
_startNodeCombo.DisplayMember = "RfidId";
_targetNodeCombo.DisplayMember = "RfidId";
}
private void UpdateAGVComboBox()
@@ -681,8 +454,128 @@ namespace AGVSimulator.Forms
_calculatePathButton.Enabled = _startNodeCombo.SelectedItem != null &&
_targetNodeCombo.SelectedItem != null;
// RFID 위치 설정 관련
var hasSelectedAGV = _agvListCombo.SelectedItem != null;
var hasRfidNodes = _mapNodes != null && _mapNodes.Any(n => n.HasRfid());
_setPositionButton.Enabled = hasSelectedAGV && hasRfidNodes;
_rfidTextBox.Enabled = hasSelectedAGV && hasRfidNodes;
// 맵 다시열기 버튼
var hasCurrentMap = !string.IsNullOrEmpty(_currentMapFilePath);
reloadMapToolStripMenuItem.Enabled = hasCurrentMap;
reloadMapToolStripButton.Enabled = hasCurrentMap;
}
private void OnReloadMap_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(_currentMapFilePath))
{
MessageBox.Show("다시 로드할 맵 파일이 없습니다. 먼저 맵을 열어주세요.", "알림",
MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
if (!File.Exists(_currentMapFilePath))
{
MessageBox.Show($"맵 파일을 찾을 수 없습니다:\n{_currentMapFilePath}", "오류",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
try
{
LoadMapFile(_currentMapFilePath);
_statusLabel.Text = $"맵 다시 로드 완료: {Path.GetFileName(_currentMapFilePath)}";
}
catch (Exception ex)
{
MessageBox.Show($"맵 파일을 다시 로드할 수 없습니다:\n{ex.Message}", "오류",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void OnLaunchMapEditor_Click(object sender, EventArgs e)
{
try
{
// MapEditor 실행 파일 경로 확인
string mapEditorPath = _config.MapEditorExecutablePath;
// 경로가 설정되지 않았거나 파일이 없는 경우 사용자에게 선택을 요청
if (string.IsNullOrEmpty(mapEditorPath) || !File.Exists(mapEditorPath))
{
using (var openDialog = new OpenFileDialog())
{
openDialog.Filter = "실행 파일 (*.exe)|*.exe|모든 파일 (*.*)|*.*";
openDialog.Title = "AGV MapEditor 실행 파일 선택";
openDialog.InitialDirectory = Application.StartupPath;
if (openDialog.ShowDialog() == DialogResult.OK)
{
mapEditorPath = openDialog.FileName;
// 설정에 저장
_config.MapEditorExecutablePath = mapEditorPath;
if (_config.AutoSave)
{
_config.Save();
}
}
else
{
return; // 사용자가 취소함
}
}
}
// MapEditor 실행
var startInfo = new System.Diagnostics.ProcessStartInfo
{
FileName = mapEditorPath,
UseShellExecute = true
};
// 현재 로드된 맵 파일이 있으면 파라미터로 전달
if (!string.IsNullOrEmpty(_currentMapFilePath) && File.Exists(_currentMapFilePath))
{
startInfo.Arguments = $"\"{_currentMapFilePath}\"";
}
System.Diagnostics.Process.Start(startInfo);
_statusLabel.Text = "MapEditor 실행됨";
}
catch (Exception ex)
{
MessageBox.Show($"MapEditor를 실행할 수 없습니다:\n{ex.Message}", "오류",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
#endregion
private void btAllReset_Click(object sender, EventArgs e)
{
// 시뮬레이션 정지
if (_simulationState.IsRunning)
{
OnStopSimulation_Click(sender, e);
}
// AGV 초기화
_agvList.Clear();
_simulatorCanvas.AGVList = new List<IAGV>();
// 경로 초기화
_simulatorCanvas.CurrentPath = null;
// UI 업데이트
UpdateAGVComboBox();
UpdateNodeComboBoxes();
UpdateUI();
_statusLabel.Text = "초기화 완료";
}
}
}

View File

@@ -1,5 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
@@ -28,9 +87,9 @@
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
@@ -39,7 +98,7 @@
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -58,4 +117,13 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="_menuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="_toolStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>132, 17</value>
</metadata>
<metadata name="_statusStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>237, 17</value>
</metadata>
</root>

View File

@@ -0,0 +1,114 @@
using System;
using System.IO;
using Newtonsoft.Json;
namespace AGVSimulator.Models
{
/// <summary>
/// 시뮬레이터 환경 설정 클래스
/// </summary>
public class SimulatorConfig
{
#region Properties
/// <summary>
/// MapEditor 실행 파일 경로
/// </summary>
public string MapEditorExecutablePath { get; set; } = string.Empty;
/// <summary>
/// 마지막으로 로드한 맵 파일 경로
/// </summary>
public string LastMapFilePath { get; set; } = string.Empty;
/// <summary>
/// 설정 파일 자동 저장 여부
/// </summary>
public bool AutoSave { get; set; } = true;
#endregion
#region Static Methods
/// <summary>
/// 설정 파일 기본 경로
/// </summary>
private static string ConfigFilePath => Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"AGVSimulator",
"config.json");
/// <summary>
/// 설정을 파일에서 로드
/// </summary>
/// <returns>로드된 설정 객체</returns>
public static SimulatorConfig Load()
{
try
{
if (File.Exists(ConfigFilePath))
{
var json = File.ReadAllText(ConfigFilePath);
return JsonConvert.DeserializeObject<SimulatorConfig>(json) ?? new SimulatorConfig();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"설정 로드 실패: {ex.Message}");
}
return new SimulatorConfig();
}
/// <summary>
/// 설정을 파일에 저장
/// </summary>
/// <param name="config">저장할 설정 객체</param>
/// <returns>저장 성공 여부</returns>
public static bool Save(SimulatorConfig config)
{
try
{
var directory = Path.GetDirectoryName(ConfigFilePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
var json = JsonConvert.SerializeObject(config, Formatting.Indented);
File.WriteAllText(ConfigFilePath, json);
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"설정 저장 실패: {ex.Message}");
return false;
}
}
#endregion
#region Instance Methods
/// <summary>
/// 현재 설정을 저장
/// </summary>
/// <returns>저장 성공 여부</returns>
public bool Save()
{
return Save(this);
}
/// <summary>
/// MapEditor 실행 파일 경로 유효성 확인
/// </summary>
/// <returns>유효한 경로인지 여부</returns>
public bool IsMapEditorPathValid()
{
return !string.IsNullOrEmpty(MapEditorExecutablePath) &&
File.Exists(MapEditorExecutablePath);
}
#endregion
}
}

View File

@@ -3,27 +3,18 @@ using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using AGVMapEditor.Models;
using AGVNavigationCore.Models;
using AGVNavigationCore.PathFinding;
using AGVNavigationCore.Controls;
namespace AGVSimulator.Models
{
/// <summary>
/// 가상 AGV 상태
/// </summary>
public enum AGVState
{
Idle, // 대기
Moving, // 이동 중
Rotating, // 회전 중
Docking, // 도킹 중
Charging, // 충전 중
Error // 오류
}
/// <summary>
/// 가상 AGV 클래스
/// 실제 AGV의 동작을 시뮬레이션
/// </summary>
public class VirtualAGV
public class VirtualAGV : IAGV
{
#region Events
@@ -181,7 +172,7 @@ namespace AGVSimulator.Models
}
_currentPath = path;
_remainingNodes = new List<string>(path.NodeSequence);
_remainingNodes = new List<string>(path.Path);
_currentNodeIndex = 0;
// 시작 노드 위치로 이동

View File

@@ -0,0 +1,29 @@
@echo off
echo Building V2GDecoder VC++ Project...
REM Check if Visual Studio 2022 is installed (Professional or Community)
set MSBUILD_PRO="C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe"
set MSBUILD_COM="C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe"
set MSBUILD_BT="F:\(VHD) Program Files\Microsoft Visual Studio\2022\MSBuild\Current\Bin\MSBuild.exe"
if exist %MSBUILD_PRO% (
echo "Found Visual Studio 2022 Professional"
set MSBUILD=%MSBUILD_PRO%
) else if exist %MSBUILD_COM% (
echo "Found Visual Studio 2022 Community"
set MSBUILD=%MSBUILD_COM%
) else if exist %MSBUILD_BT% (
echo "Found Visual Studio 2022 BuildTools"
set MSBUILD=%MSBUILD_BT%
) else (
echo "Visual Studio 2022 (Professional or Community) not found!"
echo "Please install Visual Studio 2022 or update the MSBuild path."
pause
exit /b 1
)
REM Build Debug x64 configuration
echo Building Debug x64 configuration...
%MSBUILD% AGVSimulator.csproj
pause