Compare commits
80 Commits
9776205364
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18ee01f7bc | ||
|
|
49c40fd371 | ||
|
|
30e1ce41ee | ||
|
|
1a4b8a6a54 | ||
|
|
6802967cde | ||
|
|
46bed6eb25 | ||
|
|
43e7458866 | ||
|
|
03a53d49bf | ||
|
|
71b8a589c6 | ||
|
|
24bd2d8a7f | ||
|
|
e831752e76 | ||
|
|
c1670ddcbe | ||
|
|
d57b00095c | ||
|
|
2e9b7973c7 | ||
|
|
1948eda7ea | ||
|
|
8beaa66516 | ||
|
|
c067a76462 | ||
|
|
e7cce4c201 | ||
|
|
0e2b407e48 | ||
|
|
e2691af903 | ||
|
|
2590ad91de | ||
|
|
245749d695 | ||
|
|
6a658879f1 | ||
|
|
12b3fe50c7 | ||
|
|
dbc53e3146 | ||
|
|
213467fe3f | ||
|
|
d6aed58516 | ||
|
|
2b3a9b3d1d | ||
|
|
a512133d52 | ||
|
|
471b8ff9c4 | ||
|
|
161ff5c3e2 | ||
|
|
c2cc5d67ae | ||
|
|
7409528fbd | ||
|
|
3d5e2bc1e6 | ||
|
|
bd06f59bf1 | ||
|
|
839486db87 | ||
|
|
35a057367c | ||
|
|
09b1246bbd | ||
|
|
b57c61c6d8 | ||
|
|
f5f08de0b9 | ||
|
|
ddaab0b5da | ||
|
|
ba542beaff | ||
|
|
ec2af6ac1f | ||
|
|
b388b1917d | ||
|
|
e35dee853f | ||
|
|
b25be68986 | ||
|
|
9bca8f67d1 | ||
|
|
02105d49a3 | ||
|
|
faf13f5c37 | ||
|
|
3a8cbd3283 | ||
|
|
1dde80fa93 | ||
|
|
af0c32215b | ||
|
|
92340308be | ||
|
|
cce51478be | ||
|
|
e99edbe04d | ||
|
|
1da1f2de28 | ||
|
|
58ca67150d | ||
|
|
00cc0ef5b7 | ||
|
|
16d51a2712 | ||
|
|
5b4fdd33cf | ||
|
|
ffa6c2fb23 | ||
|
|
b0e75b351a | ||
|
|
b4d3cd8bb5 | ||
|
|
d8a9007791 | ||
|
|
9ee8295489 | ||
|
|
a04a0505d0 | ||
|
|
3b2c1a43a7 | ||
|
|
00c4663ebc | ||
|
|
3044894e5c | ||
|
|
ddf111c69f | ||
|
|
64ff2ecfb9 | ||
|
|
82592e117d | ||
|
|
0396c61517 | ||
|
|
b4da1cca00 | ||
|
|
48acb5b7b7 | ||
|
|
d5516f9708 | ||
|
|
5801137d63 | ||
|
|
880dc526da | ||
|
|
efab3d042c | ||
|
|
b84f8c7d2d |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -15,3 +15,5 @@ packages
|
|||||||
*.bak
|
*.bak
|
||||||
/Cs_HMI/Data/*.agvmap
|
/Cs_HMI/Data/*.agvmap
|
||||||
/Cs_HMI/AGVLogic/AGVMapEditor/Data/*.agvmap
|
/Cs_HMI/AGVLogic/AGVMapEditor/Data/*.agvmap
|
||||||
|
/Document/~$PICkit 프로그램 다운로드 매뉴얼.pptx
|
||||||
|
/HMI/TestProject/tts/assets
|
||||||
|
|||||||
@@ -38,14 +38,12 @@
|
|||||||
<ApplicationIcon>icons8-robot-80.ico</ApplicationIcon>
|
<ApplicationIcon>icons8-robot-80.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="arCommUtil, Version=25.11.25.2000, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\DLL\arCommUtil.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="arControl.Net4">
|
<Reference Include="arControl.Net4">
|
||||||
<HintPath>..\Cs_HMI\DLL\arControl.Net4.dll</HintPath>
|
<HintPath>..\DLL\arControl.Net4.dll</HintPath>
|
||||||
</Reference>
|
|
||||||
<Reference Include="ArLog.Net4">
|
|
||||||
<HintPath>..\Cs_HMI\DLL\ArLog.Net4.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="ArSetting.Net4">
|
|
||||||
<HintPath>..\Cs_HMI\DLL\ArSetting.Net4.dll</HintPath>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
@@ -70,16 +68,13 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="RemoteStatus.cs" />
|
||||||
<Compile Include="RS232.cs" />
|
<Compile Include="RS232.cs" />
|
||||||
<Compile Include="RunCode\_AGV.cs">
|
<Compile Include="RunCode\_AGV.cs" />
|
||||||
<SubType>Form</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="RunCode\_XBEE.cs">
|
<Compile Include="RunCode\_XBEE.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="RunCode\_BMS.cs">
|
<Compile Include="RunCode\_BMS.cs" />
|
||||||
<SubType>Form</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="UC\SerialConn.cs">
|
<Compile Include="UC\SerialConn.cs">
|
||||||
<SubType>UserControl</SubType>
|
<SubType>UserControl</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -126,15 +121,11 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Cs_HMI\AGVLogic\AGVNavigationCore\AGVNavigationCore.csproj">
|
<ProjectReference Include="..\AGVLogic\AGVNavigationCore\AGVNavigationCore.csproj">
|
||||||
<Project>{c5f7a8b2-8d3e-4a1b-9c6e-7f4d5e2a9b1c}</Project>
|
<Project>{c5f7a8b2-8d3e-4a1b-9c6e-7f4d5e2a9b1c}</Project>
|
||||||
<Name>AGVNavigationCore</Name>
|
<Name>AGVNavigationCore</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\Cs_HMI\SubProject\CommUtil\arCommUtil.csproj">
|
<ProjectReference Include="..\AGVLogic\ENIGProtocol\enigprotocol\ENIGProtocol.csproj">
|
||||||
<Project>{14e8c9a5-013e-49ba-b435-ffffff7dd623}</Project>
|
|
||||||
<Name>arCommUtil</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
<ProjectReference Include="..\Cs_HMI\SubProject\EnigProtocol\enigprotocol\ENIGProtocol.csproj">
|
|
||||||
<Project>{9365803b-933d-4237-93c7-b502c855a71c}</Project>
|
<Project>{9365803b-933d-4237-93c7-b502c855a71c}</Project>
|
||||||
<Name>ENIGProtocol</Name>
|
<Name>ENIGProtocol</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
|||||||
@@ -5,11 +5,9 @@ VisualStudioVersion = 17.14.36804.6 d17.14
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVEmulator", "AGVEmulator.csproj", "{9312AB43-72F6-4365-A266-E767215FA7F5}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVEmulator", "AGVEmulator.csproj", "{9312AB43-72F6-4365-A266-E767215FA7F5}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVNavigationCore", "..\Cs_HMI\AGVLogic\AGVNavigationCore\AGVNavigationCore.csproj", "{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVNavigationCore", "..\AGVLogic\AGVNavigationCore\AGVNavigationCore.csproj", "{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ENIGProtocol", "..\Cs_HMI\SubProject\ENIGProtocol\enigprotocol\ENIGProtocol.csproj", "{9365803B-933D-4237-93C7-B502C855A71C}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ENIGProtocol", "..\AGVLogic\ENIGProtocol\enigprotocol\ENIGProtocol.csproj", "{9365803B-933D-4237-93C7-B502C855A71C}"
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "arCommUtil", "..\Cs_HMI\SubProject\CommUtil\arCommUtil.csproj", "{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}"
|
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using arCtl;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -15,7 +14,8 @@ namespace AGVEmulator
|
|||||||
public UInt16 system0 = 0;
|
public UInt16 system0 = 0;
|
||||||
public UInt16 system1 = 0;
|
public UInt16 system1 = 0;
|
||||||
public UInt16 error = 0;
|
public UInt16 error = 0;
|
||||||
public byte signal = 0;
|
public byte signal1 = 0;
|
||||||
|
public byte signal2 = 0;
|
||||||
public char sts_bunki = 'S';
|
public char sts_bunki = 'S';
|
||||||
public char sts_speed = 'L';
|
public char sts_speed = 'L';
|
||||||
public char sts_dir = 'F';
|
public char sts_dir = 'F';
|
||||||
@@ -36,7 +36,19 @@ namespace AGVEmulator
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 호출제어기 통신 오류
|
/// 호출제어기 통신 오류
|
||||||
/// </summary>
|
/// </summary>
|
||||||
controller_comm_error = 11,
|
controller_comm_error,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 배터리 저전압
|
||||||
|
/// </summary>
|
||||||
|
battery_low_voltage,
|
||||||
|
|
||||||
|
spare08,
|
||||||
|
|
||||||
|
lift_timeout,
|
||||||
|
lift_driver_overcurrent,
|
||||||
|
lift_driver_emergency,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 도착경보기 통신 오류
|
/// 도착경보기 통신 오류
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -57,15 +69,21 @@ namespace AGVEmulator
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
cross_ctrl_comm_error,
|
cross_ctrl_comm_error,
|
||||||
}
|
}
|
||||||
public enum esignal
|
public enum esignal2
|
||||||
|
{
|
||||||
|
cart_detect1 = 0,
|
||||||
|
cart_detect2,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum esignal1
|
||||||
{
|
{
|
||||||
front_gate_out = 0,
|
front_gate_out = 0,
|
||||||
rear_sensor_out,
|
rear_gte_out,
|
||||||
mark_sensor_1,
|
mark_sensor_1,
|
||||||
mark_sensor_2,
|
mark_sensor_2,
|
||||||
front_left_sensor,
|
lift_down_sensor,
|
||||||
front_right_sensor,
|
lift_up_sensor,
|
||||||
front_center_sensor,
|
magnet_relay,
|
||||||
charger_align_sensor,
|
charger_align_sensor,
|
||||||
}
|
}
|
||||||
public enum esystemflag0
|
public enum esystemflag0
|
||||||
@@ -119,7 +137,8 @@ namespace AGVEmulator
|
|||||||
system0,
|
system0,
|
||||||
system1,
|
system1,
|
||||||
error,
|
error,
|
||||||
signal,
|
signal1,
|
||||||
|
signal2,
|
||||||
}
|
}
|
||||||
public enum estsvaluetype
|
public enum estsvaluetype
|
||||||
{
|
{
|
||||||
@@ -266,11 +285,17 @@ namespace AGVEmulator
|
|||||||
if (SetBit(ref error, idx, value))
|
if (SetBit(ref error, idx, value))
|
||||||
ValueChanged?.Invoke(this, new ValueChangedArgs(idx, value, evaluetype.error));
|
ValueChanged?.Invoke(this, new ValueChangedArgs(idx, value, evaluetype.error));
|
||||||
}
|
}
|
||||||
public void SetAGV(DevAGV.esignal flag, bool value)
|
public void SetAGV(DevAGV.esignal1 flag, bool value)
|
||||||
{
|
{
|
||||||
var idx = (int)flag;
|
var idx = (int)flag;
|
||||||
if (SetBit(ref signal, idx, value))
|
if (SetBit(ref signal1, idx, value))
|
||||||
ValueChanged?.Invoke(this, new ValueChangedArgs(idx, value, evaluetype.signal));
|
ValueChanged?.Invoke(this, new ValueChangedArgs(idx, value, evaluetype.signal1));
|
||||||
|
}
|
||||||
|
public void SetAGV(DevAGV.esignal2 flag, bool value)
|
||||||
|
{
|
||||||
|
var idx = (int)flag;
|
||||||
|
if (SetBit(ref signal2, idx, value))
|
||||||
|
ValueChanged?.Invoke(this, new ValueChangedArgs(idx, value, evaluetype.signal2));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSTS(estsvaluetype target, char value)
|
public void SetSTS(estsvaluetype target, char value)
|
||||||
@@ -311,6 +336,29 @@ namespace AGVEmulator
|
|||||||
|
|
||||||
switch (frame.cmd)
|
switch (frame.cmd)
|
||||||
{
|
{
|
||||||
|
case "CLF"://마그넷 * 리프트
|
||||||
|
var cmd2 = frame.data.Substring(0, 2);
|
||||||
|
switch(cmd2)
|
||||||
|
{
|
||||||
|
case "ON":
|
||||||
|
SetAGV(esignal1.magnet_relay, true);
|
||||||
|
break;
|
||||||
|
case "UP":
|
||||||
|
SetAGV(esignal1.lift_down_sensor, false);
|
||||||
|
SetAGV(esignal1.lift_up_sensor, true);
|
||||||
|
break;
|
||||||
|
case "DN":
|
||||||
|
SetAGV(esignal1.lift_up_sensor, false);
|
||||||
|
SetAGV(esignal1.lift_down_sensor, true);
|
||||||
|
break;
|
||||||
|
case "ST":
|
||||||
|
break;
|
||||||
|
case "OF":
|
||||||
|
SetAGV(esignal1.magnet_relay, false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "CRN": //기동명령
|
case "CRN": //기동명령
|
||||||
//sts_dir = frame.data[0];
|
//sts_dir = frame.data[0];
|
||||||
SetSTS(estsvaluetype.direction, frame.data[0]);
|
SetSTS(estsvaluetype.direction, frame.data[0]);
|
||||||
@@ -472,11 +520,12 @@ namespace AGVEmulator
|
|||||||
barr[20] = (byte)this.sts_bunki;
|
barr[20] = (byte)this.sts_bunki;
|
||||||
barr[21] = (byte)this.sts_dir;
|
barr[21] = (byte)this.sts_dir;
|
||||||
barr[22] = (byte)this.sts_sensor;
|
barr[22] = (byte)this.sts_sensor;
|
||||||
//bufarr = System.Text.Encoding.Default.GetBytes(p.sensor.ToString().PadLeft(2, '0'));
|
|
||||||
//Array.Copy(bufarr, 0, barr, 22, bufarr.Length);
|
bufarr = System.Text.Encoding.Default.GetBytes(signal1.ToString("X2").PadLeft(2, '0'));
|
||||||
bufarr = System.Text.Encoding.Default.GetBytes(signal.ToString("X2").PadLeft(2, '0'));
|
|
||||||
Array.Copy(bufarr, 0, barr, 23, bufarr.Length);
|
Array.Copy(bufarr, 0, barr, 23, bufarr.Length);
|
||||||
//barr[22] = (byte)'5';
|
|
||||||
|
bufarr = System.Text.Encoding.Default.GetBytes(signal2.ToString("X2").PadLeft(2, '0'));
|
||||||
|
Array.Copy(bufarr, 0, barr, 25, bufarr.Length);
|
||||||
|
|
||||||
barr[barr.Length - 3] = (byte)'*';
|
barr[barr.Length - 3] = (byte)'*';
|
||||||
barr[barr.Length - 2] = (byte)'*';
|
barr[barr.Length - 2] = (byte)'*';
|
||||||
|
|||||||
@@ -74,24 +74,48 @@ namespace AGVEmulator
|
|||||||
/// 카트를 가지러 들어간다
|
/// 카트를 가지러 들어간다
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id"></param>
|
/// <param name="id"></param>
|
||||||
public void SendPickOn(byte id)
|
public void SendPickOnEnter(byte id)
|
||||||
{
|
{
|
||||||
var data = new List<byte>();
|
var data = new List<byte>();
|
||||||
data.Add(id);
|
data.Add(id);
|
||||||
Send(ENIGProtocol.AGVCommandHE.PickOn, data.ToArray());
|
Send(ENIGProtocol.AGVCommandHE.PickOnEnter, data.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 카트를 내려놓는다
|
/// 카트를 내려놓으로 들어간다
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id"></param>
|
/// <param name="id"></param>
|
||||||
public void SendPickOff(byte id)
|
public void SendPickOffEnter(byte id)
|
||||||
{
|
{
|
||||||
var data = new List<byte>();
|
var data = new List<byte>();
|
||||||
data.Add(id);
|
data.Add(id);
|
||||||
Send(ENIGProtocol.AGVCommandHE.PickOff, data.ToArray());
|
Send(ENIGProtocol.AGVCommandHE.PickOffEnter, data.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 카트를 가지러 들어가서 나온다
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id"></param>
|
||||||
|
public void SendPickOnExit(byte id)
|
||||||
|
{
|
||||||
|
var data = new List<byte>();
|
||||||
|
data.Add(id);
|
||||||
|
Send(ENIGProtocol.AGVCommandHE.PickOnExit, data.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 카트를 내려놓으러 들어가서 나온다
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id"></param>
|
||||||
|
public void SendPickOffExit(byte id)
|
||||||
|
{
|
||||||
|
var data = new List<byte>();
|
||||||
|
data.Add(id);
|
||||||
|
Send(ENIGProtocol.AGVCommandHE.PickOffExit, data.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void SendCurrentPos(byte id, uint tag)
|
public void SendCurrentPos(byte id, uint tag)
|
||||||
{
|
{
|
||||||
var data = new List<byte>();
|
var data = new List<byte>();
|
||||||
|
|||||||
50
AGVEmulator/RemoteStatus.cs
Normal file
50
AGVEmulator/RemoteStatus.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
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 ushort HWError { 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"))}");
|
||||||
|
if (HWError > 0)
|
||||||
|
{
|
||||||
|
sb.Append(" [HW ERR: ");
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
if ((HWError & (1 << i)) != 0)
|
||||||
|
{
|
||||||
|
sb.Append($"{(DevAGV.eerror)i},");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.AppendLine("]");
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -80,6 +80,7 @@ namespace AGVEmulator
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
UpdateUIStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Agv_ValueChanged(object sender, DevAGV.ValueChangedArgs e)
|
private void Agv_ValueChanged(object sender, DevAGV.ValueChangedArgs e)
|
||||||
@@ -142,7 +143,7 @@ namespace AGVEmulator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DevAGV.evaluetype.signal:
|
case DevAGV.evaluetype.signal1:
|
||||||
foreach (CheckBox c in panel8.Controls)
|
foreach (CheckBox c in panel8.Controls)
|
||||||
{
|
{
|
||||||
var idx = int.Parse(c.Tag.ToString());
|
var idx = int.Parse(c.Tag.ToString());
|
||||||
@@ -153,7 +154,17 @@ namespace AGVEmulator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case DevAGV.evaluetype.signal2:
|
||||||
|
foreach (CheckBox c in panel2.Controls)
|
||||||
|
{
|
||||||
|
var idx = int.Parse(c.Tag.ToString());
|
||||||
|
if (idx == e.Idx)
|
||||||
|
{
|
||||||
|
c.Checked = e.Value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,7 +179,8 @@ namespace AGVEmulator
|
|||||||
aaplycheckboxbit(ref AGV.system0, panel6);
|
aaplycheckboxbit(ref AGV.system0, panel6);
|
||||||
aaplycheckboxbit(ref AGV.system1, panel7);
|
aaplycheckboxbit(ref AGV.system1, panel7);
|
||||||
aaplycheckboxbit(ref AGV.error, panel9);
|
aaplycheckboxbit(ref AGV.error, panel9);
|
||||||
aaplycheckboxbit(ref AGV.signal, panel8);
|
aaplycheckboxbit(ref AGV.signal1, panel8);
|
||||||
|
aaplycheckboxbit(ref AGV.signal2, panel2);
|
||||||
|
|
||||||
if (this.agvViewer1.StopbyMark) AGV.sts_speed = 'S';
|
if (this.agvViewer1.StopbyMark) AGV.sts_speed = 'S';
|
||||||
else AGV.sts_speed = GetGroupItemCheckbox(groupBox4);
|
else AGV.sts_speed = GetGroupItemCheckbox(groupBox4);
|
||||||
|
|||||||
@@ -45,9 +45,13 @@ namespace AGVEmulator
|
|||||||
{
|
{
|
||||||
if (checkBox1.Checked)
|
if (checkBox1.Checked)
|
||||||
this.trackBar1.Invoke(new Action(() =>
|
this.trackBar1.Invoke(new Action(() =>
|
||||||
|
{
|
||||||
|
if (this.trackBar1.Value > 0)
|
||||||
{
|
{
|
||||||
this.trackBar1.Value -= 1;
|
this.trackBar1.Value -= 1;
|
||||||
trackBar1_Scroll(null, null);
|
trackBar1_Scroll(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
e.CurA = (int)BMS_CurA;
|
e.CurA = (int)BMS_CurA;
|
||||||
|
|||||||
@@ -24,15 +24,44 @@ namespace AGVEmulator
|
|||||||
}
|
}
|
||||||
private void CAL_ProtocReceived(object sender, ENIG.EEProtocol.DataEventArgs e)
|
private void CAL_ProtocReceived(object sender, ENIG.EEProtocol.DataEventArgs e)
|
||||||
{
|
{
|
||||||
//throw new NotImplementedException();
|
// HMI(Host)에서 호스트로 취급되는 HMI가 보낸 패킷은 ID가 0(ACS)임.
|
||||||
var dev = (DeviceType)e.ReceivedPacket.ID;
|
// 하지만 xbee.cs에서 CreatePacket 시 PUB.setting.XBE_ID를 사용함.
|
||||||
if (dev == DeviceType.AGV1 || dev == DeviceType.AGV2)
|
// 에뮬레이터에서는 이 패킷들을 수신하여 상태를 업데이트함.
|
||||||
|
|
||||||
|
var cmd = (ENIGProtocol.AGVCommandEH)e.ReceivedPacket.Command;
|
||||||
|
var data = e.ReceivedPacket.Data;
|
||||||
|
|
||||||
|
if (cmd == ENIGProtocol.AGVCommandEH.Status)
|
||||||
{
|
{
|
||||||
//agv에서 들어오는 데이터
|
if (data.Length >= 16)
|
||||||
var cmd = e.ReceivedPacket.Command;
|
|
||||||
if(cmd == 3)
|
|
||||||
{
|
{
|
||||||
//status
|
_remoteStatus.Mode = data[0];
|
||||||
|
_remoteStatus.RunSt = data[1];
|
||||||
|
_remoteStatus.HWError = BitConverter.ToUInt16(data, 2);
|
||||||
|
_remoteStatus.RunStep = data[4];
|
||||||
|
_remoteStatus.RunStepSeq = data[5];
|
||||||
|
_remoteStatus.MotorDir = data[6];
|
||||||
|
_remoteStatus.MagnetDir = data[7];
|
||||||
|
_remoteStatus.ChargeSt = data[8];
|
||||||
|
_remoteStatus.CartSt = data[9];
|
||||||
|
_remoteStatus.LiftSt = data[10];
|
||||||
|
_remoteStatus.ErrorCode = data[11];
|
||||||
|
_remoteErrorCode = (ENIGProtocol.AGVErrorCode)data[11];
|
||||||
|
_remoteErrorMessage = ENIGProtocol.AGVUtility.GetAGVErrorMessage(_remoteErrorCode);
|
||||||
|
_remoteStatus.LastTag = Encoding.ASCII.GetString(data, 12, 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
482
AGVEmulator/fMain.Designer.cs
generated
482
AGVEmulator/fMain.Designer.cs
generated
@@ -32,34 +32,34 @@ namespace AGVEmulator
|
|||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
this.components = new System.ComponentModel.Container();
|
this.components = new System.ComponentModel.Container();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata57 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata29 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata58 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata30 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata59 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata31 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata60 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata32 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata61 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata33 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata62 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata34 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata63 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata35 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata64 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata36 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata65 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata37 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata66 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata38 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata67 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata39 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata68 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata40 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata69 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata41 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata70 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata42 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata71 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata43 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata72 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata44 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata73 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata45 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata74 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata46 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata75 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata47 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata76 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata48 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata77 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata49 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata78 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata50 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata79 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata51 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata80 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata52 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata81 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata53 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata82 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata54 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata83 = new AGVEmulator.UC.AgvViewer.ptdata();
|
AGVEmulator.UC.AgvViewer.ptdata ptdata55 = new AGVEmulator.UC.AgvViewer.ptdata();
|
||||||
AGVEmulator.UC.AgvViewer.ptdata ptdata84 = 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));
|
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(fMain));
|
||||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||||
this.rtBMS = new arCtl.LogTextBox();
|
this.rtBMS = new arCtl.LogTextBox();
|
||||||
@@ -91,6 +91,7 @@ namespace AGVEmulator
|
|||||||
this.panel4 = new System.Windows.Forms.Panel();
|
this.panel4 = new System.Windows.Forms.Panel();
|
||||||
this.groupBox9 = new System.Windows.Forms.GroupBox();
|
this.groupBox9 = new System.Windows.Forms.GroupBox();
|
||||||
this.groupBox10 = new System.Windows.Forms.GroupBox();
|
this.groupBox10 = new System.Windows.Forms.GroupBox();
|
||||||
|
this.panel2 = new System.Windows.Forms.Panel();
|
||||||
this.panel8 = new System.Windows.Forms.Panel();
|
this.panel8 = new System.Windows.Forms.Panel();
|
||||||
this.groupBox11 = new System.Windows.Forms.GroupBox();
|
this.groupBox11 = new System.Windows.Forms.GroupBox();
|
||||||
this.panel9 = new System.Windows.Forms.Panel();
|
this.panel9 = new System.Windows.Forms.Panel();
|
||||||
@@ -141,6 +142,17 @@ namespace AGVEmulator
|
|||||||
this.tabPage2 = new System.Windows.Forms.TabPage();
|
this.tabPage2 = new System.Windows.Forms.TabPage();
|
||||||
this.tabPage3 = new System.Windows.Forms.TabPage();
|
this.tabPage3 = new System.Windows.Forms.TabPage();
|
||||||
this.panel3 = new System.Windows.Forms.Panel();
|
this.panel3 = new System.Windows.Forms.Panel();
|
||||||
|
this.groupBox13 = new System.Windows.Forms.GroupBox();
|
||||||
|
this.tbErCode = new System.Windows.Forms.TextBox();
|
||||||
|
this.tbErmsg = new System.Windows.Forms.TextBox();
|
||||||
|
this.groupBox12 = new System.Windows.Forms.GroupBox();
|
||||||
|
this.rtStatus = new System.Windows.Forms.RichTextBox();
|
||||||
|
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();
|
this.nudIDAgv = new System.Windows.Forms.NumericUpDown();
|
||||||
this.label7 = new System.Windows.Forms.Label();
|
this.label7 = new System.Windows.Forms.Label();
|
||||||
this.numericUpDown2 = new System.Windows.Forms.NumericUpDown();
|
this.numericUpDown2 = new System.Windows.Forms.NumericUpDown();
|
||||||
@@ -167,8 +179,6 @@ namespace AGVEmulator
|
|||||||
this.sbBMS = new System.Windows.Forms.ToolStripStatusLabel();
|
this.sbBMS = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
this.toolStripStatusLabel2 = new System.Windows.Forms.ToolStripStatusLabel();
|
this.toolStripStatusLabel2 = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
this.sbCAL = new System.Windows.Forms.ToolStripStatusLabel();
|
this.sbCAL = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
this.button2 = new System.Windows.Forms.Button();
|
|
||||||
this.button3 = new System.Windows.Forms.Button();
|
|
||||||
this.groupBox1.SuspendLayout();
|
this.groupBox1.SuspendLayout();
|
||||||
this.panel1.SuspendLayout();
|
this.panel1.SuspendLayout();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.trbT2)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.trbT2)).BeginInit();
|
||||||
@@ -195,6 +205,8 @@ namespace AGVEmulator
|
|||||||
this.tabPage2.SuspendLayout();
|
this.tabPage2.SuspendLayout();
|
||||||
this.tabPage3.SuspendLayout();
|
this.tabPage3.SuspendLayout();
|
||||||
this.panel3.SuspendLayout();
|
this.panel3.SuspendLayout();
|
||||||
|
this.groupBox13.SuspendLayout();
|
||||||
|
this.groupBox12.SuspendLayout();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.nudIDAgv)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.nudIDAgv)).BeginInit();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.numericUpDown2)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.numericUpDown2)).BeginInit();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.nudTagNo)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.nudTagNo)).BeginInit();
|
||||||
@@ -563,6 +575,7 @@ namespace AGVEmulator
|
|||||||
//
|
//
|
||||||
// groupBox10
|
// groupBox10
|
||||||
//
|
//
|
||||||
|
this.groupBox10.Controls.Add(this.panel2);
|
||||||
this.groupBox10.Controls.Add(this.panel8);
|
this.groupBox10.Controls.Add(this.panel8);
|
||||||
this.groupBox10.Dock = System.Windows.Forms.DockStyle.Left;
|
this.groupBox10.Dock = System.Windows.Forms.DockStyle.Left;
|
||||||
this.groupBox10.Location = new System.Drawing.Point(652, 44);
|
this.groupBox10.Location = new System.Drawing.Point(652, 44);
|
||||||
@@ -572,16 +585,25 @@ namespace AGVEmulator
|
|||||||
this.groupBox10.TabStop = false;
|
this.groupBox10.TabStop = false;
|
||||||
this.groupBox10.Text = "signal";
|
this.groupBox10.Text = "signal";
|
||||||
//
|
//
|
||||||
|
// panel2
|
||||||
|
//
|
||||||
|
this.panel2.AutoScroll = true;
|
||||||
|
this.panel2.AutoSize = true;
|
||||||
|
this.panel2.Location = new System.Drawing.Point(3, 162);
|
||||||
|
this.panel2.Name = "panel2";
|
||||||
|
this.panel2.Size = new System.Drawing.Size(183, 68);
|
||||||
|
this.panel2.TabIndex = 1;
|
||||||
|
this.panel2.Tag = "sg2";
|
||||||
|
//
|
||||||
// panel8
|
// panel8
|
||||||
//
|
//
|
||||||
this.panel8.AutoScroll = true;
|
this.panel8.AutoScroll = true;
|
||||||
this.panel8.AutoSize = true;
|
this.panel8.AutoSize = true;
|
||||||
this.panel8.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.panel8.Location = new System.Drawing.Point(5, 11);
|
||||||
this.panel8.Location = new System.Drawing.Point(3, 17);
|
|
||||||
this.panel8.Name = "panel8";
|
this.panel8.Name = "panel8";
|
||||||
this.panel8.Size = new System.Drawing.Size(183, 213);
|
this.panel8.Size = new System.Drawing.Size(180, 127);
|
||||||
this.panel8.TabIndex = 1;
|
this.panel8.TabIndex = 1;
|
||||||
this.panel8.Tag = "sg";
|
this.panel8.Tag = "sg1";
|
||||||
//
|
//
|
||||||
// groupBox11
|
// groupBox11
|
||||||
//
|
//
|
||||||
@@ -1104,120 +1126,120 @@ namespace AGVEmulator
|
|||||||
this.agvViewer1.lastmarkdir = "";
|
this.agvViewer1.lastmarkdir = "";
|
||||||
this.agvViewer1.lasttag = "";
|
this.agvViewer1.lasttag = "";
|
||||||
this.agvViewer1.lasttagdir = "";
|
this.agvViewer1.lasttagdir = "";
|
||||||
ptdata57.active = false;
|
ptdata29.active = false;
|
||||||
ptdata57.data = "NOT";
|
ptdata29.data = "NOT";
|
||||||
ptdata57.pos = 30F;
|
ptdata29.pos = 30F;
|
||||||
ptdata58.active = false;
|
ptdata30.active = false;
|
||||||
ptdata58.data = "QA";
|
ptdata30.data = "QA";
|
||||||
ptdata58.pos = 200F;
|
ptdata30.pos = 200F;
|
||||||
ptdata59.active = false;
|
ptdata31.active = false;
|
||||||
ptdata59.data = "CHG";
|
ptdata31.data = "CHG";
|
||||||
ptdata59.pos = 300F;
|
ptdata31.pos = 300F;
|
||||||
ptdata60.active = false;
|
ptdata32.active = false;
|
||||||
ptdata60.data = "QC";
|
ptdata32.data = "QC";
|
||||||
ptdata60.pos = 400F;
|
ptdata32.pos = 400F;
|
||||||
ptdata61.active = false;
|
ptdata33.active = false;
|
||||||
ptdata61.data = "#FVI-1";
|
ptdata33.data = "#FVI-1";
|
||||||
ptdata61.pos = 500F;
|
ptdata33.pos = 500F;
|
||||||
ptdata62.active = false;
|
ptdata34.active = false;
|
||||||
ptdata62.data = "#FVI-2";
|
ptdata34.data = "#FVI-2";
|
||||||
ptdata62.pos = 600F;
|
ptdata34.pos = 600F;
|
||||||
ptdata63.active = false;
|
ptdata35.active = false;
|
||||||
ptdata63.data = "#FVI-3";
|
ptdata35.data = "#FVI-3";
|
||||||
ptdata63.pos = 700F;
|
ptdata35.pos = 700F;
|
||||||
ptdata64.active = false;
|
ptdata36.active = false;
|
||||||
ptdata64.data = "#FVI-4";
|
ptdata36.data = "#FVI-4";
|
||||||
ptdata64.pos = 800F;
|
ptdata36.pos = 800F;
|
||||||
ptdata65.active = false;
|
ptdata37.active = false;
|
||||||
ptdata65.data = "#FVI-5";
|
ptdata37.data = "#FVI-5";
|
||||||
ptdata65.pos = 900F;
|
ptdata37.pos = 900F;
|
||||||
ptdata66.active = false;
|
ptdata38.active = false;
|
||||||
ptdata66.data = "POT";
|
ptdata38.data = "POT";
|
||||||
ptdata66.pos = 970F;
|
ptdata38.pos = 970F;
|
||||||
this.agvViewer1.listMRK = new AGVEmulator.UC.AgvViewer.ptdata[] {
|
this.agvViewer1.listMRK = new AGVEmulator.UC.AgvViewer.ptdata[] {
|
||||||
ptdata57,
|
ptdata29,
|
||||||
ptdata58,
|
ptdata30,
|
||||||
ptdata59,
|
ptdata31,
|
||||||
ptdata60,
|
ptdata32,
|
||||||
ptdata61,
|
ptdata33,
|
||||||
ptdata62,
|
ptdata34,
|
||||||
ptdata63,
|
ptdata35,
|
||||||
ptdata64,
|
ptdata36,
|
||||||
ptdata65,
|
ptdata37,
|
||||||
ptdata66};
|
ptdata38};
|
||||||
ptdata67.active = false;
|
ptdata39.active = false;
|
||||||
ptdata67.data = "9000";
|
ptdata39.data = "9000";
|
||||||
ptdata67.pos = 80F;
|
ptdata39.pos = 80F;
|
||||||
ptdata68.active = false;
|
ptdata40.active = false;
|
||||||
ptdata68.data = "9001";
|
ptdata40.data = "9001";
|
||||||
ptdata68.pos = 120F;
|
ptdata40.pos = 120F;
|
||||||
ptdata69.active = false;
|
ptdata41.active = false;
|
||||||
ptdata69.data = "9010";
|
ptdata41.data = "9010";
|
||||||
ptdata69.pos = 180F;
|
ptdata41.pos = 180F;
|
||||||
ptdata70.active = false;
|
ptdata42.active = false;
|
||||||
ptdata70.data = "9011";
|
ptdata42.data = "9011";
|
||||||
ptdata70.pos = 220F;
|
ptdata42.pos = 220F;
|
||||||
ptdata71.active = false;
|
ptdata43.active = false;
|
||||||
ptdata71.data = "9020";
|
ptdata43.data = "9020";
|
||||||
ptdata71.pos = 280F;
|
ptdata43.pos = 280F;
|
||||||
ptdata72.active = false;
|
ptdata44.active = false;
|
||||||
ptdata72.data = "9021";
|
ptdata44.data = "9021";
|
||||||
ptdata72.pos = 320F;
|
ptdata44.pos = 320F;
|
||||||
ptdata73.active = false;
|
ptdata45.active = false;
|
||||||
ptdata73.data = "9030";
|
ptdata45.data = "9030";
|
||||||
ptdata73.pos = 380F;
|
ptdata45.pos = 380F;
|
||||||
ptdata74.active = false;
|
ptdata46.active = false;
|
||||||
ptdata74.data = "9031";
|
ptdata46.data = "9031";
|
||||||
ptdata74.pos = 420F;
|
ptdata46.pos = 420F;
|
||||||
ptdata75.active = false;
|
ptdata47.active = false;
|
||||||
ptdata75.data = "9040";
|
ptdata47.data = "9040";
|
||||||
ptdata75.pos = 480F;
|
ptdata47.pos = 480F;
|
||||||
ptdata76.active = false;
|
ptdata48.active = false;
|
||||||
ptdata76.data = "9041";
|
ptdata48.data = "9041";
|
||||||
ptdata76.pos = 520F;
|
ptdata48.pos = 520F;
|
||||||
ptdata77.active = false;
|
ptdata49.active = false;
|
||||||
ptdata77.data = "9050";
|
ptdata49.data = "9050";
|
||||||
ptdata77.pos = 580F;
|
ptdata49.pos = 580F;
|
||||||
ptdata78.active = false;
|
ptdata50.active = false;
|
||||||
ptdata78.data = "9051";
|
ptdata50.data = "9051";
|
||||||
ptdata78.pos = 620F;
|
ptdata50.pos = 620F;
|
||||||
ptdata79.active = false;
|
ptdata51.active = false;
|
||||||
ptdata79.data = "9060";
|
ptdata51.data = "9060";
|
||||||
ptdata79.pos = 680F;
|
ptdata51.pos = 680F;
|
||||||
ptdata80.active = false;
|
ptdata52.active = false;
|
||||||
ptdata80.data = "9061";
|
ptdata52.data = "9061";
|
||||||
ptdata80.pos = 720F;
|
ptdata52.pos = 720F;
|
||||||
ptdata81.active = false;
|
ptdata53.active = false;
|
||||||
ptdata81.data = "9070";
|
ptdata53.data = "9070";
|
||||||
ptdata81.pos = 780F;
|
ptdata53.pos = 780F;
|
||||||
ptdata82.active = false;
|
ptdata54.active = false;
|
||||||
ptdata82.data = "9071";
|
ptdata54.data = "9071";
|
||||||
ptdata82.pos = 820F;
|
ptdata54.pos = 820F;
|
||||||
ptdata83.active = false;
|
ptdata55.active = false;
|
||||||
ptdata83.data = "9000";
|
ptdata55.data = "9000";
|
||||||
ptdata83.pos = 10F;
|
ptdata55.pos = 10F;
|
||||||
ptdata84.active = false;
|
ptdata56.active = false;
|
||||||
ptdata84.data = "9001";
|
ptdata56.data = "9001";
|
||||||
ptdata84.pos = 50F;
|
ptdata56.pos = 50F;
|
||||||
this.agvViewer1.listTAG = new AGVEmulator.UC.AgvViewer.ptdata[] {
|
this.agvViewer1.listTAG = new AGVEmulator.UC.AgvViewer.ptdata[] {
|
||||||
ptdata67,
|
ptdata39,
|
||||||
ptdata68,
|
ptdata40,
|
||||||
ptdata69,
|
ptdata41,
|
||||||
ptdata70,
|
ptdata42,
|
||||||
ptdata71,
|
ptdata43,
|
||||||
ptdata72,
|
ptdata44,
|
||||||
ptdata73,
|
ptdata45,
|
||||||
ptdata74,
|
ptdata46,
|
||||||
ptdata75,
|
ptdata47,
|
||||||
ptdata76,
|
ptdata48,
|
||||||
ptdata77,
|
ptdata49,
|
||||||
ptdata78,
|
ptdata50,
|
||||||
ptdata79,
|
ptdata51,
|
||||||
ptdata80,
|
ptdata52,
|
||||||
ptdata81,
|
ptdata53,
|
||||||
ptdata82,
|
ptdata54,
|
||||||
ptdata83,
|
ptdata55,
|
||||||
ptdata84};
|
ptdata56};
|
||||||
this.agvViewer1.Location = new System.Drawing.Point(241, 0);
|
this.agvViewer1.Location = new System.Drawing.Point(241, 0);
|
||||||
this.agvViewer1.Name = "agvViewer1";
|
this.agvViewer1.Name = "agvViewer1";
|
||||||
this.agvViewer1.Size = new System.Drawing.Size(899, 120);
|
this.agvViewer1.Size = new System.Drawing.Size(899, 120);
|
||||||
@@ -1262,6 +1284,12 @@ namespace AGVEmulator
|
|||||||
//
|
//
|
||||||
// panel3
|
// 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);
|
||||||
|
this.panel3.Controls.Add(this.label12);
|
||||||
this.panel3.Controls.Add(this.button3);
|
this.panel3.Controls.Add(this.button3);
|
||||||
this.panel3.Controls.Add(this.button2);
|
this.panel3.Controls.Add(this.button2);
|
||||||
this.panel3.Controls.Add(this.nudIDAgv);
|
this.panel3.Controls.Add(this.nudIDAgv);
|
||||||
@@ -1282,10 +1310,117 @@ namespace AGVEmulator
|
|||||||
this.panel3.Size = new System.Drawing.Size(364, 622);
|
this.panel3.Size = new System.Drawing.Size(364, 622);
|
||||||
this.panel3.TabIndex = 15;
|
this.panel3.TabIndex = 15;
|
||||||
//
|
//
|
||||||
|
// groupBox13
|
||||||
|
//
|
||||||
|
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, 545);
|
||||||
|
this.groupBox13.Name = "groupBox13";
|
||||||
|
this.groupBox13.Size = new System.Drawing.Size(364, 77);
|
||||||
|
this.groupBox13.TabIndex = 21;
|
||||||
|
this.groupBox13.TabStop = false;
|
||||||
|
this.groupBox13.Text = "error";
|
||||||
|
//
|
||||||
|
// tbErCode
|
||||||
|
//
|
||||||
|
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;
|
||||||
|
//
|
||||||
|
// tbErmsg
|
||||||
|
//
|
||||||
|
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;
|
||||||
|
//
|
||||||
|
// groupBox12
|
||||||
|
//
|
||||||
|
this.groupBox12.Controls.Add(this.rtStatus);
|
||||||
|
this.groupBox12.Location = new System.Drawing.Point(7, 318);
|
||||||
|
this.groupBox12.Name = "groupBox12";
|
||||||
|
this.groupBox12.Size = new System.Drawing.Size(355, 221);
|
||||||
|
this.groupBox12.TabIndex = 20;
|
||||||
|
this.groupBox12.TabStop = false;
|
||||||
|
this.groupBox12.Text = "status";
|
||||||
|
//
|
||||||
|
// 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, 201);
|
||||||
|
this.rtStatus.TabIndex = 0;
|
||||||
|
this.rtStatus.Text = "";
|
||||||
|
//
|
||||||
|
// label13
|
||||||
|
//
|
||||||
|
this.label13.AutoSize = true;
|
||||||
|
this.label13.Location = new System.Drawing.Point(18, 287);
|
||||||
|
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(205, 274);
|
||||||
|
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(66, 274);
|
||||||
|
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(18, 243);
|
||||||
|
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(205, 230);
|
||||||
|
this.button3.Name = "button3";
|
||||||
|
this.button3.Size = new System.Drawing.Size(133, 38);
|
||||||
|
this.button3.TabIndex = 15;
|
||||||
|
this.button3.Tag = "--";
|
||||||
|
this.button3.Text = "Pick Off";
|
||||||
|
this.button3.UseVisualStyleBackColor = true;
|
||||||
|
this.button3.Click += new System.EventHandler(this.button3_Click);
|
||||||
|
//
|
||||||
|
// button2
|
||||||
|
//
|
||||||
|
this.button2.Location = new System.Drawing.Point(66, 230);
|
||||||
|
this.button2.Name = "button2";
|
||||||
|
this.button2.Size = new System.Drawing.Size(133, 38);
|
||||||
|
this.button2.TabIndex = 14;
|
||||||
|
this.button2.Tag = "--";
|
||||||
|
this.button2.Text = "Pick On";
|
||||||
|
this.button2.UseVisualStyleBackColor = true;
|
||||||
|
this.button2.Click += new System.EventHandler(this.button2_Click);
|
||||||
|
//
|
||||||
// nudIDAgv
|
// nudIDAgv
|
||||||
//
|
//
|
||||||
this.nudIDAgv.Font = new System.Drawing.Font("굴림", 20F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
this.nudIDAgv.Font = new System.Drawing.Font("굴림", 20F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
this.nudIDAgv.Location = new System.Drawing.Point(6, 249);
|
this.nudIDAgv.Location = new System.Drawing.Point(7, 174);
|
||||||
this.nudIDAgv.Maximum = new decimal(new int[] {
|
this.nudIDAgv.Maximum = new decimal(new int[] {
|
||||||
9999999,
|
9999999,
|
||||||
0,
|
0,
|
||||||
@@ -1304,7 +1439,7 @@ namespace AGVEmulator
|
|||||||
// label7
|
// label7
|
||||||
//
|
//
|
||||||
this.label7.AutoSize = true;
|
this.label7.AutoSize = true;
|
||||||
this.label7.Location = new System.Drawing.Point(32, 220);
|
this.label7.Location = new System.Drawing.Point(33, 145);
|
||||||
this.label7.Name = "label7";
|
this.label7.Name = "label7";
|
||||||
this.label7.Size = new System.Drawing.Size(45, 12);
|
this.label7.Size = new System.Drawing.Size(45, 12);
|
||||||
this.label7.TabIndex = 12;
|
this.label7.TabIndex = 12;
|
||||||
@@ -1313,7 +1448,7 @@ namespace AGVEmulator
|
|||||||
// numericUpDown2
|
// numericUpDown2
|
||||||
//
|
//
|
||||||
this.numericUpDown2.Font = new System.Drawing.Font("굴림", 20F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
this.numericUpDown2.Font = new System.Drawing.Font("굴림", 20F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
this.numericUpDown2.Location = new System.Drawing.Point(110, 207);
|
this.numericUpDown2.Location = new System.Drawing.Point(111, 132);
|
||||||
this.numericUpDown2.Maximum = new decimal(new int[] {
|
this.numericUpDown2.Maximum = new decimal(new int[] {
|
||||||
9999999,
|
9999999,
|
||||||
0,
|
0,
|
||||||
@@ -1331,7 +1466,7 @@ namespace AGVEmulator
|
|||||||
//
|
//
|
||||||
// button1
|
// button1
|
||||||
//
|
//
|
||||||
this.button1.Location = new System.Drawing.Point(246, 207);
|
this.button1.Location = new System.Drawing.Point(247, 132);
|
||||||
this.button1.Name = "button1";
|
this.button1.Name = "button1";
|
||||||
this.button1.Size = new System.Drawing.Size(86, 38);
|
this.button1.Size = new System.Drawing.Size(86, 38);
|
||||||
this.button1.TabIndex = 10;
|
this.button1.TabIndex = 10;
|
||||||
@@ -1343,7 +1478,7 @@ namespace AGVEmulator
|
|||||||
// nudTagNo
|
// nudTagNo
|
||||||
//
|
//
|
||||||
this.nudTagNo.Font = new System.Drawing.Font("굴림", 20F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
this.nudTagNo.Font = new System.Drawing.Font("굴림", 20F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
this.nudTagNo.Location = new System.Drawing.Point(110, 251);
|
this.nudTagNo.Location = new System.Drawing.Point(111, 176);
|
||||||
this.nudTagNo.Maximum = new decimal(new int[] {
|
this.nudTagNo.Maximum = new decimal(new int[] {
|
||||||
9999999,
|
9999999,
|
||||||
0,
|
0,
|
||||||
@@ -1361,7 +1496,7 @@ namespace AGVEmulator
|
|||||||
//
|
//
|
||||||
// btacsgoto
|
// btacsgoto
|
||||||
//
|
//
|
||||||
this.btacsgoto.Location = new System.Drawing.Point(246, 251);
|
this.btacsgoto.Location = new System.Drawing.Point(247, 176);
|
||||||
this.btacsgoto.Name = "btacsgoto";
|
this.btacsgoto.Name = "btacsgoto";
|
||||||
this.btacsgoto.Size = new System.Drawing.Size(86, 38);
|
this.btacsgoto.Size = new System.Drawing.Size(86, 38);
|
||||||
this.btacsgoto.TabIndex = 8;
|
this.btacsgoto.TabIndex = 8;
|
||||||
@@ -1372,7 +1507,7 @@ namespace AGVEmulator
|
|||||||
//
|
//
|
||||||
// button7
|
// button7
|
||||||
//
|
//
|
||||||
this.button7.Location = new System.Drawing.Point(83, 72);
|
this.button7.Location = new System.Drawing.Point(219, 12);
|
||||||
this.button7.Name = "button7";
|
this.button7.Name = "button7";
|
||||||
this.button7.Size = new System.Drawing.Size(62, 54);
|
this.button7.Size = new System.Drawing.Size(62, 54);
|
||||||
this.button7.TabIndex = 7;
|
this.button7.TabIndex = 7;
|
||||||
@@ -1381,7 +1516,7 @@ namespace AGVEmulator
|
|||||||
//
|
//
|
||||||
// button8
|
// button8
|
||||||
//
|
//
|
||||||
this.button8.Location = new System.Drawing.Point(83, 132);
|
this.button8.Location = new System.Drawing.Point(87, 72);
|
||||||
this.button8.Name = "button8";
|
this.button8.Name = "button8";
|
||||||
this.button8.Size = new System.Drawing.Size(62, 54);
|
this.button8.Size = new System.Drawing.Size(62, 54);
|
||||||
this.button8.TabIndex = 6;
|
this.button8.TabIndex = 6;
|
||||||
@@ -1391,7 +1526,7 @@ namespace AGVEmulator
|
|||||||
//
|
//
|
||||||
// button9
|
// button9
|
||||||
//
|
//
|
||||||
this.button9.Location = new System.Drawing.Point(15, 130);
|
this.button9.Location = new System.Drawing.Point(19, 70);
|
||||||
this.button9.Name = "button9";
|
this.button9.Name = "button9";
|
||||||
this.button9.Size = new System.Drawing.Size(62, 54);
|
this.button9.Size = new System.Drawing.Size(62, 54);
|
||||||
this.button9.TabIndex = 5;
|
this.button9.TabIndex = 5;
|
||||||
@@ -1401,7 +1536,7 @@ namespace AGVEmulator
|
|||||||
//
|
//
|
||||||
// button10
|
// button10
|
||||||
//
|
//
|
||||||
this.button10.Location = new System.Drawing.Point(15, 70);
|
this.button10.Location = new System.Drawing.Point(151, 10);
|
||||||
this.button10.Name = "button10";
|
this.button10.Name = "button10";
|
||||||
this.button10.Size = new System.Drawing.Size(62, 54);
|
this.button10.Size = new System.Drawing.Size(62, 54);
|
||||||
this.button10.TabIndex = 3;
|
this.button10.TabIndex = 3;
|
||||||
@@ -1542,28 +1677,6 @@ namespace AGVEmulator
|
|||||||
this.sbCAL.Size = new System.Drawing.Size(19, 17);
|
this.sbCAL.Size = new System.Drawing.Size(19, 17);
|
||||||
this.sbCAL.Text = "●";
|
this.sbCAL.Text = "●";
|
||||||
//
|
//
|
||||||
// button2
|
|
||||||
//
|
|
||||||
this.button2.Location = new System.Drawing.Point(246, 295);
|
|
||||||
this.button2.Name = "button2";
|
|
||||||
this.button2.Size = new System.Drawing.Size(86, 38);
|
|
||||||
this.button2.TabIndex = 14;
|
|
||||||
this.button2.Tag = "--";
|
|
||||||
this.button2.Text = "Pick On";
|
|
||||||
this.button2.UseVisualStyleBackColor = true;
|
|
||||||
this.button2.Click += new System.EventHandler(this.button2_Click);
|
|
||||||
//
|
|
||||||
// button3
|
|
||||||
//
|
|
||||||
this.button3.Location = new System.Drawing.Point(246, 339);
|
|
||||||
this.button3.Name = "button3";
|
|
||||||
this.button3.Size = new System.Drawing.Size(86, 38);
|
|
||||||
this.button3.TabIndex = 15;
|
|
||||||
this.button3.Tag = "--";
|
|
||||||
this.button3.Text = "Pick Off";
|
|
||||||
this.button3.UseVisualStyleBackColor = true;
|
|
||||||
this.button3.Click += new System.EventHandler(this.button3_Click);
|
|
||||||
//
|
|
||||||
// fMain
|
// fMain
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||||
@@ -1612,6 +1725,9 @@ namespace AGVEmulator
|
|||||||
this.tabPage3.ResumeLayout(false);
|
this.tabPage3.ResumeLayout(false);
|
||||||
this.panel3.ResumeLayout(false);
|
this.panel3.ResumeLayout(false);
|
||||||
this.panel3.PerformLayout();
|
this.panel3.PerformLayout();
|
||||||
|
this.groupBox13.ResumeLayout(false);
|
||||||
|
this.groupBox13.PerformLayout();
|
||||||
|
this.groupBox12.ResumeLayout(false);
|
||||||
((System.ComponentModel.ISupportInitialize)(this.nudIDAgv)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.nudIDAgv)).EndInit();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.numericUpDown2)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.numericUpDown2)).EndInit();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.nudTagNo)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.nudTagNo)).EndInit();
|
||||||
@@ -1734,6 +1850,16 @@ namespace AGVEmulator
|
|||||||
private ToolStripButton toolStripButton5;
|
private ToolStripButton toolStripButton5;
|
||||||
private Button button3;
|
private Button button3;
|
||||||
private Button button2;
|
private Button button2;
|
||||||
|
private Panel panel2;
|
||||||
|
private Label label12;
|
||||||
|
private Label label13;
|
||||||
|
private Button button6;
|
||||||
|
private Button button13;
|
||||||
|
private GroupBox groupBox13;
|
||||||
|
private GroupBox groupBox12;
|
||||||
|
private TextBox tbErCode;
|
||||||
|
private TextBox tbErmsg;
|
||||||
|
private RichTextBox rtStatus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,22 +8,26 @@ using System.Globalization;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using System.Windows.Forms.VisualStyles;
|
using System.Windows.Forms.VisualStyles;
|
||||||
using static AGVEmulator.DevAGV;
|
using static AGVEmulator.DevAGV;
|
||||||
using AGVNavigationCore.Controls;
|
using AGVNavigationCore.Controls;
|
||||||
using AGVNavigationCore.Models;
|
using AGVNavigationCore.Models;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace AGVEmulator
|
namespace AGVEmulator
|
||||||
{
|
{
|
||||||
public partial class fMain : Form
|
public partial class fMain : Form
|
||||||
{
|
{
|
||||||
arUtil.Log logAGV, logBMS, logCAL;
|
AR.Log logAGV, logBMS, logCAL;
|
||||||
DevBMS BMS;
|
DevBMS BMS;
|
||||||
DevAGV AGV;
|
DevAGV AGV;
|
||||||
DevXBE XBE;
|
DevXBE XBE;
|
||||||
|
|
||||||
|
public RemoteStatus _remoteStatus = new RemoteStatus();
|
||||||
|
public ENIGProtocol.AGVErrorCode _remoteErrorCode = ENIGProtocol.AGVErrorCode.None;
|
||||||
|
public string _remoteErrorMessage = "";
|
||||||
|
|
||||||
// Map Control
|
// Map Control
|
||||||
private UnifiedAGVCanvas _agvCanvas;
|
private UnifiedAGVCanvas _agvCanvas;
|
||||||
private VirtualAGV _visualAgv;
|
private VirtualAGV _visualAgv;
|
||||||
@@ -47,9 +51,9 @@ namespace AGVEmulator
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
this.Text = $"{Application.ProductName} ver.{Application.ProductVersion}";
|
this.Text = $"{Application.ProductName} ver.{Application.ProductVersion}";
|
||||||
// logPLC = new arUtil.Log();
|
// logPLC = new arUtil.Log();
|
||||||
logAGV = new arUtil.Log();
|
logAGV = new AR.Log();
|
||||||
logBMS = new arUtil.Log();
|
logBMS = new AR.Log();
|
||||||
logCAL = new arUtil.Log();
|
logCAL = new AR.Log();
|
||||||
|
|
||||||
// logPLC.FileNameFormat = "{yyyyMMdd}_PLC";
|
// logPLC.FileNameFormat = "{yyyyMMdd}_PLC";
|
||||||
logAGV.FileNameFormat = "{yyyyMMdd}_AGV";
|
logAGV.FileNameFormat = "{yyyyMMdd}_AGV";
|
||||||
@@ -153,10 +157,10 @@ namespace AGVEmulator
|
|||||||
chk.CheckedChanged += Chk_CheckedChanged;
|
chk.CheckedChanged += Chk_CheckedChanged;
|
||||||
this.panel7.Controls.Add(chk);
|
this.panel7.Controls.Add(chk);
|
||||||
}
|
}
|
||||||
arrs = Enum.GetNames(typeof(DevAGV.esignal));
|
arrs = Enum.GetNames(typeof(DevAGV.esignal1));
|
||||||
foreach (var item in arrs)
|
foreach (var item in arrs)
|
||||||
{
|
{
|
||||||
var data = (DevAGV.esignal)Enum.Parse(typeof(DevAGV.esignal), item);
|
var data = (DevAGV.esignal1)Enum.Parse(typeof(DevAGV.esignal1), item);
|
||||||
|
|
||||||
var chk = new CheckBox();
|
var chk = new CheckBox();
|
||||||
chk.Text = $"[{(int)data:00}] {item}";
|
chk.Text = $"[{(int)data:00}] {item}";
|
||||||
@@ -167,6 +171,20 @@ namespace AGVEmulator
|
|||||||
chk.CheckedChanged += Chk_CheckedChanged;
|
chk.CheckedChanged += Chk_CheckedChanged;
|
||||||
this.panel8.Controls.Add(chk);
|
this.panel8.Controls.Add(chk);
|
||||||
}
|
}
|
||||||
|
arrs = Enum.GetNames(typeof(DevAGV.esignal2));
|
||||||
|
foreach (var item in arrs)
|
||||||
|
{
|
||||||
|
var data = (DevAGV.esignal2)Enum.Parse(typeof(DevAGV.esignal2), item);
|
||||||
|
|
||||||
|
var chk = new CheckBox();
|
||||||
|
chk.Text = $"[{(int)data:00}] {item}";
|
||||||
|
chk.AutoSize = true;
|
||||||
|
chk.Visible = true;
|
||||||
|
chk.Tag = (int)data;
|
||||||
|
chk.Dock = DockStyle.Top;
|
||||||
|
chk.CheckedChanged += Chk_CheckedChanged;
|
||||||
|
this.panel2.Controls.Add(chk);
|
||||||
|
}
|
||||||
arrs = Enum.GetNames(typeof(DevAGV.eerror));
|
arrs = Enum.GetNames(typeof(DevAGV.eerror));
|
||||||
foreach (var item in arrs)
|
foreach (var item in arrs)
|
||||||
{
|
{
|
||||||
@@ -344,7 +362,7 @@ namespace AGVEmulator
|
|||||||
private void AgvViewer1_MarkTouched(object sender, UC.AgvViewer.TagArgs e)
|
private void AgvViewer1_MarkTouched(object sender, UC.AgvViewer.TagArgs e)
|
||||||
{
|
{
|
||||||
// throw new NotImplementedException();
|
// throw new NotImplementedException();
|
||||||
AGV.SetAGV(esignal.mark_sensor_1, e.Active);
|
AGV.SetAGV(esignal1.mark_sensor_1, e.Active);
|
||||||
logAGV.Add($"mark {e.Data} touch:{e.Active}");
|
logAGV.Add($"mark {e.Data} touch:{e.Active}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -730,10 +748,14 @@ namespace AGVEmulator
|
|||||||
var v2 = (DevAGV.eerror)idx;
|
var v2 = (DevAGV.eerror)idx;
|
||||||
AGV.SetAGV(v2, chk.Checked);
|
AGV.SetAGV(v2, chk.Checked);
|
||||||
break;
|
break;
|
||||||
case "sg":
|
case "sg1":
|
||||||
var v3 = (DevAGV.esignal)idx;
|
var v3 = (DevAGV.esignal1)idx;
|
||||||
AGV.SetAGV(v3, chk.Checked);
|
AGV.SetAGV(v3, chk.Checked);
|
||||||
break;
|
break;
|
||||||
|
case "sg2":
|
||||||
|
var v4 = (DevAGV.esignal2)idx;
|
||||||
|
AGV.SetAGV(v4, chk.Checked);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
chk.BackColor = chk.Checked ? Color.Lime : SystemColors.Window;
|
chk.BackColor = chk.Checked ? Color.Lime : SystemColors.Window;
|
||||||
@@ -920,21 +942,25 @@ namespace AGVEmulator
|
|||||||
private void button2_Click(object sender, EventArgs e)
|
private void button2_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var target = (byte)nudIDAgv.Value;
|
var target = (byte)nudIDAgv.Value;
|
||||||
this.XBE.SendPickOn(target);
|
this.XBE.SendPickOnEnter(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void button3_Click(object sender, EventArgs e)
|
private void button3_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var target = (byte)nudIDAgv.Value;
|
var target = (byte)nudIDAgv.Value;
|
||||||
this.XBE.SendPickOff(target);
|
this.XBE.SendPickOffEnter(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void trbT2_Scroll(object sender, EventArgs e)
|
private void trbT2_Scroll(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Temp2 = (UInt16)trbT2.Value;
|
Temp2 = (UInt16)trbT2.Value;
|
||||||
label11.Text = $"{Temp2 / 10f}º";
|
label11.Text = $"{Temp2 / 10f}º";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void toolStripButton1_Click(object sender, EventArgs e)
|
private void toolStripButton1_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
serAGV.Connect();
|
serAGV.Connect();
|
||||||
@@ -948,8 +974,59 @@ namespace AGVEmulator
|
|||||||
serBMS.Disconnect();
|
serBMS.Disconnect();
|
||||||
serCAL.Disconnect();
|
serCAL.Disconnect();
|
||||||
}
|
}
|
||||||
|
private void button13_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var target = (byte)nudIDAgv.Value;
|
||||||
|
this.XBE.SendPickOnExit(target);
|
||||||
|
}
|
||||||
|
private void button6_Click_1(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
|
||||||
|
string errCode = _remoteErrorCode.ToString();
|
||||||
|
string errMsg = _remoteErrorMessage;
|
||||||
|
|
||||||
|
if (_remoteStatus.HWError > 0)
|
||||||
|
{
|
||||||
|
errCode = $"HW:{_remoteStatus.HWError:X4}" + (errCode == "None" ? "" : $" | {errCode}");
|
||||||
|
|
||||||
|
StringBuilder sbHw = new StringBuilder();
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
if (((ushort)_remoteStatus.HWError & (1 << i)) != 0)
|
||||||
|
{
|
||||||
|
sbHw.Append($"{(DevAGV.eerror)i}, ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errMsg = $"[HW] {sbHw}" + (string.IsNullOrEmpty(errMsg) ? "" : $" | {errMsg}");
|
||||||
|
}
|
||||||
|
|
||||||
|
tbErCode.Text = errCode;
|
||||||
|
tbErmsg.Text = errMsg;
|
||||||
|
|
||||||
|
if (_remoteErrorCode != ENIGProtocol.AGVErrorCode.None || _remoteStatus.HWError > 0)
|
||||||
|
{
|
||||||
|
tbErCode.BackColor = Color.Red;
|
||||||
|
tbErCode.ForeColor = Color.White;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tbErCode.BackColor = SystemColors.Window;
|
||||||
|
tbErCode.ForeColor = SystemColors.WindowText;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Express 15 for Windows Desktop
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 15.0.36324.19
|
VisualStudioVersion = 17.14.36310.24
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVMapEditor", "AGVMapEditor\AGVMapEditor.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVMapEditor", "AGVMapEditor\AGVMapEditor.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
|
||||||
EndProject
|
EndProject
|
||||||
@@ -8,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVNavigationCore", "AGVNav
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVSimulator", "AGVSimulator\AGVSimulator.csproj", "{B2C3D4E5-0000-0000-0000-000000000000}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVSimulator", "AGVSimulator\AGVSimulator.csproj", "{B2C3D4E5-0000-0000-0000-000000000000}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ENIGProtocol", "EnigProtocol\enigprotocol\ENIGProtocol.csproj", "{9365803B-933D-4237-93C7-B502C855A71C}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -26,11 +29,15 @@ Global
|
|||||||
{B2C3D4E5-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{B2C3D4E5-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{B2C3D4E5-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{B2C3D4E5-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{B2C3D4E5-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU
|
{B2C3D4E5-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9365803B-933D-4237-93C7-B502C855A71C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9365803B-933D-4237-93C7-B502C855A71C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9365803B-933D-4237-93C7-B502C855A71C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9365803B-933D-4237-93C7-B502C855A71C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {F2C60284-CCB5-450D-BCD0-19C693529FD6}
|
SolutionGuid = {638744DA-A7C8-43E2-A98E-0DE9BDB1DA35}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>false</Optimize>
|
<Optimize>false</Optimize>
|
||||||
<OutputPath>..\..\..\..\..\..\Amkor\AGV4\Test\MapEditor\</OutputPath>
|
<OutputPath>bin\debug\</OutputPath>
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
@@ -31,6 +31,7 @@ namespace AGVMapEditor.Forms
|
|||||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
|
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
|
||||||
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
|
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
|
||||||
this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel();
|
this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
|
this.sbFile = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
|
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
|
||||||
this.tabControl1 = new System.Windows.Forms.TabControl();
|
this.tabControl1 = new System.Windows.Forms.TabControl();
|
||||||
this.tabPageNodes = new System.Windows.Forms.TabPage();
|
this.tabPageNodes = new System.Windows.Forms.TabPage();
|
||||||
@@ -40,6 +41,20 @@ namespace AGVMapEditor.Forms
|
|||||||
this.lstNodeConnection = new System.Windows.Forms.ListBox();
|
this.lstNodeConnection = new System.Windows.Forms.ListBox();
|
||||||
this.toolStrip1 = new System.Windows.Forms.ToolStrip();
|
this.toolStrip1 = new System.Windows.Forms.ToolStrip();
|
||||||
this.btNodeRemove = new System.Windows.Forms.ToolStripButton();
|
this.btNodeRemove = new System.Windows.Forms.ToolStripButton();
|
||||||
|
this.tabPage2 = new System.Windows.Forms.TabPage();
|
||||||
|
this.lstMagnetDirection = new System.Windows.Forms.ListBox();
|
||||||
|
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||||
|
this.button1 = new System.Windows.Forms.Button();
|
||||||
|
this.button2 = new System.Windows.Forms.Button();
|
||||||
|
this.button3 = new System.Windows.Forms.Button();
|
||||||
|
this.toolStrip4 = new System.Windows.Forms.ToolStrip();
|
||||||
|
this.btDirDelete = new System.Windows.Forms.ToolStripButton();
|
||||||
|
this.btMakeDirdata = new System.Windows.Forms.ToolStripButton();
|
||||||
|
this.toolStripButton2 = new System.Windows.Forms.ToolStripButton();
|
||||||
|
this.tabPage3 = new System.Windows.Forms.TabPage();
|
||||||
|
this.lstMagnet = new System.Windows.Forms.ListBox();
|
||||||
|
this.toolStrip5 = new System.Windows.Forms.ToolStrip();
|
||||||
|
this.btDelMagnet = new System.Windows.Forms.ToolStripButton();
|
||||||
this._propertyGrid = new System.Windows.Forms.PropertyGrid();
|
this._propertyGrid = new System.Windows.Forms.PropertyGrid();
|
||||||
this.panel1 = new System.Windows.Forms.Panel();
|
this.panel1 = new System.Windows.Forms.Panel();
|
||||||
this.toolStrip3 = new System.Windows.Forms.ToolStrip();
|
this.toolStrip3 = new System.Windows.Forms.ToolStrip();
|
||||||
@@ -51,11 +66,12 @@ namespace AGVMapEditor.Forms
|
|||||||
this.btnDelete = new System.Windows.Forms.ToolStripButton();
|
this.btnDelete = new System.Windows.Forms.ToolStripButton();
|
||||||
this.btnEditImage = new System.Windows.Forms.ToolStripButton();
|
this.btnEditImage = new System.Windows.Forms.ToolStripButton();
|
||||||
this.separator1 = new System.Windows.Forms.ToolStripSeparator();
|
this.separator1 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
this.btnConnect = new System.Windows.Forms.ToolStripButton();
|
this.btnConnNode = new System.Windows.Forms.ToolStripButton();
|
||||||
this.btnDeleteConnection = new System.Windows.Forms.ToolStripButton();
|
this.btnConnDir = new System.Windows.Forms.ToolStripButton();
|
||||||
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
this.btnToggleGrid = new System.Windows.Forms.ToolStripButton();
|
this.btnToggleGrid = new System.Windows.Forms.ToolStripButton();
|
||||||
this.btnFitMap = new System.Windows.Forms.ToolStripButton();
|
this.btnFitMap = new System.Windows.Forms.ToolStripButton();
|
||||||
|
this.btAddMagnet = new System.Windows.Forms.ToolStripButton();
|
||||||
this.toolStrip2 = new System.Windows.Forms.ToolStrip();
|
this.toolStrip2 = new System.Windows.Forms.ToolStrip();
|
||||||
this.btnNew = new System.Windows.Forms.ToolStripButton();
|
this.btnNew = new System.Windows.Forms.ToolStripButton();
|
||||||
this.btnOpen = new System.Windows.Forms.ToolStripButton();
|
this.btnOpen = new System.Windows.Forms.ToolStripButton();
|
||||||
@@ -67,6 +83,7 @@ namespace AGVMapEditor.Forms
|
|||||||
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
|
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
this.toolStripButton1 = new System.Windows.Forms.ToolStripDropDownButton();
|
this.toolStripButton1 = new System.Windows.Forms.ToolStripDropDownButton();
|
||||||
this.allTurnLeftRightCrossOnToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.allTurnLeftRightCrossOnToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.toolStripButton3 = new System.Windows.Forms.ToolStripButton();
|
||||||
this.statusStrip1.SuspendLayout();
|
this.statusStrip1.SuspendLayout();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
|
||||||
this.splitContainer1.Panel1.SuspendLayout();
|
this.splitContainer1.Panel1.SuspendLayout();
|
||||||
@@ -76,6 +93,11 @@ namespace AGVMapEditor.Forms
|
|||||||
this.tabPageNodes.SuspendLayout();
|
this.tabPageNodes.SuspendLayout();
|
||||||
this.tabPage1.SuspendLayout();
|
this.tabPage1.SuspendLayout();
|
||||||
this.toolStrip1.SuspendLayout();
|
this.toolStrip1.SuspendLayout();
|
||||||
|
this.tabPage2.SuspendLayout();
|
||||||
|
this.tableLayoutPanel1.SuspendLayout();
|
||||||
|
this.toolStrip4.SuspendLayout();
|
||||||
|
this.tabPage3.SuspendLayout();
|
||||||
|
this.toolStrip5.SuspendLayout();
|
||||||
this.toolStrip3.SuspendLayout();
|
this.toolStrip3.SuspendLayout();
|
||||||
this.toolStrip2.SuspendLayout();
|
this.toolStrip2.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
@@ -83,7 +105,8 @@ namespace AGVMapEditor.Forms
|
|||||||
// statusStrip1
|
// statusStrip1
|
||||||
//
|
//
|
||||||
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
this.toolStripStatusLabel1});
|
this.toolStripStatusLabel1,
|
||||||
|
this.sbFile});
|
||||||
this.statusStrip1.Location = new System.Drawing.Point(0, 751);
|
this.statusStrip1.Location = new System.Drawing.Point(0, 751);
|
||||||
this.statusStrip1.Name = "statusStrip1";
|
this.statusStrip1.Name = "statusStrip1";
|
||||||
this.statusStrip1.Size = new System.Drawing.Size(1200, 22);
|
this.statusStrip1.Size = new System.Drawing.Size(1200, 22);
|
||||||
@@ -96,6 +119,12 @@ namespace AGVMapEditor.Forms
|
|||||||
this.toolStripStatusLabel1.Size = new System.Drawing.Size(39, 17);
|
this.toolStripStatusLabel1.Size = new System.Drawing.Size(39, 17);
|
||||||
this.toolStripStatusLabel1.Text = "Ready";
|
this.toolStripStatusLabel1.Text = "Ready";
|
||||||
//
|
//
|
||||||
|
// sbFile
|
||||||
|
//
|
||||||
|
this.sbFile.Name = "sbFile";
|
||||||
|
this.sbFile.Size = new System.Drawing.Size(17, 17);
|
||||||
|
this.sbFile.Text = "--";
|
||||||
|
//
|
||||||
// splitContainer1
|
// splitContainer1
|
||||||
//
|
//
|
||||||
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
@@ -120,6 +149,8 @@ namespace AGVMapEditor.Forms
|
|||||||
//
|
//
|
||||||
this.tabControl1.Controls.Add(this.tabPageNodes);
|
this.tabControl1.Controls.Add(this.tabPageNodes);
|
||||||
this.tabControl1.Controls.Add(this.tabPage1);
|
this.tabControl1.Controls.Add(this.tabPage1);
|
||||||
|
this.tabControl1.Controls.Add(this.tabPage2);
|
||||||
|
this.tabControl1.Controls.Add(this.tabPage3);
|
||||||
this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.tabControl1.Location = new System.Drawing.Point(0, 0);
|
this.tabControl1.Location = new System.Drawing.Point(0, 0);
|
||||||
this.tabControl1.Name = "tabControl1";
|
this.tabControl1.Name = "tabControl1";
|
||||||
@@ -201,6 +232,161 @@ namespace AGVMapEditor.Forms
|
|||||||
this.btNodeRemove.Text = "Remove";
|
this.btNodeRemove.Text = "Remove";
|
||||||
this.btNodeRemove.Click += new System.EventHandler(this.btNodeRemove_Click);
|
this.btNodeRemove.Click += new System.EventHandler(this.btNodeRemove_Click);
|
||||||
//
|
//
|
||||||
|
// tabPage2
|
||||||
|
//
|
||||||
|
this.tabPage2.Controls.Add(this.lstMagnetDirection);
|
||||||
|
this.tabPage2.Controls.Add(this.tableLayoutPanel1);
|
||||||
|
this.tabPage2.Controls.Add(this.toolStrip4);
|
||||||
|
this.tabPage2.Location = new System.Drawing.Point(4, 22);
|
||||||
|
this.tabPage2.Name = "tabPage2";
|
||||||
|
this.tabPage2.Size = new System.Drawing.Size(292, 309);
|
||||||
|
this.tabPage2.TabIndex = 2;
|
||||||
|
this.tabPage2.Text = "방향 관리";
|
||||||
|
this.tabPage2.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// lstMagnetDirection
|
||||||
|
//
|
||||||
|
this.lstMagnetDirection.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.lstMagnetDirection.FormattingEnabled = true;
|
||||||
|
this.lstMagnetDirection.ItemHeight = 12;
|
||||||
|
this.lstMagnetDirection.Location = new System.Drawing.Point(0, 25);
|
||||||
|
this.lstMagnetDirection.Name = "lstMagnetDirection";
|
||||||
|
this.lstMagnetDirection.Size = new System.Drawing.Size(292, 246);
|
||||||
|
this.lstMagnetDirection.TabIndex = 3;
|
||||||
|
//
|
||||||
|
// tableLayoutPanel1
|
||||||
|
//
|
||||||
|
this.tableLayoutPanel1.ColumnCount = 3;
|
||||||
|
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
|
||||||
|
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
|
||||||
|
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
|
||||||
|
this.tableLayoutPanel1.Controls.Add(this.button1, 0, 0);
|
||||||
|
this.tableLayoutPanel1.Controls.Add(this.button2, 1, 0);
|
||||||
|
this.tableLayoutPanel1.Controls.Add(this.button3, 2, 0);
|
||||||
|
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
|
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 271);
|
||||||
|
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||||
|
this.tableLayoutPanel1.RowCount = 1;
|
||||||
|
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||||
|
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 38F));
|
||||||
|
this.tableLayoutPanel1.Size = new System.Drawing.Size(292, 38);
|
||||||
|
this.tableLayoutPanel1.TabIndex = 6;
|
||||||
|
//
|
||||||
|
// button1
|
||||||
|
//
|
||||||
|
this.button1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.button1.Location = new System.Drawing.Point(3, 3);
|
||||||
|
this.button1.Name = "button1";
|
||||||
|
this.button1.Size = new System.Drawing.Size(91, 32);
|
||||||
|
this.button1.TabIndex = 0;
|
||||||
|
this.button1.Text = "Left";
|
||||||
|
this.button1.UseVisualStyleBackColor = true;
|
||||||
|
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||||
|
//
|
||||||
|
// button2
|
||||||
|
//
|
||||||
|
this.button2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.button2.Location = new System.Drawing.Point(100, 3);
|
||||||
|
this.button2.Name = "button2";
|
||||||
|
this.button2.Size = new System.Drawing.Size(91, 32);
|
||||||
|
this.button2.TabIndex = 0;
|
||||||
|
this.button2.Text = "Straight";
|
||||||
|
this.button2.UseVisualStyleBackColor = true;
|
||||||
|
this.button2.Click += new System.EventHandler(this.button2_Click);
|
||||||
|
//
|
||||||
|
// button3
|
||||||
|
//
|
||||||
|
this.button3.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.button3.Location = new System.Drawing.Point(197, 3);
|
||||||
|
this.button3.Name = "button3";
|
||||||
|
this.button3.Size = new System.Drawing.Size(92, 32);
|
||||||
|
this.button3.TabIndex = 0;
|
||||||
|
this.button3.Text = "Right";
|
||||||
|
this.button3.UseVisualStyleBackColor = true;
|
||||||
|
this.button3.Click += new System.EventHandler(this.button3_Click);
|
||||||
|
//
|
||||||
|
// toolStrip4
|
||||||
|
//
|
||||||
|
this.toolStrip4.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
|
this.btDirDelete,
|
||||||
|
this.btMakeDirdata,
|
||||||
|
this.toolStripButton2});
|
||||||
|
this.toolStrip4.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.toolStrip4.Name = "toolStrip4";
|
||||||
|
this.toolStrip4.Size = new System.Drawing.Size(292, 25);
|
||||||
|
this.toolStrip4.TabIndex = 5;
|
||||||
|
this.toolStrip4.Text = "toolStrip4";
|
||||||
|
//
|
||||||
|
// btDirDelete
|
||||||
|
//
|
||||||
|
this.btDirDelete.Image = ((System.Drawing.Image)(resources.GetObject("btDirDelete.Image")));
|
||||||
|
this.btDirDelete.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||||
|
this.btDirDelete.Name = "btDirDelete";
|
||||||
|
this.btDirDelete.Size = new System.Drawing.Size(61, 22);
|
||||||
|
this.btDirDelete.Text = "Delete";
|
||||||
|
this.btDirDelete.Click += new System.EventHandler(this.btDirDelete_Click);
|
||||||
|
//
|
||||||
|
// btMakeDirdata
|
||||||
|
//
|
||||||
|
this.btMakeDirdata.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
|
||||||
|
this.btMakeDirdata.Image = ((System.Drawing.Image)(resources.GetObject("btMakeDirdata.Image")));
|
||||||
|
this.btMakeDirdata.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||||
|
this.btMakeDirdata.Name = "btMakeDirdata";
|
||||||
|
this.btMakeDirdata.Size = new System.Drawing.Size(69, 22);
|
||||||
|
this.btMakeDirdata.Text = "Remake";
|
||||||
|
this.btMakeDirdata.Click += new System.EventHandler(this.toolStripButton3_Click);
|
||||||
|
//
|
||||||
|
// toolStripButton2
|
||||||
|
//
|
||||||
|
this.toolStripButton2.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
|
||||||
|
this.toolStripButton2.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton2.Image")));
|
||||||
|
this.toolStripButton2.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||||
|
this.toolStripButton2.Name = "toolStripButton2";
|
||||||
|
this.toolStripButton2.Size = new System.Drawing.Size(54, 22);
|
||||||
|
this.toolStripButton2.Text = "Clear";
|
||||||
|
this.toolStripButton2.Click += new System.EventHandler(this.toolStripButton2_Click);
|
||||||
|
//
|
||||||
|
// tabPage3
|
||||||
|
//
|
||||||
|
this.tabPage3.Controls.Add(this.lstMagnet);
|
||||||
|
this.tabPage3.Controls.Add(this.toolStrip5);
|
||||||
|
this.tabPage3.Location = new System.Drawing.Point(4, 22);
|
||||||
|
this.tabPage3.Name = "tabPage3";
|
||||||
|
this.tabPage3.Size = new System.Drawing.Size(292, 309);
|
||||||
|
this.tabPage3.TabIndex = 3;
|
||||||
|
this.tabPage3.Text = "마그넷라인";
|
||||||
|
this.tabPage3.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// lstMagnet
|
||||||
|
//
|
||||||
|
this.lstMagnet.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.lstMagnet.FormattingEnabled = true;
|
||||||
|
this.lstMagnet.ItemHeight = 12;
|
||||||
|
this.lstMagnet.Location = new System.Drawing.Point(0, 25);
|
||||||
|
this.lstMagnet.Name = "lstMagnet";
|
||||||
|
this.lstMagnet.Size = new System.Drawing.Size(292, 284);
|
||||||
|
this.lstMagnet.TabIndex = 2;
|
||||||
|
//
|
||||||
|
// toolStrip5
|
||||||
|
//
|
||||||
|
this.toolStrip5.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
|
this.btDelMagnet,
|
||||||
|
this.toolStripButton3});
|
||||||
|
this.toolStrip5.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.toolStrip5.Name = "toolStrip5";
|
||||||
|
this.toolStrip5.Size = new System.Drawing.Size(292, 25);
|
||||||
|
this.toolStrip5.TabIndex = 6;
|
||||||
|
this.toolStrip5.Text = "toolStrip5";
|
||||||
|
//
|
||||||
|
// btDelMagnet
|
||||||
|
//
|
||||||
|
this.btDelMagnet.Image = ((System.Drawing.Image)(resources.GetObject("btDelMagnet.Image")));
|
||||||
|
this.btDelMagnet.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||||
|
this.btDelMagnet.Name = "btDelMagnet";
|
||||||
|
this.btDelMagnet.Size = new System.Drawing.Size(61, 22);
|
||||||
|
this.btDelMagnet.Text = "Delete";
|
||||||
|
this.btDelMagnet.Click += new System.EventHandler(this.btDelMagnet_Click);
|
||||||
|
//
|
||||||
// _propertyGrid
|
// _propertyGrid
|
||||||
//
|
//
|
||||||
this._propertyGrid.Dock = System.Windows.Forms.DockStyle.Bottom;
|
this._propertyGrid.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
@@ -226,11 +412,12 @@ namespace AGVMapEditor.Forms
|
|||||||
this.btnDelete,
|
this.btnDelete,
|
||||||
this.btnEditImage,
|
this.btnEditImage,
|
||||||
this.separator1,
|
this.separator1,
|
||||||
this.btnConnect,
|
this.btnConnNode,
|
||||||
this.btnDeleteConnection,
|
this.btnConnDir,
|
||||||
this.toolStripSeparator1,
|
this.toolStripSeparator1,
|
||||||
this.btnToggleGrid,
|
this.btnToggleGrid,
|
||||||
this.btnFitMap});
|
this.btnFitMap,
|
||||||
|
this.btAddMagnet});
|
||||||
this.toolStrip3.Location = new System.Drawing.Point(0, 0);
|
this.toolStrip3.Location = new System.Drawing.Point(0, 0);
|
||||||
this.toolStrip3.Name = "toolStrip3";
|
this.toolStrip3.Name = "toolStrip3";
|
||||||
this.toolStrip3.Size = new System.Drawing.Size(896, 25);
|
this.toolStrip3.Size = new System.Drawing.Size(896, 25);
|
||||||
@@ -264,6 +451,8 @@ namespace AGVMapEditor.Forms
|
|||||||
this.btnAddNode.Size = new System.Drawing.Size(111, 22);
|
this.btnAddNode.Size = new System.Drawing.Size(111, 22);
|
||||||
this.btnAddNode.Text = "노드 추가 (A)";
|
this.btnAddNode.Text = "노드 추가 (A)";
|
||||||
this.btnAddNode.ToolTipText = "노드 추가 (A)";
|
this.btnAddNode.ToolTipText = "노드 추가 (A)";
|
||||||
|
this.btnAddNode.ButtonClick += new System.EventHandler(this.btnAddNode_ButtonClick);
|
||||||
|
this.btnAddNode.BackColorChanged += new System.EventHandler(this.btnAddNode_BackColorChanged);
|
||||||
//
|
//
|
||||||
// btnAddLabel
|
// btnAddLabel
|
||||||
//
|
//
|
||||||
@@ -308,21 +497,20 @@ namespace AGVMapEditor.Forms
|
|||||||
this.separator1.Name = "separator1";
|
this.separator1.Name = "separator1";
|
||||||
this.separator1.Size = new System.Drawing.Size(6, 25);
|
this.separator1.Size = new System.Drawing.Size(6, 25);
|
||||||
//
|
//
|
||||||
// btnConnect
|
// btnConnNode
|
||||||
//
|
//
|
||||||
this.btnConnect.Image = ((System.Drawing.Image)(resources.GetObject("btnConnect.Image")));
|
this.btnConnNode.Image = ((System.Drawing.Image)(resources.GetObject("btnConnNode.Image")));
|
||||||
this.btnConnect.Name = "btnConnect";
|
this.btnConnNode.Name = "btnConnNode";
|
||||||
this.btnConnect.Size = new System.Drawing.Size(95, 22);
|
this.btnConnNode.Size = new System.Drawing.Size(95, 22);
|
||||||
this.btnConnect.Text = "노드연결 (C)";
|
this.btnConnNode.Text = "노드연결 (C)";
|
||||||
//
|
//
|
||||||
// btnDeleteConnection
|
// btnConnDir
|
||||||
//
|
//
|
||||||
this.btnDeleteConnection.Image = ((System.Drawing.Image)(resources.GetObject("btnDeleteConnection.Image")));
|
this.btnConnDir.Image = ((System.Drawing.Image)(resources.GetObject("btnConnDir.Image")));
|
||||||
this.btnDeleteConnection.ImageTransparentColor = System.Drawing.Color.Magenta;
|
this.btnConnDir.Name = "btnConnDir";
|
||||||
this.btnDeleteConnection.Name = "btnDeleteConnection";
|
this.btnConnDir.Size = new System.Drawing.Size(95, 22);
|
||||||
this.btnDeleteConnection.Size = new System.Drawing.Size(94, 22);
|
this.btnConnDir.Text = "방향연결 (C)";
|
||||||
this.btnDeleteConnection.Text = "연결삭제 (X)";
|
this.btnConnDir.Click += new System.EventHandler(this.btnConnDir_Click);
|
||||||
this.btnDeleteConnection.ToolTipText = "연결 삭제 (X)";
|
|
||||||
//
|
//
|
||||||
// toolStripSeparator1
|
// toolStripSeparator1
|
||||||
//
|
//
|
||||||
@@ -345,6 +533,15 @@ namespace AGVMapEditor.Forms
|
|||||||
this.btnFitMap.Text = "맵 맞춤";
|
this.btnFitMap.Text = "맵 맞춤";
|
||||||
this.btnFitMap.ToolTipText = "맵 전체 보기";
|
this.btnFitMap.ToolTipText = "맵 전체 보기";
|
||||||
//
|
//
|
||||||
|
// btAddMagnet
|
||||||
|
//
|
||||||
|
this.btAddMagnet.Image = ((System.Drawing.Image)(resources.GetObject("btAddMagnet.Image")));
|
||||||
|
this.btAddMagnet.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||||
|
this.btAddMagnet.Name = "btAddMagnet";
|
||||||
|
this.btAddMagnet.Size = new System.Drawing.Size(87, 22);
|
||||||
|
this.btAddMagnet.Text = "마그넷추가";
|
||||||
|
this.btAddMagnet.Click += new System.EventHandler(this.btAddMagnet_Click);
|
||||||
|
//
|
||||||
// toolStrip2
|
// toolStrip2
|
||||||
//
|
//
|
||||||
this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
@@ -444,6 +641,16 @@ namespace AGVMapEditor.Forms
|
|||||||
this.allTurnLeftRightCrossOnToolStripMenuItem.Text = "All TurnLeft/Right/Cross On";
|
this.allTurnLeftRightCrossOnToolStripMenuItem.Text = "All TurnLeft/Right/Cross On";
|
||||||
this.allTurnLeftRightCrossOnToolStripMenuItem.Click += new System.EventHandler(this.allTurnLeftRightCrossOnToolStripMenuItem_Click);
|
this.allTurnLeftRightCrossOnToolStripMenuItem.Click += new System.EventHandler(this.allTurnLeftRightCrossOnToolStripMenuItem_Click);
|
||||||
//
|
//
|
||||||
|
// toolStripButton3
|
||||||
|
//
|
||||||
|
this.toolStripButton3.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
|
||||||
|
this.toolStripButton3.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton3.Image")));
|
||||||
|
this.toolStripButton3.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||||
|
this.toolStripButton3.Name = "toolStripButton3";
|
||||||
|
this.toolStripButton3.Size = new System.Drawing.Size(66, 22);
|
||||||
|
this.toolStripButton3.Text = "Refresh";
|
||||||
|
this.toolStripButton3.Click += new System.EventHandler(this.toolStripButton3_Click_1);
|
||||||
|
//
|
||||||
// MainForm
|
// MainForm
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||||
@@ -471,6 +678,15 @@ namespace AGVMapEditor.Forms
|
|||||||
this.tabPage1.PerformLayout();
|
this.tabPage1.PerformLayout();
|
||||||
this.toolStrip1.ResumeLayout(false);
|
this.toolStrip1.ResumeLayout(false);
|
||||||
this.toolStrip1.PerformLayout();
|
this.toolStrip1.PerformLayout();
|
||||||
|
this.tabPage2.ResumeLayout(false);
|
||||||
|
this.tabPage2.PerformLayout();
|
||||||
|
this.tableLayoutPanel1.ResumeLayout(false);
|
||||||
|
this.toolStrip4.ResumeLayout(false);
|
||||||
|
this.toolStrip4.PerformLayout();
|
||||||
|
this.tabPage3.ResumeLayout(false);
|
||||||
|
this.tabPage3.PerformLayout();
|
||||||
|
this.toolStrip5.ResumeLayout(false);
|
||||||
|
this.toolStrip5.PerformLayout();
|
||||||
this.toolStrip3.ResumeLayout(false);
|
this.toolStrip3.ResumeLayout(false);
|
||||||
this.toolStrip3.PerformLayout();
|
this.toolStrip3.PerformLayout();
|
||||||
this.toolStrip2.ResumeLayout(false);
|
this.toolStrip2.ResumeLayout(false);
|
||||||
@@ -506,9 +722,8 @@ namespace AGVMapEditor.Forms
|
|||||||
private System.Windows.Forms.ToolStripButton btnSelect;
|
private System.Windows.Forms.ToolStripButton btnSelect;
|
||||||
private System.Windows.Forms.ToolStripButton btnMove;
|
private System.Windows.Forms.ToolStripButton btnMove;
|
||||||
private System.Windows.Forms.ToolStripButton btnEditImage;
|
private System.Windows.Forms.ToolStripButton btnEditImage;
|
||||||
private System.Windows.Forms.ToolStripButton btnConnect;
|
private System.Windows.Forms.ToolStripButton btnConnNode;
|
||||||
private System.Windows.Forms.ToolStripButton btnDelete;
|
private System.Windows.Forms.ToolStripButton btnDelete;
|
||||||
private System.Windows.Forms.ToolStripButton btnDeleteConnection;
|
|
||||||
private System.Windows.Forms.ToolStripSeparator separator1;
|
private System.Windows.Forms.ToolStripSeparator separator1;
|
||||||
private System.Windows.Forms.ToolStripButton btnToggleGrid;
|
private System.Windows.Forms.ToolStripButton btnToggleGrid;
|
||||||
private System.Windows.Forms.ToolStripButton btnFitMap;
|
private System.Windows.Forms.ToolStripButton btnFitMap;
|
||||||
@@ -520,5 +735,23 @@ namespace AGVMapEditor.Forms
|
|||||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
|
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
|
||||||
private System.Windows.Forms.ToolStripDropDownButton toolStripButton1;
|
private System.Windows.Forms.ToolStripDropDownButton toolStripButton1;
|
||||||
private System.Windows.Forms.ToolStripMenuItem allTurnLeftRightCrossOnToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem allTurnLeftRightCrossOnToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.TabPage tabPage2;
|
||||||
|
private System.Windows.Forms.ListBox lstMagnetDirection;
|
||||||
|
private System.Windows.Forms.ToolStrip toolStrip4;
|
||||||
|
private System.Windows.Forms.ToolStripButton btDirDelete;
|
||||||
|
private System.Windows.Forms.ToolStripButton btMakeDirdata;
|
||||||
|
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||||
|
private System.Windows.Forms.Button button1;
|
||||||
|
private System.Windows.Forms.Button button2;
|
||||||
|
private System.Windows.Forms.Button button3;
|
||||||
|
private System.Windows.Forms.ToolStripButton toolStripButton2;
|
||||||
|
private System.Windows.Forms.ToolStripButton btAddMagnet;
|
||||||
|
private System.Windows.Forms.TabPage tabPage3;
|
||||||
|
private System.Windows.Forms.ListBox lstMagnet;
|
||||||
|
private System.Windows.Forms.ToolStrip toolStrip5;
|
||||||
|
private System.Windows.Forms.ToolStripButton btDelMagnet;
|
||||||
|
private System.Windows.Forms.ToolStripStatusLabel sbFile;
|
||||||
|
private System.Windows.Forms.ToolStripButton btnConnDir;
|
||||||
|
private System.Windows.Forms.ToolStripButton toolStripButton3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ using AGVNavigationCore.Models;
|
|||||||
using MapImage = AGVNavigationCore.Models.MapImage;
|
using MapImage = AGVNavigationCore.Models.MapImage;
|
||||||
using MapLabel = AGVNavigationCore.Models.MapLabel;
|
using MapLabel = AGVNavigationCore.Models.MapLabel;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
namespace AGVMapEditor.Forms
|
namespace AGVMapEditor.Forms
|
||||||
{
|
{
|
||||||
@@ -35,10 +36,8 @@ namespace AGVMapEditor.Forms
|
|||||||
public class NodeConnectionInfo
|
public class NodeConnectionInfo
|
||||||
{
|
{
|
||||||
public string FromNodeId { get; set; }
|
public string FromNodeId { get; set; }
|
||||||
public string FromNodeName { get; set; }
|
|
||||||
public ushort FromRfidId { get; set; }
|
public ushort FromRfidId { get; set; }
|
||||||
public string ToNodeId { get; set; }
|
public string ToNodeId { get; set; }
|
||||||
public string ToNodeName { get; set; }
|
|
||||||
public ushort ToRfidId { get; set; }
|
public ushort ToRfidId { get; set; }
|
||||||
public string ConnectionType { get; set; }
|
public string ConnectionType { get; set; }
|
||||||
|
|
||||||
@@ -46,12 +45,12 @@ namespace AGVMapEditor.Forms
|
|||||||
{
|
{
|
||||||
// RFID가 있으면 RFID(노드이름), 없으면 NodeID(노드이름) 형태로 표시
|
// RFID가 있으면 RFID(노드이름), 없으면 NodeID(노드이름) 형태로 표시
|
||||||
string fromDisplay = FromRfidId > 0
|
string fromDisplay = FromRfidId > 0
|
||||||
? $"{FromRfidId}({FromNodeName})"
|
? $"{FromRfidId:0000}(*{FromNodeId.PadLeft(4, '0')})"
|
||||||
: $"---({FromNodeId})";
|
: $"(*{FromNodeId})";
|
||||||
|
|
||||||
string toDisplay = ToRfidId > 0
|
string toDisplay = ToRfidId > 0
|
||||||
? $"{ToRfidId}({ToNodeName})"
|
? $"{ToRfidId:0000}(*{ToNodeId.PadLeft(4, '0')})"
|
||||||
: $"---({ToNodeId})";
|
: $"(*{ToNodeId})";
|
||||||
|
|
||||||
// 양방향 연결은 ↔ 기호 사용
|
// 양방향 연결은 ↔ 기호 사용
|
||||||
string arrow = ConnectionType == "양방향" ? "↔" : "→";
|
string arrow = ConnectionType == "양방향" ? "↔" : "→";
|
||||||
@@ -60,6 +59,7 @@ namespace AGVMapEditor.Forms
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructor
|
#region Constructor
|
||||||
@@ -107,6 +107,7 @@ namespace AGVMapEditor.Forms
|
|||||||
{
|
{
|
||||||
_mapCanvas = new UnifiedAGVCanvas();
|
_mapCanvas = new UnifiedAGVCanvas();
|
||||||
_mapCanvas.Dock = DockStyle.Fill;
|
_mapCanvas.Dock = DockStyle.Fill;
|
||||||
|
_mapCanvas.Mode = UnifiedAGVCanvas.CanvasMode.Edit;
|
||||||
|
|
||||||
// 이벤트 연결
|
// 이벤트 연결
|
||||||
_mapCanvas.NodeAdded += OnNodeAdded;
|
_mapCanvas.NodeAdded += OnNodeAdded;
|
||||||
@@ -114,6 +115,7 @@ namespace AGVMapEditor.Forms
|
|||||||
_mapCanvas.NodesSelected += OnNodesSelected; // 다중 선택 이벤트
|
_mapCanvas.NodesSelected += OnNodesSelected; // 다중 선택 이벤트
|
||||||
_mapCanvas.NodeMoved += OnNodeMoved;
|
_mapCanvas.NodeMoved += OnNodeMoved;
|
||||||
_mapCanvas.NodeDeleted += OnNodeDeleted;
|
_mapCanvas.NodeDeleted += OnNodeDeleted;
|
||||||
|
_mapCanvas.ConnectionCreated += OnConnectionCreated;
|
||||||
_mapCanvas.ConnectionDeleted += OnConnectionDeleted;
|
_mapCanvas.ConnectionDeleted += OnConnectionDeleted;
|
||||||
_mapCanvas.ImageDoubleClicked += OnImageDoubleClicked;
|
_mapCanvas.ImageDoubleClicked += OnImageDoubleClicked;
|
||||||
_mapCanvas.MapChanged += OnMapChanged;
|
_mapCanvas.MapChanged += OnMapChanged;
|
||||||
@@ -143,9 +145,8 @@ namespace AGVMapEditor.Forms
|
|||||||
btnAddLabel.Click += (s, e) => _mapCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.AddLabel;
|
btnAddLabel.Click += (s, e) => _mapCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.AddLabel;
|
||||||
btnAddImage.Click += (s, e) => _mapCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.AddImage;
|
btnAddImage.Click += (s, e) => _mapCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.AddImage;
|
||||||
|
|
||||||
btnConnect.Click += (s, e) => _mapCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.Connect;
|
btnConnNode.Click += (s, e) => _mapCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.Connect;
|
||||||
btnDelete.Click += (s, e) => _mapCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.Delete;
|
btnDelete.Click += (s, e) => _mapCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.Delete;
|
||||||
btnDeleteConnection.Click += (s, e) => _mapCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.DeleteConnection;
|
|
||||||
|
|
||||||
// 그리드 토글 버튼
|
// 그리드 토글 버튼
|
||||||
btnToggleGrid.Click += (s, e) => _mapCanvas.ShowGrid = !_mapCanvas.ShowGrid;
|
btnToggleGrid.Click += (s, e) => _mapCanvas.ShowGrid = !_mapCanvas.ShowGrid;
|
||||||
@@ -181,6 +182,7 @@ namespace AGVMapEditor.Forms
|
|||||||
_hasChanges = true;
|
_hasChanges = true;
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
RefreshNodeList();
|
RefreshNodeList();
|
||||||
|
RefreshMagnetList(); // 추가
|
||||||
// RFID 자동 할당
|
// RFID 자동 할당
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,6 +216,20 @@ namespace AGVMapEditor.Forms
|
|||||||
{
|
{
|
||||||
// 단일 선택은 기존 방식 사용
|
// 단일 선택은 기존 방식 사용
|
||||||
_selectedNode = nodes[0];
|
_selectedNode = nodes[0];
|
||||||
|
|
||||||
|
// Sync with lstMagnet
|
||||||
|
if (_selectedNode is MapMagnet magnet)
|
||||||
|
{
|
||||||
|
lstMagnet.SelectedItem = magnet;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lstMagnet.SelectedItem = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//this._mapCanvas.SelectedNode = nodes;
|
||||||
|
//this._mapCanvas.Invalidate();
|
||||||
|
|
||||||
UpdateNodeProperties();
|
UpdateNodeProperties();
|
||||||
UpdateImageEditButton();
|
UpdateImageEditButton();
|
||||||
}
|
}
|
||||||
@@ -241,17 +257,20 @@ namespace AGVMapEditor.Forms
|
|||||||
_hasChanges = true;
|
_hasChanges = true;
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
RefreshNodeList();
|
RefreshNodeList();
|
||||||
|
RefreshMagnetList(); // 추가
|
||||||
ClearNodeProperties();
|
ClearNodeProperties();
|
||||||
// RFID 자동 할당
|
// RFID 자동 할당
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnConnectionCreated(object sender, (MapNode From, MapNode To) connection)
|
private void OnConnectionCreated(object sender, (MapNode From, MapNode To) connection)
|
||||||
{
|
{
|
||||||
_hasChanges = true;
|
_hasChanges = true;
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
|
RefreshNodeConnectionList();
|
||||||
UpdateNodeProperties(); // 연결 정보 업데이트
|
UpdateNodeProperties(); // 연결 정보 업데이트
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void OnConnectionDeleted(object sender, (MapNode From, MapNode To) connection)
|
private void OnConnectionDeleted(object sender, (MapNode From, MapNode To) connection)
|
||||||
{
|
{
|
||||||
_hasChanges = true;
|
_hasChanges = true;
|
||||||
@@ -260,6 +279,8 @@ namespace AGVMapEditor.Forms
|
|||||||
UpdateNodeProperties(); // 연결 정보 업데이트
|
UpdateNodeProperties(); // 연결 정보 업데이트
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void OnImageDoubleClicked(object sender, MapImage image)
|
private void OnImageDoubleClicked(object sender, MapImage image)
|
||||||
{
|
{
|
||||||
// 이미지 노드 더블클릭 시 이미지 편집창 표시
|
// 이미지 노드 더블클릭 시 이미지 편집창 표시
|
||||||
@@ -279,6 +300,7 @@ namespace AGVMapEditor.Forms
|
|||||||
{
|
{
|
||||||
_hasChanges = true;
|
_hasChanges = true;
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
|
RefreshMagnetDirectionList(); // 방향 정보 업데이트
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBackgroundClicked(object sender, Point location)
|
private void OnBackgroundClicked(object sender, Point location)
|
||||||
@@ -402,7 +424,7 @@ namespace AGVMapEditor.Forms
|
|||||||
var nodeId = GenerateNodeId();
|
var nodeId = GenerateNodeId();
|
||||||
var position = new Point(100 + this._mapCanvas.Nodes.Count * 50, 100 + this._mapCanvas.Nodes.Count * 50);
|
var position = new Point(100 + this._mapCanvas.Nodes.Count * 50, 100 + this._mapCanvas.Nodes.Count * 50);
|
||||||
|
|
||||||
var node = new MapNode(nodeId, position, StationType.Normal);
|
var node = new MapNode(nodeId, position, Station.Normal);
|
||||||
|
|
||||||
this._mapCanvas.Nodes.Add(node);
|
this._mapCanvas.Nodes.Add(node);
|
||||||
_hasChanges = true;
|
_hasChanges = true;
|
||||||
@@ -620,7 +642,7 @@ namespace AGVMapEditor.Forms
|
|||||||
private void LoadMapFromFile(string filePath)
|
private void LoadMapFromFile(string filePath)
|
||||||
{
|
{
|
||||||
var result = MapLoader.LoadMapFromFile(filePath);
|
var result = MapLoader.LoadMapFromFile(filePath);
|
||||||
|
sbFile.Text = filePath;
|
||||||
if (result.Success)
|
if (result.Success)
|
||||||
{
|
{
|
||||||
// 맵 캔버스에 데이터 설정
|
// 맵 캔버스에 데이터 설정
|
||||||
@@ -636,6 +658,7 @@ namespace AGVMapEditor.Forms
|
|||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
UpdateNodeList();
|
UpdateNodeList();
|
||||||
RefreshNodeConnectionList();
|
RefreshNodeConnectionList();
|
||||||
|
RefreshMagnetList(); // 추가
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -766,6 +789,7 @@ namespace AGVMapEditor.Forms
|
|||||||
{
|
{
|
||||||
RefreshNodeList();
|
RefreshNodeList();
|
||||||
RefreshNodeConnectionList();
|
RefreshNodeConnectionList();
|
||||||
|
RefreshMagnetList(); // 추가
|
||||||
RefreshMapCanvas();
|
RefreshMapCanvas();
|
||||||
ClearNodeProperties();
|
ClearNodeProperties();
|
||||||
}
|
}
|
||||||
@@ -773,7 +797,7 @@ namespace AGVMapEditor.Forms
|
|||||||
private void RefreshNodeList()
|
private void RefreshNodeList()
|
||||||
{
|
{
|
||||||
listBoxNodes.DataSource = null;
|
listBoxNodes.DataSource = null;
|
||||||
listBoxNodes.DataSource = this._mapCanvas.Nodes;
|
listBoxNodes.DataSource = this._mapCanvas.Items;
|
||||||
listBoxNodes.DisplayMember = "DisplayText";
|
listBoxNodes.DisplayMember = "DisplayText";
|
||||||
listBoxNodes.ValueMember = "Id";
|
listBoxNodes.ValueMember = "Id";
|
||||||
|
|
||||||
@@ -789,7 +813,7 @@ namespace AGVMapEditor.Forms
|
|||||||
|
|
||||||
private void ListBoxNodes_SelectedIndexChanged(object sender, EventArgs e)
|
private void ListBoxNodes_SelectedIndexChanged(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (listBoxNodes.SelectedItem is MapNode selectedNode)
|
if (listBoxNodes.SelectedItem is NodeBase selectedNode)
|
||||||
{
|
{
|
||||||
_selectedNode = selectedNode;
|
_selectedNode = selectedNode;
|
||||||
UpdateNodeProperties();
|
UpdateNodeProperties();
|
||||||
@@ -826,9 +850,9 @@ namespace AGVMapEditor.Forms
|
|||||||
case NodeType.Normal:
|
case NodeType.Normal:
|
||||||
|
|
||||||
var item = node as MapNode;
|
var item = node as MapNode;
|
||||||
if (item.StationType == StationType.Normal)
|
if (item.StationType == Station.Normal)
|
||||||
foreColor = Color.DimGray;
|
foreColor = Color.DimGray;
|
||||||
else if (item.StationType == StationType.Charger)
|
else if (item.StationType == Station.Charger)
|
||||||
foreColor = Color.Red;
|
foreColor = Color.Red;
|
||||||
else
|
else
|
||||||
foreColor = Color.DarkGreen;
|
foreColor = Color.DarkGreen;
|
||||||
@@ -907,10 +931,8 @@ namespace AGVMapEditor.Forms
|
|||||||
connections.Add(new NodeConnectionInfo
|
connections.Add(new NodeConnectionInfo
|
||||||
{
|
{
|
||||||
FromNodeId = firstNode.Id,
|
FromNodeId = firstNode.Id,
|
||||||
FromNodeName = "",
|
|
||||||
FromRfidId = firstNode.RfidId,
|
FromRfidId = firstNode.RfidId,
|
||||||
ToNodeId = secondNode.Id,
|
ToNodeId = secondNode.Id,
|
||||||
ToNodeName = "",
|
|
||||||
ToRfidId = secondNode.RfidId,
|
ToRfidId = secondNode.RfidId,
|
||||||
ConnectionType = "양방향" // 모든 연결이 양방향
|
ConnectionType = "양방향" // 모든 연결이 양방향
|
||||||
});
|
});
|
||||||
@@ -923,6 +945,7 @@ namespace AGVMapEditor.Forms
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 리스트박스에 표시
|
// 리스트박스에 표시
|
||||||
|
lstNodeConnection.Font = new Font("돋움체", 10);
|
||||||
lstNodeConnection.DataSource = null;
|
lstNodeConnection.DataSource = null;
|
||||||
lstNodeConnection.DataSource = connections;
|
lstNodeConnection.DataSource = connections;
|
||||||
lstNodeConnection.DisplayMember = "ToString";
|
lstNodeConnection.DisplayMember = "ToString";
|
||||||
@@ -945,13 +968,21 @@ namespace AGVMapEditor.Forms
|
|||||||
_mapCanvas?.HighlightConnection(connectionInfo.FromNodeId, connectionInfo.ToNodeId);
|
_mapCanvas?.HighlightConnection(connectionInfo.FromNodeId, connectionInfo.ToNodeId);
|
||||||
|
|
||||||
// 연결된 노드들을 맵에서 하이라이트 표시 (선택적)
|
// 연결된 노드들을 맵에서 하이라이트 표시 (선택적)
|
||||||
var fromNode = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == connectionInfo.FromNodeId);
|
//var fromNode = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == connectionInfo.FromNodeId);
|
||||||
if (fromNode != null)
|
//if (fromNode != null)
|
||||||
{
|
//{
|
||||||
_selectedNode = fromNode;
|
// if (_selectedNode != fromNode)
|
||||||
UpdateNodeProperties();
|
// {
|
||||||
_mapCanvas?.Invalidate();
|
// _selectedNode = fromNode;
|
||||||
}
|
// _mapCanvas.SelectedNode = fromNode; // 캔버스 선택 상태 동기화
|
||||||
|
|
||||||
|
// // 속성창 업데이트 (리스트 리프레시 포함)
|
||||||
|
// // 주의: RefreshMagnetDirectionList()가 호출되어도 lstNodeConnection에는 영향이 없으므로 안전함
|
||||||
|
// UpdateNodeProperties();
|
||||||
|
|
||||||
|
// _mapCanvas?.Invalidate();
|
||||||
|
// }
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -984,6 +1015,9 @@ namespace AGVMapEditor.Forms
|
|||||||
|
|
||||||
// 이미지 노드인 경우 편집 버튼 활성화
|
// 이미지 노드인 경우 편집 버튼 활성화
|
||||||
UpdateImageEditButton();
|
UpdateImageEditButton();
|
||||||
|
|
||||||
|
// 마그넷 방향 리스트 업데이트
|
||||||
|
RefreshMagnetDirectionList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -999,6 +1033,7 @@ namespace AGVMapEditor.Forms
|
|||||||
{
|
{
|
||||||
_propertyGrid.SelectedObject = null;
|
_propertyGrid.SelectedObject = null;
|
||||||
DisableImageEditButton();
|
DisableImageEditButton();
|
||||||
|
lstMagnetDirection.DataSource = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1104,6 +1139,13 @@ namespace AGVMapEditor.Forms
|
|||||||
// 변경된 속성명 디버그 출력
|
// 변경된 속성명 디버그 출력
|
||||||
System.Diagnostics.Debug.WriteLine($"[PropertyGrid] 속성 변경됨: {e.ChangedItem.PropertyDescriptor.Name}");
|
System.Diagnostics.Debug.WriteLine($"[PropertyGrid] 속성 변경됨: {e.ChangedItem.PropertyDescriptor.Name}");
|
||||||
|
|
||||||
|
// 🔥 MagnetDirectionInfo 변경 처리
|
||||||
|
if (_propertyGrid.SelectedObject is MagnetDirectionInfo magInfo)
|
||||||
|
{
|
||||||
|
ApplyDirectionChange(magInfo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// RFID 값 변경시 중복 검사
|
// RFID 값 변경시 중복 검사
|
||||||
if (e.ChangedItem.PropertyDescriptor.Name == "RFID")
|
if (e.ChangedItem.PropertyDescriptor.Name == "RFID")
|
||||||
{
|
{
|
||||||
@@ -1292,9 +1334,9 @@ namespace AGVMapEditor.Forms
|
|||||||
{
|
{
|
||||||
foreach (var node in this._mapCanvas.Nodes)
|
foreach (var node in this._mapCanvas.Nodes)
|
||||||
{
|
{
|
||||||
node.CanTurnLeft = true;
|
node.CanTurnLeft = false;
|
||||||
node.CanTurnRight = true;
|
node.CanTurnRight = false;
|
||||||
node.DisableCross = false;
|
node.DisableCross = true;
|
||||||
node.ModifiedDate = DateTime.Now;
|
node.ModifiedDate = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1311,5 +1353,560 @@ namespace AGVMapEditor.Forms
|
|||||||
UpdateStatusBar($"모든 노드의 회전/교차 속성 활성화 완료");
|
UpdateStatusBar($"모든 노드의 회전/교차 속성 활성화 완료");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 마그넷 방향 정보를 표현하는 클래스
|
||||||
|
/// </summary>
|
||||||
|
public class MagnetDirectionInfo
|
||||||
|
{
|
||||||
|
[JsonIgnore, Browsable(false)]
|
||||||
|
public MapNode FromNode { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore, Browsable(false)]
|
||||||
|
public MapNode ToNode { get; set; }
|
||||||
|
|
||||||
|
[Category("연결 정보")]
|
||||||
|
[DisplayName("출발 노드")]
|
||||||
|
[Description("출발 노드의 ID입니다.")]
|
||||||
|
[ReadOnly(true)]
|
||||||
|
public string FromNodeId => FromNode?.ID2 ?? "Unknown";
|
||||||
|
|
||||||
|
[Category("연결 정보")]
|
||||||
|
[DisplayName("도착 노드")]
|
||||||
|
[Description("도착 노드의 ID입니다.")]
|
||||||
|
[ReadOnly(true)]
|
||||||
|
public string ToNodeId => ToNode?.ID2 ?? "Unknown";
|
||||||
|
|
||||||
|
[Category("설정")]
|
||||||
|
[DisplayName("방향")]
|
||||||
|
[Description("이동할 마그넷 방향입니다.")]
|
||||||
|
public MagnetPosition? Direction { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
string dirStr = Direction.HasValue ? Direction.Value.ToString() : "None";
|
||||||
|
|
||||||
|
string fromStr = FromNode != null ? FromNode.ID2 : "Unknown";
|
||||||
|
string toStr = ToNode != null ? ToNode.ID2 : "Unknown";
|
||||||
|
|
||||||
|
return $"{fromStr} -> {toStr} : {dirStr}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void RefreshMagnetDirectionList()
|
||||||
|
{
|
||||||
|
// 이벤트 임시 제거 (DataSource 변경 시 불필요한 이벤트 발생 방지)
|
||||||
|
lstMagnetDirection.SelectedIndexChanged -= LstMagnetDirection_SelectedIndexChanged;
|
||||||
|
|
||||||
|
// 현재 선택된 항목 기억
|
||||||
|
int selectedIndex = lstMagnetDirection.SelectedIndex;
|
||||||
|
|
||||||
|
// 데이터 소스 초기화 (UI 갱신 강제)
|
||||||
|
lstMagnetDirection.DataSource = null;
|
||||||
|
lstMagnetDirection.Items.Clear();
|
||||||
|
|
||||||
|
if (this._mapCanvas.Nodes == null)
|
||||||
|
{
|
||||||
|
// 이벤트 다시 연결 (빠른 리턴 시에도 연결 필요)
|
||||||
|
lstMagnetDirection.SelectedIndexChanged += LstMagnetDirection_SelectedIndexChanged;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var directions = new List<MagnetDirectionInfo>();
|
||||||
|
|
||||||
|
// 모든 노드 검색
|
||||||
|
foreach (var nodeItem in this._mapCanvas.Nodes)
|
||||||
|
{
|
||||||
|
if (nodeItem is MapNode node)
|
||||||
|
{
|
||||||
|
if (node.MagnetDirections != null && node.MagnetDirections.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var kvp in node.MagnetDirections)
|
||||||
|
{
|
||||||
|
var neighborId = kvp.Key;
|
||||||
|
var dir = kvp.Value;
|
||||||
|
var neighbor = this._mapCanvas.Nodes.FirstOrDefault(t => t.Id == neighborId);
|
||||||
|
|
||||||
|
directions.Add(new MagnetDirectionInfo
|
||||||
|
{
|
||||||
|
FromNode = node,
|
||||||
|
ToNode = neighbor,
|
||||||
|
Direction = dir
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 보기 좋게 정렬 (FromNode ID 순)
|
||||||
|
directions.Sort((a, b) => string.Compare(a.FromNode.Id, b.FromNode.Id));
|
||||||
|
|
||||||
|
if (directions.Count > 0)
|
||||||
|
{
|
||||||
|
lstMagnetDirection.DataSource = directions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 이벤트 다시 연결
|
||||||
|
lstMagnetDirection.SelectedIndexChanged += LstMagnetDirection_SelectedIndexChanged;
|
||||||
|
|
||||||
|
lstMagnetDirection.DoubleClick -= LstMagnetDirection_DoubleClick;
|
||||||
|
lstMagnetDirection.DoubleClick += LstMagnetDirection_DoubleClick;
|
||||||
|
|
||||||
|
// 선택 항목 복원 (가능한 경우) -> 선택된 객체가 다르게 생성되므로 인덱스로 복원 시도
|
||||||
|
if (selectedIndex >= 0 && selectedIndex < lstMagnetDirection.Items.Count)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lstMagnetDirection.SelectedIndex = selectedIndex;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstMagnetDirection_SelectedIndexChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (lstMagnetDirection.SelectedItem is MagnetDirectionInfo info)
|
||||||
|
{
|
||||||
|
// 버튼 상태 업데이트
|
||||||
|
UpdateDirectionButtons(info);
|
||||||
|
|
||||||
|
// 캔버스에서 해당 연결선 강조 표시
|
||||||
|
if (info.FromNode != null && info.ToNode != null)
|
||||||
|
{
|
||||||
|
_mapCanvas.HighlightConnection(info.FromNode.Id, info.ToNode.Id);
|
||||||
|
|
||||||
|
// FromNode 선택 (속성창 갱신 루프 방지 위해 _propertyGrid 직접 설정 고려)
|
||||||
|
// 하지만 _selectedNode 변경 시 RefreshMagnetDirectionList()가 호출되어 리스트가 재생성되면 선택이 풀릴 수 있음
|
||||||
|
// 따라서 여기서는 캔버스 상의 선택 표시만 변경하고, 전체 속성 업데이트(리스트 리프레시 포함)는 건너뛰거나
|
||||||
|
// 리스트 리프레시 로직에서 선택 상태 유지를 보완해야 함.
|
||||||
|
|
||||||
|
// 일단 _selectedNode를 변경하되, RefreshMagnetDirectionList에서 선택 인덱스 복원을 하므로 괜찮을 것으로 예상됨.
|
||||||
|
// 만약 깜빡임이나 끊김이 심하면 UpdateNodeProperties 내의 RefreshMagnetDirectionList 호출을 조건부로 변경해야 함.
|
||||||
|
|
||||||
|
if (_selectedNode != info.FromNode)
|
||||||
|
{
|
||||||
|
var prevSelected = _selectedNode;
|
||||||
|
_selectedNode = info.FromNode;
|
||||||
|
|
||||||
|
// _mapCanvas.SelectedNode 설정 (이것만으로는 PropertyGrid 갱신 안됨)
|
||||||
|
_mapCanvas.SelectedNode = info.FromNode;
|
||||||
|
|
||||||
|
// PropertyGrid 갱신 (리스트 리프레시 포함)
|
||||||
|
// 주의: 여기서 UpdateNodeProperties()를 부르면 리스트가 다시 그려지면서 선택 이벤트가 다시 발생할 수 있음.
|
||||||
|
// 하지만 인덱스 복원 로직이 있으므로 무한루프는 아닐 수 있으나, 비효율적임.
|
||||||
|
|
||||||
|
// 해결책: 리스트 리프레시 없이 속성창만 갱신
|
||||||
|
_propertyGrid.SelectedObject = _selectedNode;
|
||||||
|
UpdateImageEditButton();
|
||||||
|
|
||||||
|
// 캔버스 다시 그리기
|
||||||
|
_mapCanvas.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_mapCanvas.ClearHighlightedConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstMagnetDirection_DoubleClick(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (lstMagnetDirection.SelectedItem is MagnetDirectionInfo info)
|
||||||
|
{
|
||||||
|
var node = info.FromNode;
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
// 방향 순환
|
||||||
|
if (info.Direction == MagnetPosition.S) info.Direction = MagnetPosition.L;
|
||||||
|
else if (info.Direction == MagnetPosition.L) info.Direction = MagnetPosition.R;
|
||||||
|
else if (info.Direction == MagnetPosition.R) info.Direction = MagnetPosition.S;
|
||||||
|
|
||||||
|
ApplyDirectionChange(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyDirectionChange(MagnetDirectionInfo info)
|
||||||
|
{
|
||||||
|
var node = info.FromNode;
|
||||||
|
if (node == null) return;
|
||||||
|
|
||||||
|
// 딕셔너리 업데이트
|
||||||
|
if (node.MagnetDirections == null)
|
||||||
|
node.MagnetDirections = new Dictionary<string, MagnetPosition>();
|
||||||
|
|
||||||
|
if (info.ToNode != null)
|
||||||
|
{
|
||||||
|
if (info.Direction == null)
|
||||||
|
{
|
||||||
|
if (node.MagnetDirections.ContainsKey(info.ToNode.Id))
|
||||||
|
node.MagnetDirections.Remove(info.ToNode.Id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node.MagnetDirections[info.ToNode.Id] = info.Direction.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_hasChanges = true;
|
||||||
|
UpdateTitle();
|
||||||
|
|
||||||
|
// 리스트 갱신
|
||||||
|
RefreshMagnetDirectionList();
|
||||||
|
|
||||||
|
// 캔버스 등 갱신
|
||||||
|
_mapCanvas.Invalidate();
|
||||||
|
|
||||||
|
// 속성창 갱신 (선택된 객체가 바뀌었을 수 있으므로 다시 설정은 RefreshMagnetDirectionList의 선택 복원에서 처리됨)
|
||||||
|
_propertyGrid.Refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btMakeDirdata_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toolStripButton3_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (this._mapCanvas.Nodes == null || this._mapCanvas.Nodes.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 기존 목록을 모두 지울지 물어보고
|
||||||
|
var result = MessageBox.Show(
|
||||||
|
"마그넷방향을 자동 생성할까요? 없는 부분만 추가됩니다.",
|
||||||
|
"마그넷 방향 자동 생성",
|
||||||
|
MessageBoxButtons.YesNo,
|
||||||
|
MessageBoxIcon.Question);
|
||||||
|
|
||||||
|
if (result != DialogResult.Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool clearAll = false;// (result == DialogResult.Yes);
|
||||||
|
|
||||||
|
int updateCount = 0;
|
||||||
|
|
||||||
|
foreach (var node in this._mapCanvas.Nodes)
|
||||||
|
{
|
||||||
|
// 연결 노드가 3개 이상인 노드들을 찾아서
|
||||||
|
if (node.Type == NodeType.Normal && node is MapNode mapNode)
|
||||||
|
{
|
||||||
|
if (clearAll)
|
||||||
|
{
|
||||||
|
if (mapNode.MagnetDirections != null)
|
||||||
|
mapNode.MagnetDirections.Clear();
|
||||||
|
else mapNode.MagnetDirections = new Dictionary<string, MagnetPosition>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapNode.ConnectedNodes.Count >= 3)
|
||||||
|
{
|
||||||
|
// 마그넷 방향 딕셔너리가 없으면 생성
|
||||||
|
if (mapNode.MagnetDirections == null)
|
||||||
|
mapNode.MagnetDirections = new Dictionary<string, MagnetPosition>();
|
||||||
|
|
||||||
|
foreach (var connectedId in mapNode.ConnectedNodes)
|
||||||
|
{
|
||||||
|
// 이미 설정된 경우 건너뜀 (모두 초기화 안 한 경우)
|
||||||
|
if (!clearAll && mapNode.MagnetDirections.ContainsKey(connectedId))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 모두 자동 생성을 해준다 (기본은 직진으로)
|
||||||
|
mapNode.MagnetDirections[connectedId] = MagnetPosition.S;
|
||||||
|
updateCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateCount > 0)
|
||||||
|
{
|
||||||
|
_hasChanges = true;
|
||||||
|
UpdateTitle();
|
||||||
|
|
||||||
|
// 현재 선택된 노드의 속성창 및 리스트 갱신
|
||||||
|
UpdateNodeProperties();
|
||||||
|
|
||||||
|
MessageBox.Show($"총 {updateCount}개의 연결에 대해 마그넷 방향(직진)이 설정되었습니다.", "완료", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MessageBox.Show("변경된 사항이 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btDirDelete_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (lstMagnetDirection.SelectedItem is MagnetDirectionInfo info)
|
||||||
|
{
|
||||||
|
// 선택된 방향정보를 삭제한다.
|
||||||
|
var result = MessageBox.Show(
|
||||||
|
$"선택한 마그넷 방향 정보를 삭제하시겠습니까?\n{info.FromNodeId} -> {info.ToNodeId} : {info.Direction}",
|
||||||
|
"삭제 확인",
|
||||||
|
MessageBoxButtons.YesNo,
|
||||||
|
MessageBoxIcon.Question);
|
||||||
|
|
||||||
|
if (result == DialogResult.Yes)
|
||||||
|
{
|
||||||
|
if (info.FromNode != null && info.FromNode.MagnetDirections != null)
|
||||||
|
{
|
||||||
|
if (info.ToNode != null && info.FromNode.MagnetDirections.ContainsKey(info.ToNode.Id))
|
||||||
|
{
|
||||||
|
info.FromNode.MagnetDirections.Remove(info.ToNode.Id);
|
||||||
|
|
||||||
|
_hasChanges = true;
|
||||||
|
UpdateTitle();
|
||||||
|
|
||||||
|
// 리스트 및 UI 갱신
|
||||||
|
RefreshMagnetDirectionList();
|
||||||
|
|
||||||
|
// 캔버스 갱신
|
||||||
|
_mapCanvas.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MessageBox.Show("삭제할 항목을 선택해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button1_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
//set left
|
||||||
|
if (lstMagnetDirection.SelectedItem is MagnetDirectionInfo info)
|
||||||
|
{
|
||||||
|
info.Direction = MagnetPosition.L;
|
||||||
|
ApplyDirectionChange(info);
|
||||||
|
|
||||||
|
UpdateDirectionButtons(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button2_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
//set straight
|
||||||
|
if (lstMagnetDirection.SelectedItem is MagnetDirectionInfo info)
|
||||||
|
{
|
||||||
|
info.Direction = MagnetPosition.S;
|
||||||
|
ApplyDirectionChange(info);
|
||||||
|
|
||||||
|
UpdateDirectionButtons(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button3_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
//set right
|
||||||
|
if (lstMagnetDirection.SelectedItem is MagnetDirectionInfo info)
|
||||||
|
{
|
||||||
|
info.Direction = MagnetPosition.R;
|
||||||
|
ApplyDirectionChange(info);
|
||||||
|
|
||||||
|
UpdateDirectionButtons(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateDirectionButtons(MagnetDirectionInfo info)
|
||||||
|
{
|
||||||
|
button1.BackColor = SystemColors.Control;
|
||||||
|
button2.BackColor = SystemColors.Control;
|
||||||
|
button3.BackColor = SystemColors.Control;
|
||||||
|
|
||||||
|
if (info.Direction == MagnetPosition.L) button1.BackColor = Color.Lime;
|
||||||
|
else if (info.Direction == MagnetPosition.S) button2.BackColor = Color.Lime;
|
||||||
|
else if (info.Direction == MagnetPosition.R) button3.BackColor = Color.Lime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toolStripButton2_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var result = MessageBox.Show(
|
||||||
|
"기존 설정된 마그넷 방향 정보를 모두 초기화하시겠습니까?",
|
||||||
|
"마그넷 방향 일괄 삭제",
|
||||||
|
MessageBoxButtons.YesNoCancel,
|
||||||
|
MessageBoxIcon.Question);
|
||||||
|
|
||||||
|
if (result == DialogResult.Cancel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool clearAll = (result == DialogResult.Yes);
|
||||||
|
|
||||||
|
int updateCount = 0;
|
||||||
|
|
||||||
|
foreach (var node in this._mapCanvas.Nodes)
|
||||||
|
{
|
||||||
|
// 연결 노드가 3개 이상인 노드들을 찾아서
|
||||||
|
if (node.Type == NodeType.Normal && node is MapNode mapNode)
|
||||||
|
{
|
||||||
|
if (clearAll)
|
||||||
|
{
|
||||||
|
if (mapNode.MagnetDirections != null)
|
||||||
|
mapNode.MagnetDirections.Clear();
|
||||||
|
else mapNode.MagnetDirections = new Dictionary<string, MagnetPosition>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 현재 선택된 노드의 속성창 및 리스트 갱신
|
||||||
|
UpdateNodeProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btAddMagnet_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// 마그넷 추가
|
||||||
|
var result = MessageBox.Show("곡선 마그넷(Bezier)을 추가하시겠습니까?\n(예: 베지어 곡선, 아니오: 직선, 취소: 중단)",
|
||||||
|
"마그넷 타입 선택", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
|
||||||
|
|
||||||
|
if (result == DialogResult.Cancel) return;
|
||||||
|
|
||||||
|
bool isBezier = (result == DialogResult.Yes);
|
||||||
|
|
||||||
|
// 화면 중앙 좌표 계산 (World Coordinate)
|
||||||
|
float zoom = _mapCanvas.ZoomFactor;
|
||||||
|
PointF pan = _mapCanvas.PanOffset;
|
||||||
|
|
||||||
|
float worldCX = (_mapCanvas.Width / 2f) / zoom - pan.X;
|
||||||
|
float worldCY = (_mapCanvas.Height / 2f) / zoom - pan.Y;
|
||||||
|
|
||||||
|
// 고유 ID 생성
|
||||||
|
string id = _mapCanvas.GenerateUniqueNodeId();
|
||||||
|
|
||||||
|
var magnet = new MapMagnet { Id = id };
|
||||||
|
|
||||||
|
// 점 생성 시 정규화(Snap) 처리
|
||||||
|
int cx = (int)worldCX;
|
||||||
|
int cy = (int)worldCY;
|
||||||
|
|
||||||
|
magnet.StartPoint = new Point(cx - 50, cy);
|
||||||
|
magnet.EndPoint = new Point(cx + 50, cy);
|
||||||
|
|
||||||
|
if (isBezier)
|
||||||
|
{
|
||||||
|
magnet.ControlPoint = new MapMagnet.MagnetPoint { X = cx, Y = cy - 50 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 캔버스에 추가
|
||||||
|
_mapCanvas.Magnets.Add(magnet);
|
||||||
|
_hasChanges = true;
|
||||||
|
|
||||||
|
UpdateTitle();
|
||||||
|
RefreshMapCanvas();
|
||||||
|
RefreshNodeList();
|
||||||
|
RefreshMagnetList(); // 추가
|
||||||
|
|
||||||
|
// 추가된 마그넷 선택
|
||||||
|
//_mapCanvas.SelectedNode = magnet;
|
||||||
|
UpdateNodeProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btDelMagnet_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
//선택한 마그넷라인을 삭제 (삭제 후 맵에 바로 반영되도록 업데이트필요)
|
||||||
|
if (lstMagnet.SelectedItem is MapMagnet magnet)
|
||||||
|
{
|
||||||
|
_mapCanvas.RemoveMagnet(magnet);
|
||||||
|
RefreshMagnetList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshMagnetList()
|
||||||
|
{
|
||||||
|
lstMagnet.DataSource = null;
|
||||||
|
lstMagnet.Items.Clear();
|
||||||
|
|
||||||
|
if (_mapCanvas.Magnets != null && _mapCanvas.Magnets.Count > 0)
|
||||||
|
{
|
||||||
|
lstMagnet.DataSource = _mapCanvas.Magnets;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 이벤트 연결
|
||||||
|
lstMagnet.SelectedIndexChanged -= LstMagnet_SelectedIndexChanged;
|
||||||
|
lstMagnet.SelectedIndexChanged += LstMagnet_SelectedIndexChanged;
|
||||||
|
|
||||||
|
lstMagnet.DoubleClick -= LstMagnet_DoubleClick;
|
||||||
|
lstMagnet.DoubleClick += LstMagnet_DoubleClick;
|
||||||
|
|
||||||
|
lstMagnet.DrawMode = DrawMode.OwnerDrawFixed;
|
||||||
|
lstMagnet.DrawItem -= LstMagnet_DrawItem;
|
||||||
|
lstMagnet.DrawItem += LstMagnet_DrawItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstMagnet_DrawItem(object sender, DrawItemEventArgs e)
|
||||||
|
{
|
||||||
|
e.DrawBackground();
|
||||||
|
|
||||||
|
if (e.Index >= 0 && e.Index < lstMagnet.Items.Count)
|
||||||
|
{
|
||||||
|
var magnet = lstMagnet.Items[e.Index] as MapMagnet;
|
||||||
|
if (magnet != null)
|
||||||
|
{
|
||||||
|
Brush brush = Brushes.Black;
|
||||||
|
if (magnet.ControlPoint != null) // Curve
|
||||||
|
{
|
||||||
|
brush = Brushes.Blue; // Curve는 파란색
|
||||||
|
}
|
||||||
|
|
||||||
|
// 선택된 항목은 흰색 글씨 (배경이 파란색이므로)
|
||||||
|
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
|
||||||
|
{
|
||||||
|
brush = Brushes.White;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Graphics.DrawString(magnet.ToString(), e.Font, brush, e.Bounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e.DrawFocusRectangle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstMagnet_SelectedIndexChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (lstMagnet.SelectedItem is MapMagnet magnet)
|
||||||
|
{
|
||||||
|
_mapCanvas.SelectedNode = magnet;
|
||||||
|
//UpdateNodeProperties(); // SelectedNode setter에서 Invalidate 호출됨
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LstMagnet_DoubleClick(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (lstMagnet.SelectedItem is MapMagnet magnet)
|
||||||
|
{
|
||||||
|
_mapCanvas.PanTo(magnet.Position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnAddNode_ButtonClick(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnAddNode_BackColorChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnDeleteConnection_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
_mapCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.DeleteConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnConnDir_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
//방향연결(노드연결과 유사), 두 노드간의 방향을 생성한다 기본값 straight
|
||||||
|
_mapCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.ConnectDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toolStripButton3_Click_1(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
RefreshMagnetList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,8 +123,29 @@
|
|||||||
<metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
<metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
<value>249, 17</value>
|
<value>249, 17</value>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
<metadata name="toolStrip4.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>17, 56</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="toolStrip5.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>123, 56</value>
|
||||||
|
</metadata>
|
||||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||||
<data name="btNodeRemove.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btDelMagnet.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
|
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||||
|
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||||
|
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||||
|
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||||
|
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||||
|
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||||
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="toolStripButton3.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
@@ -142,18 +163,84 @@
|
|||||||
<metadata name="toolStrip3.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
<metadata name="toolStrip3.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
<value>462, 17</value>
|
<value>462, 17</value>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
<metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>249, 17</value>
|
||||||
|
</metadata>
|
||||||
|
<data name="btNodeRemove.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
|
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||||
|
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||||
|
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||||
|
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||||
|
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||||
|
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||||
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<metadata name="toolStrip4.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>17, 56</value>
|
||||||
|
</metadata>
|
||||||
|
<data name="btDirDelete.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
|
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||||
|
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||||
|
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||||
|
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||||
|
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||||
|
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||||
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="btMakeDirdata.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
|
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||||
|
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||||
|
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||||
|
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||||
|
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||||
|
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||||
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="toolStripButton2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
|
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||||
|
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||||
|
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||||
|
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||||
|
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||||
|
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||||
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
<data name="btnSelect.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnSelect.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHrSURBVDhPldBLaBNBHMfx/0kUVBBJ0lxWPIhihBJKyAqS
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHsSURBVDhPldBLaBNBHMfx/0kUVBBJ0lxWPIhihBJKyAqS
|
||||||
pHkQIS+9hXg3RhQviicrITnmJqFnQQ8RqiamRqkhj6VCQtuIQaVQcxdq3d3s61Dy0w002KnU9nP67+x8
|
pHkQIS+9hXg3RhQviicrITnmJqFnQQ8RqiamRqkhj6VCQtuIQaVQc64H6+5mX4eSn26gwU6ltp/Tf2fn
|
||||||
h2GIDmD0kT+mLk/fZNf3pQkznCrM3DFnZflSRG05euast7izcpM72GGqMP1ZFRw1tXm+qq9dg9LiHgwb
|
OwxDdACjj/wxdXn6Jru+L02Y4VRh5o45K8uXImrL0TNnvcWdlZvcwQ5ThenPquCoqc3zVX3tGpQW92DY
|
||||||
dnFYP51i9/6T0r4wp39Kwfh2F8bGI2irEYjvTmo/Gpbj7N4JpXNxShUcdbV1DvpaHMb3HNrP4uiVb2Cj
|
sIvD+ukUu/eflPaFOf1TCsa3uzA2HkFbjUB8d1L70bAcZ/dOKJ2LU6rgqKutc9DX4jC+59B+FkevfAMb
|
||||||
cQtadxbSh6OQ3tM82+6iNLk5rXcd7ecJGIaB0WiE1dcp6F9v41eNvmxV6QzbTMjtKYtct9Wi0Si63S50
|
jVvQurOQPhyF9J7m2XYXpcnNab3raD9PwDAMjEYjrL5OQf96G79q9GWrSmfYZkJuT1nkuq0WjUbR7Xah
|
||||||
XUe/30fjaQTG+n1IVRpKb4lnuzFtyc4Nl06VE4kE0uk0CoUCSqUSqvOzMNYfYnORtqVFWhEr9JhtJ+Lx
|
6zr6/T4aTyMw1u9DqtJQeks8241pS3ZuuHSqnEgkkE6nUSgUUCqVUJ2fhbH+EJuLtC0t0opYocdsOxGP
|
||||||
+DjmeR5+vx+xWAzqSgRy3Q65dgJbFeLYZmIndrvd8Pl8sFqt5pWfbL6hbalCl6Uy9cSXlGG7sWQyiXw+
|
x8cxz/Pw+/2IxWJQVyKQ63bItRPYqhDHNhM7sdvths/ng9VqNa/8ZPMNbUsVuiyVqSe+pAzbjSWTSeTz
|
||||||
P469Xi8sFgvMdblCV6RXVDNnvKAjPxfoKttSOBxGLpfbE+8QFyj09/cugUAA2WwWLpcLHo9nT7yvTCaD
|
+XHs9XphsVhgrssVuiK9opo54wUd+blAV9mWwuEwcrncnniHuEChv793CQQCyGazcLlc8Hg8e+J9ZTIZ
|
||||||
wWAAp9OJUCh0uNhkHtDpdFAsFscPxv7/r2AweM+8ts1mO3z8x29OYwsb4/6fnQAAAABJRU5ErkJggg==
|
DAYDOJ1OhEKhw8Um84BOp4NisTh+MPb/fwWDwXvmtW022+HjP34DP4sLE797GZoAAAAASUVORK5CYII=
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btnMove.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnMove.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
@@ -169,83 +256,83 @@
|
|||||||
YIGDKaeH7rEGFFd1IN1M4c5nAYIcIXLXvmW+uOKfXMvpRO9rFnzJi9lqBKPZYVCedzYsH6SQ2l+Eu2SD
|
YIGDKaeH7rEGFFd1IN1M4c5nAYIcIXLXvmW+uOKfXMvpRO9rFnzJi9lqBKPZYVCedzYsH6SQ2l+Eu2SD
|
||||||
bfNyWeHqqhbxahSCGIM2MwSKrYzDWboBx5sxIsP6yvTPH0lk3YoGI9lhaB8NQZO+gl8Dj7SN1tpAvgAA
|
bfNyWeHqqhbxahSCGIM2MwSKrYzDWboBx5sxIsP6yvTPH0lk3YoGI9lhaB8NQZO+gl8Dj7SN1tpAvgAA
|
||||||
AABJRU5ErkJggg==
|
AABJRU5ErkJggg==
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="btnAddNode.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHZSURBVDhPnZJda9NwFMb3JbxV/BaD4ufZjcwhejsdiqig
|
||||||
|
8+1u8w2vUprNrE3axDWmKW1aQ0rbtPaFUkra2paCotWU9YVHzh8SidMVfSCEnHOe33MgZ61araJSqaBc
|
||||||
|
LqNUKqFYLKJQKMCyLHqfW1sl27axXC5PPb1ej0F0XT8bQslkcByHJXc6HR9CNdM0z4bQyjRMafSdz+et
|
||||||
|
yWSC+XzO6gQ0DOPvEEr1BlOp1Ekmk3Gm0ylGX7pI2Yes1263kU6n/wyhZG/lxWIBMo+/fsKj6BbuHWxA
|
||||||
|
sw9Yr9VqQdM0SJIUhJim6Y7HYzY0m80w/NzFbvQK9t5t46V6C4+jWz6k2WwimUwGIblc7pJhGO5oNGJD
|
||||||
|
H50PuH+4gefHN/FGu4tX6u0ApNFoQJblICSbzYYURXGHwyEbqncLeChs4sXxjg/ZPdpEshRm/VqtBlEU
|
||||||
|
wXHcL4iu66FEIuEOBoNTkNfv7+CZdA1Pj65jvpixLeLxOCKRyAUfQFJVlUH6/b4PefD2Mp7ErmJf2cH3
|
||||||
|
H998M8dxFwNmT7IsMwhdIlvXsbCv3MD0xF1t9iSKYigWi7l0id4vJrMkSavNngRBWBcEwaUDq9fr/2b2
|
||||||
|
xPP8Os/z7n+ZPREkHA6f/73u6SfD/w8v3D5c0gAAAABJRU5ErkJggg==
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btnAddLabel.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnAddLabel.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHISURBVDhPnZJfa9pgFMb7JXa7sW9RkH2y3awro5ddS3Et
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHGSURBVDhPnZJfa9pgFMb7JXa7sm9RkH2y3awro5dbS3Et
|
||||||
u2vHRsRcBEw0iEHiYpSg+Hcm0blUtOocdOCWbEZ5xnnlTXFule2BEN7znuf3HJKz12630Wq10Gg0UK/X
|
u2vHRsRcBEw0gqJxGiUo/q1JdDYVnVoLLbglm1GecV55U5xbZXsghPe85/k9h+TstFotNJtN1Ot11Go1
|
||||||
UavVUK1WUalU6P1gb5eazSZWq9XWMxwOGUTX9fshlEyGwWDAkj3PiyBUsyzrfgiNTM2URudyuVyZz+cI
|
VKtVVCoVlMtlej/Z2aZGo4HlcrnxDAYDBtE07XEIJZOh3++zZMdxAgjVDMN4HEIjUzOl0blUKpVnsxl8
|
||||||
w5DVCWia5t8hlMobDcP4aRjGIAgCfL75DkOdsrt+v49CofBnCCXzkZfLJcj8ZRrg1aGLk6cO3mfWkF6v
|
32d1Auq6/ncIpfLGbDb7M5/P9z3Pw83X78irE3bX6/WQy+X+DKFkPvJisQCZbyce3h3YOHph4XNiBel2
|
||||||
h3w+D0VRNiGWZfmz2Yw1LRYLlhx/7uLyuI+3Zx7OD7sRxHVd5HK5TUipVHpimqY/na6b7PotS746+QTh
|
u8hkMlAUZR1iGIY7nU5Z03w+Z8nhVzbO3/Tw8cTB6UEngNi2jVQqtQ4pFovPdV13J5NVk1m7Y8kXR1cQ
|
||||||
/HoL4jgOVFXdhBSLxZimaf5kMmFN3dZXxA8cvDm9g8QPXOjK+r7T6UCWZQiCcAfRdT2WzWb98Xi8BXkX
|
Tq83IJZlQVXVdUihUAglk0l3PB6zpk7zHuF9Cx+OHyDhfRuasrpvt9uQZRmCIDxANE0LJRIJdzQabUA+
|
||||||
9/D6qIeLow8IwyWbIp1OI5lMPooAJE3TYplMxh+NRhHk7JmNixddXJ128W0eRGZBEB5vmLlUVWUQ2kT2
|
hR28P+zi7PASvr9gU8TjcUSj0d0AQEqn0wwyHA4DyMlLE2evO7g47uDbzAvMgiA8WzNzqarKILSJ7MM1
|
||||||
4Zq3uHz5ET+CcLeZS5blWCqV8mkT+S8ms6Iou81ckiTtS5Lk04LZtv1vZi5RFPdFUfT/y8xFkEQi8fD3
|
7nD+9gt+eP52M5csy6FYLObSJvJfTGZFUbabuSRJ2pMkyaUFM03z38xcoijuiaLo/peZiyCRSOTp73Wu
|
||||||
Otcvn84Wo7k6b1AAAAAASUVORK5CYII=
|
X1R9FoLvbSO9AAAAAElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btnAddImage.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnAddImage.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAG6SURBVDhPnZLditpQFIXnJXrb0rcYkD5YL4pMYS7bKaXM
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAG3SURBVDhPnZLditpQFIXnJXrb0rcYkD5YL4pMYS7bKaX0
|
||||||
O0TMRUp+ETM2EqMERY3BH0TE0ap4UQaZE/TksIZzhpPBOjPSLgghe+/1rQ3ZZ3Eco9frodvtotPpoN1u
|
HSLmIiW/ghmNaJSgqDH4g4hYrYoXpUgT9OSwyj7DyWBtR9oFIWTvvb61IfsqiiIMBgP0+330ej10u110
|
||||||
o9Vqodls8vebs1OKogiMsaNnPp8LiOd5r0N4MjfMZjORPJ1OMwivhWH4OoSvzId5Gv9uNBrN7XYLSqmo
|
Oh202216P7u6pDAMwTk/e5bLpYB4nvc0hJLJsFgsRPJ8Ps8gVAuC4GkIrUzDlEbfrVarvd/vwRgTdQL6
|
||||||
c2AQBC9DeKoc9H1/5/v+LEkS0D+/QWNb9CaTCarV6vMQnixXTtMU3Mzu1kitC6Q/P4LGluiNx2NUKhWY
|
vv93CKXKwWq1eqjX64skScC+fwOLbNGbzWao1Wp/hlCyXDlNU5CZ/9gitW6QfnkNFlmiN51OUalUYJrm
|
||||||
pnkICcOQbDYbMbTf70VyaubB3C9gv76DWRcZZDQaoVwuH0Lq9fqHIAjIer1+hNy2RTK7uQKrXh9BhsMh
|
KSQIgni324mh4/EoklMzD+6+A69+BLduMshkMkG5XD6FNJvNV77vx9vt9gHytSuS+f0deO3zGWQ8HsNx
|
||||||
HMc5hNRqtZzrumS1Wj1CFhGokQe7+fYEMfOgkS76/X4fhmFAUZQniOd5uVKpRJbL5THE+wHmXOLeuARL
|
nFNIo9HIlUqleLPZPEBWIZiRB7//8Agx82ChLvrD4RCGYUBRlEeI53k527bj9Xp9DvE+gTu3+GncgqdM
|
||||||
qdjCsiwUi8V3GYDLdd2cbdtksVhkkL3+Ccz+jKR8hR3ZZmZFUd4fmKUcxxEQfokCMu+Cul/Bdslps5Rh
|
bGFZForF4osMQHJdV0BWq1UGOepvwO23SMp3OMT7zKwoyssTs5TjOAJClyggyz6Y+x78kFw2SxmGkdN1
|
||||||
GDld1wm/RPmLudk0zdNmKU3TzjVNI/zABoPBv5mlVFU9V1WV/JdZikMKhcLbv+tSD5T6HZWMaVplAAAA
|
PaZLlL+YzKZpXjZLaZp2rWlaTAc2Go3+zSylquq1qqrxf5mlCFIoFJ7/Xpf6BUmpHXRPK0SnAAAAAElF
|
||||||
AElFTkSuQmCC
|
TkSuQmCC
|
||||||
</value>
|
|
||||||
</data>
|
|
||||||
<data name="btnAddNode.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
|
||||||
<value>
|
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHdSURBVDhPnZLda9NgFMb3T3ir+F8Min/PbmQO0VvdUEQF
|
|
||||||
nV93bk7xKqXZzNqkzbrGmJKmXUhpm9Z+UEpJW9tSULT6lvWDR94X3kicrugDIeSc8/yeAzkrlUoF5XIZ
|
|
||||||
pVIJxWIRhUIB+XwejuPQ94WVZXJdF4vF4szT7XYZxDCM8yE0mRo8z2PJ7Xbbh9CabdvnQ+jKdJim0e9c
|
|
||||||
LueMx2PMZjNWp0DLsv4Ooal80DTNU9M0vclkguGXDj64B6zXarWQTqf/DKHJfOX5fA5qHn39hCfRDTzY
|
|
||||||
X4Pu7rNes9mErutQFCUIsW2bjEYjNjSdTjH43MF29BpeJm9hT7uDp9ENH9JoNJBKpYKQbDZ7xbIsMhwO
|
|
||||||
2dBH7wQPD9awe7yJt/p9vNbuBiD1eh2qqgYhmUwmpGkaGQwGbKjWyeOxtI5Xx1s+ZPtwHalimPWr1Spk
|
|
||||||
WYYgCL8ghmGEkskk6ff7ZyBv3t/DC+UGnh/exGw+ZVvE43FEIpFLPoBK07RQIpEgvV7Phzx6dxXPYtex
|
|
||||||
c7SF7z+++WZBEC4HzFyqqjIIvUS2rudg5+g2JqdkuZlLluVQLBYj9BL5L6ZmRVGWm7kkSVqVJInQA6vV
|
|
||||||
av9m5hJFcVUURfJfZi4KCYfDF3+vc/0ED18PUDextaQAAAAASUVORK5CYII=
|
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btnDelete.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnDelete.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGvSURBVDhP7Y89T1phGIZpgR/QqTiQ6NDZP+BgCiZd2jA0
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVDhP7Y89S1thGIZToz+gk+kg4uDsH3CQJh0VB0EH
|
||||||
sUNNBx0cjIMDtB3EycGGxY2Evmym7eBGhfgVeuB8cM7hcIDQGKSU7+NBYWlrEw23ed+oMUdr/QHeybW8
|
pUM7iDh1SNRBnRwqLkKGQvoGnKxDF0lV6gfxJOfk5JyT8xGJlDSmMV+nJ5osVguR3OV9MRKOae0P8IZr
|
||||||
ea77fR6b7T4s0WjUQwgRCSG4I5VIJPLisoAQUlJVAc1mmdFqUfYZ7XaF0en8gGFUYRg/USiotKRxtcCs
|
eXmu+30eh+MpLMFg0E0IiRFC8J9kAoHAyH0BISSlKAIKhTSjWKR8Z5RKGUa5fAbTzMI0fyCZVGhJvrXA
|
||||||
Vkvg+RSSySSD4zikUimk02nwPA9RFCFJElRVZSXhcPjYUvAdkiQw0SplMhkoisJkXc/BNBvXC2q1PSiK
|
ymZT4PkIwuEwg+M4RCIRRKNR8DyPWCwGURShKAor8fv9N7aCU4iiwES7FI/HIcsyk3Vdg2XlHxbkct8g
|
||||||
DEEQmEglWZaZlM1mkcvloOs6isUCut3W9YJ6vQxNU5lEf6OSpmlMzOfzUMKL+Db1BFseJ3ZfubE2//Lk
|
yxIEQWAilSRJYlIikYCmadB1HScnSVQqxYcF5+dpqKrCJPoblVRVZaJhGJA/LOJ4qh/77i4cTfRgY2as
|
||||||
QsbOziYOD9s4OqJ00OsZ6PcPzjFR/vwBef8YjmMhDEpx/P60AHluFF8Cr8EK/kfM58KfWAhYfQ4EHgHL
|
3pRxePgVFxclXF5SyqhWTdRqP++wkP60CsM7iJvQGhqpXfzafAdpdgBbvkmwgscIjbpwHVoD1ocB33Ng
|
||||||
I+iujOOr7zEuz7gtG08dg4G6jqvpB12g79bZG5Pw2hu/Pk4DQRf++m3o+W2ozdoRn7A3rbM3hpscWpLe
|
pQ+V90P4MtqN+zP+le2XnY2G8hmtqS25QN/ts22z53Hmrz6+AZZc+O11oOp1IDftxO4rZ8E+2zbc+Itl
|
||||||
DJ+0AyMw3zlRmXmA7WeO0w3vw/fW2X+Gm3S/TXjsVbo23ehCPgMKrqo38mZYEwAAAABJRU5ErkJggg==
|
8XVvveTrgzXfhczbZzjwdN7ueDoW7LN/DTfeM7fndmbp2nSjpvwHq8ip+rkEjbgAAAAASUVORK5CYII=
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btnEditImage.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnEditImage.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAL4SURBVDhPfdLxU9N1HMdx/oN+7/qp7vzF6+qX7vqts9K0
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAALySURBVDhPhdLfT1tlHMdx/gPvjYnJLrxZjF7otZk63XRx
|
||||||
vDKzjvIcepx1GZ3XpQ1HJGL6FRbjKwOBDfg2CKMNJRkChps6JB34hcEWWmGIgIODue/23faRI3x229US
|
OjdFl7UxZBonZjFulhVxyNzOoFLOKAi0wLHDbdhuw9EOmNhuK0PWsQMHimwqk0FBIND1tKftM4Lsbdpo
|
||||||
znrdvX/6vD+Pz93n/c5R3MMVSrs6J7tUzdL8mHKsLtmpavazamXOP2noHJy/Fw4/1BMJEknxv7WoC+aj
|
sxF/fJLv1fN9Xk/yfL8Fim+oWvGq87JX1e3u/y/Zo+qus2pNwd9p7ry2MB2N3jNSKVJp8Z+1ZAgW4kka
|
||||||
cWrb/NEskH45rieo6ZqmoitAyfkfcNyw4hz9mtOBUqp9Zj5usvF5Yz/GxnEW4gLZNaJlgcpmVUvrteen
|
Tg3E80D25aSRot4fpdqvUX7+W9zXHXhGvuCkVkFdyMb7rU4+bunD0jLOYlIge4f1PFDjVvWs3nA+itTd
|
||||||
kXp6cQxZaRs7gH3oI2qv51Pn34e57yj76lsxKTeZ1x4DxJOC+p4ZSrpb+D5QSp1/Lyd/3oXZ9z7SlVws
|
g3vQQfvoPlyD79FwtYjG8B5svYfY03QCq3KDBf0fgGRa0NQ9Q3lXG6e0ChrDuzn24y5sobeQLhdiD32E
|
||||||
vs/Iq/qG4uZfmdNSyK7h1YCWFNh7ZznsdvCtWow8YEC6/B5HvNv56uJ2JE8BBrkMpa2FW625/H7uGIHG
|
ufZLyo7/zLyeQfYOPQjoaYGrZ5aDPjdfq2XI/SakS2/weXAbn/2wDSlQjEmuRGlv4+aJQn49dxit5QW0
|
||||||
TQRq1hsyQDQhaLxwD8ndjdV3gsqBfEq872D66S0Oe3L58sdCapoPEvYa0ca6QJ8lOtaOr3zzXAa4rwuU
|
+vWmHBBPCVou/I7k68IROkpNfxHlwdexfr+Vg4FCPv2uhPrj+5kLWtBH/WDMEh89Tahq03wOuGMIlN45
|
||||||
vjDW7lEKnQpS537KPXuR+vZQ5PoES1MBC6ESlqY7CV82kwqe5c87VxmxfShy0rOOxAUOz1wGqXIH+KLU
|
HF0jlHgUpM69VAV2I/W+Q6n3A+ytxSyOlbMc7WTuko1M5Cx/3L7C8FfvioLsbGNJgTswn0NqfRqfVJg4
|
||||||
wHHLq0jmDTRUbSYSKmYldZXU7QIig59ysyWfgC130W/dti4DLMYEJuebGNu2cuD069iKXiCo7CD03Ubu
|
Yn8eybaB5tpNxMbKWM1cIXOrmNi1D7nRVoTmfHMp7Hj1sRywlBBYPa9gad/CvpMv4Sx9moiynbFvNnIn
|
||||||
Bw+xIq6RnMhD3N1JfNxE6NTWhyOVG5/PfGKF44YWiQl8E5eyNRDsoP/cfsJDxr8vGxBTHxALFXG7/g0U
|
coBVMUB6woyY3kly3IpWu+XecM3GJ3OfWO2+rscSgtDExXz1RzroO7eXuUHLX5dNiKm3SYyVcqvpZRTl
|
||||||
5YyenYIlA6RWAd5LZYxfOMhKyoeYzOfB3V1owUP8VvcaM1OTyM5HxljtUqci0RgxXRBLPGDE28Iv3SZG
|
jJGfgj0HZB4AghcrGb+wn9VMCDFZxN3pXeiRA/zS+CIzU5PInvvGWOdVp2LxBAlDkEjdZTjYxk9dVka6
|
||||||
e2Rm/O+SnNxJJFDIRMPbLM7+wUJmlQf/XWV7h1puax/uTy9HWnaWvQLxO9yy7cBrepKBui14jr+00qS4
|
ZWbCO0hP7iSmlTDR/BpLs7+xuHaVXR1qlfP0UF92ObKyp/I5SN7mpnM7QevD9DduJnDkmdVWxWdkz+Uz
|
||||||
9fR51Zlhzd6hyllgbY7uXre8dK2aJf9J/Mc20Jr3lN5rXP/c2r7/zLYXnxBS3jPLvSe2LPdVvHylq/DZ
|
Q7qrQ5XzwNocMq9bWR6oYzl8jPDhDbjNjxg9lvVPrO3712x96iEhmR9d8R/dvNJb/exlf8nj69b23J8/
|
||||||
p9f2PJq/AD40i0VffXQ/AAAAAElFTkSuQmCC
|
AbuKiwBr5ZOIAAAAAElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btnConnect.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnConnNode.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
|
||||||
dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHrSURBVDhPY/j//z8DJRhDgFSMIUAqxhAgFcMZdnZ2
|
dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHrSURBVDhPY/j//z8DJRhDgFSMIUAqxhAgFcMZdnZ2
|
||||||
@@ -260,19 +347,19 @@
|
|||||||
gg==
|
gg==
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btnDeleteConnection.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnConnDir.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHxSURBVDhP7Y7Ni1JhGMVvXadgWrWzRdQsU5BcRW4cR72E
|
dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHrSURBVDhPY/j//z8DJRhDgFSMIUAqxhAgFcMZdnZ2
|
||||||
H8FtoS6iFinWKwn2MaPdheJikIEJvGCuElpcaKOtTKUMdNxEZBut1Z1BipAiDZrSJvPEM5CYOP9BB154
|
3FlZWaWtra3v5s2b97+mpuZhTk6OeU5OjkRNTc1dkBhIDqQGpBbFACMjIzYfH5+uiRMn/n/x4sX/u3fv
|
||||||
OOd3Di/H/ddEdrv9GGPsbiKR6OVyOUiS1GGMnWOMaSVJUsmjjBhi/ynrdLojTqdzI51Oo9vtQlVV1Go1
|
/j948OD/xsbGe9XV1Ydu3rz5AyR25MiR/yA1ILUgPXADDA0N/YqLiz9cvXr1XURExDZvb+8/qamp/ydN
|
||||||
xOPxbUmS6q1Wa0heo9EAMcRSZzJgNBovhsPhr+12u+fz+Z4KgjAKBAKQZRmxWAx0k0eZoigdYqkzPbCR
|
mvS/srLyP4gNEgPJrVix4iFILUgPsgu6mpqavk+ePDnd39+fy97e/vPevXv/nzt37v+hQ4f+r1279j9I
|
||||||
TCYHsiwHXS7Xoslk+latVtFsNlGv15HP50EeZcQQS53JgMFguBEMBvuZTOaOKIoVi8WyJwgCRFHcf3ST
|
DCQHUgNSC9IDN0BXVzczPT39/bRp00qCgoJ2Ojo6/nJzc/sfGBgIxiA2SAwkB1IDUgvSAzdAU1NT09fX
|
||||||
RxkxxFJnMqDX68+43e53qVSqryjKl0KhgGw2i0gkgmg0un+X1xleXDr1+/nKAp4Ii3uPlo/enwxwHMeb
|
93pvb+/7VatWvV23bt3/6dOn/y8sLPxfUVEBZvcvbP1fODXmX1qf//+IJse/Lrm6E+EGqKmpMdvb2xt4
|
||||||
zeazDodjx+/374ZCoQFj7KfH47ni9XqtDy5bhm9un8eguInx2zK+P76FVyHd6JlVc3N65ECVbLz6o7gJ
|
eXndT0lJ+ZKdnf09Ozv7Z3h4eFx4eLhzZm3cz8bVif+3XJn6/8KzXf/7d2f9D+/X/W+eLdmDEa/YcGy7
|
||||||
yG5g7TiwvoTPG8soWvjtWXauSiua8fh1AdPqx7Ugf5adq4qNf7/78BoQ12K4yqG3yqFznUfZzn+YZedq
|
y/eNlyf833ht0n8Q6NmT+r9/TzrIgO8YirFhn2rD/9uuzAFrhoFNl6aCDMBUjA1b5kh/796V9L99VwJY
|
||||||
y3si8fLq6V8f15bwKbYA1X8I1QuaUcl2+N4se6C2vCejFSu/Q9+mH/0t/wFGlxos/Pd5kgAAAABJRU5E
|
c/vOBNJcYJ4t2RLco/G/d1cK2GYQDeITHQZQQzrMsyU/gZwNpTtA4gBRO5Y8lpxI5AAAAABJRU5ErkJg
|
||||||
rkJggg==
|
gg==
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="btnToggleGrid.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnToggleGrid.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
@@ -287,18 +374,33 @@
|
|||||||
<data name="btnFitMap.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="btnFitMap.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKYSURBVDhPhZBfSFNRHMfvY0RPEfQSFD2tniKh9WBZmM5W
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKXSURBVDhPhZBfSFNRHMfvY0RPEfQSFD2tngqh9WBZmVOX
|
||||||
es02del1ii7LJAsjtG6pOCdZuiJLZg8TbFMzuiSKlm7m/LPU3XOWkUKF89xBK0ak51pBcDlx1L1cF33h
|
ess23ZrzD7r+mGRhhKNbKs6JVq7IktnDBNvUjFaSuNTNnJtD3D1nGSlUOM8dtGJEeq4VBJcTR93LddEX
|
||||||
vBy+n+/v+/sxzIZm3flasatAAF3m6Ey3WZl2c4rfyUX97TmCv/WsNuaLK+jmLPBpUUTsLACzHef4gNOk
|
zsvh+/n+vr8fw2xo1lWi5PtK3aCvLDHdb5CCLr0UdBgToe5id6jzrDLpSynoMprg04o431sKZnvOcWGH
|
||||||
mXRkafztBt7vMIKJtpyIz55pUXNropPX4TybGML7wBI2ACRXQEkuD6KV9Dm0vNd3n7WN29mI965+cxPR
|
XhG0FypC3RouZNeCQFdx3G87bZJza6KT12GDlY/ifWAJawASa6Eg1kTQSv4cWt7rv89ap2xs3HdHvbkJ
|
||||||
XSDQyYEQ1gCELwKEq6GErwNp9RpE8lUoyXkw9GPPWAsLPI16Qc0zwG2Oznbk8sEwzgIIV63DcuUGXA4k
|
7yp108nhKFYAhC8BhM1QwDeAsHodIvEaFEQDjP7YM9nBAm+r2i3nGeAqS8z26LhIDBcChOvXYbFuA64B
|
||||||
XAaQfMLToOdfNaRH1Twz08UpAYdJAyX5UmxqbAXaCCJcSlsM1aRqvE16Rc0zb135Cj0YNVIQLMmXaZiI
|
Aq4GSDzpbVFznpb8hJxnpvv0UtiuV0BBvJycmlyBNoIIX6AtPA0qha9dJcl5JuDUSfRg1EhBsCReoWE8
|
||||||
8IW1PwmXBJGcQwM8Nt3mgElnXtT/2MCL0mraegtctgaHsQVIuBiilUJRwkcHb6bwQzdSN68w1WYQplqN
|
whfX/gRcFUFiMQ3wWjM3BwQdhkTosYbjhdWc9Ra4eg2OYRMQcCVEK+W8gI+O3MziPGZVihW6NO7pTi2g
|
||||||
gB4KIjl7AyyB0koRhQHC7Lvwz139dSd/D1QlT6t5xmfP0E48yoy8aWFt4uKv3TAsH6eVIZKNQYQTKext
|
h4JILNoAq6CwUkFhgDD7LvZz18um3N/D9Zkzcp7x2wqUgUds/G0Ha+UXf+2GMfE4rQyRqI0gnE7h0ba8
|
||||||
zvwz11tBvJXHyIuKRJM6gxltyrD47GzEc+cUGK7X8XRf+gark/nRe+nKfH81CQccZHGimYzUs+RlsXan
|
P3ODtcRXd4y8qE3XyzOYifYCk9/Gxr1tp8BYczZH96VvxJzJTdzNleZfm0ksbCeLgXtkvJklryqVO+UZ
|
||||||
OoPx2vTa11a9MNSgi9JjjVhTlb7buuXhutPk02gj+fqhk3xbcJGPw7Wk13xEcccLiSfhShI31pJLQpN2
|
jM+qVo5a1G5PiypBjzVuOSEN3c5eHmvKI58mWsnXD73k24KTfBxrJIOGI5IrVUgqua9mGCc7dCQatJH4
|
||||||
EnnfQZb8D8n0kyJiS9F87jufsFXtj6vnZYnciPUMWRi4ReYHasj4g2zSW3rw+zPj/m1q7z/VYznEufIT
|
+x6yFHpIZp5UEGuW4vPQ+bStcn9KPa9ON45bzpCF4VtkfriBTD0oIoMXDn5/pt2/Te79pwZMh4zOkjTy
|
||||||
yKvaNNJdqP3iMiXsUHv+q57iw9vbcw9YneakLbG/v5ifpNsR5bepAAAAAElFTkSuQmCC
|
pjGH9Jcrvzj1aTvknv9qoPLw9m7dAYujLGNL8u8v3CKkjDkyCW0AAAAASUVORK5CYII=
|
||||||
|
</value>
|
||||||
|
</data>
|
||||||
|
<data name="btAddMagnet.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
|
||||||
|
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
|
||||||
|
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
|
||||||
|
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
|
||||||
|
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
|
||||||
|
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
|
||||||
|
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
|
||||||
|
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
|
||||||
|
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
|
||||||
|
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<metadata name="toolStrip2.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
<metadata name="toolStrip2.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호를
|
// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호를
|
||||||
// 기본값으로 할 수 있습니다.
|
// 기본값으로 할 수 있습니다.
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
[assembly: AssemblyVersion("26.02.10.0900")]
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
[assembly: AssemblyFileVersion("26.02.10.0900")]
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.VisualBasic" />
|
<Reference Include="Microsoft.VisualBasic" />
|
||||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
|
<HintPath>..\..\HMI\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
@@ -84,6 +84,7 @@
|
|||||||
<Compile Include="Models\NodeBase.cs" />
|
<Compile Include="Models\NodeBase.cs" />
|
||||||
<Compile Include="Models\MapLabel.cs" />
|
<Compile Include="Models\MapLabel.cs" />
|
||||||
<Compile Include="Models\MapImage.cs" />
|
<Compile Include="Models\MapImage.cs" />
|
||||||
|
<Compile Include="PathFinding\Core\Utility.cs" />
|
||||||
<Compile Include="PathFinding\Planning\AGVPathfinder.cs" />
|
<Compile Include="PathFinding\Planning\AGVPathfinder.cs" />
|
||||||
<Compile Include="PathFinding\Planning\DirectionChangePlanner.cs" />
|
<Compile Include="PathFinding\Planning\DirectionChangePlanner.cs" />
|
||||||
<Compile Include="PathFinding\Planning\DirectionalPathfinder.cs" />
|
<Compile Include="PathFinding\Planning\DirectionalPathfinder.cs" />
|
||||||
@@ -93,7 +94,7 @@
|
|||||||
<Compile Include="PathFinding\Core\PathNode.cs" />
|
<Compile Include="PathFinding\Core\PathNode.cs" />
|
||||||
<Compile Include="PathFinding\Core\AStarPathfinder.cs" />
|
<Compile Include="PathFinding\Core\AStarPathfinder.cs" />
|
||||||
<Compile Include="PathFinding\Core\AGVPathResult.cs" />
|
<Compile Include="PathFinding\Core\AGVPathResult.cs" />
|
||||||
<Compile Include="PathFinding\Planning\NodeMotorInfo.cs" />
|
<Compile Include="Models\NodeMotorInfo.cs" />
|
||||||
<Compile Include="Controls\UnifiedAGVCanvas.cs">
|
<Compile Include="Controls\UnifiedAGVCanvas.cs">
|
||||||
<SubType>UserControl</SubType>
|
<SubType>UserControl</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -72,6 +72,9 @@ namespace AGVNavigationCore.Controls
|
|||||||
DrawDragGhost(g);
|
DrawDragGhost(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 마그넷 방향 텍스트 그리기 (노드 위에 표시)
|
||||||
|
DrawMagnetDirections(g);
|
||||||
|
|
||||||
// AGV 그리기
|
// AGV 그리기
|
||||||
DrawAGVs(g);
|
DrawAGVs(g);
|
||||||
|
|
||||||
@@ -87,7 +90,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UI 정보 그리기 (변환 없이)
|
// UI 정보 그리기 (변환 없이)
|
||||||
if (_showGrid)
|
//if (_showGrid)
|
||||||
DrawUIInfo(g);
|
DrawUIInfo(g);
|
||||||
|
|
||||||
// 동기화 화면 그리기 (변환 없이, 최상위)
|
// 동기화 화면 그리기 (변환 없이, 최상위)
|
||||||
@@ -97,25 +100,276 @@ namespace AGVNavigationCore.Controls
|
|||||||
}
|
}
|
||||||
|
|
||||||
//예측문자는 디버깅시에만 표시한다.
|
//예측문자는 디버깅시에만 표시한다.
|
||||||
if (string.IsNullOrEmpty(PredictMessage) == false && System.Diagnostics.Debugger.IsAttached)
|
if (string.IsNullOrEmpty(PredictMessage) == false)
|
||||||
{
|
{
|
||||||
g.DrawString(this.PredictMessage, this.Font, Brushes.White, 10, 100);
|
g.DrawString(this.PredictMessage, this.Font, Brushes.White, 10, 100);
|
||||||
}
|
}
|
||||||
|
DrawSystemMessage(g);
|
||||||
DrawAlertMessage(g);
|
DrawAlertMessage(g);
|
||||||
|
DrawTagIgnoreMessage(g);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawMagnetDirections(Graphics g)
|
||||||
|
{
|
||||||
|
if (_nodes == null) return;
|
||||||
|
|
||||||
|
using (var font = new Font("Arial", 8, FontStyle.Bold))
|
||||||
|
using (var brushS = new SolidBrush(Color.Magenta))
|
||||||
|
using (var brushL = new SolidBrush(Color.Green))
|
||||||
|
using (var brushR = new SolidBrush(Color.Blue))
|
||||||
|
using (var brushBg = new SolidBrush(Color.FromArgb(180, 255, 255, 255)))
|
||||||
|
{
|
||||||
|
foreach (var node in _nodes)
|
||||||
|
{
|
||||||
|
if (node.MagnetDirections != null && node.MagnetDirections.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var kvp in node.MagnetDirections)
|
||||||
|
{
|
||||||
|
var targetId = kvp.Key;
|
||||||
|
var dir = kvp.Value;
|
||||||
|
var targetNode = _nodes.FirstOrDefault(n => n.Id == targetId);
|
||||||
|
|
||||||
|
if (targetNode != null)
|
||||||
|
{
|
||||||
|
// 방향 텍스트 위치 계산 (출발 -> 도착 벡터의 일정 거리 지점)
|
||||||
|
var start = node.Position;
|
||||||
|
var end = targetNode.Position;
|
||||||
|
var angle = Math.Atan2(end.Y - start.Y, end.X - start.X);
|
||||||
|
|
||||||
|
// 박스(텍스트) 중심 위치: 약 40px 거리
|
||||||
|
var boxDist = 40;
|
||||||
|
var boxX = start.X + boxDist * Math.Cos(angle);
|
||||||
|
var boxY = start.Y + boxDist * Math.Sin(angle);
|
||||||
|
|
||||||
|
string text = dir.ToString();
|
||||||
|
|
||||||
|
Color color = Color.Blue;
|
||||||
|
if (dir == MagnetPosition.L) color = Color.LimeGreen;
|
||||||
|
else if (dir == MagnetPosition.R) color = Color.Red;
|
||||||
|
|
||||||
|
// 화살표 및 텍스트 설정
|
||||||
|
using (var arrowBrush = new SolidBrush(color))
|
||||||
|
using (var arrowPen = new Pen(color, 2)) // 두께 약간 증가
|
||||||
|
using (var textBrush = new SolidBrush(color))
|
||||||
|
{
|
||||||
|
// 1. 화살표 그리기 (박스를 가로지르는 선)
|
||||||
|
// 시작점: 노드 근처 (25px)
|
||||||
|
// 끝점: 박스 너머 (55px)
|
||||||
|
var arrowStartDist = 25;
|
||||||
|
var arrowEndDist = 55;
|
||||||
|
|
||||||
|
var pStart = new PointF((float)(start.X + arrowStartDist * Math.Cos(angle)), (float)(start.Y + arrowStartDist * Math.Sin(angle)));
|
||||||
|
var pEnd = new PointF((float)(start.X + arrowEndDist * Math.Cos(angle)), (float)(start.Y + arrowEndDist * Math.Sin(angle)));
|
||||||
|
|
||||||
|
// 화살표 선 그리기
|
||||||
|
g.DrawLine(arrowPen, pStart, pEnd);
|
||||||
|
|
||||||
|
// 화살표 머리 그리기 (끝점에)
|
||||||
|
var arrowSize = 6;
|
||||||
|
var pHead1 = new PointF((float)(pEnd.X + arrowSize * Math.Cos(angle)), (float)(pEnd.Y + arrowSize * Math.Sin(angle))); // 뾰족한 끝
|
||||||
|
// 삼각형 머리 (채우기)
|
||||||
|
var pBackL = new PointF((float)(pEnd.X + arrowSize * Math.Cos(angle + 2.5)), (float)(pEnd.Y + arrowSize * Math.Sin(angle + 2.5)));
|
||||||
|
var pBackR = new PointF((float)(pEnd.X + arrowSize * Math.Cos(angle - 2.5)), (float)(pEnd.Y + arrowSize * Math.Sin(angle - 2.5)));
|
||||||
|
|
||||||
|
// pHead1이 가장 먼 쪽이 되도록 조정 (pEnd가 삼각형의 뒷부분 중심이 되도록)
|
||||||
|
// pEnd에서 시작해서 앞으로 나가는 삼각형
|
||||||
|
// pTip = pEnd + size * angle
|
||||||
|
// pBackL = pEnd + size/2 * angle_back_L (약간 뒤로)
|
||||||
|
// 현재 코드는 pEnd를 중심으로, pHead1이 앞, pBackL/R이 뒤... 가 아니라
|
||||||
|
// pHead1, pBackK, pBackR로 삼각형을 그림.
|
||||||
|
// pHead1이 팁.
|
||||||
|
g.FillPolygon(arrowBrush, new PointF[] { pHead1, pBackL, pBackR });
|
||||||
|
|
||||||
|
// 2. 텍스트 그리기 (화살표 위에 박스, 그 위에 텍스트)
|
||||||
|
var textSize = g.MeasureString(text, font);
|
||||||
|
var textPoint = new PointF((float)(boxX - textSize.Width / 2), (float)(boxY - textSize.Height / 2));
|
||||||
|
|
||||||
|
//편집모드에서만 글자를 표시한다.
|
||||||
|
if (Mode == CanvasMode.Edit)
|
||||||
|
{
|
||||||
|
// 텍스트 배경 (반투명 - 선이 은은하게 보이도록 투명도 조절하거나, 가독성을 위해 불투명하게 처리)
|
||||||
|
// 사용자가 "박스를 가로지르는" 느낌을 원했으므로 선이 보여야 함. 하지만 텍스트 가독성도 필요.
|
||||||
|
// 배경을 아주 옅게 (Alpha 100정도) 처리하여 선이 보이게 함.
|
||||||
|
using (var translucentBg = new SolidBrush(Color.FromArgb(120, 255, 255, 255)))
|
||||||
|
{
|
||||||
|
g.FillRectangle(translucentBg, textPoint.X - 1, textPoint.Y - 1, textSize.Width + 2, textSize.Height + 2);
|
||||||
|
}
|
||||||
|
g.DrawString(text, font, textBrush, textPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawSystemMessage(Graphics g)
|
||||||
|
{
|
||||||
|
if (!showalertsystem || String.IsNullOrEmpty(this._systemmesage)) return;
|
||||||
|
|
||||||
|
// 상단 중앙에 반투명 빨간색 배경 바 표시
|
||||||
|
int barHeight = 40;
|
||||||
|
int barWidth = Math.Min(600, this.Width - 40); // 최대 600px, 좌우 여백 20px
|
||||||
|
int barX = (this.Width - barWidth) / 2;
|
||||||
|
int barY = 20;
|
||||||
|
|
||||||
|
// 둥근 사각형 배경
|
||||||
|
using (var path = new GraphicsPath())
|
||||||
|
{
|
||||||
|
int radius = 10;
|
||||||
|
path.AddArc(barX, barY, radius * 2, radius * 2, 180, 90);
|
||||||
|
path.AddArc(barX + barWidth - radius * 2, barY, radius * 2, radius * 2, 270, 90);
|
||||||
|
path.AddArc(barX + barWidth - radius * 2, barY + barHeight - radius * 2, radius * 2, radius * 2, 0, 90);
|
||||||
|
path.AddArc(barX, barY + barHeight - radius * 2, radius * 2, radius * 2, 90, 90);
|
||||||
|
path.CloseFigure();
|
||||||
|
|
||||||
|
using (var brush = new SolidBrush(Color.FromArgb(200, 255, 69, 58))) // 진한 붉은색 (Apple Red 계열)
|
||||||
|
{
|
||||||
|
g.FillPath(brush, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var pen = new Pen(Color.FromArgb(255, 255, 255), 2))
|
||||||
|
{
|
||||||
|
g.DrawPath(pen, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 텍스트 깜박임 효과 (배경은 유지하고 텍스트만 깜박임)
|
||||||
|
|
||||||
|
using (var font = new Font("Malgun Gothic", 12, FontStyle.Bold))
|
||||||
|
using (var brush = new SolidBrush(Color.White))
|
||||||
|
{
|
||||||
|
var textSize = g.MeasureString(_systemmesage, font);
|
||||||
|
g.DrawString(_systemmesage, font, brush,
|
||||||
|
barX + (barWidth - textSize.Width) / 2,
|
||||||
|
barY + (barHeight - textSize.Height) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 경고 아이콘 그리기 (왼쪽)
|
||||||
|
// 간단한 느낌표 아이콘
|
||||||
|
int iconX = barX + 15;
|
||||||
|
int iconY = barY + barHeight / 2;
|
||||||
|
using (var brush = new SolidBrush(Color.White))
|
||||||
|
{
|
||||||
|
g.FillEllipse(brush, iconX - 2, iconY - 8, 4, 16); // body
|
||||||
|
g.FillEllipse(brush, iconX - 2, iconY + 10, 4, 4); // dot
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
void DrawAlertMessage(Graphics g)
|
void DrawAlertMessage(Graphics g)
|
||||||
{
|
{
|
||||||
if (showalert == false) return;
|
if (!showinfo || String.IsNullOrEmpty(this._infomessage)) return;
|
||||||
|
|
||||||
//상단에 경고 메세지를 추가한다
|
// 상단 중앙에 반투명 빨간색 배경 바 표시
|
||||||
if (String.IsNullOrEmpty(this._alertmesage)) return;
|
int barHeight = 40;
|
||||||
|
int barWidth = Math.Min(600, this.Width - 40); // 최대 600px, 좌우 여백 20px
|
||||||
|
int barX = (this.Width - barWidth) / 2;
|
||||||
|
int barY = 20+50;
|
||||||
|
|
||||||
|
// 둥근 사각형 배경
|
||||||
|
using (var path = new GraphicsPath())
|
||||||
|
{
|
||||||
|
int radius = 10;
|
||||||
|
path.AddArc(barX, barY, radius * 2, radius * 2, 180, 90);
|
||||||
|
path.AddArc(barX + barWidth - radius * 2, barY, radius * 2, radius * 2, 270, 90);
|
||||||
|
path.AddArc(barX + barWidth - radius * 2, barY + barHeight - radius * 2, radius * 2, radius * 2, 0, 90);
|
||||||
|
path.AddArc(barX, barY + barHeight - radius * 2, radius * 2, radius * 2, 90, 90);
|
||||||
|
path.CloseFigure();
|
||||||
|
|
||||||
g.DrawString(this._alertmesage, this.Font, Brushes.Gold, 10, 10);
|
using (var brush = new SolidBrush(Color.FromArgb(255, Color.Lime)))
|
||||||
|
{
|
||||||
|
g.FillPath(brush, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using (var pen = new Pen(Color.FromArgb(255, 255, 255), 2))
|
||||||
|
{
|
||||||
|
g.DrawPath(pen, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 텍스트 깜박임 효과 (배경은 유지하고 텍스트만 깜박임)
|
||||||
|
|
||||||
|
using (var font = new Font("Malgun Gothic", 12, FontStyle.Bold))
|
||||||
|
using (var brush = new SolidBrush(Color.Black))
|
||||||
|
{
|
||||||
|
var textSize = g.MeasureString(_infomessage, font);
|
||||||
|
g.DrawString(_infomessage, font, brush,
|
||||||
|
barX + (barWidth - textSize.Width) / 2,
|
||||||
|
barY + (barHeight - textSize.Height) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 경고 아이콘 그리기 (왼쪽)
|
||||||
|
// 간단한 느낌표 아이콘
|
||||||
|
int iconX = barX + 15;
|
||||||
|
int iconY = barY + barHeight / 2;
|
||||||
|
using (var brush = new SolidBrush(Color.Black))
|
||||||
|
{
|
||||||
|
g.FillEllipse(brush, iconX - 2, iconY - 8, 4, 16); // body
|
||||||
|
g.FillEllipse(brush, iconX - 2, iconY + 10, 4, 4); // dot
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
void DrawTagIgnoreMessage(Graphics g)
|
||||||
|
{
|
||||||
|
if (!showtagigreno || String.IsNullOrEmpty(this._tagignoreMessage)) return;
|
||||||
|
var ts = DateTime.Now - tagignoretime;
|
||||||
|
if (ts.TotalSeconds > 5) return;
|
||||||
|
|
||||||
|
// 상단 중앙에 반투명 빨간색 배경 바 표시
|
||||||
|
int barHeight = 40;
|
||||||
|
int barWidth = Math.Min(600, this.Width - 40); // 최대 600px, 좌우 여백 20px
|
||||||
|
int barX = (this.Width - barWidth) / 2;
|
||||||
|
int barY = this.Height - barHeight-20;
|
||||||
|
|
||||||
|
// 둥근 사각형 배경
|
||||||
|
using (var path = new GraphicsPath())
|
||||||
|
{
|
||||||
|
int radius = 10;
|
||||||
|
path.AddArc(barX, barY, radius * 2, radius * 2, 180, 90);
|
||||||
|
path.AddArc(barX + barWidth - radius * 2, barY, radius * 2, radius * 2, 270, 90);
|
||||||
|
path.AddArc(barX + barWidth - radius * 2, barY + barHeight - radius * 2, radius * 2, radius * 2, 0, 90);
|
||||||
|
path.AddArc(barX, barY + barHeight - radius * 2, radius * 2, radius * 2, 90, 90);
|
||||||
|
path.CloseFigure();
|
||||||
|
|
||||||
|
using (var brush = new SolidBrush(Color.FromArgb(255, Color.Violet)))
|
||||||
|
{
|
||||||
|
g.FillPath(brush, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var pen = new Pen(Color.FromArgb(255, 255, 255), 2))
|
||||||
|
{
|
||||||
|
g.DrawPath(pen, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 텍스트 깜박임 효과 (배경은 유지하고 텍스트만 깜박임)
|
||||||
|
|
||||||
|
using (var font = new Font("Malgun Gothic", 12, FontStyle.Bold))
|
||||||
|
using (var brush = new SolidBrush(Color.Black))
|
||||||
|
{
|
||||||
|
var textSize = g.MeasureString(_tagignoreMessage, font);
|
||||||
|
g.DrawString(_tagignoreMessage, font, brush,
|
||||||
|
barX + (barWidth - textSize.Width) / 2,
|
||||||
|
barY + (barHeight - textSize.Height) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 경고 아이콘 그리기 (왼쪽)
|
||||||
|
// 간단한 느낌표 아이콘
|
||||||
|
int iconX = barX + 15;
|
||||||
|
int iconY = barY + barHeight / 2;
|
||||||
|
using (var brush = new SolidBrush(Color.Black))
|
||||||
|
{
|
||||||
|
g.FillEllipse(brush, iconX - 2, iconY - 8, 4, 16); // body
|
||||||
|
g.FillEllipse(brush, iconX - 2, iconY + 10, 4, 4); // dot
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
private void DrawSyncScreen(Graphics g)
|
private void DrawSyncScreen(Graphics g)
|
||||||
{
|
{
|
||||||
// 반투명 검은색 배경
|
// 반투명 검은색 배경
|
||||||
@@ -228,11 +482,26 @@ namespace AGVNavigationCore.Controls
|
|||||||
foreach (var targetNode in node.ConnectedMapNodes)
|
foreach (var targetNode in node.ConnectedMapNodes)
|
||||||
{
|
{
|
||||||
if (targetNode == null) continue;
|
if (targetNode == null) continue;
|
||||||
|
|
||||||
|
// 강조된 연결은 나중에 그리기 위해 건너뜀
|
||||||
|
if (IsConnectionHighlighted(node.Id, targetNode.Id)) continue;
|
||||||
|
|
||||||
DrawConnection(g, node, targetNode);
|
DrawConnection(g, node, targetNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1.1 강조된 연결 그리기 (항상 위에 표시되도록)
|
||||||
|
if (_highlightedConnection.HasValue)
|
||||||
|
{
|
||||||
|
var n1 = _nodes.FirstOrDefault(n => n.Id == _highlightedConnection.Value.FromNodeId);
|
||||||
|
var n2 = _nodes.FirstOrDefault(n => n.Id == _highlightedConnection.Value.ToNodeId);
|
||||||
|
if (n1 != null && n2 != null)
|
||||||
|
{
|
||||||
|
DrawConnection(g, n1, n2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 2. 마그넷 그리기 (별도 리스트 사용)
|
// 2. 마그넷 그리기 (별도 리스트 사용)
|
||||||
if (_magnets != null)
|
if (_magnets != null)
|
||||||
{
|
{
|
||||||
@@ -296,10 +565,17 @@ namespace AGVNavigationCore.Controls
|
|||||||
g.DrawLine(_magnetPen, startPoint, endPoint);
|
g.DrawLine(_magnetPen, startPoint, endPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 호버된 마그넷 강조
|
// 호버되거나 선택된 마그넷 강조 (선택: Red, 호버: Orange)
|
||||||
if (magnet == _hoveredNode)
|
bool isHovered = (magnet == _hoveredNode);
|
||||||
|
bool isSelected = (magnet == _selectedNode);
|
||||||
|
|
||||||
|
if (isHovered || isSelected)
|
||||||
{
|
{
|
||||||
using (var highlightPen = new Pen(Color.Orange, 19))
|
Color highlightColor = isSelected ? Color.Red : Color.Orange;
|
||||||
|
// 선택된 상태에서 호버되면? -> 선택 색상 우선 (Red) 또는 명확한 구분 필요
|
||||||
|
// 여기서는 선택이 더 중요하므로 Red 유지
|
||||||
|
|
||||||
|
using (var highlightPen = new Pen(highlightColor, 19) { StartCap = LineCap.Round, EndCap = LineCap.Round })
|
||||||
{
|
{
|
||||||
if (magnet.ControlPoint != null)
|
if (magnet.ControlPoint != null)
|
||||||
{
|
{
|
||||||
@@ -324,16 +600,31 @@ namespace AGVNavigationCore.Controls
|
|||||||
g.DrawLine(highlightPen, startPoint, endPoint);
|
g.DrawLine(highlightPen, startPoint, endPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Redraw normal to keep it on top? No, highlight is usually outer.
|
}
|
||||||
// If I draw highlight AFTER, it covers.
|
|
||||||
// But DrawMagnet is void. If I draw highlight after, it's fine if I want it to glow.
|
// 선택된 마그넷 핸들 그리기
|
||||||
// Actually _magnetPen is Width 15, very thick.
|
if (magnet == _selectedNode && _canvasMode == CanvasMode.Edit)
|
||||||
// If I draw highlight Width 19 *before* normal, it acts as border.
|
{
|
||||||
// But this method draws normal first.
|
using (var handleBrush = new SolidBrush(Color.White))
|
||||||
// So I should refactor to calculate path first, then draw?
|
using (var handlePen = new Pen(Color.Black, 1))
|
||||||
// Or just draw highlight on top with alpha?
|
{
|
||||||
// Let's draw highlight on top with non-filled center? No, it's a line.
|
float size = HANDLE_SIZE / _zoomFactor;
|
||||||
// I'll draw highlight on top for now, maybe with alpha.
|
float half = size / 2;
|
||||||
|
|
||||||
|
// 시작점, 끝점 핸들
|
||||||
|
g.FillRectangle(handleBrush, startPoint.X - half, startPoint.Y - half, size, size);
|
||||||
|
g.DrawRectangle(handlePen, startPoint.X - half, startPoint.Y - half, size, size);
|
||||||
|
g.FillRectangle(handleBrush, endPoint.X - half, endPoint.Y - half, size, size);
|
||||||
|
g.DrawRectangle(handlePen, endPoint.X - half, endPoint.Y - half, size, size);
|
||||||
|
|
||||||
|
// 제어점 핸들 (곡선일 경우)
|
||||||
|
if (magnet.ControlPoint != null)
|
||||||
|
{
|
||||||
|
var cp = magnet.ControlPoint;
|
||||||
|
g.FillRectangle(handleBrush, (float)cp.X - half, (float)cp.Y - half, size, size);
|
||||||
|
g.DrawRectangle(handlePen, (float)cp.X - half, (float)cp.Y - half, size, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,11 +633,10 @@ namespace AGVNavigationCore.Controls
|
|||||||
if (_marks == null) return; // _marks 리스트 사용
|
if (_marks == null) return; // _marks 리스트 사용
|
||||||
|
|
||||||
int sensorSize = 12; // 크기 설정
|
int sensorSize = 12; // 크기 설정
|
||||||
int lineLength = 20; // 선 길이 설정
|
|
||||||
int halfLength = lineLength / 2;
|
|
||||||
|
|
||||||
foreach (var mark in _marks)
|
foreach (var mark in _marks)
|
||||||
{
|
{
|
||||||
|
int lineLength = (int)mark.Length; // 저장된 길이 사용
|
||||||
|
int halfLength = lineLength / 2;
|
||||||
Point p = mark.Position;
|
Point p = mark.Position;
|
||||||
double radians = mark.Rotation * Math.PI / 180.0;
|
double radians = mark.Rotation * Math.PI / 180.0;
|
||||||
|
|
||||||
@@ -368,6 +658,22 @@ namespace AGVNavigationCore.Controls
|
|||||||
g.DrawLine(highlightPen, p1, p2);
|
g.DrawLine(highlightPen, p1, p2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 선택된 마크 핸들 그리기
|
||||||
|
if (mark == _selectedNode && _canvasMode == CanvasMode.Edit)
|
||||||
|
{
|
||||||
|
using (var handleBrush = new SolidBrush(Color.White))
|
||||||
|
using (var handlePen = new Pen(Color.Black, 1))
|
||||||
|
{
|
||||||
|
float size = HANDLE_SIZE / _zoomFactor;
|
||||||
|
float half = size / 2;
|
||||||
|
|
||||||
|
g.FillRectangle(handleBrush, p1.X - half, p1.Y - half, size, size);
|
||||||
|
g.DrawRectangle(handlePen, p1.X - half, p1.Y - half, size, size);
|
||||||
|
g.FillRectangle(handleBrush, p2.X - half, p2.Y - half, size, size);
|
||||||
|
g.DrawRectangle(handlePen, p2.X - half, p2.Y - half, size, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,7 +727,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
(int)(point.Y + arrowSize * Math.Sin(angle + 4 * Math.PI / 3))
|
(int)(point.Y + arrowSize * Math.Sin(angle + 4 * Math.PI / 3))
|
||||||
);
|
);
|
||||||
|
|
||||||
var arrowColor = direction == AgvDirection.Forward ? Color.Blue : Color.Red;
|
var arrowColor = direction == AgvDirection.Forward ? Color.Blue : Color.Yellow;
|
||||||
var arrowBrush = new SolidBrush(arrowColor);
|
var arrowBrush = new SolidBrush(arrowColor);
|
||||||
|
|
||||||
// 정삼각형으로 화살표 그리기 (내부 채움)
|
// 정삼각형으로 화살표 그리기 (내부 채움)
|
||||||
@@ -525,7 +831,15 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
var angle = Math.Atan2(nextNode.Position.Y - currentNode.Position.Y,
|
var angle = Math.Atan2(nextNode.Position.Y - currentNode.Position.Y,
|
||||||
nextNode.Position.X - currentNode.Position.X);
|
nextNode.Position.X - currentNode.Position.X);
|
||||||
DrawDirectionArrow(g, midPoint, angle, AgvDirection.Forward);
|
|
||||||
|
// 상세 경로 정보가 있으면 해당 방향 사용, 없으면 Forward
|
||||||
|
AgvDirection arrowDir = AgvDirection.Forward;
|
||||||
|
if (path.DetailedPath != null && i < path.DetailedPath.Count)
|
||||||
|
{
|
||||||
|
arrowDir = path.DetailedPath[i].MotorDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawDirectionArrow(g, midPoint, angle, arrowDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,57 +849,85 @@ namespace AGVNavigationCore.Controls
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 경로에 포함된 교차로(3개 이상의 노드가 연결된 노드)를 파란색으로 강조 표시
|
/// 경로에 포함된 교차로(3개 이상의 노드가 연결된 노드)를 파란색으로 강조 표시
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <summary>
|
||||||
|
/// 경로에 포함된 특정 노드(Gateway 등)를 강조 표시
|
||||||
|
/// HighlightNodeId가 설정된 경우 해당 노드만 표시하고, 없으면 기존대로 교차로 표시(또는 표시 안함)
|
||||||
|
/// 사용자가 "교차로 대신 게이트웨이만 강조"를 원하므로 우선순위 적용
|
||||||
|
/// </summary>
|
||||||
private void HighlightJunctionsInPath(Graphics g, AGVPathResult path)
|
private void HighlightJunctionsInPath(Graphics g, AGVPathResult path)
|
||||||
{
|
{
|
||||||
if (path?.Path == null || _nodes == null || _nodes.Count == 0)
|
if (path?.Path == null || _nodes == null || _nodes.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const int JUNCTION_CONNECTIONS = 3; // 교차로 판정 기준: 3개 이상의 연결
|
// 1. HighlightNodeId가 설정되어 있다면 해당 노드만 강조
|
||||||
|
if (!string.IsNullOrEmpty(HighlightNodeId))
|
||||||
|
{
|
||||||
|
var targetNode = path.Path.FirstOrDefault(n => n.Id == HighlightNodeId);
|
||||||
|
if (targetNode != null)
|
||||||
|
{
|
||||||
|
DrawJunctionHighlight(g, targetNode, true); // true = Gateway 강조 색상 사용
|
||||||
|
}
|
||||||
|
// HighlightNodeId가 설정된 경우 다른 교차로는 표시하지 않음 (사용자 요청)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 설정이 없다면 기존 로직 (교차로 표시) 유지 여부 결정
|
||||||
|
// 사용자가 "게이트웨이만 강조해줘"라고 했으므로, 혼란을 피하기 위해
|
||||||
|
// HighlightNodeId가 없을 때는 아무것도 표시하지 않거나, 필요한 경우 복구.
|
||||||
|
// 현재는 사용자 요청에 따라 Gateway 지정이 안된 경우(일반 경로)에는 교차로 강조를 끄는 것이 맞아 보임.
|
||||||
|
// 하지만 일반 주행시에도 교차로 정보가 필요할 수 있으니 일단 둡니다.
|
||||||
|
// 단, Gateway 로직을 타는 경우(HighlightNodeId가 Set됨)에는 위에서 return 되므로 OK.
|
||||||
|
|
||||||
|
/*
|
||||||
|
const int JUNCTION_CONNECTIONS = 3;
|
||||||
|
|
||||||
foreach (var node in path.Path)
|
foreach (var node in path.Path)
|
||||||
{
|
{
|
||||||
if (node == null) continue;
|
if (node == null) continue;
|
||||||
|
|
||||||
// 교차로 판정: 3개 이상의 노드가 연결된 경우
|
|
||||||
if (node.ConnectedMapNodes != null && node.ConnectedMapNodes.Count >= JUNCTION_CONNECTIONS)
|
if (node.ConnectedMapNodes != null && node.ConnectedMapNodes.Count >= JUNCTION_CONNECTIONS)
|
||||||
{
|
{
|
||||||
DrawJunctionHighlight(g, node);
|
DrawJunctionHighlight(g, node, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 교차로 노드를 파란색 반투명 배경으로 강조 표시
|
/// 노드 강조 표시
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void DrawJunctionHighlight(Graphics g, MapNode junctionNode)
|
private void DrawJunctionHighlight(Graphics g, MapNode junctionNode, bool isGateway)
|
||||||
{
|
{
|
||||||
if (junctionNode == null) return;
|
if (junctionNode == null) return;
|
||||||
|
|
||||||
const int JUNCTION_HIGHLIGHT_RADIUS = 25; // 강조 표시 반경
|
int radius = isGateway ? 23 : 18; // 게이트웨이는 좀 더 크게
|
||||||
|
|
||||||
// 파란색 반투명 브러시로 배경 원 그리기
|
// 색상 결정: Gateway=진한 주황/골드, 일반 교차로=기존 파랑
|
||||||
using (var highlightBrush = new SolidBrush(Color.FromArgb(80, 70, 130, 200))) // 파란색 (70, 130, 200) 알파 80
|
Color fillColor = isGateway ? Color.FromArgb(100, 255, 140, 0) : Color.FromArgb(80, 70, 130, 200);
|
||||||
using (var highlightPen = new Pen(Color.FromArgb(150, 100, 150, 220), 2)) // 파란 테두리
|
Color penColor = isGateway ? Color.OrangeRed : Color.FromArgb(150, 100, 150, 220);
|
||||||
|
|
||||||
|
using (var highlightBrush = new SolidBrush(fillColor))
|
||||||
|
using (var highlightPen = new Pen(penColor, 3))
|
||||||
{
|
{
|
||||||
g.FillEllipse(
|
g.FillEllipse(
|
||||||
highlightBrush,
|
highlightBrush,
|
||||||
junctionNode.Position.X - JUNCTION_HIGHLIGHT_RADIUS,
|
junctionNode.Position.X - radius,
|
||||||
junctionNode.Position.Y - JUNCTION_HIGHLIGHT_RADIUS,
|
junctionNode.Position.Y - radius,
|
||||||
JUNCTION_HIGHLIGHT_RADIUS * 2,
|
radius * 2,
|
||||||
JUNCTION_HIGHLIGHT_RADIUS * 2
|
radius * 2
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 테두리 점선 효과 (Gateway 인 경우)
|
||||||
|
if (isGateway) highlightPen.DashStyle = DashStyle.Dot;
|
||||||
|
|
||||||
g.DrawEllipse(
|
g.DrawEllipse(
|
||||||
highlightPen,
|
highlightPen,
|
||||||
junctionNode.Position.X - JUNCTION_HIGHLIGHT_RADIUS,
|
junctionNode.Position.X - radius,
|
||||||
junctionNode.Position.Y - JUNCTION_HIGHLIGHT_RADIUS,
|
junctionNode.Position.Y - radius,
|
||||||
JUNCTION_HIGHLIGHT_RADIUS * 2,
|
radius * 2,
|
||||||
JUNCTION_HIGHLIGHT_RADIUS * 2
|
radius * 2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 교차로 라벨 추가
|
|
||||||
//DrawJunctionLabel(g, junctionNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -638,7 +980,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
{
|
{
|
||||||
case NodeType.Normal:
|
case NodeType.Normal:
|
||||||
var item = _selectedNode as MapNode;
|
var item = _selectedNode as MapNode;
|
||||||
if (item.StationType == StationType.Charger)
|
if (item.StationType == Station.Charger)
|
||||||
DrawTriangleGhost(g, ghostBrush);
|
DrawTriangleGhost(g, ghostBrush);
|
||||||
else
|
else
|
||||||
DrawPentagonGhost(g, ghostBrush);
|
DrawPentagonGhost(g, ghostBrush);
|
||||||
@@ -827,16 +1169,16 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
switch (node.StationType)
|
switch (node.StationType)
|
||||||
{
|
{
|
||||||
case StationType.Loader:
|
case Station.Loder:
|
||||||
case StationType.UnLoader:
|
case Station.Cleaner:
|
||||||
case StationType.Clearner:
|
case Station.Plating:
|
||||||
case StationType.Buffer:
|
case Station.Buffer:
|
||||||
DrawPentagonNodeShape(g, node, brush);
|
DrawPentagonNodeShape(g, node, brush);
|
||||||
break;
|
break;
|
||||||
case StationType.Charger:
|
case Station.Charger:
|
||||||
DrawTriangleNodeShape(g, node, brush);
|
DrawTriangleNodeShape(g, node, brush);
|
||||||
break;
|
break;
|
||||||
case StationType.Limit:
|
case Station.Lmt:
|
||||||
DrawRectangleNodeShape(g, node, brush);
|
DrawRectangleNodeShape(g, node, brush);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -1248,7 +1590,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
// 🔥 노드의 폰트 설정 사용 (0 이하일 경우 기본값 7.0f 사용)
|
// 🔥 노드의 폰트 설정 사용 (0 이하일 경우 기본값 7.0f 사용)
|
||||||
var topFont = new Font("Arial", 9, FontStyle.Bold);
|
var topFont = new Font("Arial", 9, FontStyle.Bold);
|
||||||
var btmFont = new Font("Arial", 12, FontStyle.Bold);
|
var btmFont = new Font("Arial", node.NodeTextFontSize, FontStyle.Bold);
|
||||||
|
|
||||||
// 메인 텍스트 크기 측정
|
// 메인 텍스트 크기 측정
|
||||||
var TopSize = g.MeasureString(TopIDText, topFont);
|
var TopSize = g.MeasureString(TopIDText, topFont);
|
||||||
@@ -1274,20 +1616,20 @@ namespace AGVNavigationCore.Controls
|
|||||||
Color bgColor = Color.White;
|
Color bgColor = Color.White;
|
||||||
switch (node.StationType)
|
switch (node.StationType)
|
||||||
{
|
{
|
||||||
case StationType.Charger:
|
case Station.Charger:
|
||||||
fgColor = Color.White;
|
fgColor = Color.White;
|
||||||
bgColor = Color.Tomato;
|
bgColor = Color.Tomato;
|
||||||
break;
|
break;
|
||||||
case StationType.Buffer:
|
case Station.Buffer:
|
||||||
fgColor = Color.Black;
|
fgColor = Color.Black;
|
||||||
bgColor = Color.White;
|
bgColor = Color.White;
|
||||||
break;
|
break;
|
||||||
case StationType.Clearner:
|
case Station.Plating:
|
||||||
fgColor = Color.Black;
|
fgColor = Color.Black;
|
||||||
bgColor = Color.DeepSkyBlue;
|
bgColor = Color.DeepSkyBlue;
|
||||||
break;
|
break;
|
||||||
case StationType.Loader:
|
case Station.Loder:
|
||||||
case StationType.UnLoader:
|
case Station.Cleaner:
|
||||||
fgColor = Color.Black;
|
fgColor = Color.Black;
|
||||||
bgColor = Color.Gold;
|
bgColor = Color.Gold;
|
||||||
break;
|
break;
|
||||||
@@ -1298,6 +1640,8 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var rectpaddingx = 4;
|
var rectpaddingx = 4;
|
||||||
var rectpaddingy = 2;
|
var rectpaddingy = 2;
|
||||||
var roundRect = new Rectangle((int)(btmPoint.X - rectpaddingx),
|
var roundRect = new Rectangle((int)(btmPoint.X - rectpaddingx),
|
||||||
@@ -1318,7 +1662,8 @@ namespace AGVNavigationCore.Controls
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
using (var descBrush = new SolidBrush(fgColor))
|
|
||||||
|
using (var descBrush = new SolidBrush(node.NodeTextForeColor))
|
||||||
{
|
{
|
||||||
g.DrawString(BottomLabelText, btmFont, descBrush, roundRect, new StringFormat
|
g.DrawString(BottomLabelText, btmFont, descBrush, roundRect, new StringFormat
|
||||||
{
|
{
|
||||||
@@ -1570,13 +1915,18 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
switch (node.StationType)
|
switch (node.StationType)
|
||||||
{
|
{
|
||||||
case StationType.Normal: bgColor = Color.DeepSkyBlue; break;
|
case Station.Normal:
|
||||||
case StationType.Charger: bgColor = Color.Tomato; break;
|
if (node.CanTurnLeft || node.CanTurnRight)
|
||||||
case StationType.Loader:
|
bgColor = Color.Violet;
|
||||||
case StationType.UnLoader: bgColor = Color.Gold; break;
|
else
|
||||||
case StationType.Clearner: bgColor = Color.DeepSkyBlue; break;
|
bgColor = Color.DeepSkyBlue;
|
||||||
case StationType.Buffer: bgColor = Color.WhiteSmoke; break;
|
break;
|
||||||
case StationType.Limit: bgColor = Color.Red; break;
|
case Station.Charger: bgColor = Color.Tomato; break;
|
||||||
|
case Station.Loder:
|
||||||
|
case Station.Cleaner: bgColor = Color.Gold; break;
|
||||||
|
case Station.Plating: bgColor = Color.DeepSkyBlue; break;
|
||||||
|
case Station.Buffer: bgColor = Color.WhiteSmoke; break;
|
||||||
|
case Station.Lmt: bgColor = Color.Red; break;
|
||||||
default: bgColor = Color.White; break;
|
default: bgColor = Color.White; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2255,6 +2605,14 @@ namespace AGVNavigationCore.Controls
|
|||||||
g.FillRectangle(bgBrush, scaleRect);
|
g.FillRectangle(bgBrush, scaleRect);
|
||||||
g.DrawRectangle(Pens.Gray, scaleRect.X, scaleRect.Y, scaleRect.Width, scaleRect.Height);
|
g.DrawRectangle(Pens.Gray, scaleRect.X, scaleRect.Y, scaleRect.Width, scaleRect.Height);
|
||||||
g.DrawString(scaleText, font, Brushes.Black, scaleRect.X + 5, scaleRect.Y + 2);
|
g.DrawString(scaleText, font, Brushes.Black, scaleRect.X + 5, scaleRect.Y + 2);
|
||||||
|
|
||||||
|
// 팬 정보 (스케일 정보 위에)
|
||||||
|
var panText = $"Pan: {_panOffset.X:F1}, {_panOffset.Y:F1}";
|
||||||
|
var panSize = g.MeasureString(panText, font);
|
||||||
|
var panRect = new RectangleF(10, Height - zoomSize.Height - scaleSize.Height - panSize.Height - 35, panSize.Width + 10, panSize.Height + 5);
|
||||||
|
g.FillRectangle(bgBrush, panRect);
|
||||||
|
g.DrawRectangle(Pens.Gray, panRect.X, panRect.Y, panRect.Width, panRect.Height);
|
||||||
|
g.DrawString(panText, font, Brushes.Black, panRect.X + 5, panRect.Y + 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -104,6 +104,10 @@ namespace AGVNavigationCore.Controls
|
|||||||
HandleConnectClick(hitNode as MapNode);
|
HandleConnectClick(hitNode as MapNode);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EditMode.ConnectDirection:
|
||||||
|
HandleConnectDirectionClick(hitNode as MapNode);
|
||||||
|
break;
|
||||||
|
|
||||||
case EditMode.Delete:
|
case EditMode.Delete:
|
||||||
HandleDeleteClick(hitNode);
|
HandleDeleteClick(hitNode);
|
||||||
break;
|
break;
|
||||||
@@ -220,12 +224,45 @@ namespace AGVNavigationCore.Controls
|
|||||||
{
|
{
|
||||||
if (_editMode == EditMode.Move)
|
if (_editMode == EditMode.Move)
|
||||||
{
|
{
|
||||||
|
// 0. 핸들 선택 확인 (이미 선택된 노드가 있을 때)
|
||||||
|
if (_selectedNode != null)
|
||||||
|
{
|
||||||
|
int handleIdx = GetHandleAt(worldPoint);
|
||||||
|
if (handleIdx != -1)
|
||||||
|
{
|
||||||
|
_dragHandleIndex = handleIdx;
|
||||||
|
|
||||||
|
// 핸들 드래그 시 초기 오프셋 설정 (점프 현상 방지)
|
||||||
|
if (_selectedNode is MapMagnet magnet)
|
||||||
|
{
|
||||||
|
Point handlePos = Point.Empty;
|
||||||
|
if (handleIdx == 0) handlePos = magnet.StartPoint;
|
||||||
|
else if (handleIdx == 1) handlePos = magnet.EndPoint;
|
||||||
|
else if (handleIdx == 2 && magnet.ControlPoint != null)
|
||||||
|
handlePos = new Point((int)magnet.ControlPoint.X, (int)magnet.ControlPoint.Y);
|
||||||
|
|
||||||
|
_dragOffset = new Point(worldPoint.X - handlePos.X, worldPoint.Y - handlePos.Y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_dragOffset = Point.Empty; // Mark 등은 오프셋 없이 마우스 포인터 기준 계산
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDragging = true;
|
||||||
|
_isPanning = false;
|
||||||
|
Capture = true;
|
||||||
|
Invalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 1. 노드 선택 확인
|
// 1. 노드 선택 확인
|
||||||
var hitNode = GetItemAt(worldPoint);
|
var hitNode = GetItemAt(worldPoint);
|
||||||
if (hitNode != null)
|
if (hitNode != null)
|
||||||
{
|
{
|
||||||
_isDragging = true;
|
_isDragging = true;
|
||||||
_isPanning = false;
|
_isPanning = false;
|
||||||
|
_dragHandleIndex = -1; // 노드 전체 드래그
|
||||||
_selectedNode = hitNode;
|
_selectedNode = hitNode;
|
||||||
_dragStartPosition = hitNode.Position;
|
_dragStartPosition = hitNode.Position;
|
||||||
_dragOffset = new Point(worldPoint.X - hitNode.Position.X, worldPoint.Y - hitNode.Position.Y);
|
_dragOffset = new Point(worldPoint.X - hitNode.Position.X, worldPoint.Y - hitNode.Position.Y);
|
||||||
@@ -322,8 +359,38 @@ namespace AGVNavigationCore.Controls
|
|||||||
// 노드 드래그
|
// 노드 드래그
|
||||||
if (_selectedNode != null)
|
if (_selectedNode != null)
|
||||||
{
|
{
|
||||||
|
if (_dragHandleIndex != -1)
|
||||||
|
{
|
||||||
|
// 핸들 드래그 (포인트별 수정)
|
||||||
|
if (_selectedNode is MapMagnet magnet)
|
||||||
|
{
|
||||||
|
if (_dragHandleIndex == 0) magnet.StartPoint = newPosition;
|
||||||
|
else if (_dragHandleIndex == 1) magnet.EndPoint = newPosition;
|
||||||
|
else if (_dragHandleIndex == 2 && magnet.ControlPoint != null)
|
||||||
|
{
|
||||||
|
magnet.ControlPoint.X = newPosition.X;
|
||||||
|
magnet.ControlPoint.Y = newPosition.Y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_selectedNode is MapMark mark)
|
||||||
|
{
|
||||||
|
// 마크는 중심점 대비 각도와 길이를 계산하여 수정
|
||||||
|
var dx = newPosition.X - mark.Position.X;
|
||||||
|
var dy = newPosition.Y - mark.Position.Y;
|
||||||
|
|
||||||
|
// 핸들 인덱스에 따라 각도 반전 (p1 vs p2)
|
||||||
|
if (_dragHandleIndex == 0) { dx = -dx; dy = -dy; }
|
||||||
|
|
||||||
|
mark.Rotation = Math.Atan2(dy, dx) * 180.0 / Math.PI;
|
||||||
|
mark.Length = Math.Sqrt(dx * dx + dy * dy) * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 노드 전체 드래그
|
||||||
_selectedNode.Position = newPosition;
|
_selectedNode.Position = newPosition;
|
||||||
NodeMoved?.Invoke(this, _selectedNode);
|
NodeMoved?.Invoke(this, _selectedNode);
|
||||||
|
}
|
||||||
moved = true;
|
moved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,6 +419,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
if (_isDragging && _canvasMode == CanvasMode.Edit)
|
if (_isDragging && _canvasMode == CanvasMode.Edit)
|
||||||
{
|
{
|
||||||
_isDragging = false;
|
_isDragging = false;
|
||||||
|
_dragHandleIndex = -1;
|
||||||
Capture = false; // 🔥 마우스 캡처 해제
|
Capture = false; // 🔥 마우스 캡처 해제
|
||||||
Cursor = GetCursorForMode(_editMode);
|
Cursor = GetCursorForMode(_editMode);
|
||||||
}
|
}
|
||||||
@@ -463,6 +531,25 @@ namespace AGVNavigationCore.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_marks != null)
|
||||||
|
{
|
||||||
|
for (int i = _marks.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var node = _marks[i];
|
||||||
|
if (IsPointInNode(worldPoint, node))
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_magnets != null)
|
||||||
|
{
|
||||||
|
for (int i = _magnets.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var node = _magnets[i];
|
||||||
|
if (IsPointInNode(worldPoint, node))
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -477,6 +564,14 @@ namespace AGVNavigationCore.Controls
|
|||||||
{
|
{
|
||||||
return IsPointInImage(point, image);
|
return IsPointInImage(point, image);
|
||||||
}
|
}
|
||||||
|
if (node is MapMark mark)
|
||||||
|
{
|
||||||
|
return IsPointInMark(point, mark);
|
||||||
|
}
|
||||||
|
if (node is MapMagnet magnet)
|
||||||
|
{
|
||||||
|
return IsPointInMagnet(point, magnet);
|
||||||
|
}
|
||||||
// 라벨과 이미지는 별도 리스트로 관리되므로 여기서 처리하지 않음
|
// 라벨과 이미지는 별도 리스트로 관리되므로 여기서 처리하지 않음
|
||||||
// 하지만 혹시 모를 하위 호환성을 위해 타입 체크는 유지하되,
|
// 하지만 혹시 모를 하위 호환성을 위해 타입 체크는 유지하되,
|
||||||
// 실제 로직은 CircularNode 등으로 분기
|
// 실제 로직은 CircularNode 등으로 분기
|
||||||
@@ -487,12 +582,12 @@ namespace AGVNavigationCore.Controls
|
|||||||
{
|
{
|
||||||
switch (node.StationType)
|
switch (node.StationType)
|
||||||
{
|
{
|
||||||
case StationType.Loader:
|
case Station.Loder:
|
||||||
case StationType.UnLoader:
|
case Station.Cleaner:
|
||||||
case StationType.Clearner:
|
case Station.Plating:
|
||||||
case StationType.Buffer:
|
case Station.Buffer:
|
||||||
return IsPointInPentagon(point, node);
|
return IsPointInPentagon(point, node);
|
||||||
case StationType.Charger:
|
case Station.Charger:
|
||||||
return IsPointInTriangle(point, node);
|
return IsPointInTriangle(point, node);
|
||||||
default:
|
default:
|
||||||
return IsPointInCircle(point, node);
|
return IsPointInCircle(point, node);
|
||||||
@@ -647,6 +742,55 @@ namespace AGVNavigationCore.Controls
|
|||||||
return imageRect.Contains(point);
|
return imageRect.Contains(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsPointInMark(Point point, MapMark mark)
|
||||||
|
{
|
||||||
|
int lineLength = (int)mark.Length;
|
||||||
|
int halfLength = lineLength / 2;
|
||||||
|
double radians = mark.Rotation * Math.PI / 180.0;
|
||||||
|
int dx = (int)(halfLength * Math.Cos(radians));
|
||||||
|
int dy = (int)(halfLength * Math.Sin(radians));
|
||||||
|
|
||||||
|
Point p1 = new Point(mark.Position.X - dx, mark.Position.Y - dy);
|
||||||
|
Point p2 = new Point(mark.Position.X + dx, mark.Position.Y + dy);
|
||||||
|
|
||||||
|
// 마크 선택을 위해 약간 넉넉한 히트 영역 (7픽셀)
|
||||||
|
return CalculatePointToLineDistance(point, p1, p2) <= 7 / _zoomFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsPointInMagnet(Point point, MapMagnet magnet)
|
||||||
|
{
|
||||||
|
// 마그넷은 두꺼우므로 (Pen Width 15) 절반인 7.5 정도를 히트 영역으로 잡음
|
||||||
|
float hitThreshold = Math.Max(8f, 12f / _zoomFactor);
|
||||||
|
|
||||||
|
if (magnet.ControlPoint != null)
|
||||||
|
{
|
||||||
|
// 베지어 곡선 정밀 샘플링 (10개 세그먼트)
|
||||||
|
Point prevPoint = magnet.StartPoint;
|
||||||
|
for (int i = 1; i <= 10; i++)
|
||||||
|
{
|
||||||
|
float t = i / 10f;
|
||||||
|
// Quadratic Bezier: (1-t)^2*P0 + 2(1-t)t*P1 + t^2*P2
|
||||||
|
float u = 1 - t;
|
||||||
|
float tt = t * t;
|
||||||
|
float uu = u * u;
|
||||||
|
|
||||||
|
float x = uu * magnet.StartPoint.X + 2 * u * t * (float)magnet.ControlPoint.X + tt * magnet.EndPoint.X;
|
||||||
|
float y = uu * magnet.StartPoint.Y + 2 * u * t * (float)magnet.ControlPoint.Y + tt * magnet.EndPoint.Y;
|
||||||
|
Point currentPoint = new Point((int)x, (int)y);
|
||||||
|
|
||||||
|
if (CalculatePointToLineDistance(point, prevPoint, currentPoint) <= hitThreshold)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
prevPoint = currentPoint;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return CalculatePointToLineDistance(point, magnet.StartPoint, magnet.EndPoint) <= hitThreshold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//private MapLabel GetLabelAt(Point worldPoint)
|
//private MapLabel GetLabelAt(Point worldPoint)
|
||||||
//{
|
//{
|
||||||
// if (_labels == null) return null;
|
// if (_labels == null) return null;
|
||||||
@@ -763,7 +907,9 @@ namespace AGVNavigationCore.Controls
|
|||||||
var newNode = new MapNode
|
var newNode = new MapNode
|
||||||
{
|
{
|
||||||
Id = newNodeId,
|
Id = newNodeId,
|
||||||
Position = worldPoint
|
Position = worldPoint,
|
||||||
|
CanTurnLeft=false,
|
||||||
|
CanTurnRight= false,
|
||||||
};
|
};
|
||||||
|
|
||||||
_nodes.Add(newNode);
|
_nodes.Add(newNode);
|
||||||
@@ -832,7 +978,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 중복되지 않는 고유한 NodeId 생성
|
/// 중복되지 않는 고유한 NodeId 생성
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string GenerateUniqueNodeId()
|
public string GenerateUniqueNodeId()
|
||||||
{
|
{
|
||||||
string nodeId;
|
string nodeId;
|
||||||
int counter = _nodeCounter;
|
int counter = _nodeCounter;
|
||||||
@@ -874,6 +1020,31 @@ namespace AGVNavigationCore.Controls
|
|||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandleConnectDirectionClick(MapNode hitNode)
|
||||||
|
{
|
||||||
|
if (hitNode == null) return;
|
||||||
|
|
||||||
|
if (!_isConnectionMode)
|
||||||
|
{
|
||||||
|
// 연결 시작 (방향 설정)
|
||||||
|
_isConnectionMode = true;
|
||||||
|
_connectionStartNode = hitNode;
|
||||||
|
_selectedNode = hitNode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 연결 완료
|
||||||
|
if (_connectionStartNode != null && _connectionStartNode != hitNode)
|
||||||
|
{
|
||||||
|
// 기본값 S (Straight)로 방향 설정
|
||||||
|
SetMagnetDirection(_connectionStartNode, hitNode, MagnetPosition.S);
|
||||||
|
}
|
||||||
|
CancelConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
private void HandleDeleteClick(MapNode hitNode)
|
private void HandleDeleteClick(MapNode hitNode)
|
||||||
{
|
{
|
||||||
if (hitNode == null) return;
|
if (hitNode == null) return;
|
||||||
@@ -901,10 +1072,46 @@ namespace AGVNavigationCore.Controls
|
|||||||
toNode.ConnectedNodes.Contains(fromNode.Id))
|
toNode.ConnectedNodes.Contains(fromNode.Id))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// 양방향 연결 생성 (AGV가 양쪽 방향으로 이동 가능하도록)
|
||||||
// 양방향 연결 생성 (AGV가 양쪽 방향으로 이동 가능하도록)
|
// 양방향 연결 생성 (AGV가 양쪽 방향으로 이동 가능하도록)
|
||||||
fromNode.AddConnection(toNode.Id);
|
fromNode.AddConnection(toNode.Id);
|
||||||
toNode.AddConnection(fromNode.Id);
|
toNode.AddConnection(fromNode.Id);
|
||||||
|
|
||||||
|
// 🔥 화면 표시용 ConnectedMapNodes 리스트도 즉시 갱신해야 함
|
||||||
|
if (!fromNode.ConnectedMapNodes.Contains(toNode))
|
||||||
|
fromNode.ConnectedMapNodes.Add(toNode);
|
||||||
|
|
||||||
|
if (!toNode.ConnectedMapNodes.Contains(fromNode))
|
||||||
|
toNode.ConnectedMapNodes.Add(fromNode);
|
||||||
|
|
||||||
|
ConnectionCreated?.Invoke(this, (fromNode, toNode));
|
||||||
|
MapChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMagnetDirection(MapNode fromNode, MapNode toNode, MagnetPosition direction)
|
||||||
|
{
|
||||||
|
if (fromNode == null || toNode == null) return;
|
||||||
|
|
||||||
|
// 이미 연결된 노드인지 확인 (연결되어 있어야 방향 설정 가능)
|
||||||
|
// 이미 연결된 노드인지 확인 (연결되어 있어야 방향 설정 가능)
|
||||||
|
if (!fromNode.ConnectedNodes.Contains(toNode.Id))
|
||||||
|
{
|
||||||
|
// 연결되어 있지 않으면 자동 연결
|
||||||
|
CreateConnection(fromNode, toNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromNode.MagnetDirections == null)
|
||||||
|
fromNode.MagnetDirections = new Dictionary<string, MagnetPosition>();
|
||||||
|
|
||||||
|
if (fromNode.MagnetDirections.ContainsKey(toNode.Id))
|
||||||
|
{
|
||||||
|
fromNode.MagnetDirections[toNode.Id] = direction;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fromNode.MagnetDirections.Add(toNode.Id, direction);
|
||||||
|
}
|
||||||
|
|
||||||
MapChanged?.Invoke(this, EventArgs.Empty);
|
MapChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1052,8 +1259,8 @@ namespace AGVNavigationCore.Controls
|
|||||||
var C = lineEnd.X - lineStart.X;
|
var C = lineEnd.X - lineStart.X;
|
||||||
var D = lineEnd.Y - lineStart.Y;
|
var D = lineEnd.Y - lineStart.Y;
|
||||||
|
|
||||||
var dot = A * C + B * D;
|
var dot = (double)A * C + (double)B * D;
|
||||||
var lenSq = C * C + D * D;
|
var lenSq = (double)C * C + (double)D * D;
|
||||||
|
|
||||||
if (lenSq == 0) return CalculateDistance(point, lineStart);
|
if (lenSq == 0) return CalculateDistance(point, lineStart);
|
||||||
|
|
||||||
@@ -1101,6 +1308,39 @@ namespace AGVNavigationCore.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int GetHandleAt(Point worldPoint)
|
||||||
|
{
|
||||||
|
if (_selectedNode == null) return -1;
|
||||||
|
|
||||||
|
float hitTolerance = (HANDLE_SIZE + 4) / _zoomFactor;
|
||||||
|
|
||||||
|
if (_selectedNode is MapMagnet magnet)
|
||||||
|
{
|
||||||
|
if (CalculateDistance(worldPoint, magnet.StartPoint) <= hitTolerance) return 0;
|
||||||
|
if (CalculateDistance(worldPoint, magnet.EndPoint) <= hitTolerance) return 1;
|
||||||
|
if (magnet.ControlPoint != null)
|
||||||
|
{
|
||||||
|
if (CalculateDistance(worldPoint, new Point((int)magnet.ControlPoint.X, (int)magnet.ControlPoint.Y)) <= hitTolerance) return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_selectedNode is MapMark mark)
|
||||||
|
{
|
||||||
|
int lineLength = (int)mark.Length;
|
||||||
|
int halfLength = lineLength / 2;
|
||||||
|
double radians = mark.Rotation * Math.PI / 180.0;
|
||||||
|
int dx = (int)(halfLength * Math.Cos(radians));
|
||||||
|
int dy = (int)(halfLength * Math.Sin(radians));
|
||||||
|
|
||||||
|
Point p1 = new Point(mark.Position.X - dx, mark.Position.Y - dy);
|
||||||
|
Point p2 = new Point(mark.Position.X + dx, mark.Position.Y + dy);
|
||||||
|
|
||||||
|
if (CalculateDistance(worldPoint, p1) <= hitTolerance) return 0;
|
||||||
|
if (CalculateDistance(worldPoint, p2) <= hitTolerance) return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region View Control Methods
|
#region View Control Methods
|
||||||
@@ -20,7 +20,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
{
|
{
|
||||||
#region Constants
|
#region Constants
|
||||||
|
|
||||||
private const int NODE_SIZE = 24;
|
private const int NODE_SIZE = 18;
|
||||||
private const int NODE_RADIUS = NODE_SIZE / 2;
|
private const int NODE_RADIUS = NODE_SIZE / 2;
|
||||||
private const int GRID_SIZE = 20;
|
private const int GRID_SIZE = 20;
|
||||||
private const float CONNECTION_WIDTH = 1.0f;
|
private const float CONNECTION_WIDTH = 1.0f;
|
||||||
@@ -56,6 +56,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
DeleteConnection, // 연결 삭제 모드
|
DeleteConnection, // 연결 삭제 모드
|
||||||
AddLabel, // 라벨 추가 모드
|
AddLabel, // 라벨 추가 모드
|
||||||
AddImage, // 이미지 추가 모드
|
AddImage, // 이미지 추가 모드
|
||||||
|
ConnectDirection, // 방향 연결 모드
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -109,6 +110,8 @@ namespace AGVNavigationCore.Controls
|
|||||||
private MapNode _connectionStartNode;
|
private MapNode _connectionStartNode;
|
||||||
private Point _connectionEndPoint;
|
private Point _connectionEndPoint;
|
||||||
private int _mouseMoveCounter = 0; // 디버그용: MouseMove 실행 횟수
|
private int _mouseMoveCounter = 0; // 디버그용: MouseMove 실행 횟수
|
||||||
|
private int _dragHandleIndex = -1; // 드래그 중인 핸들 인덱스
|
||||||
|
private const int HANDLE_SIZE = 8; // 편집 핸들 크기
|
||||||
|
|
||||||
// 영역 선택 관련
|
// 영역 선택 관련
|
||||||
private bool _isAreaSelecting;
|
private bool _isAreaSelecting;
|
||||||
@@ -135,12 +138,28 @@ namespace AGVNavigationCore.Controls
|
|||||||
private float _syncProgress = 0.0f;
|
private float _syncProgress = 0.0f;
|
||||||
private string _syncDetail = "";
|
private string _syncDetail = "";
|
||||||
|
|
||||||
string _alertmesage = "";
|
string _systemmesage = "";
|
||||||
bool showalert = false;
|
string _infomessage = "";
|
||||||
public void SetAlertMessage(string m)
|
string _tagignoreMessage = "";
|
||||||
|
bool showalertsystem = false;
|
||||||
|
bool showinfo = false;
|
||||||
|
bool showtagigreno = false;
|
||||||
|
DateTime tagignoretime = DateTime.Now;
|
||||||
|
public void SetTagIgnore(string m)
|
||||||
{
|
{
|
||||||
_alertmesage = m;
|
_tagignoreMessage = m;
|
||||||
showalert = !string.IsNullOrEmpty(m);
|
tagignoretime = DateTime.Now;
|
||||||
|
showtagigreno = !string.IsNullOrEmpty(m);
|
||||||
|
}
|
||||||
|
public void SetInfoMessage(string m)
|
||||||
|
{
|
||||||
|
_infomessage = m;
|
||||||
|
showinfo = !string.IsNullOrEmpty(m);
|
||||||
|
}
|
||||||
|
public void SetSystemMessage(string m)
|
||||||
|
{
|
||||||
|
_systemmesage = m;
|
||||||
|
showalertsystem = !string.IsNullOrEmpty(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 브러쉬 및 펜
|
// 브러쉬 및 펜
|
||||||
@@ -185,6 +204,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
public event EventHandler<List<NodeBase>> NodesSelected; // 다중 선택 이벤트
|
public event EventHandler<List<NodeBase>> NodesSelected; // 다중 선택 이벤트
|
||||||
public event EventHandler<NodeBase> NodeDeleted;
|
public event EventHandler<NodeBase> NodeDeleted;
|
||||||
public event EventHandler<NodeBase> NodeMoved;
|
public event EventHandler<NodeBase> NodeMoved;
|
||||||
|
public event EventHandler<(MapNode From, MapNode To)> ConnectionCreated; // 연결 생성 이벤트 추가
|
||||||
public event EventHandler<(MapNode From, MapNode To)> ConnectionDeleted;
|
public event EventHandler<(MapNode From, MapNode To)> ConnectionDeleted;
|
||||||
public event EventHandler<MapImage> ImageDoubleClicked;
|
public event EventHandler<MapImage> ImageDoubleClicked;
|
||||||
public event EventHandler<MapLabel> LabelDoubleClicked;
|
public event EventHandler<MapLabel> LabelDoubleClicked;
|
||||||
@@ -211,6 +231,12 @@ namespace AGVNavigationCore.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 강조해서 표시할 특정 노드 ID (예: Gateway)
|
||||||
|
/// 이 값이 설정되면 해당 노드만 강조 표시됩니다.
|
||||||
|
/// </summary>
|
||||||
|
public string HighlightNodeId { get; set; }
|
||||||
|
|
||||||
public void RemoveItem(NodeBase item)
|
public void RemoveItem(NodeBase item)
|
||||||
{
|
{
|
||||||
if (item is MapImage img) RemoveImage(img);
|
if (item is MapImage img) RemoveImage(img);
|
||||||
@@ -225,6 +251,18 @@ namespace AGVNavigationCore.Controls
|
|||||||
{
|
{
|
||||||
if (_nodes != null && _nodes.Contains(node))
|
if (_nodes != null && _nodes.Contains(node))
|
||||||
{
|
{
|
||||||
|
// 🔥 삭제되는 노드와 연결된 다른 노드들의 연결 정보도 삭제
|
||||||
|
foreach (var otherNode in _nodes.ToList()) // ToList()로 복사본 순회 (안전장치)
|
||||||
|
{
|
||||||
|
if (otherNode == node) continue;
|
||||||
|
|
||||||
|
// 다른 노드 -> 삭제되는 노드 연결 제거
|
||||||
|
if (otherNode.ConnectedNodes.Contains(node.Id))
|
||||||
|
{
|
||||||
|
otherNode.RemoveConnection(node.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_nodes.Remove(node);
|
_nodes.Remove(node);
|
||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
@@ -285,6 +323,34 @@ namespace AGVNavigationCore.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 외부에서 Pan(X,Y) 및 Zoom 값을 설정합니다.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="panX">Pan X 좌표</param>
|
||||||
|
/// <param name="panY">Pan Y 좌표</param>
|
||||||
|
/// <param name="zoom">Zoom Level (0.1 ~ 5.0)</param>
|
||||||
|
public void SetView(float panX, float panY, float zoom)
|
||||||
|
{
|
||||||
|
// Zoom 값 범위 제한
|
||||||
|
float newZoom = Math.Max(0.1f, Math.Min(5.0f, zoom));
|
||||||
|
|
||||||
|
_panOffset = new PointF(panX, panY);
|
||||||
|
_zoomFactor = newZoom;
|
||||||
|
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Browsable(false)]
|
||||||
|
public PointF PanOffset
|
||||||
|
{
|
||||||
|
get => _panOffset;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_panOffset = value;
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 그리드 표시 여부
|
/// 그리드 표시 여부
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -339,9 +405,23 @@ namespace AGVNavigationCore.Controls
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 선택된 노드 (단일)
|
/// 선택된 노드 (단일)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MapNode SelectedNode
|
public NodeBase SelectedNode
|
||||||
{
|
{
|
||||||
get { return this._selectedNode as MapNode; }
|
get { return this._selectedNode; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_selectedNode = value;
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
_selectedNodes.Clear();
|
||||||
|
_selectedNodes.Add(value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_selectedNodes.Clear();
|
||||||
|
}
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -386,6 +466,19 @@ namespace AGVNavigationCore.Controls
|
|||||||
this.FitToNodes();
|
this.FitToNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 맵 데이터를 셋팅합니다
|
||||||
|
/// </summary>
|
||||||
|
public void SetMapData(List<MapNode> nodes, List<MapLabel> labels = null, List<MapImage> images = null, List<MapMark> marks = null, List<MapMagnet> magnets = null)
|
||||||
|
{
|
||||||
|
this.Nodes = nodes;
|
||||||
|
this.Labels = labels ?? new List<MapLabel>();
|
||||||
|
this.Images = images ?? new List<MapImage>();
|
||||||
|
this.Marks = marks ?? new List<MapMark>();
|
||||||
|
this.Magnets = magnets ?? new List<MapMagnet>();
|
||||||
|
this.FitToNodes();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 노드 목록
|
/// 노드 목록
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -650,8 +743,8 @@ namespace AGVNavigationCore.Controls
|
|||||||
_destinationNodePen = new Pen(Color.Orange, 4);
|
_destinationNodePen = new Pen(Color.Orange, 4);
|
||||||
_pathPen = new Pen(Color.Purple, 3);
|
_pathPen = new Pen(Color.Purple, 3);
|
||||||
_agvPen = new Pen(Color.Red, 3);
|
_agvPen = new Pen(Color.Red, 3);
|
||||||
_highlightedConnectionPen = new Pen(Color.Red, 4) { DashStyle = DashStyle.Solid };
|
_highlightedConnectionPen = new Pen(Color.Red, 6) { DashStyle = DashStyle.Solid };
|
||||||
_magnetPen = new Pen(Color.FromArgb(100, Color.LightSkyBlue), 15) { DashStyle = DashStyle.Solid };
|
_magnetPen = new Pen(Color.FromArgb(100, Color.LightSkyBlue), 15) { DashStyle = DashStyle.Solid, StartCap = LineCap.Round, EndCap = LineCap.Round };
|
||||||
_markPen = new Pen(Color.White, 3); // 마크는 흰색 선으로 표시
|
_markPen = new Pen(Color.White, 3); // 마크는 흰색 선으로 표시
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -804,11 +897,11 @@ namespace AGVNavigationCore.Controls
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 동기화 모드 종료
|
/// 동기화 모드 종료
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ExitSyncMode()
|
public void ExitSyncMode(CanvasMode newmode)
|
||||||
{
|
{
|
||||||
if (_canvasMode == CanvasMode.Sync)
|
if (_canvasMode == CanvasMode.Sync)
|
||||||
{
|
{
|
||||||
_canvasMode = CanvasMode.Edit; // 기본 모드로 복귀 (또는 이전 모드)
|
_canvasMode = newmode; // 기본 모드로 복귀 (또는 이전 모드)
|
||||||
UpdateModeUI();
|
UpdateModeUI();
|
||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
@@ -852,6 +945,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
// 이미지 정리
|
// 이미지 정리
|
||||||
_companyLogo?.Dispose();
|
_companyLogo?.Dispose();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
@@ -21,6 +21,9 @@
|
|||||||
/// <summary>명령 이유- (디버깅/로깅용)</summary>
|
/// <summary>명령 이유- (디버깅/로깅용)</summary>
|
||||||
public eAGVCommandReason Reason { get; set; }
|
public eAGVCommandReason Reason { get; set; }
|
||||||
|
|
||||||
|
/// <summary>방향 전환 명령 여부 (180도 Left Turn 등)</summary>
|
||||||
|
public bool IsTurn { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 생성자
|
/// 생성자
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -58,27 +58,27 @@ namespace AGVNavigationCore.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 장비 타입 열거형
|
/// 장비 타입 열거형
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum StationType
|
public enum Station
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 일반노드
|
/// 일반노드
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Normal,
|
Normal,
|
||||||
/// <summary>로더</summary>
|
/// <summary>로더</summary>
|
||||||
Loader,
|
Loder,
|
||||||
/// <summary>클리너</summary>
|
/// <summary>클리너</summary>
|
||||||
Clearner,
|
Plating,
|
||||||
/// <summary>오프로더</summary>
|
/// <summary>오프로더</summary>
|
||||||
UnLoader,
|
Cleaner,
|
||||||
/// <summary>버퍼</summary>
|
/// <summary>버퍼</summary>
|
||||||
Buffer,
|
Buffer,
|
||||||
/// <summary>충전기</summary>
|
/// <summary>충전기1</summary>
|
||||||
Charger,
|
Charger,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 끝점(더이상 이동불가)
|
/// 끝점(더이상 이동불가)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Limit,
|
Lmt,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,4 +177,7 @@ namespace AGVNavigationCore.Models
|
|||||||
Complete,
|
Complete,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -56,17 +56,37 @@ namespace AGVNavigationCore.Models
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 시작점 Point 반환
|
/// 시작점 Point 반환 및 설정
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public Point StartPoint => new Point((int)P1.X, (int)P1.Y);
|
public Point StartPoint
|
||||||
|
{
|
||||||
|
get => new Point((int)P1.X, (int)P1.Y);
|
||||||
|
set { P1.X = value.X; P1.Y = value.Y; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 끝점 Point 반환
|
/// 끝점 Point 반환 및 설정
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public Point EndPoint => new Point((int)P2.X, (int)P2.Y);
|
public Point EndPoint
|
||||||
|
{
|
||||||
|
get => new Point((int)P2.X, (int)P2.Y);
|
||||||
|
set { P2.X = value.X; P2.Y = value.Y; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (ControlPoint == null)
|
||||||
|
{
|
||||||
|
return $"[LINE] ({P1.X:F0},{P1.Y:F0}) -> ({P2.X:F0},{P2.Y:F0})";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return $"[CURVE] ({P1.X:F0},{P1.Y:F0}) -> ({P2.X:F0},{P2.Y:F0})";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,5 +33,9 @@ namespace AGVNavigationCore.Models
|
|||||||
[Category("위치 정보")]
|
[Category("위치 정보")]
|
||||||
[Description("마크의 회전 각도")]
|
[Description("마크의 회전 각도")]
|
||||||
public double Rotation { get; set; }
|
public double Rotation { get; set; }
|
||||||
|
|
||||||
|
[Category("위치 정보")]
|
||||||
|
[Description("마크의 길이")]
|
||||||
|
public double Length { get; set; } = 20.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,22 +15,20 @@ namespace AGVNavigationCore.Models
|
|||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
[Category("라벨 설정")]
|
|
||||||
[Description("표시할 텍스트입니다.")]
|
|
||||||
public string Text { get; set; } = "";
|
|
||||||
|
|
||||||
public StationType StationType { get; set; }
|
|
||||||
|
public Station StationType { get; set; }
|
||||||
|
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
public bool CanDocking
|
public bool CanDocking
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (StationType == StationType.Buffer) return true;
|
if (StationType == Station.Buffer) return true;
|
||||||
if (StationType == StationType.Loader) return true;
|
if (StationType == Station.Loder) return true;
|
||||||
if (StationType == StationType.UnLoader) return true;
|
if (StationType == Station.Cleaner) return true;
|
||||||
if (StationType == StationType.Clearner) return true;
|
if (StationType == Station.Plating) return true;
|
||||||
if (StationType == StationType.Charger) return true;
|
if (StationType == Station.Charger) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,6 +37,11 @@ namespace AGVNavigationCore.Models
|
|||||||
[Description("도킹/충전 노드의 진입 방향입니다.")]
|
[Description("도킹/충전 노드의 진입 방향입니다.")]
|
||||||
public DockingDirection DockDirection { get; set; } = DockingDirection.DontCare;
|
public DockingDirection DockDirection { get; set; } = DockingDirection.DontCare;
|
||||||
|
|
||||||
|
|
||||||
|
[Category("노드 설정")]
|
||||||
|
[Description("각 연결된 노드로 향할 때의 마그넷 방향 정보입니다.")]
|
||||||
|
public Dictionary<string, MagnetPosition> MagnetDirections { get; set; } = new Dictionary<string, MagnetPosition>();
|
||||||
|
|
||||||
[Category("연결 정보")]
|
[Category("연결 정보")]
|
||||||
[Description("연결된 노드 ID 목록입니다.")]
|
[Description("연결된 노드 ID 목록입니다.")]
|
||||||
[ReadOnly(true)]
|
[ReadOnly(true)]
|
||||||
@@ -50,11 +53,11 @@ namespace AGVNavigationCore.Models
|
|||||||
|
|
||||||
[Category("주행 설정")]
|
[Category("주행 설정")]
|
||||||
[Description("제자리 회전(좌) 가능 여부입니다.")]
|
[Description("제자리 회전(좌) 가능 여부입니다.")]
|
||||||
public bool CanTurnLeft { get; set; } = true;
|
public bool CanTurnLeft { get; set; } = false;
|
||||||
|
|
||||||
[Category("주행 설정")]
|
[Category("주행 설정")]
|
||||||
[Description("제자리 회전(우) 가능 여부입니다.")]
|
[Description("제자리 회전(우) 가능 여부입니다.")]
|
||||||
public bool CanTurnRight { get; set; } = true;
|
public bool CanTurnRight { get; set; } = false;
|
||||||
|
|
||||||
[Category("주행 설정")]
|
[Category("주행 설정")]
|
||||||
[Description("교차로 주행 가능 여부입니다.")]
|
[Description("교차로 주행 가능 여부입니다.")]
|
||||||
@@ -67,11 +70,11 @@ namespace AGVNavigationCore.Models
|
|||||||
}
|
}
|
||||||
set { _disablecross = value; }
|
set { _disablecross = value; }
|
||||||
}
|
}
|
||||||
private bool _disablecross = false;
|
private bool _disablecross = true;
|
||||||
|
|
||||||
[Category("주행 설정")]
|
[Category("주행 설정")]
|
||||||
[Description("노드 통과 시 제한 속도입니다.")]
|
[Description("노드 통과 시 제한 속도입니다.")]
|
||||||
public SpeedLevel SpeedLimit { get; set; } = SpeedLevel.M;
|
public SpeedLevel SpeedLimit { get; set; } = SpeedLevel.L;
|
||||||
|
|
||||||
[Category("노드 설정")]
|
[Category("노드 설정")]
|
||||||
[Description("장비 ID 또는 별칭입니다.")]
|
[Description("장비 ID 또는 별칭입니다.")]
|
||||||
@@ -99,12 +102,16 @@ namespace AGVNavigationCore.Models
|
|||||||
set => _textFontSize = value > 0 ? value : 7.0f;
|
set => _textFontSize = value > 0 ? value : 7.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Category("노드 텍스트")]
|
||||||
|
[Description("표시할 텍스트입니다.")]
|
||||||
|
public string Text { get; set; } = "";
|
||||||
|
|
||||||
public MapNode() : base()
|
public MapNode() : base()
|
||||||
{
|
{
|
||||||
Type = NodeType.Normal;
|
Type = NodeType.Normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MapNode(string nodeId, Point position, StationType type) : base(nodeId, position)
|
public MapNode(string nodeId, Point position, Station type) : base(nodeId, position)
|
||||||
{
|
{
|
||||||
Type = NodeType.Normal;
|
Type = NodeType.Normal;
|
||||||
}
|
}
|
||||||
@@ -115,9 +122,9 @@ namespace AGVNavigationCore.Models
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (StationType == StationType.Charger || StationType == StationType.Buffer ||
|
if (StationType == Station.Charger || StationType == Station.Buffer ||
|
||||||
StationType == StationType.Clearner || StationType == StationType.Loader ||
|
StationType == Station.Plating || StationType == Station.Loder ||
|
||||||
StationType == StationType.UnLoader) return true;
|
StationType == Station.Cleaner) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,16 +142,25 @@ namespace AGVNavigationCore.Models
|
|||||||
{
|
{
|
||||||
if (ConnectedNodes.Remove(nodeId))
|
if (ConnectedNodes.Remove(nodeId))
|
||||||
{
|
{
|
||||||
|
if (MagnetDirections != null && MagnetDirections.ContainsKey(nodeId))
|
||||||
|
{
|
||||||
|
MagnetDirections.Remove(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔥 ConnectedMapNodes에서도 제거 (화면 갱신용)
|
||||||
|
var target = ConnectedMapNodes.Find(n => n.Id == nodeId);
|
||||||
|
if (target != null) ConnectedMapNodes.Remove(target);
|
||||||
|
|
||||||
ModifiedDate = DateTime.Now;
|
ModifiedDate = DateTime.Now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetChargingStation(string stationId)
|
public void SetChargingStation(string stationId)
|
||||||
{
|
{
|
||||||
StationType = StationType.Charger;
|
//StationType = StationType.Charger;
|
||||||
Id = stationId;
|
//Id = stationId;
|
||||||
DockDirection = DockingDirection.Forward;
|
//DockDirection = DockingDirection.Forward;
|
||||||
ModifiedDate = DateTime.Now;
|
//ModifiedDate = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
using AGVNavigationCore.Models;
|
namespace AGVNavigationCore.Models
|
||||||
|
|
||||||
namespace AGVNavigationCore.PathFinding.Planning
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AGV 마그넷 센서 방향 제어
|
/// AGV 마그넷 센서 방향 제어
|
||||||
@@ -60,7 +58,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 다음 노드 ID (경로예측용)
|
/// 다음 노드 ID (경로예측용)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string NextNodeId { get; set; }
|
public MapNode NextNode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 회전 가능 노드 여부
|
/// 회전 가능 노드 여부
|
||||||
@@ -82,19 +80,22 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsPass { get; set; }
|
public bool IsPass { get; set; }
|
||||||
|
|
||||||
|
public bool IsTurn { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 특수 동작 설명
|
/// 특수 동작 설명
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SpecialActionDescription { get; set; }
|
public string SpecialActionDescription { get; set; }
|
||||||
|
|
||||||
public NodeMotorInfo(int seqno,string nodeId,ushort rfid, AgvDirection motorDirection, string nextNodeId = null, MagnetDirection magnetDirection = MagnetDirection.Straight)
|
public NodeMotorInfo(int seqno,string nodeId,ushort rfid,
|
||||||
|
AgvDirection motorDirection, MapNode nextNodeId = null, MagnetDirection magnetDirection = MagnetDirection.Straight,bool turn=false)
|
||||||
{
|
{
|
||||||
|
IsTurn = turn;
|
||||||
seq = seqno;
|
seq = seqno;
|
||||||
NodeId = nodeId;
|
NodeId = nodeId;
|
||||||
RfidId = rfid;
|
RfidId = rfid;
|
||||||
MotorDirection = motorDirection;
|
MotorDirection = motorDirection;
|
||||||
MagnetDirection = magnetDirection;
|
MagnetDirection = magnetDirection;
|
||||||
NextNodeId = nextNodeId;
|
NextNode = nextNodeId;
|
||||||
CanRotate = false;
|
CanRotate = false;
|
||||||
IsDirectionChangePoint = false;
|
IsDirectionChangePoint = false;
|
||||||
RequiresSpecialAction = false;
|
RequiresSpecialAction = false;
|
||||||
@@ -108,7 +109,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
var result = $"R{RfidId}[N{NodeId}]:{MotorDirection}";
|
var result = $"R{RfidId}[*{NodeId}]:{MotorDirection}";
|
||||||
|
|
||||||
// 마그넷 방향이 직진이 아닌 경우 표시
|
// 마그넷 방향이 직진이 아닌 경우 표시
|
||||||
if (MagnetDirection != MagnetDirection.Straight)
|
if (MagnetDirection != MagnetDirection.Straight)
|
||||||
@@ -56,7 +56,7 @@ namespace AGVNavigationCore.Models
|
|||||||
private AgvDirection _prevDirection;
|
private AgvDirection _prevDirection;
|
||||||
private AGVState _currentState;
|
private AGVState _currentState;
|
||||||
private float _currentSpeed;
|
private float _currentSpeed;
|
||||||
|
private MapNode _targetnode = null;
|
||||||
// 경로 관련
|
// 경로 관련
|
||||||
private AGVPathResult _currentPath;
|
private AGVPathResult _currentPath;
|
||||||
private List<string> _remainingNodes;
|
private List<string> _remainingNodes;
|
||||||
@@ -92,7 +92,6 @@ namespace AGVNavigationCore.Models
|
|||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
|
|
||||||
public bool Turn180 { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 대상 이동시 모터 방향
|
/// 대상 이동시 모터 방향
|
||||||
@@ -312,7 +311,6 @@ namespace AGVNavigationCore.Models
|
|||||||
/// <returns>다음에 수행할 모터/마그넷/속도 명령</returns>
|
/// <returns>다음에 수행할 모터/마그넷/속도 명령</returns>
|
||||||
public AGVCommand Predict()
|
public AGVCommand Predict()
|
||||||
{
|
{
|
||||||
|
|
||||||
// 1. 위치 미확정 상태 (RFID 2개 미만 감지)
|
// 1. 위치 미확정 상태 (RFID 2개 미만 감지)
|
||||||
if (!_isPositionConfirmed)
|
if (!_isPositionConfirmed)
|
||||||
{
|
{
|
||||||
@@ -347,12 +345,11 @@ namespace AGVNavigationCore.Models
|
|||||||
var lastNode = _currentPath.DetailedPath.Last();
|
var lastNode = _currentPath.DetailedPath.Last();
|
||||||
if (_currentPath.DetailedPath.Where(t => t.seq < lastNode.seq && t.IsPass == false).Any() == false)
|
if (_currentPath.DetailedPath.Where(t => t.seq < lastNode.seq && t.IsPass == false).Any() == false)
|
||||||
{
|
{
|
||||||
// 마지막 노드에 도착했는지 확인 (현재 노드가 마지막 노드와 같은지)
|
// 마지막 노드에 도착했는지 확인 (현재 노드가 마지막 노드와 같은지) -
|
||||||
|
// 모터방향오 같아야한다. 간혹 방향전환 후 MARK STOP하는경우가있다. 260127
|
||||||
|
if (_currentNode != null && _currentNode.Id == lastNode.NodeId && lastNode.MotorDirection == CurrentDirection)
|
||||||
|
|
||||||
if (_currentNode != null && _currentNode.Id == lastNode.NodeId)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
if (lastNode.IsPass) //이미완료되었다.
|
if (lastNode.IsPass) //이미완료되었다.
|
||||||
{
|
{
|
||||||
return new AGVCommand(
|
return new AGVCommand(
|
||||||
@@ -365,12 +362,23 @@ namespace AGVNavigationCore.Models
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
//움직이지 않는다면 움직이게하고, 움직인다면 마크스탑하낟.i
|
||||||
|
|
||||||
|
|
||||||
|
//도킹노드라면 markstop 을 나머지는 바로 스탑한다.
|
||||||
|
eAGVCommandReason reason = eAGVCommandReason.MarkStop;
|
||||||
|
if (TargetNode.StationType == Station.Normal || TargetNode.StationType == Station.Lmt)
|
||||||
|
{
|
||||||
|
//일반노드는 마크스탑포인트가 없으니 바로 종료되도록 한다
|
||||||
|
reason = eAGVCommandReason.Complete;
|
||||||
|
}
|
||||||
|
|
||||||
//마지막노드는 일혔지만 완료되지 않았다. 마크스탑필요
|
//마지막노드는 일혔지만 완료되지 않았다. 마크스탑필요
|
||||||
return new AGVCommand(
|
return new AGVCommand(
|
||||||
MotorCommand.Stop,
|
MotorCommand.Stop,
|
||||||
MagnetPosition.S,
|
MagnetPosition.S,
|
||||||
SpeedLevel.L,
|
SpeedLevel.L,
|
||||||
eAGVCommandReason.MarkStop,
|
reason,
|
||||||
$"목적지 도착 전(MarkStop) - 최종:{CurrentNodeID2}"
|
$"목적지 도착 전(MarkStop) - 최종:{CurrentNodeID2}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -379,8 +387,8 @@ namespace AGVNavigationCore.Models
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. 경로이탈
|
// 4. 경로이탈
|
||||||
var TargetNode = _currentPath.DetailedPath.Where(t => t.IsPass == false && t.NodeId.Equals(_currentNode.Id)).FirstOrDefault();
|
var checkPathOutNode = _currentPath.DetailedPath.Where(t => t.IsPass == false && t.NodeId.Equals(_currentNode.Id)).FirstOrDefault();
|
||||||
if (TargetNode == null)
|
if (checkPathOutNode == null)
|
||||||
{
|
{
|
||||||
return new AGVCommand(
|
return new AGVCommand(
|
||||||
MotorCommand.Stop,
|
MotorCommand.Stop,
|
||||||
@@ -712,6 +720,15 @@ namespace AGVNavigationCore.Models
|
|||||||
|
|
||||||
// MotorDirection → MotorCommand 변환
|
// MotorDirection → MotorCommand 변환
|
||||||
MotorCommand motorCmd;
|
MotorCommand motorCmd;
|
||||||
|
eAGVCommandReason reason = eAGVCommandReason.Normal;
|
||||||
|
|
||||||
|
if (nodeInfo.IsTurn)
|
||||||
|
{
|
||||||
|
motorCmd = MotorCommand.Stop;
|
||||||
|
reason = eAGVCommandReason.MarkStop;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
switch (nodeInfo.MotorDirection)
|
switch (nodeInfo.MotorDirection)
|
||||||
{
|
{
|
||||||
case AgvDirection.Forward:
|
case AgvDirection.Forward:
|
||||||
@@ -724,18 +741,19 @@ namespace AGVNavigationCore.Models
|
|||||||
motorCmd = MotorCommand.Stop;
|
motorCmd = MotorCommand.Stop;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MagnetDirection → MagnetPosition 변换
|
// MagnetDirection → MagnetPosition 변换
|
||||||
MagnetPosition magnetPos;
|
MagnetPosition magnetPos;
|
||||||
switch (nodeInfo.MagnetDirection)
|
switch (nodeInfo.MagnetDirection)
|
||||||
{
|
{
|
||||||
case PathFinding.Planning.MagnetDirection.Left:
|
case MagnetDirection.Left:
|
||||||
magnetPos = MagnetPosition.L;
|
magnetPos = MagnetPosition.L;
|
||||||
break;
|
break;
|
||||||
case PathFinding.Planning.MagnetDirection.Right:
|
case MagnetDirection.Right:
|
||||||
magnetPos = MagnetPosition.R;
|
magnetPos = MagnetPosition.R;
|
||||||
break;
|
break;
|
||||||
case PathFinding.Planning.MagnetDirection.Straight:
|
case MagnetDirection.Straight:
|
||||||
default:
|
default:
|
||||||
magnetPos = MagnetPosition.S;
|
magnetPos = MagnetPosition.S;
|
||||||
break;
|
break;
|
||||||
@@ -753,9 +771,12 @@ namespace AGVNavigationCore.Models
|
|||||||
motorCmd,
|
motorCmd,
|
||||||
magnetPos,
|
magnetPos,
|
||||||
speed,
|
speed,
|
||||||
eAGVCommandReason.Normal,
|
reason,
|
||||||
$"{actionDescription} → {targetNode.Id} (Motor:{motorCmd}, Magnet:{magnetPos})"
|
$"{actionDescription} → {targetNode.Id} (Motor:{motorCmd}, Magnet:{magnetPos})"
|
||||||
);
|
)
|
||||||
|
{
|
||||||
|
IsTurn = nodeInfo.IsTurn
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartMovement()
|
private void StartMovement()
|
||||||
@@ -827,8 +848,6 @@ namespace AGVNavigationCore.Models
|
|||||||
|
|
||||||
public MapNode StartNode { get; set; } = null;
|
public MapNode StartNode { get; set; } = null;
|
||||||
|
|
||||||
private MapNode _targetnode = null;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 목적지를 설정합니다. 목적지가 변경되면 경로계산정보가 삭제 됩니다.
|
/// 목적지를 설정합니다. 목적지가 변경되면 경로계산정보가 삭제 됩니다.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -848,6 +867,10 @@ namespace AGVNavigationCore.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void ProcessNextNode()
|
private void ProcessNextNode()
|
||||||
{
|
{
|
||||||
if (_remainingNodes == null || _currentNodeIndex >= _remainingNodes.Count - 1)
|
if (_remainingNodes == null || _currentNodeIndex >= _remainingNodes.Count - 1)
|
||||||
@@ -64,7 +64,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 오류 메시지 (실패시)
|
/// 오류 메시지 (실패시)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ErrorMessage { get; set; }
|
public string Message { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 도킹 검증 결과
|
/// 도킹 검증 결과
|
||||||
@@ -102,6 +102,8 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public AgvDirection PrevDirection { get; set; }
|
public AgvDirection PrevDirection { get; set; }
|
||||||
|
|
||||||
|
public MapNode Gateway { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 기본 생성자
|
/// 기본 생성자
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -116,7 +118,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
ExploredNodes = 0;
|
ExploredNodes = 0;
|
||||||
EstimatedTimeSeconds = 0;
|
EstimatedTimeSeconds = 0;
|
||||||
RotationCount = 0;
|
RotationCount = 0;
|
||||||
ErrorMessage = string.Empty;
|
Message = string.Empty;
|
||||||
PlanDescription = string.Empty;
|
PlanDescription = string.Empty;
|
||||||
RequiredDirectionChange = false;
|
RequiredDirectionChange = false;
|
||||||
DirectionChangeNode = string.Empty;
|
DirectionChangeNode = string.Empty;
|
||||||
@@ -149,38 +151,21 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 실패 결과 생성
|
/// 실패 결과 생성
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="errorMessage">오류 메시지</param>
|
/// <param name="errorMessage">오류 메시지</param>
|
||||||
/// <param name="calculationTimeMs">계산 시간</param>
|
/// <param name="calculationTimeMs">계산 시간</param>
|
||||||
/// <returns>실패 결과</returns>
|
|
||||||
public static AGVPathResult CreateFailure(string errorMessage, long calculationTimeMs)
|
|
||||||
{
|
|
||||||
return new AGVPathResult
|
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
ErrorMessage = errorMessage,
|
|
||||||
CalculationTimeMs = calculationTimeMs
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 실패 결과 생성 (확장)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="errorMessage">오류 메시지</param>
|
|
||||||
/// <param name="calculationTimeMs">계산 시간</param>
|
|
||||||
/// <param name="exploredNodes">탐색된 노드 수</param>
|
/// <param name="exploredNodes">탐색된 노드 수</param>
|
||||||
/// <returns>실패 결과</returns>
|
/// <returns>실패 결과</returns>
|
||||||
public static AGVPathResult CreateFailure(string errorMessage, long calculationTimeMs, int exploredNodes)
|
public static AGVPathResult CreateFailure(string errorMessage, long calculationTimeMs = 0, int exploredNodes = 0)
|
||||||
{
|
{
|
||||||
return new AGVPathResult
|
return new AGVPathResult
|
||||||
{
|
{
|
||||||
Success = false,
|
Success = false,
|
||||||
ErrorMessage = errorMessage,
|
Message = errorMessage,
|
||||||
CalculationTimeMs = calculationTimeMs,
|
CalculationTimeMs = calculationTimeMs,
|
||||||
ExploredNodes = exploredNodes
|
ExploredNodes = exploredNodes,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,34 +261,22 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 상세 경로 정보 반환
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>상세 정보 문자열</returns>
|
|
||||||
public string GetDetailedInfo()
|
|
||||||
{
|
|
||||||
if (!Success)
|
|
||||||
{
|
|
||||||
return $"경로 계산 실패: {ErrorMessage} (계산시간: {CalculationTimeMs}ms)";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"경로: {Path.Count}개 노드, 거리: {TotalDistance:F1}px, " +
|
|
||||||
$"회전: {RotationCount}회, 예상시간: {EstimatedTimeSeconds:F1}초, " +
|
|
||||||
$"계산시간: {CalculationTimeMs}ms";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 경로의 노드 정보를 포함
|
/// 경로의 노드 정보를 포함
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public string GetDetailedPathInfo()
|
public string GetDetailedPathInfo(bool shortmessage = false)
|
||||||
{
|
{
|
||||||
if (!Success)
|
if (!Success)
|
||||||
{
|
{
|
||||||
return $"경로 계산 실패: {ErrorMessage} (계산시간: {CalculationTimeMs}ms)";
|
return $"경로 계산 실패: {Message} (계산시간: {CalculationTimeMs}ms)";
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = DetailedPath.Select(t => {
|
var data = DetailedPath.Select(t =>
|
||||||
|
{
|
||||||
|
if (shortmessage)
|
||||||
|
return $"{t.RfidId:00}{t.MotorDirection.ToString().Substring(0, 1)}{t.MagnetDirection.ToString().Substring(0, 1)}";
|
||||||
|
else
|
||||||
return $"{t.RfidId}[{t.NodeId}] {t.MotorDirection.ToString().Substring(0, 1)}-{t.MagnetDirection.ToString().Substring(0, 1)}";
|
return $"{t.RfidId}[{t.NodeId}] {t.MotorDirection.ToString().Substring(0, 1)}-{t.MagnetDirection.ToString().Substring(0, 1)}";
|
||||||
});
|
});
|
||||||
return string.Join(" → ", data);
|
return string.Join(" → ", data);
|
||||||
@@ -334,7 +307,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return $"Failed: {ErrorMessage}";
|
return $"Failed: {Message}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,31 +101,32 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
/// <param name="startNodeId">시작 노드 ID</param>
|
/// <param name="startNodeId">시작 노드 ID</param>
|
||||||
/// <param name="endNodeId">목적지 노드 ID</param>
|
/// <param name="endNodeId">목적지 노드 ID</param>
|
||||||
/// <returns>경로 계산 결과</returns>
|
/// <returns>경로 계산 결과</returns>
|
||||||
public AGVPathResult FindPathAStar(string startNodeId, string endNodeId)
|
public AGVPathResult FindPathAStar(MapNode start, MapNode end)
|
||||||
{
|
{
|
||||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!_nodeMap.ContainsKey(startNodeId))
|
if (!_nodeMap.ContainsKey(start.Id))
|
||||||
{
|
{
|
||||||
return AGVPathResult.CreateFailure($"시작 노드를 찾을 수 없습니다: {startNodeId}", stopwatch.ElapsedMilliseconds, 0);
|
return AGVPathResult.CreateFailure($"시작 노드를 찾을 수 없습니다: {start.Id}", stopwatch.ElapsedMilliseconds, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_nodeMap.ContainsKey(endNodeId))
|
if (!_nodeMap.ContainsKey(end.Id))
|
||||||
{
|
{
|
||||||
return AGVPathResult.CreateFailure($"목적지 노드를 찾을 수 없습니다: {endNodeId}", stopwatch.ElapsedMilliseconds, 0);
|
return AGVPathResult.CreateFailure($"목적지 노드를 찾을 수 없습니다: {end.Id}", stopwatch.ElapsedMilliseconds, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startNodeId == endNodeId)
|
//출발지와 목적지가 동일한 경우
|
||||||
|
if (start.Id == end.Id)
|
||||||
{
|
{
|
||||||
var startMapNode = GetMapNode(startNodeId);
|
//var startMapNode = GetMapNode(start);
|
||||||
var singlePath = new List<MapNode> { startMapNode };
|
var singlePath = new List<MapNode> { start };
|
||||||
return AGVPathResult.CreateSuccess(singlePath, new List<AgvDirection>(), 0, stopwatch.ElapsedMilliseconds);
|
return AGVPathResult.CreateSuccess(singlePath, new List<AgvDirection>(), 0, stopwatch.ElapsedMilliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
var startNode = _nodeMap[startNodeId];
|
var startNode = _nodeMap[start.Id];
|
||||||
var endNode = _nodeMap[endNodeId];
|
var endNode = _nodeMap[end.Id];
|
||||||
var openSet = new List<PathNode>();
|
var openSet = new List<PathNode>();
|
||||||
var closedSet = new HashSet<string>();
|
var closedSet = new HashSet<string>();
|
||||||
var exploredCount = 0;
|
var exploredCount = 0;
|
||||||
@@ -142,7 +143,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
closedSet.Add(currentNode.NodeId);
|
closedSet.Add(currentNode.NodeId);
|
||||||
exploredCount++;
|
exploredCount++;
|
||||||
|
|
||||||
if (currentNode.NodeId == endNodeId)
|
if (currentNode.NodeId == end.Id)
|
||||||
{
|
{
|
||||||
var path = ReconstructPath(currentNode);
|
var path = ReconstructPath(currentNode);
|
||||||
var totalDistance = CalculatePathDistance(path);
|
var totalDistance = CalculatePathDistance(path);
|
||||||
@@ -180,277 +181,6 @@ namespace AGVNavigationCore.PathFinding.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 경유지를 거쳐 경로 찾기 (오버로드)
|
|
||||||
/// 여러 경유지를 순차적으로 거쳐서 최종 목적지까지의 경로를 계산합니다.
|
|
||||||
/// 기존 FindPath를 여러 번 호출하여 각 구간의 경로를 합칩니다.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="startNodeId">시작 노드 ID</param>
|
|
||||||
/// <param name="endNodeId">최종 목적지 노드 ID</param>
|
|
||||||
/// <param name="waypointNodeIds">경유지 노드 ID 배열 (선택사항)</param>
|
|
||||||
/// <returns>경로 계산 결과 (모든 경유지를 거친 전체 경로)</returns>
|
|
||||||
public AGVPathResult FindPath(string startNodeId, string endNodeId, params string[] waypointNodeIds)
|
|
||||||
{
|
|
||||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 경유지가 없으면 기본 FindPath 호출
|
|
||||||
if (waypointNodeIds == null || waypointNodeIds.Length == 0)
|
|
||||||
{
|
|
||||||
return FindPathAStar(startNodeId, endNodeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 경유지 유효성 검증
|
|
||||||
var validWaypoints = new List<string>();
|
|
||||||
foreach (var waypointId in waypointNodeIds)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(waypointId))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!_nodeMap.ContainsKey(waypointId))
|
|
||||||
{
|
|
||||||
return AGVPathResult.CreateFailure($"경유지 노드를 찾을 수 없습니다: {waypointId}", stopwatch.ElapsedMilliseconds, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
validWaypoints.Add(waypointId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 경유지가 없으면 기본 경로 계산
|
|
||||||
if (validWaypoints.Count == 0)
|
|
||||||
{
|
|
||||||
return FindPathAStar(startNodeId, endNodeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 첫 번째 경유지가 시작노드와 같은지 검사
|
|
||||||
if (validWaypoints[0] == startNodeId)
|
|
||||||
{
|
|
||||||
return AGVPathResult.CreateFailure(
|
|
||||||
$"첫 번째 경유지({validWaypoints[0]})가 시작 노드({startNodeId})와 동일합니다. 경유지는 시작노드와 달라야 합니다.",
|
|
||||||
stopwatch.ElapsedMilliseconds, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 마지막 경유지가 목적지노드와 같은지 검사
|
|
||||||
if (validWaypoints[validWaypoints.Count - 1] == endNodeId)
|
|
||||||
{
|
|
||||||
return AGVPathResult.CreateFailure(
|
|
||||||
$"마지막 경유지({validWaypoints[validWaypoints.Count - 1]})가 목적지 노드({endNodeId})와 동일합니다. 경유지는 목적지노드와 달라야 합니다.",
|
|
||||||
stopwatch.ElapsedMilliseconds, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 연속된 중복만 제거 (순서 유지)
|
|
||||||
// 예: [1, 2, 2, 3, 2] -> [1, 2, 3, 2] (연속 중복만 제거)
|
|
||||||
var deduplicatedWaypoints = new List<string>();
|
|
||||||
string lastWaypoint = null;
|
|
||||||
foreach (var waypoint in validWaypoints)
|
|
||||||
{
|
|
||||||
if (waypoint != lastWaypoint)
|
|
||||||
{
|
|
||||||
deduplicatedWaypoints.Add(waypoint);
|
|
||||||
lastWaypoint = waypoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
validWaypoints = deduplicatedWaypoints;
|
|
||||||
|
|
||||||
// 최종 경로 리스트와 누적 값
|
|
||||||
var combinedPath = new List<MapNode>();
|
|
||||||
float totalDistance = 0;
|
|
||||||
long totalCalculationTime = 0;
|
|
||||||
|
|
||||||
// 현재 시작점
|
|
||||||
string currentStart = startNodeId;
|
|
||||||
|
|
||||||
// 1단계: 각 경유지까지의 경로 계산
|
|
||||||
for (int i = 0; i < validWaypoints.Count; i++)
|
|
||||||
{
|
|
||||||
string waypoint = validWaypoints[i];
|
|
||||||
|
|
||||||
// 현재 위치에서 경유지까지의 경로 계산
|
|
||||||
var segmentResult = FindPathAStar(currentStart, waypoint);
|
|
||||||
|
|
||||||
if (!segmentResult.Success)
|
|
||||||
{
|
|
||||||
return AGVPathResult.CreateFailure(
|
|
||||||
$"경유지 {i + 1}({waypoint})까지의 경로 계산 실패: {segmentResult.ErrorMessage}",
|
|
||||||
stopwatch.ElapsedMilliseconds, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 경로 합치기 (첫 번째 구간이 아니면 시작점 제거하여 중복 방지)
|
|
||||||
if (combinedPath.Count > 0 && segmentResult.Path.Count > 0)
|
|
||||||
{
|
|
||||||
// 시작 노드 제거 (이전 경로의 마지막 노드와 동일)
|
|
||||||
combinedPath.AddRange(segmentResult.Path.Skip(1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
combinedPath.AddRange(segmentResult.Path);
|
|
||||||
}
|
|
||||||
|
|
||||||
totalDistance += segmentResult.TotalDistance;
|
|
||||||
totalCalculationTime += segmentResult.CalculationTimeMs;
|
|
||||||
|
|
||||||
// 다음 경유지의 시작점은 현재 경유지
|
|
||||||
currentStart = waypoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2단계: 마지막 경유지에서 최종 목적지까지의 경로 계산
|
|
||||||
var finalSegmentResult = FindPathAStar(currentStart, endNodeId);
|
|
||||||
|
|
||||||
if (!finalSegmentResult.Success)
|
|
||||||
{
|
|
||||||
return AGVPathResult.CreateFailure(
|
|
||||||
$"최종 목적지까지의 경로 계산 실패: {finalSegmentResult.ErrorMessage}",
|
|
||||||
stopwatch.ElapsedMilliseconds, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 최종 경로 합치기 (시작점 제거)
|
|
||||||
if (combinedPath.Count > 0 && finalSegmentResult.Path.Count > 0)
|
|
||||||
{
|
|
||||||
combinedPath.AddRange(finalSegmentResult.Path.Skip(1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
combinedPath.AddRange(finalSegmentResult.Path);
|
|
||||||
}
|
|
||||||
|
|
||||||
totalDistance += finalSegmentResult.TotalDistance;
|
|
||||||
totalCalculationTime += finalSegmentResult.CalculationTimeMs;
|
|
||||||
|
|
||||||
stopwatch.Stop();
|
|
||||||
|
|
||||||
// 결과 생성
|
|
||||||
return AGVPathResult.CreateSuccess(
|
|
||||||
combinedPath,
|
|
||||||
new List<AgvDirection>(),
|
|
||||||
totalDistance,
|
|
||||||
totalCalculationTime
|
|
||||||
);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return AGVPathResult.CreateFailure($"경로 계산 중 오류: {ex.Message}", stopwatch.ElapsedMilliseconds, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 두 경로 결과를 합치기
|
|
||||||
/// 이전 경로의 마지막 노드와 현재 경로의 시작 노드가 같으면 시작 노드를 제거하고 합침
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="previousResult">이전 경로 결과</param>
|
|
||||||
/// <param name="currentResult">현재 경로 결과</param>
|
|
||||||
/// <returns>합쳐진 경로 결과</returns>
|
|
||||||
public AGVPathResult CombineResults( AGVPathResult previousResult, AGVPathResult currentResult)
|
|
||||||
{
|
|
||||||
// 입력 검증
|
|
||||||
if (previousResult == null)
|
|
||||||
return currentResult;
|
|
||||||
|
|
||||||
if (currentResult == null)
|
|
||||||
return previousResult;
|
|
||||||
|
|
||||||
if (!previousResult.Success)
|
|
||||||
return AGVPathResult.CreateFailure(
|
|
||||||
$"이전 경로 결과 실패: {previousResult.ErrorMessage}",
|
|
||||||
previousResult.CalculationTimeMs);
|
|
||||||
|
|
||||||
if (!currentResult.Success)
|
|
||||||
return AGVPathResult.CreateFailure(
|
|
||||||
$"현재 경로 결과 실패: {currentResult.ErrorMessage}",
|
|
||||||
currentResult.CalculationTimeMs);
|
|
||||||
|
|
||||||
// 경로가 비어있는 경우 처리
|
|
||||||
if (previousResult.Path == null || previousResult.Path.Count == 0)
|
|
||||||
return currentResult;
|
|
||||||
|
|
||||||
if (currentResult.Path == null || currentResult.Path.Count == 0)
|
|
||||||
return previousResult;
|
|
||||||
|
|
||||||
// 합친 경로 생성
|
|
||||||
var combinedPath = new List<MapNode>(previousResult.Path);
|
|
||||||
var combinedCommands = new List<AgvDirection>(previousResult.Commands);
|
|
||||||
var combinedDetailedPath = new List<NodeMotorInfo>(previousResult.DetailedPath ?? new List<NodeMotorInfo>());
|
|
||||||
|
|
||||||
// 이전 경로의 마지막 노드와 현재 경로의 시작 노드 비교
|
|
||||||
string lastNodeOfPrevious = previousResult.Path[previousResult.Path.Count - 1].Id;
|
|
||||||
string firstNodeOfCurrent = currentResult.Path[0].Id;
|
|
||||||
|
|
||||||
if (lastNodeOfPrevious == firstNodeOfCurrent)
|
|
||||||
{
|
|
||||||
// 첫 번째 노드 제거 (중복 제거)
|
|
||||||
combinedPath.RemoveAt(combinedPath.Count - 1);
|
|
||||||
combinedPath.AddRange(currentResult.Path);
|
|
||||||
|
|
||||||
// DetailedPath도 첫 번째 노드 제거
|
|
||||||
if (currentResult.DetailedPath != null && currentResult.DetailedPath.Count > 0)
|
|
||||||
{
|
|
||||||
combinedDetailedPath.RemoveAt(combinedDetailedPath.Count - 1);
|
|
||||||
combinedDetailedPath.AddRange(currentResult.DetailedPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 그대로 붙임
|
|
||||||
combinedPath.AddRange(currentResult.Path);
|
|
||||||
|
|
||||||
// DetailedPath도 그대로 붙임
|
|
||||||
if (currentResult.DetailedPath != null && currentResult.DetailedPath.Count > 0)
|
|
||||||
{
|
|
||||||
combinedDetailedPath.AddRange(currentResult.DetailedPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 명령어 합치기
|
|
||||||
combinedCommands.AddRange(currentResult.Commands);
|
|
||||||
|
|
||||||
// 총 거리 합산
|
|
||||||
float combinedDistance = previousResult.TotalDistance + currentResult.TotalDistance;
|
|
||||||
|
|
||||||
// 계산 시간 합산
|
|
||||||
long combinedCalculationTime = previousResult.CalculationTimeMs + currentResult.CalculationTimeMs;
|
|
||||||
|
|
||||||
// 합쳐진 결과 생성
|
|
||||||
var result = AGVPathResult.CreateSuccess(
|
|
||||||
combinedPath,
|
|
||||||
combinedCommands,
|
|
||||||
combinedDistance,
|
|
||||||
combinedCalculationTime
|
|
||||||
);
|
|
||||||
|
|
||||||
// DetailedPath 설정
|
|
||||||
result.DetailedPath = combinedDetailedPath;
|
|
||||||
result.PrevNode = previousResult.PrevNode;
|
|
||||||
result.PrevDirection = previousResult.PrevDirection;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 여러 목적지 중 가장 가까운 노드로의 경로 찾기
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="startNodeId">시작 노드 ID</param>
|
|
||||||
/// <param name="targetNodeIds">목적지 후보 노드 ID 목록</param>
|
|
||||||
/// <returns>경로 계산 결과</returns>
|
|
||||||
public AGVPathResult FindNearestPath(string startNodeId, List<string> targetNodeIds)
|
|
||||||
{
|
|
||||||
if (targetNodeIds == null || targetNodeIds.Count == 0)
|
|
||||||
{
|
|
||||||
return AGVPathResult.CreateFailure("목적지 노드가 지정되지 않았습니다", 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
AGVPathResult bestResult = null;
|
|
||||||
foreach (var targetId in targetNodeIds)
|
|
||||||
{
|
|
||||||
var result = FindPathAStar(startNodeId, targetId);
|
|
||||||
if (result.Success && (bestResult == null || result.TotalDistance < bestResult.TotalDistance))
|
|
||||||
{
|
|
||||||
bestResult = result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bestResult ?? AGVPathResult.CreateFailure("모든 목적지로의 경로를 찾을 수 없습니다", 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 휴리스틱 거리 계산 (유클리드 거리)
|
/// 휴리스틱 거리 계산 (유클리드 거리)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
106
AGVLogic/AGVNavigationCore/PathFinding/Core/Utility.cs
Normal file
106
AGVLogic/AGVNavigationCore/PathFinding/Core/Utility.cs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
using AGVNavigationCore.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace AGVNavigationCore.PathFinding.Core
|
||||||
|
{
|
||||||
|
public static class Utility
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 두 경로 결과를 합치기
|
||||||
|
/// 이전 경로의 마지막 노드와 현재 경로의 시작 노드가 같으면 시작 노드를 제거하고 합침
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="previousResult">이전 경로 결과</param>
|
||||||
|
/// <param name="currentResult">현재 경로 결과</param>
|
||||||
|
/// <returns>합쳐진 경로 결과</returns>
|
||||||
|
public static AGVPathResult CombineResults(AGVPathResult previousResult, AGVPathResult currentResult)
|
||||||
|
{
|
||||||
|
// 입력 검증
|
||||||
|
if (previousResult == null)
|
||||||
|
return currentResult;
|
||||||
|
|
||||||
|
if (currentResult == null)
|
||||||
|
return previousResult;
|
||||||
|
|
||||||
|
if (!previousResult.Success)
|
||||||
|
return AGVPathResult.CreateFailure(
|
||||||
|
$"이전 경로 결과 실패: {previousResult.Message}",
|
||||||
|
previousResult.CalculationTimeMs);
|
||||||
|
|
||||||
|
if (!currentResult.Success)
|
||||||
|
return AGVPathResult.CreateFailure(
|
||||||
|
$"현재 경로 결과 실패: {currentResult.Message}",
|
||||||
|
currentResult.CalculationTimeMs);
|
||||||
|
|
||||||
|
// 경로가 비어있는 경우 처리
|
||||||
|
if (previousResult.Path == null || previousResult.Path.Count == 0)
|
||||||
|
return currentResult;
|
||||||
|
|
||||||
|
if (currentResult.Path == null || currentResult.Path.Count == 0)
|
||||||
|
return previousResult;
|
||||||
|
|
||||||
|
// 합친 경로 생성
|
||||||
|
var combinedPath = new List<MapNode>(previousResult.Path);
|
||||||
|
var combinedCommands = new List<AgvDirection>(previousResult.Commands);
|
||||||
|
var combinedDetailedPath = new List<NodeMotorInfo>(previousResult.DetailedPath ?? new List<NodeMotorInfo>());
|
||||||
|
|
||||||
|
// 이전 경로의 마지막 노드와 현재 경로의 시작 노드 비교
|
||||||
|
string lastNodeOfPrevious = previousResult.Path[previousResult.Path.Count - 1].Id;
|
||||||
|
string firstNodeOfCurrent = currentResult.Path[0].Id;
|
||||||
|
|
||||||
|
if (lastNodeOfPrevious == firstNodeOfCurrent)
|
||||||
|
{
|
||||||
|
// 첫 번째 노드 제거 (중복 제거)
|
||||||
|
combinedPath.RemoveAt(combinedPath.Count - 1);
|
||||||
|
combinedPath.AddRange(currentResult.Path);
|
||||||
|
|
||||||
|
// DetailedPath도 첫 번째 노드 제거
|
||||||
|
if (currentResult.DetailedPath != null && currentResult.DetailedPath.Count > 0)
|
||||||
|
{
|
||||||
|
combinedDetailedPath.RemoveAt(combinedDetailedPath.Count - 1);
|
||||||
|
combinedDetailedPath.AddRange(currentResult.DetailedPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 그대로 붙임
|
||||||
|
combinedPath.AddRange(currentResult.Path);
|
||||||
|
|
||||||
|
// DetailedPath도 그대로 붙임
|
||||||
|
if (currentResult.DetailedPath != null && currentResult.DetailedPath.Count > 0)
|
||||||
|
{
|
||||||
|
combinedDetailedPath.AddRange(currentResult.DetailedPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 명령어 합치기
|
||||||
|
combinedCommands.AddRange(currentResult.Commands);
|
||||||
|
|
||||||
|
// 총 거리 합산
|
||||||
|
float combinedDistance = previousResult.TotalDistance + currentResult.TotalDistance;
|
||||||
|
|
||||||
|
// 계산 시간 합산
|
||||||
|
long combinedCalculationTime = previousResult.CalculationTimeMs + currentResult.CalculationTimeMs;
|
||||||
|
|
||||||
|
// 합쳐진 결과 생성
|
||||||
|
var result = AGVPathResult.CreateSuccess(
|
||||||
|
combinedPath,
|
||||||
|
combinedCommands,
|
||||||
|
combinedDistance,
|
||||||
|
combinedCalculationTime
|
||||||
|
);
|
||||||
|
|
||||||
|
// DetailedPath 설정
|
||||||
|
result.DetailedPath = combinedDetailedPath;
|
||||||
|
result.PrevNode = previousResult.PrevNode;
|
||||||
|
result.PrevDirection = previousResult.PrevDirection;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
1976
AGVLogic/AGVNavigationCore/PathFinding/Planning/AGVPathfinder.cs
Normal file
1976
AGVLogic/AGVNavigationCore/PathFinding/Planning/AGVPathfinder.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,71 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using AGVNavigationCore.Models;
|
||||||
|
using AGVNavigationCore.PathFinding.Core;
|
||||||
|
using AGVNavigationCore.PathFinding.Analysis;
|
||||||
|
using AGVNavigationCore.PathFinding.Validation;
|
||||||
|
|
||||||
|
namespace AGVNavigationCore.PathFinding.Planning
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// AGV 방향 전환 경로 계획 시스템
|
||||||
|
/// 물리적 제약사항을 고려한 방향 전환 경로 생성
|
||||||
|
/// </summary>
|
||||||
|
public class DirectionChangePlanner
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 방향 전환 계획 결과
|
||||||
|
/// </summary>
|
||||||
|
public class DirectionChangePlan
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public List<MapNode> DirectionChangePath { get; set; }
|
||||||
|
public string DirectionChangeNode { get; set; }
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
public string PlanDescription { get; set; }
|
||||||
|
|
||||||
|
public DirectionChangePlan()
|
||||||
|
{
|
||||||
|
DirectionChangePath = new List<MapNode>();
|
||||||
|
ErrorMessage = string.Empty;
|
||||||
|
PlanDescription = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DirectionChangePlan CreateSuccess(List<MapNode> path, string changeNode, string description)
|
||||||
|
{
|
||||||
|
return new DirectionChangePlan
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
DirectionChangePath = path,
|
||||||
|
DirectionChangeNode = changeNode,
|
||||||
|
PlanDescription = description
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DirectionChangePlan CreateFailure(string error)
|
||||||
|
{
|
||||||
|
return new DirectionChangePlan
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
ErrorMessage = error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly List<MapNode> _mapNodes;
|
||||||
|
private readonly JunctionAnalyzer _junctionAnalyzer;
|
||||||
|
private readonly AStarPathfinder _pathfinder;
|
||||||
|
|
||||||
|
public DirectionChangePlanner(List<MapNode> mapNodes)
|
||||||
|
{
|
||||||
|
_mapNodes = mapNodes ?? new List<MapNode>();
|
||||||
|
_junctionAnalyzer = new JunctionAnalyzer(_mapNodes);
|
||||||
|
_pathfinder = new AStarPathfinder();
|
||||||
|
_pathfinder.SetMapNodes(_mapNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
11
AGVLogic/AGVNavigationCore/PathFinding/Planning/test.cs
Normal file
11
AGVLogic/AGVNavigationCore/PathFinding/Planning/test.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
class Program {
|
||||||
|
static void Main() {
|
||||||
|
var paths = new List<string[]> { new[] { \"20F\", \"21F\", \"70B\" } };
|
||||||
|
var valid = paths.Select(p => p.Select(t => new { Tag = t, IdStr = new string(t.Where(char.IsDigit).ToArray()) }).ToList()).ToList();
|
||||||
|
Console.WriteLine(\"Compiled\");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -191,7 +191,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 검증 수행
|
// 검증 수행
|
||||||
if (LastNodeInfo.MotorDirection == requiredDirection && pathResult.DetailedPath[pathResult.DetailedPath.Count - 2].MotorDirection == requiredDirection)
|
if (LastNodeInfo.MotorDirection == requiredDirection && pathResult.DetailedPath[pathResult.DetailedPath.Count - 1].MotorDirection == requiredDirection)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"[DockingValidator] ✅ 도킹 검증 성공");
|
System.Diagnostics.Debug.WriteLine($"[DockingValidator] ✅ 도킹 검증 성공");
|
||||||
return DockingValidationResult.CreateValid(
|
return DockingValidationResult.CreateValid(
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>false</Optimize>
|
<Optimize>false</Optimize>
|
||||||
<OutputPath>..\..\..\..\..\..\Amkor\AGV4\Test\Simulator\</OutputPath>
|
<OutputPath>bin\debug\</OutputPath>
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
@@ -45,6 +45,14 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="fMain.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="fMain.Designer.cs">
|
||||||
|
<DependentUpon>fMain.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Forms\ComboBoxItem.cs" />
|
||||||
|
<Compile Include="Forms\DirectionItem.cs" />
|
||||||
<Compile Include="Forms\PathTestLogItem.cs" />
|
<Compile Include="Forms\PathTestLogItem.cs" />
|
||||||
<Compile Include="Forms\ProgressLogForm.cs">
|
<Compile Include="Forms\ProgressLogForm.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
@@ -54,20 +62,9 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Models\SimulatorConfig.cs" />
|
<Compile Include="Models\SimulatorConfig.cs" />
|
||||||
<Compile Include="Models\SimulationState.cs" />
|
<Compile Include="Models\SimulationState.cs" />
|
||||||
<Compile Include="Forms\SimulatorForm.cs">
|
|
||||||
<SubType>Form</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Forms\SimulatorForm.Designer.cs">
|
|
||||||
<DependentUpon>SimulatorForm.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<EmbeddedResource Include="Forms\SimulatorForm.resx">
|
|
||||||
<DependentUpon>SimulatorForm.cs</DependentUpon>
|
|
||||||
</EmbeddedResource>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="build.bat" />
|
<None Include="build.bat" />
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
@@ -82,5 +79,10 @@
|
|||||||
<Name>AGVMapEditor</Name>
|
<Name>AGVMapEditor</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="fMain.resx">
|
||||||
|
<DependentUpon>fMain.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
||||||
23
AGVLogic/AGVSimulator/Forms/ComboBoxItem.cs
Normal file
23
AGVLogic/AGVSimulator/Forms/ComboBoxItem.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
namespace AGVSimulator.Forms
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 제네릭 콤보박스 아이템 클래스
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">값의 타입</typeparam>
|
||||||
|
public class ComboBoxItem<T>
|
||||||
|
{
|
||||||
|
public T Value { get; }
|
||||||
|
public string DisplayText { get; }
|
||||||
|
|
||||||
|
public ComboBoxItem(T value, string displayText)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
DisplayText = displayText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return DisplayText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
AGVLogic/AGVSimulator/Forms/DirectionItem.cs
Normal file
24
AGVLogic/AGVSimulator/Forms/DirectionItem.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using AGVNavigationCore.Models;
|
||||||
|
|
||||||
|
namespace AGVSimulator.Forms
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 방향 콤보박스용 아이템 클래스
|
||||||
|
/// </summary>
|
||||||
|
public class DirectionItem
|
||||||
|
{
|
||||||
|
public AgvDirection Direction { get; }
|
||||||
|
public string DisplayText { get; }
|
||||||
|
|
||||||
|
public DirectionItem(AgvDirection direction, string displayText)
|
||||||
|
{
|
||||||
|
Direction = direction;
|
||||||
|
DisplayText = displayText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return DisplayText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,6 +56,11 @@ namespace AGVSimulator.Forms
|
|||||||
listItem.BackColor = Color.LightPink;
|
listItem.BackColor = Color.LightPink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dockpos = item.DockingPosition ?? string.Empty;
|
||||||
|
var targerpos = item.TargetPosition ?? string.Empty;
|
||||||
|
if (dockpos.Equals("충전기") && targerpos.StartsWith("0015"))
|
||||||
|
listItem.ForeColor = Color.DarkViolet;
|
||||||
|
|
||||||
_logListView.Items.Add(listItem);
|
_logListView.Items.Add(listItem);
|
||||||
_logListView.EnsureVisible(_logListView.Items.Count - 1);
|
_logListView.EnsureVisible(_logListView.Items.Count - 1);
|
||||||
}
|
}
|
||||||
@@ -172,6 +177,10 @@ namespace AGVSimulator.Forms
|
|||||||
SaveToCSV(saveDialog.FileName);
|
SaveToCSV(saveDialog.FileName);
|
||||||
MessageBox.Show($"CSV 파일이 저장되었습니다.\n{saveDialog.FileName}",
|
MessageBox.Show($"CSV 파일이 저장되었습니다.\n{saveDialog.FileName}",
|
||||||
"저장 완료", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
"저장 완료", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
|
||||||
|
var prc = new System.Diagnostics.Process();
|
||||||
|
prc.StartInfo = new System.Diagnostics.ProcessStartInfo("explorer", saveDialog.FileName);
|
||||||
|
prc.Start();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -55,6 +55,7 @@ namespace AGVSimulator.Forms
|
|||||||
this.맵다른이름으로저장ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.맵다른이름으로저장ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
this.launchMapEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.launchMapEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.btSelectMapEditor = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator();
|
this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.simulationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.simulationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
@@ -85,14 +86,17 @@ namespace AGVSimulator.Forms
|
|||||||
this._statusLabel = new System.Windows.Forms.ToolStripStatusLabel();
|
this._statusLabel = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
this._coordLabel = new System.Windows.Forms.ToolStripStatusLabel();
|
this._coordLabel = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
this.prb1 = new System.Windows.Forms.ToolStripProgressBar();
|
this.prb1 = new System.Windows.Forms.ToolStripProgressBar();
|
||||||
|
this.sbFile = new System.Windows.Forms.ToolStripStatusLabel();
|
||||||
this._controlPanel = new System.Windows.Forms.Panel();
|
this._controlPanel = new System.Windows.Forms.Panel();
|
||||||
|
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||||
|
this.propertyNode = new System.Windows.Forms.PropertyGrid();
|
||||||
this._statusGroup = new System.Windows.Forms.GroupBox();
|
this._statusGroup = new System.Windows.Forms.GroupBox();
|
||||||
this._pathLengthLabel = new System.Windows.Forms.Label();
|
this._pathLengthLabel = new System.Windows.Forms.Label();
|
||||||
this._agvCountLabel = new System.Windows.Forms.Label();
|
this._agvCountLabel = new System.Windows.Forms.Label();
|
||||||
this._simulationStatusLabel = new System.Windows.Forms.Label();
|
this._simulationStatusLabel = new System.Windows.Forms.Label();
|
||||||
this._pathGroup = new System.Windows.Forms.GroupBox();
|
this._pathGroup = new System.Windows.Forms.GroupBox();
|
||||||
|
this.btPath2 = new System.Windows.Forms.Button();
|
||||||
this._clearPathButton = new System.Windows.Forms.Button();
|
this._clearPathButton = new System.Windows.Forms.Button();
|
||||||
this._calculatePathButton = new System.Windows.Forms.Button();
|
|
||||||
this._targetCalcButton = new System.Windows.Forms.Button();
|
this._targetCalcButton = new System.Windows.Forms.Button();
|
||||||
this._avoidRotationCheckBox = new System.Windows.Forms.CheckBox();
|
this._avoidRotationCheckBox = new System.Windows.Forms.CheckBox();
|
||||||
this._targetNodeCombo = new System.Windows.Forms.ComboBox();
|
this._targetNodeCombo = new System.Windows.Forms.ComboBox();
|
||||||
@@ -118,18 +122,17 @@ namespace AGVSimulator.Forms
|
|||||||
this._liftDirectionLabel = new System.Windows.Forms.Label();
|
this._liftDirectionLabel = new System.Windows.Forms.Label();
|
||||||
this._motorDirectionLabel = new System.Windows.Forms.Label();
|
this._motorDirectionLabel = new System.Windows.Forms.Label();
|
||||||
this.timer1 = new System.Windows.Forms.Timer(this.components);
|
this.timer1 = new System.Windows.Forms.Timer(this.components);
|
||||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
this.button1 = new System.Windows.Forms.Button();
|
||||||
this.propertyNode = new System.Windows.Forms.PropertyGrid();
|
|
||||||
this._menuStrip.SuspendLayout();
|
this._menuStrip.SuspendLayout();
|
||||||
this._toolStrip.SuspendLayout();
|
this._toolStrip.SuspendLayout();
|
||||||
this._statusStrip.SuspendLayout();
|
this._statusStrip.SuspendLayout();
|
||||||
this._controlPanel.SuspendLayout();
|
this._controlPanel.SuspendLayout();
|
||||||
|
this.groupBox1.SuspendLayout();
|
||||||
this._statusGroup.SuspendLayout();
|
this._statusGroup.SuspendLayout();
|
||||||
this._pathGroup.SuspendLayout();
|
this._pathGroup.SuspendLayout();
|
||||||
this._agvControlGroup.SuspendLayout();
|
this._agvControlGroup.SuspendLayout();
|
||||||
this._canvasPanel.SuspendLayout();
|
this._canvasPanel.SuspendLayout();
|
||||||
this._agvInfoPanel.SuspendLayout();
|
this._agvInfoPanel.SuspendLayout();
|
||||||
this.groupBox1.SuspendLayout();
|
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// _menuStrip
|
// _menuStrip
|
||||||
@@ -141,7 +144,7 @@ namespace AGVSimulator.Forms
|
|||||||
this.helpToolStripMenuItem});
|
this.helpToolStripMenuItem});
|
||||||
this._menuStrip.Location = new System.Drawing.Point(0, 0);
|
this._menuStrip.Location = new System.Drawing.Point(0, 0);
|
||||||
this._menuStrip.Name = "_menuStrip";
|
this._menuStrip.Name = "_menuStrip";
|
||||||
this._menuStrip.Size = new System.Drawing.Size(1034, 24);
|
this._menuStrip.Size = new System.Drawing.Size(1248, 24);
|
||||||
this._menuStrip.TabIndex = 0;
|
this._menuStrip.TabIndex = 0;
|
||||||
this._menuStrip.Text = "menuStrip";
|
this._menuStrip.Text = "menuStrip";
|
||||||
//
|
//
|
||||||
@@ -154,6 +157,7 @@ namespace AGVSimulator.Forms
|
|||||||
this.맵다른이름으로저장ToolStripMenuItem,
|
this.맵다른이름으로저장ToolStripMenuItem,
|
||||||
this.toolStripSeparator1,
|
this.toolStripSeparator1,
|
||||||
this.launchMapEditorToolStripMenuItem,
|
this.launchMapEditorToolStripMenuItem,
|
||||||
|
this.btSelectMapEditor,
|
||||||
this.toolStripSeparator4,
|
this.toolStripSeparator4,
|
||||||
this.exitToolStripMenuItem});
|
this.exitToolStripMenuItem});
|
||||||
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
|
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
|
||||||
@@ -203,6 +207,13 @@ namespace AGVSimulator.Forms
|
|||||||
this.launchMapEditorToolStripMenuItem.Text = "MapEditor 실행(&M)";
|
this.launchMapEditorToolStripMenuItem.Text = "MapEditor 실행(&M)";
|
||||||
this.launchMapEditorToolStripMenuItem.Click += new System.EventHandler(this.OnLaunchMapEditor_Click);
|
this.launchMapEditorToolStripMenuItem.Click += new System.EventHandler(this.OnLaunchMapEditor_Click);
|
||||||
//
|
//
|
||||||
|
// btSelectMapEditor
|
||||||
|
//
|
||||||
|
this.btSelectMapEditor.Name = "btSelectMapEditor";
|
||||||
|
this.btSelectMapEditor.Size = new System.Drawing.Size(221, 22);
|
||||||
|
this.btSelectMapEditor.Text = "Mapeditor 선택";
|
||||||
|
this.btSelectMapEditor.Click += new System.EventHandler(this.btSelectMapEditor_Click);
|
||||||
|
//
|
||||||
// toolStripSeparator4
|
// toolStripSeparator4
|
||||||
//
|
//
|
||||||
this.toolStripSeparator4.Name = "toolStripSeparator4";
|
this.toolStripSeparator4.Name = "toolStripSeparator4";
|
||||||
@@ -309,7 +320,7 @@ namespace AGVSimulator.Forms
|
|||||||
this.btMakeMap});
|
this.btMakeMap});
|
||||||
this._toolStrip.Location = new System.Drawing.Point(0, 24);
|
this._toolStrip.Location = new System.Drawing.Point(0, 24);
|
||||||
this._toolStrip.Name = "_toolStrip";
|
this._toolStrip.Name = "_toolStrip";
|
||||||
this._toolStrip.Size = new System.Drawing.Size(1034, 25);
|
this._toolStrip.Size = new System.Drawing.Size(1248, 25);
|
||||||
this._toolStrip.TabIndex = 1;
|
this._toolStrip.TabIndex = 1;
|
||||||
this._toolStrip.Text = "toolStrip";
|
this._toolStrip.Text = "toolStrip";
|
||||||
//
|
//
|
||||||
@@ -432,10 +443,11 @@ namespace AGVSimulator.Forms
|
|||||||
this._statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this._statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
this._statusLabel,
|
this._statusLabel,
|
||||||
this._coordLabel,
|
this._coordLabel,
|
||||||
this.prb1});
|
this.prb1,
|
||||||
|
this.sbFile});
|
||||||
this._statusStrip.Location = new System.Drawing.Point(0, 689);
|
this._statusStrip.Location = new System.Drawing.Point(0, 689);
|
||||||
this._statusStrip.Name = "_statusStrip";
|
this._statusStrip.Name = "_statusStrip";
|
||||||
this._statusStrip.Size = new System.Drawing.Size(1034, 22);
|
this._statusStrip.Size = new System.Drawing.Size(1248, 22);
|
||||||
this._statusStrip.TabIndex = 2;
|
this._statusStrip.TabIndex = 2;
|
||||||
this._statusStrip.Text = "statusStrip";
|
this._statusStrip.Text = "statusStrip";
|
||||||
//
|
//
|
||||||
@@ -455,6 +467,12 @@ namespace AGVSimulator.Forms
|
|||||||
this.prb1.Name = "prb1";
|
this.prb1.Name = "prb1";
|
||||||
this.prb1.Size = new System.Drawing.Size(200, 16);
|
this.prb1.Size = new System.Drawing.Size(200, 16);
|
||||||
//
|
//
|
||||||
|
// sbFile
|
||||||
|
//
|
||||||
|
this.sbFile.Name = "sbFile";
|
||||||
|
this.sbFile.Size = new System.Drawing.Size(17, 17);
|
||||||
|
this.sbFile.Text = "--";
|
||||||
|
//
|
||||||
// _controlPanel
|
// _controlPanel
|
||||||
//
|
//
|
||||||
this._controlPanel.BackColor = System.Drawing.SystemColors.Control;
|
this._controlPanel.BackColor = System.Drawing.SystemColors.Control;
|
||||||
@@ -463,11 +481,30 @@ namespace AGVSimulator.Forms
|
|||||||
this._controlPanel.Controls.Add(this._pathGroup);
|
this._controlPanel.Controls.Add(this._pathGroup);
|
||||||
this._controlPanel.Controls.Add(this._agvControlGroup);
|
this._controlPanel.Controls.Add(this._agvControlGroup);
|
||||||
this._controlPanel.Dock = System.Windows.Forms.DockStyle.Right;
|
this._controlPanel.Dock = System.Windows.Forms.DockStyle.Right;
|
||||||
this._controlPanel.Location = new System.Drawing.Point(801, 49);
|
this._controlPanel.Location = new System.Drawing.Point(1015, 49);
|
||||||
this._controlPanel.Name = "_controlPanel";
|
this._controlPanel.Name = "_controlPanel";
|
||||||
this._controlPanel.Size = new System.Drawing.Size(233, 640);
|
this._controlPanel.Size = new System.Drawing.Size(233, 640);
|
||||||
this._controlPanel.TabIndex = 3;
|
this._controlPanel.TabIndex = 3;
|
||||||
//
|
//
|
||||||
|
// groupBox1
|
||||||
|
//
|
||||||
|
this.groupBox1.Controls.Add(this.propertyNode);
|
||||||
|
this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.groupBox1.Location = new System.Drawing.Point(0, 546);
|
||||||
|
this.groupBox1.Name = "groupBox1";
|
||||||
|
this.groupBox1.Size = new System.Drawing.Size(233, 94);
|
||||||
|
this.groupBox1.TabIndex = 4;
|
||||||
|
this.groupBox1.TabStop = false;
|
||||||
|
this.groupBox1.Text = "노드 정보";
|
||||||
|
//
|
||||||
|
// propertyNode
|
||||||
|
//
|
||||||
|
this.propertyNode.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.propertyNode.Location = new System.Drawing.Point(3, 17);
|
||||||
|
this.propertyNode.Name = "propertyNode";
|
||||||
|
this.propertyNode.Size = new System.Drawing.Size(227, 74);
|
||||||
|
this.propertyNode.TabIndex = 0;
|
||||||
|
//
|
||||||
// _statusGroup
|
// _statusGroup
|
||||||
//
|
//
|
||||||
this._statusGroup.Controls.Add(this._pathLengthLabel);
|
this._statusGroup.Controls.Add(this._pathLengthLabel);
|
||||||
@@ -510,8 +547,9 @@ namespace AGVSimulator.Forms
|
|||||||
//
|
//
|
||||||
// _pathGroup
|
// _pathGroup
|
||||||
//
|
//
|
||||||
|
this._pathGroup.Controls.Add(this.button1);
|
||||||
|
this._pathGroup.Controls.Add(this.btPath2);
|
||||||
this._pathGroup.Controls.Add(this._clearPathButton);
|
this._pathGroup.Controls.Add(this._clearPathButton);
|
||||||
this._pathGroup.Controls.Add(this._calculatePathButton);
|
|
||||||
this._pathGroup.Controls.Add(this._targetCalcButton);
|
this._pathGroup.Controls.Add(this._targetCalcButton);
|
||||||
this._pathGroup.Controls.Add(this._avoidRotationCheckBox);
|
this._pathGroup.Controls.Add(this._avoidRotationCheckBox);
|
||||||
this._pathGroup.Controls.Add(this._targetNodeCombo);
|
this._pathGroup.Controls.Add(this._targetNodeCombo);
|
||||||
@@ -526,26 +564,26 @@ namespace AGVSimulator.Forms
|
|||||||
this._pathGroup.TabStop = false;
|
this._pathGroup.TabStop = false;
|
||||||
this._pathGroup.Text = "경로 제어";
|
this._pathGroup.Text = "경로 제어";
|
||||||
//
|
//
|
||||||
|
// btPath2
|
||||||
|
//
|
||||||
|
this.btPath2.Location = new System.Drawing.Point(12, 177);
|
||||||
|
this.btPath2.Name = "btPath2";
|
||||||
|
this.btPath2.Size = new System.Drawing.Size(106, 25);
|
||||||
|
this.btPath2.TabIndex = 10;
|
||||||
|
this.btPath2.Text = "경로 계산2";
|
||||||
|
this.btPath2.UseVisualStyleBackColor = true;
|
||||||
|
this.btPath2.Click += new System.EventHandler(this.btPath2_Click);
|
||||||
|
//
|
||||||
// _clearPathButton
|
// _clearPathButton
|
||||||
//
|
//
|
||||||
this._clearPathButton.Location = new System.Drawing.Point(150, 177);
|
this._clearPathButton.Location = new System.Drawing.Point(121, 177);
|
||||||
this._clearPathButton.Name = "_clearPathButton";
|
this._clearPathButton.Name = "_clearPathButton";
|
||||||
this._clearPathButton.Size = new System.Drawing.Size(70, 25);
|
this._clearPathButton.Size = new System.Drawing.Size(111, 25);
|
||||||
this._clearPathButton.TabIndex = 6;
|
this._clearPathButton.TabIndex = 6;
|
||||||
this._clearPathButton.Text = "경로 지우기";
|
this._clearPathButton.Text = "경로 지우기";
|
||||||
this._clearPathButton.UseVisualStyleBackColor = true;
|
this._clearPathButton.UseVisualStyleBackColor = true;
|
||||||
this._clearPathButton.Click += new System.EventHandler(this.OnClearPath_Click);
|
this._clearPathButton.Click += new System.EventHandler(this.OnClearPath_Click);
|
||||||
//
|
//
|
||||||
// _calculatePathButton
|
|
||||||
//
|
|
||||||
this._calculatePathButton.Location = new System.Drawing.Point(10, 177);
|
|
||||||
this._calculatePathButton.Name = "_calculatePathButton";
|
|
||||||
this._calculatePathButton.Size = new System.Drawing.Size(65, 25);
|
|
||||||
this._calculatePathButton.TabIndex = 4;
|
|
||||||
this._calculatePathButton.Text = "경로 계산";
|
|
||||||
this._calculatePathButton.UseVisualStyleBackColor = true;
|
|
||||||
this._calculatePathButton.Click += new System.EventHandler(this.OnCalculatePath_Click);
|
|
||||||
//
|
|
||||||
// _targetCalcButton
|
// _targetCalcButton
|
||||||
//
|
//
|
||||||
this._targetCalcButton.Location = new System.Drawing.Point(10, 148);
|
this._targetCalcButton.Location = new System.Drawing.Point(10, 148);
|
||||||
@@ -569,6 +607,7 @@ namespace AGVSimulator.Forms
|
|||||||
// _targetNodeCombo
|
// _targetNodeCombo
|
||||||
//
|
//
|
||||||
this._targetNodeCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
this._targetNodeCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
|
this._targetNodeCombo.Font = new System.Drawing.Font("돋움체", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
this._targetNodeCombo.Location = new System.Drawing.Point(10, 97);
|
this._targetNodeCombo.Location = new System.Drawing.Point(10, 97);
|
||||||
this._targetNodeCombo.Name = "_targetNodeCombo";
|
this._targetNodeCombo.Name = "_targetNodeCombo";
|
||||||
this._targetNodeCombo.Size = new System.Drawing.Size(210, 20);
|
this._targetNodeCombo.Size = new System.Drawing.Size(210, 20);
|
||||||
@@ -586,6 +625,7 @@ namespace AGVSimulator.Forms
|
|||||||
// _startNodeCombo
|
// _startNodeCombo
|
||||||
//
|
//
|
||||||
this._startNodeCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
this._startNodeCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
|
this._startNodeCombo.Font = new System.Drawing.Font("돋움체", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
this._startNodeCombo.Location = new System.Drawing.Point(10, 45);
|
this._startNodeCombo.Location = new System.Drawing.Point(10, 45);
|
||||||
this._startNodeCombo.Name = "_startNodeCombo";
|
this._startNodeCombo.Name = "_startNodeCombo";
|
||||||
this._startNodeCombo.Size = new System.Drawing.Size(210, 20);
|
this._startNodeCombo.Size = new System.Drawing.Size(210, 20);
|
||||||
@@ -720,7 +760,7 @@ namespace AGVSimulator.Forms
|
|||||||
this._canvasPanel.Dock = System.Windows.Forms.DockStyle.Fill;
|
this._canvasPanel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this._canvasPanel.Location = new System.Drawing.Point(0, 129);
|
this._canvasPanel.Location = new System.Drawing.Point(0, 129);
|
||||||
this._canvasPanel.Name = "_canvasPanel";
|
this._canvasPanel.Name = "_canvasPanel";
|
||||||
this._canvasPanel.Size = new System.Drawing.Size(801, 560);
|
this._canvasPanel.Size = new System.Drawing.Size(1015, 560);
|
||||||
this._canvasPanel.TabIndex = 4;
|
this._canvasPanel.TabIndex = 4;
|
||||||
//
|
//
|
||||||
// lbPredict
|
// lbPredict
|
||||||
@@ -728,7 +768,7 @@ namespace AGVSimulator.Forms
|
|||||||
this.lbPredict.Dock = System.Windows.Forms.DockStyle.Bottom;
|
this.lbPredict.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
this.lbPredict.Location = new System.Drawing.Point(0, 513);
|
this.lbPredict.Location = new System.Drawing.Point(0, 513);
|
||||||
this.lbPredict.Name = "lbPredict";
|
this.lbPredict.Name = "lbPredict";
|
||||||
this.lbPredict.Size = new System.Drawing.Size(801, 47);
|
this.lbPredict.Size = new System.Drawing.Size(1015, 47);
|
||||||
this.lbPredict.TabIndex = 0;
|
this.lbPredict.TabIndex = 0;
|
||||||
this.lbPredict.Text = "";
|
this.lbPredict.Text = "";
|
||||||
//
|
//
|
||||||
@@ -743,7 +783,7 @@ namespace AGVSimulator.Forms
|
|||||||
this._agvInfoPanel.Dock = System.Windows.Forms.DockStyle.Top;
|
this._agvInfoPanel.Dock = System.Windows.Forms.DockStyle.Top;
|
||||||
this._agvInfoPanel.Location = new System.Drawing.Point(0, 49);
|
this._agvInfoPanel.Location = new System.Drawing.Point(0, 49);
|
||||||
this._agvInfoPanel.Name = "_agvInfoPanel";
|
this._agvInfoPanel.Name = "_agvInfoPanel";
|
||||||
this._agvInfoPanel.Size = new System.Drawing.Size(801, 80);
|
this._agvInfoPanel.Size = new System.Drawing.Size(1015, 80);
|
||||||
this._agvInfoPanel.TabIndex = 5;
|
this._agvInfoPanel.TabIndex = 5;
|
||||||
//
|
//
|
||||||
// _pathDebugLabel
|
// _pathDebugLabel
|
||||||
@@ -754,7 +794,7 @@ namespace AGVSimulator.Forms
|
|||||||
this._pathDebugLabel.Location = new System.Drawing.Point(10, 30);
|
this._pathDebugLabel.Location = new System.Drawing.Point(10, 30);
|
||||||
this._pathDebugLabel.Multiline = true;
|
this._pathDebugLabel.Multiline = true;
|
||||||
this._pathDebugLabel.Name = "_pathDebugLabel";
|
this._pathDebugLabel.Name = "_pathDebugLabel";
|
||||||
this._pathDebugLabel.Size = new System.Drawing.Size(947, 43);
|
this._pathDebugLabel.Size = new System.Drawing.Size(947, 45);
|
||||||
this._pathDebugLabel.TabIndex = 4;
|
this._pathDebugLabel.TabIndex = 4;
|
||||||
this._pathDebugLabel.Text = "경로: 설정되지 않음";
|
this._pathDebugLabel.Text = "경로: 설정되지 않음";
|
||||||
//
|
//
|
||||||
@@ -793,30 +833,21 @@ namespace AGVSimulator.Forms
|
|||||||
this.timer1.Interval = 500;
|
this.timer1.Interval = 500;
|
||||||
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
|
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
|
||||||
//
|
//
|
||||||
// groupBox1
|
// button1
|
||||||
//
|
//
|
||||||
this.groupBox1.Controls.Add(this.propertyNode);
|
this.button1.Location = new System.Drawing.Point(21, 201);
|
||||||
this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.button1.Name = "button1";
|
||||||
this.groupBox1.Location = new System.Drawing.Point(0, 546);
|
this.button1.Size = new System.Drawing.Size(106, 25);
|
||||||
this.groupBox1.Name = "groupBox1";
|
this.button1.TabIndex = 11;
|
||||||
this.groupBox1.Size = new System.Drawing.Size(233, 94);
|
this.button1.Text = "경로 계산2";
|
||||||
this.groupBox1.TabIndex = 4;
|
this.button1.UseVisualStyleBackColor = true;
|
||||||
this.groupBox1.TabStop = false;
|
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||||
this.groupBox1.Text = "노드 정보";
|
|
||||||
//
|
|
||||||
// propertyNode
|
|
||||||
//
|
|
||||||
this.propertyNode.Dock = System.Windows.Forms.DockStyle.Fill;
|
|
||||||
this.propertyNode.Location = new System.Drawing.Point(3, 17);
|
|
||||||
this.propertyNode.Name = "propertyNode";
|
|
||||||
this.propertyNode.Size = new System.Drawing.Size(227, 74);
|
|
||||||
this.propertyNode.TabIndex = 0;
|
|
||||||
//
|
//
|
||||||
// SimulatorForm
|
// SimulatorForm
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(1034, 711);
|
this.ClientSize = new System.Drawing.Size(1248, 711);
|
||||||
this.Controls.Add(this._canvasPanel);
|
this.Controls.Add(this._canvasPanel);
|
||||||
this.Controls.Add(this._agvInfoPanel);
|
this.Controls.Add(this._agvInfoPanel);
|
||||||
this.Controls.Add(this._controlPanel);
|
this.Controls.Add(this._controlPanel);
|
||||||
@@ -835,6 +866,7 @@ namespace AGVSimulator.Forms
|
|||||||
this._statusStrip.ResumeLayout(false);
|
this._statusStrip.ResumeLayout(false);
|
||||||
this._statusStrip.PerformLayout();
|
this._statusStrip.PerformLayout();
|
||||||
this._controlPanel.ResumeLayout(false);
|
this._controlPanel.ResumeLayout(false);
|
||||||
|
this.groupBox1.ResumeLayout(false);
|
||||||
this._statusGroup.ResumeLayout(false);
|
this._statusGroup.ResumeLayout(false);
|
||||||
this._statusGroup.PerformLayout();
|
this._statusGroup.PerformLayout();
|
||||||
this._pathGroup.ResumeLayout(false);
|
this._pathGroup.ResumeLayout(false);
|
||||||
@@ -844,7 +876,6 @@ namespace AGVSimulator.Forms
|
|||||||
this._canvasPanel.ResumeLayout(false);
|
this._canvasPanel.ResumeLayout(false);
|
||||||
this._agvInfoPanel.ResumeLayout(false);
|
this._agvInfoPanel.ResumeLayout(false);
|
||||||
this._agvInfoPanel.PerformLayout();
|
this._agvInfoPanel.PerformLayout();
|
||||||
this.groupBox1.ResumeLayout(false);
|
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
this.PerformLayout();
|
||||||
|
|
||||||
@@ -889,7 +920,6 @@ namespace AGVSimulator.Forms
|
|||||||
private System.Windows.Forms.ComboBox _startNodeCombo;
|
private System.Windows.Forms.ComboBox _startNodeCombo;
|
||||||
private System.Windows.Forms.Label targetNodeLabel;
|
private System.Windows.Forms.Label targetNodeLabel;
|
||||||
private System.Windows.Forms.ComboBox _targetNodeCombo;
|
private System.Windows.Forms.ComboBox _targetNodeCombo;
|
||||||
private System.Windows.Forms.Button _calculatePathButton;
|
|
||||||
private System.Windows.Forms.Button _clearPathButton;
|
private System.Windows.Forms.Button _clearPathButton;
|
||||||
private System.Windows.Forms.Button _targetCalcButton;
|
private System.Windows.Forms.Button _targetCalcButton;
|
||||||
private System.Windows.Forms.CheckBox _avoidRotationCheckBox;
|
private System.Windows.Forms.CheckBox _avoidRotationCheckBox;
|
||||||
@@ -925,5 +955,9 @@ namespace AGVSimulator.Forms
|
|||||||
private System.Windows.Forms.ToolStripMenuItem 맵다른이름으로저장ToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem 맵다른이름으로저장ToolStripMenuItem;
|
||||||
private System.Windows.Forms.GroupBox groupBox1;
|
private System.Windows.Forms.GroupBox groupBox1;
|
||||||
private System.Windows.Forms.PropertyGrid propertyNode;
|
private System.Windows.Forms.PropertyGrid propertyNode;
|
||||||
|
private System.Windows.Forms.Button btPath2;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem btSelectMapEditor;
|
||||||
|
private System.Windows.Forms.ToolStripStatusLabel sbFile;
|
||||||
|
private System.Windows.Forms.Button button1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,7 @@ namespace AGVSimulator.Forms
|
|||||||
}
|
}
|
||||||
|
|
||||||
private UnifiedAGVCanvas _simulatorCanvas;
|
private UnifiedAGVCanvas _simulatorCanvas;
|
||||||
private AGVPathfinder _advancedPathfinder;
|
// private AGVPathfinder _advancedPathfinder;
|
||||||
private List<VirtualAGV> _agvList;
|
private List<VirtualAGV> _agvList;
|
||||||
private SimulationState _simulationState;
|
private SimulationState _simulationState;
|
||||||
private System.Windows.Forms.Timer _simulationTimer;
|
private System.Windows.Forms.Timer _simulationTimer;
|
||||||
@@ -186,6 +186,7 @@ namespace AGVSimulator.Forms
|
|||||||
{
|
{
|
||||||
_simulatorCanvas = new UnifiedAGVCanvas();
|
_simulatorCanvas = new UnifiedAGVCanvas();
|
||||||
_simulatorCanvas.Dock = DockStyle.Fill;
|
_simulatorCanvas.Dock = DockStyle.Fill;
|
||||||
|
_simulatorCanvas.Mode = UnifiedAGVCanvas.CanvasMode.Emulator;
|
||||||
|
|
||||||
// 목적지 선택 이벤트 구독
|
// 목적지 선택 이벤트 구독
|
||||||
_simulatorCanvas.NodesSelected += OnTargetNodeSelected;
|
_simulatorCanvas.NodesSelected += OnTargetNodeSelected;
|
||||||
@@ -363,100 +364,7 @@ namespace AGVSimulator.Forms
|
|||||||
UpdateUI();
|
UpdateUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCalculatePath_Click(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
var rlt = CalcPath();
|
|
||||||
if (rlt.result == false) MessageBox.Show(rlt.message, "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
||||||
}
|
|
||||||
|
|
||||||
(bool result, string message) CalcPath()
|
|
||||||
{
|
|
||||||
// 시작 RFID가 없으면 AGV 현재 위치로 설정
|
|
||||||
if (_startNodeCombo.SelectedItem == null || _startNodeCombo.Text == "선택하세요")
|
|
||||||
{
|
|
||||||
SetStartNodeFromAGVPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_startNodeCombo.SelectedItem == null || _targetNodeCombo.SelectedItem == null)
|
|
||||||
{
|
|
||||||
return (false, "시작 RFID와 목표 RFID를 선택해주세요.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var startItem = _startNodeCombo.SelectedItem as ComboBoxItem<MapNode>;
|
|
||||||
var targetItem = _targetNodeCombo.SelectedItem as ComboBoxItem<MapNode>;
|
|
||||||
var startNode = startItem?.Value;
|
|
||||||
var targetNode = targetItem?.Value;
|
|
||||||
|
|
||||||
if (startNode == null || targetNode == null)
|
|
||||||
{
|
|
||||||
return (false, "선택한 노드 정보가 올바르지 않습니다.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (_advancedPathfinder == null)
|
|
||||||
{
|
|
||||||
_advancedPathfinder = new AGVPathfinder(_simulatorCanvas.Nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 현재 AGV 방향 가져오기
|
|
||||||
var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
|
|
||||||
if (selectedAGV == null)
|
|
||||||
{
|
|
||||||
return (false, "Virtual AGV 가 없습니다");
|
|
||||||
}
|
|
||||||
var currentDirection = selectedAGV.CurrentDirection;
|
|
||||||
|
|
||||||
// AGV의 이전 위치에서 가장 가까운 노드 찾기
|
|
||||||
var prevNode = selectedAGV.PrevNode;
|
|
||||||
var prevDir = selectedAGV.PrevDirection;
|
|
||||||
|
|
||||||
// 고급 경로 계획 사용 (노드 객체 직접 전달)
|
|
||||||
var advancedResult = _advancedPathfinder.FindPath(startNode, targetNode, prevNode, prevDir, currentDirection);
|
|
||||||
|
|
||||||
_simulatorCanvas.FitToNodes();
|
|
||||||
if (advancedResult.Success)
|
|
||||||
{
|
|
||||||
// 도킹 검증이 없는 경우 추가 검증 수행
|
|
||||||
if (advancedResult.DockingValidation == null || !advancedResult.DockingValidation.IsValidationRequired)
|
|
||||||
{
|
|
||||||
advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _simulatorCanvas.Nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
//마지막대상이 버퍼라면 시퀀스처리를 해야한다
|
|
||||||
if(targetNode.StationType == StationType.Buffer)
|
|
||||||
{
|
|
||||||
var lastDetailPath = advancedResult.DetailedPath.Last();
|
|
||||||
if(lastDetailPath.NodeId == targetNode.Id) //마지막노드 재확인
|
|
||||||
{
|
|
||||||
//버퍼에 도킹할때에는 마지막 노드에서 멈추고 시퀀스를 적용해야한다
|
|
||||||
advancedResult.DetailedPath = advancedResult.DetailedPath.Take(advancedResult.DetailedPath.Count - 1).ToList();
|
|
||||||
Console.WriteLine("최종위치가 버퍼이므로 마지막 RFID에서 멈추도록 합니다");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
_simulatorCanvas.CurrentPath = advancedResult;
|
|
||||||
_pathLengthLabel.Text = $"경로 길이: {advancedResult.TotalDistance:F1}";
|
|
||||||
_statusLabel.Text = $"경로 계산 완료 ({advancedResult.CalculationTimeMs}ms)";
|
|
||||||
|
|
||||||
// 🔥 VirtualAGV에도 경로 설정 (Predict()가 동작하려면 필요)
|
|
||||||
selectedAGV.SetPath(advancedResult);
|
|
||||||
|
|
||||||
// 도킹 검증 결과 확인 및 UI 표시
|
|
||||||
CheckAndDisplayDockingValidation(advancedResult);
|
|
||||||
|
|
||||||
// 고급 경로 디버깅 정보 표시
|
|
||||||
UpdateAdvancedPathDebugInfo(advancedResult);
|
|
||||||
return (true, string.Empty);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 경로 실패시 디버깅 정보 초기화
|
|
||||||
_pathDebugLabel.Text = $"경로: 실패 - {advancedResult.ErrorMessage}";
|
|
||||||
|
|
||||||
return (false, $"경로를 찾을 수 없습니다:\n{advancedResult.ErrorMessage}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnClearPath_Click(object sender, EventArgs e)
|
private void OnClearPath_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
@@ -515,8 +423,6 @@ namespace AGVSimulator.Forms
|
|||||||
var displayText = GetDisplayName(selectedNode.Id);
|
var displayText = GetDisplayName(selectedNode.Id);
|
||||||
_statusLabel.Text = $"타겟계산 - 목적지: {displayText}";
|
_statusLabel.Text = $"타겟계산 - 목적지: {displayText}";
|
||||||
|
|
||||||
// 자동으로 경로 계산 수행
|
|
||||||
OnCalculatePath_Click(this, EventArgs.Empty);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -928,8 +834,8 @@ namespace AGVSimulator.Forms
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < _startNodeCombo.Items.Count; i++)
|
for (int i = 0; i < _startNodeCombo.Items.Count; i++)
|
||||||
{
|
{
|
||||||
var item = _startNodeCombo.Items[i].ToString();
|
var item = _startNodeCombo.Items[i] as ComboBoxItem<MapNode>;//.ToString();
|
||||||
if (item.Contains($"[{nodeId}]"))
|
if (item.Value.Id.Equals(nodeId))
|
||||||
{
|
{
|
||||||
_startNodeCombo.SelectedIndex = i;
|
_startNodeCombo.SelectedIndex = i;
|
||||||
Program.WriteLine($"[SYSTEM] 시작 노드를 '{nodeId}'로 자동 선택했습니다.");
|
Program.WriteLine($"[SYSTEM] 시작 노드를 '{nodeId}'로 자동 선택했습니다.");
|
||||||
@@ -970,7 +876,7 @@ namespace AGVSimulator.Forms
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = MapLoader.LoadMapFromFile(filePath);
|
var result = MapLoader.LoadMapFromFile(filePath);
|
||||||
|
sbFile.Text = filePath;
|
||||||
if (result.Success)
|
if (result.Success)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Map File Load : {filePath}");
|
Console.WriteLine($"Map File Load : {filePath}");
|
||||||
@@ -1053,10 +959,10 @@ namespace AGVSimulator.Forms
|
|||||||
{
|
{
|
||||||
foreach (var node in _simulatorCanvas.Nodes)
|
foreach (var node in _simulatorCanvas.Nodes)
|
||||||
{
|
{
|
||||||
if (node.IsActive && node.HasRfid())
|
if (node.IsActive)
|
||||||
{
|
{
|
||||||
// {rfid} - [{node}] {name} 형식으로 ComboBoxItem 생성
|
// {rfid} - [{node}] {name} 형식으로 ComboBoxItem 생성
|
||||||
var displayText = $"{node.RfidId} - [{node.Id}]";
|
var displayText = $"{node.StationType.ToString().PadRight(7)} | {node.ID2}";
|
||||||
var item = new ComboBoxItem<MapNode>(node, displayText);
|
var item = new ComboBoxItem<MapNode>(node, displayText);
|
||||||
|
|
||||||
_startNodeCombo.Items.Add(item);
|
_startNodeCombo.Items.Add(item);
|
||||||
@@ -1102,8 +1008,8 @@ namespace AGVSimulator.Forms
|
|||||||
_stopSimulationButton.Enabled = _simulationState.IsRunning;
|
_stopSimulationButton.Enabled = _simulationState.IsRunning;
|
||||||
|
|
||||||
_removeAgvButton.Enabled = _agvListCombo.SelectedItem != null;
|
_removeAgvButton.Enabled = _agvListCombo.SelectedItem != null;
|
||||||
_calculatePathButton.Enabled = _startNodeCombo.SelectedItem != null &&
|
// btPath1.Enabled = _startNodeCombo.SelectedItem != null &&
|
||||||
_targetNodeCombo.SelectedItem != null;
|
// _targetNodeCombo.SelectedItem != null;
|
||||||
|
|
||||||
// RFID 위치 설정 관련
|
// RFID 위치 설정 관련
|
||||||
var hasSelectedAGV = _agvListCombo.SelectedItem != null;
|
var hasSelectedAGV = _agvListCombo.SelectedItem != null;
|
||||||
@@ -1368,10 +1274,24 @@ namespace AGVSimulator.Forms
|
|||||||
{
|
{
|
||||||
var info = advancedResult.DetailedPath[i];
|
var info = advancedResult.DetailedPath[i];
|
||||||
var rfidId = GetRfidByNodeId(info.NodeId);
|
var rfidId = GetRfidByNodeId(info.NodeId);
|
||||||
var nextRfidId = info.NextNodeId != null ? GetRfidByNodeId(info.NextNodeId).ToString("0000") : "-END-";
|
|
||||||
|
var nextRfidId = "";
|
||||||
|
if (info.NextNode != null && info.NextNode.HasRfid())
|
||||||
|
{
|
||||||
|
nextRfidId = info.NextNode.RfidId.ToString("0000");
|
||||||
|
}
|
||||||
|
else if (info.NextNode != null)
|
||||||
|
{
|
||||||
|
nextRfidId = info.NextNode.Id;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextRfidId = "-END-";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var flags = new List<string>();
|
var flags = new List<string>();
|
||||||
if (info.CanRotate) flags.Add("회전가능");
|
if (info.IsTurn) flags.Add("회전");
|
||||||
if (info.IsDirectionChangePoint) flags.Add("방향전환");
|
if (info.IsDirectionChangePoint) flags.Add("방향전환");
|
||||||
if (info.RequiresSpecialAction) flags.Add($"특수동작:{info.SpecialActionDescription}");
|
if (info.RequiresSpecialAction) flags.Add($"특수동작:{info.SpecialActionDescription}");
|
||||||
if (info.MagnetDirection != MagnetDirection.Straight) flags.Add($"마그넷:{info.MagnetDirection}");
|
if (info.MagnetDirection != MagnetDirection.Straight) flags.Add($"마그넷:{info.MagnetDirection}");
|
||||||
@@ -1402,6 +1322,8 @@ namespace AGVSimulator.Forms
|
|||||||
else if (motorInfo.IsDirectionChangePoint && motorInfo.CanRotate)
|
else if (motorInfo.IsDirectionChangePoint && motorInfo.CanRotate)
|
||||||
motorSymbol += "[↻]";
|
motorSymbol += "[↻]";
|
||||||
|
|
||||||
|
if (motorInfo.IsTurn) motorSymbol = "[TURN]";
|
||||||
|
|
||||||
pathWithDetails.Add($"{rfidId}{motorSymbol}");
|
pathWithDetails.Add($"{rfidId}{motorSymbol}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1619,8 +1541,14 @@ namespace AGVSimulator.Forms
|
|||||||
private string GetNodeDisplayName(MapNode node)
|
private string GetNodeDisplayName(MapNode node)
|
||||||
{
|
{
|
||||||
if (node == null) return "-";
|
if (node == null) return "-";
|
||||||
if (node.HasRfid()) return node.RfidId.ToString("0000");
|
var retval = "";
|
||||||
return $"({node.Id})";
|
if (node.HasRfid()) retval = node.RfidId.ToString("0000");
|
||||||
|
else retval = $"({node.Id})";
|
||||||
|
if (node.DockDirection == DockingDirection.Forward)
|
||||||
|
retval += "(F)";
|
||||||
|
else if (node.DockDirection == DockingDirection.Backward)
|
||||||
|
retval += "(B)";
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1659,7 +1587,7 @@ namespace AGVSimulator.Forms
|
|||||||
/// UI 상태로부터 테스트 결과 생성 (테스트용)
|
/// UI 상태로부터 테스트 결과 생성 (테스트용)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private PathTestLogItem CreateTestResultFromUI(MapNode prevNode, MapNode targetNode,
|
private PathTestLogItem CreateTestResultFromUI(MapNode prevNode, MapNode targetNode,
|
||||||
string directionName, (bool result, string message) calcResult)
|
string directionName, AGVPathResult calcResult)
|
||||||
{
|
{
|
||||||
var currentNode = _simulatorCanvas.Nodes.FirstOrDefault(n => n.Id ==
|
var currentNode = _simulatorCanvas.Nodes.FirstOrDefault(n => n.Id ==
|
||||||
(_agvListCombo.SelectedItem as VirtualAGV)?.CurrentNodeId);
|
(_agvListCombo.SelectedItem as VirtualAGV)?.CurrentNodeId);
|
||||||
@@ -1670,13 +1598,13 @@ namespace AGVSimulator.Forms
|
|||||||
MotorDirection = directionName,
|
MotorDirection = directionName,
|
||||||
CurrentPosition = GetNodeDisplayName(currentNode),
|
CurrentPosition = GetNodeDisplayName(currentNode),
|
||||||
TargetPosition = GetNodeDisplayName(targetNode),
|
TargetPosition = GetNodeDisplayName(targetNode),
|
||||||
DockingPosition = targetNode.StationType == StationType.Charger ? "충전기" : "장비"
|
DockingPosition = (targetNode.StationType == Station.Charger) ? "충전기" : "장비"
|
||||||
};
|
};
|
||||||
|
|
||||||
if (calcResult.result)
|
if (calcResult.Success)
|
||||||
{
|
{
|
||||||
// 경로 계산 성공 - 현재 화면에 표시된 경로 정보 사용
|
// 경로 계산 성공 - 현재 화면에 표시된 경로 정보 사용
|
||||||
var currentPath = _simulatorCanvas.CurrentPath;
|
var currentPath = calcResult;// _simulatorCanvas.CurrentPath;
|
||||||
if (currentPath != null && currentPath.Success)
|
if (currentPath != null && currentPath.Success)
|
||||||
{
|
{
|
||||||
// 도킹 검증
|
// 도킹 검증
|
||||||
@@ -1686,13 +1614,13 @@ namespace AGVSimulator.Forms
|
|||||||
{
|
{
|
||||||
logItem.Success = true;
|
logItem.Success = true;
|
||||||
logItem.Message = "성공";
|
logItem.Message = "성공";
|
||||||
logItem.DetailedPath = currentPath.GetDetailedPathInfo();
|
logItem.DetailedPath = currentPath.GetDetailedPathInfo(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logItem.Success = false;
|
logItem.Success = false;
|
||||||
logItem.Message = $"도킹 검증 실패: {dockingValidation.ValidationError}";
|
logItem.Message = $"도킹 검증 실패: {dockingValidation.ValidationError}";
|
||||||
logItem.DetailedPath = currentPath.GetDetailedPathInfo();
|
logItem.DetailedPath = currentPath.GetDetailedPathInfo(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1706,7 +1634,7 @@ namespace AGVSimulator.Forms
|
|||||||
{
|
{
|
||||||
// 경로 계산 실패
|
// 경로 계산 실패
|
||||||
logItem.Success = false;
|
logItem.Success = false;
|
||||||
logItem.Message = calcResult.message;
|
logItem.Message = calcResult.Message;
|
||||||
logItem.DetailedPath = "-";
|
logItem.DetailedPath = "-";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1774,9 +1702,9 @@ namespace AGVSimulator.Forms
|
|||||||
logForm.AppendLog("---");
|
logForm.AppendLog("---");
|
||||||
|
|
||||||
// 각 연결된 노드 쌍에 대해 테스트
|
// 각 연결된 노드 쌍에 대해 테스트
|
||||||
foreach (var (nodeA, nodeB) in nodePairs)
|
|
||||||
{
|
|
||||||
foreach (var (direction, directionName) in directions)
|
foreach (var (direction, directionName) in directions)
|
||||||
|
{
|
||||||
|
foreach (var (nodeA, nodeB) in nodePairs)
|
||||||
{
|
{
|
||||||
// 취소 확인
|
// 취소 확인
|
||||||
if (logForm.CancelRequested)
|
if (logForm.CancelRequested)
|
||||||
@@ -1843,15 +1771,18 @@ namespace AGVSimulator.Forms
|
|||||||
SetTargetNodeComboBox(dockingTarget.Id);
|
SetTargetNodeComboBox(dockingTarget.Id);
|
||||||
|
|
||||||
// 경로 계산 버튼 클릭 (실제 사용자 동작)
|
// 경로 계산 버튼 클릭 (실제 사용자 동작)
|
||||||
var calcResult = CalcPath();
|
var startNode = (_startNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
|
||||||
|
var targetNode = (_targetNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
|
||||||
|
var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
|
||||||
|
var calcResult = CalcPath_New(startNode, targetNode, this._simulatorCanvas.Nodes, selectedAGV.PrevNode, selectedAGV.PrevDirection);
|
||||||
|
|
||||||
// 테스트 결과 생성
|
//// 테스트 결과 생성
|
||||||
testResult = CreateTestResultFromUI(nodeA, dockingTarget, directionName, calcResult);
|
testResult = CreateTestResultFromUI(nodeA, dockingTarget, directionName, calcResult);
|
||||||
|
|
||||||
// 로그 추가
|
//// 로그 추가
|
||||||
logForm.AddLogItem(testResult);
|
logForm.AddLogItem(testResult);
|
||||||
|
|
||||||
// 실패한 경우에만 경로를 화면에 표시 (시각적 확인)
|
//// 실패한 경우에만 경로를 화면에 표시 (시각적 확인)
|
||||||
if (!testResult.Success && _simulatorCanvas.CurrentPath != null)
|
if (!testResult.Success && _simulatorCanvas.CurrentPath != null)
|
||||||
{
|
{
|
||||||
_simulatorCanvas.Invalidate();
|
_simulatorCanvas.Invalidate();
|
||||||
@@ -1920,8 +1851,15 @@ namespace AGVSimulator.Forms
|
|||||||
|
|
||||||
// 첫 번째 AGV의 다음 행동 예측
|
// 첫 번째 AGV의 다음 행동 예측
|
||||||
var agv = _agvList[0];
|
var agv = _agvList[0];
|
||||||
|
try
|
||||||
|
{
|
||||||
var command = agv.Predict();
|
var command = agv.Predict();
|
||||||
this.lbPredict.Text = $"Motor:{command.Motor},Magnet:{command.Magnet},Speed:{command.Speed} : {command.Message}";
|
this.lbPredict.Text = $"Motor:{command.Motor},Magnet:{command.Magnet},Speed:{command.Speed} : {command.Message}";
|
||||||
|
}catch ( Exception ex)
|
||||||
|
{
|
||||||
|
lbPredict.Text = "예측오류" + ex.Message;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2122,7 +2060,8 @@ namespace AGVSimulator.Forms
|
|||||||
_portCombo = new ComboBox();
|
_portCombo = new ComboBox();
|
||||||
_portCombo.Width = 100;
|
_portCombo.Width = 100;
|
||||||
_portCombo.DropDownStyle = ComboBoxStyle.DropDownList;
|
_portCombo.DropDownStyle = ComboBoxStyle.DropDownList;
|
||||||
_portCombo.DropDown += (s, e) => {
|
_portCombo.DropDown += (s, e) =>
|
||||||
|
{
|
||||||
_portCombo.Items.Clear();
|
_portCombo.Items.Clear();
|
||||||
_portCombo.Items.AddRange(SerialPort.GetPortNames());
|
_portCombo.Items.AddRange(SerialPort.GetPortNames());
|
||||||
};
|
};
|
||||||
@@ -2184,8 +2123,12 @@ namespace AGVSimulator.Forms
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_emulatorPort != null && _emulatorPort.IsOpen)
|
if (_emulatorPort != null)
|
||||||
{
|
{
|
||||||
|
// 이벤트 핸들러 해제 (중복 호출 방지)
|
||||||
|
_emulatorPort.DataReceived -= OnEmulatorDataReceived;
|
||||||
|
|
||||||
|
if (_emulatorPort.IsOpen)
|
||||||
_emulatorPort.Close();
|
_emulatorPort.Close();
|
||||||
}
|
}
|
||||||
_isEmulatorConnected = false;
|
_isEmulatorConnected = false;
|
||||||
@@ -2204,6 +2147,8 @@ namespace AGVSimulator.Forms
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (_emulatorPort == null || !_emulatorPort.IsOpen) return;
|
||||||
|
|
||||||
string data = _emulatorPort.ReadExisting();
|
string data = _emulatorPort.ReadExisting();
|
||||||
_recvBuffer.Append(data);
|
_recvBuffer.Append(data);
|
||||||
|
|
||||||
@@ -2234,6 +2179,11 @@ namespace AGVSimulator.Forms
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Emulator Recv Error: {ex.Message}");
|
Console.WriteLine($"Emulator Recv Error: {ex.Message}");
|
||||||
|
// 수신 중 오류 발생 시 연결 해제 처리 (UI 스레드에서 실행)
|
||||||
|
this.Invoke(new Action(() =>
|
||||||
|
{
|
||||||
|
if (_isEmulatorConnected) DisconnectEmulator();
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2313,7 +2263,8 @@ namespace AGVSimulator.Forms
|
|||||||
// AGV 제어 (첫 번째 AGV 대상)
|
// AGV 제어 (첫 번째 AGV 대상)
|
||||||
var agv = _agvList.FirstOrDefault();
|
var agv = _agvList.FirstOrDefault();
|
||||||
|
|
||||||
this.Invoke(new Action(() => {
|
this.Invoke(new Action(() =>
|
||||||
|
{
|
||||||
switch (cmd)
|
switch (cmd)
|
||||||
{
|
{
|
||||||
case "CRN": // 기동명령
|
case "CRN": // 기동명령
|
||||||
@@ -2438,7 +2389,11 @@ namespace AGVSimulator.Forms
|
|||||||
|
|
||||||
private void SendEmulatorStatus()
|
private void SendEmulatorStatus()
|
||||||
{
|
{
|
||||||
if (_emulatorPort == null || !_emulatorPort.IsOpen) return;
|
if (_emulatorPort == null || !_emulatorPort.IsOpen)
|
||||||
|
{
|
||||||
|
if (_isEmulatorConnected) DisconnectEmulator();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var agv = _agvList.FirstOrDefault();
|
var agv = _agvList.FirstOrDefault();
|
||||||
|
|
||||||
@@ -2491,7 +2446,10 @@ namespace AGVSimulator.Forms
|
|||||||
{
|
{
|
||||||
_emulatorPort.Write(barr, 0, barr.Length);
|
_emulatorPort.Write(barr, 0, barr.Length);
|
||||||
}
|
}
|
||||||
catch { }
|
catch
|
||||||
|
{
|
||||||
|
if (_isEmulatorConnected) DisconnectEmulator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CalculateChecksum(string data)
|
private string CalculateChecksum(string data)
|
||||||
@@ -2505,48 +2463,215 @@ namespace AGVSimulator.Forms
|
|||||||
return hex.PadLeft(2, '0');
|
return hex.PadLeft(2, '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//(bool result, string message) CalcPath()
|
||||||
|
//{
|
||||||
|
// // 시작 RFID가 없으면 AGV 현재 위치로 설정
|
||||||
|
// if (_startNodeCombo.SelectedItem == null || _startNodeCombo.Text == "선택하세요")
|
||||||
|
// {
|
||||||
|
// SetStartNodeFromAGVPosition();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (_startNodeCombo.SelectedItem == null || _targetNodeCombo.SelectedItem == null)
|
||||||
|
// {
|
||||||
|
// return (false, "시작 RFID와 목표 RFID를 선택해주세요.");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var startItem = _startNodeCombo.SelectedItem as ComboBoxItem<MapNode>;
|
||||||
|
// var targetItem = _targetNodeCombo.SelectedItem as ComboBoxItem<MapNode>;
|
||||||
|
// var startNode = startItem?.Value;
|
||||||
|
// var targetNode = targetItem?.Value;
|
||||||
|
|
||||||
|
// if (startNode == null || targetNode == null)
|
||||||
|
// {
|
||||||
|
// return (false, "선택한 노드 정보가 올바르지 않습니다.");
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// if (_advancedPathfinder == null)
|
||||||
|
// {
|
||||||
|
// _advancedPathfinder = new AGVPathfinder(_simulatorCanvas.Nodes);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 현재 AGV 방향 가져오기
|
||||||
|
// var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
|
||||||
|
// if (selectedAGV == null)
|
||||||
|
// {
|
||||||
|
// return (false, "Virtual AGV 가 없습니다");
|
||||||
|
// }
|
||||||
|
// var currentDirection = selectedAGV.CurrentDirection;
|
||||||
|
|
||||||
|
// // AGV의 이전 위치에서 가장 가까운 노드 찾기
|
||||||
|
// var prevNode = selectedAGV.PrevNode;
|
||||||
|
// var prevDir = selectedAGV.PrevDirection;
|
||||||
|
|
||||||
|
// // 고급 경로 계획 사용 (노드 객체 직접 전달)
|
||||||
|
// var advancedResult = _advancedPathfinder.FindPath(startNode, targetNode, prevNode, prevDir, currentDirection);
|
||||||
|
|
||||||
|
// _simulatorCanvas.FitToNodes();
|
||||||
|
// if (advancedResult.Success)
|
||||||
|
// {
|
||||||
|
// // 도킹 검증이 없는 경우 추가 검증 수행
|
||||||
|
// if (advancedResult.DockingValidation == null || !advancedResult.DockingValidation.IsValidationRequired)
|
||||||
|
// {
|
||||||
|
// advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _simulatorCanvas.Nodes);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //마지막대상이 버퍼라면 시퀀스처리를 해야한다
|
||||||
|
// if (targetNode.StationType == StationType.Buffer)
|
||||||
|
// {
|
||||||
|
// var lastDetailPath = advancedResult.DetailedPath.Last();
|
||||||
|
// if (lastDetailPath.NodeId == targetNode.Id) //마지막노드 재확인
|
||||||
|
// {
|
||||||
|
// //버퍼에 도킹할때에는 마지막 노드에서 멈추고 시퀀스를 적용해야한다
|
||||||
|
// advancedResult.DetailedPath = advancedResult.DetailedPath.Take(advancedResult.DetailedPath.Count - 1).ToList();
|
||||||
|
// Console.WriteLine("최종위치가 버퍼이므로 마지막 RFID에서 멈추도록 합니다");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// _simulatorCanvas.CurrentPath = advancedResult;
|
||||||
|
// _pathLengthLabel.Text = $"경로 길이: {advancedResult.TotalDistance:F1}";
|
||||||
|
// _statusLabel.Text = $"경로 계산 완료 ({advancedResult.CalculationTimeMs}ms)";
|
||||||
|
|
||||||
|
// // 🔥 VirtualAGV에도 경로 설정 (Predict()가 동작하려면 필요)
|
||||||
|
// selectedAGV.SetPath(advancedResult);
|
||||||
|
|
||||||
|
// // 도킹 검증 결과 확인 및 UI 표시
|
||||||
|
// CheckAndDisplayDockingValidation(advancedResult);
|
||||||
|
|
||||||
|
// // 고급 경로 디버깅 정보 표시
|
||||||
|
// UpdateAdvancedPathDebugInfo(advancedResult);
|
||||||
|
// return (true, string.Empty);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// // 경로 실패시 디버깅 정보 초기화
|
||||||
|
// _pathDebugLabel.Text = $"경로: 실패 - {advancedResult.ErrorMessage}";
|
||||||
|
|
||||||
|
// return (false, $"경로를 찾을 수 없습니다:\n{advancedResult.ErrorMessage}");
|
||||||
|
// }
|
||||||
|
//}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private void btPath2_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// 1. 기본 정보 획득
|
||||||
|
if (_startNodeCombo.SelectedItem == null || _startNodeCombo.Text == "선택하세요") SetStartNodeFromAGVPosition();
|
||||||
|
if (_startNodeCombo.SelectedItem == null || _targetNodeCombo.SelectedItem == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("시작/목표 노드를 확인하세요");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
|
||||||
|
//if (selectedAGV == null) return AGVPathResult.CreateFailure("Virtual AGV 없음");
|
||||||
|
var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
|
||||||
|
|
||||||
|
// 경로계산2 (Gateway Logic)
|
||||||
|
var startNode = (_startNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
|
||||||
|
var targetNode = (_targetNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
|
||||||
|
var rlt = CalcPath(startNode, targetNode, this._simulatorCanvas.Nodes, selectedAGV.PrevNode, selectedAGV.PrevDirection);
|
||||||
|
if (rlt.Success == false) MessageBox.Show(rlt.Message, "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 8. 적용
|
||||||
|
|
||||||
|
ApplyResultToSimulator(rlt, selectedAGV);
|
||||||
|
UpdateAdvancedPathDebugInfo(rlt);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 방향 콤보박스용 아이템 클래스
|
/// 길목(Gateway) 기반 경로 계산
|
||||||
|
/// 버퍼-버퍼 상태에서는 별도의 추가 로직을 적용합니다
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DirectionItem
|
public AGVPathResult CalcPath(MapNode startNode, MapNode targetNode, List<MapNode> nodes,
|
||||||
|
MapNode prevNode, AgvDirection prevDir)
|
||||||
{
|
{
|
||||||
public AgvDirection Direction { get; }
|
// Core Logic으로 이관됨
|
||||||
public string DisplayText { get; }
|
var pathFinder = new AGVPathfinder(nodes);
|
||||||
|
var result = pathFinder.CalculatePath(startNode, targetNode, prevNode, prevDir);
|
||||||
|
|
||||||
public DirectionItem(AgvDirection direction, string displayText)
|
//게이트웨이노드를 하이라이트강조 한단
|
||||||
{
|
this._simulatorCanvas.HighlightNodeId = (result.Gateway?.Id ?? string.Empty);
|
||||||
Direction = direction;
|
return result;
|
||||||
DisplayText = displayText;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return DisplayText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 제네릭 콤보박스 아이템 클래스
|
/// 길목(Gateway) 기반 경로 계산
|
||||||
|
/// 버퍼-버퍼 상태에서는 별도의 추가 로직을 적용합니다
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">값의 타입</typeparam>
|
public AGVPathResult CalcPath_New(MapNode startNode, MapNode targetNode, List<MapNode> nodes,
|
||||||
public class ComboBoxItem<T>
|
MapNode prevNode, AgvDirection prevDir)
|
||||||
{
|
{
|
||||||
public T Value { get; }
|
// Core Logic으로 이관됨
|
||||||
public string DisplayText { get; }
|
var pathFinder = new AGVPathfinder(nodes);
|
||||||
|
var result = pathFinder.CalculateScriptedPath(startNode, targetNode, prevNode, prevDir);
|
||||||
|
|
||||||
public ComboBoxItem(T value, string displayText)
|
//게이트웨이노드를 하이라이트강조 한단
|
||||||
|
this._simulatorCanvas.HighlightNodeId = (result.Gateway?.Id ?? string.Empty);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
private void ApplyResultToSimulator(AGVPathResult result, VirtualAGV agv)
|
||||||
{
|
{
|
||||||
Value = value;
|
_simulatorCanvas.CurrentPath = result;
|
||||||
DisplayText = displayText;
|
_pathLengthLabel.Text = $"Gateway경로: {result.TotalDistance:F1}";
|
||||||
|
agv.SetPath(result);
|
||||||
|
//_simulatorCanvas.CheckAndDisplayDockingValidation(result); // Optional/Needs access
|
||||||
|
_simulatorCanvas.FitToNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
private void btSelectMapEditor_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
return DisplayText;
|
using (var openDialog = new OpenFileDialog())
|
||||||
|
{
|
||||||
|
openDialog.Filter = "실행 파일 (*.exe)|*.exe|모든 파일 (*.*)|*.*";
|
||||||
|
openDialog.Title = "MapEditor 실행 파일 선택";
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_config.MapEditorExecutablePath) && File.Exists(_config.MapEditorExecutablePath))
|
||||||
|
{
|
||||||
|
openDialog.InitialDirectory = Path.GetDirectoryName(_config.MapEditorExecutablePath);
|
||||||
|
openDialog.FileName = Path.GetFileName(_config.MapEditorExecutablePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (openDialog.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
_config.MapEditorExecutablePath = openDialog.FileName;
|
||||||
|
_config.Save();
|
||||||
|
|
||||||
|
_statusLabel.Text = $"MapEditor 경로 설정: {Path.GetFileName(openDialog.FileName)}";
|
||||||
|
MessageBox.Show($"MapEditor 실행 파일이 설정되었습니다:\n{openDialog.FileName}",
|
||||||
|
"경로 설정 완료", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button1_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// 1. 기본 정보 획득
|
||||||
|
if (_startNodeCombo.SelectedItem == null || _startNodeCombo.Text == "선택하세요") SetStartNodeFromAGVPosition();
|
||||||
|
if (_startNodeCombo.SelectedItem == null || _targetNodeCombo.SelectedItem == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("시작/목표 노드를 확인하세요");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
|
||||||
|
//if (selectedAGV == null) return AGVPathResult.CreateFailure("Virtual AGV 없음");
|
||||||
|
var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
|
||||||
|
|
||||||
|
// 경로계산2 (Gateway Logic)
|
||||||
|
var startNode = (_startNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
|
||||||
|
var targetNode = (_targetNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
|
||||||
|
var rlt = CalcPath_New(startNode, targetNode, this._simulatorCanvas.Nodes, selectedAGV.PrevNode, selectedAGV.PrevDirection);
|
||||||
|
if (rlt.Success == false) MessageBox.Show(rlt.Message, "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 8. 적용
|
||||||
|
|
||||||
|
ApplyResultToSimulator(rlt, selectedAGV);
|
||||||
|
UpdateAdvancedPathDebugInfo(rlt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
13
AGVLogic/EnigProtocol/.gitignore
vendored
Normal file
13
AGVLogic/EnigProtocol/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
obj
|
||||||
|
bin
|
||||||
|
*.user
|
||||||
|
*.v12
|
||||||
|
*.suo
|
||||||
|
.git
|
||||||
|
.vs
|
||||||
|
Debug
|
||||||
|
__vm
|
||||||
|
*.pdb
|
||||||
|
desktop.ini
|
||||||
|
packages
|
||||||
|
~*.xlsx
|
||||||
53
AGVLogic/EnigProtocol/ENIGProtocol.Tests/EEProtocolTests.cs
Normal file
53
AGVLogic/EnigProtocol/ENIGProtocol.Tests/EEProtocolTests.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
using Xunit;
|
||||||
|
using ENIG;
|
||||||
|
|
||||||
|
namespace ENIGProtocol.Tests
|
||||||
|
{
|
||||||
|
public class EEProtocolTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void TestCRC16Calculation()
|
||||||
|
{
|
||||||
|
// 테스트 데이터
|
||||||
|
byte[] testData = new byte[] { 0x02,0x00,0xFF }; //payload에는 stx, len, ... crc,etx 는 제외한다
|
||||||
|
|
||||||
|
// CRC16 계산
|
||||||
|
var protocol = new EEProtocol();
|
||||||
|
ushort crc = protocol.CalculateCRC16(testData);
|
||||||
|
|
||||||
|
// 예상 결과와 비교
|
||||||
|
Assert.Equal(0x1789, crc);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TestPacketCreation()
|
||||||
|
{
|
||||||
|
// 패킷 생성 테스트
|
||||||
|
var protocol = new EEProtocol();
|
||||||
|
byte[] packet = protocol.CreatePacket(0x01, 0x02, new byte[] { 0x03, 0x04 });
|
||||||
|
|
||||||
|
// 패킷 구조 검증
|
||||||
|
Assert.Equal(0x02, packet[0]); // STX
|
||||||
|
Assert.Equal(0x04, packet[1]); // Length
|
||||||
|
Assert.Equal(0x01, packet[2]); // ID
|
||||||
|
Assert.Equal(0x02, packet[3]); // Command
|
||||||
|
Assert.Equal(0x03, packet[4]); // Data[0]
|
||||||
|
Assert.Equal(0x04, packet[5]); // Data[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TestPacketParsing()
|
||||||
|
{
|
||||||
|
// 패킷 파싱 테스트
|
||||||
|
var protocol = new EEProtocol();
|
||||||
|
|
||||||
|
//byte[] testPacket = new byte[] { 0x02, 0x04, 0x01, 0x02, 0x03, 0x04, 0x12, 0x34, 0x03 };
|
||||||
|
byte[] testPacket = new byte[] { 0x02, 0x02, 0x00, 0xFF, 0x89, 0x17, 0x03 };
|
||||||
|
|
||||||
|
|
||||||
|
bool result = protocol.ParsePacket(testPacket);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
|
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||||
|
<FileUpgradeFlags>
|
||||||
|
</FileUpgradeFlags>
|
||||||
|
<UpgradeBackupLocation>
|
||||||
|
</UpgradeBackupLocation>
|
||||||
|
<OldToolsVersion>2.0</OldToolsVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
||||||
|
<PackageReference Include="xunit" Version="2.9.3" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="6.0.4">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\enigprotocol\enigprotocol.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="Xunit" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
10
AGVLogic/EnigProtocol/ENIGProtocol.Tests/UnitTest1.cs
Normal file
10
AGVLogic/EnigProtocol/ENIGProtocol.Tests/UnitTest1.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace ENIGProtocol.Tests;
|
||||||
|
|
||||||
|
public class UnitTest1
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Test1()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
43
AGVLogic/EnigProtocol/ENIGProtocol.sln
Normal file
43
AGVLogic/EnigProtocol/ENIGProtocol.sln
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Express 15 for Windows Desktop
|
||||||
|
VisualStudioVersion = 15.0.28307.1000
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "솔루션 항목", "솔루션 항목", "{0A11874A-E5C6-4170-9787-1FFF7AF0D289}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.gitignore = .gitignore
|
||||||
|
ReadMe.MD = ReadMe.MD
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleProject", "sample\SampleProject.csproj", "{FAB31C8A-7DCF-4152-8A82-76F3C10BABA4}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ENIGProtocol.Tests", "ENIGProtocol.Tests\ENIGProtocol.Tests.csproj", "{3A677629-1F08-49B2-BC75-58282E439FD4}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ENIGProtocol", "enigprotocol\ENIGProtocol.csproj", "{499D8912-4B96-41E5-A70D-CFE797883D65}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{FAB31C8A-7DCF-4152-8A82-76F3C10BABA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{FAB31C8A-7DCF-4152-8A82-76F3C10BABA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{FAB31C8A-7DCF-4152-8A82-76F3C10BABA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FAB31C8A-7DCF-4152-8A82-76F3C10BABA4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{3A677629-1F08-49B2-BC75-58282E439FD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{3A677629-1F08-49B2-BC75-58282E439FD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{3A677629-1F08-49B2-BC75-58282E439FD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{3A677629-1F08-49B2-BC75-58282E439FD4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{499D8912-4B96-41E5-A70D-CFE797883D65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{499D8912-4B96-41E5-A70D-CFE797883D65}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{499D8912-4B96-41E5-A70D-CFE797883D65}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{499D8912-4B96-41E5-A70D-CFE797883D65}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {07331835-C46C-4B93-965F-AD2714F97F88}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
246
AGVLogic/EnigProtocol/ReadMe.MD
Normal file
246
AGVLogic/EnigProtocol/ReadMe.MD
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
# ENIG Protocol
|
||||||
|
|
||||||
|
ENIG 프로토콜 통신 라이브러리
|
||||||
|
|
||||||
|
**Explanation of the README Content:**
|
||||||
|
|
||||||
|
1. **Title and Overview:** Clearly states the purpose of the library.
|
||||||
|
2. **Key Components:** Explains the main building blocks:
|
||||||
|
* `DeviceType` enum: Lists the device types.
|
||||||
|
* `Packet` class: Describes the packet structure.
|
||||||
|
* `EEProtocol` class: Explains its role in packet handling.
|
||||||
|
3. **Key Features:** Details the core functionalities of the `EEProtocol` class:
|
||||||
|
* Packet creation.
|
||||||
|
* Packet parsing.
|
||||||
|
* Data reception.
|
||||||
|
* Event-driven nature.
|
||||||
|
* CRC16 calculation.
|
||||||
|
4. **Usage:** Provides code examples for:
|
||||||
|
* Creating a packet.
|
||||||
|
* Parsing a packet.
|
||||||
|
* Processing received data.
|
||||||
|
5. **Error Handling:** Explains how the library handles invalid data and incomplete packets.
|
||||||
|
6. **Dependencies:** Lists any required frameworks or libraries.
|
||||||
|
7. **License:** Placeholder for the project's license.
|
||||||
|
|
||||||
|
This README provides a comprehensive overview of the `ENIGProtocol` library, making it easy for other developers to understand and use it. I hope this is helpful!
|
||||||
|
|
||||||
|
|
||||||
|
## 개발 환경
|
||||||
|
- Frameworks : .NET Standard 2.0
|
||||||
|
- Visual Studio 2022 또는 Visual Studio Code
|
||||||
|
|
||||||
|
## 프로젝트 구조
|
||||||
|
|
||||||
|
- `EEProtocol.cs`: ENIG 프로토콜 구현
|
||||||
|
- `EEProtocolTests.cs`: 단위 테스트 코드
|
||||||
|
- `Sample`: C# Winform 샘플 프로젝트
|
||||||
|
|
||||||
|
## 장비 목록
|
||||||
|
```
|
||||||
|
public enum DeviceType : byte
|
||||||
|
{
|
||||||
|
ACS = 0,
|
||||||
|
AGV1 = 10+1,
|
||||||
|
AGV2 = 10+2,
|
||||||
|
BUFFER1 = 20+1,
|
||||||
|
BUFFER2 = 20+2,
|
||||||
|
BUFFER3 = 20+3,
|
||||||
|
BUFFER4 = 20+4,
|
||||||
|
BUFFER5 = 20+5,
|
||||||
|
DOOR = 30,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
public enum DeviceAlias : byte
|
||||||
|
{
|
||||||
|
B1 = 20 + 1, //BUFFER1 ~ 5
|
||||||
|
B2 = 20 + 2,
|
||||||
|
B3 = 20 + 3,
|
||||||
|
B4 = 20 + 4,
|
||||||
|
B5 = 20 + 5,
|
||||||
|
C1 = 40 + 1, //충전소 1
|
||||||
|
C2 = 40 + 2, //충전소 2
|
||||||
|
C3 = 40 + 3, //충전소 3
|
||||||
|
C4 = 40 + 4, //충전소 4
|
||||||
|
E1 = 90 + 1, //장비1 (SSOTRON Loader)
|
||||||
|
E2 = 90 + 2, //장비2 (TOPS ENIG)
|
||||||
|
E3 = 90 + 3, //장비3 (SSOTRON DIVERTER)
|
||||||
|
}
|
||||||
|
//11번 AGV야! BUFFER1로 이동해라!
|
||||||
|
//0x02 0x03 0x0B 0x6B 0x42 0x31 {CRC} 0x03
|
||||||
|
|
||||||
|
### 기본 패킷 구조
|
||||||
|
```
|
||||||
|
[STX][LEN][ID][CMD][DATA][CRC16][ETX]
|
||||||
|
```
|
||||||
|
- **STX (Start of Text)**: 0x02
|
||||||
|
- **LEN (Length)**: 데이터 길이 (1바이트) = {CMD+DATA}
|
||||||
|
- **ID (Client ID)**: 데이터 길이 (1바이트) : 디바이스식별코드(=DeviceType)
|
||||||
|
- **CMD (Command)**: 명령어 코드 (1바이트)
|
||||||
|
- **DATA**: 명령어에 따른 데이터 (가변 길이)
|
||||||
|
- **CRC16**: 데이터 무결성 검사 (2바이트)
|
||||||
|
- **ETX (End of Text)**: 0x03
|
||||||
|
|
||||||
|
### 통신 방향 (호스트=ACS, 장비=agv,buffer,door)
|
||||||
|
- H -> E: 호스트에서 장비로 전송
|
||||||
|
- E -> H: 장비에서 호스트로 전송
|
||||||
|
|
||||||
|
### 명령어 목록
|
||||||
|
1. **ACS (AGV Control System)**
|
||||||
|
|
||||||
|
|
||||||
|
2. **Buffer**
|
||||||
|
- E -> H | cmd(3): 상태 (data len=1 : 0=카트없음, 1=카트있음, 2=바쁨, 3=알수없음, 255=오류)
|
||||||
|
- H -> E | cmd(1): Lock
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
- H -> E | cmd(2): UnLock
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
|
||||||
|
3. **AGV**
|
||||||
|
- H -> E | Move : cmd(100) : 대상태그까지 이동(자동이동)
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
- TagID[4] = "0000"
|
||||||
|
|
||||||
|
- H -> E | Stop : cmd(101) : 멈춤
|
||||||
|
- H -> E | Reset : cmd(102) : 오류 소거
|
||||||
|
|
||||||
|
- H -> E | SetCurrent : cmd(103) : 현재위치설정
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
- TagID[4] = "0000"
|
||||||
|
|
||||||
|
- H -> E | MoveManual : cmd(104) : 메뉴얼이동
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
- Direction[1] : 0=Backward, 1=Forward, 2=TurnLeft, 3=TurnRight
|
||||||
|
- Speed[1] : 0=Slow, 1=Normal, 2=Fast
|
||||||
|
|
||||||
|
- H -> E | MarkStop : cmd(105) : 마크센서스톱
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
|
||||||
|
- H -> E | Lift Control : cmd(106) : 리프트제어
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
- Action[1] : 0=STOP, 1=UP, 2=DOWN
|
||||||
|
|
||||||
|
- H -> E | Move : cmd(107) : 대상별칭까지 이동(자동이동)
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
- AliasName[n] = ".....
|
||||||
|
|
||||||
|
- H -> E | MoveAuto : cmd(108) : 자동이동
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
- MotDirection[1] : 0=Backward, 1=Forward
|
||||||
|
- MagnetDirection[1] : 0=Straight,1=Left, 2=Right
|
||||||
|
- Speed[1] : 0=Slow, 1=Normal, 2=Fast
|
||||||
|
|
||||||
|
- H -> E | Charge On: cmd(109) : 충전실행(충전기 이동 후 자동 충전 진행)
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
- Action[1] : 0=Charge Off, 1=Charge On
|
||||||
|
|
||||||
|
|
||||||
|
- E -> H | Move Complete : cmd(1) : 목적지이동완료 후 전송
|
||||||
|
- TagID[4] : "0000"
|
||||||
|
- E -> H | TagID Received : cmd(2) : 태그값 인식시 전송
|
||||||
|
- TagID[4] : "0000"
|
||||||
|
|
||||||
|
- E -> H | Status : cmd(3)
|
||||||
|
- Mode[1] : 0=manual, 1=auto
|
||||||
|
- RunSt[1] : 0=stop, 1=run, 2=error
|
||||||
|
- Diection[1] : 0=straight, 1=left, 2=right, 3=markstop
|
||||||
|
- Inposition[1] : 0=off, 1=on : 목적위치에 도달완료 시 설정 이동 이동시 OFF됨
|
||||||
|
- ChargeSt[1] : 0=off, 1=on
|
||||||
|
- CartSt[1] : 0=off, 1=on, 2=unknown
|
||||||
|
- LiftSt[1] : 0=down , 1=up, 2=unknown
|
||||||
|
- LastTag[4] : "0000"
|
||||||
|
- CurrentPath[1] : Path ID , 0=미설정, 1~255(순차증가)
|
||||||
|
|
||||||
|
4. **Door**
|
||||||
|
- H -> E | cmd(1): 출입문 열기
|
||||||
|
- H -> E | cmd(2): 출입문 닫기
|
||||||
|
- E -> H | cmd(3): 출입문 상태 (data len=1 : 0=닫힘, 1=열림, 2=바쁨, 3=알수없음, 255=오류)
|
||||||
|
|
||||||
|
### CRC16 계산
|
||||||
|
- CRC16 다항식 사용
|
||||||
|
- 초기값: 0xFFFF
|
||||||
|
- 데이터 무결성 검증에 사용
|
||||||
|
|
||||||
|
|
||||||
|
#### CRC-16 테이블 값
|
||||||
|
```
|
||||||
|
0x0000, 0x408E, 0x73EF, 0x3361, 0x152D, 0x55A3, 0x66C2, 0x264C,
|
||||||
|
0x2A5A, 0x6AD4, 0x59B5, 0x193B, 0x3F77, 0x7FF9, 0x4C98, 0x0C16,
|
||||||
|
0x54B4, 0x143A, 0x275B, 0x67D5, 0x4199, 0x0117, 0x3276, 0x72F8,
|
||||||
|
0x7EEE, 0x3E60, 0x0D01, 0x4D8F, 0x6BC3, 0x2B4D, 0x182C, 0x58A2,
|
||||||
|
0x5B9B, 0x1B15, 0x2874, 0x68FA, 0x4EB6, 0x0E38, 0x3D59, 0x7DD7,
|
||||||
|
0x71C1, 0x314F, 0x022E, 0x42A0, 0x64EC, 0x2462, 0x1703, 0x578D,
|
||||||
|
0x0F2F, 0x4FA1, 0x7CC0, 0x3C4E, 0x1A02, 0x5A8C, 0x69ED, 0x2963,
|
||||||
|
0x2575, 0x65FB, 0x569A, 0x1614, 0x3058, 0x70D6, 0x43B7, 0x0339,
|
||||||
|
0x45C5, 0x054B, 0x362A, 0x76A4, 0x50E8, 0x1066, 0x2307, 0x6389,
|
||||||
|
0x6F9F, 0x2F11, 0x1C70, 0x5CFE, 0x7AB2, 0x3A3C, 0x095D, 0x49D3,
|
||||||
|
0x1171, 0x51FF, 0x629E, 0x2210, 0x045C, 0x44D2, 0x77B3, 0x373D,
|
||||||
|
0x3B2B, 0x7BA5, 0x48C4, 0x084A, 0x2E06, 0x6E88, 0x5DE9, 0x1D67,
|
||||||
|
0x1E5E, 0x5ED0, 0x6DB1, 0x2D3F, 0x0B73, 0x4BFD, 0x789C, 0x3812,
|
||||||
|
0x3404, 0x748A, 0x47EB, 0x0765, 0x2129, 0x61A7, 0x52C6, 0x1248,
|
||||||
|
0x4AEA, 0x0A64, 0x3905, 0x798B, 0x5FC7, 0x1F49, 0x2C28, 0x6CA6,
|
||||||
|
0x60B0, 0x203E, 0x135F, 0x53D1, 0x759D, 0x3513, 0x0672, 0x46FC,
|
||||||
|
0x7979, 0x39F7, 0x0A96, 0x4A18, 0x6C54, 0x2CDA, 0x1FBB, 0x5F35,
|
||||||
|
0x5323, 0x13AD, 0x20CC, 0x6042, 0x460E, 0x0680, 0x35E1, 0x756F,
|
||||||
|
0x2DCD, 0x6D43, 0x5E22, 0x1EAC, 0x38E0, 0x786E, 0x4B0F, 0x0B81,
|
||||||
|
0x0797, 0x4719, 0x7478, 0x34F6, 0x12BA, 0x5234, 0x6155, 0x21DB,
|
||||||
|
0x22E2, 0x626C, 0x510D, 0x1183, 0x37CF, 0x7741, 0x4420, 0x04AE,
|
||||||
|
0x08B8, 0x4836, 0x7B57, 0x3BD9, 0x1D95, 0x5D1B, 0x6E7A, 0x2EF4,
|
||||||
|
0x7656, 0x36D8, 0x05B9, 0x4537, 0x637B, 0x23F5, 0x1094, 0x501A,
|
||||||
|
0x5C0C, 0x1C82, 0x2FE3, 0x6F6D, 0x4921, 0x09AF, 0x3ACE, 0x7A40,
|
||||||
|
0x3CBC, 0x7C32, 0x4F53, 0x0FDD, 0x2991, 0x691F, 0x5A7E, 0x1AF0,
|
||||||
|
0x16E6, 0x5668, 0x6509, 0x2587, 0x03CB, 0x4345, 0x7024, 0x30AA,
|
||||||
|
0x6808, 0x2886, 0x1BE7, 0x5B69, 0x7D25, 0x3DAB, 0x0ECA, 0x4E44,
|
||||||
|
0x4252, 0x02DC, 0x31BD, 0x7133, 0x577F, 0x17F1, 0x2490, 0x641E,
|
||||||
|
0x6727, 0x27A9, 0x14C8, 0x5446, 0x720A, 0x3284, 0x01E5, 0x416B,
|
||||||
|
0x4D7D, 0x0DF3, 0x3E92, 0x7E1C, 0x5850, 0x18DE, 0x2BBF, 0x6B31,
|
||||||
|
0x3393, 0x731D, 0x407C, 0x00F2, 0x26BE, 0x6630, 0x5551, 0x15DF,
|
||||||
|
0x19C9, 0x5947, 0x6A26, 0x2AA8, 0x0CE4, 0x4C6A, 0x7F0B, 0x3F85,
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### CRC16 계산 테이블 생성 코드
|
||||||
|
```csharp
|
||||||
|
const ushort polynomial = 0x7979;
|
||||||
|
ushort[] CRC16_TABLE = new ushort[256];
|
||||||
|
|
||||||
|
for (ushort i = 0; i < CRC16_TABLE.Length; i++)
|
||||||
|
{
|
||||||
|
ushort value = 0;
|
||||||
|
ushort temp = i;
|
||||||
|
for (byte j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
if (((value ^ temp) & 0x0001) != 0)
|
||||||
|
{
|
||||||
|
value = (ushort)((value >> 1) ^ polynomial);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value >>= 1;
|
||||||
|
}
|
||||||
|
temp >>= 1;
|
||||||
|
}
|
||||||
|
CRC16_TABLE[i] = value;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### CRC16 계산 예시
|
||||||
|
```csharp
|
||||||
|
ushort CalculateCRC16(byte[] data)
|
||||||
|
{
|
||||||
|
ushort crc = 0xFFFF;
|
||||||
|
for (int i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
byte index = (byte)(crc ^ data[i]);
|
||||||
|
crc = (ushort)((crc >> 8) ^ CRC16_TABLE[index]);
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 라이센스
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
8
AGVLogic/EnigProtocol/enigprotocol/.gitignore
vendored
Normal file
8
AGVLogic/EnigProtocol/enigprotocol/.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
################################################################################
|
||||||
|
# 이 .gitignore 파일은 Microsoft(R) Visual Studio에서 자동으로 만들어졌습니다.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
/obj
|
||||||
|
/bin
|
||||||
|
/.vs
|
||||||
|
/.git
|
||||||
125
AGVLogic/EnigProtocol/enigprotocol/Commands.cs
Normal file
125
AGVLogic/EnigProtocol/enigprotocol/Commands.cs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace ENIGProtocol
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// host -> eq
|
||||||
|
/// </summary>
|
||||||
|
public enum AGVCommandHE : byte
|
||||||
|
{
|
||||||
|
Goto = 100,
|
||||||
|
Stop = 101,
|
||||||
|
Reset = 102,
|
||||||
|
SetCurrent = 103,
|
||||||
|
Manual = 104,
|
||||||
|
MarkStop = 105,
|
||||||
|
LiftControl = 106,
|
||||||
|
GotoAlias = 107,
|
||||||
|
AutoMove = 108,
|
||||||
|
ChargeControl = 109,
|
||||||
|
Charger = 112,
|
||||||
|
LTurn = 113,
|
||||||
|
RTurn = 114,
|
||||||
|
LTurn180 = 115,
|
||||||
|
RTurn180 = 116,
|
||||||
|
PickOnEnter = 117,
|
||||||
|
PickOffEnter = 118,
|
||||||
|
PickOnExit = 119,
|
||||||
|
PickOffExit = 120,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// eq -> host
|
||||||
|
/// </summary>
|
||||||
|
public enum AGVCommandEH : byte
|
||||||
|
{
|
||||||
|
Error = 1,
|
||||||
|
Arrived = 2,
|
||||||
|
ReadRFID = 3,
|
||||||
|
ActionComplete=4,
|
||||||
|
Status = 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AGVErrorCode : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
PredictFix,
|
||||||
|
TurnTimeout,
|
||||||
|
TurnError,
|
||||||
|
EmptyNode,
|
||||||
|
Goto,
|
||||||
|
ManualMode,
|
||||||
|
UnknownCommand,
|
||||||
|
UnknownAlias,
|
||||||
|
|
||||||
|
// Operational Errors
|
||||||
|
CART_EXIST,
|
||||||
|
MARK_TIMEOUT,
|
||||||
|
MARK_SENSOR_FAIL,
|
||||||
|
LIFT_ERROR,
|
||||||
|
AGV_SPEED_SET_FAIL,
|
||||||
|
AGV_RUN_FAIL,
|
||||||
|
AGV_STOP_FAIL,
|
||||||
|
PATH_INTEGRITY_FAIL,
|
||||||
|
TURN_FAIL,
|
||||||
|
NO_CHARGEPOINT,
|
||||||
|
NOTSET_CHARGEPOINT,
|
||||||
|
ALREADY_CHARGE,
|
||||||
|
CHARGE_RETRY_OVER,
|
||||||
|
MAGNET_ON_ERROR,
|
||||||
|
MAGNET_OF_ERROR,
|
||||||
|
BUFFER_NOT_COMPLETE,
|
||||||
|
MARK_STOP_FAIL,
|
||||||
|
LIDAR_STOP,
|
||||||
|
NOT_BUFFERPOINT,
|
||||||
|
NOT_EQUIPMENTPOINT,
|
||||||
|
PATH_COMPLETE_INTEGRITY_FAIL,
|
||||||
|
UPDATEMOTION_TIMEOUT
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AGVUtility
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 에러코드에 해당하는 오류메세지를 반환 합니다
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ecode"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetAGVErrorMessage(AGVErrorCode ecode)
|
||||||
|
{
|
||||||
|
switch (ecode)
|
||||||
|
{
|
||||||
|
case AGVErrorCode.None: return "No Error";
|
||||||
|
case AGVErrorCode.PredictFix: return "이동 예측이 동작하지 않습니다";
|
||||||
|
case AGVErrorCode.TurnTimeout: return "회전작업 시간초과";
|
||||||
|
case AGVErrorCode.TurnError: return "회전작업이 완료되지 않았습니다";
|
||||||
|
case AGVErrorCode.EmptyNode: return "노드정보를 찾을 수 없습니다";
|
||||||
|
case AGVErrorCode.Goto: return "이동 명령 오류";
|
||||||
|
case AGVErrorCode.ManualMode: return "자동운전 상태가 아닙니다";
|
||||||
|
case AGVErrorCode.UnknownCommand: return "알수 없는 명령입니다";
|
||||||
|
case AGVErrorCode.UnknownAlias: return "알수 없는 별칭 입니다";
|
||||||
|
|
||||||
|
case AGVErrorCode.CART_EXIST: return "카트 감지 센서 오류";
|
||||||
|
case AGVErrorCode.MARK_TIMEOUT: return "마크 정지 신호 시간초과";
|
||||||
|
case AGVErrorCode.MARK_SENSOR_FAIL: return "마크 센서 미감지";
|
||||||
|
case AGVErrorCode.LIFT_ERROR: return "리프트 동작 오류";
|
||||||
|
case AGVErrorCode.AGV_SPEED_SET_FAIL: return "AGV 속도 설정 실패";
|
||||||
|
case AGVErrorCode.AGV_RUN_FAIL: return "AGV 구동 실패";
|
||||||
|
case AGVErrorCode.AGV_STOP_FAIL: return "AGV 정지 실패";
|
||||||
|
case AGVErrorCode.PATH_INTEGRITY_FAIL: return "경로 무결성 검증 실패";
|
||||||
|
case AGVErrorCode.TURN_FAIL: return "턴 동작 실패";
|
||||||
|
case AGVErrorCode.NO_CHARGEPOINT: return "충전 위치 아님";
|
||||||
|
case AGVErrorCode.NOTSET_CHARGEPOINT: return "충전기 노드 미설정";
|
||||||
|
case AGVErrorCode.ALREADY_CHARGE: return "이미 충전 중 상태임";
|
||||||
|
case AGVErrorCode.CHARGE_RETRY_OVER: return $"충전명령 재전송 횟수 초과";
|
||||||
|
case AGVErrorCode.NOT_BUFFERPOINT: return "현재 위치가 버퍼가 아닙니다";
|
||||||
|
case AGVErrorCode.NOT_EQUIPMENTPOINT: return "현재 위치가 장비 노드가 아닙니다";
|
||||||
|
case AGVErrorCode.PATH_COMPLETE_INTEGRITY_FAIL: return "목적지 도착 완료 했으나 노드 혹은 방향이 일치지하지 않습니다";
|
||||||
|
default: return ecode.ToString();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
235
AGVLogic/EnigProtocol/enigprotocol/EEProtocol.cs
Normal file
235
AGVLogic/EnigProtocol/enigprotocol/EEProtocol.cs
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace ENIG
|
||||||
|
{
|
||||||
|
// 장비 타입 정의
|
||||||
|
public enum DeviceType
|
||||||
|
{
|
||||||
|
ACS = 0,
|
||||||
|
AGV1 = 10,
|
||||||
|
AGV2 = 11,
|
||||||
|
BUFFER1 = 20,
|
||||||
|
BUFFER2 = 21,
|
||||||
|
BUFFER3 = 22,
|
||||||
|
BUFFER4 = 23,
|
||||||
|
BUFFER5 = 24,
|
||||||
|
DOOR = 30,
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class EEProtocol
|
||||||
|
{
|
||||||
|
// 패킷 수신 이벤트 정의
|
||||||
|
// 데이터 수신 이벤트 정의
|
||||||
|
public event EventHandler<DataEventArgs> OnDataReceived;
|
||||||
|
public event EventHandler<MessageEventArgs> OnMessage;
|
||||||
|
|
||||||
|
// CRC16 계산을 위한 테이블
|
||||||
|
private static readonly ushort[] CRC16_TABLE = new ushort[256];
|
||||||
|
|
||||||
|
// CRC16 테이블 초기화
|
||||||
|
public EEProtocol()
|
||||||
|
{
|
||||||
|
const ushort polynomial = 0x7979;
|
||||||
|
for (ushort i = 0; i < CRC16_TABLE.Length; i++)
|
||||||
|
{
|
||||||
|
ushort value = 0;
|
||||||
|
ushort temp = i;
|
||||||
|
for (byte j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
if (((value ^ temp) & 0x0001) != 0)
|
||||||
|
{
|
||||||
|
value = (ushort)((value >> 1) ^ polynomial);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value >>= 1;
|
||||||
|
}
|
||||||
|
temp >>= 1;
|
||||||
|
}
|
||||||
|
CRC16_TABLE[i] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//// CRC 테이블 출력
|
||||||
|
//Console.WriteLine("CRC16 테이블 값:");
|
||||||
|
//for (int i = 0; i < CRC16_TABLE.Length; i++)
|
||||||
|
//{
|
||||||
|
// if (i % 8 == 0)
|
||||||
|
// {
|
||||||
|
// Console.WriteLine();
|
||||||
|
// }
|
||||||
|
// Console.Write($"0x{CRC16_TABLE[i]:X4}, ");
|
||||||
|
//}
|
||||||
|
//Console.WriteLine();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRC16 계산 메서드
|
||||||
|
public ushort CalculateCRC16(byte[] data)
|
||||||
|
{
|
||||||
|
ushort crc = 0xFFFF;
|
||||||
|
for (int i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
byte index = (byte)(crc ^ data[i]);
|
||||||
|
crc = (ushort)((crc >> 8) ^ CRC16_TABLE[index]);
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 패킷 생성 메서드
|
||||||
|
public byte[] CreatePacket(byte id, byte command, byte[] data)
|
||||||
|
{
|
||||||
|
var packet = new Packet
|
||||||
|
{
|
||||||
|
ID = id,
|
||||||
|
Command = command,
|
||||||
|
Data = data ?? new byte[0],
|
||||||
|
Length = (byte)(1 + 1 + (data?.Length ?? 0)) // ID + Command + Data 길이
|
||||||
|
};
|
||||||
|
|
||||||
|
// 패킷 조립
|
||||||
|
List<byte> packetData = new List<byte>();
|
||||||
|
packetData.Add(Packet.STX);
|
||||||
|
packetData.Add(packet.Length);
|
||||||
|
packetData.Add(packet.ID);
|
||||||
|
packetData.Add(packet.Command);
|
||||||
|
if (packet.Data != null)
|
||||||
|
packetData.AddRange(packet.Data);
|
||||||
|
|
||||||
|
// CRC16 계산
|
||||||
|
packet.CRC16 = CalculateCRC16(packetData.Skip(1).ToArray()); // STX 제외하고 계산
|
||||||
|
packetData.AddRange(BitConverter.GetBytes(packet.CRC16));
|
||||||
|
packetData.Add(Packet.ETX);
|
||||||
|
|
||||||
|
return packetData.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
//패킷테스트
|
||||||
|
public void PacketTest(byte[] rawData)
|
||||||
|
{
|
||||||
|
var hexstr = string.Join(" ", rawData.Select(t => t.ToString("X2")));
|
||||||
|
RaiseMessage( $"TestPacket : {hexstr}");
|
||||||
|
ParsePacket(rawData);
|
||||||
|
}
|
||||||
|
|
||||||
|
//메세지 발생
|
||||||
|
public void RaiseMessage(string message, bool isError = false)
|
||||||
|
{
|
||||||
|
OnMessage?.Invoke(this, new MessageEventArgs { IsError = isError, Message = message });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 패킷 파싱 메서드
|
||||||
|
public bool ParsePacket(byte[] rawData)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (rawData.Length < 7) // 최소 패킷 크기
|
||||||
|
{
|
||||||
|
var hexstring = string.Join(" ", rawData.Select(t => t.ToString("X2")));
|
||||||
|
RaiseMessage($"Too Short Data:{hexstring}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (rawData[0] != Packet.STX || rawData[rawData.Length - 1] != Packet.ETX)
|
||||||
|
{
|
||||||
|
var hexstring = string.Join(" ", rawData.Select(t => t.ToString("X2")));
|
||||||
|
RaiseMessage($"STX/ETX Error Data:{hexstring}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
byte length = rawData[1];
|
||||||
|
if (length + 5 != rawData.Length) // STX + Length + CRC16(2) + ETX = 5
|
||||||
|
{
|
||||||
|
var hexstring = string.Join(" ", rawData.Select(t => t.ToString("X2")));
|
||||||
|
RaiseMessage($"Length Error ({length+5} != {rawData.Length}) Data:{hexstring}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// CRC16 검증
|
||||||
|
byte[] dataForCrc = rawData.Skip(1).Take(length + 1).ToArray();
|
||||||
|
ushort calculatedCrc = CalculateCRC16(dataForCrc);
|
||||||
|
ushort receivedCrc = BitConverter.ToUInt16(rawData, rawData.Length - 3);
|
||||||
|
|
||||||
|
if (receivedCrc != 0xFFFF && calculatedCrc != receivedCrc) //FF 무시
|
||||||
|
{
|
||||||
|
RaiseMessage($"CRC Error ID:{rawData[2]:X2},CMD:{rawData[3]:X2}", true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 패킷 생성
|
||||||
|
var packet = new Packet
|
||||||
|
{
|
||||||
|
Length = length,
|
||||||
|
ID = rawData[2],
|
||||||
|
Command = rawData[3],
|
||||||
|
Data = rawData.Skip(4).Take(length - 2).ToArray(), // ID와 Command 길이(2) 제외
|
||||||
|
CRC16 = receivedCrc,
|
||||||
|
RawData = rawData,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 이벤트 발생
|
||||||
|
OnDataReceived?.Invoke(this, new DataEventArgs { ReceivedPacket = packet });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
RaiseMessage(ex.Message, true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 데이터 수신 처리 메서드 (시리얼 포트에서 데이터를 받았을 때 호출)
|
||||||
|
private List<byte> buffer = new List<byte>();
|
||||||
|
private int ProtocolParseError = 0;
|
||||||
|
public void ProcessReceivedData(byte[] data)
|
||||||
|
{
|
||||||
|
buffer.AddRange(data);
|
||||||
|
|
||||||
|
while (buffer.Count > 0)
|
||||||
|
{
|
||||||
|
// STX 찾기
|
||||||
|
int stxIndex = buffer.FindIndex(b => b == Packet.STX);
|
||||||
|
if (stxIndex == -1)
|
||||||
|
{
|
||||||
|
buffer.Clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 불필요한 데이터 제거
|
||||||
|
if (stxIndex > 0)
|
||||||
|
buffer.RemoveRange(0, stxIndex);
|
||||||
|
|
||||||
|
// 패킷 길이 확인을 위한 최소 데이터 확인
|
||||||
|
if (buffer.Count < 2)
|
||||||
|
break;
|
||||||
|
|
||||||
|
int expectedLength = buffer[1] + 5; // 전체 패킷 길이
|
||||||
|
if (buffer.Count < expectedLength)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 패킷 추출 및 처리
|
||||||
|
byte[] packetData = buffer.Take(expectedLength).ToArray();
|
||||||
|
buffer.RemoveRange(0, expectedLength);
|
||||||
|
|
||||||
|
var parseOK = ParsePacket(packetData);
|
||||||
|
if(parseOK==false) //분석이 실패되었다면 해당 데이터는 삭제한다.
|
||||||
|
{
|
||||||
|
ProtocolParseError += 1;
|
||||||
|
if (ProtocolParseError > 3) buffer.Clear();
|
||||||
|
} else ProtocolParseError = 0;
|
||||||
|
|
||||||
|
if(buffer.Any())
|
||||||
|
{
|
||||||
|
System.Threading.Thread.Sleep(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
57
AGVLogic/EnigProtocol/enigprotocol/ENIGProtocol.csproj
Normal file
57
AGVLogic/EnigProtocol/enigprotocol/ENIGProtocol.csproj
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{9365803B-933D-4237-93C7-B502C855A71C}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>enigprotocol</RootNamespace>
|
||||||
|
<AssemblyName>enigprotocol</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Commands.cs" />
|
||||||
|
<Compile Include="EEProtocol.cs" />
|
||||||
|
<Compile Include="EventArgs.cs" />
|
||||||
|
<Compile Include="Packet.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include=".gitignore" />
|
||||||
|
<None Include="ReadMe.MD" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
||||||
18
AGVLogic/EnigProtocol/enigprotocol/EventArgs.cs
Normal file
18
AGVLogic/EnigProtocol/enigprotocol/EventArgs.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ENIG
|
||||||
|
{
|
||||||
|
|
||||||
|
public partial class EEProtocol
|
||||||
|
{
|
||||||
|
public class MessageEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public string Message { get; set; }
|
||||||
|
public bool IsError { get; set; }
|
||||||
|
}
|
||||||
|
public class DataEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public Packet ReceivedPacket { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
AGVLogic/EnigProtocol/enigprotocol/Packet.cs
Normal file
20
AGVLogic/EnigProtocol/enigprotocol/Packet.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
namespace ENIG
|
||||||
|
{
|
||||||
|
// 패킷 구조체
|
||||||
|
public class Packet
|
||||||
|
{
|
||||||
|
public const byte STX = 0x02;
|
||||||
|
public const byte ETX = 0x03;
|
||||||
|
public byte Length { get; set; }
|
||||||
|
public byte ID { get; set; }
|
||||||
|
public byte Command { get; set; }
|
||||||
|
public byte[] Data { get; set; }
|
||||||
|
public ushort CRC16 { get; set; }
|
||||||
|
|
||||||
|
public byte[] RawData { get; set; }
|
||||||
|
public Packet()
|
||||||
|
{
|
||||||
|
Data = new byte[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,11 +5,11 @@ using System.Runtime.InteropServices;
|
|||||||
// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
|
// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
|
||||||
// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
|
// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
|
||||||
// 이러한 특성 값을 변경하세요.
|
// 이러한 특성 값을 변경하세요.
|
||||||
[assembly: AssemblyTitle("ClassLibrary1")]
|
[assembly: AssemblyTitle("enigprotocol")]
|
||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("")]
|
[assembly: AssemblyCompany("")]
|
||||||
[assembly: AssemblyProduct("ClassLibrary1")]
|
[assembly: AssemblyProduct("enigprotocol")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2025")]
|
[assembly: AssemblyCopyright("Copyright © 2025")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
@@ -20,7 +20,7 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
|
// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
|
||||||
[assembly: Guid("19675e19-eb91-493e-88c3-32b3c094b749")]
|
[assembly: Guid("9365803b-933d-4237-93c7-b502c855a71c")]
|
||||||
|
|
||||||
// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
|
// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
|
||||||
//
|
//
|
||||||
236
AGVLogic/EnigProtocol/enigprotocol/ReadMe.MD
Normal file
236
AGVLogic/EnigProtocol/enigprotocol/ReadMe.MD
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
# ENIG Protocol
|
||||||
|
|
||||||
|
ENIG 프로토콜 통신 라이브러리
|
||||||
|
|
||||||
|
**Explanation of the README Content:**
|
||||||
|
|
||||||
|
1. **Title and Overview:** Clearly states the purpose of the library.
|
||||||
|
2. **Key Components:** Explains the main building blocks:
|
||||||
|
* `DeviceType` enum: Lists the device types.
|
||||||
|
* `Packet` class: Describes the packet structure.
|
||||||
|
* `EEProtocol` class: Explains its role in packet handling.
|
||||||
|
3. **Key Features:** Details the core functionalities of the `EEProtocol` class:
|
||||||
|
* Packet creation.
|
||||||
|
* Packet parsing.
|
||||||
|
* Data reception.
|
||||||
|
* Event-driven nature.
|
||||||
|
* CRC16 calculation.
|
||||||
|
4. **Usage:** Provides code examples for:
|
||||||
|
* Creating a packet.
|
||||||
|
* Parsing a packet.
|
||||||
|
* Processing received data.
|
||||||
|
5. **Error Handling:** Explains how the library handles invalid data and incomplete packets.
|
||||||
|
6. **Dependencies:** Lists any required frameworks or libraries.
|
||||||
|
7. **License:** Placeholder for the project's license.
|
||||||
|
|
||||||
|
This README provides a comprehensive overview of the `ENIGProtocol` library, making it easy for other developers to understand and use it. I hope this is helpful!
|
||||||
|
|
||||||
|
|
||||||
|
## 개발 환경
|
||||||
|
- Frameworks : .NET Standard 2.0
|
||||||
|
- Visual Studio 2022 또는 Visual Studio Code
|
||||||
|
|
||||||
|
## 프로젝트 구조
|
||||||
|
|
||||||
|
- `EEProtocol.cs`: ENIG 프로토콜 구현
|
||||||
|
- `EEProtocolTests.cs`: 단위 테스트 코드
|
||||||
|
- `Sample`: C# Winform 샘플 프로젝트
|
||||||
|
|
||||||
|
### Xbee Setting value
|
||||||
|
|
||||||
|
- **NARMI 70 : AGV No 70
|
||||||
|
- **NARMI 70 (LIFT) : P46A6,C17,DH:0,DL:FFFF,MY40
|
||||||
|
- **NARMI 70 (AGV) : P46A6,C17,DH:0,DL:FFFF,MY41
|
||||||
|
|
||||||
|
- **NARMI 77 : AGV No 71
|
||||||
|
- **NARMI 71 (LIFT) : P46A6,C17,DH:0,DL:FFFF,MY30
|
||||||
|
- **NARMI 71 (AGV) : P46A6,C17,DH:0,DL:FFFF,MY31
|
||||||
|
|
||||||
|
- **충전기04 : P46A6,C17,DH:0,DL:FFFF,MY41
|
||||||
|
- **충전기71 : P46A6,C17,DH:0,DL:FFFF,MY46
|
||||||
|
|
||||||
|
- **ACS : P46A5,C17,DH:0,DL:FFFF,MY10
|
||||||
|
|
||||||
|
- **BUFFER : P46A5,C17,DH:0,DL:FFFF,MY60~65
|
||||||
|
|
||||||
|
- **AGV1 : P46A5,C17,DH:0,DL:FFFF,MY50
|
||||||
|
- **AGV2 : P46A5,C17,DH:0,DL:FFFF,MY51
|
||||||
|
|
||||||
|
- **DOOR : P46A5,C17,DH:0,DL:FFFF,MY30
|
||||||
|
|
||||||
|
|
||||||
|
## 장비 목록
|
||||||
|
```
|
||||||
|
public enum DeviceType : byte
|
||||||
|
{
|
||||||
|
ACS = 0,
|
||||||
|
AGV1 = 10+1,
|
||||||
|
AGV2 = 10+2,
|
||||||
|
BUFFER1 = 20+1,
|
||||||
|
BUFFER2 = 20+2,
|
||||||
|
BUFFER3 = 20+3,
|
||||||
|
BUFFER4 = 20+4,
|
||||||
|
BUFFER5 = 20+5,
|
||||||
|
DOOR = 30,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### 기본 패킷 구조
|
||||||
|
```
|
||||||
|
[STX][LEN][ID][CMD][DATA][CRC16][ETX]
|
||||||
|
```
|
||||||
|
- **STX (Start of Text)**: 0x02
|
||||||
|
- **LEN (Length)**: 데이터 길이 (1바이트) = {ID+CMD+DATA}
|
||||||
|
- **ID (Client ID)**: 데이터 길이 (1바이트) : 디바이스식별코드(=DeviceType)
|
||||||
|
- **CMD (Command)**: 명령어 코드 (1바이트)
|
||||||
|
- **DATA**: 명령어에 따른 데이터 (가변 길이)
|
||||||
|
- **CRC16**: 데이터 무결성 검사 (2바이트)
|
||||||
|
- **ETX (End of Text)**: 0x03
|
||||||
|
|
||||||
|
### 통신 방향 (호스트=ACS, 장비=agv,buffer,door)
|
||||||
|
- H -> E: 호스트에서 장비로 전송
|
||||||
|
- E -> H: 장비에서 호스트로 전송
|
||||||
|
|
||||||
|
### 명령어 목록
|
||||||
|
1. **ACS (AGV Control System)**
|
||||||
|
|
||||||
|
|
||||||
|
2. **Buffer**
|
||||||
|
- E -> H | cmd('S'): 상태 (............) BIT & 1BYTE 0:CART1, 1:CART2, 2:BASKET1, 3:BASKET2, 4:OPEN, 5:CLOSE
|
||||||
|
- H -> E | cmd('L'): Lock
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
- H -> E | cmd('U'): UnLock
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
|
||||||
|
3. **AGV**
|
||||||
|
- H -> E | Move : cmd(100) : 대상태그까지 이동(자동이동)
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
- TagID[4] = "0000"
|
||||||
|
- H -> E | Move : cmd(107) : 대상별칭까지 이동(자동이동)
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
- AliasName[n] = "....."
|
||||||
|
- H -> E | Stop : cmd(101) : 멈춤
|
||||||
|
- H -> E | Reset : cmd(102) : 오류 소거
|
||||||
|
- H -> E | Charge On: cmd(103) : 충전실행(충전기 이동 후 자동 충전 진행)
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
- Action[1] : 0=Charge Off, 1=Charge On
|
||||||
|
- H -> E | MoveManual : cmd(104) : 메뉴얼이동
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
- Direction[1] : 0=Backward, 1=Forward, 2=TurnLeft, 3=TurnRight
|
||||||
|
- Speed[1] : 0=Slow, 1=Normal, 2=Fast
|
||||||
|
- Runtime[1] : 0 second
|
||||||
|
- H -> E | MarkStop : cmd(105) : 마크센서스톱
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
|
||||||
|
- H -> E | Lift Control : cmd(106) : 리프트제어
|
||||||
|
- Target[1] = {DeviceType}
|
||||||
|
- Action[1] : 0=STOP, 1=UP, 2=DOWN
|
||||||
|
|
||||||
|
- E -> H | Move Complete : cmd(1) : 목적지이동완료 후 전송
|
||||||
|
- TagID[4] : "0000"
|
||||||
|
- E -> H | TagID Received : cmd(2) : 태그값 인식시 전송
|
||||||
|
- TagID[4] : "0000"
|
||||||
|
|
||||||
|
- E -> H | Status : cmd(3) - 총 12바이트
|
||||||
|
- Mode[1] : 0=manual, 1=auto
|
||||||
|
- RunSt[1] : 0=stop, 1=run, 2=error
|
||||||
|
MotDirection[1] : 0:Forward, 1:Backward, 0xFF:unknown
|
||||||
|
- MagDiection[1] : 0=straight, 1=left, 2=right , 0xFF:unknown
|
||||||
|
- Inposition[1] : 0=off, 1=on : 목적위치에 도달완료 시 설정 이동 이동시 OFF됨
|
||||||
|
- ChargeSt[1] : 0=off, 1=on
|
||||||
|
- CartSt[1] : 0=off, 1=on, 2=unknown
|
||||||
|
- LiftSt[1] : 0=down , 1=up, 2=unknown
|
||||||
|
- LastTag[4] : "0000" (ASCII 4바이트)
|
||||||
|
|
||||||
|
4. **Door**
|
||||||
|
- H -> E | cmd(1): 출입문 열기
|
||||||
|
- H -> E | cmd(2): 출입문 닫기
|
||||||
|
- E -> H | cmd(3): 출입문 상태 (data len=1 : 0=닫힘, 1=열림, 2=바쁨, 3=알수없음, 255=오류)
|
||||||
|
|
||||||
|
### CRC16 계산
|
||||||
|
- CRC16 다항식 사용
|
||||||
|
- 초기값: 0xFFFF
|
||||||
|
- 데이터 무결성 검증에 사용
|
||||||
|
|
||||||
|
|
||||||
|
#### CRC-16 테이블 값
|
||||||
|
```
|
||||||
|
0x0000, 0x408E, 0x73EF, 0x3361, 0x152D, 0x55A3, 0x66C2, 0x264C,
|
||||||
|
0x2A5A, 0x6AD4, 0x59B5, 0x193B, 0x3F77, 0x7FF9, 0x4C98, 0x0C16,
|
||||||
|
0x54B4, 0x143A, 0x275B, 0x67D5, 0x4199, 0x0117, 0x3276, 0x72F8,
|
||||||
|
0x7EEE, 0x3E60, 0x0D01, 0x4D8F, 0x6BC3, 0x2B4D, 0x182C, 0x58A2,
|
||||||
|
0x5B9B, 0x1B15, 0x2874, 0x68FA, 0x4EB6, 0x0E38, 0x3D59, 0x7DD7,
|
||||||
|
0x71C1, 0x314F, 0x022E, 0x42A0, 0x64EC, 0x2462, 0x1703, 0x578D,
|
||||||
|
0x0F2F, 0x4FA1, 0x7CC0, 0x3C4E, 0x1A02, 0x5A8C, 0x69ED, 0x2963,
|
||||||
|
0x2575, 0x65FB, 0x569A, 0x1614, 0x3058, 0x70D6, 0x43B7, 0x0339,
|
||||||
|
0x45C5, 0x054B, 0x362A, 0x76A4, 0x50E8, 0x1066, 0x2307, 0x6389,
|
||||||
|
0x6F9F, 0x2F11, 0x1C70, 0x5CFE, 0x7AB2, 0x3A3C, 0x095D, 0x49D3,
|
||||||
|
0x1171, 0x51FF, 0x629E, 0x2210, 0x045C, 0x44D2, 0x77B3, 0x373D,
|
||||||
|
0x3B2B, 0x7BA5, 0x48C4, 0x084A, 0x2E06, 0x6E88, 0x5DE9, 0x1D67,
|
||||||
|
0x1E5E, 0x5ED0, 0x6DB1, 0x2D3F, 0x0B73, 0x4BFD, 0x789C, 0x3812,
|
||||||
|
0x3404, 0x748A, 0x47EB, 0x0765, 0x2129, 0x61A7, 0x52C6, 0x1248,
|
||||||
|
0x4AEA, 0x0A64, 0x3905, 0x798B, 0x5FC7, 0x1F49, 0x2C28, 0x6CA6,
|
||||||
|
0x60B0, 0x203E, 0x135F, 0x53D1, 0x759D, 0x3513, 0x0672, 0x46FC,
|
||||||
|
0x7979, 0x39F7, 0x0A96, 0x4A18, 0x6C54, 0x2CDA, 0x1FBB, 0x5F35,
|
||||||
|
0x5323, 0x13AD, 0x20CC, 0x6042, 0x460E, 0x0680, 0x35E1, 0x756F,
|
||||||
|
0x2DCD, 0x6D43, 0x5E22, 0x1EAC, 0x38E0, 0x786E, 0x4B0F, 0x0B81,
|
||||||
|
0x0797, 0x4719, 0x7478, 0x34F6, 0x12BA, 0x5234, 0x6155, 0x21DB,
|
||||||
|
0x22E2, 0x626C, 0x510D, 0x1183, 0x37CF, 0x7741, 0x4420, 0x04AE,
|
||||||
|
0x08B8, 0x4836, 0x7B57, 0x3BD9, 0x1D95, 0x5D1B, 0x6E7A, 0x2EF4,
|
||||||
|
0x7656, 0x36D8, 0x05B9, 0x4537, 0x637B, 0x23F5, 0x1094, 0x501A,
|
||||||
|
0x5C0C, 0x1C82, 0x2FE3, 0x6F6D, 0x4921, 0x09AF, 0x3ACE, 0x7A40,
|
||||||
|
0x3CBC, 0x7C32, 0x4F53, 0x0FDD, 0x2991, 0x691F, 0x5A7E, 0x1AF0,
|
||||||
|
0x16E6, 0x5668, 0x6509, 0x2587, 0x03CB, 0x4345, 0x7024, 0x30AA,
|
||||||
|
0x6808, 0x2886, 0x1BE7, 0x5B69, 0x7D25, 0x3DAB, 0x0ECA, 0x4E44,
|
||||||
|
0x4252, 0x02DC, 0x31BD, 0x7133, 0x577F, 0x17F1, 0x2490, 0x641E,
|
||||||
|
0x6727, 0x27A9, 0x14C8, 0x5446, 0x720A, 0x3284, 0x01E5, 0x416B,
|
||||||
|
0x4D7D, 0x0DF3, 0x3E92, 0x7E1C, 0x5850, 0x18DE, 0x2BBF, 0x6B31,
|
||||||
|
0x3393, 0x731D, 0x407C, 0x00F2, 0x26BE, 0x6630, 0x5551, 0x15DF,
|
||||||
|
0x19C9, 0x5947, 0x6A26, 0x2AA8, 0x0CE4, 0x4C6A, 0x7F0B, 0x3F85,
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### CRC16 계산 테이블 생성 코드
|
||||||
|
```csharp
|
||||||
|
const ushort polynomial = 0x7979;
|
||||||
|
ushort[] CRC16_TABLE = new ushort[256];
|
||||||
|
|
||||||
|
for (ushort i = 0; i < CRC16_TABLE.Length; i++)
|
||||||
|
{
|
||||||
|
ushort value = 0;
|
||||||
|
ushort temp = i;
|
||||||
|
for (byte j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
if (((value ^ temp) & 0x0001) != 0)
|
||||||
|
{
|
||||||
|
value = (ushort)((value >> 1) ^ polynomial);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value >>= 1;
|
||||||
|
}
|
||||||
|
temp >>= 1;
|
||||||
|
}
|
||||||
|
CRC16_TABLE[i] = value;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### CRC16 계산 예시
|
||||||
|
```csharp
|
||||||
|
ushort CalculateCRC16(byte[] data)
|
||||||
|
{
|
||||||
|
ushort crc = 0xFFFF;
|
||||||
|
for (int i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
byte index = (byte)(crc ^ data[i]);
|
||||||
|
crc = (ushort)((crc >> 8) ^ CRC16_TABLE[index]);
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 라이센스
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user