From 90340f4a7d88b94e6b0875659703cd8430a138ac Mon Sep 17 00:00:00 2001 From: backuppc Date: Tue, 6 Jan 2026 17:35:34 +0900 Subject: [PATCH] .. --- AGVEmulator/AGVEmulator.csproj | 6 + AGVEmulator/DevXBE.cs | 49 +- AGVEmulator/fMain.Designer.cs | 436 +++---- AGVEmulator/fMain.cs | 41 +- AGVEmulator/icons8-robot-80.ico | Bin 0 -> 113050 bytes .../Controls/UnifiedAGVCanvas.Events.cs | 8 + .../Controls/UnifiedAGVCanvas.cs | 11 + .../AGVNavigationCore/Models/MapNode.cs | 16 +- .../AGVNavigationCore/Models/NodeBase.cs | 3 +- .../AGVNavigationCore/Models/VirtualAGV.cs | 59 +- .../PathFinding/Core/AGVPathResult.cs | 2 +- .../PathFinding/Planning/NodeMotorInfo.cs | 2 + Cs_HMI/Project/Class/Lang.cs | 6 +- Cs_HMI/Project/Device/Xbee.cs | 45 +- Cs_HMI/Project/StateMachine/Step/_SM_RUN.cs | 148 +-- .../StateMachine/Step/_SM_RUN_BUFFER_IN.cs | 174 ++- .../StateMachine/Step/_SM_RUN_BUFFER_OUT.cs | 81 +- .../StateMachine/Step/_SM_RUN_CLEANER_IN.cs | 16 +- .../StateMachine/Step/_SM_RUN_CLEANER_OUT.cs | 19 +- .../StateMachine/Step/_SM_RUN_GOCHARGE.cs | 27 +- .../StateMachine/Step/_SM_RUN_GOHOME.cs | 4 +- .../Project/StateMachine/Step/_SM_RUN_GOTO.cs | 4 +- .../StateMachine/Step/_SM_RUN_LOADER_IN.cs | 13 +- .../StateMachine/Step/_SM_RUN_LOADER_OUT.cs | 20 +- .../StateMachine/Step/_SM_RUN_POSCHK.cs | 10 +- .../StateMachine/Step/_SM_RUN_UNLOADER_IN.cs | 18 +- .../StateMachine/Step/_SM_RUN_UNLOADER_OUT.cs | 20 +- Cs_HMI/Project/StateMachine/Step/_Util.cs | 103 +- Cs_HMI/Project/StateMachine/_AGV.cs | 42 +- Cs_HMI/Project/StateMachine/_Loop.cs | 11 +- Cs_HMI/Project/StateMachine/_Xbee.cs | 54 +- Cs_HMI/Project/ViewForm/fManual.cs | 26 +- Cs_HMI/Project/fMain.cs | 5 +- Cs_HMI/Project/fSetup.cs | 22 +- Cs_HMI/SubProject/AGV/Command.cs | 273 +++-- Cs_HMI/SubProject/AGV/Narumi.cs | 30 +- Cs_HMI/SubProject/CommData/Enum.cs | 1 + Cs_HMI/SubProject/EnigProtocol | 2 +- Document/NewMap.json | 1005 +++++++++++++++++ 39 files changed, 2127 insertions(+), 685 deletions(-) create mode 100644 AGVEmulator/icons8-robot-80.ico create mode 100644 Document/NewMap.json diff --git a/AGVEmulator/AGVEmulator.csproj b/AGVEmulator/AGVEmulator.csproj index 5a22417..b8e128e 100644 --- a/AGVEmulator/AGVEmulator.csproj +++ b/AGVEmulator/AGVEmulator.csproj @@ -34,6 +34,9 @@ 4 false + + icons8-robot-80.ico + ..\Cs_HMI\DLL\arControl.Net4.dll @@ -136,5 +139,8 @@ ENIGProtocol + + + \ No newline at end of file diff --git a/AGVEmulator/DevXBE.cs b/AGVEmulator/DevXBE.cs index 2bdfd5f..efd096f 100644 --- a/AGVEmulator/DevXBE.cs +++ b/AGVEmulator/DevXBE.cs @@ -61,29 +61,52 @@ namespace AGVEmulator /// public void SendGotoTag(byte id, uint tag) { - var idSTR = id.ToString("X2"); - var tagSTR = tag.ToString("0000"); - var dataStr = $"{idSTR}{tagSTR}"; - Send(ENIGProtocol.AGVCommandHE.Goto, dataStr); + //var idSTR = id.ToString("X2"); + //var tagSTR = tag.ToString("0000"); + //var dataStr = $"{idSTR}{tagSTR}"; + var data = new List(); + data.Add(id); + data.AddRange(System.Text.Encoding.Default.GetBytes(tag.ToString("0000"))); + Send(ENIGProtocol.AGVCommandHE.Goto, data.ToArray()); } + + /// + /// 카트를 가지러 들어간다 + /// + /// + public void SendPickOn(byte id) + { + var data = new List(); + data.Add(id); + Send(ENIGProtocol.AGVCommandHE.PickOn, data.ToArray()); + } + + /// + /// 카트를 내려놓는다 + /// + /// + public void SendPickOff(byte id) + { + var data = new List(); + data.Add(id); + Send(ENIGProtocol.AGVCommandHE.PickOff, data.ToArray()); + } + public void SendCurrentPos(byte id, uint tag) { - var idSTR = id.ToString("X2"); - var tagSTR = tag.ToString("0000"); - var dataStr = $"{idSTR}{tagSTR}"; - Send(ENIGProtocol.AGVCommandHE.SetCurrent, dataStr); + var data = new List(); + data.Add(id); + data.AddRange(System.Text.Encoding.Default.GetBytes(tag.ToString("0000"))); + Send(ENIGProtocol.AGVCommandHE.SetCurrent, data.ToArray()); } - private void Send(ENIGProtocol.AGVCommandHE Command, string datastr) + private void Send(ENIGProtocol.AGVCommandHE Command, byte[] data) { byte id = 0; byte cmd = (byte)Command; //move to target - byte[] data = null; - if (datastr != null && string.IsNullOrEmpty(datastr) == false) - data = System.Text.Encoding.Default.GetBytes(datastr); var packet = proto.CreatePacket(id, cmd, data); if (WriteData(packet, false)) { - var hexstr = System.Text.Encoding.Default.GetString(data); + var hexstr =(data == null || data.Any()==false) ? string.Empty : System.Text.Encoding.Default.GetString(data); RaiseMessage(MessageType.Send, $"ID:{id},CMD:{cmd},DATA:{hexstr}"); } } diff --git a/AGVEmulator/fMain.Designer.cs b/AGVEmulator/fMain.Designer.cs index 1757da5..c06e5f9 100644 --- a/AGVEmulator/fMain.Designer.cs +++ b/AGVEmulator/fMain.Designer.cs @@ -32,7 +32,6 @@ namespace AGVEmulator private void InitializeComponent() { this.components = new System.ComponentModel.Container(); - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(fMain)); AGVEmulator.UC.AgvViewer.ptdata ptdata57 = new AGVEmulator.UC.AgvViewer.ptdata(); AGVEmulator.UC.AgvViewer.ptdata ptdata58 = new AGVEmulator.UC.AgvViewer.ptdata(); AGVEmulator.UC.AgvViewer.ptdata ptdata59 = new AGVEmulator.UC.AgvViewer.ptdata(); @@ -61,6 +60,7 @@ namespace AGVEmulator AGVEmulator.UC.AgvViewer.ptdata ptdata82 = new AGVEmulator.UC.AgvViewer.ptdata(); AGVEmulator.UC.AgvViewer.ptdata ptdata83 = new AGVEmulator.UC.AgvViewer.ptdata(); AGVEmulator.UC.AgvViewer.ptdata ptdata84 = new AGVEmulator.UC.AgvViewer.ptdata(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(fMain)); this.groupBox1 = new System.Windows.Forms.GroupBox(); this.rtBMS = new arCtl.LogTextBox(); this.panel1 = new System.Windows.Forms.Panel(); @@ -86,6 +86,7 @@ namespace AGVEmulator this.label2 = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label(); this.trackBar1 = new System.Windows.Forms.TrackBar(); + this.serBMS = new AGVEmulator.SerialConn(); this.rtAGV = new arCtl.LogTextBox(); this.panel4 = new System.Windows.Forms.Panel(); this.groupBox9 = new System.Windows.Forms.GroupBox(); @@ -127,6 +128,7 @@ namespace AGVEmulator this.button4 = new System.Windows.Forms.Button(); this.groupBox3 = new System.Windows.Forms.GroupBox(); this.rtCAL = new arCtl.LogTextBox(); + this.serCAL = new AGVEmulator.SerialConn(); this.timer1 = new System.Windows.Forms.Timer(this.components); this.tabControl1 = new System.Windows.Forms.TabControl(); this.tabPage4 = new System.Windows.Forms.TabPage(); @@ -134,6 +136,8 @@ namespace AGVEmulator this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); this.rtAGVPro = new arCtl.LogTextBox(); this.panel12 = new System.Windows.Forms.Panel(); + this.agvViewer1 = new AGVEmulator.UC.AgvViewer(); + this.serAGV = new AGVEmulator.SerialConn(); this.tabPage2 = new System.Windows.Forms.TabPage(); this.tabPage3 = new System.Windows.Forms.TabPage(); this.panel3 = new System.Windows.Forms.Panel(); @@ -152,6 +156,10 @@ namespace AGVEmulator this.toolStrip1 = new System.Windows.Forms.ToolStrip(); this.toolStripButton1 = new System.Windows.Forms.ToolStripButton(); this.toolStripButton2 = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.toolStripButton3 = new System.Windows.Forms.ToolStripButton(); + this.toolStripButton4 = new System.Windows.Forms.ToolStripButton(); + this.toolStripButton5 = new System.Windows.Forms.ToolStripButton(); this.statusStrip1 = new System.Windows.Forms.StatusStrip(); this.toolStripStatusLabel8 = new System.Windows.Forms.ToolStripStatusLabel(); this.sbAGV = new System.Windows.Forms.ToolStripStatusLabel(); @@ -159,14 +167,8 @@ namespace AGVEmulator this.sbBMS = new System.Windows.Forms.ToolStripStatusLabel(); this.toolStripStatusLabel2 = new System.Windows.Forms.ToolStripStatusLabel(); this.sbCAL = new System.Windows.Forms.ToolStripStatusLabel(); - this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); - this.toolStripButton3 = new System.Windows.Forms.ToolStripButton(); - this.agvViewer1 = new AGVEmulator.UC.AgvViewer(); - this.serAGV = new AGVEmulator.SerialConn(); - this.serBMS = new AGVEmulator.SerialConn(); - this.serCAL = new AGVEmulator.SerialConn(); - this.toolStripButton4 = new System.Windows.Forms.ToolStripButton(); - this.toolStripButton5 = new System.Windows.Forms.ToolStripButton(); + this.button2 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); this.groupBox1.SuspendLayout(); this.panel1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.trbT2)).BeginInit(); @@ -501,6 +503,18 @@ namespace AGVEmulator this.trackBar1.Value = 7000; this.trackBar1.Scroll += new System.EventHandler(this.trackBar1_Scroll); // + // serBMS + // + this.serBMS.BaudRate = 9600; + this.serBMS.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.serBMS.dev = null; + this.serBMS.Dock = System.Windows.Forms.DockStyle.Top; + this.serBMS.Location = new System.Drawing.Point(3, 17); + this.serBMS.Name = "serBMS"; + this.serBMS.PortName = "COM31"; + this.serBMS.Size = new System.Drawing.Size(1134, 84); + this.serBMS.TabIndex = 1; + // // rtAGV // this.rtAGV.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(24)))), ((int)(((byte)(24)))), ((int)(((byte)(24))))); @@ -984,6 +998,18 @@ namespace AGVEmulator this.rtCAL.TabIndex = 2; this.rtCAL.Text = ""; // + // serCAL + // + this.serCAL.BaudRate = 9600; + this.serCAL.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.serCAL.dev = null; + this.serCAL.Dock = System.Windows.Forms.DockStyle.Top; + this.serCAL.Location = new System.Drawing.Point(3, 17); + this.serCAL.Name = "serCAL"; + this.serCAL.PortName = "COM41"; + this.serCAL.Size = new System.Drawing.Size(776, 84); + this.serCAL.TabIndex = 1; + // // timer1 // this.timer1.Interval = 200; @@ -1069,6 +1095,149 @@ namespace AGVEmulator this.panel12.Size = new System.Drawing.Size(1140, 120); this.panel12.TabIndex = 5; // + // agvViewer1 + // + this.agvViewer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.agvViewer1.FontMrk = new System.Drawing.Font("Microsoft Sans Serif", 7F); + this.agvViewer1.FontTag = new System.Drawing.Font("Microsoft Sans Serif", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.agvViewer1.lastmark = ""; + this.agvViewer1.lastmarkdir = ""; + this.agvViewer1.lasttag = ""; + this.agvViewer1.lasttagdir = ""; + ptdata57.active = false; + ptdata57.data = "NOT"; + ptdata57.pos = 30F; + ptdata58.active = false; + ptdata58.data = "QA"; + ptdata58.pos = 200F; + ptdata59.active = false; + ptdata59.data = "CHG"; + ptdata59.pos = 300F; + ptdata60.active = false; + ptdata60.data = "QC"; + ptdata60.pos = 400F; + ptdata61.active = false; + ptdata61.data = "#FVI-1"; + ptdata61.pos = 500F; + ptdata62.active = false; + ptdata62.data = "#FVI-2"; + ptdata62.pos = 600F; + ptdata63.active = false; + ptdata63.data = "#FVI-3"; + ptdata63.pos = 700F; + ptdata64.active = false; + ptdata64.data = "#FVI-4"; + ptdata64.pos = 800F; + ptdata65.active = false; + ptdata65.data = "#FVI-5"; + ptdata65.pos = 900F; + ptdata66.active = false; + ptdata66.data = "POT"; + ptdata66.pos = 970F; + this.agvViewer1.listMRK = new AGVEmulator.UC.AgvViewer.ptdata[] { + ptdata57, + ptdata58, + ptdata59, + ptdata60, + ptdata61, + ptdata62, + ptdata63, + ptdata64, + ptdata65, + ptdata66}; + ptdata67.active = false; + ptdata67.data = "9000"; + ptdata67.pos = 80F; + ptdata68.active = false; + ptdata68.data = "9001"; + ptdata68.pos = 120F; + ptdata69.active = false; + ptdata69.data = "9010"; + ptdata69.pos = 180F; + ptdata70.active = false; + ptdata70.data = "9011"; + ptdata70.pos = 220F; + ptdata71.active = false; + ptdata71.data = "9020"; + ptdata71.pos = 280F; + ptdata72.active = false; + ptdata72.data = "9021"; + ptdata72.pos = 320F; + ptdata73.active = false; + ptdata73.data = "9030"; + ptdata73.pos = 380F; + ptdata74.active = false; + ptdata74.data = "9031"; + ptdata74.pos = 420F; + ptdata75.active = false; + ptdata75.data = "9040"; + ptdata75.pos = 480F; + ptdata76.active = false; + ptdata76.data = "9041"; + ptdata76.pos = 520F; + ptdata77.active = false; + ptdata77.data = "9050"; + ptdata77.pos = 580F; + ptdata78.active = false; + ptdata78.data = "9051"; + ptdata78.pos = 620F; + ptdata79.active = false; + ptdata79.data = "9060"; + ptdata79.pos = 680F; + ptdata80.active = false; + ptdata80.data = "9061"; + ptdata80.pos = 720F; + ptdata81.active = false; + ptdata81.data = "9070"; + ptdata81.pos = 780F; + ptdata82.active = false; + ptdata82.data = "9071"; + ptdata82.pos = 820F; + ptdata83.active = false; + ptdata83.data = "9000"; + ptdata83.pos = 10F; + ptdata84.active = false; + ptdata84.data = "9001"; + ptdata84.pos = 50F; + this.agvViewer1.listTAG = new AGVEmulator.UC.AgvViewer.ptdata[] { + ptdata67, + ptdata68, + ptdata69, + ptdata70, + ptdata71, + ptdata72, + ptdata73, + ptdata74, + ptdata75, + ptdata76, + ptdata77, + ptdata78, + ptdata79, + ptdata80, + ptdata81, + ptdata82, + ptdata83, + ptdata84}; + this.agvViewer1.Location = new System.Drawing.Point(241, 0); + this.agvViewer1.Name = "agvViewer1"; + this.agvViewer1.Size = new System.Drawing.Size(899, 120); + this.agvViewer1.StopbyMark = false; + this.agvViewer1.TabIndex = 0; + this.agvViewer1.Text = "agvViewer1"; + this.agvViewer1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.agvViewer1_MouseDown); + // + // serAGV + // + this.serAGV.BaudRate = 9600; + this.serAGV.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.serAGV.dev = null; + this.serAGV.Dock = System.Windows.Forms.DockStyle.Left; + this.serAGV.Location = new System.Drawing.Point(0, 0); + this.serAGV.Name = "serAGV"; + this.serAGV.PortName = "COM21"; + this.serAGV.Size = new System.Drawing.Size(241, 120); + this.serAGV.TabIndex = 0; + // // tabPage2 // this.tabPage2.Controls.Add(this.groupBox1); @@ -1093,6 +1262,8 @@ namespace AGVEmulator // // panel3 // + this.panel3.Controls.Add(this.button3); + this.panel3.Controls.Add(this.button2); this.panel3.Controls.Add(this.nudIDAgv); this.panel3.Controls.Add(this.label7); this.panel3.Controls.Add(this.numericUpDown2); @@ -1288,6 +1459,38 @@ namespace AGVEmulator this.toolStripButton2.Text = "All Close"; this.toolStripButton2.Click += new System.EventHandler(this.toolStripButton2_Click); // + // toolStripSeparator1 + // + this.toolStripSeparator1.Name = "toolStripSeparator1"; + this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25); + // + // toolStripButton3 + // + this.toolStripButton3.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton3.Image"))); + this.toolStripButton3.ImageTransparentColor = System.Drawing.Color.Magenta; + this.toolStripButton3.Name = "toolStripButton3"; + this.toolStripButton3.Size = new System.Drawing.Size(84, 22); + this.toolStripButton3.Text = "Open Map"; + this.toolStripButton3.Click += new System.EventHandler(this.toolStripButton3_Click); + // + // toolStripButton4 + // + this.toolStripButton4.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton4.Image"))); + this.toolStripButton4.ImageTransparentColor = System.Drawing.Color.Magenta; + this.toolStripButton4.Name = "toolStripButton4"; + this.toolStripButton4.Size = new System.Drawing.Size(117, 22); + this.toolStripButton4.Text = "Change Rotation"; + this.toolStripButton4.Click += new System.EventHandler(this.toolStripButton4_Click); + // + // toolStripButton5 + // + this.toolStripButton5.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton5.Image"))); + this.toolStripButton5.ImageTransparentColor = System.Drawing.Color.Magenta; + this.toolStripButton5.Name = "toolStripButton5"; + this.toolStripButton5.Size = new System.Drawing.Size(87, 22); + this.toolStripButton5.Text = "SetPosition"; + this.toolStripButton5.Click += new System.EventHandler(this.toolStripButton5_Click); + // // statusStrip1 // this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -1339,204 +1542,27 @@ namespace AGVEmulator this.sbCAL.Size = new System.Drawing.Size(19, 17); this.sbCAL.Text = "●"; // - // toolStripSeparator1 + // button2 // - this.toolStripSeparator1.Name = "toolStripSeparator1"; - this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25); + this.button2.Location = new System.Drawing.Point(246, 295); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(86, 38); + this.button2.TabIndex = 14; + this.button2.Tag = "--"; + this.button2.Text = "Pick On"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); // - // toolStripButton3 + // button3 // - this.toolStripButton3.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton3.Image"))); - this.toolStripButton3.ImageTransparentColor = System.Drawing.Color.Magenta; - this.toolStripButton3.Name = "toolStripButton3"; - this.toolStripButton3.Size = new System.Drawing.Size(84, 22); - this.toolStripButton3.Text = "Open Map"; - this.toolStripButton3.Click += new System.EventHandler(this.toolStripButton3_Click); - // - // agvViewer1 - // - this.agvViewer1.Dock = System.Windows.Forms.DockStyle.Fill; - this.agvViewer1.FontMrk = new System.Drawing.Font("Microsoft Sans Serif", 7F); - this.agvViewer1.FontTag = new System.Drawing.Font("Microsoft Sans Serif", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.agvViewer1.lastmark = ""; - this.agvViewer1.lastmarkdir = ""; - this.agvViewer1.lasttag = ""; - this.agvViewer1.lasttagdir = ""; - ptdata57.active = false; - ptdata57.data = "NOT"; - ptdata57.pos = 30F; - ptdata58.active = false; - ptdata58.data = "QA"; - ptdata58.pos = 200F; - ptdata59.active = false; - ptdata59.data = "CHG"; - ptdata59.pos = 300F; - ptdata60.active = false; - ptdata60.data = "QC"; - ptdata60.pos = 400F; - ptdata61.active = false; - ptdata61.data = "#FVI-1"; - ptdata61.pos = 500F; - ptdata62.active = false; - ptdata62.data = "#FVI-2"; - ptdata62.pos = 600F; - ptdata63.active = false; - ptdata63.data = "#FVI-3"; - ptdata63.pos = 700F; - ptdata64.active = false; - ptdata64.data = "#FVI-4"; - ptdata64.pos = 800F; - ptdata65.active = false; - ptdata65.data = "#FVI-5"; - ptdata65.pos = 900F; - ptdata66.active = false; - ptdata66.data = "POT"; - ptdata66.pos = 970F; - this.agvViewer1.listMRK = new AGVEmulator.UC.AgvViewer.ptdata[] { - ptdata57, - ptdata58, - ptdata59, - ptdata60, - ptdata61, - ptdata62, - ptdata63, - ptdata64, - ptdata65, - ptdata66}; - ptdata67.active = false; - ptdata67.data = "9000"; - ptdata67.pos = 80F; - ptdata68.active = false; - ptdata68.data = "9001"; - ptdata68.pos = 120F; - ptdata69.active = false; - ptdata69.data = "9010"; - ptdata69.pos = 180F; - ptdata70.active = false; - ptdata70.data = "9011"; - ptdata70.pos = 220F; - ptdata71.active = false; - ptdata71.data = "9020"; - ptdata71.pos = 280F; - ptdata72.active = false; - ptdata72.data = "9021"; - ptdata72.pos = 320F; - ptdata73.active = false; - ptdata73.data = "9030"; - ptdata73.pos = 380F; - ptdata74.active = false; - ptdata74.data = "9031"; - ptdata74.pos = 420F; - ptdata75.active = false; - ptdata75.data = "9040"; - ptdata75.pos = 480F; - ptdata76.active = false; - ptdata76.data = "9041"; - ptdata76.pos = 520F; - ptdata77.active = false; - ptdata77.data = "9050"; - ptdata77.pos = 580F; - ptdata78.active = false; - ptdata78.data = "9051"; - ptdata78.pos = 620F; - ptdata79.active = false; - ptdata79.data = "9060"; - ptdata79.pos = 680F; - ptdata80.active = false; - ptdata80.data = "9061"; - ptdata80.pos = 720F; - ptdata81.active = false; - ptdata81.data = "9070"; - ptdata81.pos = 780F; - ptdata82.active = false; - ptdata82.data = "9071"; - ptdata82.pos = 820F; - ptdata83.active = false; - ptdata83.data = "9000"; - ptdata83.pos = 10F; - ptdata84.active = false; - ptdata84.data = "9001"; - ptdata84.pos = 50F; - this.agvViewer1.listTAG = new AGVEmulator.UC.AgvViewer.ptdata[] { - ptdata67, - ptdata68, - ptdata69, - ptdata70, - ptdata71, - ptdata72, - ptdata73, - ptdata74, - ptdata75, - ptdata76, - ptdata77, - ptdata78, - ptdata79, - ptdata80, - ptdata81, - ptdata82, - ptdata83, - ptdata84}; - this.agvViewer1.Location = new System.Drawing.Point(241, 0); - this.agvViewer1.Name = "agvViewer1"; - this.agvViewer1.Size = new System.Drawing.Size(899, 120); - this.agvViewer1.StopbyMark = false; - this.agvViewer1.TabIndex = 0; - this.agvViewer1.Text = "agvViewer1"; - this.agvViewer1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.agvViewer1_MouseDown); - // - // serAGV - // - this.serAGV.BaudRate = 9600; - this.serAGV.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.serAGV.dev = null; - this.serAGV.Dock = System.Windows.Forms.DockStyle.Left; - this.serAGV.Location = new System.Drawing.Point(0, 0); - this.serAGV.Name = "serAGV"; - this.serAGV.PortName = "COM20"; - this.serAGV.Size = new System.Drawing.Size(241, 120); - this.serAGV.TabIndex = 0; - // - // serBMS - // - this.serBMS.BaudRate = 9600; - this.serBMS.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.serBMS.dev = null; - this.serBMS.Dock = System.Windows.Forms.DockStyle.Top; - this.serBMS.Location = new System.Drawing.Point(3, 17); - this.serBMS.Name = "serBMS"; - this.serBMS.PortName = "COM40"; - this.serBMS.Size = new System.Drawing.Size(1134, 84); - this.serBMS.TabIndex = 1; - // - // serCAL - // - this.serCAL.BaudRate = 9600; - this.serCAL.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.serCAL.dev = null; - this.serCAL.Dock = System.Windows.Forms.DockStyle.Top; - this.serCAL.Location = new System.Drawing.Point(3, 17); - this.serCAL.Name = "serCAL"; - this.serCAL.PortName = "COM50"; - this.serCAL.Size = new System.Drawing.Size(776, 84); - this.serCAL.TabIndex = 1; - // - // toolStripButton4 - // - this.toolStripButton4.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton4.Image"))); - this.toolStripButton4.ImageTransparentColor = System.Drawing.Color.Magenta; - this.toolStripButton4.Name = "toolStripButton4"; - this.toolStripButton4.Size = new System.Drawing.Size(117, 22); - this.toolStripButton4.Text = "Change Rotation"; - this.toolStripButton4.Click += new System.EventHandler(this.toolStripButton4_Click); - // - // toolStripButton5 - // - this.toolStripButton5.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton5.Image"))); - this.toolStripButton5.ImageTransparentColor = System.Drawing.Color.Magenta; - this.toolStripButton5.Name = "toolStripButton5"; - this.toolStripButton5.Size = new System.Drawing.Size(87, 22); - this.toolStripButton5.Text = "SetPosition"; - this.toolStripButton5.Click += new System.EventHandler(this.toolStripButton5_Click); + this.button3.Location = new System.Drawing.Point(246, 339); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(86, 38); + this.button3.TabIndex = 15; + this.button3.Tag = "--"; + this.button3.Text = "Pick Off"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.button3_Click); // // fMain // @@ -1706,6 +1732,8 @@ namespace AGVEmulator private ToolStripButton toolStripButton3; private ToolStripButton toolStripButton4; private ToolStripButton toolStripButton5; + private Button button3; + private Button button2; } } diff --git a/AGVEmulator/fMain.cs b/AGVEmulator/fMain.cs index b5cf7e8..352e443 100644 --- a/AGVEmulator/fMain.cs +++ b/AGVEmulator/fMain.cs @@ -351,8 +351,8 @@ namespace AGVEmulator private void AgvViewer1_TagTouched(object sender, UC.AgvViewer.TagArgs e) { logAGV.Add($"tag touch:{e.Data}"); - numericUpDown1.Text = e.Data;// decimal.Parse(e.Data); - button18.PerformClick(); + //numericUpDown1.Text = e.Data;// decimal.Parse(e.Data); + //button18.PerformClick(); UpdateVisualAgvPosition(e.Data); } @@ -559,7 +559,7 @@ namespace AGVEmulator -private void UpdateVisualAGV() + private void UpdateVisualAGV() { if (_visualAgv != null) { @@ -619,18 +619,18 @@ private void UpdateVisualAGV() // Send Tag if (node.Id != numericUpDown1.Text) { - if (int.TryParse(node.Id, out int tag)) - { - AGV.SendTag(node.Id); - numericUpDown1.Text = node.Id; + if (int.TryParse(node.Id, out int tag)) + { + //AGV.SendTag(node.Id); + numericUpDown1.Text = node.Id; - // Snap to node - _currentPosF = node.Position; - _visualAgv.CurrentPosition = node.Position; + // Snap to node + _currentPosF = node.Position; + _visualAgv.CurrentPosition = node.Position; - // Decide Next Move (Turn/Straight) - DecideNextMove(node); - } + // Decide Next Move (Turn/Straight) + DecideNextMove(node); + } } } } @@ -793,7 +793,7 @@ private void UpdateVisualAGV() private void toolStripButton3_Click(object sender, EventArgs e) { var file = @"C:\Data\Amkor\AGV4\route\NewMap.agvmap"; - if(System.IO.File.Exists(file)==false) + if (System.IO.File.Exists(file) == false) { var od = new OpenFileDialog(); od.Filter = "json|*.json"; @@ -916,6 +916,19 @@ private void UpdateVisualAGV() } } } + + private void button2_Click(object sender, EventArgs e) + { + var target = (byte)nudIDAgv.Value; + this.XBE.SendPickOn(target); + } + + private void button3_Click(object sender, EventArgs e) + { + var target = (byte)nudIDAgv.Value; + this.XBE.SendPickOff(target); + } + private void trbT2_Scroll(object sender, EventArgs e) { Temp2 = (UInt16)trbT2.Value; diff --git a/AGVEmulator/icons8-robot-80.ico b/AGVEmulator/icons8-robot-80.ico new file mode 100644 index 0000000000000000000000000000000000000000..a84bc77a40e6674c3623df3fa9588b8fe9a9fbd7 GIT binary patch literal 113050 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 BR*C=s literal 0 HcmV?d00001 diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs index da3bcf1..fe97a93 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.Events.cs @@ -78,6 +78,8 @@ namespace AGVNavigationCore.Controls // 노드 라벨 그리기 (가장 나중 - 선이 텍스트를 가리지 않게) DrawNodeLabels(g); DrawLabels(g); // 추가: 텍스트 라벨 + + } finally { @@ -93,6 +95,12 @@ namespace AGVNavigationCore.Controls { DrawSyncScreen(g); } + + //예측문자는 디버깅시에만 표시한다. + if (string.IsNullOrEmpty(PredictMessage) == false && System.Diagnostics.Debugger.IsAttached) + { + g.DrawString(this.PredictMessage, this.Font, Brushes.White, 10, 10); + } } private void DrawSyncScreen(Graphics g) diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs index 798e760..305c3ea 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs @@ -478,6 +478,17 @@ namespace AGVNavigationCore.Controls } } + /// + /// 상세경로가 설정되어있는가? + /// + /// + public bool HasPath() + { + if (_currentPath == null) return false; + if (_currentPath.DetailedPath == null) return false; + return _currentPath.DetailedPath.Any(); + } + /// /// 모든 경로 목록 (다중 AGV 경로 표시용) /// diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/Models/MapNode.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/Models/MapNode.cs index 1d84d99..35583af 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/Models/MapNode.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/Models/MapNode.cs @@ -19,7 +19,7 @@ namespace AGVNavigationCore.Models [Description("표시할 텍스트입니다.")] public string Text { get; set; } = ""; - public StationType StationType { get; set; } + public StationType StationType { get; set; } [Browsable(false)] public bool CanDocking @@ -103,7 +103,7 @@ namespace AGVNavigationCore.Models { Type = NodeType.Normal; } - + public MapNode(string nodeId, Point position, StationType type) : base(nodeId, position) { Type = NodeType.Normal; @@ -152,6 +152,18 @@ namespace AGVNavigationCore.Models return $"RFID:{RfidId}(NODE:{Id}): AS:{AliasName} ({Type}) at ({Position.X}, {Position.Y})"; } + /// + /// RFID(*ID) + /// + public string ID2 + { + get + { + if (HasRfid()) return $"{this.RfidId:0000}(*{this.Id})"; + else return $"(*{this.Id})"; + } + } + public bool IsNavigationNode() { // 이제 MapNode는 항상 내비게이션 노드임 (Label, Image 분리됨) diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/Models/NodeBase.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/Models/NodeBase.cs index 86fac5b..f1928ad 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/Models/NodeBase.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/Models/NodeBase.cs @@ -40,13 +40,12 @@ namespace AGVNavigationCore.Models [JsonIgnore] public bool IsSelected { get; set; } = false; - [Browsable(false)] [JsonIgnore] public bool IsHovered { get; set; } = false; - + public NodeBase() { diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/Models/VirtualAGV.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/Models/VirtualAGV.cs index 0cb21db..49f8208 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/Models/VirtualAGV.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/Models/VirtualAGV.cs @@ -83,9 +83,9 @@ namespace AGVNavigationCore.Models // 에뮬레이터용 추가 속성 public double Angle { get; set; } = 0; // 0 = Right, 90 = Down, 180 = Left, 270 = Up (Standard Math) - // But AGV Direction: Forward usually means "Front of AGV". - // Let's assume Angle is the orientation of the AGV in degrees. - + // But AGV Direction: Forward usually means "Front of AGV". + // Let's assume Angle is the orientation of the AGV in degrees. + public bool IsStopMarkOn { get; set; } = false; #endregion @@ -143,6 +143,11 @@ namespace AGVNavigationCore.Models /// public AGVPathResult CurrentPath => _currentPath; + public void ClearPath() + { + _currentPath = null; + } + /// /// 현재 노드 ID /// @@ -153,6 +158,18 @@ namespace AGVNavigationCore.Models /// public string CurrentNodeId => _currentNode?.Id; + /// + /// 현재노드의 RFID(id)값을 표시합니다 없는경우 (X)가 표시됩니다 + /// + public string CurrentNodeID2 + { + get + { + if (_currentNode == null) return "(X)"; + return _currentNode.ID2; + } + } + /// /// 이전 위치 /// @@ -282,7 +299,7 @@ namespace AGVNavigationCore.Models if (_currentNode == null) return false; if (_currentPath == null) return false; var 미완료된처음노드 = _currentPath.DetailedPath.Where(t => t.IsPass == false).OrderBy(t => t.seq).FirstOrDefault(); - if (미완료된처음노드 == null) return false; + if (미완료된처음노드 == null) return false; 미완료된처음노드.IsPass = true; Console.WriteLine($"미완료된처음노드를 true러치합니다"); return true; @@ -295,6 +312,7 @@ namespace AGVNavigationCore.Models /// 다음에 수행할 모터/마그넷/속도 명령 public AGVCommand Predict() { + // 1. 위치 미확정 상태 (RFID 2개 미만 감지) if (!_isPositionConfirmed) { @@ -311,12 +329,17 @@ namespace AGVNavigationCore.Models // 2. 위치 확정됨 + 경로 없음 → 정지 (목적지 미설정 상태) if (_currentPath == null || (_currentPath.DetailedPath?.Count ?? 0) < 1) { + var curpos = "알수없음"; + if (_currentNode != null) + { + curpos = _currentNode.HasRfid() ? $"RFID #{_currentNode.RfidId} (*{_currentNode.Id})" : $"(*{_currentNode.Id})"; + } return new AGVCommand( MotorCommand.Stop, MagnetPosition.S, SpeedLevel.L, eAGVCommandReason.NoPath, - $"위치 확정 완료 (목적지 미설정) - 현재:{_currentNode?.Id ?? "알수없음"}" + $"(목적지 미설정) - 현재={curpos}" ); } @@ -325,6 +348,9 @@ namespace AGVNavigationCore.Models if (_currentPath.DetailedPath.Where(t => t.seq < lastNode.seq && t.IsPass == false).Any() == false) { // 마지막 노드에 도착했는지 확인 (현재 노드가 마지막 노드와 같은지) + + + if (_currentNode != null && _currentNode.Id == lastNode.NodeId) { if (lastNode.IsPass) //이미완료되었다. @@ -334,7 +360,7 @@ namespace AGVNavigationCore.Models MagnetPosition.S, SpeedLevel.L, eAGVCommandReason.Complete, - $"목적지 도착 - 최종:{_currentNode?.Id ?? "알수없음"}" + $"목적지 도착 - 최종:{CurrentNodeID2}" ); } else @@ -345,7 +371,7 @@ namespace AGVNavigationCore.Models MagnetPosition.S, SpeedLevel.L, eAGVCommandReason.MarkStop, - $"목적지 도착 전(MarkStop) - 최종:{_currentNode?.Id ?? "알수없음"}" + $"목적지 도착 전(MarkStop) - 최종:{CurrentNodeID2}" ); } @@ -361,7 +387,7 @@ namespace AGVNavigationCore.Models MagnetPosition.S, SpeedLevel.L, eAGVCommandReason.PathOut, - $"(재탐색요청)경로이탈 현재위치:{_currentNode.Id}" + $"(재탐색요청)경로이탈 현재위치:{CurrentNodeID2}" ); } @@ -403,6 +429,18 @@ namespace AGVNavigationCore.Models #region Public Methods - 경로 실행 + /// + /// 경로가 설정되어있는지? + /// + /// + public bool HasPath() + { + if (_currentPath == null) return false; + if (_currentPath.DetailedPath == null) return false; + return _currentPath.DetailedPath.Any(); + + } + /// /// 경로 설정 (실제 AGV 및 시뮬레이터에서 사용) /// @@ -411,6 +449,9 @@ namespace AGVNavigationCore.Models { if (path == null) { + _currentPath = null; + _remainingNodes.Clear();// = null; + _currentNodeIndex = 0; OnError("경로가 null입니다."); return; } @@ -612,7 +653,7 @@ namespace AGVNavigationCore.Models PositionChanged?.Invoke(this, (_currentPosition, _currentDirection, _currentNode)); } - + #endregion diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Core/AGVPathResult.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Core/AGVPathResult.cs index 4578313..6f146e5 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Core/AGVPathResult.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Core/AGVPathResult.cs @@ -322,7 +322,7 @@ namespace AGVNavigationCore.PathFinding.Core } return Path?.Select(n => n.Id).ToList() ?? new List(); } - + /// /// 문자열 표현 /// diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Planning/NodeMotorInfo.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Planning/NodeMotorInfo.cs index 5c5cb71..cda4182 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Planning/NodeMotorInfo.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Planning/NodeMotorInfo.cs @@ -123,6 +123,8 @@ namespace AGVNavigationCore.PathFinding.Planning if (RequiresSpecialAction) result += $" [특수동작:{SpecialActionDescription}]"; + if (IsPass) result += "(O)"; + return result; } } diff --git a/Cs_HMI/Project/Class/Lang.cs b/Cs_HMI/Project/Class/Lang.cs index addf803..f619b08 100644 --- a/Cs_HMI/Project/Class/Lang.cs +++ b/Cs_HMI/Project/Class/Lang.cs @@ -55,10 +55,10 @@ namespace Project public static string 상차 = "상차"; public static string 하차 = "하차"; - public static string 홈검색완료 = "홈 검색 완료"; + public static string 이동완료 = "이동 완료"; public static string 오버로드감지 = "오버로드 감지"; - public static string 상차작업을시작합니다 { get { return string.Format("{0} 작업을 시작 합니다",상차); } } - public static string 하차작업을시작합니다 { get { return string.Format("{0} 작업을 시작 합니다", 하차); } } + public static string 진출을시작합니다 { get { return "진출 시작"; } } + public static string 진입을작합니다 { get { return "진입 시작"; } } public static string 안전커버를내려주세요 = "안전 커버를 내려 주세요"; public static string 안전커버를올려주세요 = "안전 커버를 올려 주세요"; public static string 안전커버를올리면상차가완료됩니다 = "안전 커버를 올리면 상차가 완료 됩니다"; diff --git a/Cs_HMI/Project/Device/Xbee.cs b/Cs_HMI/Project/Device/Xbee.cs index bcd5993..06760a7 100644 --- a/Cs_HMI/Project/Device/Xbee.cs +++ b/Cs_HMI/Project/Device/Xbee.cs @@ -14,6 +14,36 @@ using System.Windows.Forms; namespace Project.Device { + public enum eDocStep : byte + { + NotSet = 0, + + /// + /// 투입준비됨 + /// + InReady = 10, + + /// + /// 투입완료 + /// + InComplete = 11, + + /// + /// 투입(진행중) + /// + InIng = 12, + + /// + /// 진출완료 + /// + OutComplete = 21, + + /// + /// 진출중 + /// + OutIng = 22, + } + public class Xbee : SerialPort, arDev.ISerialComm { public string buffer = string.Empty; @@ -166,17 +196,10 @@ namespace Project.Device Send(packet); } - public bool BufferInReady { get; set; } - public bool BufferInComplete { get; set; } - public bool BufferOutComplete { get; set; } - public bool BufferReadyError { get; set; } - - public bool LoaderInComplete { get; set; } - public bool LoaderOutComplete { get; set; } - public bool UnloaderInComplete { get; set; } - public bool UnloaderOutComplete { get; set; } - public bool CleanerInComplete { get; set; } - public bool CleanerOutComplete { get; set; } + public eDocStep StepBuffer { get; set; } = eDocStep.NotSet; + public eDocStep StepUnloader { get; set; } = eDocStep.NotSet; + public eDocStep StepLoader { get; set; } = eDocStep.NotSet; + public eDocStep StepCleaner { get; set; } = eDocStep.NotSet; ManualResetEvent sendlock = new ManualResetEvent(true); diff --git a/Cs_HMI/Project/StateMachine/Step/_SM_RUN.cs b/Cs_HMI/Project/StateMachine/Step/_SM_RUN.cs index e7c0fe9..5100d7e 100644 --- a/Cs_HMI/Project/StateMachine/Step/_SM_RUN.cs +++ b/Cs_HMI/Project/StateMachine/Step/_SM_RUN.cs @@ -52,6 +52,7 @@ namespace Project VAR.TIME.Update(eVarTime.ReadyStart); else VAR.TIME.Update(eVarTime.RunStart); + VAR.I32[eVarInt32.PathValidationError] = 0; } //자동모드에서 대기상태 (추가동작없음) @@ -90,7 +91,12 @@ namespace Project } //현재위치를 모르는 상태라면 이동하여 현재 위치를 찾는다 - if (_SM_RUN_POSCHK(isFirst, stepTime) == false) return; + if (_SM_RUN_POSCHK(isFirst, stepTime) == false) + { + PUB.Result.result_message = "현재 위치 확인 중"; + PUB.Result.result_progressmax = 0; + return; + } //나머지 상황체크 switch (PUB.sm.RunStep) @@ -109,33 +115,46 @@ namespace Project var target = PUB._virtualAGV.TargetNode; PUB.log.Add($"목적지({target.RfidId}) 도착완료 타입:{target.Type}, 출발지:{PUB._virtualAGV.StartNode.RfidId}"); + PUB.XBE.StepLoader = Device.eDocStep.NotSet; + PUB.XBE.StepCleaner = Device.eDocStep.NotSet; + PUB.XBE.StepUnloader = Device.eDocStep.NotSet; + PUB.XBE.StepBuffer = Device.eDocStep.NotSet; + switch (target.StationType) { case AGVNavigationCore.Models.StationType.Buffer: - //현재위치가 마지막경로의 NODEID와 일치해야한다 - if (PUB._virtualAGV.CurrentPath == null) - { - PUB.log.AddAT("목적지 버퍼이동완료 했지만 상세경로가 없습니다"); - PUB.XBE.BufferInComplete = false; - PUB.XBE.BufferOutComplete = false; - break; - } var lastPath = PUB._virtualAGV.CurrentPath.DetailedPath.LastOrDefault(); if (lastPath.NodeId.Equals(PUB._virtualAGV.CurrentNode.Id)) { //버퍼진입전 노드에 도착완료했따 - PUB.XBE.BufferInReady = true; - PUB.XBE.BufferReadyError = false; + PUB.XBE.StepBuffer = Device.eDocStep.InReady; } else { //마지막위치가 아닌 다른 위치에 있으니 버퍼 작업을 할 수없다 - PUB.log.AddAT("목적지 버퍼이동완료 했지만 마지막 노드가 아닙니다"); - PUB.XBE.BufferInReady = false; - PUB.XBE.BufferReadyError = true; + PUB.XBE.StepBuffer = Device.eDocStep.NotSet; + PUB.log.AddE($"목적지가 버퍼이나 노드가 불일치 한다 오류사항"); + PUB.sm.SetNewRunStep(ERunStep.ERROR); } - PUB.XBE.BufferInComplete = false; - PUB.XBE.BufferOutComplete = false; + break; + case AGVNavigationCore.Models.StationType.Charger: + + break; + + case AGVNavigationCore.Models.StationType.Loader: + PUB.XBE.StepLoader = Device.eDocStep.InReady; + break; + + case AGVNavigationCore.Models.StationType.Clearner: + PUB.XBE.StepCleaner = Device.eDocStep.InReady; + break; + + case AGVNavigationCore.Models.StationType.UnLoader: + PUB.XBE.StepUnloader = Device.eDocStep.InReady; + break; + + default: + PUB.log.Add($"정의되지 않은 스테이션 입니다({target.StationType}) "); break; } @@ -144,7 +163,7 @@ namespace Project } break; - case ERunStep.MARKSTOPB: //후진방향으로 마크스탑 + case ERunStep.MARKSTOPB: //후진방향으로 마크스탑 case ERunStep.MARKSTOPF: //전진방향으로 마크스탑 //이동중이지 않다면 먼저 이동을 진행한다 @@ -169,10 +188,6 @@ namespace Project } else if (_SM_RUN_GOCHARGE(runStepisFirst, PUB.sm.GetRunSteptime)) { - //230601 - //if (PUB.Result != null && PUB.sm != null) - // EEMStatus.AddEEDBSQL(PUB.sm.Step, PUB.sm.RunStep.ToString(), PUB.Result.TargetPos.ToString()); - PUB.Speak(Lang.충전을시작합니다); PUB.sm.SetNewRunStep(ERunStep.CHARGECHECK); return; @@ -207,16 +222,7 @@ namespace Project //충전상태가 활성화되었으므로 대기상태로 전환한다 PUB.sm.ClearRunStep(); - //충전기위치에서 OFF를 한 경우이다 - if (PUB.Result.CurrentPos != ePosition.CHARGE || PUB.Result.TargetPos != ePosition.CHARGE) - { - PUB.Result.CurrentPos = ePosition.NONE; - } - else - { - PUB.Result.TargetPos = ePosition.QC; - } - PUB.Result.CurrentPosCW = "1"; + //대기상태로 전환 PUB.sm.SetNewRunStep(ERunStep.READY); PUB.log.AddAT("충전 해제로 대기상태로 전환 합니다"); } @@ -228,8 +234,7 @@ namespace Project PUB.Speak(Lang.버퍼도킹해제완료); //도킹완료상태를 업데이트한다. - PUB.XBE.LoaderInComplete = false; - PUB.XBE.LoaderOutComplete = true; + PUB.XBE.StepLoader = Device.eDocStep.OutComplete; //대기상태로 전환 PUB.sm.SetNewRunStep(ERunStep.READY); @@ -243,23 +248,10 @@ namespace Project PUB.Speak(Lang.버퍼도킹이완료되었습니다); //도킹완료상태를 업데이트한다. - PUB.XBE.LoaderInComplete = true; + PUB.XBE.StepLoader = Device.eDocStep.InComplete; - //로더아웃으로 자동 진행하지 않음 (ACS 명령 대기) - if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff) - { - PUB.sm.SetNewRunStep(ERunStep.READY); - PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop; // Command consumed - } - else - { - // Legacy behavior or Goto command: Auto-exit? - // User said separation is key. Let's Stop here too or keep legacy for GOTO? - // Assuming GOTO might rely on this, but safer to STOP if we want strict separation. - // However, let's keep legacy behavior for GOTO if possible, but for PickOn/Off we STOP. - PUB.sm.ClearRunStep(); - PUB.sm.SetNewRunStep(ERunStep.LOADER_OUT); - } + //대기상태로 전환 + PUB.sm.SetNewRunStep(ERunStep.READY); return; } break; @@ -270,8 +262,7 @@ namespace Project PUB.Speak(Lang.버퍼도킹해제완료); //도킹완료상태를 업데이트한다. - PUB.XBE.UnloaderInComplete = false; - PUB.XBE.UnloaderOutComplete = true; + PUB.XBE.StepUnloader = Device.eDocStep.OutComplete; //대기상태로 전환 PUB.sm.SetNewRunStep(ERunStep.READY); @@ -285,19 +276,10 @@ namespace Project PUB.Speak(Lang.버퍼도킹이완료되었습니다); //도킹완료상태를 업데이트한다. - PUB.XBE.UnloaderInComplete = true; + PUB.XBE.StepUnloader = Device.eDocStep.InComplete; - //언로더아웃으로 자동 진행하지 않음 - if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff) - { - PUB.sm.SetNewRunStep(ERunStep.READY); - PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop; - } - else - { - PUB.sm.ClearRunStep(); - PUB.sm.SetNewRunStep(ERunStep.UNLOADER_OUT); - } + //대기상태로 전환 + PUB.sm.SetNewRunStep(ERunStep.READY); return; } break; @@ -308,8 +290,7 @@ namespace Project PUB.Speak(Lang.버퍼도킹해제완료); //도킹완료상태를 업데이트한다. - PUB.XBE.CleanerInComplete = false; - PUB.XBE.CleanerOutComplete = true; + PUB.XBE.StepCleaner = Device.eDocStep.OutComplete; //대기상태로 전환 PUB.sm.SetNewRunStep(ERunStep.READY); @@ -323,19 +304,10 @@ namespace Project PUB.Speak(Lang.버퍼도킹이완료되었습니다); //도킹완료상태를 업데이트한다. - PUB.XBE.CleanerInComplete = true; + PUB.XBE.StepCleaner = Device.eDocStep.InComplete; //클리너아웃으로 자동 진행하지 않음 - if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff) - { - PUB.sm.SetNewRunStep(ERunStep.READY); - PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop; - } - else - { - PUB.sm.ClearRunStep(); - PUB.sm.SetNewRunStep(ERunStep.CLEANER_OUT); - } + PUB.sm.SetNewRunStep(ERunStep.READY); return; } break; @@ -346,13 +318,13 @@ namespace Project PUB.Speak(Lang.버퍼도킹해제완료); //도킹완료상태를 업데이트한다. - PUB.XBE.BufferInComplete = false; - PUB.XBE.BufferOutComplete = true; + PUB.XBE.StepBuffer = Device.eDocStep.OutComplete; //대기상태로 전환 PUB.sm.SetNewRunStep(ERunStep.READY); return; } + else PUB.XBE.StepBuffer = Device.eDocStep.OutIng; break; case ERunStep.BUFFER_IN: //버퍼도킹 @@ -361,28 +333,16 @@ namespace Project PUB.Speak(Lang.버퍼도킹이완료되었습니다); //도킹완료상태를 업데이트한다. - PUB.XBE.BufferInComplete = true; + PUB.XBE.StepBuffer = Device.eDocStep.InComplete; - //버퍼아웃으로 자동 진행하지 않음 - if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff) - { - PUB.sm.SetNewRunStep(ERunStep.READY); - PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop; - } - else - { - PUB.sm.ClearRunStep(); - PUB.sm.SetNewRunStep(ERunStep.BUFFER_OUT); - } + //대기상태로 전환 + PUB.sm.SetNewRunStep(ERunStep.READY); return; } + else PUB.XBE.StepBuffer = Device.eDocStep.InIng; break; - - } - - } }//cvass diff --git a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_IN.cs b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_IN.cs index 3df0ffa..e1a947f 100644 --- a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_IN.cs +++ b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_IN.cs @@ -11,26 +11,17 @@ namespace Project { public partial class fMain { - /// - /// 버퍼도킹 - /// - /// - /// - /// - public Boolean _SM_RUN_BUFFER_IN(bool isFirst, TimeSpan stepTime) + public Boolean _SM_RUN_BUFFER_IN(bool isFirst, TimeSpan seqtime) { var funcname = "_SM_RUN_BUFFER_IN"; var idx = 1; //충전 상태가 OFF되어야 동작하게한다 - if (_SM_RUN_CHGOFF(isFirst, stepTime) == false) return false; + if (_SM_RUN_CHGOFF(isFirst, seqtime) == false) return false; //라이더멈춤이 설정되어있다면 음성으로 알려준다 if (CheckLiderStop() == false) return false; - //현재 위치가 결정되어있는지 체크한다 - if (_SM_RUN_POSCHK(isFirst, stepTime) == false) return false; - /* * 버퍼IN시퀀스 * 1. 회전이 진행되지 않았다면 회전을 진행한다. @@ -41,8 +32,8 @@ namespace Project if (PUB.sm.RunStepSeq == idx++) { - PUB.log.Add("버퍼도킹시작"); - PUB.Speak(Lang.하차작업을시작합니다); + PUB.log.Add($"[{funcname}] 버퍼진입시작({PUB.NextWorkCmd}) Turn:{PUB._virtualAGV.Turn}"); + PUB.Speak(Lang.진입을작합니다); PUB.sm.UpdateRunStepSeq(); return false; } @@ -70,9 +61,9 @@ namespace Project { if (PUB._virtualAGV.Turn != AGVNavigationCore.Models.AGVTurn.L90) { - VAR.TIME.Update(eVarTime.LastTurnCommandTime); PUB.AGV.AGVMoveLeft180Turn(); PUB.log.Add("AGV Left Turn"); + VAR.TIME.Update(eVarTime.LastTurnCommandTime); PUB.sm.UpdateRunStepSeq(); } else PUB.sm.UpdateRunStepSeq(); //이미완료된상태이므로 다음으로 진행한다. @@ -80,59 +71,35 @@ namespace Project } else if (PUB.sm.RunStepSeq == idx++) { - if (PUB._virtualAGV.Turn != AGVNavigationCore.Models.AGVTurn.L90) + //왼쪽턴이 완료되지 않은경우 + if (PUB.AGV.TurnInformation.State != arDev.eNarumiTurn.Left) { - //5초이내에 턴이동 상태가 확인되어야 한다. - if (PUB.AGV.system1.agv_run == false) + //움직임 확인을 위해 3초간은 검증을 유예한다 + if (PUB.AGV.TurnInformation.Runtime.TotalSeconds < 3) return false; + + //턴 이동 상태가 확인되어야 한다. + var overtime = 10; + if (PUB.AGV.TurnInformation.Runtime.TotalSeconds > overtime) { - var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime); - if (ts.TotalSeconds > 5) - { - //5초동안 AGV까 움직이지 않았다면 오류 처리한다. - PUB.AGV.AGVMoveStop("5초이내 턴 감지 안됨"); - PUB.log.AddE("5초이내 턴 감지 안됨"); - PUB.sm.SetNewRunStep(ERunStep.ERROR); - return false; - } + //5초동안 AGV까 움직이지 않았다면 오류 처리한다. + PUB.AGV.AGVMoveStop($"[bufferin] {overtime}초이내 턴 감지 안됨"); + PUB.log.AddE($"[{funcname}] {overtime}초이내 턴 감지 안됨"); + PUB.sm.SetNewRunStep(ERunStep.ERROR); return false; } - } - PUB.sm.UpdateRunStepSeq(); //이미완료된상태이므로 다음으로 진행한다. - return false; - } - else if (PUB.sm.RunStepSeq == idx++) - { - //턴이완료되었느닞 확인한다. - if (PUB._virtualAGV.Turn != AGVNavigationCore.Models.AGVTurn.L90) - { - //10초 이상 가동하고 있다면 문제이다 - if (PUB.AGV.system1.agv_run == true) - { - var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime); - if (ts.TotalSeconds > 15) - { - PUB.AGV.AGVMoveStop("15초이내 턴 이 완료되지 않음"); - PUB.log.AddE("5초이내 턴 완료 확인 안됨"); - PUB.sm.SetNewRunStep(ERunStep.ERROR); - return false; - } - return false; - } - else PUB._virtualAGV.Turn = AGVNavigationCore.Models.AGVTurn.L90; - } - PUB.sm.UpdateRunStepSeq(); //이미완료된상태이므로 다음으로 진행한다. - return false; - } - else if (PUB.sm.RunStepSeq == idx++) - { - // [PickOn/PickOff] 초기 리프트 동작 - var liftCmd = arDev.Narumi.LiftCommand.DN; - if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff) - { - liftCmd = arDev.Narumi.LiftCommand.UP; + return false; } + PUB.log.Add($"[{funcname}] Turn(left) 완료"); + PUB.sm.UpdateRunStepSeq(); //이미완료된상태이므로 다음으로 진행한다. + return false; + } + else if (PUB.sm.RunStepSeq == idx++) + { + //버퍼에들어갈때에는 가져다 놓을때도 가지러 갈때에도 리프트는 내려서 들어간다 + var liftCmd = arDev.Narumi.LiftCommand.DN; PUB.AGV.LiftControl(liftCmd); + PUB.log.Add($"[{funcname}] 리프트를 내립니다"); VAR.TIME.Update(eVarTime.LastTurnCommandTime); PUB.sm.UpdateRunStepSeq(); return false; @@ -140,12 +107,32 @@ namespace Project else if (PUB.sm.RunStepSeq == idx++) { //리프트 센서 확인 - var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime); - if (ts.TotalSeconds > 10) - { - // Timebound check - } - PUB.log.Add("리프트 동작 확인 완료"); + if (PUB.AGV.signal1.lift_down == false) + { + if (seqtime.TotalSeconds > 20) + { + PUB.log.AddAT($"[{funcname}] 리프트가 내려가지 않아 1회 재시도 합니다"); + PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN); + //재시도를 했으니 다음으로 진행하게한다 + } + else return false; + } + PUB.sm.UpdateRunStepSeq(); + return false; + } + else if (PUB.sm.RunStepSeq == idx++) + { + //리프트 센서 확인 + if (PUB.AGV.signal1.lift_down == false) + { + if (seqtime.TotalSeconds > 20) + { + PUB.log.AddE($"[{funcname}] 리프트가 내려가지 않습니다"); + PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP); + PUB.sm.SetNewRunStep(ERunStep.ERROR); + } + return false; + } PUB.sm.UpdateRunStepSeq(); return false; } @@ -160,26 +147,32 @@ namespace Project else if (PUB.sm.RunStepSeq == idx++) { //저속이동 (후진 진입) - var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData + var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData { Bunki = arDev.Narumi.eBunki.Strate, Direction = arDev.Narumi.eMoveDir.Backward, PBSSensor = 0, Speed = arDev.Narumi.eMoveSpd.Low, }); - if (moveset == false) + + //명령이 실패되었다면 재시도를 한다 + if (ret != arDev.eNarumiCommandResult.Success) { - var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime); - if (ts.TotalSeconds > 10) + if (ret >= arDev.eNarumiCommandResult.Error) { PUB.AGV.AGVMoveStop(funcname); - PUB.log.AddE("AGV속도설정이 완료되지 않았습니다"); + PUB.log.AddE($"[{funcname}] AGV속도설정이 완료되지 않았습니다"); PUB.sm.SetNewRunStep(ERunStep.ERROR); - return false; } + return false; } + + //후진이동을한다 PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward); + + //마크스탑으로 이동 PUB.AGV.AGVMoveStop(funcname, arDev.Narumi.eStopOpt.MarkStop); + VAR.TIME.Update(eVarTime.LastTurnCommandTime); PUB.sm.UpdateRunStepSeq(); return false; @@ -193,7 +186,7 @@ namespace Project if (ts.TotalSeconds > 3) { PUB.AGV.AGVMoveStop(funcname); - PUB.log.AddE("MARK STOP신호가 확인되지 않습니다"); + PUB.log.AddE($"[{funcname}] MARK STOP신호가 확인되지 않습니다"); PUB.sm.SetNewRunStep(ERunStep.ERROR); return false; } @@ -211,7 +204,7 @@ namespace Project if (ts.TotalSeconds > 10) { PUB.AGV.AGVMoveStop(funcname); - PUB.log.AddE("AGV가 멈추지 않아 강제종료 합니다"); + PUB.log.AddE($"[{funcname}] AGV가 멈추지 않아 강제종료 합니다"); PUB.sm.SetNewRunStep(ERunStep.ERROR); return false; } @@ -223,45 +216,22 @@ namespace Project } else if (PUB.sm.RunStepSeq == idx++) { - // [Action] 진입 완료 후 리프트 동작 (Pick/Drop) - PUB.log.Add("버퍼 진입 완료. 작업 수행(Lift Pick/Drop)"); - - var liftCmd = arDev.Narumi.LiftCommand.UP; - if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff) - { - liftCmd = arDev.Narumi.LiftCommand.DN; - } - - PUB.AGV.LiftControl(liftCmd); - VAR.TIME.Update(eVarTime.LastTurnCommandTime); + if (seqtime.TotalSeconds < 2) return false; + PUB.log.Add($"[{funcname}] 작업({PUB.NextWorkCmd}) 완료. 대기 상태로 전환 (퇴출 명령 대기)"); PUB.sm.UpdateRunStepSeq(); return false; } else if (PUB.sm.RunStepSeq == idx++) { - // 리프트 동작 대기 - // TODO: 실제 센서 확인 로직 추가 필요 - var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime); - if (ts.TotalSeconds < 2) return false; - - PUB.log.Add("작업(Pick/Drop) 완료. 대기 상태로 전환 (퇴출 명령 대기)"); - PUB.sm.UpdateRunStepSeq(); - return false; - } - else if (PUB.sm.RunStepSeq == idx++) - { - //완료되었다. (ACS에 보내야함) - PUB.log.Add("버퍼 진입 및 작업 완료"); + //완료되었다. + PUB.log.Add($"[{funcname}] 버퍼 진입 및 작업 완료"); PUB.sm.UpdateRunStepSeq(); return false; } // 작업을 마치고 설비 안에 멈춰있는 상태. // ACS가 이 상태를 확인하고 NextWorkCmd로 퇴출(Out) 명령을 보내야 함. - PUB.AddEEDB($"버퍼작업완료({PUB.Result.TargetPos})"); - return true; - - PUB.AddEEDB($"버퍼투입완료({PUB.Result.TargetPos})"); + PUB.AddEEDB($"[{funcname}] 버퍼작업완료({PUB.Result.TargetPos})"); return true; } } diff --git a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_OUT.cs b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_OUT.cs index 6de2b83..6e7bf8f 100644 --- a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_OUT.cs +++ b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_OUT.cs @@ -11,30 +11,97 @@ namespace Project { public partial class fMain { - - public Boolean _SM_RUN_BUFFER_OUT(bool isFirst, TimeSpan stepTime) + public Boolean _SM_RUN_BUFFER_OUT(bool isFirst, TimeSpan seqtime) { + var funcname = "_SM_RUN_BUFFER_IN"; + var idx = 1; //충전 상태가 OFF되어야 동작하게한다 - if (_SM_RUN_CHGOFF(isFirst, stepTime) == false) return false; + if (_SM_RUN_CHGOFF(isFirst, seqtime) == false) return false; //라이더멈춤이 설정되어있다면 음성으로 알려준다 if (CheckLiderStop() == false) return false; - //현재 위치가 결정되어있는지 체크한다 - if (_SM_RUN_POSCHK(isFirst, stepTime) == false) return false; + /* + * 버퍼 OUT 시퀀스 + * 1-1. PickOn 라면 리프트를 Up 한다 + * 1-2. PickOff 라면 리프트를 Down 한다 + * 2.전진-저속-마크다운 + * 3.Turn-Right-180 + */ - var idx = 1; if (PUB.sm.RunStepSeq == idx++) + { + PUB.log.Add($"[{funcname}] 버퍼진출시작({PUB.NextWorkCmd}) Turn:{PUB._virtualAGV.Turn}"); + PUB.Speak(Lang.진출을시작합니다); + PUB.sm.UpdateRunStepSeq(); + return false; + } + else if (PUB.sm.RunStepSeq == idx++) + { + arDev.Narumi.LiftCommand lift = PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn ? arDev.Narumi.LiftCommand.UP : arDev.Narumi.LiftCommand.DN; + PUB.log.Add($"[{funcname}] 리프트제어 {lift}"); + PUB.AGV.LiftControl(lift); + PUB.sm.UpdateRunStepSeq(); + return false; + } + else if (PUB.sm.RunStepSeq == idx++) + { + //리프트 센서 확인 + var checksensor = PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn ? PUB.AGV.signal1.lift_up : PUB.AGV.signal1.lift_down; + if (checksensor == false) + { + if (seqtime.TotalSeconds > 20) + { + PUB.log.AddAT($"[{funcname}] 리프트가 완료되지 않아 1회 재시도 합니다"); + arDev.Narumi.LiftCommand lift = PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn ? arDev.Narumi.LiftCommand.UP : arDev.Narumi.LiftCommand.DN; + PUB.AGV.LiftControl(lift); + } + else return false; + } + PUB.sm.UpdateRunStepSeq(); + return false; + } + else if (PUB.sm.RunStepSeq == idx++) + { + //리프트 센서 확인 + var checksensor = PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn ? PUB.AGV.signal1.lift_up : PUB.AGV.signal1.lift_down; + if (checksensor == false) + { + if (seqtime.TotalSeconds > 20) + { + PUB.log.AddE($"[{funcname}] 리프트가 동작하지 않습니다"); + PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP); + PUB.sm.SetNewRunStep(ERunStep.ERROR); + } + return false; + } + PUB.sm.UpdateRunStepSeq(); + return false; + } + else if (PUB.sm.RunStepSeq == idx++) { //빈 상태로 아웃해야한다. - PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData + var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData { Bunki = arDev.Narumi.eBunki.Strate, Direction = arDev.Narumi.eMoveDir.Forward, PBSSensor = 0, Speed = arDev.Narumi.eMoveSpd.Low, }); + //명령이 실패되었다면 재시도를 한다 + if (ret != arDev.eNarumiCommandResult.Success) + { + if (ret >= arDev.eNarumiCommandResult.Error) + { + PUB.AGV.AGVMoveStop(funcname); + PUB.log.AddE($"[{funcname}] AGV속도설정이 완료되지 않았습니다"); + PUB.sm.SetNewRunStep(ERunStep.ERROR); + } + return false; + } + + PUB.sm.UpdateRunStepSeq(); return false; } diff --git a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_CLEANER_IN.cs b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_CLEANER_IN.cs index 2981a69..38e4e01 100644 --- a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_CLEANER_IN.cs +++ b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_CLEANER_IN.cs @@ -25,9 +25,6 @@ namespace Project //라이더멈춤이 설정되어있다면 음성으로 알려준다 if (CheckLiderStop() == false) return false; - //현재 위치가 결정되어있는지 체크한다 - if (_SM_RUN_POSCHK(isFirst, stepTime) == false) return false; - /* * 클리너 IN 시퀀스 (버퍼 복사 - 턴 제거) * 1. LIFT DOWN @@ -78,24 +75,25 @@ namespace Project else if (PUB.sm.RunStepSeq == idx++) { //저속이동 (후진 진입) - var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData + var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData { Bunki = arDev.Narumi.eBunki.Strate, Direction = arDev.Narumi.eMoveDir.Backward, PBSSensor = 0, Speed = arDev.Narumi.eMoveSpd.Low, }); - if (moveset == false) + //명령이 실패되었다면 재시도를 한다 + if (ret != arDev.eNarumiCommandResult.Success) { - var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime); - if (ts.TotalSeconds > 10) + if (ret >= arDev.eNarumiCommandResult.Error) { PUB.AGV.AGVMoveStop(funcname); - PUB.log.AddE("AGV속도설정이 완료되지 않았습니다"); + PUB.log.AddE($"[{funcname}] AGV속도설정이 완료되지 않았습니다"); PUB.sm.SetNewRunStep(ERunStep.ERROR); - return false; } + return false; } + PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward); PUB.AGV.AGVMoveStop(funcname, arDev.Narumi.eStopOpt.MarkStop); VAR.TIME.Update(eVarTime.LastTurnCommandTime); diff --git a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_CLEANER_OUT.cs b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_CLEANER_OUT.cs index 2a743ff..a92c947 100644 --- a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_CLEANER_OUT.cs +++ b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_CLEANER_OUT.cs @@ -16,26 +16,37 @@ namespace Project /// public Boolean _SM_RUN_CLEANER_OUT(bool isFirst, TimeSpan stepTime) { + var funcname = "CLEANEROUT"; + //충전 상태가 OFF되어야 동작하게한다 if (_SM_RUN_CHGOFF(isFirst, stepTime) == false) return false; //라이더멈춤이 설정되어있다면 음성으로 알려준다 if (CheckLiderStop() == false) return false; - //현재 위치가 결정되어있는지 체크한다 - if (_SM_RUN_POSCHK(isFirst, stepTime) == false) return false; - var idx = 1; if (PUB.sm.RunStepSeq == idx++) { //빈 상태로 아웃해야한다. - PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData + var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData { Bunki = arDev.Narumi.eBunki.Strate, Direction = arDev.Narumi.eMoveDir.Forward, PBSSensor = 0, Speed = arDev.Narumi.eMoveSpd.Low, }); + //명령이 실패되었다면 재시도를 한다 + if (ret != arDev.eNarumiCommandResult.Success) + { + if (ret >= arDev.eNarumiCommandResult.Error) + { + PUB.AGV.AGVMoveStop(funcname); + PUB.log.AddE($"[{funcname}] AGV속도설정이 완료되지 않았습니다"); + PUB.sm.SetNewRunStep(ERunStep.ERROR); + } + return false; + } + PUB.sm.UpdateRunStepSeq(); return false; } diff --git a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_GOCHARGE.cs b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_GOCHARGE.cs index cd1e4a9..4fdee1d 100644 --- a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_GOCHARGE.cs +++ b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_GOCHARGE.cs @@ -14,11 +14,11 @@ namespace Project DateTime tm_gocharge_command = DateTime.Now; public Boolean _SM_RUN_GOCHARGE(bool isFirst, TimeSpan stepTime) { + var funcname = "GOCHARGE"; if (runStepisFirst) { //홈을 찾도록 항상 위치를 지워버리자 PUB.Result.CurrentPos = ePosition.NONE; - PUB.Result.CurrentPos = ePosition.NONE; } //HW 연결오류 @@ -57,13 +57,7 @@ namespace Project return false; } - //현재 위치가 결정되어있는지 체크한다 - if (_SM_RUN_POSCHK(isFirst, stepTime) == false) - { - PUB.Result.result_message = "충전기 검색 전 현재 위치 검색"; - PUB.Result.result_progressmax = 0; - return false; - } + //충전작업진행상태 //220629 //if (PUB.flag.get(EFlag.FLAG_GO_CHAGER_TEMP) == false) @@ -119,17 +113,26 @@ namespace Project { PUB.log.Add($"충전:충전기 검색을 위한 전진시작"); PUB.Speak(Lang.충전기를검색합니다); - PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData + var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData { Speed = arDev.Narumi.eMoveSpd.Low, Bunki = arDev.Narumi.eBunki.Strate, Direction = arDev.Narumi.eMoveDir.Forward, PBSSensor = 1, }); - PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward); - //PUB.Result.TargetPos = ePosition.CHARGE; - VAR.TIME.Update(eVarTime.ChargeSearch); + if(ret != arDev.eNarumiCommandResult.Success) + { + if(ret >= arDev.eNarumiCommandResult.Error) + { + PUB.log.AddE($"[{funcname}] AGV속도설정 실패"); + PUB.AGV.AGVMoveStop("err"); + PUB.sm.SetNewRunStep(ERunStep.ERROR); + } + return false; + } + PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward); + VAR.TIME.Update(eVarTime.ChargeSearch); PUB.sm.UpdateRunStepSeq(); return false; } diff --git a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_GOHOME.cs b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_GOHOME.cs index 5a86d6c..0120e07 100644 --- a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_GOHOME.cs +++ b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_GOHOME.cs @@ -72,8 +72,8 @@ namespace Project else if (PUB.sm.RunStepSeq == idx++) { //QC까지 모두 완료되었다.(완전히 정차할때까지 기다린다) - PUB.Speak(Lang.홈검색완료, true); - PUB.AddEEDB($"홈검색완료({PUB.Result.TargetPos})"); + PUB.Speak(Lang.이동완료, true); + PUB.AddEEDB($"이동완료({PUB.Result.TargetPos})"); PUB.sm.UpdateRunStepSeq(); return false; } diff --git a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_GOTO.cs b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_GOTO.cs index 6f39bed..21fc0a7 100644 --- a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_GOTO.cs +++ b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_GOTO.cs @@ -67,8 +67,8 @@ namespace Project else if (PUB.sm.RunStepSeq == idx++) { //QC까지 모두 완료되었다.(완전히 정차할때까지 기다린다) - PUB.Speak(Lang.홈검색완료, true); - PUB.AddEEDB($"홈검색완료({PUB.Result.TargetPos})"); + PUB.Speak(Lang.이동완료, true); + PUB.AddEEDB($"이동완료({PUB._virtualAGV.TargetNode.ID2})"); PUB.sm.UpdateRunStepSeq(); return false; } diff --git a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_LOADER_IN.cs b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_LOADER_IN.cs index 6200f41..cc5fd0d 100644 --- a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_LOADER_IN.cs +++ b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_LOADER_IN.cs @@ -25,9 +25,7 @@ namespace Project //라이더멈춤이 설정되어있다면 음성으로 알려준다 if (CheckLiderStop() == false) return false; - //현재 위치가 결정되어있는지 체크한다 - if (_SM_RUN_POSCHK(isFirst, stepTime) == false) return false; - + /* * 로더 IN 시퀀스 (버퍼 복사 - 턴 제거) * 1. LIFT DOWN @@ -84,23 +82,22 @@ namespace Project else if (PUB.sm.RunStepSeq == idx++) { //저속이동 (후진 진입) - var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData + var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData { Bunki = arDev.Narumi.eBunki.Strate, Direction = arDev.Narumi.eMoveDir.Backward, PBSSensor = 0, Speed = arDev.Narumi.eMoveSpd.Low, }); - if (moveset == false) + if (ret != arDev.eNarumiCommandResult.Success) { - var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime); - if (ts.TotalSeconds > 10) + if (ret >= arDev.eNarumiCommandResult.Error) { PUB.AGV.AGVMoveStop(funcname); PUB.log.AddE("AGV속도설정이 완료되지 않았습니다"); PUB.sm.SetNewRunStep(ERunStep.ERROR); - return false; } + return false; } PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward); PUB.AGV.AGVMoveStop(funcname, arDev.Narumi.eStopOpt.MarkStop); diff --git a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_LOADER_OUT.cs b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_LOADER_OUT.cs index 2487ec0..1b39129 100644 --- a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_LOADER_OUT.cs +++ b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_LOADER_OUT.cs @@ -16,27 +16,37 @@ namespace Project /// public Boolean _SM_RUN_LOADER_OUT(bool isFirst, TimeSpan stepTime) { - + var funcname = "LOADEROUT"; //충전 상태가 OFF되어야 동작하게한다 if (_SM_RUN_CHGOFF(isFirst, stepTime) == false) return false; //라이더멈춤이 설정되어있다면 음성으로 알려준다 if (CheckLiderStop() == false) return false; - //현재 위치가 결정되어있는지 체크한다 - if (_SM_RUN_POSCHK(isFirst, stepTime) == false) return false; - var idx = 1; if (PUB.sm.RunStepSeq == idx++) { //빈 상태로 아웃해야한다. - PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData + var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData { Bunki = arDev.Narumi.eBunki.Strate, Direction = arDev.Narumi.eMoveDir.Forward, PBSSensor = 0, Speed = arDev.Narumi.eMoveSpd.Low, }); + //명령이 실패되었다면 재시도를 한다 + if (ret != arDev.eNarumiCommandResult.Success) + { + if (ret >= arDev.eNarumiCommandResult.Error) + { + PUB.AGV.AGVMoveStop(funcname); + PUB.log.AddE($"[{funcname}] AGV속도설정이 완료되지 않았습니다"); + PUB.sm.SetNewRunStep(ERunStep.ERROR); + } + return false; + } + + PUB.sm.UpdateRunStepSeq(); return false; } diff --git a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_POSCHK.cs b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_POSCHK.cs index e7ae26e..e8468e8 100644 --- a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_POSCHK.cs +++ b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_POSCHK.cs @@ -16,9 +16,6 @@ namespace Project if (PUB._virtualAGV.CurrentNode != null && PUB._virtualAGV.PrevNode != null) return true; - //최소2개의 노드정보가 있어야 진행가능하므로 prevNode 값이 있는지 확인한다. - - //이동을 하지 않고있다면 전진을 진행한다 if (PUB.AGV.system1.agv_run == false) { @@ -26,14 +23,17 @@ namespace Project if (ts.TotalSeconds > 5) { PUB.log.Add($"현재위치를 몰라 전진 이동 합니다"); - PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData + var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData { Bunki = arDev.Narumi.eBunki.Strate, Direction = arDev.Narumi.eMoveDir.Forward, PBSSensor = 1, Speed = arDev.Narumi.eMoveSpd.Low, }); - PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward); + if(ret == arDev.eNarumiCommandResult.Success) + { + PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward); + } VAR.TIME.Update(eVarTime.LastRunCommandTime); } } diff --git a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_UNLOADER_IN.cs b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_UNLOADER_IN.cs index 352f4f5..5ccd751 100644 --- a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_UNLOADER_IN.cs +++ b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_UNLOADER_IN.cs @@ -25,9 +25,6 @@ namespace Project //라이더멈춤이 설정되어있다면 음성으로 알려준다 if (CheckLiderStop() == false) return false; - //현재 위치가 결정되어있는지 체크한다 - if (_SM_RUN_POSCHK(isFirst, stepTime) == false) return false; - /* * 언로더 IN 시퀀스 (버퍼 복사 - 턴 제거) * 1. LIFT DOWN @@ -78,24 +75,27 @@ namespace Project else if (PUB.sm.RunStepSeq == idx++) { //저속이동 (후진 진입) - var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData + var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData { Bunki = arDev.Narumi.eBunki.Strate, Direction = arDev.Narumi.eMoveDir.Backward, PBSSensor = 0, Speed = arDev.Narumi.eMoveSpd.Low, }); - if (moveset == false) + + //명령이 실패되었다면 재시도를 한다 + if (ret != arDev.eNarumiCommandResult.Success) { - var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime); - if (ts.TotalSeconds > 10) + if (ret >= arDev.eNarumiCommandResult.Error) { PUB.AGV.AGVMoveStop(funcname); - PUB.log.AddE("AGV속도설정이 완료되지 않았습니다"); + PUB.log.AddE($"[{funcname}] AGV속도설정이 완료되지 않았습니다"); PUB.sm.SetNewRunStep(ERunStep.ERROR); - return false; } + return false; } + + PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward); PUB.AGV.AGVMoveStop(funcname, arDev.Narumi.eStopOpt.MarkStop); VAR.TIME.Update(eVarTime.LastTurnCommandTime); diff --git a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_UNLOADER_OUT.cs b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_UNLOADER_OUT.cs index eedea06..e57ab00 100644 --- a/Cs_HMI/Project/StateMachine/Step/_SM_RUN_UNLOADER_OUT.cs +++ b/Cs_HMI/Project/StateMachine/Step/_SM_RUN_UNLOADER_OUT.cs @@ -16,27 +16,37 @@ namespace Project /// public Boolean _SM_RUN_UNLOADER_OUT(bool isFirst, TimeSpan stepTime) { - + var funcname = "UNLOADEROUT"; //충전 상태가 OFF되어야 동작하게한다 if (_SM_RUN_CHGOFF(isFirst, stepTime) == false) return false; //라이더멈춤이 설정되어있다면 음성으로 알려준다 if (CheckLiderStop() == false) return false; - //현재 위치가 결정되어있는지 체크한다 - if (_SM_RUN_POSCHK(isFirst, stepTime) == false) return false; - var idx = 1; if (PUB.sm.RunStepSeq == idx++) { //빈 상태로 아웃해야한다. - PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData + var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData { Bunki = arDev.Narumi.eBunki.Strate, Direction = arDev.Narumi.eMoveDir.Forward, PBSSensor = 0, Speed = arDev.Narumi.eMoveSpd.Low, }); + //명령이 실패되었다면 재시도를 한다 + if (ret != arDev.eNarumiCommandResult.Success) + { + if (ret >= arDev.eNarumiCommandResult.Error) + { + PUB.AGV.AGVMoveStop(funcname); + PUB.log.AddE($"[{funcname}] AGV속도설정이 완료되지 않았습니다"); + PUB.sm.SetNewRunStep(ERunStep.ERROR); + } + return false; + } + + PUB.sm.UpdateRunStepSeq(); return false; } diff --git a/Cs_HMI/Project/StateMachine/Step/_Util.cs b/Cs_HMI/Project/StateMachine/Step/_Util.cs index 64e7d53..9ff11f7 100644 --- a/Cs_HMI/Project/StateMachine/Step/_Util.cs +++ b/Cs_HMI/Project/StateMachine/Step/_Util.cs @@ -34,7 +34,7 @@ namespace Project return false; } - + return true; } @@ -44,7 +44,7 @@ namespace Project /// public bool CheckLiderStop() { - + if (PUB.AGV.system1.stop_by_front_detect == true) { var tsSpeak = DateTime.Now - LastSpeakTime; @@ -67,8 +67,9 @@ namespace Project /// Boolean UpdateMotionPositionForMark(string sender) { - //현재위치를 모르는 상태라면 이동하여 현재 위치를 찾는다 - if (_SM_RUN_POSCHK(false, new TimeSpan()) == false) return false; + ////현재위치를 모르는 상태라면 처리하지 않는다 + if (PUB._virtualAGV.CurrentNode == null || PUB._virtualAGV.PrevNode == null) + return false; //현재위치노드 오류 var currentNode = PUB.FindByNodeID(PUB._virtualAGV.CurrentNode.Id); @@ -119,6 +120,7 @@ namespace Project } else { + //계산은 되었으나 현재위치가 전체 경로 없다면 시작노드를 현재로 변경해야한다. PUB._mapCanvas.CurrentPath = PathResult.result; PUB._virtualAGV.SetPath(PathResult.result); } @@ -133,19 +135,50 @@ namespace Project { PUB.AGV.AGVMoveStop("Path Integrity Fail"); } - PUB.log.AddE($"경로 무결성 오류"); - PUB.sm.SetNewRunStep(ERunStep.READY); + PUB.log.AddE($"경로 무결성 오류로 인해 경로를 삭제 합니다"); + Console.WriteLine($"경로 무결성 오류로 인해 경로를 삭제 합니다"); + PUB._virtualAGV.SetPath(null); + VAR.I32[eVarInt32.PathValidationError] += 1; + if (VAR.I32[eVarInt32.PathValidationError] > 50) + { + PUB.log.AddE($"연속 경로 무결성 오류로 인해 중지 합니다"); + PUB.sm.SetNewRunStep(ERunStep.ERROR); + } return false; } + else VAR.I32[eVarInt32.PathValidationError] = 0; + + //현재위치 기준으로 재 계산하여. 더 최적화된 루트가 있다면 처리를 해준다. + if (PUB._virtualAGV.CurrentPath.DetailedPath.Count > 5) + { + var PathResult2 = CalcPath(PUB._virtualAGV.CurrentNode, PUB._virtualAGV.TargetNode); + if (PathResult2.result != null && PathResult2.result.Success) + { + //절반이상 경로가 짧을때에는 재계산을 하게한다 + var halfcnt = (int)(PUB._virtualAGV.CurrentPath.DetailedPath.Count / 2.0); + if (PathResult2.result.DetailedPath.Count < halfcnt) + { + var msg = $"단축경로가 확인되었습니다. 경로를 삭제 합니다"; + PUB.log.AddE(msg); + Console.WriteLine(msg); + PUB._virtualAGV.SetPath(null); + } + return false; + } + } + - //predict 를 이용하여 다음 이동을 모두 확인한다. var nextAction = PUB._virtualAGV.Predict(); - if(nextAction.Reason == AGVNavigationCore.Models.eAGVCommandReason.PathOut) + if (nextAction.Reason == AGVNavigationCore.Models.eAGVCommandReason.PathOut) { //경로이탈 - PUB._virtualAGV.CurrentPath.DetailedPath.Clear(); + var logmessage = $"경로이탈감지 시작노드를 현재위치로 설정합니다 START:{PUB._virtualAGV.StartNode},CURRENT:{PUB._virtualAGV.CurrentNode}"; + PUB.log.AddE(logmessage); + Console.WriteLine(logmessage); + PUB._virtualAGV.ClearPath();//.DetailedPath.Clear(); + PUB._virtualAGV.StartNode = PUB._virtualAGV.CurrentNode; return false; } @@ -158,7 +191,7 @@ namespace Project $"현재 상태: {PUB._virtualAGV.CurrentState}\n" + $"현재 방향: {PUB._virtualAGV.CurrentDirection}\n" + $"위치 확정: {PUB._virtualAGV.IsPositionConfirmed} (RFID {PUB._virtualAGV.DetectedRfidCount}개)\n" + - $"현재 노드: {PUB._virtualAGV.CurrentNode.Id ?? "없음"}"; + $"현재 노드: {PUB._virtualAGV.CurrentNodeID2}"; //모터에서 정지를 요청했다 if (nextAction.Motor == AGVNavigationCore.Models.MotorCommand.Stop) @@ -181,13 +214,33 @@ namespace Project // 목적지 도착 여부 확인 // 현재 노드가 타겟 노드와 같고, 위치가 확정된 상태라면 도착으로 간주 // 단, AGV가 실제로 멈췄는지 확인 (agv_run == false) - if (PUB._virtualAGV.IsPositionConfirmed && - PUB._virtualAGV.CurrentNode.Id == PUB._virtualAGV.TargetNode.Id) + if (PUB._virtualAGV.IsPositionConfirmed) { if (PUB.AGV.system1.agv_run == false) { - PUB.log.AddI($"목표 도착 및 정지 확인됨(MarkStop 완료). Node:{PUB._virtualAGV.CurrentNode.Id}"); - return true; + //목적지도착완료시 + if (PUB._virtualAGV.CurrentNode.Id == PUB._virtualAGV.TargetNode.Id) + { + var node = PUB._mapCanvas.Nodes.Where(t => t.Id == PUB._virtualAGV.CurrentNodeId).FirstOrDefault(); + var rfid = node?.ID2 ?? "(X)"; + PUB.log.AddI($"목표 도착 및 정지 확인됨(MarkStop 완료) Node:{rfid}"); + return true; + } + + //목적지가 버퍼라면 그 앞에 멈춘다 + if (PUB._virtualAGV.TargetNode.StationType == AGVNavigationCore.Models.StationType.Buffer && + PUB._virtualAGV.CurrentPath != null && PUB._virtualAGV.CurrentPath.DetailedPath.Any()) + { + if (PUB._virtualAGV.CurrentNode.Id == PUB._virtualAGV.CurrentPath.DetailedPath.Last().NodeId) + { + PUB.log.AddI($"목표(버퍼) 도착 및 정지 확인됨(MarkStop 완료). Node:{PUB._virtualAGV.CurrentNodeID2}"); + return true; + } + } + } + else + { + //아직 멈추지 않았다면 기다린다. } } @@ -213,14 +266,17 @@ namespace Project PUB.AGV.data.Direction != dir.ToString()[0] || PUB.AGV.data.Speed != spd.ToString()[0]) { - PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData + var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData { Bunki = bunki, Direction = dir, PBSSensor = 1, Speed = spd, }); - PUB.log.Add($"Predict Run Setting = bunki:{bunki},dir:{dir},pbs:1,spd:{spd}"); + if (ret == arDev.eNarumiCommandResult.Success) + PUB.log.Add($"Predict Run Setting = bunki:{bunki},dir:{dir},pbs:1,spd:{spd}"); + else + PUB.log.AddE($"Predict Run Setting = bunki:{bunki},dir:{dir},pbs:1,spd:{spd}"); } // AGV가 정지 상태라면 구동 시작 @@ -250,14 +306,18 @@ namespace Project PUB.AGV.error.Emergency == false && PUB.AGV.system1.agv_run == false) { - PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData + var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData { Bunki = arDev.Narumi.eBunki.Strate, Direction = arDev.Narumi.eMoveDir.Forward, PBSSensor = 1, Speed = arDev.Narumi.eMoveSpd.Low, }); - PUB.AGV.AGVMoveRun( arDev.Narumi.eRunOpt.Forward);// + if (ret == arDev.eNarumiCommandResult.Success) + { + PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward);// + } + tm_gocharge_command = DateTime.Now; } } @@ -278,14 +338,15 @@ namespace Project PUB.AGV.error.Emergency == false && PUB.AGV.system1.agv_run == false) { - PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData + var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData { Bunki = arDev.Narumi.eBunki.Strate, Direction = arDev.Narumi.eMoveDir.Backward, PBSSensor = 1, Speed = arDev.Narumi.eMoveSpd.Low, }); - PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);// + if (ret == arDev.eNarumiCommandResult.Success) + PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);// LastCommandTime = DateTime.Now; } } @@ -311,7 +372,7 @@ namespace Project // 만약 수행되지 않았다면 여기서 수행. if (pathResult.DockingValidation == null) { - pathResult.DockingValidation = AGVNavigationCore.Utils.DockingValidator.ValidateDockingDirection(pathResult, PUB._mapCanvas.Nodes); + pathResult.DockingValidation = AGVNavigationCore.Utils.DockingValidator.ValidateDockingDirection(pathResult, PUB._mapCanvas.Nodes); } // 검증 결과 확인 diff --git a/Cs_HMI/Project/StateMachine/_AGV.cs b/Cs_HMI/Project/StateMachine/_AGV.cs index f8912fa..8c4a593 100644 --- a/Cs_HMI/Project/StateMachine/_AGV.cs +++ b/Cs_HMI/Project/StateMachine/_AGV.cs @@ -66,7 +66,7 @@ namespace Project if (PUB._virtualAGV.CurrentDirection != syncDir) PUB.UpdateAGVDirection(syncDir); } - + // [Sync] Update VirtualAGV State AGVState syncState = AGVState.Idle; @@ -106,7 +106,7 @@ namespace Project PUB.log.Add($"충전상태전환 {agv_chg}"); VAR.BOOL[eVarBool.FLAG_CHARGEONA] = agv_chg; } - + if (PUB.AGV.error.Charger_pos_error != VAR.BOOL[eVarBool.CHG_POSERR]) { @@ -199,9 +199,11 @@ namespace Project else { //모터방향 확인해서 UI와 AGV클래스에 적용한다 - var MotDireciton = PUB.AGV.data.Direction == 'B' ? AGVNavigationCore.Models.AgvDirection.Backward : AGVNavigationCore.Models.AgvDirection.Forward; + var MotDireciton = PUB.AGV.data.Direction == 'B' ? AgvDirection.Backward : AgvDirection.Forward; PUB._mapCanvas.SetAGVPosition(PUB.setting.MCID, CurrentNode, MotDireciton); PUB._virtualAGV.SetPosition(CurrentNode, MotDireciton); + + //방향을 다시 확인하여. 기존 경로의 무결성을 검증한다, 필요한 경우 다시 계산할 필요가 있다. } //태그를 읽었다면 상태를 바로 전송한다 @@ -221,16 +223,42 @@ namespace Project if (PUB._mapCanvas != null && PUB._virtualAGV != null) { var nextAction = PUB._virtualAGV.Predict(); - var message = $"[다음 행동 예측]\n\n" + - $"모터: {nextAction.Motor}\n" + + var message = $"[다음 행동 예측]\n\n"; + + if (VAR.BOOL[eVarBool.FLAG_AUTORUN] == false) + message += "[수동모드]\n\n"; + + var node = PUB._virtualAGV.CurrentNode; + var curpos = PUB._virtualAGV.CurrentNodeID2; + var targetpos = PUB._virtualAGV.TargetNode?.ID2 ?? "(X)"; + + var pathdetail = ""; + if (PUB._virtualAGV.CurrentPath != null && PUB._virtualAGV.CurrentPath.DetailedPath.Any()) + { + var idx = 0; + foreach (var item in PUB._virtualAGV.CurrentPath.DetailedPath) + { + idx += 1; + if (pathdetail.isEmpty() == false) pathdetail += "->"; + if (idx % 6 == 0) pathdetail += "\n"; + pathdetail += $"{item.RfidId:0000}({item.MotorDirection.ToString().Substring(0, 1)}{item.MagnetDirection.ToString().Substring(0, 1)}{item.Speed.ToString().Substring(0, 1)})"; + if (item.IsPass) pathdetail += "(O)"; + } + } + + message += $"모터: {nextAction.Motor}\n" + $"마그넷: {nextAction.Magnet}\n" + $"속도: {nextAction.Speed}\n" + - $"이유: {nextAction.Message}\n\n" + + $"이유: {nextAction.Message}\n" + + $"상태머신:{PUB.sm.Step}:{PUB.sm.RunStep}:{PUB.sm.RunStepSeq}\n"+ $"---\n" + $"현재 상태: {PUB._virtualAGV.CurrentState}\n" + $"현재 방향: {PUB._virtualAGV.CurrentDirection}\n" + + $"턴: {PUB._virtualAGV.Turn}\n" + $"위치 확정: {PUB._virtualAGV.IsPositionConfirmed} (RFID {PUB._virtualAGV.DetectedRfidCount}개)\n" + - $"현재 노드: {PUB._virtualAGV.CurrentNode.Id ?? "없음"}"; + $"현재 노드: {curpos}\n" + + $"대상 노드: {targetpos}\n" + + $"상세 경로: {pathdetail}"; PUB._mapCanvas.PredictMessage = message; } diff --git a/Cs_HMI/Project/StateMachine/_Loop.cs b/Cs_HMI/Project/StateMachine/_Loop.cs index ac9de15..2038fde 100644 --- a/Cs_HMI/Project/StateMachine/_Loop.cs +++ b/Cs_HMI/Project/StateMachine/_Loop.cs @@ -49,8 +49,15 @@ namespace Project else if (PUB.DriveSpeed) { var rlt = PUB.AGV.AGVCommand("SSH", PUB.setting.SPD_H.ToString("0000")); - if (rlt) PUB.DriveSpeed = false; - PUB.log.Add($"Screen Change and DriveSpeed Off:{rlt}"); + if (rlt == arDev.eNarumiCommandResult.Success) + { + PUB.DriveSpeed = false; + PUB.log.Add($"Screen Change and DriveSpeed Off:{rlt}"); + } else + { + PUB.log.AddE($"Screen Change and DriveSpeed Off:{rlt}"); + } + } MenuAuto.ForeColor = Color.FromArgb(180, 180, 180); diff --git a/Cs_HMI/Project/StateMachine/_Xbee.cs b/Cs_HMI/Project/StateMachine/_Xbee.cs index 9a0b813..c3bb679 100644 --- a/Cs_HMI/Project/StateMachine/_Xbee.cs +++ b/Cs_HMI/Project/StateMachine/_Xbee.cs @@ -79,25 +79,37 @@ namespace Project var currNode = PUB._virtualAGV.CurrentNode; if (currNode == null) { - PUB.log.AddE($"[{logPrefix}-{cmd}] 현재 노드를 알 수 없습니다 NodeID:{PUB._virtualAGV.CurrentNode.Id}"); - PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, "Unknown Node"); + PUB.log.AddE($"[{logPrefix}-{cmd}] 현재 노드를 알 수 없습니다"); + PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, "Unknown Current Node"); return; } - PUB.NextWorkCmd = cmd; - ERunStep nextStep = ERunStep.READY; + var targetNode = PUB._virtualAGV.TargetNode; + if (targetNode == null) + { + PUB.log.AddE($"[{logPrefix}-{cmd}] 목표 노드를 알 수 없습니다"); + PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, "Unknown Target Node"); + return; + } - switch (currNode.StationType) + //버퍼의 경우 직전에 멈추기 때문에 스테이션종류를 보정해준다 + var StationType = currNode.StationType; + if (StationType == StationType.Normal && targetNode.StationType == StationType.Buffer) + StationType = StationType.Buffer; + + ERunStep nextStep = ERunStep.READY; + switch (StationType) { case StationType.Loader: nextStep = ERunStep.LOADER_IN; break; case StationType.UnLoader: nextStep = ERunStep.UNLOADER_IN; break; case StationType.Buffer: nextStep = ERunStep.BUFFER_IN; break; case StationType.Clearner: nextStep = ERunStep.CLEANER_IN; break; default: - PUB.log.AddE($"[{logPrefix}-{cmd}] 해당 노드타입({currNode.Type})은 작업을 지원하지 않습니다."); + PUB.log.AddE($"[{logPrefix}-{cmd}] 해당 노드타입({StationType})은 작업을 지원하지 않습니다."); return; } + PUB.NextWorkCmd = cmd; PUB.log.AddI($"작업 시작: {nextStep} (Type: {cmd})"); PUB.sm.SetNewRunStep(nextStep); } @@ -118,10 +130,10 @@ namespace Project { var currTag = System.Text.Encoding.Default.GetString(data, 1, data.Length - 1).Trim(); MapNode targetNode = null; - if(cmd == ENIGProtocol.AGVCommandHE.GotoAlias) + if (cmd == ENIGProtocol.AGVCommandHE.GotoAlias) { targetNode = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.AliasName == currTag); - } + } else { if (ushort.TryParse(currTag, out ushort currtagvalue)) @@ -155,14 +167,14 @@ namespace Project { PUB.log.AddE($"[{logPrefix}-Goto] 시작노드가 없습니다(현재위치 없음) NodeID:{PUB._virtualAGV.CurrentNode.Id}"); } - + //대상이동으로 처리한다. - if(PUB.sm.RunStep != ERunStep.GOTO) - { + if (PUB.sm.RunStep != ERunStep.GOTO) + { PUB.sm.SetNewRunStep(StateMachine.ERunStep.GOTO); PUB.sm.ResetRunStepSeq(); } - + //Move to PUB.log.Add($"[{logPrefix}-{cmd}] {startNode.RfidId} -> {targetNode.RfidId}"); @@ -242,9 +254,15 @@ namespace Project if (Lidar == 0) bunkidata.PBSSensor = 0; else bunkidata.PBSSensor = 2; - PUB.log.Add($"[{logPrefix}-AutoMove] DIR:{bunkidata.Direction}-{bunkidata.Bunki},SPD:{bunkidata.Speed}"); - PUB.AGV.AGVMoveSet(bunkidata); - PUB.AGV.AGVMoveRun((MotDirection == 0 ? arDev.Narumi.eRunOpt.Backward : arDev.Narumi.eRunOpt.Forward)); + PUB.log.Add($"[{logPrefix}-AutoMove] DIR:{bunkidata.Direction}-{bunkidata.Bunki},SPD:{bunkidata.Speed}"); + + if (PUB.AGV.AGVMoveSet(bunkidata) != eNarumiCommandResult.Success) + PUB.log.AddE($"AGV속도설정실패로 인해 자동전환이 실패되었습니다"); + else + { + PUB.log.Add($"XBE 에서 자동모드로 전환합니다"); + PUB.AGV.AGVMoveRun((MotDirection == 0 ? arDev.Narumi.eRunOpt.Backward : arDev.Narumi.eRunOpt.Forward)); + } break; case ENIGProtocol.AGVCommandHE.MarkStop: //Set MarkStop @@ -274,7 +292,7 @@ namespace Project PUB.log.AddI($"충전을 시작합니다"); } break; - + default: PUB.logagv.AddE($"Unknown Command : {cmd} Sender:{e.ReceivedPacket.ID}, Target:{data[0]}"); PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.UnknownCommand, $"{cmd}"); @@ -323,7 +341,7 @@ namespace Project advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _mapNodes); //마지막대상이 버퍼라면 시퀀스처리를 해야한다 - if (targetNode.StationType == StationType.Buffer&& advancedResult.DetailedPath.Any()) + if (targetNode.StationType == StationType.Buffer && advancedResult.DetailedPath.Any()) { var lastDetailPath = advancedResult.DetailedPath.Last(); if (lastDetailPath.NodeId == targetNode.Id) //마지막노드 재확인 @@ -349,7 +367,7 @@ namespace Project //UpdateAdvancedPathDebugInfo(advancedResult); } - else if(advancedResult != null) + else if (advancedResult != null) { // 경로 실패시 디버깅 정보 초기화 //_pathDebugLabel.Text = $"경로: 실패 - {advancedResult.ErrorMessage}"; diff --git a/Cs_HMI/Project/ViewForm/fManual.cs b/Cs_HMI/Project/ViewForm/fManual.cs index 46c08f7..cb35c52 100644 --- a/Cs_HMI/Project/ViewForm/fManual.cs +++ b/Cs_HMI/Project/ViewForm/fManual.cs @@ -200,8 +200,8 @@ namespace Project.ViewForm var dlg = UTIL.MsgQ("자동 진행을 시작할까요?\n우측 옵션을 확인 하세요"); if (dlg != DialogResult.Yes) return; var opt = makeopt(); - PUB.AGV.AGVMoveSet(opt); - PUB.AGV.AGVMoveRun(opt.Direction == arDev.Narumi.eMoveDir.Forward ? arDev.Narumi.eRunOpt.Forward : arDev.Narumi.eRunOpt.Backward); + if (PUB.AGV.AGVMoveSet(opt) == eNarumiCommandResult.Success) + PUB.AGV.AGVMoveRun(opt.Direction == arDev.Narumi.eMoveDir.Forward ? arDev.Narumi.eRunOpt.Forward : arDev.Narumi.eRunOpt.Backward); } else { @@ -350,7 +350,7 @@ namespace Project.ViewForm PUB.log.Add($"사용자 설정 : {bunkiopt}"); if (PUB.DriveSpeed) { - if (PUB.AGV.AGVCommand("SSH", PUB.setting.SPD_H.ToString("0000")) == true) + if (PUB.AGV.AGVCommand("SSH", PUB.setting.SPD_H.ToString("0000")) == eNarumiCommandResult.Success) PUB.DriveSpeed = false; } @@ -361,7 +361,7 @@ namespace Project.ViewForm private void arLabel1_Click_1(object sender, EventArgs e) { - if (PUB.AGV.AGVCommand("SSH", PUB.setting.SPD_DRIVE.ToString("0000")) == false) + if (PUB.AGV.AGVCommand("SSH", PUB.setting.SPD_DRIVE.ToString("0000")) != eNarumiCommandResult.Success) { AR.UTIL.MsgE("고속속도에 주행속도 연결 실패\n잠시 후 다시 시도하세요"); return; @@ -369,12 +369,18 @@ namespace Project.ViewForm if (this.bunkiopt == null) bunkiopt = makeopt(); bunkiopt.Speed = arDev.Narumi.eMoveSpd.High; - PUB.AGV.AGVMoveSet(bunkiopt); - PUB.log.Add($"고속주행설정 : {bunkiopt}"); - PUB.DriveSpeed = true; + if (PUB.AGV.AGVMoveSet(bunkiopt) == eNarumiCommandResult.Success) + { + PUB.log.Add($"고속주행설정 : {bunkiopt}"); + PUB.DriveSpeed = true; - AR.UTIL.MsgI($"주행속도({PUB.setting.SPD_DRIVE})가 '고속' 속도에 연결되었습니다.\n" + - $"창을 이동하거나 '설정적용'을 누르면 원래속도({PUB.setting.SPD_H})로 변경 됩니다"); + AR.UTIL.MsgI($"주행속도({PUB.setting.SPD_DRIVE})가 '고속' 속도에 연결되었습니다.\n" + + $"창을 이동하거나 '설정적용'을 누르면 원래속도({PUB.setting.SPD_H})로 변경 됩니다"); + } + else + { + UTIL.MsgE($"고속 속도 설정이 실패되었습니다"); + } } private void btLeft180_Click(object sender, EventArgs e) @@ -398,7 +404,7 @@ namespace Project.ViewForm private void button2_Click(object sender, EventArgs e) { - PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.OFF); + PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.OFF); } } } diff --git a/Cs_HMI/Project/fMain.cs b/Cs_HMI/Project/fMain.cs index 0144139..77ee9ef 100644 --- a/Cs_HMI/Project/fMain.cs +++ b/Cs_HMI/Project/fMain.cs @@ -432,10 +432,7 @@ namespace Project //충전상태확인 if (PUB.CheckManualChargeMode() == false) return; - PUB.popup.needClose = true; - //PUB.sm.bPause = false; - //PUB.sm.ClearRunStep(); - //PUB.sm.SetNewRunStep(ERunStep.READY); + PUB.popup.needClose = true; PUB.sm.SetNewStep(eSMStep.RUN); PUB.Speak(Lang.자동전환, addlog: false); diff --git a/Cs_HMI/Project/fSetup.cs b/Cs_HMI/Project/fSetup.cs index 5f4471e..a3cf266 100644 --- a/Cs_HMI/Project/fSetup.cs +++ b/Cs_HMI/Project/fSetup.cs @@ -471,7 +471,7 @@ namespace Project if (tagStr == "XBE") tbPortXBE.Text = newPort; else if (tagStr == "AGV") tbPortAGV.Text = newPort; - // else if (tagStr == "PLC") tbPortPLC.Text = newPort; + // else if (tagStr == "PLC") tbPortPLC.Text = newPort; else if (tagStr == "BAT") tbportBMS.Text = newPort; } @@ -511,7 +511,7 @@ namespace Project } try { - if (PUB.mplayer.SoundLocation.isEmpty()==false) PUB.mplayer.Stop(); + if (PUB.mplayer.SoundLocation.isEmpty() == false) PUB.mplayer.Stop(); PUB.mplayer.SoundLocation = tbMusic.Text;// (new Uri(tbMusic.Text)); PUB.mplayer.Play(); PUB.bPlayMusic = true; @@ -535,14 +535,14 @@ namespace Project if (this.tbPortAGV.Text == "COM21") { this.tbPortAGV.Text = "COM11"; - // this.tbPortPLC.Text = "COM6"; + // this.tbPortPLC.Text = "COM6"; this.tbportBMS.Text = "COM15"; this.tbPortXBE.Text = "COM18"; } else { this.tbPortAGV.Text = "COM21"; - // this.tbPortPLC.Text = "COM31"; + // this.tbPortPLC.Text = "COM31"; this.tbportBMS.Text = "COM41"; this.tbPortXBE.Text = "COM51"; } @@ -615,35 +615,35 @@ namespace Project private void button7_Click(object sender, EventArgs e) { var value = tbagvaddr.Text.PadLeft(4, '0'); - if (PUB.AGV.AGVCommand("SAD", value)) + if (PUB.AGV.AGVCommand("SAD", value) == arDev.eNarumiCommandResult.Success) { PUB.setting.AGV_ADDRESS = value; PUB.setting.Save(); } - + } private void button5_Click(object sender, EventArgs e) { var value = tbagvpanid.Text.PadLeft(4, '0'); - if (PUB.AGV.AGVCommand("SPN", value)) + if (PUB.AGV.AGVCommand("SPN", value) == arDev.eNarumiCommandResult.Success) { PUB.setting.AGV_PANID = value; PUB.setting.Save(); } - + } private void button6_Click(object sender, EventArgs e) { var value = tbagvchannel.Text.PadLeft(4, '0'); - if (PUB.AGV.AGVCommand("SCH", value)) + if (PUB.AGV.AGVCommand("SCH", value) == arDev.eNarumiCommandResult.Success) { PUB.setting.AGV_CHANNEL = value; PUB.setting.Save(); } - + } private void tbagvaddr_Click(object sender, EventArgs e) @@ -659,7 +659,7 @@ namespace Project { var value = (ushort)vcGDS.Value; var rlt = PUB.AGV.TurnGDSCenterScope(value); - if (rlt == false) UTIL.MsgE("Error"); + if (rlt != arDev.eNarumiCommandResult.Success) UTIL.MsgE($"Error\n{rlt}"); else UTIL.MsgI("OK"); } } diff --git a/Cs_HMI/SubProject/AGV/Command.cs b/Cs_HMI/SubProject/AGV/Command.cs index 8f06845..2aeb027 100644 --- a/Cs_HMI/SubProject/AGV/Command.cs +++ b/Cs_HMI/SubProject/AGV/Command.cs @@ -12,20 +12,75 @@ using AR; namespace arDev { + public enum eNarumiTurn + { + None = 0, + LeftIng, + Left, + RightIng, + Right, + } + public class NarumiTurnInfo + { + public DateTime Start { get; set; } + public DateTime End { get; set; } + public TimeSpan Runtime + { + get + { + if (End.Year < 2000) return DateTime.Now - Start; + if (End < Start) return DateTime.Now - Start; + else return End - Start; + } + } + public eNarumiTurn State { get; set; } + public NarumiTurnInfo() + { + Start = new DateTime(1982, 11, 23); + End = new DateTime(1982, 11, 23); + State = eNarumiTurn.None; + } + } + public class NarumiCommandTime + { + public DateTime Time { get; set; } + public ushort count { get; set; } + public NarumiCommandTime(ushort cnt) + { + Time = DateTime.Now; + this.count = cnt; + } + } + + /// + /// error 이상은모두 처리불가한 오류 조건을 의미한다 + /// + public enum eNarumiCommandResult : byte + { + Fail = 0, + Success = 1, + Wait = 2, + Error = 100, + Timeout, + + } public partial class Narumi { + public NarumiTurnInfo TurnInformation { get; set; } = null; + Dictionary SendCommandFailList { get; set; } = new Dictionary(); - public bool AGVMoveSet(BunkiData opt) + public eNarumiCommandResult AGVMoveSet(BunkiData opt) { - return AddCommand(eAgvCmd.MoveSet, opt.ToString()); + var param = opt.ToString(); + return AddCommand(eAgvCmd.MoveSet, param); } - public bool AGVMoveManual(ManulOpt opt, Speed spd, Sensor ss) + public eNarumiCommandResult AGVMoveManual(ManulOpt opt, Speed spd, Sensor ss) { var param = opt.ToString() + spd.ToString()[0] + ((int)ss).ToString(); return AddCommand(eAgvCmd.ManualMove, param); } - public bool AGVMoveRun(eRunOpt opt = eRunOpt.NotSet) + public eNarumiCommandResult AGVMoveRun(eRunOpt opt = eRunOpt.NotSet) { System.Text.StringBuilder sb = new StringBuilder(); if (opt == eRunOpt.Backward) sb.Append("B"); @@ -35,7 +90,7 @@ namespace arDev return AddCommand(eAgvCmd.MoveStart, sb.ToString()); } - public bool AGVSetSpeed(eSetSpeed item, int speed) + public eNarumiCommandResult AGVSetSpeed(eSetSpeed item, int speed) { string cmd = "SS"; if (item == eSetSpeed.Rotation) cmd = "SRS"; @@ -43,7 +98,7 @@ namespace arDev cmd += speed.ToString("0000"); return AddCommand(cmd); } - public bool AGVMoveStop(string Reason, eStopOpt opt = eStopOpt.Stop) + public eNarumiCommandResult AGVMoveStop(string Reason, eStopOpt opt = eStopOpt.Stop) { System.Text.StringBuilder sb = new StringBuilder(); if (opt == eStopOpt.MarkStop) sb.Append("MS"); @@ -53,147 +108,187 @@ namespace arDev else sb.Append("0S"); sb.Append("00"); sb.Append("0000"); //재기동시간 - //if (opt == eStopOpt.MarkStop) - // VAR.BOOL[eVarBool.NEXTSTOP_MARK] = true; //동작중이면 이 멈춤 명령을 기록으로 남긴다 230116 if (this.system1.agv_run) RaiseMessage(MessageType.Normal, $"stop command from {Reason}"); - return AddCommand(eAgvCmd.MoveStop, sb.ToString()); + var retval = AddCommand(eAgvCmd.MoveStop, sb.ToString()); + if (retval == eNarumiCommandResult.Success && opt == eStopOpt.MarkStop) + VAR.BOOL[eVarBool.NEXTSTOP_MARK] = true; + + return retval; } - public bool AGVCommand(string cmd, string data) + public eNarumiCommandResult AGVCommand(string cmd, string data) { return AddCommand(cmd + data); } - public bool LiftControl(LiftCommand cmd) + public eNarumiCommandResult LiftControl(LiftCommand cmd) { return AddCommand(eAgvCmd.LiftControl, cmd.ToString()); } - public bool AGVCharge(int chargetID, bool on, int waittime = 3) + public eNarumiCommandResult AGVCharge(int chargetID, bool on, int waittime = 3) { if (on) return AddCommand(eAgvCmd.ChargeOn, chargetID.ToString("0000")); else return AddCommand(eAgvCmd.ChargeOf, chargetID.ToString("0000")); } - public bool SetBackturnTime(int time) + public eNarumiCommandResult SetBackturnTime(int time) { return AddCommand(eAgvCmd.BackTrunResumeTime, time.ToString("0000")); } - public bool SetGateOutOffTime(int time) + public eNarumiCommandResult SetGateOutOffTime(int time) { return AddCommand(eAgvCmd.GateoutTime, time.ToString("0000")); } - public bool TurnGDSCenterScope(UInt16 time) + public eNarumiCommandResult TurnGDSCenterScope(UInt16 time) { if (time > 2000) time = 2000; return AddCommand(eAgvCmd.TurnGDSCenterScope, time.ToString("0000")); } - public bool AGVMoveLeft180Turn() + public eNarumiCommandResult AGVMoveLeft180Turn() { return AddCommand(eAgvCmd.TurnLeft); } - public bool AGVMoveRight180Turn() + public eNarumiCommandResult AGVMoveRight180Turn() { return AddCommand(eAgvCmd.TurnRight); } - public bool AGVMoveBack180Turn(bool leftTurn) + public eNarumiCommandResult AGVMoveBack180Turn(bool leftTurn) { var dir = leftTurn ? "L" : "R"; return AddCommand(eAgvCmd.BackAndTurn, dir); } - - - public bool AGVErrorReset() + public eNarumiCommandResult AGVErrorReset() { return AddCommand(eAgvCmd.ErrorReset, "FFFF"); } - public bool AGVTowerLamp(bool on) + public eNarumiCommandResult AGVTowerLamp(bool on) { return AddCommand(eAgvCmd.TowerLamp, (on ? "I" : "O")); } - public bool AGVSetAddress(int value) + public eNarumiCommandResult AGVSetAddress(int value) { return AddCommand($"SAD{value:0000}"); } - public bool AGVSetPanID(string value) + public eNarumiCommandResult AGVSetPanID(string value) { value = value.PadLeft(4, '0'); return AddCommand($"SPN{value}"); } - public bool AGVSetChannel(string value) + public eNarumiCommandResult AGVSetChannel(string value) { value = value.PadLeft(4, '0'); return AddCommand($"SCH{value}"); } - //public bool AGVGateOutTimer(int value) - //{ - // return AddCommand($"SGT{value:0000}"); - //} - /// - /// 정지감속주기 및 상수 - /// - /// - public bool AGVSetStopAcc(int interval, int value) - { - var cmds = new string[] { - $"SSK{interval:0000}", - $"SCK{value:0000}" - }; - return AddCommand( cmds); - } - public bool AGVSetTagReinputTime(int value) + public eNarumiCommandResult AGVSetTagReinputTime(int value) { return AddCommand($"STT{value:0000}"); } - public bool AGVSetPID(eSetPIDSpeed speed, int P, int I, int D) - { - var cmd = "S"; - var cmd2 = ""; - if (speed == eSetPIDSpeed.High) cmd2 = "K"; - else if (speed == eSetPIDSpeed.Middle) cmd2 = "M"; - else if (speed == eSetPIDSpeed.Low) cmd2 = "L"; - else if (speed == eSetPIDSpeed.Stop) cmd2 = "S"; - var cmds = new string[] { - $"{cmd}P{cmd2}{P:0000}", - $"{cmd}I{cmd2}{I:0000}", - $"{cmd}D{cmd2}{D:0000}", - }; - return AddCommand( cmds); - } - - /// - /// 전송을시도한 시간 - /// - //public Dictionary LastCommandTime { get; set; } - - /// - /// 전송에 성공한 명령시간 - /// - //public Dictionary LastCommandOKTime { get; set; } - protected bool AddCommand(params string[] cmds) + ///// + ///// 전송에 성공한 명령시간 + ///// + ////public Dictionary LastCommandOKTime { get; set; } + protected bool SendCommand(string cmdline) { bool ret = true; - ACKData = string.Empty; //회신값 제거 - //LastCommandTime.Add(cmd, DateTime.Now); - foreach (var cmdline in cmds) - { - var fullcmd = MakeCheckSum(cmdline); - //commandQueue.Enqueue(fullcmd); - if (WriteData(fullcmd) == false) ret = false; - //else LastCommandOKTime.Add(cmd, DateTime.Now); - System.Threading.Thread.Sleep(1); - } + ACKData = string.Empty; + var fullcmd = MakeCheckSum(cmdline); + if (WriteData(fullcmd) == false) ret = false; + System.Threading.Thread.Sleep(1); return ret; } + ManualResetEvent mrecmd = new ManualResetEvent(true); + public eNarumiCommandResult AddCommand(string cmd, int waitms = 1) + { + if (mrecmd.WaitOne(waitms) == false) + { + //다른명령을 처리하는 중이므로 대기상태로 반환한다 + return eNarumiCommandResult.Wait; + } - protected bool AddCommand(eAgvCmd command, BunkiData param) + //다른신호를 처리하지 못하게한다. + mrecmd.Reset(); + + try + { + if (SendCommandFailList.ContainsKey(cmd) == false) + { + //실패기록이 없다 + var ret = SendCommand(cmd); + if (ret == false) + { + SendCommandFailList.Add(cmd, new NarumiCommandTime(1)); + return eNarumiCommandResult.Fail; + } + else return eNarumiCommandResult.Success; + } + else + { + //실패기록이 존재한다. + //1.동일 명령이 아니면 바로 전송한다 + var precmd = SendCommandFailList[cmd]; + + //동일명령의 실패기록이 존재한다. + //2초간의 간격을 둔다 + var ts = DateTime.Now - precmd.Time; + if (ts.TotalSeconds < 2) + { + precmd.Time = DateTime.Now; + SendCommandFailList[cmd] = precmd; + return eNarumiCommandResult.Wait; //대기한다 + } + else + { + //오류가 누적되었다. + var ret = SendCommand(cmd); + if (ret == false) + { + precmd.Time = DateTime.Now; + precmd.count += 1; + SendCommandFailList[cmd] = precmd; + + //5회연속 실패했다면 타임아웃처리한다 + if (precmd.count > 5) + { + //타임아웃 + return eNarumiCommandResult.Timeout; + } + else + { + //실패 + return eNarumiCommandResult.Fail; + } + } + else + { + //전송이성공했으니 키를 제거한다. + SendCommandFailList.Remove(cmd); + return eNarumiCommandResult.Success; + } + } + + } + } + catch (Exception ex) + { + Console.WriteLine($"narumi addCommand error : {ex.Message}"); + return eNarumiCommandResult.Error; + } + finally + { + mrecmd.Set(); + } + } + + + protected eNarumiCommandResult AddCommand(eAgvCmd command, BunkiData param) { return AddCommand(command, param.ToString()); } @@ -204,15 +299,15 @@ namespace arDev /// /// /// - protected bool AddCommand(BunkiData param) + protected eNarumiCommandResult AddCommand(BunkiData param) { return AddCommand(eAgvCmd.MoveSet, param.ToString()); } - protected bool AddCommand(eAgvCmd command, string param = "") + protected eNarumiCommandResult AddCommand(eAgvCmd command, string param = "") { string cmdString; - bool retval = false; + eNarumiCommandResult retval = eNarumiCommandResult.Error; switch (command) { case eAgvCmd.ErrorReset: @@ -247,10 +342,24 @@ namespace arDev case eAgvCmd.TurnLeft: cmdString = $"CTL0000"; retval = AddCommand(cmdString); + if (retval == eNarumiCommandResult.Success) + { + if (TurnInformation == null) TurnInformation = new NarumiTurnInfo(); + TurnInformation.Start = DateTime.Now; + TurnInformation.End = new DateTime(1982, 11, 23); + TurnInformation.State = eNarumiTurn.LeftIng; + } break; case eAgvCmd.TurnRight: cmdString = $"CTR0000"; retval = AddCommand(cmdString); + if (retval == eNarumiCommandResult.Success) + { + if (TurnInformation == null) TurnInformation = new NarumiTurnInfo(); + TurnInformation.Start = DateTime.Now; + TurnInformation.End = new DateTime(1982, 11, 23); + TurnInformation.State = eNarumiTurn.RightIng; + } break; case eAgvCmd.BackAndTurn: if (param.isEmpty()) param = "L"; diff --git a/Cs_HMI/SubProject/AGV/Narumi.cs b/Cs_HMI/SubProject/AGV/Narumi.cs index 48d588f..97684da 100644 --- a/Cs_HMI/SubProject/AGV/Narumi.cs +++ b/Cs_HMI/SubProject/AGV/Narumi.cs @@ -229,7 +229,7 @@ namespace arDev private void RevSTS(Dataframe frame) { LastSTS = frame.DataString; - string rcvdNow = frame.DataString.Replace("\0",""); + string rcvdNow = frame.DataString.Replace("\0", ""); byte[] bRcvData = frame.Buffer; var encoding = System.Text.Encoding.Default; try @@ -258,21 +258,39 @@ namespace arDev error.SetValue(nDataTemp); idx += 4; - data.Speed = rcvdNow.Substring(idx, 1)[0]; idx += 1; //L,M.H data.Sts = rcvdNow.Substring(idx, 1)[0]; idx += 1; //S(직진),L(좌분기),R(우분기) data.Direction = rcvdNow.Substring(idx, 1)[0]; idx += 1; //F,B,L,R data.guidesensor = int.Parse(rcvdNow.Substring(idx, 1)); idx += 1; //가이드 좌측부터 1~9 nDataTemp = Convert.ToByte(rcvdNow.Substring(idx, 2), 16); - signal1.SetValue(nDataTemp); idx += 2; + signal1.SetValue(nDataTemp); idx += 2; - if(idx <= rcvdNow.Length-2) + //agv가 멈춰있고 마크센서가 들어온경우, 턴 작업이었다면 턴 셋팅을 한다 + if (system1.agv_run == false && system1.agv_stop == true && + TurnInformation != null && signal1.mark_sensor) + { + if (TurnInformation.Start.Year > 2000) + { + if (TurnInformation.State == eNarumiTurn.LeftIng || TurnInformation.State == eNarumiTurn.RightIng) + { + TurnInformation.End = DateTime.Now; + if (TurnInformation.State == eNarumiTurn.LeftIng) TurnInformation.State = eNarumiTurn.Left; + if (TurnInformation.State == eNarumiTurn.RightIng) TurnInformation.State = eNarumiTurn.Right; + } + } + else + { + //시작시간이 설정되지 않았다면 처리하지 않는다. + } + } + + if (idx <= rcvdNow.Length - 2) { nDataTemp = Convert.ToByte(rcvdNow.Substring(idx, 2), 16); signal2.SetValue(nDataTemp); } - + DataReceive?.Invoke(this, new DataEventArgs(DataType.STS)); @@ -462,7 +480,7 @@ namespace arDev OFF } - + private bool CheckSum(byte[] bData) { if (bData.Length < 2) // 데이터 길이가 2이하일 경우 비정상 처리 diff --git a/Cs_HMI/SubProject/CommData/Enum.cs b/Cs_HMI/SubProject/CommData/Enum.cs index 41181ef..e62aa47 100644 --- a/Cs_HMI/SubProject/CommData/Enum.cs +++ b/Cs_HMI/SubProject/CommData/Enum.cs @@ -13,6 +13,7 @@ namespace COMM SumQty, ChargeWaitSec, SyncItemCount, + PathValidationError, } public enum eVarUInt32 { diff --git a/Cs_HMI/SubProject/EnigProtocol b/Cs_HMI/SubProject/EnigProtocol index 14ff055..bca75dd 160000 --- a/Cs_HMI/SubProject/EnigProtocol +++ b/Cs_HMI/SubProject/EnigProtocol @@ -1 +1 @@ -Subproject commit 14ff055fa98a3e692c796d961133635e21e9ff69 +Subproject commit bca75ddd6a85bb8bf7c196e8ac8bb2eea52ba522 diff --git a/Document/NewMap.json b/Document/NewMap.json new file mode 100644 index 0000000..5799617 --- /dev/null +++ b/Document/NewMap.json @@ -0,0 +1,1005 @@ +{ + "Nodes": [ + { + "Id": "N001", + "Text": "Unloader", + "Position": "298, 270", + "Type": 0, + "StationType": 3, + "ConnectedNodes": [ + "12", + "14" + ], + "RfidId": 1, + "NodeTextForeColor": "White", + "NodeTextFontSize": 30, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": true, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "N010", + "Text": "Cleaner", + "Position": "298, 446", + "Type": 0, + "StationType": 2, + "ConnectedNodes": [ + "5", + "15" + ], + "RfidId": 11, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": true, + "DockDirection": 1, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "N014", + "Text": "Loader", + "Position": "520, 653", + "Type": 0, + "StationType": 1, + "ConnectedNodes": [ + "6", + "16" + ], + "RfidId": 8, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": true, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "N019", + "Text": "Chg #1", + "Position": "402, 375", + "Type": 0, + "StationType": 5, + "ConnectedNodes": [], + "RfidId": 15, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": true, + "DockDirection": 1, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "N026", + "Text": "Chg #2", + "Position": "541, 570", + "Type": 0, + "StationType": 5, + "ConnectedNodes": [], + "RfidId": 19, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": true, + "DockDirection": 1, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "N018", + "Text": "", + "Position": "213, 630", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "N030", + "N005", + "1" + ], + "RfidId": 34, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "N005", + "Text": "", + "Position": "113, 629", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "N018", + "N020", + "N029" + ], + "RfidId": 33, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "N020", + "Text": "", + "Position": "38, 626", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "N005", + "N021", + "N028" + ], + "RfidId": 32, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "N021", + "Text": "", + "Position": "-54, 626", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "N020", + "N027", + "11" + ], + "RfidId": 31, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "N027", + "Text": "Buf #1", + "Position": "-76, 676", + "Type": 0, + "StationType": 4, + "ConnectedNodes": [ + "N021" + ], + "RfidId": 41, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": true, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "N028", + "Text": "Buf #2", + "Position": "12, 676", + "Type": 0, + "StationType": 4, + "ConnectedNodes": [ + "N020" + ], + "RfidId": 40, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": true, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "N029", + "Text": "Buf #3", + "Position": "89, 674", + "Type": 0, + "StationType": 4, + "ConnectedNodes": [ + "N005" + ], + "RfidId": 39, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": true, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "N030", + "Text": "Buf #4", + "Position": "183, 675", + "Type": 0, + "StationType": 4, + "ConnectedNodes": [ + "N018" + ], + "RfidId": 38, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": true, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "1", + "Text": "", + "Position": "285, 628", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "N018", + "2" + ], + "RfidId": 2, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "2", + "Text": "", + "Position": "354, 628", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "1", + "3" + ], + "RfidId": 4, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "3", + "Text": "", + "Position": "400, 578", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "2", + "4" + ], + "RfidId": 3, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "4", + "Text": "", + "Position": "400, 499", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "3", + "5" + ], + "RfidId": 5, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "5", + "Text": "", + "Position": "462, 451", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "N010", + "4", + "6", + "7" + ], + "RfidId": 6, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "6", + "Text": "", + "Position": "518, 519", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "N014", + "5" + ], + "RfidId": 13, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "7", + "Text": "", + "Position": "517, 400", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "5", + "8" + ], + "RfidId": 7, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "8", + "Text": "", + "Position": "474, 353", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "7", + "9", + "13" + ], + "RfidId": 9, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "9", + "Text": "", + "Position": "517, 311", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "8", + "10" + ], + "RfidId": 10, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "10", + "Text": "", + "Position": "458, 268", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "9", + "12" + ], + "RfidId": 12, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "12", + "Text": "", + "Position": "389, 270", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "N001", + "10" + ], + "RfidId": 16, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + }, + { + "Id": "13", + "Text": "", + "Position": "350, 353", + "Type": 0, + "StationType": 0, + "ConnectedNodes": [ + "8" + ], + "RfidId": 17, + "NodeTextForeColor": "", + "NodeTextFontSize": 7, + "AliasName": "", + "SpeedLimit": 0, + "CanDocking": false, + "DockDirection": 0, + "CanTurnLeft": true, + "CanTurnRight": true, + "DisableCross": false, + "IsActive": true + } + ], + "Labels": [ + { + "Id": "11", + "Type": 1, + "Text": "Label", + "Position": "-120, 625", + "ForeColor": "White", + "BackColor": "Transparent", + "FontFamily": "Arial", + "FontSize": 7, + "FontStyle": 0, + "Padding": 5 + }, + { + "Id": "14", + "Type": 1, + "Text": "Label", + "Position": "224, 272", + "ForeColor": "White", + "BackColor": "Transparent", + "FontFamily": "Arial", + "FontSize": 7, + "FontStyle": 0, + "Padding": 5 + }, + { + "Id": "15", + "Type": 1, + "Text": "Label", + "Position": "247, 449", + "ForeColor": "White", + "BackColor": "Transparent", + "FontFamily": "Arial", + "FontSize": 7, + "FontStyle": 0, + "Padding": 5 + }, + { + "Id": "16", + "Type": 1, + "Text": "Label", + "Position": "517, 721", + "ForeColor": "White", + "BackColor": "Transparent", + "FontFamily": "Arial", + "FontSize": 7, + "FontStyle": 0, + "Padding": 5 + }, + { + "Id": "LBL001", + "Type": 1, + "Text": "Amkor Technology Korea", + "Position": "180, 105", + "ForeColor": "White", + "BackColor": "MidnightBlue", + "FontFamily": "Arial", + "FontSize": 20, + "FontStyle": 0, + "Padding": 5 + } + ], + "Images": [ + { + "Id": "IMG001", + "Type": 2, + "Name": "Image", + "Position": "633, 310", + "ImagePath": "", + "ImageBase64": "", + "Scale": "1, 1", + "Opacity": 1, + "Rotation": 0 + } + ], + "Magnets": [ + { + "Id": "5a0edec2-7ac3-4c99-bbb4-8debde0c1d07", + "Type": 4, + "P1": { + "X": 358.5378151260505, + "Y": 628.438429217841 + }, + "P2": { + "X": -119.57759581805529, + "Y": 626.7844959888976 + }, + "ControlPoint": null + }, + { + "Id": "def7c4b9-86db-42eb-aae6-0c6c9bedcc30", + "Type": 4, + "P1": { + "X": -75.0847526191485, + "Y": 716.1415561436951 + }, + "P2": { + "X": -73.1298113651053, + "Y": 561.6311923370746 + }, + "ControlPoint": null + }, + { + "Id": "624327ee-be0f-4373-b60a-786a93c1eabf", + "Type": 4, + "P1": { + "X": 12.69302515862924, + "Y": 717.2526672548062 + }, + "P2": { + "X": 11.870188634894689, + "Y": 560.2502399561221 + }, + "ControlPoint": null + }, + { + "Id": "f1e885ae-55f7-42e9-b3aa-648541e97da0", + "Type": 4, + "P1": { + "X": 90.47080293640698, + "Y": 716.1415561436951 + }, + "P2": { + "X": 89.72733149203751, + "Y": 560.9169066227888 + }, + "ControlPoint": null + }, + { + "Id": "dc3e8061-2c99-4f24-ac9b-4020dd91fa8b", + "Type": 4, + "P1": { + "X": 185.470802936407, + "Y": 725.0304450325839 + }, + "P2": { + "X": 181.87018863489462, + "Y": 563.059763765646 + }, + "ControlPoint": null + }, + { + "Id": "f4c97a5a-2c2c-4b5e-9dd5-332b1670b827", + "Type": 4, + "P1": { + "X": 343.98784548784573, + "Y": 353.9221611721613 + }, + "P2": { + "X": 472.59413991107186, + "Y": 353.73144759338567 + }, + "ControlPoint": null + }, + { + "Id": "a5424add-e8c9-483c-a5db-733dca1b8f57", + "Type": 4, + "P1": { + "X": 519.3272283272282, + "Y": 720.2549019607841 + }, + "P2": { + "X": 516.1655684825004, + "Y": 309.44573330767145 + }, + "ControlPoint": null + }, + { + "Id": "0bbb27a4-2355-4294-9f2d-a40e4d3d2930", + "Type": 4, + "P1": { + "X": 249.6240475640633, + "Y": 449.77059623383167 + }, + "P2": { + "X": 518.0855860256017, + "Y": 452.84751931075476 + }, + "ControlPoint": null + }, + { + "Id": "92527d4a-8e63-404f-86e8-37568bd4790e", + "Type": 4, + "P1": { + "X": 225.77789371790945, + "Y": 271.3090577722932 + }, + "P2": { + "X": 463.6655684825004, + "Y": 269.44573330767145 + }, + "ControlPoint": null + }, + { + "Id": "0db9553a-f203-478d-8c17-e07f00987828", + "Type": 4, + "P1": { + "X": 463.41452991452985, + "Y": 270.2549019607843 + }, + "P2": { + "X": 515.0811965811965, + "Y": 310.25490196078425 + }, + "ControlPoint": { + "X": 517.8589743589743, + "Y": 266.3660130718954 + } + }, + { + "Id": "0650b2cb-57f9-44ec-9787-fab878cf2b47", + "Type": 4, + "P1": { + "X": 473.41452991452985, + "Y": 353.032679738562 + }, + "P2": { + "X": 515.6367521367521, + "Y": 399.1437908496731 + }, + "ControlPoint": { + "X": 521.7478632478632, + "Y": 351.3660130718954 + } + }, + { + "Id": "7007db10-b61b-4726-9775-417951454ddf", + "Type": 4, + "P1": { + "X": 465.08119658119654, + "Y": 450.2549019607842 + }, + "P2": { + "X": 518.2491858425353, + "Y": 519.0597150914111 + }, + "ControlPoint": { + "X": 522.8589743589743, + "Y": 450.8104575163398 + } + }, + { + "Id": "4ef4bfd0-8fc4-48a5-a490-92b35c7fd1c3", + "Type": 4, + "P1": { + "X": 473.3084256253576, + "Y": 351.58859045052856 + }, + "P2": { + "X": 515.4512827682147, + "Y": 309.8028761648143 + }, + "ControlPoint": { + "X": 519.0227113396433, + "Y": 358.01716187909994 + } + }, + { + "Id": "7d18ae7e-7926-4cf4-8dd6-861462e31352", + "Type": 4, + "P1": { + "X": 464.73699705392903, + "Y": 450.5171618790999 + }, + "P2": { + "X": 516.5227113396433, + "Y": 402.6600190219571 + }, + "ControlPoint": { + "X": 516.1655684825004, + "Y": 454.44573330767133 + } + }, + { + "Id": "532be14d-170e-45c5-adcf-0d555b83b010", + "Type": 4, + "P1": { + "X": 399.76697627462613, + "Y": 521.7210880257198 + }, + "P2": { + "X": 462.6173982141941, + "Y": 449.66152357736206 + }, + "ControlPoint": { + "X": 391.9923982141941, + "Y": 447.16152357736206 + } + }, + { + "Id": "086955e0-0542-4ab5-9ccf-8880e749722a", + "Type": 4, + "P1": { + "X": 399.0526905603404, + "Y": 523.5068023114342 + }, + "P2": { + "X": 400.1113258122239, + "Y": 580.622765294022 + }, + "ControlPoint": null + }, + { + "Id": "2ac7d720-a6df-4174-be74-15ddfe96459b", + "Type": 4, + "P1": { + "X": 353.8613258122239, + "Y": 629.3727652940219 + }, + "P2": { + "X": 400.1113258122239, + "Y": 576.872765294022 + }, + "ControlPoint": { + "X": 404.4863258122239, + "Y": 630.6227652940219 + } + } + ], + "Marks": [ + { + "Id": "2cb51787-c8cf-4ddb-97f0-b71f519d47dc", + "Type": 3, + "Position": "520, 690", + "X": 520, + "Y": 690, + "Rotation": -178.01940688369234 + }, + { + "Id": "f704ebe0-1653-4559-b06f-1eaecafbefba", + "Type": 3, + "Position": "-74, 624", + "X": -74, + "Y": 624, + "Rotation": 90 + }, + { + "Id": "d5b27365-79a2-4351-84c3-6767941ec0be", + "Type": 3, + "Position": "12, 625", + "X": 12, + "Y": 625, + "Rotation": 90 + }, + { + "Id": "0367cafb-9f85-4440-b6b4-c802a58e6181", + "Type": 3, + "Position": "91, 625", + "X": 91, + "Y": 625, + "Rotation": 89.2872271068898 + }, + { + "Id": "1f4ab2c9-07f8-4675-802d-9b4824b55198", + "Type": 3, + "Position": "183, 622", + "X": 183, + "Y": 622, + "Rotation": 88.40516772072262 + }, + { + "Id": "15fddfa4-ff74-48ff-b922-4aacdce1960b", + "Type": 3, + "Position": "275, 269", + "X": 275, + "Y": 269, + "Rotation": 91.71222012905176 + }, + { + "Id": "4b699847-36d4-471c-b990-4ad37967c2dc", + "Type": 3, + "Position": "89, 697", + "X": 89, + "Y": 697, + "Rotation": 0.3824344779617803 + }, + { + "Id": "a9f68317-f1c2-47d8-b029-348b5428be9f", + "Type": 3, + "Position": "183, 699", + "X": 183, + "Y": 699, + "Rotation": -1.3380194104322385 + }, + { + "Id": "fe227205-2a65-4ba9-bb4a-4efb4ed0a7b0", + "Type": 3, + "Position": "9, 699", + "X": 9, + "Y": 699, + "Rotation": 0.8431103833306963 + }, + { + "Id": "5dd29191-798c-480c-b066-7947bfcc4fb7", + "Type": 3, + "Position": "-74, 697", + "X": -74, + "Y": 697, + "Rotation": 1.659829660758831 + }, + { + "Id": "649729f0-ff04-4e11-8869-f6a39d815427", + "Type": 3, + "Position": "-71, 596", + "X": -71, + "Y": 596, + "Rotation": 0 + }, + { + "Id": "2bb9a821-f86b-4190-a182-64abe2c940ed", + "Type": 3, + "Position": "10, 601", + "X": 10, + "Y": 601, + "Rotation": 0 + }, + { + "Id": "821598e1-091a-4884-96fe-6ed5f43c4f62", + "Type": 3, + "Position": "91, 598", + "X": 91, + "Y": 598, + "Rotation": 0 + }, + { + "Id": "66c1bbee-89a8-45a9-b585-ddfd59768f6b", + "Type": 3, + "Position": "184, 596", + "X": 184, + "Y": 596, + "Rotation": 0 + }, + { + "Id": "06a10f46-bda8-4b0f-9e7a-63d66bd2f7e4", + "Type": 3, + "Position": "381, 355", + "X": 381, + "Y": 355, + "Rotation": 91.24536426676838 + }, + { + "Id": "835b8982-042b-4e2e-a83b-19b32e55cd5b", + "Type": 3, + "Position": "519, 550", + "X": 519, + "Y": 550, + "Rotation": 0 + } + ], + "Settings": { + "BackgroundColorArgb": -14671840, + "ShowGrid": false + }, + "CreatedDate": "2025-12-23T04:16:13.079Z", + "Version": "1.3" +} \ No newline at end of file