From 92dfe2978cadd5c3e3efe5430a8d9944d9ed79ad Mon Sep 17 00:00:00 2001 From: backuppc Date: Tue, 18 Nov 2025 08:50:07 +0900 Subject: [PATCH] =?UTF-8?q?UI=20=EB=B0=8F=20=EC=84=A4=EC=A0=95=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=82=AC=ED=95=AD=20=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 맵 에디터 메인폼 UI 개선 - AGV 캔버스 컨트롤 수정 - 설정 파일 업데이트 - 상태머신 AGV 로직 조정 - 자동 모드 화면 개선 - 메인폼 디자이너 및 로직 수정 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../AGVLogic/AGVMapEditor/Forms/MainForm.cs | 4 +- .../Controls/UnifiedAGVCanvas.cs | 2 + Cs_HMI/Project/CSetting.cs | 74 ++++++-- Cs_HMI/Project/StateMachine/_AGV.cs | 14 +- Cs_HMI/Project/ViewForm/fAuto.cs | 22 ++- Cs_HMI/Project/fMain.Designer.cs | 42 ++++- Cs_HMI/Project/fMain.cs | 178 +++++++++++++++++- 7 files changed, 309 insertions(+), 27 deletions(-) diff --git a/Cs_HMI/AGVLogic/AGVMapEditor/Forms/MainForm.cs b/Cs_HMI/AGVLogic/AGVMapEditor/Forms/MainForm.cs index 5c8dc98..99236d3 100644 --- a/Cs_HMI/AGVLogic/AGVMapEditor/Forms/MainForm.cs +++ b/Cs_HMI/AGVLogic/AGVMapEditor/Forms/MainForm.cs @@ -548,9 +548,11 @@ namespace AGVMapEditor.Forms var openFileDialog = new OpenFileDialog { Filter = "AGV Map Files (*.agvmap)|*.agvmap|All Files (*.*)|*.*", - DefaultExt = "agvmap" + DefaultExt = "agvmap", }; + + if (openFileDialog.ShowDialog() == DialogResult.OK) { try diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs index 3ea90f3..f6e25eb 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/Controls/UnifiedAGVCanvas.cs @@ -158,6 +158,8 @@ namespace AGVNavigationCore.Controls #region Properties + public string MapFileName { get; set; } = ""; + /// /// 캔버스 모드 /// diff --git a/Cs_HMI/Project/CSetting.cs b/Cs_HMI/Project/CSetting.cs index 2f785bd..367c99c 100644 --- a/Cs_HMI/Project/CSetting.cs +++ b/Cs_HMI/Project/CSetting.cs @@ -174,45 +174,70 @@ namespace Project #endregion #region "TAGS" + [Browsable(false)] public int TAGPOT { get; set; } + [Browsable(false)] public int TAGNOT { get; set; } + [Browsable(false)] public int TAG_F2_F3 { get; set; } + [Browsable(false)] public int TAG_QC_F1 { get; set; } + [Browsable(false)] public int TAG_F1_F2 { get; set; } + [Browsable(false)] public int TAG_F3_F4 { get; set; } + [Browsable(false)] public int TAG_F4_F5 { get; set; } - + [Browsable(false)] public int TAG_QA_QC { get; set; } + [Browsable(false)] public int TAGQAB { get; set; } + [Browsable(false)] public int TAGQAA { get; set; } - - + [Browsable(false)] public int TAGQCB { get; set; } + [Browsable(false)] public int TAGQCA { get; set; } + [Browsable(false)] public int TAGF1A { get; set; } + [Browsable(false)] public int TAGF2A { get; set; } + [Browsable(false)] public int TAGF3A { get; set; } + [Browsable(false)] public int TAGF4A { get; set; } + [Browsable(false)] public int TAGF5A { get; set; } - + [Browsable(false)] public int TAGF1B { get; set; } + [Browsable(false)] public int TAGF2B { get; set; } + [Browsable(false)] public int TAGF3B { get; set; } + [Browsable(false)] public int TAGF4B { get; set; } + [Browsable(false)] public int TAGF5B { get; set; } #endregion #region "Charge" + [Browsable(false)] public int chargerpos { get; set; } + [Browsable(false)] public int ChargetWaitSec { get; set; } + [Browsable(false)] public int ChargeEmergencyLevel { get; set; } + [Browsable(false)] public int ChargeMaxLevel { get; set; } + [Browsable(false)] public int ChargeStartLevel { get; set; } + [Browsable(false)] public Boolean Enable_AutoCharge { get; set; } - + [Browsable(false)] public int ChargeRetryTerm { get; set; } - //public int ChargeIdleInterval { get; set; } + [Browsable(false)] public int ChargeMaxTime { get; set; } + [Browsable(false)] public int ChargeSearchTime { get; set; } #endregion @@ -228,8 +253,9 @@ namespace Project /// FVI로 가능 방향이 Backward 인가? /// Forward 방향이라면 false 를 한다 /// + [Browsable(false)] public Boolean AGV_Direction_FVI_Backward { get; set; } - + [Browsable(false)] public Boolean Enable_Speak { get; set; } //public Boolean Disable_BMS { get; set; } @@ -245,48 +271,74 @@ namespace Project [Browsable(false)] public string AGV_ADDRESS { get; set; } + [Browsable(false)] public int SCK { get; set; } + [Browsable(false)] public int SSK { get; set; } + [Browsable(false)] public int STT { get; set; } + [Browsable(false)] public int SAD { get; set; } + [Browsable(false)] public byte ZSpeed { get; set; } + [Browsable(false)] public int SPD_L { get; set; } + [Browsable(false)] public int SPD_M { get; set; } + [Browsable(false)] public int SPD_H { get; set; } + [Browsable(false)] public int SPD_DRIVE { get; set; } - + [Browsable(false)] public int SPD_S { get; set; } + [Browsable(false)] public int SPD_R { get; set; } - + [Browsable(false)] public int PID_PH { get; set; } + [Browsable(false)] public int PID_PM { get; set; } + [Browsable(false)] public int PID_PL { get; set; } + [Browsable(false)] public int PID_IH { get; set; } + [Browsable(false)] public int PID_IM { get; set; } + [Browsable(false)] public int PID_IL { get; set; } + [Browsable(false)] public int PID_DH { get; set; } + [Browsable(false)] public int PID_DM { get; set; } + [Browsable(false)] public int PID_DL { get; set; } - + [Browsable(false)] public int PID_PS { get; set; } + [Browsable(false)] public int PID_IS { get; set; } + [Browsable(false)] public int PID_DS { get; set; } public double WheelSpeedCharge { get; set; } public byte HomePositionValue { get; set; } public byte HomeKitNo { get; set; } + + [Browsable(false)] public Single interval_xbe { get; set; } + [Browsable(false)] public int interval_bms { get; set; } public int doorSoundTerm { get; set; } [Browsable(false)] public int musicvol { get; set; } - + [Browsable(false)] public bool Enable_Music { get; set; } #endregion + [Browsable(false)] + public string LastMapFile { get; set; } + [Category("Report"), Browsable(false), Description("상태기록시 장비 식별코드(4자리)"), DisplayName("M/C ID")] diff --git a/Cs_HMI/Project/StateMachine/_AGV.cs b/Cs_HMI/Project/StateMachine/_AGV.cs index 932760e..3cff66a 100644 --- a/Cs_HMI/Project/StateMachine/_AGV.cs +++ b/Cs_HMI/Project/StateMachine/_AGV.cs @@ -8,6 +8,8 @@ using arCtl; using Project.StateMachine; using COMM; using AR; +using AGVNavigationCore.Models; + namespace Project { @@ -215,11 +217,13 @@ namespace Project // CurrentNode에 새로 생성한 노드 할당 CurrentNode = newNode; } - - //모터방향 확인해서 UI와 AGV클래스에 적용한다 - var MotDireciton = PUB.AGV.data.Direction == 'B' ? AGVNavigationCore.Models.AgvDirection.Backward : AGVNavigationCore.Models.AgvDirection.Forward; - PUB._virtualAGV.SetPosition(CurrentNode, MotDireciton); - PUB._mapCanvas.SetAGVPosition(PUB.setting.MCID, CurrentNode, MotDireciton); + else + { + //모터방향 확인해서 UI와 AGV클래스에 적용한다 + var MotDireciton = PUB.AGV.data.Direction == 'B' ? AGVNavigationCore.Models.AgvDirection.Backward : AGVNavigationCore.Models.AgvDirection.Forward; + PUB._virtualAGV.SetPosition(CurrentNode, MotDireciton); + PUB._mapCanvas.SetAGVPosition(PUB.setting.MCID, CurrentNode, MotDireciton); + } //태그를 읽었다면 상태를 바로 전송한다 PUB.XBE.SendStatus(); diff --git a/Cs_HMI/Project/ViewForm/fAuto.cs b/Cs_HMI/Project/ViewForm/fAuto.cs index e430a6a..07e29bd 100644 --- a/Cs_HMI/Project/ViewForm/fAuto.cs +++ b/Cs_HMI/Project/ViewForm/fAuto.cs @@ -43,7 +43,7 @@ namespace Project.ViewForm PUB._mapCanvas = new AGVNavigationCore.Controls.UnifiedAGVCanvas(); PUB._mapCanvas.Dock = DockStyle.Fill; PUB._mapCanvas.ShowGrid = false; - PUB._mapCanvas.BackColor = Color.FromArgb(32,32,32); + PUB._mapCanvas.BackColor = Color.FromArgb(32, 32, 32); PUB._mapCanvas.ForeColor = Color.White; // RfidMappings 제거 - MapNode에 통합됨 @@ -78,13 +78,20 @@ namespace Project.ViewForm //auto load - var path = new System.IO.DirectoryInfo("route"); - if (path.Exists == false) path.Create(); - var files = path.GetFiles("*.route"); + var mapPath = new System.IO.DirectoryInfo("route"); + if (mapPath.Exists == false) mapPath.Create(); //맵파일로딩 - var filePath = new System.IO.FileInfo(@".\route\NewMap.agvmap"); + if (PUB.setting.LastMapFile.isEmpty()) PUB.setting.LastMapFile = System.IO.Path.Combine(mapPath.FullName, "default.agvmap"); + System.IO.FileInfo filePath = new System.IO.FileInfo(PUB.setting.LastMapFile); + if (filePath.Exists == false) filePath = new System.IO.FileInfo(System.IO.Path.Combine(mapPath.FullName,"default.agvmap")); + if(filePath.Exists==false) //그래도없다면 맵폴더에서 파일을 찾아본다. + { + var files = mapPath.GetFiles("*.agvmap"); + if (files.Any()) filePath = files[0]; + } + if (filePath.Exists) { var result = MapLoader.LoadMapFromFile(filePath.FullName); @@ -97,7 +104,8 @@ namespace Project.ViewForm // 맵 캔버스에 데이터 설정 PUB._mapCanvas.Nodes = PUB._mapNodes; - + PUB._mapCanvas.MapFileName = filePath.FullName; + // 🔥 맵 설정 적용 (배경색, 그리드 표시) if (result.Settings != null) { @@ -144,7 +152,7 @@ namespace Project.ViewForm { PUB.log.Add($"맵 파일을 찾을 수 없습니다: {filePath.FullName}"); } - + //var fn = string.Empty; //if (files.Any() == false) //{ diff --git a/Cs_HMI/Project/fMain.Designer.cs b/Cs_HMI/Project/fMain.Designer.cs index 043b077..0bc4f29 100644 --- a/Cs_HMI/Project/fMain.Designer.cs +++ b/Cs_HMI/Project/fMain.Designer.cs @@ -128,6 +128,9 @@ namespace Project this.lbBat = new AGVControl.BatteryLevelGauge(); this.pictureBox1 = new System.Windows.Forms.PictureBox(); this.cmDebug = new System.Windows.Forms.ContextMenuStrip(this.components); + this.mapFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.loadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.demoListLotToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripMenuItem(); this.refreshListToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -140,6 +143,7 @@ namespace Project this.arPanel2 = new arCtl.arPanel(); this.arPanel1 = new arCtl.arPanel(); this.ctlPos1 = new Project.CtlPos(); + this.editorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.panRight.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout(); this.panel4.SuspendLayout(); @@ -1669,6 +1673,7 @@ namespace Project // cmDebug // this.cmDebug.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mapFileToolStripMenuItem, this.systemParameterToolStripMenuItem, this.toolStripMenuItem4, this.demoRunToolStripMenuItem, @@ -1685,7 +1690,31 @@ namespace Project this.toolStripMenuItem5, this.refreshListToolStripMenuItem}); this.cmDebug.Name = "cmVision"; - this.cmDebug.Size = new System.Drawing.Size(229, 302); + this.cmDebug.Size = new System.Drawing.Size(229, 346); + // + // mapFileToolStripMenuItem + // + this.mapFileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.saveToolStripMenuItem, + this.loadToolStripMenuItem, + this.editorToolStripMenuItem}); + this.mapFileToolStripMenuItem.Name = "mapFileToolStripMenuItem"; + this.mapFileToolStripMenuItem.Size = new System.Drawing.Size(228, 22); + this.mapFileToolStripMenuItem.Text = "Map File"; + // + // saveToolStripMenuItem + // + this.saveToolStripMenuItem.Name = "saveToolStripMenuItem"; + this.saveToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.saveToolStripMenuItem.Text = "Save"; + this.saveToolStripMenuItem.Click += new System.EventHandler(this.saveToolStripMenuItem_Click); + // + // loadToolStripMenuItem + // + this.loadToolStripMenuItem.Name = "loadToolStripMenuItem"; + this.loadToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.loadToolStripMenuItem.Text = "Load"; + this.loadToolStripMenuItem.Click += new System.EventHandler(this.loadToolStripMenuItem_Click); // // demoListLotToolStripMenuItem // @@ -2159,6 +2188,13 @@ namespace Project this.ctlPos1.Text = "ctlPos1"; this.ctlPos1.Visible = false; // + // editorToolStripMenuItem + // + this.editorToolStripMenuItem.Name = "editorToolStripMenuItem"; + this.editorToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.editorToolStripMenuItem.Text = "Editor"; + this.editorToolStripMenuItem.Click += new System.EventHandler(this.editorToolStripMenuItem_Click); + // // fMain // this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; @@ -2278,6 +2314,10 @@ namespace Project private arCtl.arLabel arLabel4; private arCtl.arLabel lbCntQC; private arCtl.arLabel arLabel9; + private ToolStripMenuItem mapFileToolStripMenuItem; + private ToolStripMenuItem saveToolStripMenuItem; + private ToolStripMenuItem loadToolStripMenuItem; + private ToolStripMenuItem editorToolStripMenuItem; } } diff --git a/Cs_HMI/Project/fMain.cs b/Cs_HMI/Project/fMain.cs index d586464..89c129c 100644 --- a/Cs_HMI/Project/fMain.cs +++ b/Cs_HMI/Project/fMain.cs @@ -12,6 +12,8 @@ using System.CodeDom; using AR; using Project.StateMachine; using System.Security.Cryptography.X509Certificates; +using AGVNavigationCore.Models; +using System.IO; namespace Project { @@ -243,7 +245,8 @@ namespace Project tmDisplay.Tick += tmDisplay_Tick; tmDisplay.Start(); //start Display - this.btDebug.Visible = PUB.setting.UseDebugMode; + + this.btDebug.Visible = System.Diagnostics.Debugger.IsAttached || PUB.setting.UseDebugMode; PUB.log.Add("Program Start"); @@ -262,7 +265,7 @@ namespace Project } - + #region "Mouse Form Move" @@ -887,5 +890,176 @@ namespace Project } } + private void saveToolStripMenuItem_Click(object sender, EventArgs e) + { + //mapsave + using (var sd = new SaveFileDialog()) + { + sd.Filter = "AGV Map Files (*.agvmap)|*.agvmap|All Files (*.*)|*.*"; + sd.DefaultExt = "agvmap"; + sd.FileName = PUB._mapCanvas.MapFileName; + if (sd.ShowDialog() == DialogResult.OK) + { + SaveMapToFile(sd.FileName); + } + } + } + + private void loadToolStripMenuItem_Click(object sender, EventArgs e) + { + //load + + var od = new OpenFileDialog + { + Filter = "AGV Map Files (*.agvmap)|*.agvmap|All Files (*.*)|*.*", + DefaultExt = "agvmap", + FileName = PUB._mapCanvas.MapFileName, + }; + + if (od.ShowDialog() == DialogResult.OK) + { + try + { + LoadMapFromFile(od.FileName); + } + catch (Exception ex) + { + MessageBox.Show($"맵 로드 중 오류가 발생했습니다: {ex.Message}", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } + + private void LoadMapFromFile(string filePath) + { + var result = MapLoader.LoadMapFromFile(filePath); + + if (result.Success) + { + var _mapCanvas = PUB._mapCanvas; + PUB._mapNodes = result.Nodes; + + // 맵 캔버스에 데이터 설정 + _mapCanvas.Nodes = result.Nodes; + // RfidMappings 제거됨 - MapNode에 통합 + + // 🔥 맵 설정 적용 (배경색, 그리드 표시) + if (result.Settings != null) + { + _mapCanvas.BackColor = System.Drawing.Color.FromArgb(result.Settings.BackgroundColorArgb); + _mapCanvas.ShowGrid = result.Settings.ShowGrid; + } + + // 설정에 마지막 맵 파일 경로 저장 + PUB.setting.LastMapFile = filePath; + PUB.setting.Save(); + + // 맵 로드 후 자동으로 맵에 맞춤 + _mapCanvas.FitToNodes(); + } + else + { + MessageBox.Show($"맵 파일 로딩 실패: {result.ErrorMessage}", "오류", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void SaveMapToFile(string filePath) + { + // 🔥 백업 파일 생성 (기존 파일이 있을 경우) + if (File.Exists(filePath)) + { + try + { + // 날짜시간 포함 백업 파일명 생성 + var directory = Path.GetDirectoryName(filePath); + var fileNameWithoutExt = Path.GetFileNameWithoutExtension(filePath); + var extension = Path.GetExtension(filePath); + var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); + var backupFileName = $"{fileNameWithoutExt}_{timestamp}{extension}.bak"; + var backupFilePath = Path.Combine(directory, backupFileName); + + // 기존 파일을 백업 파일로 복사 + File.Copy(filePath, backupFilePath, true); + } + catch (Exception ex) + { + // 백업 파일 생성 실패 시 경고만 표시하고 계속 진행 + MessageBox.Show($"백업 파일 생성 실패: {ex.Message}\n원본 파일은 계속 저장됩니다.", "백업 경고", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + } + + var _mapCanvas = PUB._mapCanvas; + var _mapNodes = PUB._mapNodes; + + // 🔥 현재 캔버스 설정을 맵 파일에 저장 + var settings = new MapLoader.MapSettings + { + BackgroundColorArgb = _mapCanvas.BackColor.ToArgb(), + ShowGrid = _mapCanvas.ShowGrid + }; + + if (MapLoader.SaveMapToFile(filePath, _mapNodes, settings)) + { + // 설정에 마지막 맵 파일 경로 저장 + PUB.setting.LastMapFile = filePath; + PUB.setting.Save(); + } + else + { + MessageBox.Show("맵 파일 저장 실패", "오류", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void editorToolStripMenuItem_Click(object sender, EventArgs e) + { + try + { + // MapEditor 실행 파일 경로 확인 + string mapEditorPath = "AGVMapEditor.exe"; + + // 경로가 설정되지 않았거나 파일이 없는 경우 사용자에게 선택을 요청 + if (string.IsNullOrEmpty(mapEditorPath) || !File.Exists(mapEditorPath)) + { + using (var openDialog = new OpenFileDialog()) + { + openDialog.Filter = "실행 파일 (*.exe)|*.exe|모든 파일 (*.*)|*.*"; + openDialog.Title = "AGV MapEditor 실행 파일 선택"; + openDialog.InitialDirectory = Application.StartupPath; + + if (openDialog.ShowDialog() == DialogResult.OK) + { + mapEditorPath = openDialog.FileName; + } + else + { + return; // 사용자가 취소함 + } + } + } + + // MapEditor 실행 + var startInfo = new System.Diagnostics.ProcessStartInfo + { + FileName = mapEditorPath, + UseShellExecute = true + }; + + // 현재 로드된 맵 파일이 있으면 파라미터로 전달 + var _currentMapFilePath = PUB._mapCanvas.MapFileName; + if (!string.IsNullOrEmpty(_currentMapFilePath) && File.Exists(_currentMapFilePath)) + { + startInfo.Arguments = $"\"{_currentMapFilePath}\""; + } + + System.Diagnostics.Process.Start(startInfo); + } + catch (Exception ex) + { + MessageBox.Show($"MapEditor를 실행할 수 없습니다:\n{ex.Message}", "오류", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } } }