From 4bdc36040d3d6d2c6e3e53811698af7c6f5a6338 Mon Sep 17 00:00:00 2001 From: LGram16 Date: Thu, 18 Dec 2025 00:38:59 +0900 Subject: [PATCH] "feat:Implement-Canvas-Run-Mode-and-Logic" --- .../Controls/UnifiedAGVCanvas.Mouse.cs | 3 + .../Controls/UnifiedAGVCanvas.cs | 3 +- Cs_HMI/Project/ViewForm/fAuto.cs | 60 ++++++++++++++++++- Cs_HMI/Project/ViewForm/fAuto.cs.new | 55 +++++++++++++++++ 4 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 Cs_HMI/Project/ViewForm/fAuto.cs.new diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Mouse.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Mouse.cs index c6d81be..d094aee 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Mouse.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Mouse.cs @@ -119,6 +119,9 @@ namespace AGVNavigationCore.Controls var worldPoint = ScreenToWorld(e.Location); var hitNode = GetItemAt(worldPoint); + // 가동 모드에서는 더블클릭 편집 방지 + if (_canvasMode == CanvasMode.Run) return; + if (hitNode == null) return; if (hitNode.Type == NodeType.Normal) diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs index 4555ef3..798e760 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs @@ -39,7 +39,8 @@ namespace AGVNavigationCore.Controls { Edit, // 편집 가능 (맵 에디터) Sync, // 동기화 모드 (장비 설정 동기화) - Emulator // 에뮬레이터 모드 + Emulator, // 에뮬레이터 모드 + Run // 가동 모드 (User Request) } /// diff --git a/Cs_HMI/Project/ViewForm/fAuto.cs b/Cs_HMI/Project/ViewForm/fAuto.cs index 4e9d3c9..41fbf47 100644 --- a/Cs_HMI/Project/ViewForm/fAuto.cs +++ b/Cs_HMI/Project/ViewForm/fAuto.cs @@ -43,11 +43,19 @@ namespace Project.ViewForm private void OnNodeSelected(object sender, NodeBase node, MouseEventArgs e) { - if (e.Button != MouseButtons.Right) return; if (node == null) return; var mapnode = node as MapNode; if (mapnode == null) return; + // [Run Mode] Left Click: AGV Operation + if (PUB._mapCanvas.Mode == AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Run && e.Button == MouseButtons.Left) + { + HandleRunModeClick(mapnode); + return; + } + + if (e.Button != MouseButtons.Right) return; + // 도킹 가능한 노드인지 또는 작업 노드인지 확인 if (mapnode.isDockingNode == false) return; @@ -158,6 +166,9 @@ namespace Project.ViewForm //} this.timer1.Start(); + + // Set Run Mode + PUB._mapCanvas.Mode = AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Run; } private void AGV_DataReceive(object sender, arDev.Narumi.DataEventArgs e) { @@ -194,6 +205,9 @@ namespace Project.ViewForm PUB.sm.StepChanged -= Sm_StepChanged; this.ctlAuto1.ButtonClick -= CtlAuto1_ButtonClick; PUB.AGV.DataReceive -= AGV_DataReceive; + + // Reset Mode to Edit + PUB._mapCanvas.Mode = AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Edit; } bool tmrun = false; @@ -236,5 +250,49 @@ namespace Project.ViewForm //tmrun = false; } + + private void HandleRunModeClick(MapNode targetNode) + { + if (targetNode == null) return; + + ENIGProtocol.AGVCommandHE targetCmd = ENIGProtocol.AGVCommandHE.Goto; + string confirmMsg = ""; + + if (targetNode.StationType == StationType.Charger) + { + if (MessageBox.Show($"[{targetNode.Id}] 충전기로 이동하여 충전을 진행하시겠습니까?", "작업 확인", MessageBoxButtons.YesNo) == DialogResult.Yes) + { + targetCmd = ENIGProtocol.AGVCommandHE.Charger; + ExecuteManualCommand(targetNode, targetCmd); + } + return; + } + else if (targetNode.isDockingNode) + { + // Loader, Unloader, Buffer, Cleaner - Pick/Drop Selection + ContextMenuStrip menu = new ContextMenuStrip(); + + var pickOn = new ToolStripMenuItem("Pick On (Move & Pick)"); + pickOn.Click += (s, args) => ExecuteManualCommand(targetNode, ENIGProtocol.AGVCommandHE.PickOn); + menu.Items.Add(pickOn); + + var pickOff = new ToolStripMenuItem("Pick Off (Move & Drop)"); + pickOff.Click += (s, args) => ExecuteManualCommand(targetNode, ENIGProtocol.AGVCommandHE.PickOff); + menu.Items.Add(pickOff); + + menu.Show(Cursor.Position); + return; + } + else + { + // Normal Node + if (MessageBox.Show($"[{targetNode.Id}] 노드로 이동하시겠습니까?", "이동 확인", MessageBoxButtons.YesNo) == DialogResult.Yes) + { + targetCmd = ENIGProtocol.AGVCommandHE.Goto; + ExecuteManualCommand(targetNode, targetCmd); + } + return; + } + } } } diff --git a/Cs_HMI/Project/ViewForm/fAuto.cs.new b/Cs_HMI/Project/ViewForm/fAuto.cs.new new file mode 100644 index 0000000..3d40426 --- /dev/null +++ b/Cs_HMI/Project/ViewForm/fAuto.cs.new @@ -0,0 +1,55 @@ + private void HandleRunModeClick(MapNode targetNode) + { + if (targetNode == null) return; + + ENIGProtocol.AGVCommandHE targetCmd = ENIGProtocol.AGVCommandHE.Move; + string confirmMsg = ""; + + if (targetNode.StationType == StationType.Charger) + { + if (MessageBox.Show($"[{targetNode.Id}] 충전기로 이동하여 충전을 진행하시겠습니까?", "작업 확인", MessageBoxButtons.YesNo) == DialogResult.Yes) + { + targetCmd = ENIGProtocol.AGVCommandHE.Charger; + ExecuteManualCommand(targetNode, targetCmd); + } + return; + } + else if (targetNode.isDockingNode) + { + // Loader, Unloader, Buffer, Cleaner + // Custom Dialog needed for PickOn / PickOff + // For now, let's use a simple MessageBox or just a ContextMenu like logic? + // User said: "click -> PickOn/Off selection -> Execute" + // A context menu at cursor position is good for selection. + + ContextMenuStrip menu = new ContextMenuStrip(); + + var pickOn = new ToolStripMenuItem("Pick On (Move & Pick)"); + pickOn.Click += (s, args) => ExecuteManualCommand(targetNode, ENIGProtocol.AGVCommandHE.PickOn); + menu.Items.Add(pickOn); + + var pickOff = new ToolStripMenuItem("Pick Off (Move & Drop)"); + pickOff.Click += (s, args) => ExecuteManualCommand(targetNode, ENIGProtocol.AGVCommandHE.PickOff); + menu.Items.Add(pickOff); + + menu.Show(Cursor.Position); + return; + } + else + { + // Normal Node + if (MessageBox.Show($"[{targetNode.Id}] 노드로 이동하시겠습니까?", "이동 확인", MessageBoxButtons.YesNo) == DialogResult.Yes) + { + targetCmd = ENIGProtocol.AGVCommandHE.Move; // Or PickOff as default move? usually just Move logic inside ExecuteManualCommand handles GOTO. + // But ExecuteManualCommand takes AGVCommandHE. + // Let's check ENIGProtocol.AGVCommandHE. + // If not present, we might need to modify ExecuteManualCommand or just pass PickOff as dummy? + // Actually ExecuteManualCommand uses 'cmd' for log and 'PUB.NextWorkCmd'. + // If 'Move', just GOTO. + // Let's assume 'Move' exists or we use 'PickOff' (Move & Drop often implies MoveTo). + // Or we check `ENIGProtocol.AGVCommandHE`. + ExecuteManualCommand(targetNode, ENIGProtocol.AGVCommandHE.Move); + } + return; + } + }