From faf13f5c371b7a25973033e75d61c54a8bd9aa9d Mon Sep 17 00:00:00 2001 From: backuppc Date: Fri, 30 Jan 2026 16:58:14 +0900 Subject: [PATCH] .. --- AGVEmulator/AGVEmulator.csproj | 1 + AGVEmulator/RemoteStatus.cs | 37 ++ AGVEmulator/RunCode/_AGV.cs | 1 + AGVEmulator/RunCode/_XBEE.cs | 40 +- AGVEmulator/fMain.Designer.cs | 414 ++++++++++-------- AGVEmulator/fMain.cs | 29 +- .../Controls/UnifiedAGVCanvas.cs | 27 ++ HMI/Project/AGV4.csproj | 13 +- HMI/Project/Device/Xbee.cs | 10 +- .../Dialog/fSetCurrentPosition.Designer.cs | 230 ++++++++++ HMI/Project/Dialog/fSetCurrentPosition.cs | 128 ++++++ HMI/Project/Dialog/fSetCurrentPosition.resx | 120 +++++ HMI/Project/PUB.cs | 140 +++--- .../StateMachine/Step/_SM_RUN_BUFFER_IN.cs | 105 +++-- .../StateMachine/Step/_SM_RUN_BUFFER_OUT.cs | 104 +++-- HMI/Project/StateMachine/Step/_Util.cs | 2 + HMI/Project/StateMachine/_AGV.cs | 25 +- HMI/Project/StateMachine/_Loop.cs | 1 - HMI/Project/StateMachine/_Xbee.cs | 41 +- HMI/Project/fMain.cs | 39 +- HMI/SubProject/AGV/Command.cs | 1 + HMI/SubProject/AGV/Narumi.cs | 46 +- 22 files changed, 1137 insertions(+), 417 deletions(-) create mode 100644 AGVEmulator/RemoteStatus.cs create mode 100644 HMI/Project/Dialog/fSetCurrentPosition.Designer.cs create mode 100644 HMI/Project/Dialog/fSetCurrentPosition.cs create mode 100644 HMI/Project/Dialog/fSetCurrentPosition.resx diff --git a/AGVEmulator/AGVEmulator.csproj b/AGVEmulator/AGVEmulator.csproj index 4add5f4..9465bf3 100644 --- a/AGVEmulator/AGVEmulator.csproj +++ b/AGVEmulator/AGVEmulator.csproj @@ -68,6 +68,7 @@ + Form diff --git a/AGVEmulator/RemoteStatus.cs b/AGVEmulator/RemoteStatus.cs new file mode 100644 index 0000000..557b8b1 --- /dev/null +++ b/AGVEmulator/RemoteStatus.cs @@ -0,0 +1,37 @@ +using System.Text; + +namespace AGVEmulator +{ + + public class RemoteStatus + { + public byte Mode { get; set; } // 0=manual, 1=auto + public byte RunSt { get; set; } // 0=stop, 1=run, 2=error + public byte RunStep { get; set; } + public byte RunStepSeq { get; set; } + public byte MotorDir { get; set; } // 0=F, 1=B + public byte MagnetDir { get; set; } // 0=S, 1=L, 2=R + public byte ChargeSt { get; set; } // 0=off, 1=on + public byte CartSt { get; set; } // 0=off, 1=on, 2=unknown + public byte LiftSt { get; set; } // 0=down, 1=up, 2=unknown + public byte ErrorCode { get; set; } + public string LastTag { get; set; } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine($"Mode: {(Mode == 1 ? "Auto" : "Manual")}"); + sb.AppendLine($"RunSt: {(RunSt == 0 ? "Stop" : (RunSt == 1 ? "Run" : "Error"))}"); + sb.AppendLine($"Step: {RunStep}, Seq: {RunStepSeq}"); + sb.AppendLine($"Dir: {(MotorDir == 1 ? "B" : "F")}, Mag: {(MagnetDir == 1 ? "L" : (MagnetDir == 2 ? "R" : "S"))}"); + sb.AppendLine($"Charge: {(ChargeSt == 1 ? "ON" : "OFF")}"); + sb.AppendLine($"Cart: {(CartSt == 1 ? "ON" : (CartSt == 0 ? "OFF" : "Unk"))}"); + sb.AppendLine($"Lift: {(LiftSt == 1 ? "UP" : (LiftSt == 0 ? "DOWN" : "Unk"))}"); + sb.Append($"Tag: {LastTag}"); + return sb.ToString(); + } + } + +} + + diff --git a/AGVEmulator/RunCode/_AGV.cs b/AGVEmulator/RunCode/_AGV.cs index f26ff10..9e76e12 100644 --- a/AGVEmulator/RunCode/_AGV.cs +++ b/AGVEmulator/RunCode/_AGV.cs @@ -80,6 +80,7 @@ namespace AGVEmulator } break; } + UpdateUIStatus(); } private void Agv_ValueChanged(object sender, DevAGV.ValueChangedArgs e) diff --git a/AGVEmulator/RunCode/_XBEE.cs b/AGVEmulator/RunCode/_XBEE.cs index 9d9696a..1029bce 100644 --- a/AGVEmulator/RunCode/_XBEE.cs +++ b/AGVEmulator/RunCode/_XBEE.cs @@ -24,15 +24,41 @@ namespace AGVEmulator } private void CAL_ProtocReceived(object sender, ENIG.EEProtocol.DataEventArgs e) { - //throw new NotImplementedException(); - var dev = (DeviceType)e.ReceivedPacket.ID; - if (dev == DeviceType.AGV1 || dev == DeviceType.AGV2) + // HMI(Host)에서 호스트로 취급되는 HMI가 보낸 패킷은 ID가 0(ACS)임. + // 하지만 xbee.cs에서 CreatePacket 시 PUB.setting.XBE_ID를 사용함. + // 에뮬레이터에서는 이 패킷들을 수신하여 상태를 업데이트함. + + var cmd = (ENIGProtocol.AGVCommandEH)e.ReceivedPacket.Command; + var data = e.ReceivedPacket.Data; + + if (cmd == ENIGProtocol.AGVCommandEH.Status) { - //agv에서 들어오는 데이터 - var cmd = e.ReceivedPacket.Command; - if(cmd == 3) + if (data.Length >= 14) { - //status + _remoteStatus.Mode = data[0]; + _remoteStatus.RunSt = data[1]; + _remoteStatus.RunStep = data[2]; + _remoteStatus.RunStepSeq = data[3]; + _remoteStatus.MotorDir = data[4]; + _remoteStatus.MagnetDir = data[5]; + _remoteStatus.ChargeSt = data[6]; + _remoteStatus.CartSt = data[7]; + _remoteStatus.LiftSt = data[8]; + _remoteStatus.ErrorCode = data[9]; + _remoteStatus.LastTag = Encoding.ASCII.GetString(data, 10, 4); + + UpdateUIStatus(); + } + } + else if (cmd == ENIGProtocol.AGVCommandEH.Error) + { + if (data.Length >= 1) + { + _remoteErrorCode = (ENIGProtocol.AGVErrorCode)data[0]; + // _remoteErrorMessage = ... Error 메시지 자체는 패킷에 포함되지 않으므로 유틸리티 사용 가능 + _remoteErrorMessage = ENIGProtocol.AGVUtility.GetAGVErrorMessage(_remoteErrorCode); + + UpdateUIStatus(); } } } diff --git a/AGVEmulator/fMain.Designer.cs b/AGVEmulator/fMain.Designer.cs index 5c2203b..f20e904 100644 --- a/AGVEmulator/fMain.Designer.cs +++ b/AGVEmulator/fMain.Designer.cs @@ -32,34 +32,34 @@ namespace AGVEmulator private void InitializeComponent() { this.components = new System.ComponentModel.Container(); - AGVEmulator.UC.AgvViewer.ptdata ptdata113 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata114 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata115 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata116 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata117 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata118 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata119 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata120 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata121 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata122 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata123 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata124 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata125 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata126 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata127 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata128 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata129 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata130 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata131 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata132 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata133 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata134 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata135 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata136 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata137 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata138 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata139 = new AGVEmulator.UC.AgvViewer.ptdata(); - AGVEmulator.UC.AgvViewer.ptdata ptdata140 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata29 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata30 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata31 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata32 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata33 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata34 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata35 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata36 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata37 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata38 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata39 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata40 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata41 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata42 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata43 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata44 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata45 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata46 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata47 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata48 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata49 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata50 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata51 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata52 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata53 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata54 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata55 = new AGVEmulator.UC.AgvViewer.ptdata(); + AGVEmulator.UC.AgvViewer.ptdata ptdata56 = 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(); @@ -142,6 +142,10 @@ namespace AGVEmulator this.tabPage2 = new System.Windows.Forms.TabPage(); this.tabPage3 = new System.Windows.Forms.TabPage(); this.panel3 = new System.Windows.Forms.Panel(); + this.label13 = new System.Windows.Forms.Label(); + this.button6 = new System.Windows.Forms.Button(); + this.button13 = new System.Windows.Forms.Button(); + this.label12 = new System.Windows.Forms.Label(); this.button3 = new System.Windows.Forms.Button(); this.button2 = new System.Windows.Forms.Button(); this.nudIDAgv = new System.Windows.Forms.NumericUpDown(); @@ -170,10 +174,11 @@ namespace AGVEmulator this.sbBMS = new System.Windows.Forms.ToolStripStatusLabel(); this.toolStripStatusLabel2 = new System.Windows.Forms.ToolStripStatusLabel(); this.sbCAL = new System.Windows.Forms.ToolStripStatusLabel(); - this.label12 = new System.Windows.Forms.Label(); - this.label13 = new System.Windows.Forms.Label(); - this.button6 = new System.Windows.Forms.Button(); - this.button13 = new System.Windows.Forms.Button(); + this.groupBox12 = new System.Windows.Forms.GroupBox(); + this.groupBox13 = new System.Windows.Forms.GroupBox(); + this.tbErmsg = new System.Windows.Forms.TextBox(); + this.tbErCode = new System.Windows.Forms.TextBox(); + this.rtStatus = new System.Windows.Forms.RichTextBox(); this.groupBox1.SuspendLayout(); this.panel1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.trbT2)).BeginInit(); @@ -205,6 +210,8 @@ namespace AGVEmulator ((System.ComponentModel.ISupportInitialize)(this.nudTagNo)).BeginInit(); this.toolStrip1.SuspendLayout(); this.statusStrip1.SuspendLayout(); + this.groupBox12.SuspendLayout(); + this.groupBox13.SuspendLayout(); this.SuspendLayout(); // // groupBox1 @@ -1119,120 +1126,120 @@ namespace AGVEmulator this.agvViewer1.lastmarkdir = ""; this.agvViewer1.lasttag = ""; this.agvViewer1.lasttagdir = ""; - ptdata113.active = false; - ptdata113.data = "NOT"; - ptdata113.pos = 30F; - ptdata114.active = false; - ptdata114.data = "QA"; - ptdata114.pos = 200F; - ptdata115.active = false; - ptdata115.data = "CHG"; - ptdata115.pos = 300F; - ptdata116.active = false; - ptdata116.data = "QC"; - ptdata116.pos = 400F; - ptdata117.active = false; - ptdata117.data = "#FVI-1"; - ptdata117.pos = 500F; - ptdata118.active = false; - ptdata118.data = "#FVI-2"; - ptdata118.pos = 600F; - ptdata119.active = false; - ptdata119.data = "#FVI-3"; - ptdata119.pos = 700F; - ptdata120.active = false; - ptdata120.data = "#FVI-4"; - ptdata120.pos = 800F; - ptdata121.active = false; - ptdata121.data = "#FVI-5"; - ptdata121.pos = 900F; - ptdata122.active = false; - ptdata122.data = "POT"; - ptdata122.pos = 970F; + ptdata29.active = false; + ptdata29.data = "NOT"; + ptdata29.pos = 30F; + ptdata30.active = false; + ptdata30.data = "QA"; + ptdata30.pos = 200F; + ptdata31.active = false; + ptdata31.data = "CHG"; + ptdata31.pos = 300F; + ptdata32.active = false; + ptdata32.data = "QC"; + ptdata32.pos = 400F; + ptdata33.active = false; + ptdata33.data = "#FVI-1"; + ptdata33.pos = 500F; + ptdata34.active = false; + ptdata34.data = "#FVI-2"; + ptdata34.pos = 600F; + ptdata35.active = false; + ptdata35.data = "#FVI-3"; + ptdata35.pos = 700F; + ptdata36.active = false; + ptdata36.data = "#FVI-4"; + ptdata36.pos = 800F; + ptdata37.active = false; + ptdata37.data = "#FVI-5"; + ptdata37.pos = 900F; + ptdata38.active = false; + ptdata38.data = "POT"; + ptdata38.pos = 970F; this.agvViewer1.listMRK = new AGVEmulator.UC.AgvViewer.ptdata[] { - ptdata113, - ptdata114, - ptdata115, - ptdata116, - ptdata117, - ptdata118, - ptdata119, - ptdata120, - ptdata121, - ptdata122}; - ptdata123.active = false; - ptdata123.data = "9000"; - ptdata123.pos = 80F; - ptdata124.active = false; - ptdata124.data = "9001"; - ptdata124.pos = 120F; - ptdata125.active = false; - ptdata125.data = "9010"; - ptdata125.pos = 180F; - ptdata126.active = false; - ptdata126.data = "9011"; - ptdata126.pos = 220F; - ptdata127.active = false; - ptdata127.data = "9020"; - ptdata127.pos = 280F; - ptdata128.active = false; - ptdata128.data = "9021"; - ptdata128.pos = 320F; - ptdata129.active = false; - ptdata129.data = "9030"; - ptdata129.pos = 380F; - ptdata130.active = false; - ptdata130.data = "9031"; - ptdata130.pos = 420F; - ptdata131.active = false; - ptdata131.data = "9040"; - ptdata131.pos = 480F; - ptdata132.active = false; - ptdata132.data = "9041"; - ptdata132.pos = 520F; - ptdata133.active = false; - ptdata133.data = "9050"; - ptdata133.pos = 580F; - ptdata134.active = false; - ptdata134.data = "9051"; - ptdata134.pos = 620F; - ptdata135.active = false; - ptdata135.data = "9060"; - ptdata135.pos = 680F; - ptdata136.active = false; - ptdata136.data = "9061"; - ptdata136.pos = 720F; - ptdata137.active = false; - ptdata137.data = "9070"; - ptdata137.pos = 780F; - ptdata138.active = false; - ptdata138.data = "9071"; - ptdata138.pos = 820F; - ptdata139.active = false; - ptdata139.data = "9000"; - ptdata139.pos = 10F; - ptdata140.active = false; - ptdata140.data = "9001"; - ptdata140.pos = 50F; + ptdata29, + ptdata30, + ptdata31, + ptdata32, + ptdata33, + ptdata34, + ptdata35, + ptdata36, + ptdata37, + ptdata38}; + ptdata39.active = false; + ptdata39.data = "9000"; + ptdata39.pos = 80F; + ptdata40.active = false; + ptdata40.data = "9001"; + ptdata40.pos = 120F; + ptdata41.active = false; + ptdata41.data = "9010"; + ptdata41.pos = 180F; + ptdata42.active = false; + ptdata42.data = "9011"; + ptdata42.pos = 220F; + ptdata43.active = false; + ptdata43.data = "9020"; + ptdata43.pos = 280F; + ptdata44.active = false; + ptdata44.data = "9021"; + ptdata44.pos = 320F; + ptdata45.active = false; + ptdata45.data = "9030"; + ptdata45.pos = 380F; + ptdata46.active = false; + ptdata46.data = "9031"; + ptdata46.pos = 420F; + ptdata47.active = false; + ptdata47.data = "9040"; + ptdata47.pos = 480F; + ptdata48.active = false; + ptdata48.data = "9041"; + ptdata48.pos = 520F; + ptdata49.active = false; + ptdata49.data = "9050"; + ptdata49.pos = 580F; + ptdata50.active = false; + ptdata50.data = "9051"; + ptdata50.pos = 620F; + ptdata51.active = false; + ptdata51.data = "9060"; + ptdata51.pos = 680F; + ptdata52.active = false; + ptdata52.data = "9061"; + ptdata52.pos = 720F; + ptdata53.active = false; + ptdata53.data = "9070"; + ptdata53.pos = 780F; + ptdata54.active = false; + ptdata54.data = "9071"; + ptdata54.pos = 820F; + ptdata55.active = false; + ptdata55.data = "9000"; + ptdata55.pos = 10F; + ptdata56.active = false; + ptdata56.data = "9001"; + ptdata56.pos = 50F; this.agvViewer1.listTAG = new AGVEmulator.UC.AgvViewer.ptdata[] { - ptdata123, - ptdata124, - ptdata125, - ptdata126, - ptdata127, - ptdata128, - ptdata129, - ptdata130, - ptdata131, - ptdata132, - ptdata133, - ptdata134, - ptdata135, - ptdata136, - ptdata137, - ptdata138, - ptdata139, - ptdata140}; + ptdata39, + ptdata40, + ptdata41, + ptdata42, + ptdata43, + ptdata44, + ptdata45, + ptdata46, + ptdata47, + ptdata48, + ptdata49, + ptdata50, + ptdata51, + ptdata52, + ptdata53, + ptdata54, + ptdata55, + ptdata56}; this.agvViewer1.Location = new System.Drawing.Point(241, 0); this.agvViewer1.Name = "agvViewer1"; this.agvViewer1.Size = new System.Drawing.Size(899, 120); @@ -1277,6 +1284,8 @@ namespace AGVEmulator // // panel3 // + this.panel3.Controls.Add(this.groupBox13); + this.panel3.Controls.Add(this.groupBox12); this.panel3.Controls.Add(this.label13); this.panel3.Controls.Add(this.button6); this.panel3.Controls.Add(this.button13); @@ -1301,6 +1310,46 @@ namespace AGVEmulator this.panel3.Size = new System.Drawing.Size(364, 622); this.panel3.TabIndex = 15; // + // label13 + // + this.label13.AutoSize = true; + this.label13.Location = new System.Drawing.Point(17, 362); + this.label13.Name = "label13"; + this.label13.Size = new System.Drawing.Size(26, 12); + this.label13.TabIndex = 19; + this.label13.Text = "Exit"; + // + // button6 + // + this.button6.Location = new System.Drawing.Point(204, 349); + this.button6.Name = "button6"; + this.button6.Size = new System.Drawing.Size(133, 38); + this.button6.TabIndex = 18; + this.button6.Tag = "--"; + this.button6.Text = "Pick Off"; + this.button6.UseVisualStyleBackColor = true; + this.button6.Click += new System.EventHandler(this.button6_Click_1); + // + // button13 + // + this.button13.Location = new System.Drawing.Point(65, 349); + this.button13.Name = "button13"; + this.button13.Size = new System.Drawing.Size(133, 38); + this.button13.TabIndex = 17; + this.button13.Tag = "--"; + this.button13.Text = "Pick On"; + this.button13.UseVisualStyleBackColor = true; + this.button13.Click += new System.EventHandler(this.button13_Click); + // + // label12 + // + this.label12.AutoSize = true; + this.label12.Location = new System.Drawing.Point(17, 318); + this.label12.Name = "label12"; + this.label12.Size = new System.Drawing.Size(34, 12); + this.label12.TabIndex = 16; + this.label12.Text = "Enter"; + // // button3 // this.button3.Location = new System.Drawing.Point(204, 305); @@ -1583,45 +1632,50 @@ namespace AGVEmulator this.sbCAL.Size = new System.Drawing.Size(19, 17); this.sbCAL.Text = "●"; // - // label12 + // groupBox12 // - this.label12.AutoSize = true; - this.label12.Location = new System.Drawing.Point(17, 318); - this.label12.Name = "label12"; - this.label12.Size = new System.Drawing.Size(34, 12); - this.label12.TabIndex = 16; - this.label12.Text = "Enter"; + this.groupBox12.Controls.Add(this.rtStatus); + this.groupBox12.Location = new System.Drawing.Point(6, 393); + this.groupBox12.Name = "groupBox12"; + this.groupBox12.Size = new System.Drawing.Size(355, 147); + this.groupBox12.TabIndex = 20; + this.groupBox12.TabStop = false; + this.groupBox12.Text = "status"; // - // label13 + // groupBox13 // - this.label13.AutoSize = true; - this.label13.Location = new System.Drawing.Point(17, 362); - this.label13.Name = "label13"; - this.label13.Size = new System.Drawing.Size(26, 12); - this.label13.TabIndex = 19; - this.label13.Text = "Exit"; + this.groupBox13.Controls.Add(this.tbErCode); + this.groupBox13.Controls.Add(this.tbErmsg); + this.groupBox13.Dock = System.Windows.Forms.DockStyle.Bottom; + this.groupBox13.Location = new System.Drawing.Point(0, 546); + this.groupBox13.Name = "groupBox13"; + this.groupBox13.Size = new System.Drawing.Size(364, 76); + this.groupBox13.TabIndex = 21; + this.groupBox13.TabStop = false; + this.groupBox13.Text = "error"; // - // button6 + // tbErmsg // - this.button6.Location = new System.Drawing.Point(204, 349); - this.button6.Name = "button6"; - this.button6.Size = new System.Drawing.Size(133, 38); - this.button6.TabIndex = 18; - this.button6.Tag = "--"; - this.button6.Text = "Pick Off"; - this.button6.UseVisualStyleBackColor = true; - this.button6.Click += new System.EventHandler(this.button6_Click_1); + this.tbErmsg.Location = new System.Drawing.Point(13, 45); + this.tbErmsg.Name = "tbErmsg"; + this.tbErmsg.Size = new System.Drawing.Size(287, 21); + this.tbErmsg.TabIndex = 0; // - // button13 + // tbErCode // - this.button13.Location = new System.Drawing.Point(65, 349); - this.button13.Name = "button13"; - this.button13.Size = new System.Drawing.Size(133, 38); - this.button13.TabIndex = 17; - this.button13.Tag = "--"; - this.button13.Text = "Pick On"; - this.button13.UseVisualStyleBackColor = true; - this.button13.Click += new System.EventHandler(this.button13_Click); + this.tbErCode.Location = new System.Drawing.Point(12, 18); + this.tbErCode.Name = "tbErCode"; + this.tbErCode.Size = new System.Drawing.Size(287, 21); + this.tbErCode.TabIndex = 1; + // + // rtStatus + // + this.rtStatus.Dock = System.Windows.Forms.DockStyle.Fill; + this.rtStatus.Location = new System.Drawing.Point(3, 17); + this.rtStatus.Name = "rtStatus"; + this.rtStatus.Size = new System.Drawing.Size(349, 127); + this.rtStatus.TabIndex = 0; + this.rtStatus.Text = ""; // // fMain // @@ -1678,6 +1732,9 @@ namespace AGVEmulator this.toolStrip1.PerformLayout(); this.statusStrip1.ResumeLayout(false); this.statusStrip1.PerformLayout(); + this.groupBox12.ResumeLayout(false); + this.groupBox13.ResumeLayout(false); + this.groupBox13.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); @@ -1798,6 +1855,11 @@ namespace AGVEmulator private Label label13; private Button button6; private Button button13; + private GroupBox groupBox13; + private GroupBox groupBox12; + private TextBox tbErCode; + private TextBox tbErmsg; + private RichTextBox rtStatus; } } diff --git a/AGVEmulator/fMain.cs b/AGVEmulator/fMain.cs index 315af05..1d3f375 100644 --- a/AGVEmulator/fMain.cs +++ b/AGVEmulator/fMain.cs @@ -8,7 +8,6 @@ using System.Globalization; using System.IO; using System.Linq; using System.Security.Cryptography.X509Certificates; -using System.Text; using System.Windows.Forms; using System.Windows.Forms.VisualStyles; using static AGVEmulator.DevAGV; @@ -24,6 +23,10 @@ namespace AGVEmulator DevAGV AGV; DevXBE XBE; + public RemoteStatus _remoteStatus = new RemoteStatus(); + public ENIGProtocol.AGVErrorCode _remoteErrorCode = ENIGProtocol.AGVErrorCode.None; + public string _remoteErrorMessage = ""; + // Map Control private UnifiedAGVCanvas _agvCanvas; private VirtualAGV _visualAgv; @@ -980,6 +983,30 @@ namespace AGVEmulator var target = (byte)nudIDAgv.Value; this.XBE.SendPickOffExit(target); } + + public void UpdateUIStatus() + { + if (this.InvokeRequired) + { + this.BeginInvoke(new Action(UpdateUIStatus)); + return; + } + + rtStatus.Text = _remoteStatus.ToString(); + tbErCode.Text = _remoteErrorCode.ToString(); + tbErmsg.Text = _remoteErrorMessage; + + if (_remoteErrorCode != ENIGProtocol.AGVErrorCode.None) + { + tbErCode.BackColor = Color.Red; + tbErCode.ForeColor = Color.White; + } + else + { + tbErCode.BackColor = SystemColors.Window; + tbErCode.ForeColor = SystemColors.WindowText; + } + } } } diff --git a/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs b/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs index 3f30bb4..9136863 100644 --- a/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs +++ b/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs @@ -349,6 +349,20 @@ namespace AGVNavigationCore.Controls public MapNode SelectedNode { get { return this._selectedNode as MapNode; } + set + { + _selectedNode = value; + if (value != null) + { + _selectedNodes.Clear(); + _selectedNodes.Add(value); + } + else + { + _selectedNodes.Clear(); + } + Invalidate(); + } } /// @@ -393,6 +407,19 @@ namespace AGVNavigationCore.Controls this.FitToNodes(); } + /// + /// 맵 데이터를 셋팅합니다 + /// + public void SetMapData(List nodes, List labels = null, List images = null, List marks = null, List magnets = null) + { + this.Nodes = nodes; + this.Labels = labels ?? new List(); + this.Images = images ?? new List(); + this.Marks = marks ?? new List(); + this.Magnets = magnets ?? new List(); + this.FitToNodes(); + } + /// /// 노드 목록 /// diff --git a/HMI/Project/AGV4.csproj b/HMI/Project/AGV4.csproj index 497fa76..7b17160 100644 --- a/HMI/Project/AGV4.csproj +++ b/HMI/Project/AGV4.csproj @@ -197,6 +197,12 @@ fCounter.cs + + Form + + + fSetCurrentPosition.cs + Form @@ -412,6 +418,9 @@ fCounter.cs + + fSetCurrentPosition.cs + fXbeeSetting.cs @@ -573,9 +582,7 @@ CommData - - - + diff --git a/HMI/Project/Device/Xbee.cs b/HMI/Project/Device/Xbee.cs index 0b9c459..38727ec 100644 --- a/HMI/Project/Device/Xbee.cs +++ b/HMI/Project/Device/Xbee.cs @@ -181,20 +181,20 @@ namespace Project.Device /// 오류코드를 호스트에 전송합니다 /// /// - public void SendError(ENIGProtocol.AGVErrorCode errcode, string errormessage) + public void SendError(ENIGProtocol.AGVErrorCode errcode) { // Update global error state so it persists in Status messages PUB.Result.RunStepErrorCode = errcode; - PUB.Result.ResultMessage = errormessage; + //PUB.Result.ResultMessage = errormessage; var id = PUB.setting.XBE_ID; byte cmd = (byte)ENIGProtocol.AGVCommandEH.Error; - if (errormessage.Length > 30) errormessage = errormessage.Substring(0, 29); + // if (errormessage.Length > 30) errormessage = errormessage.Substring(0, 29); var data = new List(); data.Add((byte)errcode); - var datamsg = System.Text.Encoding.Default.GetBytes(errormessage); - data.AddRange(datamsg); + //var datamsg = System.Text.Encoding.Default.GetBytes(errormessage); + // data.AddRange(datamsg); var packet = proto.CreatePacket(id, cmd, data.ToArray()); Send(packet); diff --git a/HMI/Project/Dialog/fSetCurrentPosition.Designer.cs b/HMI/Project/Dialog/fSetCurrentPosition.Designer.cs new file mode 100644 index 0000000..4e43ab0 --- /dev/null +++ b/HMI/Project/Dialog/fSetCurrentPosition.Designer.cs @@ -0,0 +1,230 @@ +namespace Project.Dialog +{ + partial class fSetCurrentPosition + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.mainLayout = new System.Windows.Forms.TableLayoutPanel(); + this.controlPanel = new System.Windows.Forms.Panel(); + this._lblStatus = new System.Windows.Forms.Label(); + this.btnForward = new System.Windows.Forms.Button(); + this.btnBackward = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnTurnR90 = new System.Windows.Forms.Button(); + this.lblTurn = new System.Windows.Forms.Label(); + this.btnTurnNone = new System.Windows.Forms.Button(); + this.btnTurnL90 = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.mainLayout.SuspendLayout(); + this.controlPanel.SuspendLayout(); + this.SuspendLayout(); + // + // mainLayout + // + this.mainLayout.ColumnCount = 1; + this.mainLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.mainLayout.Controls.Add(this.controlPanel, 0, 2); + this.mainLayout.Controls.Add(this.label1, 0, 0); + this.mainLayout.Dock = System.Windows.Forms.DockStyle.Fill; + this.mainLayout.Location = new System.Drawing.Point(0, 0); + this.mainLayout.Name = "mainLayout"; + this.mainLayout.RowCount = 3; + this.mainLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.mainLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 84.60411F)); + this.mainLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 15.39589F)); + this.mainLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.mainLayout.Size = new System.Drawing.Size(1264, 761); + this.mainLayout.TabIndex = 0; + // + // controlPanel + // + this.controlPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(30)))), ((int)(((byte)(30))))); + this.controlPanel.Controls.Add(this._lblStatus); + this.controlPanel.Controls.Add(this.btnForward); + this.controlPanel.Controls.Add(this.btnBackward); + this.controlPanel.Controls.Add(this.btnOK); + this.controlPanel.Controls.Add(this.btnCancel); + this.controlPanel.Controls.Add(this.btnTurnR90); + this.controlPanel.Controls.Add(this.lblTurn); + this.controlPanel.Controls.Add(this.btnTurnNone); + this.controlPanel.Controls.Add(this.btnTurnL90); + this.controlPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.controlPanel.Location = new System.Drawing.Point(3, 652); + this.controlPanel.Name = "controlPanel"; + this.controlPanel.Padding = new System.Windows.Forms.Padding(10); + this.controlPanel.Size = new System.Drawing.Size(1258, 106); + this.controlPanel.TabIndex = 1; + // + // _lblStatus + // + this._lblStatus.Font = new System.Drawing.Font("맑은 고딕", 12F, System.Drawing.FontStyle.Bold); + this._lblStatus.ForeColor = System.Drawing.Color.White; + this._lblStatus.Location = new System.Drawing.Point(7, 7); + this._lblStatus.Margin = new System.Windows.Forms.Padding(3, 10, 20, 0); + this._lblStatus.Name = "_lblStatus"; + this._lblStatus.Size = new System.Drawing.Size(653, 42); + this._lblStatus.TabIndex = 0; + this._lblStatus.Text = "위치를 선택해 주세요."; + this._lblStatus.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // btnForward + // + this.btnForward.BackColor = System.Drawing.Color.DarkSlateGray; + this.btnForward.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnForward.ForeColor = System.Drawing.Color.White; + this.btnForward.Location = new System.Drawing.Point(721, 51); + this.btnForward.Name = "btnForward"; + this.btnForward.Size = new System.Drawing.Size(120, 38); + this.btnForward.TabIndex = 1; + this.btnForward.Text = "전진 (Forward)"; + this.btnForward.UseVisualStyleBackColor = false; + // + // btnBackward + // + this.btnBackward.BackColor = System.Drawing.Color.DarkSlateGray; + this.btnBackward.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnBackward.ForeColor = System.Drawing.Color.White; + this.btnBackward.Location = new System.Drawing.Point(847, 51); + this.btnBackward.Name = "btnBackward"; + this.btnBackward.Size = new System.Drawing.Size(126, 38); + this.btnBackward.TabIndex = 2; + this.btnBackward.Text = "후진 (Backward)"; + this.btnBackward.UseVisualStyleBackColor = false; + // + // btnOK + // + this.btnOK.BackColor = System.Drawing.Color.DarkGreen; + this.btnOK.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnOK.ForeColor = System.Drawing.Color.White; + this.btnOK.Location = new System.Drawing.Point(7, 51); + this.btnOK.Margin = new System.Windows.Forms.Padding(50, 3, 3, 3); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(150, 38); + this.btnOK.TabIndex = 7; + this.btnOK.Text = "설정 완료"; + this.btnOK.UseVisualStyleBackColor = false; + // + // btnCancel + // + this.btnCancel.BackColor = System.Drawing.Color.Maroon; + this.btnCancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnCancel.ForeColor = System.Drawing.Color.White; + this.btnCancel.Location = new System.Drawing.Point(163, 51); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(100, 38); + this.btnCancel.TabIndex = 8; + this.btnCancel.Text = "취소"; + this.btnCancel.UseVisualStyleBackColor = false; + // + // btnTurnR90 + // + this.btnTurnR90.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(60)))), ((int)(((byte)(60))))); + this.btnTurnR90.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnTurnR90.ForeColor = System.Drawing.Color.White; + this.btnTurnR90.Location = new System.Drawing.Point(893, 7); + this.btnTurnR90.Name = "btnTurnR90"; + this.btnTurnR90.Size = new System.Drawing.Size(80, 40); + this.btnTurnR90.TabIndex = 6; + this.btnTurnR90.Text = "R90"; + this.btnTurnR90.UseVisualStyleBackColor = false; + // + // lblTurn + // + this.lblTurn.AutoSize = true; + this.lblTurn.ForeColor = System.Drawing.Color.White; + this.lblTurn.Location = new System.Drawing.Point(677, 20); + this.lblTurn.Margin = new System.Windows.Forms.Padding(20, 10, 5, 0); + this.lblTurn.Name = "lblTurn"; + this.lblTurn.Size = new System.Drawing.Size(35, 12); + this.lblTurn.TabIndex = 3; + this.lblTurn.Text = "Turn:"; + // + // btnTurnNone + // + this.btnTurnNone.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(60)))), ((int)(((byte)(60))))); + this.btnTurnNone.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnTurnNone.ForeColor = System.Drawing.Color.White; + this.btnTurnNone.Location = new System.Drawing.Point(721, 7); + this.btnTurnNone.Name = "btnTurnNone"; + this.btnTurnNone.Size = new System.Drawing.Size(80, 40); + this.btnTurnNone.TabIndex = 4; + this.btnTurnNone.Text = "None"; + this.btnTurnNone.UseVisualStyleBackColor = false; + // + // btnTurnL90 + // + this.btnTurnL90.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(60)))), ((int)(((byte)(60))))); + this.btnTurnL90.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnTurnL90.ForeColor = System.Drawing.Color.White; + this.btnTurnL90.Location = new System.Drawing.Point(807, 7); + this.btnTurnL90.Name = "btnTurnL90"; + this.btnTurnL90.Size = new System.Drawing.Size(80, 40); + this.btnTurnL90.TabIndex = 5; + this.btnTurnL90.Text = "L90"; + this.btnTurnL90.UseVisualStyleBackColor = false; + // + // label1 + // + this.label1.Dock = System.Windows.Forms.DockStyle.Fill; + this.label1.Font = new System.Drawing.Font("맑은 고딕", 12F, System.Drawing.FontStyle.Bold); + this.label1.Location = new System.Drawing.Point(3, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(1258, 40); + this.label1.TabIndex = 2; + this.label1.Text = "현재 위치에 해당하는 노드를 선택하세요"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // fSetCurrentPosition + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1264, 761); + this.Controls.Add(this.mainLayout); + this.Name = "fSetCurrentPosition"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "AGV 시작 위치 및 방향 설정"; + this.mainLayout.ResumeLayout(false); + this.controlPanel.ResumeLayout(false); + this.controlPanel.PerformLayout(); + this.ResumeLayout(false); + + } + + + private System.Windows.Forms.TableLayoutPanel mainLayout; + private System.Windows.Forms.Panel controlPanel; + private System.Windows.Forms.Label _lblStatus; + private System.Windows.Forms.Button btnForward; + private System.Windows.Forms.Button btnBackward; + private System.Windows.Forms.Label lblTurn; + private System.Windows.Forms.Button btnTurnNone; + private System.Windows.Forms.Button btnTurnL90; + private System.Windows.Forms.Button btnTurnR90; + private System.Windows.Forms.Button btnOK; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Label label1; + } +} \ No newline at end of file diff --git a/HMI/Project/Dialog/fSetCurrentPosition.cs b/HMI/Project/Dialog/fSetCurrentPosition.cs new file mode 100644 index 0000000..9dc5a78 --- /dev/null +++ b/HMI/Project/Dialog/fSetCurrentPosition.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using AGVNavigationCore.Models; +using AGVNavigationCore.Controls; + +namespace Project.Dialog +{ + public partial class fSetCurrentPosition : Form + { + private UnifiedAGVCanvas _canvas; + private MapNode _selectedNode; + private AgvDirection _selectedDirection = AgvDirection.Forward; + private AGVTurn _selectedTurn = AGVTurn.None; + + public MapNode SelectedNode => _selectedNode; + public AgvDirection SelectedDirection => _selectedDirection; + public AGVTurn SelectedTurn => _selectedTurn; + + public fSetCurrentPosition() + { + InitializeComponent(); + if (PUB.setting.FullScreen) this.WindowState = FormWindowState.Maximized; + SetupCanvas(); + WireEvents(); + this.Load += FSetCurrentPosition_Load; + + } + + private void SetupCanvas() + { + _canvas = new UnifiedAGVCanvas + { + Dock = DockStyle.Fill, + Mode = UnifiedAGVCanvas.CanvasMode.Run, + BackColor = Color.FromArgb(45, 45, 48), + ShowGrid=false, + }; + mainLayout.Controls.Add(_canvas, 1, 0); + } + + private void WireEvents() + { + _canvas.NodeSelect += _canvas_NodeSelect; + btnForward.Click += (s, e) => { _selectedDirection = AgvDirection.Forward; UpdateStatus(); }; + btnBackward.Click += (s, e) => { _selectedDirection = AgvDirection.Backward; UpdateStatus(); }; + btnTurnNone.Click += (s, e) => { _selectedTurn = AGVTurn.None; UpdateStatus(); }; + btnTurnL90.Click += (s, e) => { _selectedTurn = AGVTurn.L90; UpdateStatus(); }; + btnTurnR90.Click += (s, e) => { _selectedTurn = AGVTurn.R90; UpdateStatus(); }; + btnOK.Click += BtnOK_Click; + btnCancel.Click += (s, e) => { this.DialogResult = DialogResult.Cancel; this.Close(); }; + } + + private void FSetCurrentPosition_Load(object sender, EventArgs e) + { + // 맵 데이터 강제 복사 (PUB과 동일한 맵 사용) + if (PUB._mapCanvas != null) + { + _canvas.SetMapData(PUB._mapCanvas.Nodes, PUB._mapCanvas.Labels, PUB._mapCanvas.Images, PUB._mapCanvas.Marks, PUB._mapCanvas.Magnets); + } + + // 마지막 위치 로드 + var lastPos = PUB.LoadLastPosition(); + if (lastPos != null) + { + _selectedNode = _canvas.Nodes.FirstOrDefault(n => n.Id == lastPos.NodeId); + _selectedDirection = lastPos.Direction; + _selectedTurn = lastPos.Turn; + + if (_selectedNode != null) + { + _canvas.SelectedNode = _selectedNode; + _canvas.PanToNode(_selectedNode.Id); + UpdateStatus(isRestore: true); + } + } + this.Show(); + _canvas.ResetZoom(); + } + + private void _canvas_NodeSelect(object sender, NodeBase node, MouseEventArgs e) + { + if (node is MapNode mapNode && mapNode.IsNavigationNode()) + { + _selectedNode = mapNode; + _canvas.SelectedNode = mapNode; + UpdateStatus(); + } + } + + private void UpdateStatus(bool isRestore = false) + { + if (_selectedNode == null) + { + _lblStatus.Text = "위치를 선택해 주세요."; + _lblStatus.ForeColor = Color.White; + return; + } + + string dirStr = _selectedDirection == AgvDirection.Forward ? "전진" : "후진"; + _lblStatus.Text = $"{(isRestore ? "[복원됨] " : "")}위치: {_selectedNode.Id} ({_selectedNode.ID2}) | 방향: {dirStr} | Turn: {_selectedTurn}"; + _lblStatus.ForeColor = isRestore ? Color.Yellow : Color.Cyan; + + // 캔버스에 가상 AGV 미리보기 (선택 사항) + _canvas.Invalidate(); + } + + private void BtnOK_Click(object sender, EventArgs e) + { + if (_selectedNode == null) + { + MessageBox.Show("AGV의 현재 위치를 맵에서 클릭하여 선택해 주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + this.DialogResult = DialogResult.OK; + this.Close(); + } + + + } +} diff --git a/HMI/Project/Dialog/fSetCurrentPosition.resx b/HMI/Project/Dialog/fSetCurrentPosition.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/HMI/Project/Dialog/fSetCurrentPosition.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/HMI/Project/PUB.cs b/HMI/Project/PUB.cs index 4b79f4d..4ce1c3b 100644 --- a/HMI/Project/PUB.cs +++ b/HMI/Project/PUB.cs @@ -8,6 +8,8 @@ using System.Media; using System.Runtime.InteropServices; using System.Windows.Forms; using System; +using System.IO; +using Newtonsoft.Json; #if SPEECH using Microsoft.Speech.Synthesis; @@ -23,6 +25,14 @@ using System.Drawing; namespace Project { + public class LastPositionData + { + public string NodeId { get; set; } + public AgvDirection Direction { get; set; } + public AGVTurn Turn { get; set; } + public DateTime SaveTime { get; set; } + } + public static class PUB { //public static Device.CFlag flag; @@ -633,72 +643,8 @@ namespace Project if (rfidValue.isEmpty()) return null; return _mapNodes.Where(t => t.RfidId.Equals(rfidValue)).FirstOrDefault(); } - public static List FindByNodeAlias(string alias) - { - var _mapNodes = PUB._mapCanvas.Nodes; - if (_mapNodes == null || _mapNodes.Any() == false) return null; - if (alias.isEmpty()) return null; - var lst = _mapNodes.Where(t => t.AliasName.Equals(alias)); - if (lst.Any() == false) return null; - return lst.ToList(); - } - public static List FindByNodeType(AGVNavigationCore.Models.MapNode type) - { - var _mapNodes = PUB._mapCanvas.Nodes; - if (_mapNodes == null || _mapNodes.Any() == false) return null; - var lst = _mapNodes.Where(t => t.Type.Equals(type)); - if (lst.Any() == false) return null; - return lst.ToList(); - } - /// - /// RFID 읽기 시 해당 노드 위치로 AGV 업데이트 - /// - /// 읽은 RFID ID - /// 모터 방향 (Forward/Backward) - /// 업데이트 성공 여부 - public static bool UpdateAGVFromRFID(ushort rfidId, AgvDirection motorDirection = AgvDirection.Forward) - { - var _mapNodes = PUB._mapCanvas.Nodes; - if (_virtualAGV == null || _mapNodes == null) return false; + - // RFID에 해당하는 노드 찾기 - var node = _mapNodes.FirstOrDefault(n => n.RfidId == rfidId); - if (node != null) - { - _virtualAGV.SetPosition(node, motorDirection); - RefreshAGVCanvas(); - - log.Add($"[AGV] RFID {rfidId} 감지 → 노드 {node.Id} 위치 업데이트 (방향: {motorDirection})"); - return true; - } - - log.Add($"[AGV] RFID {rfidId}에 해당하는 노드를 찾을 수 없음"); - return false; - } - - /// - /// 노드ID로 AGV 위치 업데이트 - /// - /// 노드 ID - /// 모터 방향 (Forward/Backward) - /// 업데이트 성공 여부 - public static bool UpdateAGVToNode(string nodeId, AgvDirection motorDirection = AgvDirection.Forward) - { - var _mapNodes = PUB._mapCanvas.Nodes; - if (_virtualAGV == null || _mapNodes == null) return false; - - var node = _mapNodes.FirstOrDefault(n => n.Id == nodeId); - if (node != null) - { - _virtualAGV.SetPosition(node, motorDirection); - RefreshAGVCanvas(); - - log.Add($"[AGV] 노드 {nodeId} 위치로 이동 (방향: {motorDirection})"); - return true; - } - - return false; - } /// /// 모든 로그를 플러시 합니다. @@ -718,10 +664,14 @@ namespace Project /// 새로운 방향 public static void UpdateAGVDirection(AgvDirection direction) { - if (_virtualAGV == null) return; - _virtualAGV.CurrentDirection = direction; - RefreshAGVCanvas(); + if (_virtualAGV == null) return; + if(_virtualAGV.CurrentDirection != direction) + { + PUB.log.Add($"[PUB] AGV Direction Change {_virtualAGV.CurrentDirection}->{direction}"); + _virtualAGV.CurrentDirection = direction; + RefreshAGVCanvas(); + } } /// @@ -732,8 +682,13 @@ namespace Project { if (_virtualAGV == null) return; - _virtualAGV.CurrentState = state; - RefreshAGVCanvas(); + if(_virtualAGV.CurrentState != state) + { + PUB.log.Add($"[PUB] AGV State Change {_virtualAGV.CurrentState}->{state}"); + _virtualAGV.CurrentState = state; + RefreshAGVCanvas(); + } + } /// @@ -760,6 +715,51 @@ namespace Project } + #region "Starting Position Persistence" + + private static string LastPosFilePath => Path.Combine(UTIL.CurrentPath, "Data", "last_pos.json"); + + public static void SaveLastPosition() + { + if (_virtualAGV == null || _virtualAGV.CurrentNode == null) return; + + try + { + var data = new LastPositionData + { + NodeId = _virtualAGV.CurrentNode.Id, + Direction = _virtualAGV.CurrentDirection, + Turn = _virtualAGV.Turn, + SaveTime = DateTime.Now + }; + + string json = JsonConvert.SerializeObject(data, Formatting.Indented); + File.WriteAllText(LastPosFilePath, json); + } + catch (Exception ex) + { + log.AddE($"[PUB] Failed to save last position: {ex.Message}"); + } + } + + public static LastPositionData LoadLastPosition() + { + try + { + if (!File.Exists(LastPosFilePath)) return null; + + string json = File.ReadAllText(LastPosFilePath); + return JsonConvert.DeserializeObject(json); + } + catch (Exception ex) + { + log.AddE($"[PUB] Failed to load last position: {ex.Message}"); + return null; + } + } + + #endregion + #endregion } diff --git a/HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_IN.cs b/HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_IN.cs index 55e9274..fec8238 100644 --- a/HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_IN.cs +++ b/HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_IN.cs @@ -39,6 +39,21 @@ namespace Project return false; } else if (PUB.sm.RunStepSeq == idx++) + { + //카트감지 + if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOnEnter) + { + //가지러 들어가야하는데 이미 카트가 감지되어있다면 진행할 수 없다. + if (PUB.AGV.signal2.cart_detect1 || PUB.AGV.signal2.cart_detect2) + { + SetRunStepError(ENIGProtocol.AGVErrorCode.CART_EXIST, $"[{funcname}-{PUB.sm.RunStepSeq}] 이미 카트가 감지되어 진입할 수 없습니다"); + return false; + } + } + PUB.sm.UpdateRunStepSeq(); + return false; + } + else if (PUB.sm.RunStepSeq == idx++) { //모션 전후진 제어 if (PUB._virtualAGV.Turn != AGVNavigationCore.Models.AGVTurn.L90) @@ -69,27 +84,27 @@ namespace Project else { //하드웨어 상태 확인 - var turnState = PUB.AGV.TurnInformation?.State ?? arDev.eNarumiTurn.None; + //var turnState = PUB.AGV.TurnInformation?.StateNew ?? arDev.eNarumiTurn.None; - if (turnState == arDev.eNarumiTurn.Left || turnState == arDev.eNarumiTurn.LeftIng) - { - //이미 좌회전 중이거나 완료된 하드웨어 상태 - PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] 하드웨어 좌회전 상태 확인됨({turnState}). 명령을 건너뜁니다."); - PUB.sm.UpdateRunStepSeq(); - } - else if (turnState == arDev.eNarumiTurn.Right || turnState == arDev.eNarumiTurn.RightIng) - { - //비정상 상태 (우회전 중?) - SetRunStepError(ENIGProtocol.AGVErrorCode.TURN_FAIL, $"[{funcname}-{PUB.sm.RunStepSeq}] 턴 방향 불일치(Current:{turnState}). 우회전 상태에서 좌회전을 시도할 수 없습니다."); - } - else - { - //정상 (None) -> 턴 명령 실행 - PUB.AGV.AGVMoveLeft180Turn(); - PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] AGV Left Turn"); - VAR.TIME.Update(eVarTime.LastTurnCommandTime); - PUB.sm.UpdateRunStepSeq(); - } + //if (turnState == arDev.eNarumiTurn.Left || turnState == arDev.eNarumiTurn.LeftIng) + //{ + // //이미 좌회전 중이거나 완료된 하드웨어 상태 + // PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] 하드웨어 좌회전 상태 확인됨({turnState}). 명령을 건너뜁니다."); + // PUB.sm.UpdateRunStepSeq(); + //} + //else if (turnState == arDev.eNarumiTurn.Right || turnState == arDev.eNarumiTurn.RightIng) + //{ + // //비정상 상태 (우회전 중?) + // SetRunStepError(ENIGProtocol.AGVErrorCode.TURN_FAIL, $"[{funcname}-{PUB.sm.RunStepSeq}] 턴 방향 불일치(Current:{turnState}). 우회전 상태에서 좌회전을 시도할 수 없습니다."); + //} + //else + //{ + //정상 (None) -> 턴 명령 실행 + PUB.AGV.AGVMoveLeft180Turn(); + PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] AGV Left Turn"); + VAR.TIME.Update(eVarTime.LastTurnCommandTime); + PUB.sm.UpdateRunStepSeq(); + //} } PUB._mapCanvas.SetAlertMessage($"턴 진행 중"); return false; @@ -99,29 +114,31 @@ namespace Project //이미 완료된 상태라면 대기 과정을 건너뛴다. if (PUB._virtualAGV.Turn == AGVNavigationCore.Models.AGVTurn.L90) { - PUB.sm.UpdateRunStepSeq(); - return false; + PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] 이미 턴이완료된 상태입니다"); } - - //왼쪽턴이 완료되지 않은경우 - if (PUB.AGV.TurnInformation.State != arDev.eNarumiTurn.Left) + else { - //움직임 확인을 위해 3초간은 검증을 유예한다 - if (PUB.AGV.TurnInformation.Runtime.TotalSeconds < 3) return false; - - //턴 이동 상태가 확인되어야 한다. - var overtime = 30; - if (PUB.AGV.TurnInformation.Runtime.TotalSeconds > overtime) + //왼쪽턴이 완료되지 않은경우 + if (PUB.AGV.TurnInformation.State != arDev.eNarumiTurn.Left) { - //30초동안 AGV까 움직이지 않았다면 오류 처리한다. - SetRunStepError(ENIGProtocol.AGVErrorCode.TURN_FAIL, $"[{funcname}] {overtime}초이내 턴 감지 안됨"); + //움직임 확인을 위해 3초간은 검증을 유예한다 + if (PUB.AGV.TurnInformation.Runtime.TotalSeconds < 3) return false; + + //턴 이동 상태가 확인되어야 한다. + var overtime = 30; + if (PUB.AGV.TurnInformation.Runtime.TotalSeconds > overtime) + { + //30초동안 AGV까 움직이지 않았다면 오류 처리한다. + SetRunStepError(ENIGProtocol.AGVErrorCode.TURN_FAIL, $"[{funcname}] {overtime}초이내 턴 감지 안됨"); + return false; + } + else PUB._mapCanvas.SetAlertMessage($"턴 진행 중({PUB.AGV.TurnInformation.Runtime.TotalSeconds:N0}/{overtime})"); return false; } - else PUB._mapCanvas.SetAlertMessage($"턴 진행 중({PUB.AGV.TurnInformation.Runtime.TotalSeconds:N0}/{overtime})"); - return false; + PUB._virtualAGV.Turn = AGVNavigationCore.Models.AGVTurn.L90; //턴완료 + PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] Turn(left) 완료"); } - PUB._virtualAGV.Turn = AGVNavigationCore.Models.AGVTurn.L90; //턴완료 - PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] Turn(left) 완료"); + PUB.sm.UpdateRunStepSeq(); //이미완료된상태이므로 다음으로 진행한다. return false; } @@ -174,7 +191,7 @@ namespace Project else if (PUB.sm.RunStepSeq == idx++) { //저속이동 (후진 진입) - + // [Smart Restart] 재시작 시 안전 검사 // 이미 턴을 완료했고(L90), 현재 마크 센서가 감지된다면(ON), // 이미 목적지(Mark 2)에 도착한 것으로 간주하여 후진을 생략한다. @@ -203,7 +220,13 @@ namespace Project return false; } - //후진이동을한다 + //후진이동을한다 + PUB.sm.UpdateRunStepSeq(); + return false; + } + else if (PUB.sm.RunStepSeq == idx++) + { + //이동확인을 한다. PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] 도킹을 위해 후진 이동 시작 (Dir:Backward, Spd:Low)"); PUB._mapCanvas.SetAlertMessage($"도킹을 위해 후진 이동 시작"); PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward); @@ -213,9 +236,9 @@ namespace Project else if (PUB.sm.RunStepSeq == idx++) { //이동확인을 한다. - if(PUB.AGV.system1.agv_run == false) + if (PUB.AGV.system1.agv_run == false) { - if(seqtime.TotalSeconds > 3) + if (seqtime.TotalSeconds > 10) { SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_RUN_FAIL); } diff --git a/HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_OUT.cs b/HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_OUT.cs index 5514f03..a94892e 100644 --- a/HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_OUT.cs +++ b/HMI/Project/StateMachine/Step/_SM_RUN_BUFFER_OUT.cs @@ -80,69 +80,85 @@ namespace Project } else if (PUB.sm.RunStepSeq == idx++) { - //빈 상태로 아웃해야한다. - var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData + if (PUB.AGV.signal1.mark_sensor == false) { - 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) + //빈 상태로 아웃해야한다. + var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData { - SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_SPEED_SET_FAIL); + 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) + { + SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_SPEED_SET_FAIL); + } + return false; } - return false; + + PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] AGV 이동 설정 완료 (Dir:Forward, Spd:Low)"); } - PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] AGV 이동 설정 완료 (Dir:Forward, Spd:Low)"); PUB.sm.UpdateRunStepSeq(); return false; } else if (PUB.sm.RunStepSeq == idx++) { //전진이동 - PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] AGV 전진 구동 시작"); - PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward); + if (PUB.AGV.signal1.mark_sensor == false) + { + PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] AGV 전진 구동 시작"); + PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward); + } + PUB.sm.UpdateRunStepSeq(); return false; } else if (PUB.sm.RunStepSeq == idx++) { //AGV구동을 확인하고 마크스탑을 설정한다. - if (PUB.AGV.system1.agv_run == false) + if (PUB.AGV.signal1.mark_sensor == false) { - if (seqtime.TotalSeconds > 3) + if (PUB.AGV.system1.agv_run == false) { - //구동이확인되지 않으면 오류처리를 한다. - SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_RUN_FAIL); + if (seqtime.TotalSeconds > 3) + { + //구동이확인되지 않으면 오류처리를 한다. + SetRunStepError(ENIGProtocol.AGVErrorCode.AGV_RUN_FAIL); + } + return false; } - return false; - } - PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] AGV 구동 확인 완료"); - //마크스탑설정 - PUB.AGV.AGVMoveStop(funcname, arDev.Narumi.eStopOpt.MarkStop); - PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] MarkStop 명령 전송 (진출 정지 장소)"); + PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] AGV 구동 확인 완료"); + //마크스탑설정 + PUB.AGV.AGVMoveStop(funcname, arDev.Narumi.eStopOpt.MarkStop); + PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] MarkStop 명령 전송 (진출 정지 장소)"); + } PUB.sm.UpdateRunStepSeq(); return false; } else if (PUB.sm.RunStepSeq == idx++) { - //마크스탑신호가 3초이내로 들어와야 한다 - if (PUB.AGV.data.Speed != 'S') + if (PUB.AGV.signal1.mark_sensor == false) { - if (seqtime.TotalSeconds > 3) + //마크스탑신호가 3초이내로 들어와야 한다 + if (PUB.AGV.data.Speed != 'S') { - SetRunStepError(ENIGProtocol.AGVErrorCode.MARK_TIMEOUT); + if (seqtime.TotalSeconds > 3) + { + SetRunStepError(ENIGProtocol.AGVErrorCode.MARK_TIMEOUT); + return false; + } return false; } - return false; + PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] MarkStop 신구 확인 완료"); } - PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] MarkStop 신구 확인 완료"); + else PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}]마크스탑이 미리 들어와있어. 이동및 스탑을 하지 않습니다"); + PUB.sm.UpdateRunStepSeq(); return false; } @@ -173,18 +189,18 @@ namespace Project else { //하드웨어 상태 확인 - var turnState = PUB.AGV.TurnInformation?.State ?? arDev.eNarumiTurn.None; - if (turnState == arDev.eNarumiTurn.Right) - { - SetRunStepError(ENIGProtocol.AGVErrorCode.TURN_FAIL, $"[{funcname}-{PUB.sm.RunStepSeq}] 턴 방향 불일치(Current:{turnState}). 버퍼진출시에는 LEFT-TURN 상태여야 합니다"); - return false; - } - else - { - //정상 (None) -> 턴 명령 실행 - PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] AGV Right Turn 명령 전송"); - PUB.AGV.AGVMoveRight180Turn(); - } + //var turnState = PUB.AGV.TurnInformation?.StateOld ?? arDev.eNarumiTurn.None; + //if (turnState == arDev.eNarumiTurn.Right) + //{ + // SetRunStepError(ENIGProtocol.AGVErrorCode.TURN_FAIL, $"[{funcname}-{PUB.sm.RunStepSeq}] 턴 방향 불일치(Current:{turnState}). 버퍼진출시에는 LEFT-TURN 상태여야 합니다"); + // return false; + //} + //else + //{ + //정상 (None) -> 턴 명령 실행 + PUB.log.Add($"[{funcname}-{PUB.sm.RunStepSeq}] AGV Right Turn 명령 전송"); + PUB.AGV.AGVMoveRight180Turn(); + //} } PUB.sm.UpdateRunStepSeq(); return false; diff --git a/HMI/Project/StateMachine/Step/_Util.cs b/HMI/Project/StateMachine/Step/_Util.cs index b756173..ccc9056 100644 --- a/HMI/Project/StateMachine/Step/_Util.cs +++ b/HMI/Project/StateMachine/Step/_Util.cs @@ -80,6 +80,8 @@ namespace Project { errmsg = ENIGProtocol.AGVUtility.GetAGVErrorMessage(ecode); } + PUB.Result.ResultMessage = errmsg; + PUB.XBE.SendError(ecode); PUB.AGV.AGVMoveStop(errmsg); PUB.log.AddE(errmsg); diff --git a/HMI/Project/StateMachine/_AGV.cs b/HMI/Project/StateMachine/_AGV.cs index e5669c5..acde730 100644 --- a/HMI/Project/StateMachine/_AGV.cs +++ b/HMI/Project/StateMachine/_AGV.cs @@ -61,12 +61,8 @@ namespace Project var syncDir = PUB.AGV.data.Direction == 'B' ? AgvDirection.Backward : AgvDirection.Forward; // [Sync] Update VirtualAGV Direction - if (PUB._virtualAGV != null) - { - if (PUB._virtualAGV.CurrentDirection != syncDir) - PUB.UpdateAGVDirection(syncDir); - } - + if (PUB._virtualAGV != null && PUB._virtualAGV.CurrentDirection != syncDir) + PUB.UpdateAGVDirection(syncDir); // [Sync] Update VirtualAGV State AGVState syncState = AGVState.Idle; @@ -107,7 +103,7 @@ namespace Project VAR.BOOL[eVarBool.FLAG_CHARGEONA] = agv_chg; } - + //충전기위치오류 if (PUB.AGV.error.Charger_pos_error != VAR.BOOL[eVarBool.CHG_POSERR]) { if (PUB.AGV.error.Charger_pos_error) @@ -117,15 +113,6 @@ namespace Project VAR.BOOL[eVarBool.CHG_POSERR] = PUB.AGV.error.Charger_pos_error; } - ////나르미가 멈췄다면 다음 마크 이동 기능이 OFF 된다 - //if (agv_stp) - //{ - // if (VAR.BOOL[eVarBool.NEXTSTOP_MARK]) - // { - // VAR.BOOL[eVarBool.NEXTSTOP_MARK] = false; - // PUB.logagv.Add($"NEXTSTOP_MARK 변경({VAR.BOOL[eVarBool.NEXTSTOP_MARK]})"); - // } - //} //마크센서 상태가 변경이 되었다면 if (VAR.BOOL[eVarBool.MARK_SENSOR] != PUB.AGV.signal1.mark_sensor) @@ -155,9 +142,9 @@ namespace Project case arDev.Narumi.DataType.TAG: { //자동 실행 중이다. - PUB.Result.LastTAG = PUB.AGV.data.TagNo;//.ToString("0000"); PUB.log.Add($"[_AGV] AGV 태그수신 : {PUB.AGV.data.TagNo} LastTag:{PUB.Result.LastTAG}"); + //POT/NOT 보면 일단 바로 멈추게한다 if (PUB.Result.CurrentPos == ePosition.POT || PUB.Result.CurrentPos == ePosition.NOT) { @@ -197,8 +184,7 @@ namespace Project var MotDireciton = PUB.AGV.data.Direction == 'B' ? AgvDirection.Backward : AgvDirection.Forward; PUB._mapCanvas.SetAGVPosition(PUB.setting.MCID, CurrentNode, MotDireciton); PUB._virtualAGV.SetPosition(CurrentNode, MotDireciton); - - //방향을 다시 확인하여. 기존 경로의 무결성을 검증한다, 필요한 경우 다시 계산할 필요가 있다. + PUB.SaveLastPosition(); } //태그를 읽었다면 상태를 바로 전송한다 @@ -264,7 +250,6 @@ namespace Project $"S/W 턴 상태: {PUB._virtualAGV.Turn}\n" + $"H/W 턴 상태: {PUB.AGV.TurnInformation?.State}\n" + $"위치 확정: {PUB._virtualAGV.IsPositionConfirmed} (RFID {PUB._virtualAGV.DetectedRfidCount}개)\n" + - $"대상 노드: {targetpos}\n" + $"상세 경로: {pathdetail}"; diff --git a/HMI/Project/StateMachine/_Loop.cs b/HMI/Project/StateMachine/_Loop.cs index 6bbc4e2..86304d6 100644 --- a/HMI/Project/StateMachine/_Loop.cs +++ b/HMI/Project/StateMachine/_Loop.cs @@ -206,7 +206,6 @@ namespace Project if (PUB._mapCanvas != null) PUB._mapCanvas.SetSyncStatus("설정 동기화", 0f, "환경설정 값으로 AGV컨트롤러를 설정 합니다"); })); - } if (_SM_RUN_SYNC(runStepisFirst, PUB.sm.GetRunSteptime)) { diff --git a/HMI/Project/StateMachine/_Xbee.cs b/HMI/Project/StateMachine/_Xbee.cs index 72658ff..b2faf04 100644 --- a/HMI/Project/StateMachine/_Xbee.cs +++ b/HMI/Project/StateMachine/_Xbee.cs @@ -39,9 +39,17 @@ namespace Project //대상디바이스 var TargetID = data[0]; - //해당 패킷의 대상이 나라면 처리한다. + //해당 패킷의 대상이 내가아니면 처리하지 않는다 if (PUB.setting.XBE_ID != TargetID) return; + + //자동실행모드가 아니라면 무조건 에러를 반환한다 + if (VAR.BOOL[eVarBool.FLAG_AUTORUN] == false) + { + SetRunStepError(ENIGProtocol.AGVErrorCode.ManualMode, "현재 자동실행 모드가 아닙니다"); + return; + } + switch (cmd) { @@ -56,8 +64,8 @@ namespace Project var node = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId == currtagValue); if (node == null) { - PUB.log.AddE($"[{logPrefix}-SetCurrent] 노드정보를 찾을 수 없습니다 RFID:{currTag}"); - PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, $"{currTag}"); + var ermsg = ($"[{logPrefix}-SetCurrent] 노드정보를 찾을 수 없습니다 RFID:{currTag}"); + SetRunStepError(ENIGProtocol.AGVErrorCode.EmptyNode, ermsg); return; } else @@ -83,16 +91,16 @@ namespace Project var currNode = PUB._virtualAGV.CurrentNode; if (currNode == null) { - PUB.log.AddE($"[{logPrefix}-{cmd}] 현재 노드를 알 수 없습니다"); - PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, "Unknown Current Node"); + var msg = $"[{logPrefix}-{cmd}] 현재 노드를 알 수 없습니다"; + SetRunStepError(ENIGProtocol.AGVErrorCode.EmptyNode, msg); return; } var targetNode = PUB._virtualAGV.TargetNode; if (targetNode == null) { - PUB.log.AddE($"[{logPrefix}-{cmd}] 목표 노드를 알 수 없습니다"); - PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, "Unknown Target Node"); + var msg = $"[{logPrefix}-{cmd}] 목표 노드를 알 수 없습니다"; + SetRunStepError(ENIGProtocol.AGVErrorCode.EmptyNode, msg); return; } @@ -119,7 +127,7 @@ namespace Project PUB.sm.SetNewRunStep(nextStep); } break; - case ENIGProtocol.AGVCommandHE.PickOnExit: + case ENIGProtocol.AGVCommandHE.PickOnExit: case ENIGProtocol.AGVCommandHE.PickOffExit: { Resultclear(); @@ -129,16 +137,16 @@ namespace Project var currNode = PUB._virtualAGV.CurrentNode; if (currNode == null) { - PUB.log.AddE($"[{logPrefix}-{cmd}] 현재 노드를 알 수 없습니다"); - PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, "Unknown Current Node"); + var msg = $"[{logPrefix}-{cmd}] 현재 노드를 알 수 없습니다"; + SetRunStepError(ENIGProtocol.AGVErrorCode.EmptyNode, msg); return; } var targetNode = PUB._virtualAGV.TargetNode; if (targetNode == null) { - PUB.log.AddE($"[{logPrefix}-{cmd}] 목표 노드를 알 수 없습니다"); - PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, "Unknown Target Node"); + var msg = $"[{logPrefix}-{cmd}] 목표 노드를 알 수 없습니다"; + SetRunStepError(ENIGProtocol.AGVErrorCode.EmptyNode, msg); return; } @@ -198,8 +206,7 @@ namespace Project //자동상태가아니라면 처리하지 않는다. if (VAR.BOOL[eVarBool.FLAG_AUTORUN] == false) { - PUB.log.AddE($"[{logPrefix}-Goto] 자동실행상태가 아닙니다"); - PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.ManualMode, $"{currTag}"); + SetRunStepError(ENIGProtocol.AGVErrorCode.ManualMode, $"[{logPrefix}-Goto] 자동실행상태가 아닙니다"); return; } @@ -207,8 +214,7 @@ namespace Project PUB._virtualAGV.TargetNode = targetNode; if (targetNode == null) { - PUB.log.AddE($"[{logPrefix}-Goto] 노드정보를 찾을 수 없습니다 RFID:{currTag}"); - PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, $"{currTag}"); + SetRunStepError(ENIGProtocol.AGVErrorCode.EmptyNode, $"[{logPrefix}-Goto] 노드정보를 찾을 수 없습니다 RFID:{currTag}"); return; } @@ -348,8 +354,7 @@ namespace Project break; default: - PUB.logagv.AddE($"Unknown Command : {cmd} Sender:{e.ReceivedPacket.ID}, Target:{data[0]}"); - PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.UnknownCommand, $"{cmd}"); + SetRunStepError(ENIGProtocol.AGVErrorCode.UnknownCommand, $"Unknown Command : {cmd} Sender:{e.ReceivedPacket.ID}, Target:{data[0]}"); break; } } diff --git a/HMI/Project/fMain.cs b/HMI/Project/fMain.cs index bff14eb..bed37db 100644 --- a/HMI/Project/fMain.cs +++ b/HMI/Project/fMain.cs @@ -289,21 +289,17 @@ namespace Project PUB._mapCanvas.MapFileName = filePath.FullName; // 🔥 가상 AGV 초기화 (첫 노드 위치에 생성) - if (PUB._virtualAGV == null && PUB._mapCanvas.Nodes.Count > 0) + if (PUB._virtualAGV == null) { - var startNode = PUB._mapCanvas.Nodes.FirstOrDefault(n => n.IsNavigationNode()); - if (startNode != null) - { - PUB._virtualAGV = new VirtualAGV(PUB.setting.MCID, startNode.Position, AgvDirection.Forward); - PUB._virtualAGV.LowBatteryThreshold = PUB.setting.BatteryLimit_Low; - PUB._virtualAGV.SetPosition(startNode, AgvDirection.Forward); + PUB._virtualAGV = new VirtualAGV(PUB.setting.MCID, Point.Empty, AgvDirection.Forward); + PUB._virtualAGV.LowBatteryThreshold = PUB.setting.BatteryLimit_Low; + //PUB._virtualAGV.SetPosition(startNode, AgvDirection.Forward); - // 캔버스에 AGV 리스트 설정 - var agvList = new System.Collections.Generic.List { PUB._virtualAGV }; - PUB._mapCanvas.AGVList = agvList; + // 캔버스에 AGV 리스트 설정 + var agvList = new System.Collections.Generic.List { PUB._virtualAGV }; + PUB._mapCanvas.AGVList = agvList; - PUB.log.Add($"가상 AGV 생성: {startNode.Id} 위치"); - } + PUB.log.Add($"가상 AGV 생성: PointZero 위치"); } else if (PUB._virtualAGV != null) { @@ -313,6 +309,25 @@ namespace Project PUB._mapCanvas.AGVList = agvList; } PUB.log.Add($"맵 파일 로드 완료: {filePath.Name}, 노드 수: {result.Nodes.Count}"); + + // 🔥 초기 위치 설정 및 확인 화면 표시 + this.BeginInvoke(new Action(() => + { + using (var f = new Dialog.fSetCurrentPosition()) + { + if (f.ShowDialog() == DialogResult.OK) + { + if (f.SelectedNode != null) + { + PUB._virtualAGV.Turn = f.SelectedTurn; + PUB._virtualAGV.SetPosition(f.SelectedNode, f.SelectedDirection); + PUB._mapCanvas.SetAGVPosition(PUB.setting.MCID, f.SelectedNode, f.SelectedDirection); + PUB.SaveLastPosition(); + PUB.log.Add($"[초기위치] 설정 완료: {f.SelectedNode.Id}, {f.SelectedDirection}, Turn:{f.SelectedTurn}"); + } + } + } + })); } else { diff --git a/HMI/SubProject/AGV/Command.cs b/HMI/SubProject/AGV/Command.cs index 29b62e9..625ddec 100644 --- a/HMI/SubProject/AGV/Command.cs +++ b/HMI/SubProject/AGV/Command.cs @@ -34,6 +34,7 @@ namespace arDev } } public eNarumiTurn State { get; set; } + //public eNarumiTurn StateNew { get; set; } public NarumiTurnInfo() { Start = new DateTime(1982, 11, 23); diff --git a/HMI/SubProject/AGV/Narumi.cs b/HMI/SubProject/AGV/Narumi.cs index 6a61665..95d048b 100644 --- a/HMI/SubProject/AGV/Narumi.cs +++ b/HMI/SubProject/AGV/Narumi.cs @@ -160,11 +160,19 @@ namespace arDev // [Turn Completion Check] if (frame.DataString.Contains("LEFT-TURN OK")) { - if (TurnInformation != null) { TurnInformation.State = eNarumiTurn.Left; TurnInformation.End = DateTime.Now; } + if (TurnInformation != null) + { + TurnInformation.State = eNarumiTurn.Left; + TurnInformation.End = DateTime.Now; + } } else if (frame.DataString.Contains("RIGHT-TURN OK")) { - if (TurnInformation != null) { TurnInformation.State = eNarumiTurn.Right; TurnInformation.End = DateTime.Now; } + if (TurnInformation != null) + { + TurnInformation.State = eNarumiTurn.Right; + TurnInformation.End = DateTime.Now; + } } // $로 시작되는 AGV 상태 표시 //var text_Sts_Etc = Encoding.Default.GetString(bRcvData, 3, bRcvData.Length - 2).TrimStart(' '); //20210311 김정만 - SmartX FrameWork 사용 안함으로 주석처리 @@ -278,23 +286,23 @@ namespace arDev //agv가 멈춰있고 들어온경우, 턴 작업이었다면 턴 셋팅을 한다 // [REMOVED] Logic replaced by message-based check ($AGV...TURN OK) - if (false && system1.agv_run == false && system1.agv_stop == true && - TurnInformation != null) - { - 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 (false && system1.agv_run == false && system1.agv_stop == true && + // TurnInformation != null) + //{ + // if (TurnInformation.Start.Year > 2000) + // { + // if (TurnInformation.StateNew == eNarumiTurn.LeftIng || TurnInformation.StateNew == 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) {