From 471b8ff9c4cd3a670f63fc5d81f438b828e11245 Mon Sep 17 00:00:00 2001 From: backuppc Date: Tue, 10 Feb 2026 14:53:54 +0900 Subject: [PATCH] .. --- .../AGVMapEditor/Forms/MainForm.Designer.cs | 274 +++++---- AGVLogic/AGVMapEditor/Forms/MainForm.cs | 51 +- AGVLogic/AGVMapEditor/Forms/MainForm.resx | 159 ++--- .../AGVMapEditor/Properties/AssemblyInfo.cs | 4 +- .../Controls/UnifiedAGVCanvas.Events.cs | 49 +- .../Controls/UnifiedAGVCanvas.Mouse.cs | 166 ++++- .../Controls/UnifiedAGVCanvas.cs | 13 + .../AGVNavigationCore/Models/MapMagnet.cs | 16 +- AGVLogic/AGVNavigationCore/Models/MapMark.cs | 4 + .../Forms/SimulatorForm.Designer.cs | 23 +- AGVLogic/AGVSimulator/Forms/SimulatorForm.cs | 2 +- HMI/Project/StateMachine/_AGV.cs | 28 + HMI/Project/fMain.cs | 2 + HMI/SubProject/AGV/DataEventArgs.cs | 14 + HMI/SubProject/AGV/Narumi.cs | 3 +- HMI/TestProject/Test_ACS/MainForm.Designer.cs | 116 ++-- HMI/TestProject/Test_ACS/MainForm.cs | 30 +- NewMap.json | 575 +++++------------- 18 files changed, 786 insertions(+), 743 deletions(-) diff --git a/AGVLogic/AGVMapEditor/Forms/MainForm.Designer.cs b/AGVLogic/AGVMapEditor/Forms/MainForm.Designer.cs index 6da0e28..323576b 100644 --- a/AGVLogic/AGVMapEditor/Forms/MainForm.Designer.cs +++ b/AGVLogic/AGVMapEditor/Forms/MainForm.Designer.cs @@ -40,6 +40,16 @@ namespace AGVMapEditor.Forms this.lstNodeConnection = new System.Windows.Forms.ListBox(); this.toolStrip1 = new System.Windows.Forms.ToolStrip(); this.btNodeRemove = new System.Windows.Forms.ToolStripButton(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.lstMagnetDirection = new System.Windows.Forms.ListBox(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.button1 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); + this.toolStrip4 = new System.Windows.Forms.ToolStrip(); + this.btDirDelete = new System.Windows.Forms.ToolStripButton(); + this.btMakeDirdata = new System.Windows.Forms.ToolStripButton(); + this.toolStripButton2 = new System.Windows.Forms.ToolStripButton(); this._propertyGrid = new System.Windows.Forms.PropertyGrid(); this.panel1 = new System.Windows.Forms.Panel(); this.toolStrip3 = new System.Windows.Forms.ToolStrip(); @@ -67,16 +77,7 @@ namespace AGVMapEditor.Forms this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); this.toolStripButton1 = new System.Windows.Forms.ToolStripDropDownButton(); this.allTurnLeftRightCrossOnToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.tabPage2 = new System.Windows.Forms.TabPage(); - this.lstMagnetDirection = new System.Windows.Forms.ListBox(); - this.toolStrip4 = new System.Windows.Forms.ToolStrip(); - this.btDirDelete = new System.Windows.Forms.ToolStripButton(); - this.btMakeDirdata = new System.Windows.Forms.ToolStripButton(); - this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); - this.button1 = new System.Windows.Forms.Button(); - this.button2 = new System.Windows.Forms.Button(); - this.button3 = new System.Windows.Forms.Button(); - this.toolStripButton2 = new System.Windows.Forms.ToolStripButton(); + this.btAddMagnet = new System.Windows.Forms.ToolStripButton(); this.statusStrip1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); this.splitContainer1.Panel1.SuspendLayout(); @@ -86,11 +87,11 @@ namespace AGVMapEditor.Forms this.tabPageNodes.SuspendLayout(); this.tabPage1.SuspendLayout(); this.toolStrip1.SuspendLayout(); + this.tabPage2.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.toolStrip4.SuspendLayout(); this.toolStrip3.SuspendLayout(); this.toolStrip2.SuspendLayout(); - this.tabPage2.SuspendLayout(); - this.toolStrip4.SuspendLayout(); - this.tableLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // // statusStrip1 @@ -215,6 +216,120 @@ namespace AGVMapEditor.Forms this.btNodeRemove.Text = "Remove"; this.btNodeRemove.Click += new System.EventHandler(this.btNodeRemove_Click); // + // tabPage2 + // + this.tabPage2.Controls.Add(this.lstMagnetDirection); + this.tabPage2.Controls.Add(this.tableLayoutPanel1); + this.tabPage2.Controls.Add(this.toolStrip4); + this.tabPage2.Location = new System.Drawing.Point(4, 22); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Size = new System.Drawing.Size(292, 309); + this.tabPage2.TabIndex = 2; + this.tabPage2.Text = "방향 관리"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // lstMagnetDirection + // + this.lstMagnetDirection.Dock = System.Windows.Forms.DockStyle.Fill; + this.lstMagnetDirection.FormattingEnabled = true; + this.lstMagnetDirection.ItemHeight = 12; + this.lstMagnetDirection.Location = new System.Drawing.Point(0, 25); + this.lstMagnetDirection.Name = "lstMagnetDirection"; + this.lstMagnetDirection.Size = new System.Drawing.Size(292, 246); + this.lstMagnetDirection.TabIndex = 3; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 3; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); + this.tableLayoutPanel1.Controls.Add(this.button1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.button2, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.button3, 2, 0); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 271); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 38F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(292, 38); + this.tableLayoutPanel1.TabIndex = 6; + // + // button1 + // + this.button1.Dock = System.Windows.Forms.DockStyle.Fill; + this.button1.Location = new System.Drawing.Point(3, 3); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(91, 32); + this.button1.TabIndex = 0; + this.button1.Text = "Left"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // button2 + // + this.button2.Dock = System.Windows.Forms.DockStyle.Fill; + this.button2.Location = new System.Drawing.Point(100, 3); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(91, 32); + this.button2.TabIndex = 0; + this.button2.Text = "Straight"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // button3 + // + this.button3.Dock = System.Windows.Forms.DockStyle.Fill; + this.button3.Location = new System.Drawing.Point(197, 3); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(92, 32); + this.button3.TabIndex = 0; + this.button3.Text = "Right"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.button3_Click); + // + // toolStrip4 + // + this.toolStrip4.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.btDirDelete, + this.btMakeDirdata, + this.toolStripButton2}); + this.toolStrip4.Location = new System.Drawing.Point(0, 0); + this.toolStrip4.Name = "toolStrip4"; + this.toolStrip4.Size = new System.Drawing.Size(292, 25); + this.toolStrip4.TabIndex = 5; + this.toolStrip4.Text = "toolStrip4"; + // + // btDirDelete + // + this.btDirDelete.Image = ((System.Drawing.Image)(resources.GetObject("btDirDelete.Image"))); + this.btDirDelete.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btDirDelete.Name = "btDirDelete"; + this.btDirDelete.Size = new System.Drawing.Size(61, 22); + this.btDirDelete.Text = "Delete"; + this.btDirDelete.Click += new System.EventHandler(this.btDirDelete_Click); + // + // btMakeDirdata + // + this.btMakeDirdata.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; + this.btMakeDirdata.Image = ((System.Drawing.Image)(resources.GetObject("btMakeDirdata.Image"))); + this.btMakeDirdata.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btMakeDirdata.Name = "btMakeDirdata"; + this.btMakeDirdata.Size = new System.Drawing.Size(69, 22); + this.btMakeDirdata.Text = "Remake"; + this.btMakeDirdata.Click += new System.EventHandler(this.toolStripButton3_Click); + // + // toolStripButton2 + // + this.toolStripButton2.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; + this.toolStripButton2.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton2.Image"))); + this.toolStripButton2.ImageTransparentColor = System.Drawing.Color.Magenta; + this.toolStripButton2.Name = "toolStripButton2"; + this.toolStripButton2.Size = new System.Drawing.Size(54, 22); + this.toolStripButton2.Text = "Clear"; + this.toolStripButton2.Click += new System.EventHandler(this.toolStripButton2_Click); + // // _propertyGrid // this._propertyGrid.Dock = System.Windows.Forms.DockStyle.Bottom; @@ -244,7 +359,8 @@ namespace AGVMapEditor.Forms this.btnDeleteConnection, this.toolStripSeparator1, this.btnToggleGrid, - this.btnFitMap}); + this.btnFitMap, + this.btAddMagnet}); this.toolStrip3.Location = new System.Drawing.Point(0, 0); this.toolStrip3.Name = "toolStrip3"; this.toolStrip3.Size = new System.Drawing.Size(896, 25); @@ -458,119 +574,14 @@ namespace AGVMapEditor.Forms this.allTurnLeftRightCrossOnToolStripMenuItem.Text = "All TurnLeft/Right/Cross On"; this.allTurnLeftRightCrossOnToolStripMenuItem.Click += new System.EventHandler(this.allTurnLeftRightCrossOnToolStripMenuItem_Click); // - // tabPage2 + // btAddMagnet // - this.tabPage2.Controls.Add(this.lstMagnetDirection); - this.tabPage2.Controls.Add(this.tableLayoutPanel1); - this.tabPage2.Controls.Add(this.toolStrip4); - this.tabPage2.Location = new System.Drawing.Point(4, 22); - this.tabPage2.Name = "tabPage2"; - this.tabPage2.Size = new System.Drawing.Size(292, 309); - this.tabPage2.TabIndex = 2; - this.tabPage2.Text = "방향 관리"; - this.tabPage2.UseVisualStyleBackColor = true; - // - // lstMagnetDirection - // - this.lstMagnetDirection.Dock = System.Windows.Forms.DockStyle.Fill; - this.lstMagnetDirection.FormattingEnabled = true; - this.lstMagnetDirection.ItemHeight = 12; - this.lstMagnetDirection.Location = new System.Drawing.Point(0, 25); - this.lstMagnetDirection.Name = "lstMagnetDirection"; - this.lstMagnetDirection.Size = new System.Drawing.Size(292, 246); - this.lstMagnetDirection.TabIndex = 3; - // - // toolStrip4 - // - this.toolStrip4.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.btDirDelete, - this.btMakeDirdata, - this.toolStripButton2}); - this.toolStrip4.Location = new System.Drawing.Point(0, 0); - this.toolStrip4.Name = "toolStrip4"; - this.toolStrip4.Size = new System.Drawing.Size(292, 25); - this.toolStrip4.TabIndex = 5; - this.toolStrip4.Text = "toolStrip4"; - // - // btDirDelete - // - this.btDirDelete.Image = ((System.Drawing.Image)(resources.GetObject("btDirDelete.Image"))); - this.btDirDelete.ImageTransparentColor = System.Drawing.Color.Magenta; - this.btDirDelete.Name = "btDirDelete"; - this.btDirDelete.Size = new System.Drawing.Size(61, 22); - this.btDirDelete.Text = "Delete"; - this.btDirDelete.Click += new System.EventHandler(this.btDirDelete_Click); - // - // btMakeDirdata - // - this.btMakeDirdata.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; - this.btMakeDirdata.Image = ((System.Drawing.Image)(resources.GetObject("btMakeDirdata.Image"))); - this.btMakeDirdata.ImageTransparentColor = System.Drawing.Color.Magenta; - this.btMakeDirdata.Name = "btMakeDirdata"; - this.btMakeDirdata.Size = new System.Drawing.Size(69, 22); - this.btMakeDirdata.Text = "Remake"; - this.btMakeDirdata.Click += new System.EventHandler(this.toolStripButton3_Click); - // - // tableLayoutPanel1 - // - this.tableLayoutPanel1.ColumnCount = 3; - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); - this.tableLayoutPanel1.Controls.Add(this.button1, 0, 0); - this.tableLayoutPanel1.Controls.Add(this.button2, 1, 0); - this.tableLayoutPanel1.Controls.Add(this.button3, 2, 0); - this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Bottom; - this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 271); - this.tableLayoutPanel1.Name = "tableLayoutPanel1"; - this.tableLayoutPanel1.RowCount = 1; - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.Size = new System.Drawing.Size(292, 38); - this.tableLayoutPanel1.TabIndex = 6; - // - // button1 - // - this.button1.Dock = System.Windows.Forms.DockStyle.Fill; - this.button1.Location = new System.Drawing.Point(3, 3); - this.button1.Name = "button1"; - this.button1.Size = new System.Drawing.Size(91, 32); - this.button1.TabIndex = 0; - this.button1.Text = "Left"; - this.button1.UseVisualStyleBackColor = true; - this.button1.Click += new System.EventHandler(this.button1_Click); - // - // button2 - // - this.button2.Dock = System.Windows.Forms.DockStyle.Fill; - this.button2.Location = new System.Drawing.Point(100, 3); - this.button2.Name = "button2"; - this.button2.Size = new System.Drawing.Size(91, 32); - this.button2.TabIndex = 0; - this.button2.Text = "Straight"; - this.button2.UseVisualStyleBackColor = true; - this.button2.Click += new System.EventHandler(this.button2_Click); - // - // button3 - // - this.button3.Dock = System.Windows.Forms.DockStyle.Fill; - this.button3.Location = new System.Drawing.Point(197, 3); - this.button3.Name = "button3"; - this.button3.Size = new System.Drawing.Size(92, 32); - this.button3.TabIndex = 0; - this.button3.Text = "Right"; - this.button3.UseVisualStyleBackColor = true; - this.button3.Click += new System.EventHandler(this.button3_Click); - // - // toolStripButton2 - // - this.toolStripButton2.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; - this.toolStripButton2.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton2.Image"))); - this.toolStripButton2.ImageTransparentColor = System.Drawing.Color.Magenta; - this.toolStripButton2.Name = "toolStripButton2"; - this.toolStripButton2.Size = new System.Drawing.Size(54, 22); - this.toolStripButton2.Text = "Clear"; - this.toolStripButton2.Click += new System.EventHandler(this.toolStripButton2_Click); + this.btAddMagnet.Image = ((System.Drawing.Image)(resources.GetObject("btAddMagnet.Image"))); + this.btAddMagnet.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btAddMagnet.Name = "btAddMagnet"; + this.btAddMagnet.Size = new System.Drawing.Size(87, 22); + this.btAddMagnet.Text = "마그넷추가"; + this.btAddMagnet.Click += new System.EventHandler(this.btAddMagnet_Click); // // MainForm // @@ -599,15 +610,15 @@ namespace AGVMapEditor.Forms this.tabPage1.PerformLayout(); this.toolStrip1.ResumeLayout(false); this.toolStrip1.PerformLayout(); + this.tabPage2.ResumeLayout(false); + this.tabPage2.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.toolStrip4.ResumeLayout(false); + this.toolStrip4.PerformLayout(); this.toolStrip3.ResumeLayout(false); this.toolStrip3.PerformLayout(); this.toolStrip2.ResumeLayout(false); this.toolStrip2.PerformLayout(); - this.tabPage2.ResumeLayout(false); - this.tabPage2.PerformLayout(); - this.toolStrip4.ResumeLayout(false); - this.toolStrip4.PerformLayout(); - this.tableLayoutPanel1.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -663,5 +674,6 @@ namespace AGVMapEditor.Forms private System.Windows.Forms.Button button2; private System.Windows.Forms.Button button3; private System.Windows.Forms.ToolStripButton toolStripButton2; + private System.Windows.Forms.ToolStripButton btAddMagnet; } } \ No newline at end of file diff --git a/AGVLogic/AGVMapEditor/Forms/MainForm.cs b/AGVLogic/AGVMapEditor/Forms/MainForm.cs index f4ad0ad..810bb56 100644 --- a/AGVLogic/AGVMapEditor/Forms/MainForm.cs +++ b/AGVLogic/AGVMapEditor/Forms/MainForm.cs @@ -774,7 +774,7 @@ namespace AGVMapEditor.Forms private void RefreshNodeList() { listBoxNodes.DataSource = null; - listBoxNodes.DataSource = this._mapCanvas.Nodes; + listBoxNodes.DataSource = this._mapCanvas.Items; listBoxNodes.DisplayMember = "DisplayText"; listBoxNodes.ValueMember = "Id"; @@ -790,7 +790,7 @@ namespace AGVMapEditor.Forms private void ListBoxNodes_SelectedIndexChanged(object sender, EventArgs e) { - if (listBoxNodes.SelectedItem is MapNode selectedNode) + if (listBoxNodes.SelectedItem is NodeBase selectedNode) { _selectedNode = selectedNode; UpdateNodeProperties(); @@ -1673,5 +1673,52 @@ namespace AGVMapEditor.Forms // 현재 선택된 노드의 속성창 및 리스트 갱신 UpdateNodeProperties(); } + + private void btAddMagnet_Click(object sender, EventArgs e) + { + // 마그넷 추가 + var result = MessageBox.Show("곡선 마그넷(Bezier)을 추가하시겠습니까?\n(예: 베지어 곡선, 아니오: 직선, 취소: 중단)", + "마그넷 타입 선택", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); + + if (result == DialogResult.Cancel) return; + + bool isBezier = (result == DialogResult.Yes); + + // 화면 중앙 좌표 계산 (World Coordinate) + float zoom = _mapCanvas.ZoomFactor; + PointF pan = _mapCanvas.PanOffset; + + float worldCX = (_mapCanvas.Width / 2f) / zoom - pan.X; + float worldCY = (_mapCanvas.Height / 2f) / zoom - pan.Y; + + // 고유 ID 생성 + string id = _mapCanvas.GenerateUniqueNodeId(); + + var magnet = new MapMagnet { Id = id }; + + // 점 생성 시 정규화(Snap) 처리 + int cx = (int)worldCX; + int cy = (int)worldCY; + + magnet.StartPoint = new Point(cx - 50, cy); + magnet.EndPoint = new Point(cx + 50, cy); + + if (isBezier) + { + magnet.ControlPoint = new MapMagnet.MagnetPoint { X = cx, Y = cy - 50 }; + } + + // 캔버스에 추가 + _mapCanvas.Magnets.Add(magnet); + _hasChanges = true; + + UpdateTitle(); + RefreshMapCanvas(); + RefreshNodeList(); + + // 추가된 마그넷 선택 + //_mapCanvas.SelectedNode = magnet; + UpdateNodeProperties(); + } } } \ No newline at end of file diff --git a/AGVLogic/AGVMapEditor/Forms/MainForm.resx b/AGVLogic/AGVMapEditor/Forms/MainForm.resx index 8cc428a..1bc0a34 100644 --- a/AGVLogic/AGVMapEditor/Forms/MainForm.resx +++ b/AGVLogic/AGVMapEditor/Forms/MainForm.resx @@ -194,14 +194,14 @@ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHrSURBVDhPldBLaBNBHMfx/0kUVBBJ0lxWPIhihBJKyAqS - pHkQIS+9hXg3RhQviicrITnmJqFnQQ8RqiamRqkhj6VCQtuIQaVQc5di3d3s61Dy0w002KnU9nP67+x8 + pHkQIS+9hXg3RhQviicrITnmJqFnQQ8RqiamRqkhj6VCQtuIQaVQc7dg3d3s61Dy0w002KnU9nP67+x8 h2GIDmD0kT+mLk/fZNf3pQkznCrM3DFnZflSRG05euast7izcpM72GGqMP1ZFRw1tXm+qq9dg9LiHgwb dnFYP51i9/6T0r4wp39Kwfh2F8bGI2irEYjvTmo/Gpbj7N4JpXNxShUcdbV1DvpaHMb3HNrP4uiVb2Cj cQtadxbSh6OQ3tM82+6iNLk5rXcd7ecJGIaB0WiE1dcp6F9v41eNvmxV6QzbTMjtKYtct9Wi0Si63S50 XUe/30fjaQTG+n1IVRpKb4lnuzFtyc4Nl06VE4kE0uk0CoUCSqUSqvOzMNYfYnORtqVFWhEr9JhtJ+Lx +DjmeR5+vx+xWAzqSgRy3Q65dgJbFeLYZmIndrvd8Pl8sFqt5pWfbL6hbalCl6Uy9cSXlGG7sWQyiXw+ P469Xi8sFgvMdblCV6RXVDNnvKAjPxfoKttSOBxGLpfbE+8QFyj09/cugUAA2WwWLpcLHo9nT7yvTCaD - wWAAp9OJUCh0uNhkHtDpdFAsFscPxv7/r2AweM+8ts1mO3z8x29KrQsZMgRtMAAAAABJRU5ErkJggg== + wWAAp9OJUCh0uNhkHtDpdFAsFscPxv7/r2AweM+8ts1mO3z8x29G9wsX9Ki7DgAAAABJRU5ErkJggg== @@ -222,75 +222,75 @@ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHJSURBVDhPnZJfa9pgFMb7JXa7sW9RkH2y3awro5dbS7Et - u2vHRsRcBEw0QkTinyhB8e9MonOZ6NRZaME12YzyjPPKm+LcKtsDIbznPc/vOSRnr91uo9VqodFooF6v - o1aroVqtolKp0PvR3i41m02sVqutZzgcMoiu6w9DKJkMg8GAJbuuG0KoZprmwxAamZopjc7lcrkyn88R - BAGrE9AwjL9DKJU35vP5n4VCYeD7Pr59vUNBnbK7fr+PXC73Zwgl85GXyyXIfD31cXbo4Pi5jXxqDen1 - eshms1AUZRNimqY3m81Y02KxYMnRlw4uX/fx/tTF+WE3hDiOA03TNiGlUumZYRjedLpusuo3LPnq+DOE - 8y9bENu2oarqJqRYLEY0TfMmkwlr6rZuET2w8e7kHhI9cKAr6/tOpwNZliEIwj1E1/VIOp32xuPxFuRD - 1MXbox4ujj4iCJZsimQyiXg8/iQEkDKZTCSVSnmj0SiEnL6wcPGqi6uTLr7P/dAsCMLTDTOXqqoMQpvI - PlzzBpdvPuGHH+w2c8myHEkkEh5tIv/FZFYUZbeZS5KkfUmSPFowy7L+zcwliuK+KIref5m5CBKLxR7/ - Xuf6BYuvFpozmyYBAAAAAElFTkSuQmCC + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHHSURBVDhPnZJva9pQFMb7JfZ2Y9+iIPtke7OujL7sWopr + 2bt2bETMi4CJXlCROI0SFP/OJDqXiladgw7cks0ozzhXbopzq2wPhHDPOc/vOSR3r91uo9VqodFooF6v + o1aroVqtolKp0PvB3i41m02sVqutZzgccoiu6/dDKJkMg8GAJ7uuG0KoZprm/RBamYYpjc7lcrkyn88R + BAGvE9AwjL9DKFUM5vP5n4VCYeD7Pj7ffEeBTXmv3+9T788QShYrL5dLkPnL1MerQwcnT228T60hvV4P + uVwOmqZtQkzT9GazGR9aLBY8OfrcweVxH2/PXJwfdkOI4zjIZDKbkFKp9MQwDG86XQ9Z9VuefHXyCdL5 + 9RbEtm0wxjYhxWIxkk6nvclkwoe6ra+IHth4c3oHiR440LV1v9PpQFVVSJJ0B9F1PcIY88bj8RbkXdTF + 66MeLo4+IAiWfItkMol4PP4oBJCy2WwklUp5o9EohJw9s3Dxoour0y6+zf3QLEnS4w2zEGOMQ+gm8g/X + vMXly4/44Qe7zUKqqkYSiYRHN1H8YjJrmrbbLKQoyr6iKB5dMMuy/s0sJMvyvizL3n+ZhQgSi8Ue/l4X + +gV3kBaRV83skgAAAABJRU5ErkJggg== iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAG7SURBVDhPnZLditpQFIXnJXrb0rcYkD5YL4pMYS7bGUrp - O0TMRUp+hWRs/IkSFDUGfxARR6viRRmkJ+jJYZVzhpPB2o60C0LI3nt9a0P2RRzH6Pf76PV66Ha76HQ6 - aLfbaLVa/P3i4pyiKAJj7ORZLBYC4vv+8xCezA3z+Vwkz2azDMJrYRg+D+Er82Gexr+bzWZrt9uBUirq - HBgEwd8hPFUOVqvVfa1WmydJAvrjO2hsi950OkWlUvkzhCfLldM0BTezhw1S6wrp17egsSV6k8kE5XIZ - pmkeQ8IwJNvtVgwdDgeRnJp5MO8D2LdPYNZVBhmPx3Bd9xjSaDTeBEFANpvNI+S+I5LZ3Q1Y5csJZDQa - wXGcY0i9Xs+5rkvW6/UjZBmBGnmwu9sniJkHjXTRHwwGMAwDiqI8QXzfz5VKJbJarU4h/mcw5xo/jWuw - lIotLMtCsVh8lQG4PM/L2bZNlstlBjno78Ds90jcG+zJLjMrivL6yCzlOI6A8EsUkEUP1PsItk/Om6UM - w8jpuk74JcpfzM2maZ43S2madqlpGuEHNhwO/80sparqpaqq5L/MUhxSKBRe/l6X+gWA2x2MFEPZrwAA - AABJRU5ErkJggg== + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAG4SURBVDhPnZLditpQFIXnJXrb0rcYkD5YL4pMYS7bKaX0 + HSLmIiV/BzRjIxolKGoM/iAijlbFizLIJOjJYZV9hmSwtiPtghCy917f2pB9EYYh+v0+er0eut0uOp0O + 2u02Wq0WvV9cnFMQBBBCnDyLxUJCXNd9HkLJZJjP5zJ5NptlEKr5vv88hFamYUqj72az2drtduCcyzoB + Pc/7O4RS08Fqtbqv1WrzOI7Bf/4AD23Zm06n1PszhJLTlZMkAZnF/QaJdYXk21vw0JK9yWSCSqUC0zSP + Ib7vR9vtVg4dDgeZnJh5COcDxPfPENZVBhmPxyiXy8eQRqPxxvO8aLPZPELuOjJZ3N5AVL+eQEajERhj + x5B6vZ4rlUrRer1+hCwDcCMPcfvpCWLmwQNd9geDAQzDgKIoTxDXdXOMsWi1Wp1C3C8Q7BoPxjVEwuUW + lmWhWCy+ygAkx3Fytm1Hy+Uygxz0dxD2e8TlG+yjXWZWFOX1kTkVY0xC6BIlZNEDdz5C7OPz5lSGYeR0 + XY/oEtNfTGbTNM+bU2madqlpWkQHNhwO/82cSlXVS1VVo/8ypyJIoVB4+Xs91S9svB2DRfl8BwAAAABJ + RU5ErkJggg== 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 - ns8f9tyLJQW2uh8AAAAASUVORK5CYII= + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAL3SURBVDhPhdLfV9N1HMdx/oPuO93U6XTj6dRN3XasLC1P + lmlRHrfT4Vgno+PppA1HJGL6FRbjK4PD2IBvc6m0qSTDgeGmDgknfmEwQisMERZwYO67fbd95BA+O9up + pZx+vM55X33en8fnnM/7XaT4hmoVrzone1XN6vr/kj2q5jyt1hX9lZbOq/Mzsdg9PZ0mnRH/WYu6YD6R + ounEQKIA5F5O6Wkau6ap7YpQefZbXNdseEa+5HikioaQhQ/bHHza2oepdZyFlED2DmsFoM6lajm96ew0 + UncPrkEb7aO7cQ5+QNOVEuzhnVh6D7Cz+Rhm5Trz2j8AqYyguXuGSr+bE5Eq7OEdHPlhO5bQO0iXirGG + PsFY/xUVR39iTssie4ceBLSMwNkTY5/PxddqBXK/AeniW+wPbuaL85uRAqUY5GqUdjc3jhXzy5mDRFpf + ItK4xpAHEmlB67nfkHx+bKHD1PWXUBl8E/P3m9gXKObz78poPLqH2aAJbbQL9BiJ0ZOEatbP5YE7ukDp + ncXmH6HMoyB17qImsAOp9z3KvR9hbStlYaySpelOZi9ayEZP8/utywzb3xdFudnGUwJXYC6P1PsifFZl + 4JD1RSTLWlrq1xMfq2Ale5nszVLiVz/muruEiOPtxbDt9SfywGJSYPa8hql9I7uPv4Kj/BmiyhbGvlnH + neheVsQAmQkj4vY2UuNmRhs23huuW/d0/hNrXde0eFIQmrhQqP5oB31ndjE7aPrzsgEx9S7JsXJuNr+K + opzSC1Ow5oHsA0DwQjXj5/awkg0hJku4e3s7WnQvP9tfZmZqEtlz3xgbvOpUPJEkqQuS6bsMB9386Dcz + 0i0zE95KZnIb8UgZEy1vsBj7lYX8Kl/5e5WdHWqN4+RQX245crKn+gVI3eKGYwtB88P02zcQOPTcSpvi + 03Pn8qkhzdmhygVgdQ4YH19eGmhgKXyE8MG1uI2P6D2mNU+t7vvXbHr2ISEZH132H96w3Fv7/KWusicf + W91zf/4A4AuLF1bhN90AAAAASUVORK5CYII= @@ -311,15 +311,15 @@ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 - YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHvSURBVDhP7Y7Ni1JhGMVvXadgWrWzRQyzTEFyFa7U0Wvg - R3A3KgS1SLFeSbCPGe0uHFyMIEzgBXOVECi00VbOKGWg4yYiW4i1ujNIEVKkQVPaZJ7hGUhMnP+gAy88 - nPM7h5fj/msiQRDOMMbux2KxXjabhSRJHcbYJcaYWpIkhTzKiCH2n7JGoznlcDiSqVQK3W4XiqKgVqtR - YVeSpHqr1RqS12g0QAyx1JkM6PX6K6FQ6Fu73e55vd4tQRBGfr8fsiwjGo2CbvIoy+fzHWKpMz2QjMfj - A1mWA06nc9FgMHyvVqtoNpuo1+soFAogjzJiiKXOZECn090KBAL9dDp9TxTFitlsPrDZbBBF8ejRTR5l - xBBLncmAVqu94HK53icSiX4ul/taLBaRyWQQDocRiUSO7vIGw0tx6c+LlQU8sy0ePDGdfjgZ4DiONxqN - F+12+57P59sPBoMDxtgvt9t9zePxWB5dNQ3f3jVgUNrE+F0ZP57eweugZvTcoro9PXKstq288rO0Ccgu - YO0ssLGML0kTSmZ+d5adq60V1Xj8pohp9WNqkD/LzlXFyn/Yf3wDiKkxXOXQW+XQucmjLPAfZ9m52vGc - W391fen3p7VlfI4uQPGdQPWyarRtPflglj1WO57zkYqF36Nv04/+lg8BALcaCRX7gQ0AAAAASUVORK5C + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHvSURBVDhP7Y7Ni1JhGMVvXadgWrWzRQyzTEFyla0cx6sL + P6K7USGoRYr1SoJ9zGh3YbgYQZjAC+YqIURoo61slDLQcRORgWCt7gxShBRp0JQ2mSeegS4mzn/QgRce + zvmdw8tx/6XKZrOdYIzdicfj/VwuB0mSuoyxc4wxrSRJCnmUEUPsP2WdTnfM6XSm0uk0er0eFEVBvV6n + wo4kSY12uz0ir9lsghhiqaMOGI3GC+Fw+Gun0+n7fL6ngiCMA4EAZFlGLBYD3eRRVigUusRSZ3oglUgk + hrIsB10u16LJZPpWq9XQarXQaDRQLBZBHmXEEEsddcBgMFwPBoODTCZzWxTFqsVi2bfb7RBF8eDRTR5l + xBBLHXVAr9efcbvd75LJ5CCfz38plUrIZrOIRCKIRqMHd2WD4cXFpd/PVxfwxL64/2jl+H11gOM43mw2 + n3U4HLt+v38vFAoNGWM/PR7PZa/Xa31wyTx6c+s8huVNTN5W8P3xTbwK6cbPrJob0yOHakvglR/lTUB2 + A+sngY1lfE6toGzhd2bZuSqvaiaT1yVMaxDXgvxZdq6qAv9+7+FVIK7FaI1Df41D9xqPio3/MMvO1bb3 + 1L2XV5Z+fVxfxqfYAhT/EdTsmvGWcPTuLHuotr2no1Urv0vfph/9Lf8B0zIZ8W7n4ScAAAAASUVORK5C YII= @@ -335,18 +335,33 @@ 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 + nOccAdABIDXXE1nzAAAAAElFTkSuQmCC diff --git a/AGVLogic/AGVMapEditor/Properties/AssemblyInfo.cs b/AGVLogic/AGVMapEditor/Properties/AssemblyInfo.cs index 7ef9156..59fbba6 100644 --- a/AGVLogic/AGVMapEditor/Properties/AssemblyInfo.cs +++ b/AGVLogic/AGVMapEditor/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호를 // 기본값으로 할 수 있습니다. // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file +[assembly: AssemblyVersion("26.02.10.0900")] +[assembly: AssemblyFileVersion("26.02.10.0900")] \ No newline at end of file diff --git a/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs b/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs index 8d87a4f..af87165 100644 --- a/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs +++ b/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs @@ -478,8 +478,32 @@ namespace AGVNavigationCore.Controls // But this method draws normal first. // So I should refactor to calculate path first, then draw? // Or just draw highlight on top with alpha? - // Let's draw highlight on top with non-filled center? No, it's a line. - // I'll draw highlight on top for now, maybe with alpha. + // Let's draw highlight on top for now, maybe with alpha. + } + + // 선택된 마그넷 핸들 그리기 + if (magnet == _selectedNode && _canvasMode == CanvasMode.Edit) + { + using (var handleBrush = new SolidBrush(Color.White)) + using (var handlePen = new Pen(Color.Black, 1)) + { + float size = HANDLE_SIZE / _zoomFactor; + float half = size / 2; + + // 시작점, 끝점 핸들 + g.FillRectangle(handleBrush, startPoint.X - half, startPoint.Y - half, size, size); + g.DrawRectangle(handlePen, startPoint.X - half, startPoint.Y - half, size, size); + g.FillRectangle(handleBrush, endPoint.X - half, endPoint.Y - half, size, size); + g.DrawRectangle(handlePen, endPoint.X - half, endPoint.Y - half, size, size); + + // 제어점 핸들 (곡선일 경우) + if (magnet.ControlPoint != null) + { + var cp = magnet.ControlPoint; + g.FillRectangle(handleBrush, (float)cp.X - half, (float)cp.Y - half, size, size); + g.DrawRectangle(handlePen, (float)cp.X - half, (float)cp.Y - half, size, size); + } + } } } @@ -488,11 +512,10 @@ namespace AGVNavigationCore.Controls if (_marks == null) return; // _marks 리스트 사용 int sensorSize = 12; // 크기 설정 - int lineLength = 20; // 선 길이 설정 - int halfLength = lineLength / 2; - foreach (var mark in _marks) { + int lineLength = (int)mark.Length; // 저장된 길이 사용 + int halfLength = lineLength / 2; Point p = mark.Position; double radians = mark.Rotation * Math.PI / 180.0; @@ -514,6 +537,22 @@ namespace AGVNavigationCore.Controls g.DrawLine(highlightPen, p1, p2); } } + + // 선택된 마크 핸들 그리기 + if (mark == _selectedNode && _canvasMode == CanvasMode.Edit) + { + using (var handleBrush = new SolidBrush(Color.White)) + using (var handlePen = new Pen(Color.Black, 1)) + { + float size = HANDLE_SIZE / _zoomFactor; + float half = size / 2; + + g.FillRectangle(handleBrush, p1.X - half, p1.Y - half, size, size); + g.DrawRectangle(handlePen, p1.X - half, p1.Y - half, size, size); + g.FillRectangle(handleBrush, p2.X - half, p2.Y - half, size, size); + g.DrawRectangle(handlePen, p2.X - half, p2.Y - half, size, size); + } + } } } diff --git a/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Mouse.cs b/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Mouse.cs index 45e91c5..6c45c2a 100644 --- a/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Mouse.cs +++ b/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Mouse.cs @@ -220,12 +220,28 @@ namespace AGVNavigationCore.Controls { if (_editMode == EditMode.Move) { + // 0. 핸들 선택 확인 (이미 선택된 노드가 있을 때) + if (_selectedNode != null) + { + int handleIdx = GetHandleAt(worldPoint); + if (handleIdx != -1) + { + _dragHandleIndex = handleIdx; + _isDragging = true; + _isPanning = false; + Capture = true; + Invalidate(); + return; + } + } + // 1. 노드 선택 확인 var hitNode = GetItemAt(worldPoint); if (hitNode != null) { _isDragging = true; _isPanning = false; + _dragHandleIndex = -1; // 노드 전체 드래그 _selectedNode = hitNode; _dragStartPosition = hitNode.Position; _dragOffset = new Point(worldPoint.X - hitNode.Position.X, worldPoint.Y - hitNode.Position.Y); @@ -322,8 +338,38 @@ namespace AGVNavigationCore.Controls // 노드 드래그 if (_selectedNode != null) { - _selectedNode.Position = newPosition; - NodeMoved?.Invoke(this, _selectedNode); + if (_dragHandleIndex != -1) + { + // 핸들 드래그 (포인트별 수정) + if (_selectedNode is MapMagnet magnet) + { + if (_dragHandleIndex == 0) magnet.StartPoint = newPosition; + else if (_dragHandleIndex == 1) magnet.EndPoint = newPosition; + else if (_dragHandleIndex == 2 && magnet.ControlPoint != null) + { + magnet.ControlPoint.X = newPosition.X; + magnet.ControlPoint.Y = newPosition.Y; + } + } + else if (_selectedNode is MapMark mark) + { + // 마크는 중심점 대비 각도와 길이를 계산하여 수정 + var dx = newPosition.X - mark.Position.X; + var dy = newPosition.Y - mark.Position.Y; + + // 핸들 인덱스에 따라 각도 반전 (p1 vs p2) + if (_dragHandleIndex == 0) { dx = -dx; dy = -dy; } + + mark.Rotation = Math.Atan2(dy, dx) * 180.0 / Math.PI; + mark.Length = Math.Sqrt(dx * dx + dy * dy) * 2; + } + } + else + { + // 노드 전체 드래그 + _selectedNode.Position = newPosition; + NodeMoved?.Invoke(this, _selectedNode); + } moved = true; } @@ -352,6 +398,7 @@ namespace AGVNavigationCore.Controls if (_isDragging && _canvasMode == CanvasMode.Edit) { _isDragging = false; + _dragHandleIndex = -1; Capture = false; // 🔥 마우스 캡처 해제 Cursor = GetCursorForMode(_editMode); } @@ -463,6 +510,25 @@ namespace AGVNavigationCore.Controls } } + if (_marks != null) + { + for (int i = _marks.Count - 1; i >= 0; i--) + { + var node = _marks[i]; + if (IsPointInNode(worldPoint, node)) + return node; + } + } + + if (_magnets != null) + { + for (int i = _magnets.Count - 1; i >= 0; i--) + { + var node = _magnets[i]; + if (IsPointInNode(worldPoint, node)) + return node; + } + } return null; } @@ -477,6 +543,14 @@ namespace AGVNavigationCore.Controls { return IsPointInImage(point, image); } + if (node is MapMark mark) + { + return IsPointInMark(point, mark); + } + if (node is MapMagnet magnet) + { + return IsPointInMagnet(point, magnet); + } // 라벨과 이미지는 별도 리스트로 관리되므로 여기서 처리하지 않음 // 하지만 혹시 모를 하위 호환성을 위해 타입 체크는 유지하되, // 실제 로직은 CircularNode 등으로 분기 @@ -648,6 +722,55 @@ namespace AGVNavigationCore.Controls return imageRect.Contains(point); } + private bool IsPointInMark(Point point, MapMark mark) + { + int lineLength = (int)mark.Length; + int halfLength = lineLength / 2; + double radians = mark.Rotation * Math.PI / 180.0; + int dx = (int)(halfLength * Math.Cos(radians)); + int dy = (int)(halfLength * Math.Sin(radians)); + + Point p1 = new Point(mark.Position.X - dx, mark.Position.Y - dy); + Point p2 = new Point(mark.Position.X + dx, mark.Position.Y + dy); + + // 마크 선택을 위해 약간 넉넉한 히트 영역 (7픽셀) + return CalculatePointToLineDistance(point, p1, p2) <= 7 / _zoomFactor; + } + + private bool IsPointInMagnet(Point point, MapMagnet magnet) + { + // 마그넷은 두꺼우므로 (Pen Width 15) 절반인 7.5 정도를 히트 영역으로 잡음 + float hitThreshold = Math.Max(8f, 12f / _zoomFactor); + + if (magnet.ControlPoint != null) + { + // 베지어 곡선 정밀 샘플링 (10개 세그먼트) + Point prevPoint = magnet.StartPoint; + for (int i = 1; i <= 10; i++) + { + float t = i / 10f; + // Quadratic Bezier: (1-t)^2*P0 + 2(1-t)t*P1 + t^2*P2 + float u = 1 - t; + float tt = t * t; + float uu = u * u; + + float x = uu * magnet.StartPoint.X + 2 * u * t * (float)magnet.ControlPoint.X + tt * magnet.EndPoint.X; + float y = uu * magnet.StartPoint.Y + 2 * u * t * (float)magnet.ControlPoint.Y + tt * magnet.EndPoint.Y; + Point currentPoint = new Point((int)x, (int)y); + + if (CalculatePointToLineDistance(point, prevPoint, currentPoint) <= hitThreshold) + return true; + + prevPoint = currentPoint; + } + return false; + } + else + { + return CalculatePointToLineDistance(point, magnet.StartPoint, magnet.EndPoint) <= hitThreshold; + } + } + //private MapLabel GetLabelAt(Point worldPoint) //{ // if (_labels == null) return null; @@ -833,7 +956,7 @@ namespace AGVNavigationCore.Controls /// /// 중복되지 않는 고유한 NodeId 생성 /// - private string GenerateUniqueNodeId() + public string GenerateUniqueNodeId() { string nodeId; int counter = _nodeCounter; @@ -1053,8 +1176,8 @@ namespace AGVNavigationCore.Controls var C = lineEnd.X - lineStart.X; var D = lineEnd.Y - lineStart.Y; - var dot = A * C + B * D; - var lenSq = C * C + D * D; + var dot = (double)A * C + (double)B * D; + var lenSq = (double)C * C + (double)D * D; if (lenSq == 0) return CalculateDistance(point, lineStart); @@ -1102,6 +1225,39 @@ namespace AGVNavigationCore.Controls } } + private int GetHandleAt(Point worldPoint) + { + if (_selectedNode == null) return -1; + + float hitTolerance = (HANDLE_SIZE + 4) / _zoomFactor; + + if (_selectedNode is MapMagnet magnet) + { + if (CalculateDistance(worldPoint, magnet.StartPoint) <= hitTolerance) return 0; + if (CalculateDistance(worldPoint, magnet.EndPoint) <= hitTolerance) return 1; + if (magnet.ControlPoint != null) + { + if (CalculateDistance(worldPoint, new Point((int)magnet.ControlPoint.X, (int)magnet.ControlPoint.Y)) <= hitTolerance) return 2; + } + } + else if (_selectedNode is MapMark mark) + { + int lineLength = (int)mark.Length; + int halfLength = lineLength / 2; + double radians = mark.Rotation * Math.PI / 180.0; + int dx = (int)(halfLength * Math.Cos(radians)); + int dy = (int)(halfLength * Math.Sin(radians)); + + Point p1 = new Point(mark.Position.X - dx, mark.Position.Y - dy); + Point p2 = new Point(mark.Position.X + dx, mark.Position.Y + dy); + + if (CalculateDistance(worldPoint, p1) <= hitTolerance) return 0; + if (CalculateDistance(worldPoint, p2) <= hitTolerance) return 1; + } + + return -1; + } + #endregion #region View Control Methods diff --git a/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs b/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs index fd30f2f..4458ace 100644 --- a/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs +++ b/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs @@ -109,6 +109,8 @@ namespace AGVNavigationCore.Controls private MapNode _connectionStartNode; private Point _connectionEndPoint; private int _mouseMoveCounter = 0; // 디버그용: MouseMove 실행 횟수 + private int _dragHandleIndex = -1; // 드래그 중인 핸들 인덱스 + private const int HANDLE_SIZE = 8; // 편집 핸들 크기 // 영역 선택 관련 private bool _isAreaSelecting; @@ -341,6 +343,17 @@ namespace AGVNavigationCore.Controls Invalidate(); } + [Browsable(false)] + public PointF PanOffset + { + get => _panOffset; + set + { + _panOffset = value; + Invalidate(); + } + } + /// /// 그리드 표시 여부 /// diff --git a/AGVLogic/AGVNavigationCore/Models/MapMagnet.cs b/AGVLogic/AGVNavigationCore/Models/MapMagnet.cs index 1384890..5556b00 100644 --- a/AGVLogic/AGVNavigationCore/Models/MapMagnet.cs +++ b/AGVLogic/AGVNavigationCore/Models/MapMagnet.cs @@ -56,17 +56,25 @@ namespace AGVNavigationCore.Models } /// - /// 시작점 Point 반환 + /// 시작점 Point 반환 및 설정 /// [Browsable(false)] [JsonIgnore] - public Point StartPoint => new Point((int)P1.X, (int)P1.Y); + public Point StartPoint + { + get => new Point((int)P1.X, (int)P1.Y); + set { P1.X = value.X; P1.Y = value.Y; } + } /// - /// 끝점 Point 반환 + /// 끝점 Point 반환 및 설정 /// [Browsable(false)] [JsonIgnore] - public Point EndPoint => new Point((int)P2.X, (int)P2.Y); + public Point EndPoint + { + get => new Point((int)P2.X, (int)P2.Y); + set { P2.X = value.X; P2.Y = value.Y; } + } } } diff --git a/AGVLogic/AGVNavigationCore/Models/MapMark.cs b/AGVLogic/AGVNavigationCore/Models/MapMark.cs index 6cd657e..4a2e5d1 100644 --- a/AGVLogic/AGVNavigationCore/Models/MapMark.cs +++ b/AGVLogic/AGVNavigationCore/Models/MapMark.cs @@ -33,5 +33,9 @@ namespace AGVNavigationCore.Models [Category("위치 정보")] [Description("마크의 회전 각도")] public double Rotation { get; set; } + + [Category("위치 정보")] + [Description("마크의 길이")] + public double Length { get; set; } = 20.0; } } diff --git a/AGVLogic/AGVSimulator/Forms/SimulatorForm.Designer.cs b/AGVLogic/AGVSimulator/Forms/SimulatorForm.Designer.cs index a5ad18b..f3e8cc4 100644 --- a/AGVLogic/AGVSimulator/Forms/SimulatorForm.Designer.cs +++ b/AGVLogic/AGVSimulator/Forms/SimulatorForm.Designer.cs @@ -55,6 +55,7 @@ namespace AGVSimulator.Forms this.맵다른이름으로저장ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); this.launchMapEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.btSelectMapEditor = 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(); @@ -120,7 +121,7 @@ namespace AGVSimulator.Forms this._liftDirectionLabel = new System.Windows.Forms.Label(); this._motorDirectionLabel = new System.Windows.Forms.Label(); this.timer1 = new System.Windows.Forms.Timer(this.components); - this.btSelectMapEditor = new System.Windows.Forms.ToolStripMenuItem(); + this.sbFile = new System.Windows.Forms.ToolStripStatusLabel(); this._menuStrip.SuspendLayout(); this._toolStrip.SuspendLayout(); this._statusStrip.SuspendLayout(); @@ -205,6 +206,13 @@ namespace AGVSimulator.Forms this.launchMapEditorToolStripMenuItem.Text = "MapEditor 실행(&M)"; this.launchMapEditorToolStripMenuItem.Click += new System.EventHandler(this.OnLaunchMapEditor_Click); // + // btSelectMapEditor + // + this.btSelectMapEditor.Name = "btSelectMapEditor"; + this.btSelectMapEditor.Size = new System.Drawing.Size(221, 22); + this.btSelectMapEditor.Text = "Mapeditor 선택"; + this.btSelectMapEditor.Click += new System.EventHandler(this.btSelectMapEditor_Click); + // // toolStripSeparator4 // this.toolStripSeparator4.Name = "toolStripSeparator4"; @@ -434,7 +442,8 @@ namespace AGVSimulator.Forms this._statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this._statusLabel, this._coordLabel, - this.prb1}); + this.prb1, + this.sbFile}); this._statusStrip.Location = new System.Drawing.Point(0, 689); this._statusStrip.Name = "_statusStrip"; this._statusStrip.Size = new System.Drawing.Size(1248, 22); @@ -816,12 +825,11 @@ namespace AGVSimulator.Forms this.timer1.Interval = 500; this.timer1.Tick += new System.EventHandler(this.timer1_Tick); // - // btSelectMapEditor + // sbFile // - this.btSelectMapEditor.Name = "btSelectMapEditor"; - this.btSelectMapEditor.Size = new System.Drawing.Size(221, 22); - this.btSelectMapEditor.Text = "Mapeditor 선택"; - this.btSelectMapEditor.Click += new System.EventHandler(this.btSelectMapEditor_Click); + this.sbFile.Name = "sbFile"; + this.sbFile.Size = new System.Drawing.Size(17, 17); + this.sbFile.Text = "--"; // // SimulatorForm // @@ -937,5 +945,6 @@ namespace AGVSimulator.Forms private System.Windows.Forms.PropertyGrid propertyNode; private System.Windows.Forms.Button btPath2; private System.Windows.Forms.ToolStripMenuItem btSelectMapEditor; + private System.Windows.Forms.ToolStripStatusLabel sbFile; } } \ No newline at end of file diff --git a/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs b/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs index b58c104..ea25bee 100644 --- a/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs +++ b/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs @@ -876,7 +876,7 @@ namespace AGVSimulator.Forms try { var result = MapLoader.LoadMapFromFile(filePath); - + sbFile.Text = filePath; if (result.Success) { Console.WriteLine($"Map File Load : {filePath}"); diff --git a/HMI/Project/StateMachine/_AGV.cs b/HMI/Project/StateMachine/_AGV.cs index 48dbb49..73168bc 100644 --- a/HMI/Project/StateMachine/_AGV.cs +++ b/HMI/Project/StateMachine/_AGV.cs @@ -16,6 +16,34 @@ namespace Project public partial class fMain { + private void AGV_TurnComplete(object sender, arDev.Narumi.TurnEventArgs e) + { + //턴작업이완료되었을때 발생된다. + PUB.log.Add($"AGV Turn Complete:{e.Direction}"); + + //일반노드에서 턴작업이 진행되었다면, 이전경로와 현재경로의 모터 방향을 바꿔준다(일반노드에서만 사용) + if (PUB._virtualAGV.CurrentNode != null && PUB._virtualAGV.CurrentNode.StationType == StationType.Normal) + { + var prevnodeid = PUB._virtualAGV.PrevNode;//.Id; + var currnodeid = PUB._virtualAGV.CurrentNode;//.Id; + + //현재 방향과 반대로 모터방향을 셋팅한다. 기존에 Fwd로 왔다면 BWD로 온것으로 처리 + var motdir = PUB._virtualAGV.PrevDirection == AgvDirection.Forward ? AgvDirection.Backward : AgvDirection.Forward; + + PUB.log.Add($"일반노드에서 TURN 완료({e.Direction}) 이전노드:{prevnodeid.ID2},현재노드:{currnodeid.ID2},이전방향:{PUB._virtualAGV.PrevDirection},변경방향:{motdir}"); + + //이전노드이동한것으로처리 + PUB._mapCanvas.SetAGVPosition(PUB.setting.MCID, prevnodeid, motdir); + PUB._virtualAGV.SetPosition(prevnodeid, motdir); + + //현재노드이동한것으로처리 + PUB._mapCanvas.SetAGVPosition(PUB.setting.MCID, currnodeid, motdir); + PUB._virtualAGV.SetPosition(currnodeid, motdir); + } + + } + + private void AGV_Message(object sender, arDev.Narumi.MessageEventArgs e) { if (e.MsgType == arDev.NarumiSerialComm.MessageType.Normal) diff --git a/HMI/Project/fMain.cs b/HMI/Project/fMain.cs index 3a7004d..a74d24a 100644 --- a/HMI/Project/fMain.cs +++ b/HMI/Project/fMain.cs @@ -166,6 +166,7 @@ namespace Project PUB.AGV = new arDev.Narumi(); PUB.AGV.Message += AGV_Message; PUB.AGV.DataReceive += AGV_DataReceive; + PUB.AGV.TurnComplete += AGV_TurnComplete; //배터리관리시스템 PUB.BMS = new arDev.BMS(); @@ -261,6 +262,7 @@ namespace Project PUB.AddEEDB("프로그램 시작"); } + void AutoLoadLastPosition() { PUB.log.Add("autoload last position"); diff --git a/HMI/SubProject/AGV/DataEventArgs.cs b/HMI/SubProject/AGV/DataEventArgs.cs index a5a5c36..1052a43 100644 --- a/HMI/SubProject/AGV/DataEventArgs.cs +++ b/HMI/SubProject/AGV/DataEventArgs.cs @@ -13,6 +13,20 @@ namespace arDev DataType = type; } } + public enum eTurnEvent + { + Left=0, + Right + } + public class TurnEventArgs : EventArgs + { + public eTurnEvent Direction { get; set; } + public TurnEventArgs(eTurnEvent data) + { + this.Direction = data; + } + + } } diff --git a/HMI/SubProject/AGV/Narumi.cs b/HMI/SubProject/AGV/Narumi.cs index 68bd663..4d1289c 100644 --- a/HMI/SubProject/AGV/Narumi.cs +++ b/HMI/SubProject/AGV/Narumi.cs @@ -71,7 +71,8 @@ namespace arDev /// 분석완료된 데이터 이벤트 /// public event EventHandler DataReceive; - + public event EventHandler TurnComplete; + #endregion diff --git a/HMI/TestProject/Test_ACS/MainForm.Designer.cs b/HMI/TestProject/Test_ACS/MainForm.Designer.cs index c573442..66e94f4 100644 --- a/HMI/TestProject/Test_ACS/MainForm.Designer.cs +++ b/HMI/TestProject/Test_ACS/MainForm.Designer.cs @@ -29,12 +29,7 @@ namespace Test_ACS this.rbAGV2 = new System.Windows.Forms.RadioButton(); this.rbAGV1 = new System.Windows.Forms.RadioButton(); this.grpCommands = new System.Windows.Forms.GroupBox(); - this.button11 = new System.Windows.Forms.Button(); - this.button12 = new System.Windows.Forms.Button(); - this.btnLiftStop = new System.Windows.Forms.Button(); this.button8 = new System.Windows.Forms.Button(); - this.btnLiftDown = new System.Windows.Forms.Button(); - this.btnLiftUp = new System.Windows.Forms.Button(); this.button10 = new System.Windows.Forms.Button(); this.button7 = new System.Windows.Forms.Button(); this.button9 = new System.Windows.Forms.Button(); @@ -71,6 +66,11 @@ namespace Test_ACS this.lblAlias = new System.Windows.Forms.Label(); this.txtRFID = new System.Windows.Forms.NumericUpDown(); this.lblRFID = new System.Windows.Forms.Label(); + this.button11 = new System.Windows.Forms.Button(); + this.button12 = new System.Windows.Forms.Button(); + this.btnLiftStop = new System.Windows.Forms.Button(); + this.btnLiftDown = new System.Windows.Forms.Button(); + this.btnLiftUp = new System.Windows.Forms.Button(); this.grpLogs = new System.Windows.Forms.GroupBox(); this.tabLogs = new System.Windows.Forms.TabControl(); this.tabRX = new System.Windows.Forms.TabPage(); @@ -245,36 +245,6 @@ namespace Test_ACS this.grpCommands.TabStop = false; this.grpCommands.Text = "ACS 명령"; // - // button11 - // - this.button11.Location = new System.Drawing.Point(451, 200); - this.button11.Name = "button11"; - this.button11.Size = new System.Drawing.Size(100, 43); - this.button11.TabIndex = 18; - this.button11.Text = "전자석 OFF"; - this.button11.UseVisualStyleBackColor = true; - this.button11.Click += new System.EventHandler(this.button11_Click); - // - // button12 - // - this.button12.Location = new System.Drawing.Point(451, 155); - this.button12.Name = "button12"; - this.button12.Size = new System.Drawing.Size(100, 43); - this.button12.TabIndex = 17; - this.button12.Text = "전자석 ON"; - this.button12.UseVisualStyleBackColor = true; - this.button12.Click += new System.EventHandler(this.button12_Click); - // - // btnLiftStop - // - this.btnLiftStop.Location = new System.Drawing.Point(451, 110); - this.btnLiftStop.Name = "btnLiftStop"; - this.btnLiftStop.Size = new System.Drawing.Size(100, 43); - this.btnLiftStop.TabIndex = 2; - this.btnLiftStop.Text = "리프트 정지"; - this.btnLiftStop.UseVisualStyleBackColor = true; - this.btnLiftStop.Click += new System.EventHandler(this.btnLiftStop_Click); - // // button8 // this.button8.Location = new System.Drawing.Point(102, 264); @@ -285,26 +255,6 @@ namespace Test_ACS this.button8.UseVisualStyleBackColor = true; this.button8.Click += new System.EventHandler(this.button8_Click); // - // btnLiftDown - // - this.btnLiftDown.Location = new System.Drawing.Point(451, 65); - this.btnLiftDown.Name = "btnLiftDown"; - this.btnLiftDown.Size = new System.Drawing.Size(100, 43); - this.btnLiftDown.TabIndex = 1; - this.btnLiftDown.Text = "리프트 DN"; - this.btnLiftDown.UseVisualStyleBackColor = true; - this.btnLiftDown.Click += new System.EventHandler(this.btnLiftDown_Click); - // - // btnLiftUp - // - this.btnLiftUp.Location = new System.Drawing.Point(451, 20); - this.btnLiftUp.Name = "btnLiftUp"; - this.btnLiftUp.Size = new System.Drawing.Size(100, 43); - this.btnLiftUp.TabIndex = 0; - this.btnLiftUp.Text = "리프트 UP"; - this.btnLiftUp.UseVisualStyleBackColor = true; - this.btnLiftUp.Click += new System.EventHandler(this.btnLiftUp_Click); - // // button10 // this.button10.Location = new System.Drawing.Point(102, 229); @@ -655,12 +605,6 @@ namespace Test_ACS // txtAlias // this.txtAlias.Font = new System.Drawing.Font("Consolas", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.txtAlias.Items.AddRange(new object[] { - "LOADER", - "UNLOADER", - "CLEANNER", - "CHARGER1", - "CHARGER2"}); this.txtAlias.Location = new System.Drawing.Point(86, 49); this.txtAlias.Name = "txtAlias"; this.txtAlias.Size = new System.Drawing.Size(101, 27); @@ -706,6 +650,56 @@ namespace Test_ACS this.lblRFID.TabIndex = 0; this.lblRFID.Text = "RFID 번호:"; // + // button11 + // + this.button11.Location = new System.Drawing.Point(451, 200); + this.button11.Name = "button11"; + this.button11.Size = new System.Drawing.Size(100, 43); + this.button11.TabIndex = 18; + this.button11.Text = "전자석 OFF"; + this.button11.UseVisualStyleBackColor = true; + this.button11.Click += new System.EventHandler(this.button11_Click); + // + // button12 + // + this.button12.Location = new System.Drawing.Point(451, 155); + this.button12.Name = "button12"; + this.button12.Size = new System.Drawing.Size(100, 43); + this.button12.TabIndex = 17; + this.button12.Text = "전자석 ON"; + this.button12.UseVisualStyleBackColor = true; + this.button12.Click += new System.EventHandler(this.button12_Click); + // + // btnLiftStop + // + this.btnLiftStop.Location = new System.Drawing.Point(451, 110); + this.btnLiftStop.Name = "btnLiftStop"; + this.btnLiftStop.Size = new System.Drawing.Size(100, 43); + this.btnLiftStop.TabIndex = 2; + this.btnLiftStop.Text = "리프트 정지"; + this.btnLiftStop.UseVisualStyleBackColor = true; + this.btnLiftStop.Click += new System.EventHandler(this.btnLiftStop_Click); + // + // btnLiftDown + // + this.btnLiftDown.Location = new System.Drawing.Point(451, 65); + this.btnLiftDown.Name = "btnLiftDown"; + this.btnLiftDown.Size = new System.Drawing.Size(100, 43); + this.btnLiftDown.TabIndex = 1; + this.btnLiftDown.Text = "리프트 DN"; + this.btnLiftDown.UseVisualStyleBackColor = true; + this.btnLiftDown.Click += new System.EventHandler(this.btnLiftDown_Click); + // + // btnLiftUp + // + this.btnLiftUp.Location = new System.Drawing.Point(451, 20); + this.btnLiftUp.Name = "btnLiftUp"; + this.btnLiftUp.Size = new System.Drawing.Size(100, 43); + this.btnLiftUp.TabIndex = 0; + this.btnLiftUp.Text = "리프트 UP"; + this.btnLiftUp.UseVisualStyleBackColor = true; + this.btnLiftUp.Click += new System.EventHandler(this.btnLiftUp_Click); + // // grpLogs // this.grpLogs.Controls.Add(this.tabLogs); diff --git a/HMI/TestProject/Test_ACS/MainForm.cs b/HMI/TestProject/Test_ACS/MainForm.cs index bb63671..f7fdd61 100644 --- a/HMI/TestProject/Test_ACS/MainForm.cs +++ b/HMI/TestProject/Test_ACS/MainForm.cs @@ -21,6 +21,22 @@ namespace Test_ACS InitializeComponent(); InitializeProtocol(); LoadPortList(); + + txtAlias.Items.Clear(); + this.txtAlias.Items.AddRange(new[] { + "LOADER", + "PLATING", + "CLEANER", + "CHARGER", + "BUFFER1", + "BUFFER2", + "BUFFER3", + "BUFFER4", + "BUFFER5", + "BUFFER6", + }); + this.txtAlias.Text = "LOADER"; + } @@ -611,19 +627,7 @@ namespace Test_ACS private void MainForm_Load(object sender, EventArgs e) { - this.txtAlias.Items.AddRange(new[] { - "LOADER", - "UNLOADER", - "CLEANNER", - "CHARGER1", - "CHARGER2", - "BUFFER1", - "BUFFER2", - "BUFFER3", - "BUFFER4", - "BUFFER5", - "BUFFER6", - }); + } } } diff --git a/NewMap.json b/NewMap.json index 558b572..dd9904e 100644 --- a/NewMap.json +++ b/NewMap.json @@ -1,20 +1,19 @@ { "Nodes": [ { - "Text": "Unloader", + "Text": "CLEANER", "StationType": 3, "CanDocking": true, "DockDirection": 2, "MagnetDirections": {}, "ConnectedNodes": [ - "12", - "N028" + "10" ], "CanTurnLeft": true, "CanTurnRight": true, "DisableCross": false, "SpeedLimit": 0, - "AliasName": "", + "AliasName": "CLEANER", "IsActive": true, "RfidId": 30, "NodeTextForeColor": "White", @@ -22,23 +21,22 @@ "ID2": "0030(*N001)", "Id": "N001", "Type": 0, - "Position": "298, 270" + "Position": "395, 269" }, { - "Text": "Cleaner", + "Text": "PLATING", "StationType": 2, "CanDocking": true, "DockDirection": 2, "MagnetDirections": {}, "ConnectedNodes": [ - "5", - "N030" + "5" ], "CanTurnLeft": true, "CanTurnRight": true, "DisableCross": false, "SpeedLimit": 0, - "AliasName": "", + "AliasName": "PLATING", "IsActive": true, "RfidId": 11, "NodeTextForeColor": "", @@ -46,82 +44,10 @@ "ID2": "0011(*N010)", "Id": "N010", "Type": 0, - "Position": "298, 446" + "Position": "350, 506" }, { - "Text": "Loader", - "StationType": 1, - "CanDocking": true, - "DockDirection": 2, - "MagnetDirections": {}, - "ConnectedNodes": [ - "N026", - "N029" - ], - "CanTurnLeft": true, - "CanTurnRight": true, - "DisableCross": false, - "SpeedLimit": 0, - "AliasName": "", - "IsActive": true, - "RfidId": 8, - "NodeTextForeColor": "", - "NodeTextFontSize": 7.0, - "ID2": "0008(*N014)", - "Id": "N014", - "Type": 0, - "Position": "520, 653" - }, - { - "Text": "Charger #1", - "StationType": 5, - "CanDocking": true, - "DockDirection": 1, - "MagnetDirections": {}, - "ConnectedNodes": [ - "8", - "13" - ], - "CanTurnLeft": true, - "CanTurnRight": true, - "DisableCross": false, - "SpeedLimit": 0, - "AliasName": "", - "IsActive": true, - "RfidId": 15, - "NodeTextForeColor": "", - "NodeTextFontSize": 7.0, - "ID2": "0015(*N019)", - "Id": "N019", - "Type": 0, - "Position": "405, 351" - }, - { - "Text": "Charger #2", - "StationType": 6, - "CanDocking": true, - "DockDirection": 2, - "MagnetDirections": {}, - "ConnectedNodes": [ - "6", - "N014" - ], - "CanTurnLeft": true, - "CanTurnRight": true, - "DisableCross": false, - "SpeedLimit": 0, - "AliasName": "", - "IsActive": true, - "RfidId": 19, - "NodeTextForeColor": "", - "NodeTextFontSize": 7.0, - "ID2": "0019(*N026)", - "Id": "N026", - "Type": 0, - "Position": "520, 576" - }, - { - "Text": "Buffer4", + "Text": "Buffer1", "StationType": 4, "CanDocking": true, "DockDirection": 2, @@ -134,7 +60,7 @@ "CanTurnRight": true, "DisableCross": false, "SpeedLimit": 0, - "AliasName": "", + "AliasName": "BUFFER1", "IsActive": true, "RfidId": 34, "NodeTextForeColor": "", @@ -142,10 +68,10 @@ "ID2": "0034(*N018)", "Id": "N018", "Type": 0, - "Position": "213, 630" + "Position": "238, 627" }, { - "Text": "Buffer3", + "Text": "Buffer2", "StationType": 4, "CanDocking": true, "DockDirection": 2, @@ -158,7 +84,7 @@ "CanTurnRight": true, "DisableCross": false, "SpeedLimit": 0, - "AliasName": "", + "AliasName": "BUFFER2", "IsActive": true, "RfidId": 33, "NodeTextForeColor": "", @@ -166,10 +92,10 @@ "ID2": "0033(*N005)", "Id": "N005", "Type": 0, - "Position": "113, 629" + "Position": "164, 627" }, { - "Text": "Buffer2", + "Text": "Buffer3", "StationType": 4, "CanDocking": true, "DockDirection": 2, @@ -182,7 +108,7 @@ "CanTurnRight": true, "DisableCross": false, "SpeedLimit": 0, - "AliasName": "", + "AliasName": "BUFFER3", "IsActive": true, "RfidId": 32, "NodeTextForeColor": "", @@ -190,23 +116,23 @@ "ID2": "0032(*N020)", "Id": "N020", "Type": 0, - "Position": "38, 626" + "Position": "89, 629" }, { - "Text": "Buffer 1", + "Text": "Buffer4", "StationType": 4, "CanDocking": true, "DockDirection": 2, "MagnetDirections": {}, "ConnectedNodes": [ "N020", - "N027" + "N028" ], "CanTurnLeft": true, "CanTurnRight": true, "DisableCross": false, "SpeedLimit": 0, - "AliasName": "", + "AliasName": "BUFFER4", "IsActive": true, "RfidId": 31, "NodeTextForeColor": "", @@ -214,7 +140,7 @@ "ID2": "0031(*N021)", "Id": "N021", "Type": 0, - "Position": "-54, 626" + "Position": "15, 629" }, { "Text": "", @@ -238,7 +164,7 @@ "ID2": "0002(*1)", "Id": "1", "Type": 0, - "Position": "285, 628" + "Position": "338, 625" }, { "Text": "", @@ -248,7 +174,7 @@ "MagnetDirections": {}, "ConnectedNodes": [ "1", - "3" + "4" ], "CanTurnLeft": true, "CanTurnRight": true, @@ -262,7 +188,7 @@ "ID2": "0004(*2)", "Id": "2", "Type": 0, - "Position": "354, 628" + "Position": "406, 621" }, { "Text": "", @@ -271,32 +197,8 @@ "DockDirection": 0, "MagnetDirections": {}, "ConnectedNodes": [ - "2", - "4" - ], - "CanTurnLeft": true, - "CanTurnRight": true, - "DisableCross": false, - "SpeedLimit": 0, - "AliasName": "", - "IsActive": true, - "RfidId": 3, - "NodeTextForeColor": "", - "NodeTextFontSize": 7.0, - "ID2": "0003(*3)", - "Id": "3", - "Type": 0, - "Position": "400, 578" - }, - { - "Text": "", - "StationType": 0, - "CanDocking": false, - "DockDirection": 0, - "MagnetDirections": {}, - "ConnectedNodes": [ - "3", - "5" + "5", + "2" ], "CanTurnLeft": true, "CanTurnRight": true, @@ -310,7 +212,7 @@ "ID2": "0005(*4)", "Id": "4", "Type": 0, - "Position": "400, 499" + "Position": "435, 569" }, { "Text": "", @@ -319,15 +221,14 @@ "DockDirection": 0, "MagnetDirections": { "4": 1, - "6": 2, "7": 1, - "N010": 0 + "N010": 2 }, "ConnectedNodes": [ "N010", "4", - "6", - "7" + "7", + "8" ], "CanTurnLeft": true, "CanTurnRight": true, @@ -341,28 +242,25 @@ "ID2": "0006(*5)", "Id": "5", "Type": 0, - "Position": "462, 451" + "Position": "461, 503" }, { - "Text": "", - "StationType": 0, - "CanDocking": false, - "DockDirection": 0, + "Text": "Loader", + "StationType": 1, + "CanDocking": true, + "DockDirection": 2, "MagnetDirections": { - "5": 1, "7": 0, "N026": 0 }, "ConnectedNodes": [ - "5", - "7", - "N026" + "7" ], "CanTurnLeft": true, "CanTurnRight": true, "DisableCross": false, "SpeedLimit": 0, - "AliasName": "", + "AliasName": "LOADER", "IsActive": true, "RfidId": 13, "NodeTextForeColor": "", @@ -370,7 +268,7 @@ "ID2": "0013(*6)", "Id": "6", "Type": 0, - "Position": "518, 519" + "Position": "521, 533" }, { "Text": "", @@ -379,13 +277,10 @@ "DockDirection": 0, "MagnetDirections": { "5": 2, - "8": 1, - "6": 0, - "9": 0 + "6": 0 }, "ConnectedNodes": [ "5", - "8", "6", "9" ], @@ -401,28 +296,24 @@ "ID2": "0007(*7)", "Id": "7", "Type": 0, - "Position": "517, 400" + "Position": "517, 404" }, { - "Text": "", - "StationType": 0, - "CanDocking": false, - "DockDirection": 0, + "Text": "Charger", + "StationType": 5, + "CanDocking": true, + "DockDirection": 1, "MagnetDirections": { - "7": 2, - "9": 1, "N019": 0 }, "ConnectedNodes": [ - "7", - "9", - "N019" + "5" ], "CanTurnLeft": true, "CanTurnRight": true, "DisableCross": false, "SpeedLimit": 0, - "AliasName": "", + "AliasName": "CHARGER", "IsActive": true, "RfidId": 9, "NodeTextForeColor": "", @@ -430,7 +321,7 @@ "ID2": "0009(*8)", "Id": "8", "Type": 0, - "Position": "474, 353" + "Position": "439, 420" }, { "Text": "", @@ -438,12 +329,9 @@ "CanDocking": false, "DockDirection": 0, "MagnetDirections": { - "8": 2, - "10": 0, - "7": 0 + "10": 0 }, "ConnectedNodes": [ - "8", "10", "7" ], @@ -469,7 +357,7 @@ "MagnetDirections": {}, "ConnectedNodes": [ "9", - "12" + "N001" ], "CanTurnLeft": true, "CanTurnRight": true, @@ -483,54 +371,7 @@ "ID2": "0012(*10)", "Id": "10", "Type": 0, - "Position": "458, 268" - }, - { - "Text": "", - "StationType": 0, - "CanDocking": false, - "DockDirection": 0, - "MagnetDirections": {}, - "ConnectedNodes": [ - "N001", - "10" - ], - "CanTurnLeft": true, - "CanTurnRight": true, - "DisableCross": false, - "SpeedLimit": 0, - "AliasName": "", - "IsActive": true, - "RfidId": 16, - "NodeTextForeColor": "", - "NodeTextFontSize": 7.0, - "ID2": "0016(*12)", - "Id": "12", - "Type": 0, - "Position": "389, 270" - }, - { - "Text": "", - "StationType": 0, - "CanDocking": false, - "DockDirection": 0, - "MagnetDirections": {}, - "ConnectedNodes": [ - "N019" - ], - "CanTurnLeft": true, - "CanTurnRight": true, - "DisableCross": false, - "SpeedLimit": 0, - "AliasName": "", - "IsActive": true, - "RfidId": 17, - "NodeTextForeColor": "", - "NodeTextFontSize": 7.0, - "ID2": "0017(*13)", - "Id": "13", - "Type": 0, - "Position": "350, 353" + "Position": "490, 277" }, { "Text": "", @@ -539,7 +380,7 @@ "DockDirection": 0, "MagnetDirections": {}, "ConnectedNodes": [ - "N021" + "N029" ], "CanTurnLeft": true, "CanTurnRight": true, @@ -547,59 +388,61 @@ "SpeedLimit": 1, "AliasName": "", "IsActive": true, - "RfidId": 37, + "RfidId": 91, "NodeTextForeColor": "Black", "NodeTextFontSize": 7.0, - "ID2": "0037(*N027)", + "ID2": "0091(*N027)", "Id": "N027", "Type": 0, - "Position": "-114, 625" + "Position": "-211, 632" }, { - "Text": "", - "StationType": 7, - "CanDocking": false, - "DockDirection": 0, + "Text": "Buffer5", + "StationType": 4, + "CanDocking": true, + "DockDirection": 2, "MagnetDirections": {}, "ConnectedNodes": [ - "N001" + "N021", + "N029" ], "CanTurnLeft": true, "CanTurnRight": true, "DisableCross": false, "SpeedLimit": 1, - "AliasName": "", + "AliasName": "BUFFER5", "IsActive": true, - "RfidId": 39, + "RfidId": 35, "NodeTextForeColor": "Black", "NodeTextFontSize": 7.0, - "ID2": "0039(*N028)", + "ID2": "0035(*N028)", "Id": "N028", "Type": 0, - "Position": "225, 269" + "Position": "-59, 629" }, { - "Text": "", - "StationType": 7, - "CanDocking": false, - "DockDirection": 0, + "Text": "Buffer6", + "StationType": 4, + "CanDocking": true, + "DockDirection": 2, "MagnetDirections": {}, "ConnectedNodes": [ - "N014" + "N028", + "N027" ], "CanTurnLeft": true, "CanTurnRight": true, "DisableCross": false, "SpeedLimit": 1, - "AliasName": "", + "AliasName": "BUFFER6", "IsActive": true, - "RfidId": 38, + "RfidId": 36, "NodeTextForeColor": "Black", "NodeTextFontSize": 7.0, - "ID2": "0038(*N029)", + "ID2": "0036(*N029)", "Id": "N029", "Type": 0, - "Position": "519, 720" + "Position": "-133, 629" }, { "Text": "", @@ -607,56 +450,30 @@ "CanDocking": false, "DockDirection": 0, "MagnetDirections": {}, - "ConnectedNodes": [ - "N010" - ], + "ConnectedNodes": [], "CanTurnLeft": true, "CanTurnRight": true, "DisableCross": false, "SpeedLimit": 1, "AliasName": "", "IsActive": true, - "RfidId": 0, + "RfidId": 90, "NodeTextForeColor": "Black", "NodeTextFontSize": 7.0, - "ID2": "(*N030)", + "ID2": "0090(*N030)", "Id": "N030", "Type": 0, - "Position": "246, 447" - } - ], - "Labels": [ - { - "Text": "Amkor Technology Korea", - "ForeColor": "White", - "BackColor": "MidnightBlue", - "FontFamily": "Arial", - "FontSize": 20.0, - "FontStyle": 0, - "Padding": 5, - "Id": "LBL001", - "Type": 1, - "Position": "180, 105" - } - ], - "Images": [ - { - "Name": "Image", - "ImagePath": "", - "ImageBase64": "", - "Scale": "1, 1", - "Opacity": 1.0, - "Rotation": 0.0, - "Id": "IMG001", - "Type": 2, - "Position": "633, 310" + "Position": "435, 349" } ], + "Labels": [], + "Images": [], "Marks": [ { "X": 520.0, "Y": 690.0, "Rotation": -178.01940688369234, + "Length": 0.0, "Id": "2cb51787-c8cf-4ddb-97f0-b71f519d47dc", "Type": 3, "Position": "520, 690" @@ -665,6 +482,7 @@ "X": -74.0, "Y": 624.0, "Rotation": 90.0, + "Length": 0.0, "Id": "f704ebe0-1653-4559-b06f-1eaecafbefba", "Type": 3, "Position": "-74, 624" @@ -673,6 +491,7 @@ "X": 12.0, "Y": 625.0, "Rotation": 90.0, + "Length": 0.0, "Id": "d5b27365-79a2-4351-84c3-6767941ec0be", "Type": 3, "Position": "12, 625" @@ -681,6 +500,7 @@ "X": 91.0, "Y": 625.0, "Rotation": 89.2872271068898, + "Length": 0.0, "Id": "0367cafb-9f85-4440-b6b4-c802a58e6181", "Type": 3, "Position": "91, 625" @@ -689,6 +509,7 @@ "X": 183.0, "Y": 622.0, "Rotation": 88.405167720722616, + "Length": 0.0, "Id": "1f4ab2c9-07f8-4675-802d-9b4824b55198", "Type": 3, "Position": "183, 622" @@ -697,6 +518,7 @@ "X": 275.0, "Y": 269.0, "Rotation": 91.712220129051758, + "Length": 0.0, "Id": "15fddfa4-ff74-48ff-b922-4aacdce1960b", "Type": 3, "Position": "275, 269" @@ -705,6 +527,7 @@ "X": 89.0, "Y": 697.0, "Rotation": 0.38243447796178032, + "Length": 0.0, "Id": "4b699847-36d4-471c-b990-4ad37967c2dc", "Type": 3, "Position": "89, 697" @@ -713,6 +536,7 @@ "X": 183.0, "Y": 699.0, "Rotation": -1.3380194104322385, + "Length": 0.0, "Id": "a9f68317-f1c2-47d8-b029-348b5428be9f", "Type": 3, "Position": "183, 699" @@ -721,6 +545,7 @@ "X": 9.0, "Y": 699.0, "Rotation": 0.84311038333069632, + "Length": 0.0, "Id": "fe227205-2a65-4ba9-bb4a-4efb4ed0a7b0", "Type": 3, "Position": "9, 699" @@ -729,6 +554,7 @@ "X": -74.0, "Y": 697.0, "Rotation": 1.659829660758831, + "Length": 0.0, "Id": "5dd29191-798c-480c-b066-7947bfcc4fb7", "Type": 3, "Position": "-74, 697" @@ -737,22 +563,25 @@ "X": -71.0, "Y": 596.0, "Rotation": 0.0, + "Length": 0.0, "Id": "649729f0-ff04-4e11-8869-f6a39d815427", "Type": 3, "Position": "-71, 596" }, { - "X": 10.0, - "Y": 601.0, + "X": 36.0, + "Y": 600.0, "Rotation": 0.0, + "Length": 0.0, "Id": "2bb9a821-f86b-4190-a182-64abe2c940ed", "Type": 3, - "Position": "10, 601" + "Position": "36, 600" }, { "X": 91.0, "Y": 598.0, "Rotation": 0.0, + "Length": 0.0, "Id": "821598e1-091a-4884-96fe-6ed5f43c4f62", "Type": 3, "Position": "91, 598" @@ -761,6 +590,7 @@ "X": 184.0, "Y": 596.0, "Rotation": 0.0, + "Length": 0.0, "Id": "66c1bbee-89a8-45a9-b585-ddfd59768f6b", "Type": 3, "Position": "184, 596" @@ -769,6 +599,7 @@ "X": 381.0, "Y": 355.0, "Rotation": 91.245364266768377, + "Length": 0.0, "Id": "06a10f46-bda8-4b0f-9e7a-63d66bd2f7e4", "Type": 3, "Position": "381, 355" @@ -777,6 +608,7 @@ "X": 519.0, "Y": 550.0, "Rotation": 0.0, + "Length": 0.0, "Id": "835b8982-042b-4e2e-a83b-19b32e55cd5b", "Type": 3, "Position": "519, 550" @@ -785,12 +617,12 @@ "Magnets": [ { "P1": { - "X": 358.53781512605048, - "Y": 628.438429217841 + "X": 438.0, + "Y": 623.0 }, "P2": { - "X": -119.57759581805529, - "Y": 626.78449598889756 + "X": -216.0, + "Y": 628.0 }, "ControlPoint": null, "Id": "5a0edec2-7ac3-4c99-bbb4-8debde0c1d07", @@ -798,73 +630,8 @@ }, { "P1": { - "X": -75.0847526191485, - "Y": 716.14155614369508 - }, - "P2": { - "X": -73.1298113651053, - "Y": 561.63119233707459 - }, - "ControlPoint": null, - "Id": "def7c4b9-86db-42eb-aae6-0c6c9bedcc30", - "Type": 4 - }, - { - "P1": { - "X": 12.69302515862924, - "Y": 717.25266725480617 - }, - "P2": { - "X": 11.870188634894689, - "Y": 560.25023995612207 - }, - "ControlPoint": null, - "Id": "624327ee-be0f-4373-b60a-786a93c1eabf", - "Type": 4 - }, - { - "P1": { - "X": 90.470802936406983, - "Y": 716.14155614369508 - }, - "P2": { - "X": 89.727331492037507, - "Y": 560.91690662278882 - }, - "ControlPoint": null, - "Id": "f1e885ae-55f7-42e9-b3aa-648541e97da0", - "Type": 4 - }, - { - "P1": { - "X": 185.470802936407, - "Y": 725.03044503258388 - }, - "P2": { - "X": 181.87018863489462, - "Y": 563.059763765646 - }, - "ControlPoint": null, - "Id": "dc3e8061-2c99-4f24-ac9b-4020dd91fa8b", - "Type": 4 - }, - { - "P1": { - "X": 343.98784548784573, - "Y": 353.92216117216128 - }, - "P2": { - "X": 472.59413991107186, - "Y": 353.73144759338567 - }, - "ControlPoint": null, - "Id": "f4c97a5a-2c2c-4b5e-9dd5-332b1670b827", - "Type": 4 - }, - { - "P1": { - "X": 519.32722832722823, - "Y": 720.25490196078408 + "X": 520.0, + "Y": 565.0 }, "P2": { "X": 516.16556848250036, @@ -876,12 +643,12 @@ }, { "P1": { - "X": 249.62404756406329, - "Y": 449.77059623383167 + "X": 468.0, + "Y": 504.0 }, "P2": { - "X": 518.08558602560174, - "Y": 452.84751931075476 + "X": 332.0, + "Y": 508.0 }, "ControlPoint": null, "Id": "0bbb27a4-2355-4294-9f2d-a40e4d3d2930", @@ -889,11 +656,11 @@ }, { "P1": { - "X": 225.77789371790945, - "Y": 271.30905777229322 + "X": 371.0, + "Y": 270.0 }, "P2": { - "X": 463.66556848250042, + "X": 468.66556848250042, "Y": 269.44573330767145 }, "ControlPoint": null, @@ -918,111 +685,41 @@ }, { "P1": { - "X": 473.41452991452985, - "Y": 353.032679738562 + "X": 454.0, + "Y": 504.0 }, "P2": { - "X": 515.63675213675208, - "Y": 399.14379084967311 - }, - "ControlPoint": { - "X": 521.74786324786317, - "Y": 351.3660130718954 - }, - "Id": "0650b2cb-57f9-44ec-9787-fab878cf2b47", - "Type": 4 - }, - { - "P1": { - "X": 465.08119658119654, - "Y": 450.2549019607842 - }, - "P2": { - "X": 518.24918584253533, - "Y": 519.05971509141114 - }, - "ControlPoint": { - "X": 522.85897435897425, - "Y": 450.8104575163398 - }, - "Id": "7007db10-b61b-4726-9775-417951454ddf", - "Type": 4 - }, - { - "P1": { - "X": 473.30842562535759, - "Y": 351.58859045052856 - }, - "P2": { - "X": 515.4512827682147, - "Y": 309.80287616481428 - }, - "ControlPoint": { - "X": 519.02271133964325, - "Y": 358.01716187909994 - }, - "Id": "4ef4bfd0-8fc4-48a5-a490-92b35c7fd1c3", - "Type": 4 - }, - { - "P1": { - "X": 464.73699705392903, - "Y": 450.51716187909989 - }, - "P2": { - "X": 516.52271133964325, - "Y": 402.66001902195711 - }, - "ControlPoint": { - "X": 516.16556848250036, - "Y": 454.44573330767133 - }, - "Id": "7d18ae7e-7926-4cf4-8dd6-861462e31352", - "Type": 4 - }, - { - "P1": { - "X": 399.76697627462613, - "Y": 521.72108802571984 - }, - "P2": { - "X": 462.61739821419411, - "Y": 449.66152357736206 - }, - "ControlPoint": { - "X": 391.99239821419411, - "Y": 447.16152357736206 - }, - "Id": "532be14d-170e-45c5-adcf-0d555b83b010", - "Type": 4 - }, - { - "P1": { - "X": 399.05269056034041, - "Y": 523.50680231143417 - }, - "P2": { - "X": 400.11132581222392, - "Y": 580.622765294022 + "X": 517.0, + "Y": 434.0 }, "ControlPoint": null, - "Id": "086955e0-0542-4ab5-9ccf-8880e749722a", + "Id": "N032", "Type": 4 }, { "P1": { - "X": 353.86132581222392, - "Y": 629.37276529402186 + "X": 434.0, + "Y": 624.0 }, "P2": { - "X": 400.11132581222392, - "Y": 576.872765294022 + "X": 437.0, + "Y": 506.0 }, - "ControlPoint": { - "X": 404.48632581222392, - "Y": 630.62276529402186 + "ControlPoint": null, + "Id": "N031", + "Type": 4 + }, + { + "P1": { + "X": 436.0, + "Y": 508.0 }, - "Id": "2ac7d720-a6df-4174-be74-15ddfe96459b", + "P2": { + "X": 438.0, + "Y": 352.0 + }, + "ControlPoint": null, + "Id": "N030", "Type": 4 } ], @@ -1030,6 +727,6 @@ "BackgroundColorArgb": -14671840, "ShowGrid": false }, - "CreatedDate": "2026-01-20T10:36:03.7843912+09:00", + "CreatedDate": "2026-02-10T12:53:40.5602334+09:00", "Version": "1.3" } \ No newline at end of file