diff --git a/Cs_HMI/.vscode/launch.json b/Cs_HMI/.vscode/launch.json
new file mode 100644
index 0000000..bddd567
--- /dev/null
+++ b/Cs_HMI/.vscode/launch.json
@@ -0,0 +1,26 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
+ "name": ".NET Core Launch (console)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "../../../Amkor/AGV4/Debug/net8.0-windows/AGV4.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/Project",
+ // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
+ "console": "internalConsole",
+ "stopAtEntry": false
+ },
+ {
+ "name": ".NET Core Attach",
+ "type": "coreclr",
+ "request": "attach"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Cs_HMI/.vscode/tasks.json b/Cs_HMI/.vscode/tasks.json
new file mode 100644
index 0000000..47f8d63
--- /dev/null
+++ b/Cs_HMI/.vscode/tasks.json
@@ -0,0 +1,41 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/AGVCSharp.sln",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary;ForceNoAlign"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "publish",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "publish",
+ "${workspaceFolder}/AGVCSharp.sln",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary;ForceNoAlign"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "watch",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "watch",
+ "run",
+ "--project",
+ "${workspaceFolder}/AGVCSharp.sln"
+ ],
+ "problemMatcher": "$msCompile"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Cs_HMI/AGVCSharp.sln b/Cs_HMI/AGVCSharp.sln
index 60edcdc..eb6340b 100644
--- a/Cs_HMI/AGVCSharp.sln
+++ b/Cs_HMI/AGVCSharp.sln
@@ -42,6 +42,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "arCommUtil", "SubProject\Co
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ENIGProtocol", "..\..\Protocol\enigprotocol\ENIGProtocol.csproj", "{16BD025D-CB0F-4A4B-A452-8FE629F02F0C}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVMapControl", "SubProject\AGVMapControl\AGVMapControl.csproj", "{D37909BE-DCD9-4408-AF06-28F92C69F83D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -231,6 +233,18 @@ Global
{16BD025D-CB0F-4A4B-A452-8FE629F02F0C}.Release|x64.Build.0 = Release|Any CPU
{16BD025D-CB0F-4A4B-A452-8FE629F02F0C}.Release|x86.ActiveCfg = Release|Any CPU
{16BD025D-CB0F-4A4B-A452-8FE629F02F0C}.Release|x86.Build.0 = Release|Any CPU
+ {D37909BE-DCD9-4408-AF06-28F92C69F83D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D37909BE-DCD9-4408-AF06-28F92C69F83D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D37909BE-DCD9-4408-AF06-28F92C69F83D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D37909BE-DCD9-4408-AF06-28F92C69F83D}.Debug|x64.Build.0 = Debug|Any CPU
+ {D37909BE-DCD9-4408-AF06-28F92C69F83D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D37909BE-DCD9-4408-AF06-28F92C69F83D}.Debug|x86.Build.0 = Debug|Any CPU
+ {D37909BE-DCD9-4408-AF06-28F92C69F83D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D37909BE-DCD9-4408-AF06-28F92C69F83D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D37909BE-DCD9-4408-AF06-28F92C69F83D}.Release|x64.ActiveCfg = Release|Any CPU
+ {D37909BE-DCD9-4408-AF06-28F92C69F83D}.Release|x64.Build.0 = Release|Any CPU
+ {D37909BE-DCD9-4408-AF06-28F92C69F83D}.Release|x86.ActiveCfg = Release|Any CPU
+ {D37909BE-DCD9-4408-AF06-28F92C69F83D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -249,6 +263,7 @@ Global
{AAF68D20-4590-4AB0-BB91-E0DD04C91DEC} = {C423C39A-44E7-4F09-B2F7-7943975FF948}
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623} = {C423C39A-44E7-4F09-B2F7-7943975FF948}
{16BD025D-CB0F-4A4B-A452-8FE629F02F0C} = {C423C39A-44E7-4F09-B2F7-7943975FF948}
+ {D37909BE-DCD9-4408-AF06-28F92C69F83D} = {C423C39A-44E7-4F09-B2F7-7943975FF948}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B5B1FD72-356F-4840-83E8-B070AC21C8D9}
diff --git a/Cs_HMI/Project/AGV4.csproj b/Cs_HMI/Project/AGV4.csproj
index 0f2b449..9c0f260 100644
--- a/Cs_HMI/Project/AGV4.csproj
+++ b/Cs_HMI/Project/AGV4.csproj
@@ -8,12 +8,15 @@
enable
x64
D:\Amkor\AGV4
+ true
+ true
+
@@ -41,21 +44,19 @@
SettingsSingleFileGenerator
Settings.Designer.cs
+
+ PreserveNewest
+
-
- lib\Winsock_Orcas.dll
-
-
- lib\EEProtocol.dll
-
-
- lib\arUtil.dll
+
+ ..\DLL\Winsock Orcas.dll
+
diff --git a/Cs_HMI/Project/Class/EEMStatus.cs b/Cs_HMI/Project/Class/EEMStatus.cs
index 7d14c78..61667e0 100644
--- a/Cs_HMI/Project/Class/EEMStatus.cs
+++ b/Cs_HMI/Project/Class/EEMStatus.cs
@@ -108,24 +108,24 @@ public static partial class EEMStatus
StatusChecktime = DateTime.Now;
}
- //내부실행모드일때에만 파일을 처리한다
- if (_extrun == false)
- {
- var tsfile = DateTime.Now - FileCheckTime;
- if (tsfile.TotalSeconds >= UpdateFileInterval)
- {
- if (UpdateRun == false)
- {
- UpdateRun = true;
- Task.Run(() =>
- {
- UpdateFileToDB();
- UpdateRun = false;
- });
- }
- FileCheckTime = DateTime.Now;
- }
- }
+ ////내부실행모드일때에만 파일을 처리한다
+ //if (_extrun == false)
+ //{
+ // var tsfile = DateTime.Now - FileCheckTime;
+ // if (tsfile.TotalSeconds >= UpdateFileInterval)
+ // {
+ // if (UpdateRun == false)
+ // {
+ // UpdateRun = true;
+ // Task.Run(() =>
+ // {
+ // UpdateFileToDB();
+ // UpdateRun = false;
+ // });
+ // }
+ // FileCheckTime = DateTime.Now;
+ // }
+ //}
}
///
@@ -258,7 +258,7 @@ public static partial class EEMStatus
//var di = new System.IO.DirectoryInfo(path);
//var fi = di.GetFiles("*.sql", System.IO.SearchOption.TopDirectoryOnly).Where(t => t.LastWriteTime < DateTime.Now.AddMinutes(-3)).FirstOrDefault();
//if (fi != null) fi.Delete();
- if (state == 4 && extrun == false) UpdateFileToDB();
+ //if (state == 4 && extrun == false) UpdateFileToDB();
return string.Empty;
}
catch (Exception ex)
@@ -269,97 +269,97 @@ public static partial class EEMStatus
- static void UpdateFileToDB()
- {
- if (mre.WaitOne(1000) == false) return;
- mre.Reset();
- var cs = "Data Source=10.131.15.18;Initial Catalog=EE;Persist Security Info=True;User ID=eeuser;Password=Amkor123!";
- var path = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Status");
- var di = new System.IO.DirectoryInfo(path);
- if (di.Exists == false) return;
- var file = di.GetFiles("*.sql", System.IO.SearchOption.TopDirectoryOnly)
- .Where(t => t.LastWriteTime < DateTime.Now.AddSeconds(-3))
- .OrderByDescending(t => t.LastWriteTime).FirstOrDefault();
+ //static void UpdateFileToDB()
+ //{
+ // if (mre.WaitOne(1000) == false) return;
+ // mre.Reset();
+ // var cs = "Data Source=10.131.15.18;Initial Catalog=EE;Persist Security Info=True;User ID=eeuser;Password=Amkor123!";
+ // var path = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Status");
+ // var di = new System.IO.DirectoryInfo(path);
+ // if (di.Exists == false) return;
+ // var file = di.GetFiles("*.sql", System.IO.SearchOption.TopDirectoryOnly)
+ // .Where(t => t.LastWriteTime < DateTime.Now.AddSeconds(-3))
+ // .OrderByDescending(t => t.LastWriteTime).FirstOrDefault();
- if (file == null)
- {
- mre.Set();
- return;
- }
+ // if (file == null)
+ // {
+ // mre.Set();
+ // return;
+ // }
- //파일을 찾아야한다
- // PUB.log.Add($">> {file.FullName}");
+ // //파일을 찾아야한다
+ // // PUB.log.Add($">> {file.FullName}");
- try
- {
- var sql = System.IO.File.ReadAllText(file.FullName, System.Text.Encoding.Default);
- if (string.IsNullOrEmpty(sql))
- {
- //비어잇다면
- var errpath = System.IO.Path.Combine(di.FullName, "Error");
- var errfile = System.IO.Path.Combine(errpath, file.Name);
- if (System.IO.Directory.Exists(errpath) == false) System.IO.Directory.CreateDirectory(errpath);
- System.IO.File.Move(file.FullName, errfile);// file.MoveTo(errfile);
- // ecnt += 1;
- }
- else
- {
- // var csstr = PUB.setting.ConnectionString;
- // if (string.IsNullOrEmpty(csstr)) csstr = "Data Source=10.131.15.18;Initial Catalog=EE;Persist Security Info=True;User ID=eeuser;Password=Amkor123!";
- var cn = new System.Data.SqlClient.SqlConnection(cs);
- var cmd = new System.Data.SqlClient.SqlCommand(sql, cn);
- cn.Open();
- var cnt = cmd.ExecuteNonQuery();
- //if (cnt == 0) PUB.log.Add($"Result Empty : {sql}");
- cn.Close();
- cnt += 1;
+ // try
+ // {
+ // var sql = System.IO.File.ReadAllText(file.FullName, System.Text.Encoding.Default);
+ // if (string.IsNullOrEmpty(sql))
+ // {
+ // //비어잇다면
+ // var errpath = System.IO.Path.Combine(di.FullName, "Error");
+ // var errfile = System.IO.Path.Combine(errpath, file.Name);
+ // if (System.IO.Directory.Exists(errpath) == false) System.IO.Directory.CreateDirectory(errpath);
+ // System.IO.File.Move(file.FullName, errfile);// file.MoveTo(errfile);
+ // // ecnt += 1;
+ // }
+ // else
+ // {
+ // // var csstr = PUB.setting.ConnectionString;
+ // // if (string.IsNullOrEmpty(csstr)) csstr = "Data Source=10.131.15.18;Initial Catalog=EE;Persist Security Info=True;User ID=eeuser;Password=Amkor123!";
+ // var cn = new System.Data.SqlClient.SqlConnection(cs);
+ // var cmd = new System.Data.SqlClient.SqlCommand(sql, cn);
+ // cn.Open();
+ // var cnt = cmd.ExecuteNonQuery();
+ // //if (cnt == 0) PUB.log.Add($"Result Empty : {sql}");
+ // cn.Close();
+ // cnt += 1;
- var errpath = System.IO.Path.Combine(di.FullName, "Complete");
- var errfile = System.IO.Path.Combine(errpath, file.Name);
- if (System.IO.Directory.Exists(errpath) == false) System.IO.Directory.CreateDirectory(errpath);
- //file.MoveTo(errfile);
- System.IO.File.Move(file.FullName, errfile);
- }
+ // var errpath = System.IO.Path.Combine(di.FullName, "Complete");
+ // var errfile = System.IO.Path.Combine(errpath, file.Name);
+ // if (System.IO.Directory.Exists(errpath) == false) System.IO.Directory.CreateDirectory(errpath);
+ // //file.MoveTo(errfile);
+ // System.IO.File.Move(file.FullName, errfile);
+ // }
- }
- catch (Exception ex)
- {
- var errpath = System.IO.Path.Combine(di.FullName, "Error");
- var errfile = System.IO.Path.Combine(errpath, file.Name);
- if (System.IO.Directory.Exists(errpath) == false) System.IO.Directory.CreateDirectory(errpath);
- try
- {
- //file.MoveTo(errfile);
- System.IO.File.Move(file.FullName, errfile);
- }
- catch (Exception ex2)
- {
- Console.WriteLine($"move error : {ex2.Message} : {errfile}");
- }
+ // }
+ // catch (Exception ex)
+ // {
+ // var errpath = System.IO.Path.Combine(di.FullName, "Error");
+ // var errfile = System.IO.Path.Combine(errpath, file.Name);
+ // if (System.IO.Directory.Exists(errpath) == false) System.IO.Directory.CreateDirectory(errpath);
+ // try
+ // {
+ // //file.MoveTo(errfile);
+ // System.IO.File.Move(file.FullName, errfile);
+ // }
+ // catch (Exception ex2)
+ // {
+ // Console.WriteLine($"move error : {ex2.Message} : {errfile}");
+ // }
- //ecnt += 1;
- }
+ // //ecnt += 1;
+ // }
- try
- {
- //생성된지 10일이 넘은 자료는 삭제한다.
- //시간소비를 피해서 1개의 파일만 작업한다
- var sqlfiles = di.GetFiles("*.sql", System.IO.SearchOption.AllDirectories);
- //총3번의 데이터를 처리한다
- var files = sqlfiles.Where(t => t.LastWriteTime < DateTime.Now.AddDays(-10)).Select(t => t.FullName);
- int i = 0;
- var dellist = files.TakeWhile(t => i++ < 3);
- foreach (var delfile in dellist)
- System.IO.File.Delete(delfile);
- }
- catch
- {
+ // try
+ // {
+ // //생성된지 10일이 넘은 자료는 삭제한다.
+ // //시간소비를 피해서 1개의 파일만 작업한다
+ // var sqlfiles = di.GetFiles("*.sql", System.IO.SearchOption.AllDirectories);
+ // //총3번의 데이터를 처리한다
+ // var files = sqlfiles.Where(t => t.LastWriteTime < DateTime.Now.AddDays(-10)).Select(t => t.FullName);
+ // int i = 0;
+ // var dellist = files.TakeWhile(t => i++ < 3);
+ // foreach (var delfile in dellist)
+ // System.IO.File.Delete(delfile);
+ // }
+ // catch
+ // {
- }
- mre.Set();
- }
+ // }
+ // mre.Set();
+ //}
}
diff --git a/Cs_HMI/Project/PUB.cs b/Cs_HMI/Project/PUB.cs
index fdc44fa..0842f4b 100644
--- a/Cs_HMI/Project/PUB.cs
+++ b/Cs_HMI/Project/PUB.cs
@@ -4,16 +4,10 @@ using System.Net;
using System.Management;
using System.Data;
using AR;
-using System.Security.Policy;
-using Project.StateMachine;
-using System.Data.SqlClient;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Speech.Synthesis;
using System.Media;
using System.Runtime.InteropServices;
+using Microsoft.Data.SqlClient;
namespace Project
{
@@ -406,7 +400,7 @@ namespace Project
var conn = new SqlConnection(AGV4.Properties.Settings.Default.CS);// "Data Source=10.131.15.18;Initial Catalog=EE;Persist Security Info=True;User ID=eeuser;Password=Amkor123!");
conn.Open();
string ProcName = "AddPrgmUser3";
- SqlCommand cmd = new SqlCommand(ProcName, conn)
+ var cmd = new SqlCommand(ProcName, conn)
{
CommandType = CommandType.StoredProcedure
};
diff --git a/Cs_HMI/Project/ViewForm/fAuto.Designer.cs b/Cs_HMI/Project/ViewForm/fAuto.Designer.cs
index 38e8041..c72eb09 100644
--- a/Cs_HMI/Project/ViewForm/fAuto.Designer.cs
+++ b/Cs_HMI/Project/ViewForm/fAuto.Designer.cs
@@ -28,59 +28,68 @@
///
private void InitializeComponent()
{
- this.components = new System.ComponentModel.Container();
- this.timer1 = new System.Windows.Forms.Timer(this.components);
- this.ctlAuto1 = new Project.CtlAuto();
- this.SuspendLayout();
+ components = new System.ComponentModel.Container();
+ timer1 = new System.Windows.Forms.Timer(components);
+ ctlAuto1 = new CtlAuto();
+ panel1 = new Panel();
+ SuspendLayout();
//
// timer1
//
- this.timer1.Interval = 200;
- this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
+ timer1.Interval = 200;
+ timer1.Tick += timer1_Tick;
//
// ctlAuto1
//
- this.ctlAuto1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
- this.ctlAuto1.BorderColor = System.Drawing.Color.Transparent;
- this.ctlAuto1.Dock = System.Windows.Forms.DockStyle.Fill;
- this.ctlAuto1.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
- this.ctlAuto1.Font_BMS_Level = new System.Drawing.Font("Bahnschrift Condensed", 250F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
- this.ctlAuto1.Font_BMS_Volt = new System.Drawing.Font("Bahnschrift Condensed", 50.25F, System.Drawing.FontStyle.Bold);
- this.ctlAuto1.Font_MAIN_BMSLevel = new System.Drawing.Font("Arial Narrow", 150F, System.Drawing.FontStyle.Bold);
- this.ctlAuto1.ForeColor = System.Drawing.Color.White;
- this.ctlAuto1.ItemGap = 0;
- this.ctlAuto1.Items = null;
- this.ctlAuto1.Location = new System.Drawing.Point(0, 0);
- this.ctlAuto1.MinimumSize = new System.Drawing.Size(100, 30);
- this.ctlAuto1.Name = "ctlAuto1";
- this.ctlAuto1.ProgressMax = 100F;
- this.ctlAuto1.ProgressVal = 50F;
- this.ctlAuto1.Scean = Project.CtlAuto.eScean.Normal;
- this.ctlAuto1.Size = new System.Drawing.Size(1014, 579);
- this.ctlAuto1.StatusMessage = "상태메세지 입니다";
- this.ctlAuto1.StopMessage = "";
- this.ctlAuto1.StopTime = new System.DateTime(((long)(0)));
- this.ctlAuto1.TabIndex = 20;
- this.ctlAuto1.Text = "ctlAuto1";
+ ctlAuto1.BackColor = Color.FromArgb(32, 32, 32);
+ ctlAuto1.BorderColor = Color.Transparent;
+ ctlAuto1.Font = new Font("맑은 고딕", 20F, FontStyle.Regular, GraphicsUnit.Point, 129);
+ ctlAuto1.Font_BMS_Level = new Font("Bahnschrift Condensed", 250F, FontStyle.Bold, GraphicsUnit.Point, 0);
+ ctlAuto1.Font_BMS_Volt = new Font("Bahnschrift Condensed", 50.25F, FontStyle.Bold);
+ ctlAuto1.Font_MAIN_BMSLevel = new Font("Arial Narrow", 150F, FontStyle.Bold);
+ ctlAuto1.ForeColor = Color.White;
+ ctlAuto1.ItemGap = 0;
+ ctlAuto1.Items = null;
+ ctlAuto1.Location = new Point(835, 338);
+ ctlAuto1.MinimumSize = new Size(100, 30);
+ ctlAuto1.Name = "ctlAuto1";
+ ctlAuto1.ProgressMax = 100F;
+ ctlAuto1.ProgressVal = 50F;
+ ctlAuto1.Scean = CtlAuto.eScean.Normal;
+ ctlAuto1.Size = new Size(179, 241);
+ ctlAuto1.StatusMessage = "상태메세지 입니다";
+ ctlAuto1.StopMessage = "";
+ ctlAuto1.StopTime = new DateTime(0L);
+ ctlAuto1.TabIndex = 20;
+ ctlAuto1.Text = "ctlAuto1";
+ //
+ // panel1
+ //
+ panel1.Dock = DockStyle.Fill;
+ panel1.Location = new Point(0, 0);
+ panel1.Name = "panel1";
+ panel1.Size = new Size(1014, 579);
+ panel1.TabIndex = 21;
//
// fAuto
//
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
- this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(15)))), ((int)(((byte)(15)))), ((int)(((byte)(15)))));
- this.ClientSize = new System.Drawing.Size(1014, 579);
- this.Controls.Add(this.ctlAuto1);
- this.DoubleBuffered = true;
- this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
- this.Name = "fAuto";
- this.Text = "fAuto";
- this.Load += new System.EventHandler(this.fAuto_Load);
- this.VisibleChanged += new System.EventHandler(this.fAuto_VisibleChanged);
- this.ResumeLayout(false);
-
+ AutoScaleMode = AutoScaleMode.None;
+ BackColor = Color.FromArgb(15, 15, 15);
+ ClientSize = new Size(1014, 579);
+ Controls.Add(panel1);
+ Controls.Add(ctlAuto1);
+ DoubleBuffered = true;
+ FormBorderStyle = FormBorderStyle.None;
+ Name = "fAuto";
+ Text = "fAuto";
+ Load += fAuto_Load;
+ VisibleChanged += fAuto_VisibleChanged;
+ ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Timer timer1;
private CtlAuto ctlAuto1;
+ private Panel panel1;
}
}
\ No newline at end of file
diff --git a/Cs_HMI/Project/ViewForm/fAuto.cs b/Cs_HMI/Project/ViewForm/fAuto.cs
index 9e29e0f..ec8d635 100644
--- a/Cs_HMI/Project/ViewForm/fAuto.cs
+++ b/Cs_HMI/Project/ViewForm/fAuto.cs
@@ -15,6 +15,7 @@ namespace Project.ViewForm
{
public partial class fAuto : Form
{
+ AGVMapControl.MapControl mapctl;
public fAuto()
{
InitializeComponent();
@@ -25,6 +26,11 @@ namespace Project.ViewForm
this.ctlAuto1.Scean = CtlAuto.eScean.Progress;
else
this.ctlAuto1.Scean = CtlAuto.eScean.Normal;
+ this.mapctl = new AGVMapControl.MapControl();
+ this.mapctl.Dock = DockStyle.Fill;
+ this.mapctl.Visible = true;
+ this.mapctl.BackColor = Color.FromArgb(32, 32, 32);
+ this.panel1.Controls.Add(mapctl);
}
private void fAuto_Load(object sender, EventArgs e)
{
diff --git a/Cs_HMI/Project/ViewForm/fAuto.resx b/Cs_HMI/Project/ViewForm/fAuto.resx
index aac33d5..5655bc4 100644
--- a/Cs_HMI/Project/ViewForm/fAuto.resx
+++ b/Cs_HMI/Project/ViewForm/fAuto.resx
@@ -1,17 +1,17 @@
-
diff --git a/Cs_HMI/Project/sample.route b/Cs_HMI/Project/sample.route
new file mode 100644
index 0000000..113664f
--- /dev/null
+++ b/Cs_HMI/Project/sample.route
@@ -0,0 +1,144 @@
+[RFID_POINTS]
+100,486,T0002
+164,494,T0003
+229,493,T0004
+309,489,T0005
+370,490,T0006
+437,486,T0007
+483,459,T0008
+511,421,T0009
+543,371,T0010
+569,329,T0011
+608,289,T0012
+661,279,T0013
+700,297,T0014
+698,349,T0015
+698,391,T0016
+699,449,T0017
+691,491,T0018
+569,275,T0019
+518,264,T0020
+454,264,T0021
+388,261,T0022
+314,258,T0023
+639,234,T0024
+621,182,T0025
+629,143,T0026
+657,101,T0027
+627,82,T0028
+560,73,T0029
+499,65,T0030
+432,65,T0031
+264,232,T0032
+363,60,T0033
+654,508,T0034
+96,542,T0050
+159,542,T0051
+226,542,T0061
+309,541,T0070
+369,542,T0071
+483,165,T0010
+735,163,T0011
+523,170,T0001
+565,175,T0002
+597,182,T0003
+665,181,T0004
+700,176,T0005
+721,170,T0006
+[MAGNET_LINES]
+99,485,220,492
+220,492,375,488
+375,488,457,479
+457,479,512,432
+512,432,552,353
+552,353,602,291
+602,291,651,278
+651,278,700,289
+700,289,704,380
+704,380,700,469
+700,469,688,488
+666,277,637,229
+637,229,625,170
+625,170,654,94
+654,94,624,78
+624,78,484,63
+484,63,365,64
+608,287,523,267
+523,267,409,260
+409,260,275,258
+99,486,95,542
+161,495,161,543
+230,496,228,537
+228,537,228,544
+306,488,306,543
+373,489,371,544
+655,508,691,486
+481,164,624,183
+624,183,706,181
+706,181,743,161
+[MAP_TEXTS]
+179,251,-1,-16777216,Arial,12,TOPS-2
+239,52,-256,-65408,Arial,12,SSOTRON-3
+617,527,-16711936,-16777216,Arial,12,SSOTRON-1
+87,551,-16777216,16777215,Arial,12,B1
+150,551,-16777216,16777215,Arial,12,B2
+227,553,-16777216,16777215,Arial,12,B3
+299,555,-16777216,16777215,Arial,12,B4
+367,554,-16777216,16777215,Arial,12,B5
+453,128,-16777216,-8323073,Arial,12,CHG1
+725,124,-16777216,-8323073,Arial,12,CHG2
+[CUSTOM_LINES]
+[RFID_LINES]
+96,542,100,486,T0005,T0002,True,56.14267
+100,486,164,494,T0002,T0003,True,64.49806
+164,494,229,493,T0003,T0004,True,65.00769
+229,493,309,489,T0004,T0005,True,80.09994
+309,489,370,490,T0005,T0006,True,61.0082
+370,490,437,486,T0006,T0007,True,67.11929
+437,486,483,459,T0007,T0008,True,53.33854
+483,459,511,421,T0008,T0009,True,47.20169
+511,421,543,371,T0009,T0010,True,59.36329
+543,371,569,329,T0010,T0011,True,49.39635
+569,329,608,289,T0011,T0012,True,55.86591
+608,289,661,279,T0012,T0013,True,53.93515
+661,279,700,297,T0013,T0014,True,42.95346
+700,297,698,349,T0014,T0015,True,52.03845
+698,349,698,391,T0015,T0016,True,42
+698,391,699,449,T0016,T0017,True,58.00862
+699,449,691,491,T0017,T0018,True,42.75512
+691,491,654,508,T0018,T0034,True,40.71855
+159,542,164,494,T0006,T0003,True,48.25971
+226,542,229,493,T0007,T0004,True,49.09175
+309,541,309,489,T0008,T0005,True,52
+369,542,370,490,T0009,T0006,True,52.00961
+370,490,370,490,T0006,T0006,True,0
+608,289,569,275,T0012,T0019,True,41.4367
+569,275,518,264,T0019,T0020,True,52.17279
+518,264,454,264,T0020,T0021,True,64
+454,264,388,261,T0021,T0022,True,66.06815
+388,261,314,258,T0022,T0023,True,74.06078
+661,279,639,234,T0013,T0024,True,50.08992
+639,234,621,182,T0024,T0025,True,55.02727
+621,182,629,143,T0025,T0026,True,39.81206
+629,143,657,101,T0026,T0027,True,50.47772
+657,101,627,82,T0027,T0028,True,35.51056
+627,82,560,73,T0028,T0029,True,67.60178
+560,73,499,65,T0029,T0030,True,61.52235
+483,165,523,170,T0010,T0001,True,40.31129
+523,170,565,175,T0001,T0002,True,42.29657
+565,175,597,182,T0002,T0003,True,32.75668
+597,182,621,182,T0003,T0025,True,24
+621,182,665,181,T0025,T0004,True,44.01136
+665,181,700,176,T0004,T0005,True,35.35534
+700,176,721,170,T0005,T0006,True,21.84033
+721,170,735,163,T0006,T0011,True,15.65248
+735,163,721,170,T0011,T0006,True,15.65248
+721,170,735,163,T0006,T0011,True,15.65248
+314,258,388,261,T0023,T0022,True,74.06078
+388,261,454,264,T0022,T0021,True,66.06815
+454,264,518,264,T0021,T0020,True,64
+518,264,569,275,T0020,T0019,True,52.17279
+569,275,608,289,T0019,T0012,True,41.4367
+264,232,314,258,T0032,T0023,True,56.35601
+363,60,432,65,T0033,T0031,True,69.18092
+432,65,499,65,T0031,T0030,True,67
diff --git a/Cs_HMI/SubProject/AGVMapControl/AGVMapControl.csproj b/Cs_HMI/SubProject/AGVMapControl/AGVMapControl.csproj
new file mode 100644
index 0000000..effcb7c
--- /dev/null
+++ b/Cs_HMI/SubProject/AGVMapControl/AGVMapControl.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net8.0-windows
+ enable
+ true
+ enable
+
+
+
+
+
+
+
diff --git a/Cs_HMI/SubProject/AGVMapControl/MapControl.Designer.cs b/Cs_HMI/SubProject/AGVMapControl/MapControl.Designer.cs
new file mode 100644
index 0000000..9625998
--- /dev/null
+++ b/Cs_HMI/SubProject/AGVMapControl/MapControl.Designer.cs
@@ -0,0 +1,37 @@
+namespace AGVMapControl
+{
+ partial class MapControl
+ {
+ ///
+ /// 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);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ components = new System.ComponentModel.Container();
+ this.ClientSize = new System.Drawing.Size(800, 450);
+ }
+
+ #endregion
+ }
+}
diff --git a/Cs_HMI/SubProject/AGVMapControl/MapControl.cs b/Cs_HMI/SubProject/AGVMapControl/MapControl.cs
new file mode 100644
index 0000000..031a293
--- /dev/null
+++ b/Cs_HMI/SubProject/AGVMapControl/MapControl.cs
@@ -0,0 +1,1688 @@
+using System.Collections.Immutable;
+using System.Drawing.Design;
+using System.Drawing.Drawing2D;
+using System.Text.RegularExpressions;
+using System.Windows.Forms;
+using AGVMapControl.Models;
+
+namespace AGVMapControl
+{
+ public partial class MapControl : Control
+ {
+ private List rfidPoints;
+ private List magnetLines;
+ private List mapTexts;
+ private List customLines;
+ private List rfidLines;
+ private AGV agv;
+ private float zoom = 1.0f;
+ private PointF offset = PointF.Empty;
+ private Point lastMousePosition;
+ private bool isDragging = false;
+ private Point? previewStartPoint = null;
+ private Point currentMousePosition;
+ private const int SNAP_DISTANCE = 10; // 점 근접 거리
+ private const int LINE_WIDTH = 20; // 선 굵기
+ private const int TOOLBAR_WIDTH = 58;
+ private const int TOOLBAR_WIDTHR = 78;
+ private const int TOOLBAR_BUTTON_HEIGHT = 40;
+ private const int TOOLBAR_MARGIN = 5;
+
+ private List toolbarRects;
+
+
+ private bool isAddingText = false;
+ private bool isAddingPoint = false;
+ private bool isDrawingCustomLine = false;
+ private bool isDrawingMagnetLine = false;
+ private bool isDrawingRFIDLine = false;
+ private bool isDeletingRFIDLine = false;
+ private bool isDrawingLine = false;
+ private MapText selectedText = null;
+ private CustomLine selectedLine = null;
+ private RFIDPoint selectedRFID = null;
+ private MagnetLine selectedMagnetLine = null;
+ private RFIDLine selectedRFIDLine = null;
+ private Point? draggingPoint = null;
+ private bool isDraggingPoint = false;
+ private const int SELECTION_DISTANCE = 15; // 선택 가능 거리를 늘림
+ private bool isDraggingText = false;
+ private Point dragOffset;
+ private List currentPath;
+
+
+ public MapText SelectedText => selectedText;
+ public CustomLine SelectedLine => selectedLine;
+ public RFIDPoint SelectedRFID => selectedRFID;
+ public MagnetLine SelectedMagnetLine => selectedMagnetLine;
+ public RFIDLine SelectedRFIDLine => selectedRFIDLine;
+
+
+
+ public float GetDistance(Point p1, Point p2)
+ {
+ float dx = p1.X - p2.X;
+ float dy = p1.Y - p2.Y;
+ return (float)Math.Sqrt(dx * dx + dy * dy); // double을 float로 명시적 캐스팅
+ }
+
+ public MapControl()
+ {
+ this.DoubleBuffered = true;
+ rfidPoints = new List();
+ magnetLines = new List();
+ mapTexts = new List();
+ customLines = new List();
+ rfidLines = new List();
+ agv = new AGV();
+
+ // 툴바 버튼 영역 초기화
+ UpdateToolbarRects();
+
+ // 마우스 이벤트 핸들러 추가
+ this.MouseWheel += MapControl_MouseWheel;
+ this.MouseDown += MapControl_MouseDown;
+ this.MouseMove += MapControl_MouseMove;
+ this.MouseUp += MapControl_MouseUp;
+ this.MouseClick += MapControl_MouseClick;
+ this.MouseDoubleClick += MapControl_MouseDoubleClick;
+ this.Resize += MapControl_Resize;
+ }
+
+ private void MapControl_Resize(object sender, EventArgs e)
+ {
+ UpdateToolbarRects();
+ this.Invalidate();
+ }
+
+ private void UpdateToolbarRects()
+ {
+ int x, y, c, row;
+ var idx = 0;
+
+ if (this.toolbarRects == null) this.toolbarRects = new List();
+ else this.toolbarRects.Clear();
+
+ //left toolbar
+ x = TOOLBAR_MARGIN;
+ y = 10;
+ toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "+", Bounds = new Rectangle(x, y, TOOLBAR_WIDTH - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
+
+ y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
+ toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "-", Bounds = new Rectangle(x, y, TOOLBAR_WIDTH - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
+
+ y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
+ toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "1:1", Bounds = new Rectangle(x, y, TOOLBAR_WIDTH - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
+
+ y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
+ toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Cut", Bounds = new Rectangle(x, y, TOOLBAR_WIDTH - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
+
+
+ //right toolbar
+ y = 10;
+ row = 0;
+ x = DisplayRectangle.Right - TOOLBAR_WIDTHR - TOOLBAR_MARGIN;
+ toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Text", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
+
+ y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
+ toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Line", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
+
+ y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
+ toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Point", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
+
+ y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
+ toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Magnet", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
+
+ y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
+ toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Load", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
+
+ y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
+ toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Save", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
+
+ y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
+ toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Pos", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
+
+ y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
+ toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Path", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
+
+ }
+
+ public void SetPreviewStartPoint(Point? point)
+ {
+ previewStartPoint = point;
+ this.Invalidate();
+ }
+
+ public void UpdatePreviewLine(Point currentPosition)
+ {
+ currentMousePosition = currentPosition;
+ this.Invalidate();
+ }
+
+ private Point SnapToPoint(Point point)
+ {
+ // RFID 포인트와 근접한지 확인
+ foreach (var rfid in rfidPoints)
+ {
+ if (GetDistance(point, rfid.Location) <= SNAP_DISTANCE)
+ {
+ return rfid.Location;
+ }
+ }
+
+ // 마그넷 라인의 끝점과 근접한지 확인
+ foreach (var line in magnetLines)
+ {
+ if (GetDistance(point, line.StartPoint) <= SNAP_DISTANCE)
+ {
+ return line.StartPoint;
+ }
+ if (GetDistance(point, line.EndPoint) <= SNAP_DISTANCE)
+ {
+ return line.EndPoint;
+ }
+ }
+
+ return point;
+ }
+
+ // 화면 좌표를 실제 맵 좌표로 변환
+ public Point ScreenToMap(Point screenPoint)
+ {
+ int adjustedX = screenPoint.X;
+ return new Point(
+ (int)((adjustedX - offset.X) / zoom),
+ (int)((screenPoint.Y - offset.Y) / zoom)
+ );
+ }
+
+ public RFIDPoint FindRFIDPoint(string rfidValue)
+ {
+ return rfidPoints.FirstOrDefault(r => r.RFIDValue.ToUpper() == rfidValue.ToUpper());
+ }
+
+ public bool AGVMoveToRFID(string rfidValue)
+ {
+ var rfidPoint = FindRFIDPoint(rfidValue);
+ if (rfidPoint != null)
+ {
+ agv.CurrentPosition = rfidPoint.Location;
+ agv.CurrentRFID = rfidValue;
+ this.Invalidate();
+ return true;
+ }
+ return false;
+ }
+ private void MapControl_MouseWheel(object sender, MouseEventArgs e)
+ {
+ if (e.Delta > 0)
+ {
+ zoom *= 1.1f;
+ }
+ else
+ {
+ zoom /= 1.1f;
+ }
+
+ zoom = Math.Max(0.1f, Math.Min(10.0f, zoom));
+ this.Invalidate();
+ }
+
+ private void MapControl_MouseDown(object sender, MouseEventArgs e)
+ {
+ lastMousePosition = e.Location;
+ var mapPoint = ScreenToMap(e.Location);
+
+ if (e.Button == MouseButtons.Middle)
+ {
+ isDragging = true;
+ this.Cursor = Cursors.SizeAll;
+ }
+ else if (e.Button == MouseButtons.Left && !isAddingText && !isDrawingCustomLine && !isDrawingRFIDLine && !isDrawingMagnetLine)
+ {
+ isDragging = true;
+
+ // 텍스트 선택 및 드래그 시작
+ foreach (var text in mapTexts)
+ {
+ var textSize = CreateGraphics().MeasureString(text.Text, text.Font);
+ var rect = new RectangleF(
+ text.Location.X - SELECTION_DISTANCE,
+ text.Location.Y - SELECTION_DISTANCE,
+ textSize.Width + SELECTION_DISTANCE * 2,
+ textSize.Height + SELECTION_DISTANCE * 2
+ );
+
+ if (rect.Contains(mapPoint))
+ {
+ selectedText = text;
+ isDraggingText = true;
+ // 드래그 시작점과 텍스트 위치의 차이를 저장
+ dragOffset = new Point(
+ text.Location.X - mapPoint.X,
+ text.Location.Y - mapPoint.Y
+ );
+ return;
+ }
+ }
+
+ // 커스텀 라인 선택 및 드래그 포인트 설정
+ foreach (var line in customLines)
+ {
+ float startDistance = GetDistance(mapPoint, line.StartPoint);
+ float endDistance = GetDistance(mapPoint, line.EndPoint);
+
+ if (startDistance < SELECTION_DISTANCE)
+ {
+ selectedLine = line;
+ draggingPoint = line.StartPoint;
+ isDraggingPoint = true;
+ return;
+ }
+ else if (endDistance < SELECTION_DISTANCE)
+ {
+ selectedLine = line;
+ draggingPoint = line.EndPoint;
+ isDraggingPoint = true;
+ return;
+ }
+ }
+
+
+ // RFID 포인트 선택
+ var clickedRFID = rfidPoints.FirstOrDefault(r =>
+ GetDistance(mapPoint, r.Location) <= SNAP_DISTANCE);
+ if (clickedRFID != null)
+ {
+ selectedRFID = clickedRFID;
+ draggingPoint = clickedRFID.Location;
+ isDraggingPoint = true;
+ return;
+ }
+
+
+ }
+ }
+
+ Point? lineStartPoint = null;
+ Point? branchPoint = null;
+
+ private void MapControl_MouseMove(object sender, MouseEventArgs e)
+ {
+ currentMousePosition = e.Location;
+ var mapPoint = ScreenToMap(e.Location);
+
+ if ((mousemode == eMouseMode.addcustomline || mousemode == eMouseMode.addrfidline) && branchPoint.HasValue)
+ {
+ UpdatePreviewLine(e.Location);
+ }
+
+ // 툴바 버튼 호버 상태 업데이트
+ var oldHovering = toolbarRects.OrderBy(t => t.Idx).Select(t => t.isHovering).ToArray();
+
+ //toolbar check
+ toolbarRects.ForEach(t => t.isHovering = t.Bounds.Contains(e.Location));
+
+ //hovering check
+ if (toolbarRects.Where(t => t.isHovering).Any())
+ {
+ this.Cursor = Cursors.Hand;
+ }
+ else if (isDeletingRFIDLine)
+ {
+ this.Cursor = GetScissorsCursor();
+ }
+ else
+ {
+ this.Cursor = Cursors.Default;
+ }
+
+ //hovering display update
+ if (toolbarRects.Where(t => t.Dirty).Any())
+ {
+ this.Invalidate();
+ }
+
+ if (isDragging)
+ {
+ if (e.Button == MouseButtons.Middle)
+ {
+ offset = new PointF(
+ offset.X + (e.Location.X - lastMousePosition.X),
+ offset.Y + (e.Location.Y - lastMousePosition.Y)
+ );
+ }
+ else if (isDraggingText && selectedText != null)
+ {
+ // 텍스트 이동 - 드래그 오프셋 적용
+ selectedText.Location = new Point(
+ mapPoint.X + dragOffset.X,
+ mapPoint.Y + dragOffset.Y
+ );
+ }
+ else if (isDraggingPoint && draggingPoint.HasValue)
+ {
+ // 포인트 이동
+ var delta = new Point(
+ mapPoint.X - ScreenToMap(lastMousePosition).X,
+ mapPoint.Y - ScreenToMap(lastMousePosition).Y
+ );
+
+ if (selectedLine != null)
+ {
+ // 커스텀 라인 포인트 이동
+ if (draggingPoint.Value == selectedLine.StartPoint)
+ {
+ selectedLine.StartPoint = new Point(
+ selectedLine.StartPoint.X + delta.X,
+ selectedLine.StartPoint.Y + delta.Y
+ );
+ }
+ else if (draggingPoint.Value == selectedLine.EndPoint)
+ {
+ selectedLine.EndPoint = new Point(
+ selectedLine.EndPoint.X + delta.X,
+ selectedLine.EndPoint.Y + delta.Y
+ );
+ }
+ }
+ else if (selectedRFID != null) // RFID 포인트 이동
+ {
+ // RFID 포인트 위치 업데이트
+ selectedRFID.Location = new Point(
+ selectedRFID.Location.X + delta.X,
+ selectedRFID.Location.Y + delta.Y
+ );
+
+ // 연결된 RFID 라인 업데이트
+ foreach (var line in rfidLines)
+ {
+ if (line.StartPoint == draggingPoint.Value)
+ {
+ line.StartPoint = selectedRFID.Location;
+ line.Distance = GetDistance(line.StartPoint, line.EndPoint);
+ }
+ if (line.EndPoint == draggingPoint.Value)
+ {
+ line.EndPoint = selectedRFID.Location;
+ line.Distance = GetDistance(line.StartPoint, line.EndPoint);
+ }
+ }
+ }
+ }
+
+ lastMousePosition = e.Location;
+ this.Invalidate();
+ }
+
+ // 미리보기 라인 업데이트를 위한 마우스 위치 저장
+ if (isDrawingRFIDLine || isDrawingCustomLine || previewStartPoint.HasValue)
+ {
+ currentMousePosition = mapPoint;
+ }
+
+ this.Invalidate();
+ }
+ private List CalculatePath(Point start, Point end)
+ {
+ var openList = new List { start };
+ var closedList = new List();
+ var cameFrom = new Dictionary();
+ var gScore = new Dictionary { { start, 0 } };
+ var fScore = new Dictionary { { start, Heuristic(start, end) } };
+
+ while (openList.Count > 0)
+ {
+ var current = openList.OrderBy(p => fScore.ContainsKey(p) ? fScore[p] : float.MaxValue).First();
+
+ if (current == end)
+ {
+ return ReconstructPath(cameFrom, current);
+ }
+
+ openList.Remove(current);
+ closedList.Add(current);
+
+ foreach (var neighbor in GetNeighbors(current))
+ {
+ if (closedList.Contains(neighbor))
+ continue;
+
+ float tentativeGScore = gScore[current] + Distance(current, neighbor);
+
+ if (!openList.Contains(neighbor))
+ openList.Add(neighbor);
+ else if (tentativeGScore >= gScore[neighbor])
+ continue;
+
+ cameFrom[neighbor] = current;
+ gScore[neighbor] = tentativeGScore;
+ fScore[neighbor] = gScore[neighbor] + Heuristic(neighbor, end);
+ }
+ }
+
+ return null;
+ }
+ private List ReconstructPath(Dictionary cameFrom, Point current)
+ {
+ var path = new List { current };
+ while (cameFrom.ContainsKey(current))
+ {
+ current = cameFrom[current];
+ path.Insert(0, current);
+ }
+ return path;
+ }
+ private float Heuristic(Point a, Point b)
+ {
+ return (float)Math.Sqrt(Math.Pow(a.X - b.X, 2) + Math.Pow(a.Y - b.Y, 2));
+ }
+
+
+ private void MapControl_MouseUp(object sender, MouseEventArgs e)
+ {
+ if (e.Button == MouseButtons.Middle)
+ {
+ isDragging = false;
+ this.Cursor = Cursors.Default;
+ }
+ else if (e.Button == MouseButtons.Left)
+ {
+ isDragging = false;
+ isDraggingPoint = false;
+ isDraggingText = false;
+ }
+ }
+ private float Distance(Point a, Point b)
+ {
+ // RFID 라인을 통한 연결 확인
+ var rfidLines = GetRFIDLines();
+ var directConnection = rfidLines.FirstOrDefault(line =>
+ (line.StartPoint == a && line.EndPoint == b) ||
+ (line.IsBidirectional && line.StartPoint == b && line.EndPoint == a));
+
+ if (directConnection != null)
+ {
+ return directConnection.Distance;
+ }
+
+ // 직접 연결되지 않은 경우 매우 큰 값 반환
+ return float.MaxValue;
+ }
+
+ private List GetNeighbors(Point point)
+ {
+ var neighbors = new List();
+ var rfidLines = GetRFIDLines();
+
+ // RFID 라인에서 이웃 노드 찾기
+ foreach (var line in rfidLines)
+ {
+ if (line.StartPoint == point)
+ {
+ neighbors.Add(line.EndPoint);
+ }
+ else if (line.EndPoint == point)
+ {
+ // 양방향 연결인 경우에만 시작점을 이웃으로 추가
+ if (line.IsBidirectional)
+ {
+ neighbors.Add(line.StartPoint);
+ }
+ }
+ }
+
+ return neighbors.Distinct().ToList();
+ }
+ public void SetPath(List rfids)
+ {
+ if (rfids == null || rfids.Count == 0)
+ return;
+
+ var path = new List();
+ foreach (var rfid in rfids)
+ {
+ var point = GetRFIDPoints().FirstOrDefault(p => p.RFIDValue == rfid);
+ if (point != null)
+ {
+ path.Add(point.Location);
+ }
+ }
+
+ if (path.Count > 0)
+ {
+ DrawPath(path);
+ }
+ }
+
+
+ public string RFIDStartNo { get; set; } = string.Empty;
+ public int RFIDLastNumber = 0;
+ string filename = string.Empty;
+ private void MapControl_MouseClick(object sender, MouseEventArgs e)
+ {
+ var mapPoint = ScreenToMap(e.Location);
+ if (e.Button == MouseButtons.Right)
+ {
+ OnRightClick?.Invoke(this, e);
+ this.MouseMode = eMouseMode.Default;
+ }
+ else if (e.Button == MouseButtons.Left)
+ {
+ // 툴바 버튼 클릭 처리
+ var toolbar = toolbarRects.FirstOrDefault(t => t.Bounds.Contains(e.Location));
+ if (toolbar != null)
+ {
+ switch (toolbar.Title.ToLower())
+ {
+ case "+": ZoomIn(); break;
+ case "-": ZoomOut(); break;
+ case "1:1": ResetZoom(); break;
+ case "cut": MouseMode = (eMouseMode.rfidcut); break;
+ case "text": MouseMode = (eMouseMode.addtext); break;
+ case "line": MouseMode = (eMouseMode.addrfidline); break;
+ case "cline": MouseMode = (eMouseMode.addcustomline); break;
+ case "point": MouseMode = (eMouseMode.addrfidpoint); break;
+ case "path":
+
+ var input1 = AR.UTIL.InputBox("input start");
+ if (input1.Item1 == false) return;
+ var input2 = AR.UTIL.InputBox("input end");
+ if (input2.Item1 == false) return;
+
+ var startRFID = input1.Item2;
+ var endRFID = input2.Item2;
+ var startPoint = FindRFIDPoint(startRFID);
+ var endPoint = FindRFIDPoint(endRFID);
+
+ if (startPoint == null || endPoint == null)
+ {
+ MessageBox.Show("유효한 RFID 값을 입력해주세요.", "경로 계산", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ var path = CalculatePath(startPoint.Location, endPoint.Location);
+ if (path != null && path.Count > 0)
+ {
+ DrawPath(path);
+
+ // 경로 상의 모든 RFID 값을 가져옴
+ var rfidPath = new List();
+ foreach (var point in path)
+ {
+ var rfid = GetRFIDPoints()
+ .FirstOrDefault(r => r.Location == point);
+ if (rfid != null)
+ {
+ rfidPath.Add(rfid.RFIDValue);
+ }
+ }
+
+ MessageBox.Show($"경로가 계산되었습니다.\nRFID 순서: {string.Join(" -> ", rfidPath)}",
+ "경로 계산", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ else
+ {
+ MessageBox.Show("경로를 찾을 수 없습니다.", "경로 계산", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ }
+ break;
+ case "pos":
+ var tag = AR.UTIL.InputBox("input rfid tag value");
+ if (tag.Item1 && tag.Item2 != "")
+ {
+ var targetRFID = AGVMoveToRFID(tag.Item2);
+ }
+ break;
+ case "save":
+ using (var od = new SaveFileDialog())
+ {
+ od.Filter = "path data|*.route";
+ od.FilterIndex = 0;
+ od.RestoreDirectory = true;
+ od.FileName = System.IO.Path.GetFileName(this.filename);
+ od.InitialDirectory = System.IO.Path.GetDirectoryName(this.filename);
+ if (od.ShowDialog() == DialogResult.OK)
+ {
+ filename = od.FileName;
+ this.SaveToFile(filename);
+ this.Invalidate();
+ }
+ }
+ break;
+ case "load":
+ using (var od = new OpenFileDialog())
+ {
+ od.Filter = "path data|*.route";
+ od.FilterIndex = 0;
+
+
+ if (string.IsNullOrEmpty(this.filename) == false)
+ {
+ od.FileName = System.IO.Path.GetFileName(this.filename);
+ od.InitialDirectory = System.IO.Path.GetDirectoryName(this.filename);
+ }
+ else
+ {
+ od.RestoreDirectory = true;
+ }
+
+
+ if (od.ShowDialog() == DialogResult.OK)
+ {
+ filename = od.FileName;
+ this.LoadFromFile(filename);
+ this.Invalidate();
+ }
+ }
+ break;
+ }
+ return;
+ }
+
+ // RFID 포인트 선택
+ var clickedRFID = rfidPoints.FirstOrDefault(r => GetDistance(mapPoint, r.Location) <= SNAP_DISTANCE);
+ switch (mousemode)
+ {
+ case eMouseMode.rfidcut:
+ DeleteNearbyRFIDLine(mapPoint);
+ break;
+ case eMouseMode.addrfidpoint:
+ if (string.IsNullOrEmpty(this.RFIDStartNo) == false)
+ {
+
+ AddRFIDPoint(mapPoint, RFIDStartNo);
+
+ // 숫자로 끝나는 RFID 값인 경우 자동 증가
+ if (Regex.IsMatch(RFIDStartNo, @"^[A-Za-z]+\d+$"))
+ {
+ // 마지막 숫자 부분 추출
+ Match match = Regex.Match(RFIDStartNo, @"\d+$");
+ if (match.Success)
+ {
+ int currentNumber = int.Parse(match.Value);
+ if (currentNumber > this.RFIDLastNumber)
+ {
+ RFIDLastNumber = currentNumber;
+ }
+ RFIDLastNumber++;
+
+ // 숫자 부분을 새로운 번호로 교체
+ RFIDStartNo = RFIDStartNo.Substring(0, match.Index) + RFIDLastNumber.ToString("D4");
+ }
+ }
+ }
+ break;
+ case eMouseMode.addtext:
+ var text = new MapText
+ {
+ Location = mapPoint,
+ Text = "새 텍스트",
+ TextColor = Color.Black,
+ BackgroundColor = Color.Transparent,
+ Font = new Font("Arial", 12)
+ };
+ mapTexts.Add(text);
+ selectedText = text;
+ this.Invalidate();
+ break;
+ case eMouseMode.addcustomline:
+ if (previewStartPoint == null)
+ {
+ previewStartPoint = mapPoint;
+ }
+ else
+ {
+ var line = new CustomLine
+ {
+ StartPoint = previewStartPoint.Value,
+ EndPoint = mapPoint,
+ LineColor = Color.Red,
+ LineWidth = 2
+ };
+ customLines.Add(line);
+ selectedLine = line;
+ previewStartPoint = null;
+ this.Invalidate();
+ }
+ break;
+ case eMouseMode.addrfidline:
+ if (clickedRFID != null)
+ {
+ if (previewStartPoint == null)
+ {
+ previewStartPoint = clickedRFID.Location;
+ }
+ else
+ {
+ var startRFID = rfidPoints.FirstOrDefault(r => r.Location == previewStartPoint);
+ if (startRFID != null)
+ {
+ var line = new RFIDLine
+ {
+ StartPoint = previewStartPoint.Value,
+ EndPoint = clickedRFID.Location,
+ StartRFID = startRFID.RFIDValue,
+ EndRFID = clickedRFID.RFIDValue,
+ Distance = GetDistance(previewStartPoint.Value, clickedRFID.Location)
+ };
+ rfidLines.Add(line);
+ selectedRFIDLine = line;
+
+ // 선 근처의 RFID 포인트들을 찾아서 추가
+ AddNearbyRFIDPoints(line);
+ }
+ // 다음 라인을 위해 현재 클릭한 RFID를 시작점으로 설정
+ previewStartPoint = clickedRFID.Location;
+ }
+ this.Invalidate();
+ }
+ break;
+ }
+ }
+ }
+
+ private void AddNearbyRFIDPoints(RFIDLine line)
+ {
+ const float NEARBY_DISTANCE = 20.0f; // 근처로 간주하는 거리를 50에서 20으로 줄임
+
+ // 선 근처의 RFID 포인트들을 찾아서 거리에 따라 정렬
+ var nearbyPoints = new List<(RFIDPoint Point, float Distance, float ProjectionRatio)>();
+
+ foreach (var rfid in rfidPoints)
+ {
+ if (rfid.Location == line.StartPoint || rfid.Location == line.EndPoint)
+ continue;
+
+ // 선분과 RFID 포인트 사이의 최단 거리 계산
+ float distance = GetDistanceToLine(rfid.Location, line.StartPoint, line.EndPoint);
+
+ if (distance <= NEARBY_DISTANCE)
+ {
+ // 시작점으로부터의 투영 비율 계산 (0~1 사이 값)
+ float projectionRatio = GetProjectionRatio(rfid.Location, line.StartPoint, line.EndPoint);
+ if (projectionRatio >= 0 && projectionRatio <= 1) // 선분 위에 있는 점만 포함
+ {
+ nearbyPoints.Add((rfid, distance, projectionRatio));
+ }
+ }
+ }
+
+ // 시작점에서 끝점 방향으로 정렬
+ nearbyPoints.Sort((a, b) => a.ProjectionRatio.CompareTo(b.ProjectionRatio));
+
+ // 이전 RFID 값과 위치를 저장
+ string prevRFID = line.StartRFID;
+ Point prevPoint = line.StartPoint;
+
+ // 정렬된 포인트들을 순차적으로 연결
+ foreach (var item in nearbyPoints)
+ {
+ var rfidLine = new RFIDLine
+ {
+ StartPoint = prevPoint,
+ EndPoint = item.Point.Location,
+ StartRFID = prevRFID,
+ EndRFID = item.Point.RFIDValue,
+ Distance = GetDistance(prevPoint, item.Point.Location)
+ };
+ rfidLines.Add(rfidLine);
+
+ // 현재 포인트를 다음 선분의 시작점으로 설정
+ prevPoint = item.Point.Location;
+ prevRFID = item.Point.RFIDValue;
+ }
+
+ // 마지막 포인트와 원래 선의 끝점을 연결
+ var finalLine = new RFIDLine
+ {
+ StartPoint = prevPoint,
+ EndPoint = line.EndPoint,
+ StartRFID = prevRFID,
+ EndRFID = line.EndRFID,
+ Distance = GetDistance(prevPoint, line.EndPoint)
+ };
+ rfidLines.Add(finalLine);
+
+ // 원래 선은 제거
+ rfidLines.Remove(line);
+ }
+
+ private float GetProjectionRatio(Point point, Point lineStart, Point lineEnd)
+ {
+ float lineLength = GetDistance(lineStart, lineEnd);
+ if (lineLength == 0) return 0;
+
+ return ((point.X - lineStart.X) * (lineEnd.X - lineStart.X) +
+ (point.Y - lineStart.Y) * (lineEnd.Y - lineStart.Y)) / (lineLength * lineLength);
+ }
+
+ private float GetDistanceToLine(Point point, Point lineStart, Point lineEnd)
+ {
+ float lineLength = GetDistance(lineStart, lineEnd);
+ if (lineLength == 0) return GetDistance(point, lineStart);
+
+ float t = ((point.X - lineStart.X) * (lineEnd.X - lineStart.X) +
+ (point.Y - lineStart.Y) * (lineEnd.Y - lineStart.Y)) / (lineLength * lineLength);
+
+ t = Math.Max(0, Math.Min(1, t));
+
+ float projectionX = lineStart.X + t * (lineEnd.X - lineStart.X);
+ float projectionY = lineStart.Y + t * (lineEnd.Y - lineStart.Y);
+
+ return GetDistance(point, new Point((int)projectionX, (int)projectionY));
+ }
+
+ private void MapControl_MouseDoubleClick(object sender, MouseEventArgs e)
+ {
+ if (e.Button == MouseButtons.Left)
+ {
+ // 화면 좌표를 맵 좌표로 변환
+ var mapPoint = ScreenToMap(e.Location);
+
+ // 텍스트 객체 찾기
+ foreach (var text in mapTexts)
+ {
+ var textSize = CreateGraphics().MeasureString(text.Text, text.Font);
+ var rect = new RectangleF(
+ text.Location.X - SELECTION_DISTANCE,
+ text.Location.Y - SELECTION_DISTANCE,
+ textSize.Width + SELECTION_DISTANCE * 2,
+ textSize.Height + SELECTION_DISTANCE * 2
+ );
+
+ if (rect.Contains(mapPoint))
+ {
+ var rlt = AR.UTIL.InputBox("input", text.Text);
+ if (rlt.Item1 == true)
+ {
+ text.Text = rlt.Item2;// dialog.InputText;
+ this.Invalidate();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public event EventHandler OnRightClick;
+
+ public void SetRFIDPoints(List points)
+ {
+ rfidPoints = points;
+ this.Invalidate();
+ }
+
+ public void SetMagnetLines(List lines)
+ {
+ magnetLines = lines;
+ this.Invalidate();
+ }
+
+ public void SetAGV(AGV vehicle)
+ {
+ agv = vehicle;
+ this.Invalidate();
+ }
+
+ public void SetMapTexts(List texts)
+ {
+ mapTexts = texts;
+ if (mapTexts == null) mapTexts = new List();
+ this.Invalidate();
+ }
+
+ public void SetCustomLines(List lines)
+ {
+ customLines = lines;
+ if (customLines == null) customLines = new List();
+ this.Invalidate();
+ }
+
+ public void SetIsAddingText(bool value)
+ {
+ isAddingText = value;
+ isDrawingCustomLine = false;
+ this.Cursor = value ? Cursors.IBeam : Cursors.Default;
+ }
+
+ public void SetIsDrawingCustomLine(bool value)
+ {
+ isDrawingCustomLine = value;
+ isAddingText = false;
+ this.Cursor = value ? Cursors.Cross : Cursors.Default;
+ }
+
+ public void SetIsDrawingRFIDLine(bool value)
+ {
+ isDrawingRFIDLine = value;
+ isDrawingCustomLine = false;
+ isAddingText = false;
+ this.Cursor = value ? Cursors.Cross : Cursors.Default;
+ }
+
+ public void SetIsDeletingRFIDLine(bool value)
+ {
+ isDeletingRFIDLine = value;
+ isDrawingCustomLine = false;
+ isAddingText = false;
+ isDrawingRFIDLine = false;
+ this.Cursor = value ? GetScissorsCursor() : Cursors.Default;
+ }
+
+ public void SetIsDrawingLine(bool value)
+ {
+ isDrawingLine = value;
+ isDrawingCustomLine = false;
+ isAddingText = false;
+ isDrawingRFIDLine = false;
+ this.Cursor = value ? Cursors.Cross : Cursors.Default;
+ }
+
+ public enum eMouseMode : byte
+ {
+ Default = 0,
+ pan,
+ rfidcut,
+ addtext,
+ addcustomline,
+ addrfidpoint,
+ addrfidline,
+ }
+
+ private eMouseMode mousemode = eMouseMode.Default;
+ public eMouseMode MouseMode
+ {
+ get { return mousemode; }
+ set
+ {
+ if (this.mousemode == value) mousemode = eMouseMode.Default;
+ else this.mousemode = value;
+ switch (this.mousemode)
+ {
+ case eMouseMode.pan: this.Cursor = Cursors.Hand; break;
+ case eMouseMode.addrfidline: this.Cursor = Cursors.Default; break;
+ case eMouseMode.addrfidpoint: this.Cursor = Cursors.Default; break;
+ case eMouseMode.addtext: this.Cursor = Cursors.Default; break;
+ case eMouseMode.addcustomline: this.Cursor = Cursors.Default; break;
+ default: this.Cursor = Cursors.Default; break;
+ }
+ previewStartPoint = null;
+ Invalidate();
+ }
+ }
+
+
+ public void SetIsAddingMagnet(bool value)
+ {
+
+ }
+
+ public void SetIsAddingPoint(bool value)
+ {
+ isDrawingCustomLine = false;
+ isDrawingLine = false;
+ isDrawingMagnetLine = false;
+ isDrawingRFIDLine = false;
+ isAddingPoint = value;
+ this.Cursor = value ? Cursors.Cross : Cursors.Default;
+ }
+
+ private Cursor GetScissorsCursor()
+ {
+ // 가위 커서 아이콘 생성
+ using (var bitmap = new Bitmap(32, 32))
+ using (var g = Graphics.FromImage(bitmap))
+ {
+ g.Clear(Color.Transparent);
+
+ // 가위 모양 그리기
+ using (var pen = new Pen(Color.Black, 2))
+ {
+ // 가위 손잡이
+ g.DrawEllipse(pen, 12, 20, 8, 8);
+ g.DrawLine(pen, 16, 20, 16, 16);
+
+ // 가위 날
+ g.DrawLine(pen, 16, 16, 8, 8);
+ g.DrawLine(pen, 16, 16, 24, 8);
+ }
+
+ return new Cursor(bitmap.GetHicon());
+ }
+ }
+
+ public void DrawPath(List path)
+ {
+ currentPath = path;
+ this.Invalidate();
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ //base.OnPaint(e);
+
+
+
+ e.Graphics.TranslateTransform(offset.X, offset.Y);
+ e.Graphics.ScaleTransform(zoom, zoom);
+
+ DrawMap(e.Graphics);
+ DrawCustomLines(e.Graphics);
+ DrawMapTexts(e.Graphics);
+ DrawRFIDLines(e.Graphics);
+ DrawPath(e.Graphics);
+ DrawAGV(e.Graphics);
+
+ // 선택된 개체 강조 표시
+ if (selectedRFID != null)
+ {
+ using (Pen pen = new Pen(Color.Blue, 2))
+ {
+ pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
+ e.Graphics.DrawEllipse(pen,
+ selectedRFID.Location.X - 10,
+ selectedRFID.Location.Y - 10,
+ 20, 20);
+ }
+ }
+
+ if (selectedMagnetLine != null)
+ {
+ using (Pen pen = new Pen(Color.Blue, 2))
+ {
+ pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
+ e.Graphics.DrawLine(pen, selectedMagnetLine.StartPoint, selectedMagnetLine.EndPoint);
+ foreach (var branch in selectedMagnetLine.BranchPoints)
+ {
+ e.Graphics.DrawEllipse(pen, branch.X - 5, branch.Y - 5, 10, 10);
+ }
+ }
+ }
+
+ if (selectedLine != null)
+ {
+ using (Pen pen = new Pen(Color.Blue, 2))
+ {
+ pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
+ e.Graphics.DrawLine(pen, selectedLine.StartPoint, selectedLine.EndPoint);
+ }
+ }
+
+ // 미리보기 라인 그리기
+ if (previewStartPoint.HasValue)
+ {
+ using (Pen previewPen = new Pen(Color.FromArgb(180, Color.Yellow), LINE_WIDTH))
+ {
+ previewPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
+ e.Graphics.DrawLine(previewPen, previewStartPoint.Value, currentMousePosition);
+ }
+ }
+
+ // 그래픽스 변환 초기화
+ e.Graphics.ResetTransform();
+
+ // 툴바 버튼 그리기
+ foreach (var item in this.toolbarRects)
+ DrawToolbarButton(e.Graphics, item.Bounds, item.Title, item.isHovering);
+
+
+ }
+
+
+ private void DrawMap(Graphics g)
+ {
+ //// 마그넷 라인 그리기
+ //foreach (var line in magnetLines)
+ //{
+ // using (Pen linePen = new Pen(Color.FromArgb(180, Color.Yellow), LINE_WIDTH))
+ // {
+ // g.DrawLine(linePen, line.StartPoint, line.EndPoint);
+ // }
+
+ // // 분기점 그리기
+ // foreach (var branch in line.BranchPoints)
+ // {
+ // g.FillEllipse(Brushes.Red, branch.X - 3, branch.Y - 3, 6, 6);
+ // var direction = line.BranchDirections[branch];
+ // g.DrawString(direction.ToString(), Font, Brushes.Black, branch.X + 5, branch.Y - 5);
+ // }
+ //}
+
+
+
+ // RFID 포인트 그리기
+ foreach (var rfid in rfidPoints)
+ {
+ g.FillEllipse(Brushes.Green, rfid.Location.X - 3, rfid.Location.Y - 3, 6, 6);
+ g.DrawString(rfid.RFIDValue, Font, Brushes.Black, rfid.Location.X + 5, rfid.Location.Y - 5);
+ }
+
+ //var rfidpts = rfidPoints.Select(t => t.Location).ToArray();
+ //g.DrawLines(new Pen(Color.FromArgb(100, Color.Magenta), 10), rfidpts);
+
+ }
+
+ private void DrawAGV(Graphics g)
+ {
+ var agvsize = 30;
+ var halfsize = (int)(agvsize / 2);
+ var rect = new Rectangle(agv.CurrentPosition.X - halfsize,
+ agv.CurrentPosition.Y - halfsize,
+ agvsize, agvsize);
+
+ var recti = new Rectangle(rect.X + 5, rect.Y + 5, rect.Width - 10, rect.Height - 10);
+
+ using (var sb = new SolidBrush(Color.FromArgb(150, Color.Gold)))
+ g.FillEllipse(sb, rect);
+
+ using (var pen = new Pen(Color.Black))
+ g.DrawEllipse(pen, rect);
+
+ using (var sb = new SolidBrush(Color.FromArgb(150, Color.White)))
+ g.FillEllipse(sb, recti);
+
+ using (var pen = new Pen(Color.Black))
+ g.DrawEllipse(pen, recti);
+ }
+
+ private void DrawCustomLines(Graphics g)
+ {
+ if (customLines == null) return;
+ foreach (var line in customLines)
+ {
+ using (Pen linePen = new Pen(line.LineColor, line.LineWidth))
+ {
+ g.DrawLine(linePen, line.StartPoint, line.EndPoint);
+ }
+ }
+ }
+
+ private void DrawMapTexts(Graphics g)
+ {
+ if (mapTexts == null) return;
+ foreach (var text in mapTexts)
+ {
+ var textSize = g.MeasureString(text.Text, text.Font);
+ var rect = new RectangleF(
+ text.Location.X,
+ text.Location.Y,
+ textSize.Width,
+ textSize.Height
+ );
+
+ if (text.BackgroundColor != Color.Transparent)
+ {
+ using (var brush = new SolidBrush(text.BackgroundColor))
+ {
+ g.FillRectangle(brush, rect);
+ }
+ }
+
+ using (var brush = new SolidBrush(text.TextColor))
+ {
+ g.DrawString(text.Text, text.Font, brush, text.Location);
+ }
+
+ if (text == selectedText)
+ {
+ using (Pen pen = new Pen(Color.Blue, 1))
+ {
+ pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
+ g.DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height);
+ }
+ }
+ }
+ }
+
+ private void DrawRFIDLines(Graphics g)
+ {
+ foreach (var line in rfidLines)
+ {
+ using (Pen linePen = new Pen(Color.FromArgb(100, Color.Black), 2))
+ {
+ if (!line.IsBidirectional)
+ {
+ // 단방향 화살표 그리기
+ var arrowSize = 10;
+ var angle = Math.Atan2(line.EndPoint.Y - line.StartPoint.Y, line.EndPoint.X - line.StartPoint.X);
+ var arrowPoint = new PointF(
+ line.EndPoint.X - (float)(arrowSize * Math.Cos(angle)),
+ line.EndPoint.Y - (float)(arrowSize * Math.Sin(angle))
+ );
+ g.DrawLine(linePen, line.StartPoint, arrowPoint);
+
+ // 화살표 머리 그리기
+ var arrowAngle = Math.PI / 6;
+ var arrowLength = 15;
+ var arrow1 = new PointF(
+ arrowPoint.X - (float)(arrowLength * Math.Cos(angle - arrowAngle)),
+ arrowPoint.Y - (float)(arrowLength * Math.Sin(angle - arrowAngle))
+ );
+ var arrow2 = new PointF(
+ arrowPoint.X - (float)(arrowLength * Math.Cos(angle + arrowAngle)),
+ arrowPoint.Y - (float)(arrowLength * Math.Sin(angle + arrowAngle))
+ );
+ g.DrawLine(linePen, arrowPoint, arrow1);
+ g.DrawLine(linePen, arrowPoint, arrow2);
+ }
+ else
+ {
+ g.DrawLine(linePen, line.StartPoint, line.EndPoint);
+ }
+ }
+ }
+
+ // 미리보기 라인 그리기
+ if (previewStartPoint.HasValue && isDrawingRFIDLine)
+ {
+ using (Pen previewPen = new Pen(Color.FromArgb(180, Color.Green), 2))
+ {
+ previewPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
+ var currentMapPosition = ScreenToMap(currentMousePosition);
+ g.DrawLine(previewPen, previewStartPoint.Value, currentMapPosition);
+ }
+ }
+ }
+
+ private void DrawPath(Graphics g)
+ {
+ if (currentPath == null || currentPath.Count < 2)
+ return;
+
+ Color pathColor = Color.FromArgb(180, Color.HotPink);
+ int pathWidth = 10;
+
+ using (Pen pathPen = new Pen(pathColor, pathWidth))
+ {
+ pathPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
+ for (int i = 0; i < currentPath.Count - 1; i++)
+ {
+ g.DrawLine(pathPen, currentPath[i], currentPath[i + 1]);
+ }
+ }
+ }
+
+ public List GetRFIDPoints()
+ {
+ return rfidPoints;
+ }
+
+ public List GetMagnetLines()
+ {
+ return magnetLines;
+ }
+
+ public List GetMapTexts()
+ {
+ return mapTexts;
+ }
+
+ public List GetCustomLines()
+ {
+ return customLines;
+ }
+
+ public List GetRFIDLines()
+ {
+ return rfidLines;
+ }
+
+ public void SetRFIDLines(List lines)
+ {
+ rfidLines = lines;
+ this.Invalidate();
+ }
+
+ public void AddRFIDLine(Point startPoint, Point endPoint, string startRFID, string endRFID, bool isBidirectional = true)
+ {
+ // 시작점과 끝점 사이의 모든 RFID 포인트 찾기
+ var allPoints = new List<(RFIDPoint Point, float Distance)>();
+ var lineVector = new Point(endPoint.X - startPoint.X, endPoint.Y - startPoint.Y);
+ var lineLength = (float)Math.Sqrt(lineVector.X * lineVector.X + lineVector.Y * lineVector.Y);
+
+ foreach (var rfid in rfidPoints)
+ {
+ if (rfid.Location == startPoint || rfid.Location == endPoint)
+ continue;
+
+ // RFID 포인트가 선 위에 있는지 확인
+ var pointVector = new Point(rfid.Location.X - startPoint.X, rfid.Location.Y - startPoint.Y);
+ var dotProduct = pointVector.X * lineVector.X + pointVector.Y * lineVector.Y;
+ var projectionLength = dotProduct / (lineLength * lineLength);
+
+ if (projectionLength >= 0 && projectionLength <= 1)
+ {
+ // 선과 RFID 포인트 사이의 거리 계산
+ var distance = GetDistanceToLine(rfid.Location, startPoint, endPoint);
+ if (distance <= SNAP_DISTANCE)
+ {
+ allPoints.Add((rfid, projectionLength));
+ }
+ }
+ }
+
+ // 시작점에서 끝점 방향으로 정렬
+ allPoints.Sort((a, b) => a.Distance.CompareTo(b.Distance));
+
+ // 모든 연결 정보를 포함하는 RFID 라인 생성
+ var line = new RFIDLine
+ {
+ StartPoint = startPoint,
+ EndPoint = endPoint,
+ StartRFID = startRFID,
+ EndRFID = endRFID,
+ IsBidirectional = isBidirectional,
+ Distance = GetDistance(startPoint, endPoint)
+ };
+
+ // 연결된 RFID 목록 생성
+ line.ConnectedRFIDs.Add(startRFID);
+ foreach (var point in allPoints)
+ {
+ line.ConnectedRFIDs.Add(point.Point.RFIDValue);
+ }
+ line.ConnectedRFIDs.Add(endRFID);
+
+ // 다음/이전 RFID 정보 설정
+ for (int i = 0; i < line.ConnectedRFIDs.Count - 1; i++)
+ {
+ var current = line.ConnectedRFIDs[i];
+ var next = line.ConnectedRFIDs[i + 1];
+ line.NextRFID[current] = next;
+ line.PrevRFID[next] = current;
+
+ if (isBidirectional)
+ {
+ line.NextRFID[next] = current;
+ line.PrevRFID[current] = next;
+ }
+ }
+
+ rfidLines.Add(line);
+ this.Invalidate();
+ }
+
+ public void LoadMapData(MapData mapData)
+ {
+ rfidPoints = mapData.RFIDPoints ?? new List();
+ magnetLines = mapData.MagnetLines ?? new List();
+ mapTexts = mapData.MapTexts ?? new List();
+ customLines = mapData.CustomLines ?? new List();
+ rfidLines = mapData.RFIDLines ?? new List();
+ this.Invalidate();
+ }
+
+ public void ClearMap()
+ {
+ rfidPoints.Clear();
+ magnetLines.Clear();
+ mapTexts.Clear();
+ customLines.Clear();
+ rfidLines.Clear();
+
+ // 선택 상태도 초기화
+ selectedText = null;
+ selectedLine = null;
+ selectedRFID = null;
+ selectedMagnetLine = null;
+ selectedRFIDLine = null;
+ draggingPoint = null;
+
+ // 미리보기 상태도 초기화
+ previewStartPoint = null;
+
+ // 화면 갱신
+ this.Invalidate();
+ }
+
+ public void AddRFIDPoint(Point mapLocation, string rfidValue)
+ {
+ // 이미 맵 좌표로 변환된 위치를 사용
+ var rfidPoint = new RFIDPoint
+ {
+ Location = mapLocation,
+ RFIDValue = rfidValue
+ };
+
+ rfidPoints.Add(rfidPoint);
+ this.Invalidate();
+ }
+
+ public void AddMagnetLine(Point startPoint, Point endPoint, Point? branchPoint = null, BranchDirection? branchDirection = null)
+ {
+ // 이미 맵 좌표로 변환된 위치를 받아서 사용
+ var line = new MagnetLine
+ {
+ StartPoint = startPoint,
+ EndPoint = endPoint
+ };
+
+ if (branchPoint.HasValue && branchDirection.HasValue)
+ {
+ line.BranchPoints.Add(branchPoint.Value);
+ line.BranchDirections[branchPoint.Value] = branchDirection.Value;
+ }
+
+ magnetLines.Add(line);
+ this.Invalidate();
+ }
+
+ public void SaveToFile(string filename)
+ {
+ var lines = new List();
+
+ // RFID 포인트 저장
+ lines.Add("[RFID_POINTS]");
+ foreach (var point in rfidPoints)
+ {
+ lines.Add($"{point.Location.X},{point.Location.Y},{point.RFIDValue}");
+ }
+
+ // 자석선 저장
+ lines.Add("[MAGNET_LINES]");
+ foreach (var line in magnetLines)
+ {
+ var branchInfo = "";
+ foreach (var branch in line.BranchPoints)
+ {
+ if (line.BranchDirections.ContainsKey(branch))
+ {
+ branchInfo += $"|{branch.X},{branch.Y},{(int)line.BranchDirections[branch]}";
+ }
+ }
+ lines.Add($"{line.StartPoint.X},{line.StartPoint.Y},{line.EndPoint.X},{line.EndPoint.Y}{branchInfo}");
+ }
+
+ // 텍스트 저장
+ lines.Add("[MAP_TEXTS]");
+ foreach (var text in mapTexts)
+ {
+ lines.Add($"{text.Location.X},{text.Location.Y},{text.TextColor.ToArgb()},{text.BackgroundColor.ToArgb()},{text.Font.Name},{text.Font.Size},{text.Text}");
+ }
+
+ // 커스텀 라인 저장
+ lines.Add("[CUSTOM_LINES]");
+ foreach (var line in customLines)
+ {
+ lines.Add($"{line.StartPoint.X},{line.StartPoint.Y},{line.EndPoint.X},{line.EndPoint.Y},{line.LineColor.ToArgb()},{line.LineWidth}");
+ }
+
+ // RFID 라인 저장
+ lines.Add("[RFID_LINES]");
+ foreach (var line in rfidLines)
+ {
+ lines.Add($"{line.StartPoint.X},{line.StartPoint.Y},{line.EndPoint.X},{line.EndPoint.Y}," +
+ $"{line.StartRFID},{line.EndRFID},{line.IsBidirectional},{line.Distance}");
+ }
+
+ File.WriteAllLines(filename, lines);
+ }
+
+ public void LoadFromFile(string filename)
+ {
+ ClearMap();
+
+ var lines = File.ReadAllLines(filename);
+ var section = "";
+
+ foreach (var line in lines)
+ {
+ if (line.StartsWith("[") && line.EndsWith("]"))
+ {
+ section = line;
+ continue;
+ }
+
+ switch (section)
+ {
+ case "[RFID_POINTS]":
+ var rfidParts = line.Split(',');
+ if (rfidParts.Length >= 3)
+ {
+ AddRFIDPoint(
+ new Point(int.Parse(rfidParts[0]), int.Parse(rfidParts[1])),
+ rfidParts[2]
+ );
+ }
+ break;
+
+ case "[MAGNET_LINES]":
+ var lineParts = line.Split('|');
+ var mainParts = lineParts[0].Split(',');
+ if (mainParts.Length >= 4)
+ {
+ var magnetLine = new MagnetLine
+ {
+ StartPoint = new Point(int.Parse(mainParts[0]), int.Parse(mainParts[1])),
+ EndPoint = new Point(int.Parse(mainParts[2]), int.Parse(mainParts[3]))
+ };
+
+ // 분기점 정보 처리
+ for (int i = 1; i < lineParts.Length; i++)
+ {
+ var branchParts = lineParts[i].Split(',');
+ if (branchParts.Length >= 3)
+ {
+ var branchPoint = new Point(
+ int.Parse(branchParts[0]),
+ int.Parse(branchParts[1])
+ );
+ magnetLine.BranchPoints.Add(branchPoint);
+ magnetLine.BranchDirections[branchPoint] = (BranchDirection)int.Parse(branchParts[2]);
+ }
+ }
+ magnetLines.Add(magnetLine);
+ }
+ break;
+
+ case "[MAP_TEXTS]":
+ var textParts = line.Split(',');
+ if (textParts.Length >= 7)
+ {
+ var text = new MapText
+ {
+ Location = new Point(int.Parse(textParts[0]), int.Parse(textParts[1])),
+ TextColor = Color.FromArgb(int.Parse(textParts[2])),
+ BackgroundColor = Color.FromArgb(int.Parse(textParts[3])),
+ Font = new Font(textParts[4], float.Parse(textParts[5])),
+ Text = string.Join(",", textParts.Skip(6)) // 텍스트에 쉼표가 포함될 수 있으므로
+ };
+ mapTexts.Add(text);
+ }
+ break;
+
+ case "[CUSTOM_LINES]":
+ var customLineParts = line.Split(',');
+ if (customLineParts.Length >= 6)
+ {
+ var customLine = new CustomLine
+ {
+ StartPoint = new Point(int.Parse(customLineParts[0]), int.Parse(customLineParts[1])),
+ EndPoint = new Point(int.Parse(customLineParts[2]), int.Parse(customLineParts[3])),
+ LineColor = Color.FromArgb(int.Parse(customLineParts[4])),
+ LineWidth = int.Parse(customLineParts[5])
+ };
+ customLines.Add(customLine);
+ }
+ break;
+
+ case "[RFID_LINES]":
+ var rfidLineParts = line.Split(',');
+ if (rfidLineParts.Length >= 8)
+ {
+ AddRFIDLine(
+ new Point(int.Parse(rfidLineParts[0]), int.Parse(rfidLineParts[1])),
+ new Point(int.Parse(rfidLineParts[2]), int.Parse(rfidLineParts[3])),
+ rfidLineParts[4],
+ rfidLineParts[5],
+ bool.Parse(rfidLineParts[6])
+ );
+ }
+ break;
+ }
+ }
+
+ this.Invalidate();
+ }
+
+ private void DeleteNearbyRFIDLine(Point clickPoint)
+ {
+ const float DELETE_DISTANCE = 10.0f; // 클릭 지점으로부터의 허용 거리
+ RFIDLine lineToDelete = null;
+ float minDistance = float.MaxValue;
+
+ foreach (var line in rfidLines)
+ {
+ float distance = GetDistanceToLine(clickPoint, line.StartPoint, line.EndPoint);
+ if (distance < DELETE_DISTANCE && distance < minDistance)
+ {
+ minDistance = distance;
+ lineToDelete = line;
+ }
+ }
+
+ if (lineToDelete != null)
+ {
+ rfidLines.Remove(lineToDelete);
+ this.Invalidate();
+ }
+ }
+
+ private void ZoomIn()
+ {
+ zoom *= 1.2f;
+ zoom = Math.Min(10.0f, zoom);
+ this.Invalidate();
+ }
+
+ private void ZoomOut()
+ {
+ zoom /= 1.2f;
+ zoom = Math.Max(0.1f, zoom);
+ this.Invalidate();
+ }
+
+ private void ResetZoom()
+ {
+ zoom = 1.0f;
+ offset = PointF.Empty;
+ this.Invalidate();
+ }
+
+ private void DrawToolbarButton(Graphics g, Rectangle rect, string text, bool isHovering)
+ {
+ var color1 = isHovering ? Color.LightSkyBlue : Color.White;
+ var color2 = isHovering ? Color.DeepSkyBlue : Color.WhiteSmoke;
+ using (var brush = new LinearGradientBrush(rect, color1, color2, LinearGradientMode.Vertical))
+ using (var pen = new Pen(Color.Gray))
+ using (var font = new Font("Tahoma", 9, FontStyle.Bold))
+ using (var format = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center })
+ {
+ g.FillRectangle(Brushes.LightGray, rect.X + 2, rect.Y + 2, rect.Width, rect.Height);
+ g.FillRectangle(brush, rect);
+ g.DrawRectangle(pen, rect);
+ g.DrawString(text, font, Brushes.Black, rect, format);
+ }
+ }
+ }
+}
diff --git a/Cs_HMI/Project/StateMachine/_AGV.resx b/Cs_HMI/SubProject/AGVMapControl/MapControl.resx
similarity index 100%
rename from Cs_HMI/Project/StateMachine/_AGV.resx
rename to Cs_HMI/SubProject/AGVMapControl/MapControl.resx
diff --git a/Cs_HMI/SubProject/AGVMapControl/Models/AGV.cs b/Cs_HMI/SubProject/AGVMapControl/Models/AGV.cs
new file mode 100644
index 0000000..8fba0a4
--- /dev/null
+++ b/Cs_HMI/SubProject/AGVMapControl/Models/AGV.cs
@@ -0,0 +1,70 @@
+using System.Drawing;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace AGVMapControl.Models
+{
+ public class AGV
+ {
+ public Point CurrentPosition { get; set; }
+ public string CurrentRFID { get; set; }
+ public Direction CurrentDirection { get; set; }
+ public bool IsMoving { get; set; }
+ public List CurrentPath { get; set; }
+ public List PlannedPath { get; set; }
+ public List PathRFIDs { get; set; }
+
+ public AGV()
+ {
+ CurrentPath = new List();
+ PlannedPath = new List();
+ PathRFIDs = new List();
+ CurrentDirection = Direction.Forward;
+ }
+
+ public void Move()
+ {
+ if (CurrentPath.Count > 0)
+ {
+ CurrentPosition = CurrentPath[0];
+ CurrentPath.RemoveAt(0);
+ }
+ }
+
+ public void SetNewPath(List path)
+ {
+ CurrentPath = new List(path);
+ }
+
+ public void ClearPlannedPath()
+ {
+ PlannedPath.Clear();
+ PathRFIDs.Clear();
+ }
+ }
+
+ public enum Direction
+ {
+ Forward,
+ Backward
+ }
+
+ public class PathNode
+ {
+ public Point Location { get; set; }
+ public string RFID { get; set; }
+ public double G { get; set; } // 시작점에서 현재 노드까지의 비용
+ public double H { get; set; } // 현재 노드에서 목표점까지의 예상 비용
+ public double F => G + H; // 총 비용
+ public PathNode Parent { get; set; }
+
+ public PathNode(Point location, string rfid)
+ {
+ Location = location;
+ RFID = rfid;
+ G = 0;
+ H = 0;
+ Parent = null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Cs_HMI/SubProject/AGVMapControl/Models/CustomLine.cs b/Cs_HMI/SubProject/AGVMapControl/Models/CustomLine.cs
new file mode 100644
index 0000000..73b348d
--- /dev/null
+++ b/Cs_HMI/SubProject/AGVMapControl/Models/CustomLine.cs
@@ -0,0 +1,14 @@
+using System.Drawing;
+using System;
+
+
+namespace AGVMapControl.Models
+{
+ public class CustomLine
+ {
+ public Point StartPoint { get; set; }
+ public Point EndPoint { get; set; }
+ public Color LineColor { get; set; }
+ public int LineWidth { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Cs_HMI/SubProject/AGVMapControl/Models/MagnetLine.cs b/Cs_HMI/SubProject/AGVMapControl/Models/MagnetLine.cs
new file mode 100644
index 0000000..f731a93
--- /dev/null
+++ b/Cs_HMI/SubProject/AGVMapControl/Models/MagnetLine.cs
@@ -0,0 +1,20 @@
+using System.Drawing;
+using System;
+using System.Collections.Generic;
+
+namespace AGVMapControl.Models
+{
+ public class MagnetLine
+ {
+ public Point StartPoint { get; set; }
+ public Point EndPoint { get; set; }
+ public List BranchPoints { get; set; }
+ public Dictionary BranchDirections { get; set; }
+
+ public MagnetLine()
+ {
+ BranchPoints = new List();
+ BranchDirections = new Dictionary();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Cs_HMI/SubProject/AGVMapControl/Models/MapData.cs b/Cs_HMI/SubProject/AGVMapControl/Models/MapData.cs
new file mode 100644
index 0000000..e32bf12
--- /dev/null
+++ b/Cs_HMI/SubProject/AGVMapControl/Models/MapData.cs
@@ -0,0 +1,14 @@
+using System.Drawing;
+using System;
+using System.Collections.Generic;
+namespace AGVMapControl.Models
+{
+ public class MapData
+ {
+ public List RFIDPoints { get; set; } = new List();
+ public List MagnetLines { get; set; } = new List();
+ public List MapTexts { get; set; } = new List();
+ public List CustomLines { get; set; } = new List();
+ public List RFIDLines { get; set; } = new List();
+ }
+}
\ No newline at end of file
diff --git a/Cs_HMI/SubProject/AGVMapControl/Models/MapElements.cs b/Cs_HMI/SubProject/AGVMapControl/Models/MapElements.cs
new file mode 100644
index 0000000..2157d90
--- /dev/null
+++ b/Cs_HMI/SubProject/AGVMapControl/Models/MapElements.cs
@@ -0,0 +1,13 @@
+using System.Drawing;
+using System;
+using System.Collections.Generic;
+
+namespace AGVMapControl.Models
+{
+ public enum BranchDirection
+ {
+ Left,
+ Straight,
+ Right
+ }
+}
\ No newline at end of file
diff --git a/Cs_HMI/SubProject/AGVMapControl/Models/MapText.cs b/Cs_HMI/SubProject/AGVMapControl/Models/MapText.cs
new file mode 100644
index 0000000..1404242
--- /dev/null
+++ b/Cs_HMI/SubProject/AGVMapControl/Models/MapText.cs
@@ -0,0 +1,14 @@
+using System.Drawing;
+using System;
+using System.Collections.Generic;
+namespace AGVMapControl.Models
+{
+ public class MapText
+ {
+ public Point Location { get; set; }
+ public string Text { get; set; }
+ public Color TextColor { get; set; }
+ public Color BackgroundColor { get; set; }
+ public Font Font { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Cs_HMI/SubProject/AGVMapControl/Models/RFIDLine.cs b/Cs_HMI/SubProject/AGVMapControl/Models/RFIDLine.cs
new file mode 100644
index 0000000..3337cde
--- /dev/null
+++ b/Cs_HMI/SubProject/AGVMapControl/Models/RFIDLine.cs
@@ -0,0 +1,20 @@
+using System.Drawing;
+using System;
+using System.Collections.Generic;
+
+namespace AGVMapControl.Models
+{
+ public class RFIDLine
+ {
+ public Point StartPoint { get; set; }
+ public Point EndPoint { get; set; }
+ public string StartRFID { get; set; }
+ public string EndRFID { get; set; }
+ public bool IsBidirectional { get; set; } = true; // 양방향 이동 가능 여부
+ public float Distance { get; set; } // 두 RFID 포인트 사이의 거리
+
+ public List ConnectedRFIDs { get; set; } = new List(); // 연결된 모든 RFID 값들
+ public Dictionary NextRFID { get; set; } = new Dictionary(); // 각 RFID의 다음 RFID
+ public Dictionary PrevRFID { get; set; } = new Dictionary(); // 각 RFID의 이전 RFID
+ }
+}
\ No newline at end of file
diff --git a/Cs_HMI/SubProject/AGVMapControl/Models/RFIDPoint.cs b/Cs_HMI/SubProject/AGVMapControl/Models/RFIDPoint.cs
new file mode 100644
index 0000000..5939805
--- /dev/null
+++ b/Cs_HMI/SubProject/AGVMapControl/Models/RFIDPoint.cs
@@ -0,0 +1,13 @@
+using System.Drawing;
+using System;
+using System.Collections.Generic;
+namespace AGVMapControl.Models
+{
+ public class RFIDPoint
+ {
+ public Point Location { get; set; }
+ public string RFIDValue { get; set; }
+ public string NextRFID { get; set; } // 다음 RFID 포인트의 값
+ public bool IsBidirectional { get; set; } // 양방향 연결 여부
+ }
+}
\ No newline at end of file
diff --git a/Cs_HMI/SubProject/AGVMapControl/Models/ToolBarItem.cs b/Cs_HMI/SubProject/AGVMapControl/Models/ToolBarItem.cs
new file mode 100644
index 0000000..235b67c
--- /dev/null
+++ b/Cs_HMI/SubProject/AGVMapControl/Models/ToolBarItem.cs
@@ -0,0 +1,34 @@
+using System.Drawing;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace AGVMapControl.Models
+{
+ public class ToolBarItem
+ {
+ private bool _ishovering = false;
+ public int Idx { get; set; }
+ public Rectangle Bounds { get; set; }
+ public bool isHovering
+ {
+ get { return _ishovering; }
+ set
+ {
+ Dirty = _ishovering != value;
+ _ishovering = value;
+ }
+ }
+
+ public string Title { get; set; }
+ public bool Dirty { get; private set; }
+
+ public ToolBarItem()
+ {
+ Bounds = Rectangle.Empty;
+ }
+
+
+ }
+
+
+}
\ No newline at end of file