using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Project.StateMachine; using COMM; using AR; using AGVNavigationCore.Models; namespace Project.ViewForm { public partial class fAuto : Form { public fAuto() { InitializeComponent(); this.FormClosed += FAuto_FormClosed; PUB.sm.StepChanged += Sm_StepChanged; this.ctlAuto1.ButtonClick += CtlAuto1_ButtonClick; if (PUB.sm.Step == eSMStep.INIT || PUB.sm.Step == eSMStep.SYNC) this.ctlAuto1.Scean = CtlAuto.eScean.Progress; else this.ctlAuto1.Scean = CtlAuto.eScean.Normal; InitializeMapCanvas(); } private void InitializeMapCanvas() { PUB._mapCanvas.NodeSelect += OnNodeSelected;; // 스플리터 패널에 맵 캔버스 추가 panel1.Controls.Add(PUB._mapCanvas); } private void OnNodeSelected(object sender, NodeBase node, MouseEventArgs e) { 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; ContextMenuStrip menu = new ContextMenuStrip(); // PickOn var pickOn = new ToolStripMenuItem("Pick On (Move & Pick)"); pickOn.Click += (s, args) => ExecuteManualCommand(mapnode, ENIGProtocol.AGVCommandHE.PickOn); menu.Items.Add(pickOn); // PickOff var pickOff = new ToolStripMenuItem("Pick Off (Move & Drop)"); pickOff.Click += (s, args) => ExecuteManualCommand(mapnode, ENIGProtocol.AGVCommandHE.PickOff); menu.Items.Add(pickOff); // Charge if (mapnode.StationType == StationType.Charger) { var charge = new ToolStripMenuItem("Charge (Move & Charge)"); charge.Click += (s, args) => ExecuteManualCommand(mapnode, ENIGProtocol.AGVCommandHE.Charger); menu.Items.Add(charge); } menu.Show(Cursor.Position); } private void ExecuteManualCommand(MapNode targetNode, ENIGProtocol.AGVCommandHE cmd) { if (PUB._virtualAGV.CurrentNode == null) { MessageBox.Show("AGV의 현재 위치를 알 수 없습니다."); return; } if (PUB.sm.Step == eSMStep.IDLE) { if (MessageBox.Show("현재 대기상태가 아닙니다. 강제로 실행하시겠습니까?", "Warning", MessageBoxButtons.YesNo) == DialogResult.No) return; } if (targetNode.isDockingNode == false) { UTIL.MsgE("이동 가능한 노드가 아닙니다"); } // 1. 경로 생성 var pathFinder = new AGVNavigationCore.PathFinding.Planning.AGVPathfinder(PUB._mapCanvas.Nodes); // 현재위치에서 목표위치까지 var result = pathFinder.FindPath(PUB._virtualAGV.CurrentNode, targetNode); if (!result.Success || result.Path == null || result.Path.Count == 0) { MessageBox.Show("경로를 찾을 수 없습니다."); return; } // 2. 상태 설정 // 2. 상태 설정 if (targetNode is MapNode mapno) PUB.log.AddI($"[Manual Command] {cmd} to {mapno.RfidId}({targetNode.Id})"); else PUB.log.AddI($"[Manual Command] {cmd} to ({targetNode.Id})"); // FindPathResult contains DetailedPath already. PUB._virtualAGV.SetPath(result); PUB._virtualAGV.TargetNode = targetNode as MapNode; // 3. 작업 설정 PUB.NextWorkCmd = cmd; // 4. 실행 PUB.sm.SetNewRunStep(ERunStep.GOTO); // GOTO -> Arrive -> _IN sequence execution } private void fAuto_Load(object sender, EventArgs e) { ctlAuto1.dev_agv = PUB.AGV; // ctlAuto1.dev_plc = PUB.PLC; ctlAuto1.dev_bms = PUB.BMS; ctlAuto1.dev_xbe = PUB.XBE; PUB.AGV.DataReceive += AGV_DataReceive; //var fn = string.Empty; //if (files.Any() == false) //{ // fn = AR.UTIL.MakePath("sample.route"); //} //else if (files.Count() == 1) //{ // fn = files.First().FullName; //} //if (fn.isEmpty() == false) //{ // var fi = new System.IO.FileInfo(AR.UTIL.CurrentPath + "\\sample.route"); // if (fi.Exists) // { // PUB.log.Add($"autoload : {fi.FullName}"); // var rlt = PUB.mapctl.LoadFromFile(fi.FullName, out string errmsg); // if (rlt == false) AR.UTIL.MsgE(errmsg); // } //} this.timer1.Start(); // Set Run Mode PUB._mapCanvas.Mode = AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Run; } private void AGV_DataReceive(object sender, arDev.Narumi.DataEventArgs e) { switch (e.DataType) { case arDev.Narumi.DataType.TAG: //_AGV 파일에서 위치조정을 함 //var tagno = PUB.AGV.data.TagNo; //PUB.mapctl.SetCurrentPosition(tagno); break; } } private void CtlAuto1_ButtonClick(CtlAuto.UIButton idx) { if (idx.cmd.Equals("EMG")) { PUB.log.Add("ui reset click"); PUB.AGV.AGVErrorReset(); } } private void Sm_StepChanged(object sender, StateMachine.StateMachine.StepChangeEventArgs e) { if (e.New == eSMStep.INIT || e.New == eSMStep.SYNC) this.ctlAuto1.Scean = CtlAuto.eScean.Progress; else this.ctlAuto1.Scean = CtlAuto.eScean.Normal; } private void FAuto_FormClosed(object sender, FormClosedEventArgs e) { timer1.Stop(); 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; private void fAuto_VisibleChanged(object sender, EventArgs e) { this.timer1.Enabled = this.Visible; if (timer1.Enabled) { timer1.Start(); // 화면이 보일 때 Run 모드로 강제 설정 if (PUB._mapCanvas.Mode != AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Run) { PUB._mapCanvas.Mode = AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Run; } } else timer1.Stop(); } private void timer1_Tick_1(object sender, EventArgs e) { //if (this.Visible == false) return; //if (tmrun == true) return; //tmrun = true; //this.ctlAuto1.OnUpdateMode = true; //if (this.ctlAuto1.Scean == CtlAuto.eScean.Progress) //{ // ctlAuto1.ProgressVal = PUB.Result.SMSG_ProgressValue; // ctlAuto1.ProgressMax = PUB.Result.SMSG_ProgressMax; // ctlAuto1.StatusMessage = VAR.STR?.Get(eVarString.StatusMessage) ?? string.Empty; //} //this.ctlAuto1.StopMessage = string.Empty; //if (PUB.sm.Step == StateMachine.eSMStep.RUN) //{ // this.ctlAuto1.runStep = PUB.sm.RunStep; //} //else //{ // this.ctlAuto1.runStep = ERunStep.READY; //} //this.ctlAuto1.OnUpdateMode = false; //this.ctlAuto1.Invalidate(); //PUB.mapctl.PredictNextAction(); //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; } } } }