Compare commits

...

28 Commits

Author SHA1 Message Date
ChiKyun Kim
649d87cae3 .. 2025-12-23 17:25:47 +09:00
backuppc
b3f661969a .. 2025-12-23 13:25:33 +09:00
backuppc
4f162e50b5 .. 2025-12-23 13:12:13 +09:00
backuppc
35df73fd29 xbee 값 설정기능 추가 2025-12-23 13:07:01 +09:00
ChiKyun Kim
8499c1c5be .. 2025-12-23 11:41:56 +09:00
backuppc
3408e3fc30 ... 2025-12-22 16:02:57 +09:00
ChiKyun Kim
5a4c73e4df Merge branch 'master' of file://k4fs3201n/k4bpartcenter$/Repository/K4/ENIG_AGV 2025-12-22 09:52:53 +09:00
ChiKyun Kim
34d1bdf504 add layout 2025-12-22 09:52:21 +09:00
backuppc
3cae423736 .. 2025-12-19 16:25:30 +09:00
ChiKyun Kim
d777adc219 BMS 를 RS232 클래스에서 폴링방식 전용 클래스로 변경
BMS 정보중 현재 사용 전류와 와트를 표시함
사용전류를 통해서 충전여부를 자동 판다시키고, 해당 값은 Manual Charge 플래그에 설정함.
2025-12-18 14:44:00 +09:00
ChiKyun Kim
b62cd5f52e 충방전 전류 및 전력량 계산 코드 추가 2025-12-18 10:55:04 +09:00
backuppc
32217c8501 Merge branch 'master' of https://git.tindevil.com/Amkor/ENIG 2025-12-18 10:32:48 +09:00
backuppc
9274727fa9 .. 2025-12-18 10:32:20 +09:00
2a44ba28a8 "fix:Enforce-Run-Mode-in-fAuto-VisibleChanged" 2025-12-18 00:43:22 +09:00
4bdc36040d "feat:Implement-Canvas-Run-Mode-and-Logic" 2025-12-18 00:38:59 +09:00
384f2affcb "refactor:Switch-serial-comm-to-polling" 2025-12-18 00:34:37 +09:00
ChiKyun Kim
51579591a2 add docment -
enable : signal2
2025-12-17 16:36:12 +09:00
ChiKyun Kim
cef2fa8095 flag,error 항목 정리, 2025-12-17 15:53:18 +09:00
backuppc
1f37871336 .. 2025-12-17 14:54:33 +09:00
eb0e08d290 .. 2025-12-16 23:31:47 +09:00
backuppc
4153362588 .. 2025-12-16 17:29:28 +09:00
67a48531ad .. 2025-12-15 22:06:53 +09:00
backuppc
a7f938ff19 .. 2025-12-15 17:34:43 +09:00
backuppc
9db88e5d6b .agvmap 확장자제거하고 .json 으로 통일한 mapeditor 와 호환성 유지 2025-12-15 08:33:42 +09:00
a8cb952ea4 .. 2025-12-14 22:56:04 +09:00
3c8eae889c "refactor:Improve-map-loading-and-use-canvas-nodes" 2025-12-14 22:55:09 +09:00
764fbbd204 "feat:Enable-hover-highlight-and-refactor" 2025-12-14 17:20:50 +09:00
34b038c4be Implement ACS Command Handlers (PickOn, PickOff, Charge), Manual Mode Safety, and Map UI Commands 2025-12-13 02:40:55 +09:00
143 changed files with 62565 additions and 7544 deletions

View File

@@ -26,7 +26,6 @@ namespace AGVEmulator
// Map Control // Map Control
private UnifiedAGVCanvas _agvCanvas; private UnifiedAGVCanvas _agvCanvas;
private List<MapNode> _mapNodes;
private VirtualAGV _visualAgv; private VirtualAGV _visualAgv;
// Emulator State // Emulator State
@@ -385,14 +384,12 @@ namespace AGVEmulator
try try
{ {
var mapresult = MapLoader.LoadMapFromFile(mapPath); var mapresult = MapLoader.LoadMapFromFile(mapPath);
_mapNodes = mapresult.Nodes; _agvCanvas.SetMapLoadResult(mapresult);//.Nodes = _mapNodes;
_agvCanvas.Nodes = _mapNodes;
_agvCanvas.FitToNodes();
// Initialize Visual AGV // Initialize Visual AGV
if (_mapNodes.Count > 0) if (_agvCanvas.Nodes.Count > 0)
{ {
_visualAgv = new VirtualAGV("AGV01", _mapNodes[0].Position); _visualAgv = new VirtualAGV("AGV01", _agvCanvas.Nodes[0].Position);
_agvCanvas.AGVList = new List<IAGV> { _visualAgv }; _agvCanvas.AGVList = new List<IAGV> { _visualAgv };
} }
} }
@@ -405,16 +402,16 @@ namespace AGVEmulator
void UpdateVisualAgvPosition(string tag) void UpdateVisualAgvPosition(string tag)
{ {
if (_visualAgv == null || _mapNodes == null) return; if (_visualAgv == null || _agvCanvas.Nodes == null) return;
// Find node by tag // Find node by tag
// Assuming NodeId might be the tag or contain it // Assuming NodeId might be the tag or contain it
var node = _mapNodes.FirstOrDefault(n => n.NodeId == tag || n.NodeId.EndsWith(tag)); var node = _agvCanvas.Nodes.FirstOrDefault(n => n.Id == tag || n.Id.EndsWith(tag));
// If not found, try to parse tag as int and match // If not found, try to parse tag as int and match
if (node == null && int.TryParse(tag, out int tagNum)) if (node == null && int.TryParse(tag, out int tagNum))
{ {
node = _mapNodes.FirstOrDefault(n => n.NodeId == tagNum.ToString()); node = _agvCanvas.Nodes.FirstOrDefault(n => n.Id == tagNum.ToString());
} }
if (node != null) if (node != null)
@@ -575,7 +572,7 @@ private void UpdateVisualAGV()
private void UpdateEmulatorLogic() private void UpdateEmulatorLogic()
{ {
if (_visualAgv == null || _mapNodes == null) return; if (_visualAgv == null || _agvCanvas.Nodes == null) return;
// Initialize float position if needed // Initialize float position if needed
if (_currentPosF.IsEmpty) _currentPosF = _visualAgv.CurrentPosition; if (_currentPosF.IsEmpty) _currentPosF = _visualAgv.CurrentPosition;
@@ -614,18 +611,18 @@ private void UpdateVisualAGV()
_visualAgv.CurrentPosition = Point.Round(_currentPosF); _visualAgv.CurrentPosition = Point.Round(_currentPosF);
// Check for Nodes (RFID Trigger) // Check for Nodes (RFID Trigger)
foreach (var node in _mapNodes) foreach (var node in _agvCanvas.Nodes)
{ {
double dist = Math.Sqrt(Math.Pow(node.Position.X - _visualAgv.CurrentPosition.X, 2) + Math.Pow(node.Position.Y - _visualAgv.CurrentPosition.Y, 2)); double dist = Math.Sqrt(Math.Pow(node.Position.X - _visualAgv.CurrentPosition.X, 2) + Math.Pow(node.Position.Y - _visualAgv.CurrentPosition.Y, 2));
if (dist < 15) // Hit Node if (dist < 15) // Hit Node
{ {
// Send Tag // Send Tag
if (node.NodeId != numericUpDown1.Text) if (node.Id != numericUpDown1.Text)
{ {
if (int.TryParse(node.NodeId, out int tag)) if (int.TryParse(node.Id, out int tag))
{ {
AGV.SendTag(node.NodeId); AGV.SendTag(node.Id);
numericUpDown1.Text = node.NodeId; numericUpDown1.Text = node.Id;
// Snap to node // Snap to node
_currentPosF = node.Position; _currentPosF = node.Position;
@@ -665,14 +662,14 @@ private void UpdateVisualAGV()
while (_targetAngle < 0) _targetAngle += 360; while (_targetAngle < 0) _targetAngle += 360;
} }
private void _agvCanvas_NodeRightClicked(object sender, MapNode e) private void _agvCanvas_NodeRightClicked(object sender, NodeBase e)
{ {
if (e != null && _visualAgv != null) if (e != null && _visualAgv != null)
{ {
_visualAgv.CurrentPosition = e.Position; _visualAgv.CurrentPosition = e.Position;
_currentPosF = e.Position; _currentPosF = e.Position;
if (int.TryParse(e.NodeId, out int tag)) if (int.TryParse(e.Id, out int tag))
{ {
numericUpDown1.Text = tag.ToString(); numericUpDown1.Text = tag.ToString();
} }
@@ -796,6 +793,13 @@ private void UpdateVisualAGV()
private void toolStripButton3_Click(object sender, EventArgs e) private void toolStripButton3_Click(object sender, EventArgs e)
{ {
var file = @"C:\Data\Amkor\AGV4\route\NewMap.agvmap"; var file = @"C:\Data\Amkor\AGV4\route\NewMap.agvmap";
if(System.IO.File.Exists(file)==false)
{
var od = new OpenFileDialog();
od.Filter = "json|*.json";
if (od.ShowDialog() != DialogResult.OK) return;
file = od.FileName;
}
LoadMapFile(file); LoadMapFile(file);
} }
private string _currentMapFilePath; private string _currentMapFilePath;
@@ -809,13 +813,8 @@ private void UpdateVisualAGV()
{ {
Console.WriteLine($"Map File Load : {filePath}"); Console.WriteLine($"Map File Load : {filePath}");
_mapNodes = result.Nodes;
_currentMapFilePath = filePath;
// RFID 자동 할당 제거 - 에디터에서 설정한 값 그대로 사용
// 시뮬레이터 캔버스에 맵 설정 // 시뮬레이터 캔버스에 맵 설정
this._agvCanvas.Nodes = _mapNodes; this._agvCanvas.SetMapLoadResult(result);// = _mapNodes;
// 맵 설정 적용 (배경색, 그리드 표시) // 맵 설정 적용 (배경색, 그리드 표시)
if (result.Settings != null) if (result.Settings != null)
@@ -871,17 +870,17 @@ private void UpdateVisualAGV()
} }
} }
if (!string.IsNullOrEmpty(input) && _mapNodes != null && _visualAgv != null) if (!string.IsNullOrEmpty(input) && _agvCanvas.Nodes != null && _visualAgv != null)
{ {
var node = _mapNodes.FirstOrDefault(n => n.NodeId == input); var node = _agvCanvas.Nodes.FirstOrDefault(n => n.Id == input);
if (node != null) if (node != null)
{ {
_visualAgv.CurrentPosition = node.Position; _visualAgv.CurrentPosition = node.Position;
_currentPosF = node.Position; _currentPosF = node.Position;
numericUpDown1.Text = node.NodeId; numericUpDown1.Text = node.Id;
// Auto-orient: Prefer Right (0) or Up (270) // Auto-orient: Prefer Right (0) or Up (270)
var neighbors = _mapNodes.Where(n => n != node && var neighbors = _agvCanvas.Nodes.Where(n => n != node &&
Math.Sqrt(Math.Pow(n.Position.X - node.Position.X, 2) + Math.Pow(n.Position.Y - node.Position.Y, 2)) < 150) Math.Sqrt(Math.Pow(n.Position.X - node.Position.X, 2) + Math.Pow(n.Position.Y - node.Position.Y, 2)) < 150)
.ToList(); .ToList();

3
Cs_HMI/.cursorrules Normal file
View File

@@ -0,0 +1,3 @@
# User Preferences
- **프로젝트 컴파일은 build.bat 를 사용하세요(윈도우 환경)
- **콘솔명령 실행시에는 항상 cmd /c 를 사용해서 실행하세요

View File

@@ -1,7 +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.36804.6 d17.14
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sub", "Sub", "{C423C39A-44E7-4F09-B2F7-7943975FF948}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sub", "Sub", "{C423C39A-44E7-4F09-B2F7-7943975FF948}"
EndProject EndProject
@@ -38,8 +38,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVSimulator", "AGVLogic\AG
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "arCommUtil", "SubProject\commutil\arCommUtil.csproj", "{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "arCommUtil", "SubProject\commutil\arCommUtil.csproj", "{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Supertonic.Netfx48", "SubProject\SuperTonic\Supertonic.Netfx48.csproj", "{19675E19-EB91-493E-88C3-32B3C094B749}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -182,18 +180,6 @@ Global
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Release|x64.Build.0 = Release|Any CPU {14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Release|x64.Build.0 = Release|Any CPU
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Release|x86.ActiveCfg = Release|x86 {14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Release|x86.ActiveCfg = Release|x86
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Release|x86.Build.0 = Release|x86 {14E8C9A5-013E-49BA-B435-FFFFFF7DD623}.Release|x86.Build.0 = Release|x86
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|x64.ActiveCfg = Debug|x64
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|x64.Build.0 = Debug|x64
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|x86.ActiveCfg = Debug|Win32
{19675E19-EB91-493E-88C3-32B3C094B749}.Debug|x86.Build.0 = Debug|Win32
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|Any CPU.Build.0 = Release|Any CPU
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|x64.ActiveCfg = Release|x64
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|x64.Build.0 = Release|x64
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|x86.ActiveCfg = Release|Win32
{19675E19-EB91-493E-88C3-32B3C094B749}.Release|x86.Build.0 = Release|Win32
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -207,7 +193,6 @@ Global
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890} = {E5C75D32-5AD6-44DD-8F27-E32023206EBB} {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} = {E5C75D32-5AD6-44DD-8F27-E32023206EBB}
{B2C3D4E5-0000-0000-0000-000000000000} = {E5C75D32-5AD6-44DD-8F27-E32023206EBB} {B2C3D4E5-0000-0000-0000-000000000000} = {E5C75D32-5AD6-44DD-8F27-E32023206EBB}
{14E8C9A5-013E-49BA-B435-FFFFFF7DD623} = {C423C39A-44E7-4F09-B2F7-7943975FF948} {14E8C9A5-013E-49BA-B435-FFFFFF7DD623} = {C423C39A-44E7-4F09-B2F7-7943975FF948}
{19675E19-EB91-493E-88C3-32B3C094B749} = {C423C39A-44E7-4F09-B2F7-7943975FF948}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B5B1FD72-356F-4840-83E8-B070AC21C8D9} SolutionGuid = {B5B1FD72-356F-4840-83E8-B070AC21C8D9}

View File

@@ -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\</OutputPath> <OutputPath>..\..\..\..\..\..\Amkor\AGV4\Test\MapEditor\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>

View File

@@ -13,9 +13,9 @@ namespace AGVMapEditor.Forms
/// </summary> /// </summary>
public partial class ImageEditorForm : Form public partial class ImageEditorForm : Form
{ {
private MapNode _targetNode; private MapImage _targetNode;
public ImageEditorForm(MapNode imageNode = null) public ImageEditorForm(MapImage imageNode = null)
{ {
InitializeComponent(); InitializeComponent();
_targetNode = imageNode; _targetNode = imageNode;
@@ -48,7 +48,7 @@ namespace AGVMapEditor.Forms
imageCanvas.BrushSize = trackBrush.Value; imageCanvas.BrushSize = trackBrush.Value;
} }
private void LoadImageFromNode(MapNode node) private void LoadImageFromNode(MapImage node)
{ {
if (node.LoadedImage != null) if (node.LoadedImage != null)
{ {
@@ -160,7 +160,7 @@ namespace AGVMapEditor.Forms
return; return;
} }
if (_targetNode != null && _targetNode.Type == NodeType.Image) if (_targetNode != null)
{ {
// 표시 크기로 리사이즈된 이미지 가져오기 // 표시 크기로 리사이즈된 이미지 가져오기
var finalImage = imageCanvas.GetResizedImage(); var finalImage = imageCanvas.GetResizedImage();

View File

@@ -7,6 +7,8 @@ using System.Windows.Forms;
using AGVMapEditor.Models; using AGVMapEditor.Models;
using AGVNavigationCore.Controls; using AGVNavigationCore.Controls;
using AGVNavigationCore.Models; using AGVNavigationCore.Models;
using MapImage = AGVNavigationCore.Models.MapImage;
using MapLabel = AGVNavigationCore.Models.MapLabel;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace AGVMapEditor.Forms namespace AGVMapEditor.Forms
@@ -18,11 +20,11 @@ namespace AGVMapEditor.Forms
{ {
#region Fields #region Fields
private List<MapNode> _mapNodes; // private List<MapNode> this._mapCanvas.Nodes;
private UnifiedAGVCanvas _mapCanvas; private UnifiedAGVCanvas _mapCanvas;
// 현재 선택된 노드 // 현재 선택된 노드
private MapNode _selectedNode; private NodeBase _selectedNode;
// 파일 경로 // 파일 경로
private string _currentMapFile = string.Empty; private string _currentMapFile = string.Empty;
@@ -34,20 +36,20 @@ namespace AGVMapEditor.Forms
{ {
public string FromNodeId { get; set; } public string FromNodeId { get; set; }
public string FromNodeName { get; set; } public string FromNodeName { get; set; }
public string FromRfidId { get; set; } public ushort FromRfidId { get; set; }
public string ToNodeId { get; set; } public string ToNodeId { get; set; }
public string ToNodeName { get; set; } public string ToNodeName { get; set; }
public string ToRfidId { get; set; } public ushort ToRfidId { get; set; }
public string ConnectionType { get; set; } public string ConnectionType { get; set; }
public override string ToString() public override string ToString()
{ {
// RFID가 있으면 RFID(노드이름), 없으면 NodeID(노드이름) 형태로 표시 // RFID가 있으면 RFID(노드이름), 없으면 NodeID(노드이름) 형태로 표시
string fromDisplay = !string.IsNullOrEmpty(FromRfidId) string fromDisplay = FromRfidId > 0
? $"{FromRfidId}({FromNodeName})" ? $"{FromRfidId}({FromNodeName})"
: $"---({FromNodeId})"; : $"---({FromNodeId})";
string toDisplay = !string.IsNullOrEmpty(ToRfidId) string toDisplay = ToRfidId > 0
? $"{ToRfidId}({ToNodeName})" ? $"{ToRfidId}({ToNodeName})"
: $"---({ToNodeId})"; : $"---({ToNodeId})";
@@ -98,15 +100,13 @@ namespace AGVMapEditor.Forms
private void InitializeData() private void InitializeData()
{ {
_mapNodes = new List<MapNode>(); // this._mapCanvas.Nodes = new List<MapNode>();
} }
private void InitializeMapCanvas() private void InitializeMapCanvas()
{ {
_mapCanvas = new UnifiedAGVCanvas(); _mapCanvas = new UnifiedAGVCanvas();
_mapCanvas.Dock = DockStyle.Fill; _mapCanvas.Dock = DockStyle.Fill;
_mapCanvas.Nodes = _mapNodes;
// RfidMappings 제거 - MapNode에 통합됨
// 이벤트 연결 // 이벤트 연결
_mapCanvas.NodeAdded += OnNodeAdded; _mapCanvas.NodeAdded += OnNodeAdded;
@@ -115,12 +115,14 @@ namespace AGVMapEditor.Forms
_mapCanvas.NodeMoved += OnNodeMoved; _mapCanvas.NodeMoved += OnNodeMoved;
_mapCanvas.NodeDeleted += OnNodeDeleted; _mapCanvas.NodeDeleted += OnNodeDeleted;
_mapCanvas.ConnectionDeleted += OnConnectionDeleted; _mapCanvas.ConnectionDeleted += OnConnectionDeleted;
_mapCanvas.ImageNodeDoubleClicked += OnImageNodeDoubleClicked; _mapCanvas.ImageDoubleClicked += OnImageDoubleClicked;
_mapCanvas.MapChanged += OnMapChanged; _mapCanvas.MapChanged += OnMapChanged;
// 스플리터 패널에 맵 캔버스 추가 // 스플리터 패널에 맵 캔버스 추가
panel1.Controls.Add(_mapCanvas); panel1.Controls.Add(_mapCanvas);
// ...
// 툴바 버튼 이벤트 연결 // 툴바 버튼 이벤트 연결
WireToolbarButtonEvents(); WireToolbarButtonEvents();
} }
@@ -174,7 +176,7 @@ namespace AGVMapEditor.Forms
} }
} }
private void OnNodeAdded(object sender, MapNode node) private void OnNodeAdded(object sender, NodeBase node)
{ {
_hasChanges = true; _hasChanges = true;
UpdateTitle(); UpdateTitle();
@@ -199,7 +201,7 @@ namespace AGVMapEditor.Forms
// } // }
//} //}
private void OnNodesSelected(object sender, List<MapNode> nodes) private void OnNodesSelected(object sender, List<NodeBase> nodes)
{ {
// 다중 선택 시 처리 // 다중 선택 시 처리
if (nodes == null || nodes.Count == 0) if (nodes == null || nodes.Count == 0)
@@ -227,14 +229,14 @@ namespace AGVMapEditor.Forms
} }
} }
private void OnNodeMoved(object sender, MapNode node) private void OnNodeMoved(object sender, NodeBase node)
{ {
_hasChanges = true; _hasChanges = true;
UpdateTitle(); UpdateTitle();
RefreshNodeList(); RefreshNodeList();
} }
private void OnNodeDeleted(object sender, MapNode node) private void OnNodeDeleted(object sender, NodeBase node)
{ {
_hasChanges = true; _hasChanges = true;
UpdateTitle(); UpdateTitle();
@@ -258,12 +260,10 @@ namespace AGVMapEditor.Forms
UpdateNodeProperties(); // 연결 정보 업데이트 UpdateNodeProperties(); // 연결 정보 업데이트
} }
private void OnImageNodeDoubleClicked(object sender, MapNode node) private void OnImageDoubleClicked(object sender, MapImage image)
{ {
// 이미지 노드 더블클릭 시 이미지 편집창 표시 // 이미지 노드 더블클릭 시 이미지 편집창 표시
if (node != null && node.Type == NodeType.Image) using (var editor = new ImageEditorForm(image))
{
using (var editor = new ImageEditorForm(node))
{ {
if (editor.ShowDialog(this) == DialogResult.OK) if (editor.ShowDialog(this) == DialogResult.OK)
{ {
@@ -274,7 +274,6 @@ namespace AGVMapEditor.Forms
} }
} }
} }
}
private void OnMapChanged(object sender, EventArgs e) private void OnMapChanged(object sender, EventArgs e)
{ {
@@ -401,12 +400,11 @@ namespace AGVMapEditor.Forms
private void AddNewNode() private void AddNewNode()
{ {
var nodeId = GenerateNodeId(); var nodeId = GenerateNodeId();
var nodeName = $"노드{_mapNodes.Count + 1}"; var position = new Point(100 + this._mapCanvas.Nodes.Count * 50, 100 + this._mapCanvas.Nodes.Count * 50);
var position = new Point(100 + _mapNodes.Count * 50, 100 + _mapNodes.Count * 50);
var node = new MapNode(nodeId, nodeName, position, NodeType.Normal); var node = new MapNode(nodeId, position, StationType.Normal);
_mapNodes.Add(node); this._mapCanvas.Nodes.Add(node);
_hasChanges = true; _hasChanges = true;
RefreshNodeList(); RefreshNodeList();
@@ -422,13 +420,14 @@ namespace AGVMapEditor.Forms
return; return;
} }
var result = MessageBox.Show($"노드 '{_selectedNode.Name}'를 삭제하시겠습니까?\n연결된 RFID 매핑도 함께 삭제됩니다.", var rfidDisplay = (_selectedNode as MapNode)?.RfidId ?? 0;
var result = MessageBox.Show($"노드 {rfidDisplay}[{_selectedNode.Id}] 를 삭제하시겠습니까?\n연결된 RFID 매핑도 함께 삭제됩니다.",
"삭제 확인", MessageBoxButtons.YesNo, MessageBoxIcon.Question); "삭제 확인", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result == DialogResult.Yes) if (result == DialogResult.Yes)
{ {
// 노드 제거 // 노드 제거
_mapNodes.Remove(_selectedNode); _mapCanvas.RemoveItem(_selectedNode);
_selectedNode = null; _selectedNode = null;
_hasChanges = true; _hasChanges = true;
@@ -441,15 +440,15 @@ namespace AGVMapEditor.Forms
private void AddConnectionToSelectedNode() private void AddConnectionToSelectedNode()
{ {
if (_selectedNode == null) if (!(_selectedNode is MapNode selectedMapNode))
{ {
MessageBox.Show("연결을 추가할 노드를 선택하세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBox.Show("연결을 추가할 노드(MapNode)를 선택하세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
return; return;
} }
// 다른 노드들 중에서 선택 // 다른 노드들 중에서 선택
var availableNodes = _mapNodes.Where(n => n.NodeId != _selectedNode.NodeId && var availableNodes = this._mapCanvas.Nodes.Where(n => n.Id != selectedMapNode.Id &&
!_selectedNode.ConnectedNodes.Contains(n.NodeId)).ToList(); !selectedMapNode.ConnectedNodes.Contains(n.Id)).ToList();
if (availableNodes.Count == 0) if (availableNodes.Count == 0)
{ {
@@ -458,13 +457,13 @@ namespace AGVMapEditor.Forms
} }
// 간단한 선택 다이얼로그 (실제로는 별도 폼을 만들어야 함) // 간단한 선택 다이얼로그 (실제로는 별도 폼을 만들어야 함)
var nodeNames = availableNodes.Select(n => $"{n.NodeId}: {n.Name}").ToArray(); var nodeNames = availableNodes.Select(n => $"{n.Id}: {n.RfidId}").ToArray();
var input = Microsoft.VisualBasic.Interaction.InputBox("연결할 노드를 선택하세요:", "노드 연결", nodeNames[0]); var input = Microsoft.VisualBasic.Interaction.InputBox("연결할 노드를 선택하세요:", "노드 연결", nodeNames[0]);
var targetNode = availableNodes.FirstOrDefault(n => input.StartsWith(n.NodeId)); var targetNode = availableNodes.FirstOrDefault(n => input.StartsWith(n.Id));
if (targetNode != null) if (targetNode != null)
{ {
_selectedNode.AddConnection(targetNode.NodeId); selectedMapNode.AddConnection(targetNode.Id);
_hasChanges = true; _hasChanges = true;
RefreshMapCanvas(); RefreshMapCanvas();
UpdateNodeProperties(); UpdateNodeProperties();
@@ -474,25 +473,25 @@ namespace AGVMapEditor.Forms
private void RemoveConnectionFromSelectedNode() private void RemoveConnectionFromSelectedNode()
{ {
if (_selectedNode == null || _selectedNode.ConnectedNodes.Count == 0) if (!(_selectedNode is MapNode selectedMapNode) || selectedMapNode.ConnectedNodes.Count == 0)
{ {
MessageBox.Show("연결을 제거할 노드를 선택하거나 연결된 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBox.Show("연결을 제거할 노드를 선택하거나 연결된 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
return; return;
} }
// 연결된 노드들 중에서 선택 // 연결된 노드들 중에서 선택
var connectedNodeNames = _selectedNode.ConnectedNodes.Select(connectedNodeId => var connectedNodeNames = selectedMapNode.ConnectedNodes.Select(connectedNodeId =>
{ {
var node = _mapNodes.FirstOrDefault(n => n.NodeId == connectedNodeId); var node = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == connectedNodeId);
return node != null ? $"{node.NodeId}: {node.Name}" : connectedNodeId; return node != null ? $"{node.Id}: {node.RfidId}" : connectedNodeId;
}).ToArray(); }).ToArray();
var input = Microsoft.VisualBasic.Interaction.InputBox("제거할 연결을 선택하세요:", "연결 제거", connectedNodeNames[0]); var input = Microsoft.VisualBasic.Interaction.InputBox("제거할 연결을 선택하세요:", "연결 제거", connectedNodeNames[0]);
var targetNodeId = input.Split(':')[0]; var targetNodeId = input.Split(':')[0];
if (_selectedNode.ConnectedNodes.Contains(targetNodeId)) if (selectedMapNode.ConnectedNodes.Contains(targetNodeId))
{ {
_selectedNode.RemoveConnection(targetNodeId); selectedMapNode.RemoveConnection(targetNodeId);
_hasChanges = true; _hasChanges = true;
RefreshMapCanvas(); RefreshMapCanvas();
UpdateNodeProperties(); UpdateNodeProperties();
@@ -509,7 +508,7 @@ namespace AGVMapEditor.Forms
{ {
nodeId = $"N{counter:D3}"; nodeId = $"N{counter:D3}";
counter++; counter++;
} while (_mapNodes.Any(n => n.NodeId == nodeId)); } while (this._mapCanvas.Nodes.Any(n => n.Id == nodeId));
return nodeId; return nodeId;
} }
@@ -520,7 +519,7 @@ namespace AGVMapEditor.Forms
private void NewMap() private void NewMap()
{ {
_mapNodes.Clear(); this._mapCanvas.Nodes.Clear();
_selectedNode = null; _selectedNode = null;
_currentMapFile = string.Empty; _currentMapFile = string.Empty;
_hasChanges = false; _hasChanges = false;
@@ -533,7 +532,7 @@ namespace AGVMapEditor.Forms
{ {
if (CheckSaveChanges()) if (CheckSaveChanges())
{ {
_mapNodes.Clear(); this._mapCanvas.Nodes.Clear();
_selectedNode = null; _selectedNode = null;
_currentMapFile = string.Empty; _currentMapFile = string.Empty;
_hasChanges = false; _hasChanges = false;
@@ -547,8 +546,8 @@ namespace AGVMapEditor.Forms
{ {
var openFileDialog = new OpenFileDialog var openFileDialog = new OpenFileDialog
{ {
Filter = "AGV Map Files (*.agvmap)|*.agvmap|All Files (*.*)|*.*", Filter = "AGV Map Files (*.json)|*.json|All Files (*.*)|*.*",
DefaultExt = "agvmap", DefaultExt = "json",
}; };
@@ -596,9 +595,9 @@ namespace AGVMapEditor.Forms
{ {
var saveFileDialog = new SaveFileDialog var saveFileDialog = new SaveFileDialog
{ {
Filter = "AGV Map Files (*.agvmap)|*.agvmap", Filter = "AGV Map Files (*.json)|*.json",
DefaultExt = "agvmap", DefaultExt = "json",
FileName = "NewMap.agvmap" FileName = "NewMap.json"
}; };
if (saveFileDialog.ShowDialog() == DialogResult.OK) if (saveFileDialog.ShowDialog() == DialogResult.OK)
@@ -624,18 +623,8 @@ namespace AGVMapEditor.Forms
if (result.Success) if (result.Success)
{ {
_mapNodes = result.Nodes;
// 맵 캔버스에 데이터 설정 // 맵 캔버스에 데이터 설정
_mapCanvas.Nodes = _mapNodes; _mapCanvas.SetMapLoadResult(result);
// RfidMappings 제거됨 - MapNode에 통합
// 🔥 맵 설정 적용 (배경색, 그리드 표시)
if (result.Settings != null)
{
_mapCanvas.BackColor = System.Drawing.Color.FromArgb(result.Settings.BackgroundColorArgb);
_mapCanvas.ShowGrid = result.Settings.ShowGrid;
}
// 현재 파일 경로 업데이트 // 현재 파일 경로 업데이트
_currentMapFile = filePath; _currentMapFile = filePath;
@@ -648,8 +637,8 @@ namespace AGVMapEditor.Forms
UpdateNodeList(); UpdateNodeList();
RefreshNodeConnectionList(); RefreshNodeConnectionList();
// 맵 로드 후 자동으로 맵에 맞춤
_mapCanvas.FitToNodes();
UpdateStatusBar($"맵 파일을 성공적으로 로드했습니다: {Path.GetFileName(filePath)}"); UpdateStatusBar($"맵 파일을 성공적으로 로드했습니다: {Path.GetFileName(filePath)}");
} }
@@ -693,7 +682,11 @@ namespace AGVMapEditor.Forms
ShowGrid = _mapCanvas.ShowGrid ShowGrid = _mapCanvas.ShowGrid
}; };
if (MapLoader.SaveMapToFile(filePath, _mapNodes, settings)) if (MapLoader.SaveMapToFile(filePath,
_mapCanvas.Nodes, _mapCanvas.Labels,
_mapCanvas.Images, _mapCanvas.Marks,
_mapCanvas.Magnets,
settings))
{ {
// 현재 파일 경로 업데이트 // 현재 파일 경로 업데이트
_currentMapFile = filePath; _currentMapFile = filePath;
@@ -718,7 +711,7 @@ namespace AGVMapEditor.Forms
private void UpdateRfidMappings() private void UpdateRfidMappings()
{ {
// RFID 자동 할당 제거 - 사용자가 직접 입력한 값 유지 // RFID 자동 할당 제거 - 사용자가 직접 입력한 값 유지
// MapLoader.AssignAutoRfidIds(_mapNodes); // MapLoader.AssignAutoRfidIds(this._mapCanvas.Nodes);
} }
private bool CheckSaveChanges() private bool CheckSaveChanges()
@@ -780,9 +773,9 @@ namespace AGVMapEditor.Forms
private void RefreshNodeList() private void RefreshNodeList()
{ {
listBoxNodes.DataSource = null; listBoxNodes.DataSource = null;
listBoxNodes.DataSource = _mapNodes; listBoxNodes.DataSource = this._mapCanvas.Nodes;
listBoxNodes.DisplayMember = "DisplayText"; listBoxNodes.DisplayMember = "DisplayText";
listBoxNodes.ValueMember = "NodeId"; listBoxNodes.ValueMember = "Id";
// 노드 목록 클릭 이벤트 연결 // 노드 목록 클릭 이벤트 연결
listBoxNodes.SelectedIndexChanged -= ListBoxNodes_SelectedIndexChanged; listBoxNodes.SelectedIndexChanged -= ListBoxNodes_SelectedIndexChanged;
@@ -812,9 +805,9 @@ namespace AGVMapEditor.Forms
{ {
e.DrawBackground(); e.DrawBackground();
if (e.Index >= 0 && e.Index < _mapNodes.Count) if (e.Index >= 0 && e.Index < this._mapCanvas.Items.Count)
{ {
var node = _mapNodes[e.Index]; var node = this._mapCanvas.Items[e.Index];
// 노드 타입에 따른 색상 설정 // 노드 타입에 따른 색상 설정
Color foreColor = Color.Black; Color foreColor = Color.Black;
@@ -827,22 +820,30 @@ namespace AGVMapEditor.Forms
} }
else else
{ {
backColor = Color.White;
switch (node.Type) switch (node.Type)
{ {
case NodeType.Normal: case NodeType.Normal:
foreColor = Color.Black;
backColor = Color.White; var item = node as MapNode;
break; if (item.StationType == StationType.Normal)
case NodeType.Loader: foreColor = Color.DimGray;
case NodeType.UnLoader: else if (item.StationType == StationType.Charger)
case NodeType.Clearner: foreColor = Color.Red;
case NodeType.Buffer: else
foreColor = Color.DarkGreen; foreColor = Color.DarkGreen;
backColor = Color.LightGreen;
break; break;
case NodeType.Charging:
case NodeType.Label:
case NodeType.Mark:
case NodeType.Image:
foreColor = Color.DarkBlue;
break;
case NodeType.Magnet:
foreColor = Color.DarkMagenta;
break;
default:
foreColor = Color.DarkRed; foreColor = Color.DarkRed;
backColor = Color.LightPink;
break; break;
} }
} }
@@ -854,17 +855,21 @@ namespace AGVMapEditor.Forms
} }
// 텍스트 그리기 (노드ID - 노드명 - RFID 순서) // 텍스트 그리기 (노드ID - 노드명 - RFID 순서)
var displayText = node.NodeId; string displayText;
if (node.Type == NodeType.Normal)
if (!string.IsNullOrEmpty(node.Name))
{ {
displayText += $" - {node.Name}"; var item = node as MapNode;
if (item.HasRfid())
displayText = $"[{node.Id}-{item.RfidId}] {item.StationType}";
else
displayText = $"[{node.Id}] {item.StationType}";
} }
else if (node.Type == NodeType.Label)
if (!string.IsNullOrEmpty(node.RfidId))
{ {
displayText += $" - [{node.RfidId}]"; var item = node as MapLabel;
displayText = $"{item.Type.ToString().ToUpper()} - {item.Text}";
} }
else displayText = $"{node.Type.ToString().ToUpper()}";
using (var brush = new SolidBrush(foreColor)) using (var brush = new SolidBrush(foreColor))
{ {
@@ -881,31 +886,31 @@ namespace AGVMapEditor.Forms
var processedPairs = new HashSet<string>(); var processedPairs = new HashSet<string>();
// 모든 노드의 연결 정보를 수집 (중복 방지) // 모든 노드의 연결 정보를 수집 (중복 방지)
foreach (var fromNode in _mapNodes) foreach (var fromNode in this._mapCanvas.Nodes)
{ {
foreach (var toNodeId in fromNode.ConnectedNodes) foreach (var toNodeId in fromNode.ConnectedNodes)
{ {
var toNode = _mapNodes.FirstOrDefault(n => n.NodeId == toNodeId); var toNode = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == toNodeId);
if (toNode != null) if (toNode != null)
{ {
// 중복 체크 (단일 연결만 표시) // 중복 체크 (단일 연결만 표시)
string pairKey1 = $"{fromNode.NodeId}-{toNode.NodeId}"; string pairKey1 = $"{fromNode.Id}-{toNode.Id}";
string pairKey2 = $"{toNode.NodeId}-{fromNode.NodeId}"; string pairKey2 = $"{toNode.Id}-{fromNode.Id}";
if (!processedPairs.Contains(pairKey1) && !processedPairs.Contains(pairKey2)) if (!processedPairs.Contains(pairKey1) && !processedPairs.Contains(pairKey2))
{ {
// 사전 순으로 정렬하여 일관성 있게 표시 // 사전 순으로 정렬하여 일관성 있게 표시
var (firstNode, secondNode) = string.Compare(fromNode.NodeId, toNode.NodeId) < 0 var (firstNode, secondNode) = string.Compare(fromNode.Id, toNode.Id) < 0
? (fromNode, toNode) ? (fromNode, toNode)
: (toNode, fromNode); : (toNode, fromNode);
connections.Add(new NodeConnectionInfo connections.Add(new NodeConnectionInfo
{ {
FromNodeId = firstNode.NodeId, FromNodeId = firstNode.Id,
FromNodeName = firstNode.Name, FromNodeName = "",
FromRfidId = firstNode.RfidId, FromRfidId = firstNode.RfidId,
ToNodeId = secondNode.NodeId, ToNodeId = secondNode.Id,
ToNodeName = secondNode.Name, ToNodeName = "",
ToRfidId = secondNode.RfidId, ToRfidId = secondNode.RfidId,
ConnectionType = "양방향" // 모든 연결이 양방향 ConnectionType = "양방향" // 모든 연결이 양방향
}); });
@@ -940,7 +945,7 @@ namespace AGVMapEditor.Forms
_mapCanvas?.HighlightConnection(connectionInfo.FromNodeId, connectionInfo.ToNodeId); _mapCanvas?.HighlightConnection(connectionInfo.FromNodeId, connectionInfo.ToNodeId);
// 연결된 노드들을 맵에서 하이라이트 표시 (선택적) // 연결된 노드들을 맵에서 하이라이트 표시 (선택적)
var fromNode = _mapNodes.FirstOrDefault(n => n.NodeId == connectionInfo.FromNodeId); var fromNode = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == connectionInfo.FromNodeId);
if (fromNode != null) if (fromNode != null)
{ {
_selectedNode = fromNode; _selectedNode = fromNode;
@@ -1002,7 +1007,7 @@ namespace AGVMapEditor.Forms
private void UpdateImageEditButton() private void UpdateImageEditButton()
{ {
// ToolStripButton으로 변경됨 // ToolStripButton으로 변경됨
btnEditImage.Enabled = (_selectedNode != null && _selectedNode.Type == NodeType.Image); btnEditImage.Enabled = (_mapCanvas.SelectedImage != null);
} }
/// <summary> /// <summary>
@@ -1019,9 +1024,10 @@ namespace AGVMapEditor.Forms
/// </summary> /// </summary>
private void BtnToolbarEditImage_Click(object sender, EventArgs e) private void BtnToolbarEditImage_Click(object sender, EventArgs e)
{ {
if (_selectedNode != null && _selectedNode.Type == NodeType.Image) var selectedImage = _mapCanvas.SelectedImage;
if (selectedImage != null)
{ {
using (var editor = new ImageEditorForm(_selectedNode)) using (var editor = new ImageEditorForm(selectedImage))
{ {
if (editor.ShowDialog(this) == DialogResult.OK) if (editor.ShowDialog(this) == DialogResult.OK)
{ {
@@ -1059,7 +1065,7 @@ namespace AGVMapEditor.Forms
if (listBoxNodes != null) if (listBoxNodes != null)
{ {
listBoxNodes.DataSource = null; listBoxNodes.DataSource = null;
listBoxNodes.DataSource = _mapNodes; listBoxNodes.DataSource = this._mapCanvas.Items;
listBoxNodes.DisplayMember = "DisplayText"; listBoxNodes.DisplayMember = "DisplayText";
} }
} }
@@ -1101,8 +1107,8 @@ namespace AGVMapEditor.Forms
// RFID 값 변경시 중복 검사 // RFID 값 변경시 중복 검사
if (e.ChangedItem.PropertyDescriptor.Name == "RFID") if (e.ChangedItem.PropertyDescriptor.Name == "RFID")
{ {
string newRfidValue = e.ChangedItem.Value?.ToString(); var newRfidValue = ushort.Parse(e.ChangedItem.Value?.ToString());
if (!string.IsNullOrEmpty(newRfidValue) && CheckRfidDuplicate(newRfidValue)) if (newRfidValue != 0 && CheckRfidDuplicate(newRfidValue))
{ {
// 중복된 RFID 값 발견 // 중복된 RFID 값 발견
MessageBox.Show($"RFID 값 '{newRfidValue}'이(가) 이미 다른 노드에서 사용 중입니다.\n입력값을 되돌립니다.", MessageBox.Show($"RFID 값 '{newRfidValue}'이(가) 이미 다른 노드에서 사용 중입니다.\n입력값을 되돌립니다.",
@@ -1123,11 +1129,11 @@ namespace AGVMapEditor.Forms
bool isMultiSelect = _propertyGrid.SelectedObjects is MapNode[]; // _propertyGrid.SelectedObject is MapNode[]; bool isMultiSelect = _propertyGrid.SelectedObjects is MapNode[]; // _propertyGrid.SelectedObject is MapNode[];
//var a = _propertyGrid.SelectedObject; //var a = _propertyGrid.SelectedObject;
List<MapNode> selectedNodes = null; List<NodeBase> selectedNodes = null;
if (isMultiSelect) if (isMultiSelect)
{ {
// 캔버스에서 현재 선택된 노드들 가져오기 // 캔버스에서 현재 선택된 노드들 가져오기
selectedNodes = new List<MapNode>(_mapCanvas.SelectedNodes); selectedNodes = new List<NodeBase>(_mapCanvas.SelectedNodes);
System.Diagnostics.Debug.WriteLine($"[PropertyGrid] 다중 선택 노드 수: {selectedNodes.Count}"); System.Diagnostics.Debug.WriteLine($"[PropertyGrid] 다중 선택 노드 수: {selectedNodes.Count}");
} }
@@ -1153,9 +1159,9 @@ namespace AGVMapEditor.Forms
_propertyGrid.Refresh(); _propertyGrid.Refresh();
// 🔥 단일 선택인 경우에만 노드를 다시 선택 (다중 선택은 캔버스에서 유지) // 🔥 단일 선택인 경우에만 노드를 다시 선택 (다중 선택은 캔버스에서 유지)
if (!isMultiSelect && currentSelectedNode != null) if (!isMultiSelect && currentSelectedNode is MapNode mapNode)
{ {
var nodeIndex = _mapNodes.IndexOf(currentSelectedNode); var nodeIndex = this._mapCanvas.Nodes.IndexOf(mapNode);
if (nodeIndex >= 0) if (nodeIndex >= 0)
{ {
listBoxNodes.SelectedIndex = nodeIndex; listBoxNodes.SelectedIndex = nodeIndex;
@@ -1168,38 +1174,24 @@ namespace AGVMapEditor.Forms
/// </summary> /// </summary>
/// <param name="rfidValue">검사할 RFID 값</param> /// <param name="rfidValue">검사할 RFID 값</param>
/// <returns>중복되면 true, 아니면 false</returns> /// <returns>중복되면 true, 아니면 false</returns>
private bool CheckRfidDuplicate(string rfidValue) private bool CheckRfidDuplicate(ushort rfidValue)
{ {
if (string.IsNullOrEmpty(rfidValue) || _mapNodes == null) if (rfidValue == 0 || this._mapCanvas.Nodes == null)
return false; return false;
// 현재 편집 중인 노드 제외하고 중복 검사 // 현재 편집 중인 노드 제외하고 중복 검사
string currentNodeId = null; string currentNodeId = null;
var selectedObject = _propertyGrid.SelectedObject; var selectedObject = _propertyGrid.SelectedObject;
// 다양한 PropertyWrapper 타입 처리
//if (selectedObject is NodePropertyWrapper nodeWrapper)
//{
// currentNodeId = nodeWrapper.WrappedNode?.NodeId;
//}
//else if (selectedObject is LabelNodePropertyWrapper labelWrapper)
//{
// currentNodeId = labelWrapper.WrappedNode?.NodeId;
//}
//else if (selectedObject is ImageNodePropertyWrapper imageWrapper)
//{
// currentNodeId = imageWrapper.WrappedNode?.NodeId;
//}
int duplicateCount = 0; int duplicateCount = 0;
foreach (var node in _mapNodes) foreach (var node in this._mapCanvas.Nodes)
{ {
// 현재 편집 중인 노드는 제외 // 현재 편집 중인 노드는 제외
if (node.NodeId == currentNodeId) if (node.Id == currentNodeId)
continue; continue;
// 같은 RFID 값을 가진 노드가 있는지 확인 // 같은 RFID 값을 가진 노드가 있는지 확인
if (!string.IsNullOrEmpty(node.RfidId) && node.RfidId.Equals(rfidValue, StringComparison.OrdinalIgnoreCase)) if (node.RfidId != 0 && node.RfidId == rfidValue)
{ {
duplicateCount++; duplicateCount++;
break; // 하나라도 발견되면 중복 break; // 하나라도 발견되면 중복
@@ -1234,23 +1226,23 @@ namespace AGVMapEditor.Forms
if (result == DialogResult.Yes) if (result == DialogResult.Yes)
{ {
// 단일 연결 삭제 // 단일 연결 삭제
var fromNode = _mapNodes.FirstOrDefault(n => n.NodeId == connectionInfo.FromNodeId); var fromNode = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == connectionInfo.FromNodeId);
var toNode = _mapNodes.FirstOrDefault(n => n.NodeId == connectionInfo.ToNodeId); var toNode = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == connectionInfo.ToNodeId);
if (fromNode != null && toNode != null) if (fromNode != null && toNode != null)
{ {
// 양방향 연결 삭제 (양쪽 방향 모두 제거) // 양방향 연결 삭제 (양쪽 방향 모두 제거)
bool removed = false; bool removed = false;
if (fromNode.ConnectedNodes.Contains(toNode.NodeId)) if (fromNode.ConnectedNodes.Contains(toNode.Id))
{ {
fromNode.RemoveConnection(toNode.NodeId); fromNode.RemoveConnection(toNode.Id);
removed = true; removed = true;
} }
if (toNode.ConnectedNodes.Contains(fromNode.NodeId)) if (toNode.ConnectedNodes.Contains(fromNode.Id))
{ {
toNode.RemoveConnection(fromNode.NodeId); toNode.RemoveConnection(fromNode.Id);
removed = true; removed = true;
} }
@@ -1278,107 +1270,17 @@ namespace AGVMapEditor.Forms
} }
#region Multi-Node Selection
private void ShowMultiNodeContextMenu(List<MapNode> nodes)
{
// 다중 선택 시 간단한 다이얼로그 표시
var result = MessageBox.Show(
$"{nodes.Count}개의 노드가 선택되었습니다.\n\n" +
"일괄 속성 변경을 하시겠습니까?\n\n" +
"예: 글자색 변경\n" +
"아니오: 배경색 변경\n" +
"취소: 닫기",
"다중 노드 속성 변경",
MessageBoxButtons.YesNoCancel,
MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
BatchChangeForeColor();
}
else if (result == DialogResult.No)
{
BatchChangeBackColor();
}
}
/// <summary>
/// 선택된 노드들의 글자색 일괄 변경
/// </summary>
public void BatchChangeForeColor()
{
var selectedNodes = _mapCanvas.SelectedNodes;
if (selectedNodes == null || selectedNodes.Count == 0)
{
MessageBox.Show("선택된 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
using (var colorDialog = new ColorDialog())
{
colorDialog.Color = selectedNodes[0].ForeColor;
if (colorDialog.ShowDialog() == DialogResult.OK)
{
foreach (var node in selectedNodes)
{
node.ForeColor = colorDialog.Color;
node.ModifiedDate = DateTime.Now;
}
_hasChanges = true;
UpdateTitle();
RefreshMapCanvas();
MessageBox.Show($"{selectedNodes.Count}개 노드의 글자색이 변경되었습니다.", "완료", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
/// <summary>
/// 선택된 노드들의 배경색 일괄 변경
/// </summary>
public void BatchChangeBackColor()
{
var selectedNodes = _mapCanvas.SelectedNodes;
if (selectedNodes == null || selectedNodes.Count == 0)
{
MessageBox.Show("선택된 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
using (var colorDialog = new ColorDialog())
{
colorDialog.Color = selectedNodes[0].DisplayColor;
if (colorDialog.ShowDialog() == DialogResult.OK)
{
foreach (var node in selectedNodes)
{
node.DisplayColor = colorDialog.Color;
node.ModifiedDate = DateTime.Now;
}
_hasChanges = true;
UpdateTitle();
RefreshMapCanvas();
MessageBox.Show($"{selectedNodes.Count}개 노드의 배경색이 변경되었습니다.", "완료", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
#endregion
private void allTurnLeftRightCrossOnToolStripMenuItem_Click(object sender, EventArgs e) private void allTurnLeftRightCrossOnToolStripMenuItem_Click(object sender, EventArgs e)
{ {
//모든노드의 trun left/right/ cross 속성을 true로 변경합니다 //모든노드의 trun left/right/ cross 속성을 true로 변경합니다
if (_mapNodes == null || _mapNodes.Count == 0) if (this._mapCanvas.Nodes == null || this._mapCanvas.Nodes.Count == 0)
{ {
MessageBox.Show("맵에 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBox.Show("맵에 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
return; return;
} }
var result = MessageBox.Show( var result = MessageBox.Show(
$"모든 노드({_mapNodes.Count}개)의 회전/교차 속성을 활성화하시겠습니까?\n\n" + $"모든 노드({this._mapCanvas.Nodes.Count}개)의 회전/교차 속성을 활성화하시겠습니까?\n\n" +
"• CanTurnLeft = true\n" + "• CanTurnLeft = true\n" +
"• CanTurnRight = true\n" + "• CanTurnRight = true\n" +
"• DisableCross = false", "• DisableCross = false",
@@ -1388,7 +1290,7 @@ namespace AGVMapEditor.Forms
if (result == DialogResult.Yes) if (result == DialogResult.Yes)
{ {
foreach (var node in _mapNodes) foreach (var node in this._mapCanvas.Nodes)
{ {
node.CanTurnLeft = true; node.CanTurnLeft = true;
node.CanTurnRight = true; node.CanTurnRight = true;
@@ -1401,7 +1303,7 @@ namespace AGVMapEditor.Forms
RefreshMapCanvas(); RefreshMapCanvas();
MessageBox.Show( MessageBox.Show(
$"{_mapNodes.Count}개 노드의 회전/교차 속성이 모두 활성화되었습니다.", $"{this._mapCanvas.Nodes.Count}개 노드의 회전/교차 속성이 모두 활성화되었습니다.",
"완료", "완료",
MessageBoxButtons.OK, MessageBoxButtons.OK,
MessageBoxIcon.Information); MessageBoxIcon.Information);

View File

@@ -145,15 +145,15 @@
<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
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHrSURBVDhPldBNiBJhHMfx/ykKKohQ18tEhygyWGQRJwh1 YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHrSURBVDhPldBLaBNBHMfx/0kUVBBJ0lxWPIhihBJKyAqS
fcHAt7qJ3TOj6FJ0akP06C1kz0EdDLbS3IbaxJdhA2V3jaRiYfPeIXPGeTss/mqElfbZ2HY/p/8883wf pHkQIS+9hXg3RhQviicrITnmJqFnQQ8RqiamRqkhj6VCQtuIQaVQcxdq3d3s61Dy0w002KnU9nP67+x8
Hh6iAxh/5I+pq7M32fV9aeIcp4pzd8xZWb0UUZuOrjnrTe6s3OAOdpgqzn5WRYegNs5X9Y1rUJrcg1Hd h2GIDmD0kT+mLk/fZNf3pQkznCrM3DFnZflSRG05euast7izcpM72GGqMP1ZFRw1tXm+qq9dg9LiHgwb
PhzVTqfYvf+ktC4s6J9SML7dhbH1CNp6BMP3J7Ufdctxdu+U0r44o4qOmto8B30jDuN7Dq1ncXTLN7BV dnFYP51i9/6T0r4wp39Kwfh2F8bGI2irEYjvTmo/Gpbj7N4JpXNxShUcdbV1DvpaHMb3HNrP4uiVb2Cj
vwWtMw/pw1FI72iRbXdRGtyC1r2O1vMEDMPAeDzG+usU9K+38UugL4MqnWGbKbk1Y5FrNiEajaLT6UDX cQtadxbSh6OQ3tM82+6iNLk5rXcd7ecJGIaB0WiE1dcp6F9v41eNvmxV6QzbTMjtKYtct9Wi0Si63S50
dfR6PdSfRmBs3odUpZH0lni2m9BW7Nxo5VQ5kUggnU6jUCigVCqhujgPY/MhBsu0LS3T2rBCj9l2Kh6P XUe/30fjaQTG+n1IVRpKb4lnuzFtyc4Nl06VE4kE0uk0CoUCSqUSqvOzMNYfYnORtqVFWhEr9JhtJ+Lx
T2Ke5+H3+xGLxaCuRSDX7JCFExhUiGObqZ3Y7XbD5/PBarWaV34yeEPbUoUuS2XqDl9Shu0mkskk8vn8 +DjmeR5+vx+xWAzqSgRy3Q65dgJbFeLYZmIndrvd8Pl8sFqt5pWfbL6hbalCl6Uy9cSXlGG7sWQyiXw+
JPZ6vbBYLDDX5QpdkV6RYM54QUd+LtFVtqVwOIxcLrcn3jFcotDf37sEAgFks1m4XC54PJ498b4ymQz6 P469Xi8sFgvMdblCV6RXVDNnvKAjPxfoKttSOBxGLpfbE+8QFyj09/cugUAA2WwWLpcLHo9nT7yvTCaD
/T6cTidCodDhYpN5QLvdRrFYnDwY+/+/gsHgPfPaNpvt8PEfvwFcBQshDC9YfwAAAABJRU5ErkJggg== wWAAp9OJUCh0uNhkHtDpdFAsFscPxv7/r2AweM+8ts1mO3z8x29OYwsb4/6fnQAAAABJRU5ErkJggg==
</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">
@@ -174,75 +174,75 @@
<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
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHISURBVDhPnZJfb9JgFMb3JbzV+C2WNPtk3jgXs0vdsuAW YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHISURBVDhPnZJfa9pgFMb7JXa7sW9RkH2y3awro5ddS3Et
7zajKaEXTWjhvShpilBIA+GvtAWxIyAgJjNBW6WQx5yXtBPREX2SpnnPe57fc9KevXa7jVarhUajgXq9 u2vHRsRcBEw0iEHiYpSg+Hcm0blUtOocdOCWbEZ5xnnlTXFule2BEN7znuf3HJKz12630Wq10Gg0UK/X
jlqthmq1ikqlQu97e7vUbDaxWq22nuFwyCGGYdwNoWQyDAYDnux5XgyhmmVZd0NoZGqmNDqXy+XKfD5H UavVUK1WUalU6P1gb5eazSZWq9XWMxwOGUTX9fshlEyGwWDAkj3PiyBUsyzrfgiNTM2URudyuVyZz+cI
GIa8TkDTNP8OodRfGn8UCoVBEAT49PEbCmzK7/r9PvL5/J8hlByNvFwuQebP0wAvjlycPHLwNruG9Ho9 w5DVCWia5t8hlMobDcP4aRjGIAgCfL75DkOdsrt+v49CofBnCCXzkZfLJcj8ZRrg1aGLk6cO3mfWkF6v
6LoOVVU3IZZl+bPZjDctFguenHji4vJZH6/PPJwfdWOI67rQNG0TUiqVDkzT9KfTdZNdv+HJVycfIJ5f h3w+D0VRNiGWZfmz2Yw1LRYLlhx/7uLyuI+3Zx7OD7sRxHVd5HK5TUipVHpimqY/na6b7PotS746+QTh
b0EcxwFjbBNSLBYFXdf9yWTCm7qtL0gcOnh1egtJHLow1PV9p9OBoigQRfEWYhiGoGmaPx6PtyBvEh5e /HoL4jgOVFXdhBSLxZimaf5kMmFN3dZXxA8cvDm9g8QPXOjK+r7T6UCWZQiCcAfRdT2WzWb98Xi8BXkX
HvdwcfwOYbjkU2QyGaRSqQcxgJTL5QTGmD8ajWLI2WMbF0+7uDrt4us8iM2iKD7cMEdijAnZbNanTeQf 9/D6qIeLow8IwyWbIp1OI5lMPooAJE3TYplMxh+NRhHk7JmNixddXJ128W0eRGZBEB5vmLlUVWUQ2kT2
rnmDy+fv8T0Id5sjKYoipNNpnzYx+sVkVlV1tzmSLMv7siz7tGC2bf+bOZIkSfuSJPn/ZY5EkGQyef/3 4Zq3uHz5ET+CcLeZS5blWCqV8mkT+S8ms6Iou81ckiTtS5Lk04LZtv1vZi5RFPdFUfT/y8xFkEQi8fD3
eqSfzGgWuCCdbTAAAAAASUVORK5CYII= Otcvn84Wo7k6b1AAAAAASUVORK5CYII=
</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
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAG6SURBVDhPnZLditpQFIXnJXrb0rcYCH2wXhSZwly2U0rp YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAG6SURBVDhPnZLditpQFIXnJXrb0rcYkD5YL4pMYS7bKaXM
O0TMRUr+zkUyaUSjBEWNwR9ExGpVvCiDzAl6cljlnCGZWtuRdkEI2Xuvb23IvkiSBP1+H71eD91uF51O O0TMRUp+ETM2EqMERY3BH0TE0ap4UQaZE/TksIZzhpPBOjPSLgghe+/1rQ3ZZ3Eco9frodvtotPpoN1u
B+12G61WS7yfXZxTHMfgnJ88i8VCQoIgeBoikoVhPp/L5NlsVkBELYqipyFiZTEs0sR3s9ls7XY7MMZk o9Vqodls8vebs1OKogiMsaNnPp8LiOd5r0N4MjfMZjORPJ1OMwivhWH4OoSvzId5Gv9uNBrN7XYLSqmo
XQDDMPw7RKT+Mriv1WrzNE3BfnwHSxzZm06nqFarf4aI5HzlLMsgzPxug8y+QvblNVhiy95kMoHv+7As c2AQBC9DeKoc9H1/5/v+LEkS0D+/QWNb9CaTCarV6vMQnixXTtMU3Mzu1kitC6Q/P4LGluiNx2NUKhWY
6xgSRRHdbrdy6HA4yOTMKoF778C/fgS3rwrIeDyG67rHkEaj8SoMQ7rZbB4g3zoymd/egFc/n0BGoxEI pnkICcOQbDYbMbTf70VyaubB3C9gv76DWRcZZDQaoVwuH0Lq9fqHIAjIer1+hNy2RTK7uQKrXh9BhsMh
IceQer2u+L5P1+v1A2QZg5kl8NsPjxCrBBYbsj8YDGCaJlRVfYQEQaC4rktXq9UpJPgETq5xb16DZ0xu HMc5hNRqtZzrumS1Wj1CFhGokQe7+fYEMfOgkS76/X4fhmFAUZQniOd5uVKpRJbL5THE+wHmXOLeuARL
Yds2KpXKiwIg5HmeQgihy+WygByMN+DOW6TuDfZ0V5hVVX15ZM5FCFEcx6HiEiVk0QPz3oPv0/PmXKZp qdjCsiwUi8V3GYDLdd2cbdtksVhkkL3+Ccz+jKR8hR3ZZmZFUd4fmKUcxxEQfokCMu+Cul/Bdslps5Rh
KoZhUHGJ+S8WZsuyzptz6bp+qes6FQc2HA7/zZxL07RLTdPof5lzCUi5XH7+ez3XT8GUHaqSv5fjAAAA GDld1wm/RPmLudk0zdNmKU3TzjVNI/zABoPBv5mlVFU9V1WV/JdZikMKhcLbv+tSD5T6HZWMaVplAAAA
AElFTkSuQmCC AElFTkSuQmCC
</value> </value>
</data> </data>
<data name="btnAddNode.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="btnAddNode.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHdSURBVDhPnZLda9NgFMb3T3ir+F8Mgn/PbmQO0Vt1KKKC YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHdSURBVDhPnZLda9NgFMb3T3ir+F8Min/PbmQO0VvdUEQF
zo/dbX7hVUqzmbVJ+84lxpQ2rSGlbVr7QSklbW1LQdHqW9YPHnlfSDROV/SBEHLOeX7PgZyVSqWCcrmM nV93bk7xKqXZzNqkzbrGmJKmXUhpm9Z+UEpJW9tSULT6lvWDR94X3kicrugDIeSc8/yeAzkrlUoF5XIZ
UqmEYrGIQqGAfD4Px3HY+8zKMrmui8ViceLpdrscYprm6RCWzAye5/HkdrsdQFjNtu3TIWxlNszS2Hcu pVIJxWIRhUIB+XwejuPQ94WVZXJdF4vF4szT7XYZxDCM8yE0mRo8z2PJ7Xbbh9CabdvnQ+jKdJim0e9c
l3PG4zFmsxmvM6BlWX+HsNRfBo/T6bQ3mUww/NzBO3ef91qtFlKp1J8hLNlfeT6fg5lHXz7iYWwDd/fW LueMx2PMZjNWp0DLsv4Ooal80DTNU9M0vclkguGXDj64B6zXarWQTqf/DKHJfOX5fA5qHn39hCfRDTzY
YLh7vNdsNmEYBlRVDUNs26aj0YgPTadTDD51sBW7hJ031/Bcv4lHsY0A0mg0oGlaGJLNZi9YlkWHwyEf X4Pu7rNes9mErutQFCUIsW2bjEYjNjSdTjH43MF29BpeJm9hT7uDp9ENH9JoNJBKpYKQbDZ7xbIsMhwO
+uC9x739NTw9uoFXxh280G+FIPV6HYSQMCSTyQiGYdDBYMCHap08HsjreHa0GUC2DtahFSO8X61WoSgK 2dBH7wQPD9awe7yJt/p9vNbuBiD1eh2qqgYhmUwmpGkaGQwGbKjWyeOxtI5Xx1s+ZPtwHalimPWr1Spk
RFH8CTFNU9A0jfb7/ROQl29vY1u9gicHVzGbT/kWiUQC0Wj0XABg0nVdIITQXq8XQO6/vojH8cvYPdzE WYYgCL8ghmGEkskk6ff7ZyBv3t/DC+UGnh/exGw+ZVvE43FEIpFLPoBK07RQIpEgvV7Phzx6dxXPYtex
t+9fA7MoiudDZl+EECGZTFJ2iXxdz8Hu4XVMjulysy9FUYR4PE7ZJfq/mJlVVV1u9iXL8qosy5QdWK1W c7SF7z+++WZBEC4HzFyqqjIIvUS2rudg5+g2JqdkuZlLluVQLBYj9BL5L6ZmRVGWm7kkSVqVJInQA6vV
+zezL0mSViVJov9l9sUgkUjk7O91Xz8AO/kPZbC5CrgAAAAASUVORK5CYII= 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
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGvSURBVDhP7Y+/TxphGMdR4A9wKh0cNHHuP9ChKXQxsWEw YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGvSURBVDhP7Y89T1phGIZpgR/QqTiQ6NDZP+BgCiZd2jA0
oYMOjR06dGg6QNuhdHLQsLgZ8GVFBzcqRGtDD+447o7jwGAMIkV+ncevpa0mGr7N+0YNOX+0f0C/yWd5 sUNNBx0cjIMDtB3EycGGxY2Evmym7eBGhfgVeuB8cM7hcIDQGKSU7+NBYWlrEw23ed+oMUdr/QHeybW8
83y+7/NYLP/DEg6HnYSQNCEE/0g5FAo9vy4ghBQVRUC9XmI0GpRDRrNZZrRaR9D1CnT9BwoFhZbUhguM ea77fR6b7T4s0WjUQwgRCSG4I5VIJPLisoAQUlJVAc1mmdFqUfYZ7XaF0en8gGFUYRg/USiotKRxtcCs
SqUInk8ikUgwOI5DMplEKpUCz/NIp9MQRRGKorCSYDB4airYhygKTDRLmUwGsiwzWdNyMIzazYJq9QCy Vkvg+RSSySSD4zikUimk02nwPA9RFCFJElRVZSXhcPjYUvAdkiQw0SplMhkoisJkXc/BNBvXC2q1PSiK
LEEQBCZSSZIkJmWzWeRyOWiahr29Atrtxs2C4+MSVFVhEv2NSqqqMjGfz0Ne/YTv81PYcdrx7cU4Im9n DEEQmEglWZaZlM1mkcvloOs6isUCut3W9YJ6vQxNU5lEf6OSpmlMzOfzUMKL+Db1BFseJ3ZfubE2//Lk
z69k7O5uo9NpotultNDr6ej3Ty4xUFpfRt77GKfRAAbFGH5F3kF68wgbvjmwgr8RdTvwOxoAVmYA3xiw QsbOziYOD9s4OqJ00OsZ6PcPzjFR/vwBef8YjmMhDEpx/P60AHluFF8Cr8EK/kfM58KfWAhYfQ4EHgHL
OIH20hN8cT/A9Rn3Jf7UNhgomxhO3+8AfTfP3pq4y1r7ubYA+B0481rQ81pQfW1F7Jm1bp69NZzn4Wfx I+iujOOr7zEuz7gtG08dg4G6jqvpB12g79bZG5Pw2hu/Pk4DQRf++m3o+W2ozdoRn7A3rbM3hpscWpLe
5eR50zcB44Md5Vcj+Dptu9hyjX40z94ZzjP+Pu60VujadKMr+Q87TKpZvTdvaAAAAABJRU5ErkJggg== DJ+0AyMw3zlRmXmA7WeO0w3vw/fW2X+Gm3S/TXjsVbo23ehCPgMKrqo38mZYEwAAAABJRU5ErkJggg==
</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
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAL5SURBVDhPfdLfT5tVHMdx/gPvjVcmGpPF6I033pip000X YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAL4SURBVDhPfdLxU9N1HMdx/oN+7/qp7vzF6+qX7vqts9K0
f+A0uGUlk0zjxCzGzbIiDpnbM6iUQiGUFqhd2YbtBo4yYMN2W4FtFR54oMimMhkDpA10fdqn7RlB9jZt vDKzjvIcepx1GZ3XpQ1HJGL6FRbjKwOBDfg2CKMNJRkChps6JB34hcEWWmGIgIODue/23faRI3x229US
tA4y/STfq/M9r5Oc7zfP4R2tdnTI4TqPrJraHlKu9WV2y6q9Q67J+yfNXSORcCRyX0smSabE/9ayJojE znrdvX/6vD+Pz93n/c5R3MMVSrs6J7tUzdL8mHKsLtmpavazamXOP2noHJy/Fw4/1BMJEknxv7WoC+aj
EjS6h2M5IPNyQkvS0D1HdbdC+fnvcY5YcI9/wymlgvqAkY9bbXzeMoC+ZYqlhKDWM6bmgJo2Wc3ojefn cWrb/NEskH45rieo6ZqmoitAyfkfcNyw4hz9mtOBUqp9Zj5usvF5Yz/GxnEW4gLZNaJlgcpmVUvrteen
kHr7cA5baJ84gH34IxqvF2EN7sPYf4R9TScxOG4QUR8CJFKCpt55yntcnFYqsAb3Unt1N8bA+0hXCjAF kXp6cQxZaRs7gH3oI2qv51Pn34e57yj76lsxKTeZ1x4DxJOC+p4ZSrpb+D5QSp1/Lyd/3oXZ9z7SlVws
PqOw7lvKTvxCWE1T6xldD6gpgb1vgcNeJ9/JZZiHdEiX3+Nrfz5f/ZiP5CtGZ67E0e7i5skCfjt3FKXl vs/Iq/qG4uZfmdNSyK7h1YCWFNh7ZznsdvCtWow8YEC6/B5HvNv56uJ2JE8BBrkMpa2FW625/H7uGIHG
FZSGTbosEEsKWi78geTtwRI4Ts1QEeX+dzBcfJPDvgK+/KGEhhMHWfTrUSe6QVsgNnGGwaqt4SxwVxM4 TQRq1hsyQDQhaLxwD8ndjdV3gsqBfEq872D66S0Oe3L58sdCapoPEvYa0ca6QJ8lOtaOr3zzXAa4rwuU
+hex9IxT4nYgde2nyrcXqX8PpZ5PMLUWszRZzspcF4uXjaRDHfx5exCl+UORl5l1NCFw+sJZpM6r8EWF vjDW7lEKnQpS537KPXuR+vZQ5PoES1MBC6ESlqY7CV82kwqe5c87VxmxfShy0rOOxAUOz1wGqXIH+KLU
jmOml5GMm2mu20p0soy19CDpW8VEf/qUG64iFNvO5aDlrSezwHJcYHC/gb59OwdOvYat9DlCjh1Mtm3h wHHLq0jmDTRUbSYSKmYldZXU7QIig59ysyWfgC130W/dti4DLMYEJuebGNu2cuD069iKXiCo7CD03Ubu
bugQa+IaqelCxJ1dJKYMTFm33x+r2fJs9hOrXSNqNC4ITF/K1VCok4Fz+1kc1v99WYeY3Ul8spRbTa/j Bw+xIq6RnMhD3N1JfNxE6NTWhyOVG5/PfGKF44YWiQl8E5eyNRDsoP/cfsJDxr8vGxBTHxALFXG7/g0U
cJ7VclMwZYH0OsB/qZKpCwdZSwcQM0Xcu7MbNXSIX62vMj87g9n9wBjrPfJsNBYnrgniyXuM+V383GNg 5YyenYIlA6RWAd5LZYxfOMhKyoeYzOfB3V1owUP8VvcaM1OTyM5HxljtUqci0RgxXRBLPGDE28Iv3SZG
vNfMfPBdUjO7iColTDe/zfLC7yxlV3nk31W2d8pVtjOjA5nlyMjuypcgcZubth34DY8yZN2G79gLa61O e2Rm/O+SnNxJJFDIRMPbLM7+wUJmlQf/XWV7h1puax/uTy9HWnaWvQLxO9yy7cBrepKBui14jr+00qS4
r5Y5rz87qto7ZXMO2JgjHzy1unKtnpVgLcGjmzm95zGtT7/pmY19/5n85x8RUuETqxePb1vtr37xSnfJ 9fR51Zlhzd6hyllgbY7uXre8dK2aJf9J/Mc20Jr3lN5rXP/c2r7/zLYXnxBS3jPLvSe2LPdVvHylq/DZ
049v7HkwfwEdnYukisiEOQAAAABJRU5ErkJggg== p9f2PJq/AD40i0VffXQ/AAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="btnConnect.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="btnConnect.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
@@ -263,15 +263,15 @@
<data name="btnDeleteConnection.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="btnDeleteConnection.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHxSURBVDhP7Y7Ni1JhGMVvXadgWrWzRcS0S0FyFYHgOH4g YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHxSURBVDhP7Y7Ni1JhGMVvXadgWrWzRdQsU5BcRW4cR72E
fgQXQl0NRIr1SqB9zGh34XAXs7gwgRfMVUILsY22mhmlDHTcRGQrbXVnkCKkSIOmtMk88QyMmDj/QQde H8FtoS6iFinWKwn2MaPdheJikIEJvGCuElpcaKOtTKUMdNxEZBut1Z1BipAiDZrSJvPEM5CYOP9BB154
eDjndw4vx/3XWHa7/Qxj7IEkSd1sNgtRFNuMsSuMMa0oiip5lBFD7D9lnU53yu12y6lUCp1OB6qqolqt OOd3Di/H/ddEdrv9GGPsbiKR6OVyOUiS1GGMnWOMaSVJUsmjjBhi/ynrdLojTqdzI51Oo9vtQlVV1Go1
IplM7oqiWGu1WgPy6vU6iCGWOuMBo9F4LRqNfms2m91AILDldDqHoVAIiqIgkUiAbvIoy+VybWKpMzkg xOPxbUmS6q1Wa0heo9EAMcRSZzJgNBovhsPhr+12u+fz+Z4KgjAKBAKQZRmxWAx0k0eZoigdYqkzPbCR
S5LUVxQl7PF45k0m0/dKpYJGo4FarYZCoQDyKCOGWOqMBwwGw+1wONxLp9P3BUEoWyyWA4fDAUEQDh/d TCYHsiwHXS7Xoslk+latVtFsNlGv15HP50EeZcQQS53JgMFguBEMBvuZTOaOKIoVi8WyJwgCRFHcf3ST
5FFGDLHUGQ/o9fpLXq/3vSzLvXw+/7VYLCKTySAWiyEejx/epXWGV9cv/Hm5NIfnjvmDp4unH40HOI7j RxkxxFJnMqDX68+43e53qVSqryjKl0KhgGw2i0gkgmg0un+X1xleXDr1+/nKAp4Ii3uPlo/enwxwHMeb
zWbzZZfLtRcMBvcjkUifMfbL5/Mt+/1+6+Nl6+Ddvavob25g1Crhx7O7eBPRDV9YNXcmR47Vto1Xf25u zeazDodjx+/374ZCoQFj7KfH47ni9XqtDy5bhm9un8eguInx2zK+P76FVyHd6JlVc3N65ECVbLz6o7gJ
AIoXWD0LrC/gi7yILQu/O83OVHlJMxq9LWJSvaQW5E+zM1W28R/2n9wEkloMVjh0Vzi0b/Eo2fmP0+xM yG5g7TiwvoTPG8soWvjtWXauSiua8fh1AdPqx7Ugf5adq4qNf7/78BoQ12K4yqG3yqFznUfZzn+YZedq
7fjPrb2+cfH3p9UFfE7MQQ2eQMWlGW7bTj6cZo/Vjv98vGzl9+jb9KOj8l/xBxqBzigbjwAAAABJRU5E y3si8fLq6V8f15bwKbYA1X8I1QuaUcl2+N4se6C2vCejFSu/Q9+mH/0t/wFGlxos/Pd5kgAAAABJRU5E
rkJggg== rkJggg==
</value> </value>
</data> </data>
@@ -287,18 +287,18 @@
<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
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKYSURBVDhPhZBfSFNRHMfvY0RPEfQSFD2tngKj9WBZmDqX YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAKYSURBVDhPhZBfSFNRHMfvY0RPEfQSFD2tniKh9WBZmM5W
em1t6tJtDV2WSRZGKN1ScV7JyhVZMnuYIJua0aXQ1Oamzj/DtnvuMlKocJ47aMWI9FwrCC4njrqX66Iv es02del1ii7LJAsjtG6pOCdZuiJLZg8TbFMzuiSKlm7m/LPU3XOWkUKF89xBK0ak51pBcDlx1L1cF33h
nJfD9/P9fX8/itpUyFOm5nvNHOi1JMJ9VjnkschzLlMi2FXMBTvOqpO+lBI8Jpvgtsb5HjMIdZ9jwi6j vBy+n+/v+/sxzIZm3flasatAAF3m6Ey3WZl2c4rfyUX97TmCv/WsNuaLK+jmLPBpUUTsLACzHef4gNOk
asapUwW79EzQaQDTncXxgKPQpuTWRSZvwKUsH0UHwDLSAyjVCKJUHYGr+fNwZX/gAc1OOQrj/rvarU14 mXRkafztBt7vMIKJtpyIz55pUXNropPX4TybGML7wBI2ACRXQEkuD6KV9Dm0vNd3n7WN29mI965+cxPR
j5kjk8NRpAIQXQIQ1QsiugHEtesClK4JolQqRH/sm2ynga9Vyyl5CngsiVB3CROJIR2AqG4Dlmo34Wog XSDQyYEQ1gCELwKEq6GErwNp9RpE8lUoyXkw9GPPWAsLPI16Qc0zwG2Oznbk8sEwzgIIV63DcuUGXA4k
oioApVO+Fi3jZfMTSp4K91rksNOoEkTpcnJqcgXSSICokrQYachW+dvyZCVPvXWbZXIwYiQgWJaukDAe XAaQfMLToOdfNaRH1Twz08UpAYdJAyX5UmxqbAXaCCJcSlsM1aRqvE16Rc0zb135Cj0YNVIQLMmXaZiI
oovrfyKqiECpmAT42NytAbOu0kTwiZ7hxTXNRgtUtQ7HkA2IqFyAq+d5ER0fvpnFjDDZW1eY7dRzsx0G 8IW1PwmXBJGcQwM8Nt3mgElnXtT/2MCL0mraegtctgaHsQVIuBiilUJRwkcHb6bwQzdSN68w1WYQplqN
QA4lQKloE6wQxFUrgQFE9LvYzz2vm3J/D9Vlzil5KuAoUE8/1sUn2mmWX/q1V4hJJ0llAUqGCETpBJ5w gB4KIjl7AyyB0koRhQHC7Lvwz139dSd/D1QlT6t5xmfP0E48yoy8aWFt4uKv3TAsH6eVIZKNQYQTKext
6P7MD9Rgf+0J/KIm3ajMoMbbCmwBBx333TkNvM05DNmXvOH6TGb8Hi0vDNbjWNiJl6bv47FmGr8sV+9W zvwz11tBvJXHyIuKRJM6gxltyrD47GzEc+cUGK7X8XRf+gark/nRe+nKfH81CQccZHGimYzUs+RlsXan
ZlB+Vqt+Y9dyoy25CXKsMbtGHryds+JtysOfxlvx1w89+NuiG3/0NuIB6zHZkyoklbirGabJ9hIcnXHg OoPx2vTa11a9MNSgi9JjjVhTlb7buuXhutPk02gj+fqhk3xbcJGPw7Wk13xEcccLiSfhShI31pJLQpN2
+PtuvBx8hOeeWjGbpfr86kLadqU/pZ5XpZvG7Gfw4tAtvDDUgKceFuGBysPfnxkO7lB6/6l+2xGTuywN EnnfQZb8D8n0kyJiS9F87jufsFXtj6vnZYnciPUMWRi4ReYHasj4g2zSW3rw+zPj/m1q7z/VYznEufIT
jzZqcJ9V/cVtTNul9PxX/eVHd3aVHLK7LBnbkn9/ARsspRTlWFT8AAAAAElFTkSuQmCC yKvaNNJdqP3iMiXsUHv+q57iw9vbcw9YneakLbG/v5ifpNsR5bepAAAAAElFTkSuQmCC
</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">
@@ -307,7 +307,7 @@
<data name="btnNew.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="btnNew.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAU1JREFUOE9j+E8GYEAGIIGrzz+AJYrW6IIxCMw6cO1/x/oTYFw2fQsKRjEE2eTp wQAADsEBuJFr7QAAAU1JREFUOE9j+E8GYEAGIIGrzz+AJYrW6IIxCMw6cO1/x/oTYFw2fQsKRjEE2eTp
p73/Tz3pgSz0/+nbryi4c/eb/+EFE8FycANAtk475fV/0nH3//1HXP53HrCHu+TA/b9w/OnbL7ABKIbA p73/Tz3pgSz0/+nbryi4c/eb/+EFE8FycANAtk475fV/0nH3//1HXP53HrCHu+TA/b9w/OnbL7ABKIbA
DJhw1PV/9wGH/y17rP/XbTNHMWD3HQgGGQDDQYnVCANgoGGHxf+qzcbIQnADtt36A8Ybr//BNAAUYCCA DJhw1PV/9wGH/y17rP/XbTNHMWD3HQgGGQDDQYnVCANgoGGHxf+qzcbIQnADtt36A8Ybr//BNAAUYCCA
HIgwZ6NrXn3lN6YBoJBGBzBnwzTCNMMM8AzNBquDGwASxGcrCC+5CMGg2EAxABS3bz5+wzAAm+Z55yAG HIgwZ6NrXn3lN6YBoJBGBzBnwzTCNMMM8AzNBquDGwASxGcrCC+5CMGg2EAxABS3bz5+wzAAm+Z55yAG
@@ -319,7 +319,7 @@
<data name="btnOpen.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="btnOpen.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAhFJREFUOE/dz71rU1EYBvAO7t38E1xaizQfIh1cBHFyEV2sDiJoyCApFBVBO6QN wQAADsEBuJFr7QAAAhFJREFUOE/dz71rU1EYBvAO7t38E1xaizQfIh1cBHFyEV2sDiJoyCApFBVBO6QN
iIGSqCDSKpSKtlb8QBCHEge1rW1iE733Jmlsmhh6g839vud+nHseSapJXFwFX3g45wzv7z1vT8//Ufwr iIGSqCDSKpSKtlb8QBCHEge1rW1iE733Jmlsmhh6g839vud+nHseSapJXFwFX3g45wzv7z1vT8//Ufwr
X6acGna23p3tyhmn8Hpoo/h0YO/GE1/vH3nr611cPLynDQgvghZjHgAGsGZ27y6po/o+ZFc+hKzfqa1e X6acGna23p3tyhmn8Hpoo/h0YO/GE1/vH3nr611cPLynDQgvghZjHgAGsGZ27y6po/o+ZFc+hKzfqa1e
JtWlkTL/fPBkB3gWkBklsKpxGKVJGJvTMMvzYK4E5ulgVAOjyu7bbYBR2xYWgrk2kH8cUD1HgVFKQi8m JtWlkTL/fPBkB3gWkBklsKpxGKVJGJvTMMvzYK4E5ulgVAOjyu7bbYBR2xYWgrk2kH8cUD1HgVFKQi8m
@@ -334,7 +334,7 @@
<data name="btnReopen.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="btnReopen.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAUZJREFUOE+lkzFLw0AYhvt3HP0DLqWDdCuECjooBRdBXJwEZ3VRBykUBCfRpSi4 wQAADsEBuJFr7QAAAUZJREFUOE+lkzFLw0AYhvt3HP0DLqWDdCuECjooBRdBXJwEZ3VRBykUBCfRpSi4
uXTSIYNiRSgFBxU6qHSoCMHiyfPJm9zFgKAHT0Mu9z53+fK15P4wSv5gojcYuvbNo3EU37tW587YaF8a uXTSIYNiRSgFBxU6qHSoCMHiyfPJm9zFgKAHT0Mu9z53+fK15P4wSv5gojcYuvbNo3EU37tW587YaF8a
q3unAYGEG4JInl7e3HKrFsCc5rlunj+7+spuJpFAiw4HiynN7mwqamxX3OsoMUEg4Ydjs0Ds9+cNBFud q3unAYGEG4JInl7e3HKrFsCc5rlunj+7+spuJpFAiw4HiynN7mwqamxX3OsoMUEg4Ydjs0Ds9+cNBFud
yK4SwOg9cbWFtUzA+7Lg4uHTkGgnrqdwf9YbG4UCJiWQhJ0JcwoJjrsfocA/+m8QlmA6WsoEfuH8owoF yK4SwOg9cbWFtUzA+7Lg4uHTkGgnrqdwf9YbG4UCJiWQhJ0JcwoJjrsfocA/+m8QlmA6WsoEfuH8owoF
@@ -369,7 +369,7 @@
<data name="btnSaveAs.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="btnSaveAs.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAyZJREFUOE9tku1PW1Ucx+8r/Tt86wvRxRjjEmPMULeZNAsQXczipoCdDhxZlg0Z wQAADsEBuJFr7QAAAyZJREFUOE9tku1PW1Ucx+8r/Tt86wvRxRjjEmPMULeZNAsQXczipoCdDhxZlg0Z
m7CisBYfthcDXLrBlJktUbIBGQ+hAn2cq3uga3lqBxQuUCil9OHc9t7bj2mjy3B+k29OTnK+n9/35Byp m7CisBYfthcDXLrBlJktUbIBGQ+hAn2cq3uga3lqBxQuUCil9OHc9t7bj2mjy3B+k29OTnK+n9/35Byp
+NTowffN7iGDxb1hsLgxWNzC0OoWBotHGMwusafF4dt5fPzHF8uuPyf9n3Y32cfXtsR6NCG0aELhX69v +NTowffN7iGDxb1hsLgxWNzC0OoWBotHGMwusafF4dt5fPzHF8uuPyf9n3Y32cfXtsR6NCG0aELhX69v
5S0IyFvU31yg/MJ06r2q/uf/m5f2Njsi8VRGc81l8SyouOdUHI8zjM4o3PYnUVTonUxT3zPPp+en089A 5S0IyFvU31yg/MJ06r2q/uf/m5f2Njsi8VRGc81l8SyouOdUHI8zjM4o3PYnUVTonUxT3zPPp+en089A

View File

@@ -78,7 +78,12 @@
<Compile Include="Models\IMovableAGV.cs" /> <Compile Include="Models\IMovableAGV.cs" />
<Compile Include="Models\VirtualAGV.cs" /> <Compile Include="Models\VirtualAGV.cs" />
<Compile Include="Models\MapLoader.cs" /> <Compile Include="Models\MapLoader.cs" />
<Compile Include="Models\MapMagnet.cs" />
<Compile Include="Models\MapMark.cs" />
<Compile Include="Models\MapNode.cs" /> <Compile Include="Models\MapNode.cs" />
<Compile Include="Models\NodeBase.cs" />
<Compile Include="Models\MapLabel.cs" />
<Compile Include="Models\MapImage.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" />

View File

@@ -18,8 +18,8 @@ namespace AGVNavigationCore.Controls
// 이동 경로 정보 추가 // 이동 경로 정보 추가
Point? PrevPosition { get; } Point? PrevPosition { get; }
string CurrentNodeId { get; } MapNode CurrentNode { get; }
string PrevNodeId { get; } MapNode PrevNode { get; }
DockingDirection DockingDirection { get; } DockingDirection DockingDirection { get; }
} }

View File

@@ -1,8 +1,10 @@
using AGVNavigationCore.Models;
using System; using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
using AGVNavigationCore.Models; using System.Xml.Linq;
namespace AGVNavigationCore.Controls namespace AGVNavigationCore.Controls
{ {
@@ -15,7 +17,7 @@ namespace AGVNavigationCore.Controls
Focus(); // 포커스 설정 Focus(); // 포커스 설정
var worldPoint = ScreenToWorld(e.Location); var worldPoint = ScreenToWorld(e.Location);
var hitNode = GetNodeAt(worldPoint); var hitNode = GetItemAt(worldPoint);
// 에뮬레이터 모드 처리 // 에뮬레이터 모드 처리
if (_canvasMode == CanvasMode.Emulator) if (_canvasMode == CanvasMode.Emulator)
@@ -48,7 +50,11 @@ namespace AGVNavigationCore.Controls
// 마지막 선택된 노드 업데이트 (단일 참조용) // 마지막 선택된 노드 업데이트 (단일 참조용)
_selectedNode = _selectedNodes.Count > 0 ? _selectedNodes[_selectedNodes.Count - 1] : null; _selectedNode = _selectedNodes.Count > 0 ? _selectedNodes[_selectedNodes.Count - 1] : null;
// 다중 선택 이벤트 발생 (OnNodesSelected에서 단일/다중 구분 처리) // 단일/다중 선택 이벤트 발생
if (_selectedNodes.Count == 1)
{
NodeSelect?.Invoke(this, _selectedNodes[0], e);
}
NodesSelected?.Invoke(this, _selectedNodes); NodesSelected?.Invoke(this, _selectedNodes);
Invalidate(); Invalidate();
} }
@@ -59,7 +65,8 @@ namespace AGVNavigationCore.Controls
_selectedNodes.Clear(); _selectedNodes.Clear();
_selectedNodes.Add(hitNode); _selectedNodes.Add(hitNode);
// NodesSelected 이벤트만 발생 (OnNodesSelected에서 단일/다중 구분 처리) // 단일/다중 선택 이벤트 발생
NodeSelect?.Invoke(this, hitNode, e);
NodesSelected?.Invoke(this, _selectedNodes); NodesSelected?.Invoke(this, _selectedNodes);
Invalidate(); Invalidate();
} }
@@ -94,7 +101,7 @@ namespace AGVNavigationCore.Controls
break; break;
case EditMode.Connect: case EditMode.Connect:
HandleConnectClick(hitNode); HandleConnectClick(hitNode as MapNode);
break; break;
case EditMode.Delete: case EditMode.Delete:
@@ -110,53 +117,51 @@ namespace AGVNavigationCore.Controls
private void UnifiedAGVCanvas_MouseDoubleClick(object sender, MouseEventArgs e) private void UnifiedAGVCanvas_MouseDoubleClick(object sender, MouseEventArgs e)
{ {
var worldPoint = ScreenToWorld(e.Location); var worldPoint = ScreenToWorld(e.Location);
var hitNode = GetNodeAt(worldPoint); var hitNode = GetItemAt(worldPoint);
if (hitNode != null) // 가동 모드에서는 더블클릭 편집 방지
if (_canvasMode == CanvasMode.Run) return;
if (hitNode == null) return;
if (hitNode.Type == NodeType.Normal)
{ {
// 노드 타입별 더블클릭 액션 HandleNormalNodeDoubleClick(hitNode as MapNode);
switch (hitNode.Type)
{
case NodeType.Normal:
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
case NodeType.Charging:
HandleNormalNodeDoubleClick(hitNode);
break;
case NodeType.Label:
HandleLabelNodeDoubleClick(hitNode);
break;
case NodeType.Image:
HandleImageNodeDoubleClick(hitNode);
break;
default:
// 기본 동작: 노드 선택 이벤트 발생
_selectedNode = hitNode;
_selectedNodes.Clear();
_selectedNodes.Add(hitNode);
NodesSelected?.Invoke(this, _selectedNodes);
break;
} }
else if (hitNode.Type == NodeType.Label)
{
HandleLabelDoubleClick(hitNode as MapLabel);
}
else if (hitNode.Type == NodeType.Image)
{
HandleImageDoubleClick(hitNode as MapImage);
}
else if (hitNode.Type == NodeType.Mark)
{
HandleMarkDoubleClick(hitNode as MapMark);
}
else if (hitNode.Type == NodeType.Magnet)
{
HandleMagnetDoubleClick(hitNode as MapMagnet);
} }
} }
private void HandleNormalNodeDoubleClick(MapNode node) private void HandleNormalNodeDoubleClick(MapNode node)
{ {
// RFID 입력창 표시 // RFID 입력창 표시
string currentRfid = node.RfidId ?? ""; var currentRfid = node.RfidId;
string newRfid = Microsoft.VisualBasic.Interaction.InputBox( string newRfid = Microsoft.VisualBasic.Interaction.InputBox(
$"노드 '{node.Name}'의 RFID를 입력하세요:", $"노드 '{node.RfidId}[{node.Id}]'의 RFID를 입력하세요:",
"RFID 설정", "RFID 설정",
currentRfid); currentRfid.ToString());
if (!string.IsNullOrWhiteSpace(newRfid) && newRfid != currentRfid) if (ushort.TryParse(newRfid, out ushort newrfidvalue) == false) return;
if (newrfidvalue < 1) return;
if (newrfidvalue != currentRfid)
{ {
node.RfidId = newRfid.Trim(); node.RfidId = newrfidvalue;
MapChanged?.Invoke(this, EventArgs.Empty); MapChanged?.Invoke(this, EventArgs.Empty);
Invalidate(); Invalidate();
} }
@@ -167,11 +172,19 @@ namespace AGVNavigationCore.Controls
_selectedNodes.Add(node); _selectedNodes.Add(node);
NodesSelected?.Invoke(this, _selectedNodes); NodesSelected?.Invoke(this, _selectedNodes);
} }
private void HandleMarkDoubleClick(MapMark label)
{
//TODO:
}
private void HandleMagnetDoubleClick(MapMagnet label)
{
//TODO:
}
private void HandleLabelNodeDoubleClick(MapNode node) private void HandleLabelDoubleClick(MapLabel label)
{ {
// 라벨 텍스트 입력창 표시 // 라벨 텍스트 입력창 표시
string currentText = node.LabelText ?? "새 라벨"; string currentText = label.Text ?? "새 라벨";
string newText = Microsoft.VisualBasic.Interaction.InputBox( string newText = Microsoft.VisualBasic.Interaction.InputBox(
"라벨 텍스트를 입력하세요:", "라벨 텍스트를 입력하세요:",
"라벨 편집", "라벨 편집",
@@ -179,28 +192,24 @@ namespace AGVNavigationCore.Controls
if (!string.IsNullOrWhiteSpace(newText) && newText != currentText) if (!string.IsNullOrWhiteSpace(newText) && newText != currentText)
{ {
node.LabelText = newText.Trim(); label.Text = newText.Trim();
MapChanged?.Invoke(this, EventArgs.Empty); MapChanged?.Invoke(this, EventArgs.Empty);
Invalidate(); Invalidate();
} }
// 더블클릭 시 해당 노드만 선택 (다중 선택 해제) _selectedNode = label;
_selectedNode = node; LabelDoubleClicked?.Invoke(this, label);
_selectedNodes.Clear(); Invalidate();
_selectedNodes.Add(node);
NodesSelected?.Invoke(this, _selectedNodes);
} }
private void HandleImageNodeDoubleClick(MapNode node) private void HandleImageDoubleClick(MapImage image)
{ {
// 더블클릭 시 해당 노드만 선택 (다중 선택 해제) _selectedNode = image;
_selectedNode = node;
_selectedNodes.Clear();
_selectedNodes.Add(node);
NodesSelected?.Invoke(this, _selectedNodes);
// 이미지 편집 이벤트 발생 (MainForm에서 처리) // 이미지 편집 이벤트 발생 (MainForm에서 처리)
ImageNodeDoubleClicked?.Invoke(this, node); ImageDoubleClicked?.Invoke(this, image);
Invalidate();
} }
private void UnifiedAGVCanvas_MouseDown(object sender, MouseEventArgs e) private void UnifiedAGVCanvas_MouseDown(object sender, MouseEventArgs e)
@@ -211,24 +220,23 @@ namespace AGVNavigationCore.Controls
{ {
if (_editMode == EditMode.Move) if (_editMode == EditMode.Move)
{ {
var hitNode = GetNodeAt(worldPoint); // 1. 노드 선택 확인
var hitNode = GetItemAt(worldPoint);
if (hitNode != null) if (hitNode != null)
{ {
_isDragging = true; _isDragging = true;
_isPanning = false; // 🔥 팬 모드 비활성화 - 중요! _isPanning = false;
_selectedNode = hitNode; _selectedNode = hitNode;
_dragStartPosition = hitNode.Position; // 원래 위치 저장 (고스트용) _dragStartPosition = hitNode.Position;
_dragOffset = new Point( _dragOffset = new Point(worldPoint.X - hitNode.Position.X, worldPoint.Y - hitNode.Position.Y);
worldPoint.X - hitNode.Position.X, _mouseMoveCounter = 0;
worldPoint.Y - hitNode.Position.Y
);
_mouseMoveCounter = 0; // 디버그: 카운터 리셋
Cursor = Cursors.SizeAll; Cursor = Cursors.SizeAll;
Capture = true; // 🔥 마우스 캡처 활성화 - 이게 핵심! Capture = true;
//System.Diagnostics.Debug.WriteLine($"MouseDown: 드래그 시작! Capture={Capture}, isDragging={_isDragging}, isPanning={_isPanning}, Node={hitNode.NodeId}");
Invalidate(); Invalidate();
return; return;
} }
} }
// 팬 시작 (좌클릭 - 모드에 따라) // 팬 시작 (좌클릭 - 모드에 따라)
@@ -250,7 +258,9 @@ namespace AGVNavigationCore.Controls
// 컨텍스트 메뉴 (편집 모드에서만) // 컨텍스트 메뉴 (편집 모드에서만)
if (_canvasMode == CanvasMode.Edit) if (_canvasMode == CanvasMode.Edit)
{ {
var hitNode = GetNodeAt(worldPoint); var hitNode = GetItemAt(worldPoint);
// TODO: 라벨/이미지에 대한 컨텍스트 메뉴도 지원하려면 여기서 hitLabel/hitImage 확인해서 전달
// 현재는 ShowContextMenu가 MapNode만 받으므로 노드만 처리
ShowContextMenu(e.Location, hitNode); ShowContextMenu(e.Location, hitNode);
} }
} }
@@ -266,9 +276,12 @@ namespace AGVNavigationCore.Controls
_mouseMoveCounter++; _mouseMoveCounter++;
} }
// 호버 노드 업데이트 // 호버 업데이트
var newHoveredNode = GetNodeAt(worldPoint); var newHoveredNode = GetItemAt(worldPoint);
if (newHoveredNode != _hoveredNode)
bool hoverChanged = (newHoveredNode != _hoveredNode);
if (hoverChanged)
{ {
_hoveredNode = newHoveredNode; _hoveredNode = newHoveredNode;
Invalidate(); Invalidate();
@@ -291,10 +304,7 @@ namespace AGVNavigationCore.Controls
} }
else if (_isDragging && _canvasMode == CanvasMode.Edit) else if (_isDragging && _canvasMode == CanvasMode.Edit)
{ {
// 노드 드래그 // 드래그 위치 계산
if (_selectedNode != null)
{
var oldPosition = _selectedNode.Position;
var newPosition = new Point( var newPosition = new Point(
worldPoint.X - _dragOffset.X, worldPoint.X - _dragOffset.X,
worldPoint.Y - _dragOffset.Y worldPoint.Y - _dragOffset.Y
@@ -307,8 +317,18 @@ namespace AGVNavigationCore.Controls
newPosition.Y = (newPosition.Y / GRID_SIZE) * GRID_SIZE; newPosition.Y = (newPosition.Y / GRID_SIZE) * GRID_SIZE;
} }
bool moved = false;
// 노드 드래그
if (_selectedNode != null)
{
_selectedNode.Position = newPosition; _selectedNode.Position = newPosition;
NodeMoved?.Invoke(this, _selectedNode); NodeMoved?.Invoke(this, _selectedNode);
moved = true;
}
if (moved)
{
MapChanged?.Invoke(this, EventArgs.Empty); MapChanged?.Invoke(this, EventArgs.Empty);
Invalidate(); Invalidate();
Update(); // 🔥 즉시 Paint 이벤트 처리하여 화면 업데이트 Update(); // 🔥 즉시 Paint 이벤트 처리하여 화면 업데이트
@@ -409,10 +429,21 @@ namespace AGVNavigationCore.Controls
); );
} }
private MapNode GetNodeAt(Point worldPoint) private NodeBase GetItemAt(Point worldPoint)
{ {
if (_nodes == null) return null; if (_labels != null)
{
// 역순으로 검사하여 위에 그려진 노드부터 확인
for (int i = _labels.Count - 1; i >= 0; i--)
{
var node = _labels[i];
if (IsPointInNode(worldPoint, node))
return node;
}
}
if (_nodes != null)
{
// 역순으로 검사하여 위에 그려진 노드부터 확인 // 역순으로 검사하여 위에 그려진 노드부터 확인
for (int i = _nodes.Count - 1; i >= 0; i--) for (int i = _nodes.Count - 1; i >= 0; i--)
{ {
@@ -420,40 +451,55 @@ namespace AGVNavigationCore.Controls
if (IsPointInNode(worldPoint, node)) if (IsPointInNode(worldPoint, node))
return node; return node;
} }
}
if (_images != null)
{
// 역순으로 검사하여 위에 그려진 노드부터 확인
for (int i = _images.Count - 1; i >= 0; i--)
{
var node = _images[i];
if (IsPointInNode(worldPoint, node))
return node;
}
}
return null; return null;
} }
private bool IsPointInNode(Point point, MapNode node) private bool IsPointInNode(Point point, NodeBase node)
{ {
switch (node.Type) if (node is MapLabel label)
{ {
case NodeType.Label: return IsPointInLabel(point, label);
return IsPointInLabelNode(point, node);
case NodeType.Image:
return IsPointInImageNode(point, node);
default:
return IsPointInCircularNode(point, node);
} }
if (node is MapImage image)
{
return IsPointInImage(point, image);
}
// 라벨과 이미지는 별도 리스트로 관리되므로 여기서 처리하지 않음
// 하지만 혹시 모를 하위 호환성을 위해 타입 체크는 유지하되,
// 실제 로직은 CircularNode 등으로 분기
return IsPointInCircularNode(point, node as MapNode);
} }
private bool IsPointInCircularNode(Point point, MapNode node) private bool IsPointInCircularNode(Point point, MapNode node)
{ {
switch (node.Type) switch (node.StationType)
{ {
case NodeType.Loader: case StationType.Loader:
case NodeType.UnLoader: case StationType.UnLoader:
case NodeType.Clearner: case StationType.Clearner:
case NodeType.Buffer: case StationType.Buffer:
return IsPointInPentagon(point, node); return IsPointInPentagon(point, node);
case NodeType.Charging: case StationType.Charger:
return IsPointInTriangle(point, node); return IsPointInTriangle(point, node);
default: default:
return IsPointInCircle(point, node); return IsPointInCircle(point, node);
} }
} }
private bool IsPointInCircle(Point point, MapNode node) private bool IsPointInCircle(Point point, NodeBase node)
{ {
// 화면에서 최소 20픽셀 정도의 히트 영역을 확보하되, 노드 크기보다 작아지지 않게 함 // 화면에서 최소 20픽셀 정도의 히트 영역을 확보하되, 노드 크기보다 작아지지 않게 함
var minHitRadiusInScreen = 20; var minHitRadiusInScreen = 20;
@@ -466,7 +512,7 @@ namespace AGVNavigationCore.Controls
return distance <= hitRadius; return distance <= hitRadius;
} }
private bool IsPointInPentagon(Point point, MapNode node) private bool IsPointInPentagon(Point point, NodeBase node)
{ {
// 화면에서 최소 20픽셀 정도의 히트 영역을 확보 // 화면에서 최소 20픽셀 정도의 히트 영역을 확보
var minHitRadiusInScreen = 20; var minHitRadiusInScreen = 20;
@@ -487,7 +533,7 @@ namespace AGVNavigationCore.Controls
return IsPointInPolygon(point, points); return IsPointInPolygon(point, points);
} }
private bool IsPointInTriangle(Point point, MapNode node) private bool IsPointInTriangle(Point point, NodeBase node)
{ {
// 화면에서 최소 20픽셀 정도의 히트 영역을 확보하되, 노드 크기보다 작아지지 않게 함 // 화면에서 최소 20픽셀 정도의 히트 영역을 확보하되, 노드 크기보다 작아지지 않게 함
var minHitRadiusInScreen = 20; var minHitRadiusInScreen = 20;
@@ -532,38 +578,68 @@ namespace AGVNavigationCore.Controls
return inside; return inside;
} }
private bool IsPointInLabelNode(Point point, MapNode node) private bool IsPointInLabel(Point point, MapLabel label)
{ {
var text = string.IsNullOrEmpty(node.LabelText) ? node.NodeId : node.LabelText; var text = string.IsNullOrEmpty(label.Text) ? label.Id : label.Text;
// 임시 Graphics로 텍스트 크기 측정 // Graphics 객체 임시 생성 (Using CreateGraphics is faster than new Bitmap)
using (var tempBitmap = new Bitmap(1, 1)) using (var g = this.CreateGraphics())
using (var tempGraphics = Graphics.FromImage(tempBitmap))
{ {
var font = new Font(node.FontFamily, node.FontSize, node.FontStyle); // Font 생성 로직: 사용자 정의 폰트가 있으면 생성, 없으면 기본 폰트 사용 (Dispose 주의)
var textSize = tempGraphics.MeasureString(text, font); Font fontToUse = null;
bool shouldDisposeFont = false;
try
{
if (string.IsNullOrEmpty(label.FontFamily) || label.FontSize <= 0)
{
fontToUse = this.Font;
shouldDisposeFont = false; // 컨트롤 폰트는 Dispose하면 안됨
}
else
{
try
{
fontToUse = new Font(label.FontFamily, label.FontSize, label.FontStyle);
shouldDisposeFont = true;
}
catch
{
fontToUse = this.Font;
shouldDisposeFont = false;
}
}
var textSize = g.MeasureString(text, fontToUse);
var textRect = new Rectangle( var textRect = new Rectangle(
(int)(node.Position.X - textSize.Width / 2), (int)(label.Position.X - textSize.Width / 2),
(int)(node.Position.Y - textSize.Height / 2), (int)(label.Position.Y - textSize.Height / 2),
(int)textSize.Width, (int)textSize.Width,
(int)textSize.Height (int)textSize.Height
); );
font.Dispose();
return textRect.Contains(point); return textRect.Contains(point);
} }
finally
{
if (shouldDisposeFont && fontToUse != null)
{
fontToUse.Dispose();
}
}
}
} }
private bool IsPointInImageNode(Point point, MapNode node) private bool IsPointInImage(Point point, MapImage image)
{ {
var displaySize = node.GetDisplaySize(); var displaySize = image.GetDisplaySize();
if (displaySize.IsEmpty) if (displaySize.IsEmpty)
displaySize = new Size(50, 50); // 기본 크기 displaySize = new Size(50, 50); // 기본 크기
var imageRect = new Rectangle( var imageRect = new Rectangle(
node.Position.X - displaySize.Width / 2, image.Position.X - displaySize.Width / 2,
node.Position.Y - displaySize.Height / 2, image.Position.Y - displaySize.Height / 2,
displaySize.Width, displaySize.Width,
displaySize.Height displaySize.Height
); );
@@ -571,6 +647,32 @@ namespace AGVNavigationCore.Controls
return imageRect.Contains(point); return imageRect.Contains(point);
} }
//private MapLabel GetLabelAt(Point worldPoint)
//{
// if (_labels == null) return null;
// // 역순으로 검사
// for (int i = _labels.Count - 1; i >= 0; i--)
// {
// var label = _labels[i];
// if (IsPointInLabel(worldPoint, label))
// return label;
// }
// return null;
//}
//private MapImage GetImageAt(Point worldPoint)
//{
// if (_images == null) return null;
// // 역순으로 검사
// for (int i = _images.Count - 1; i >= 0; i--)
// {
// var image = _images[i];
// if (IsPointInImage(worldPoint, image))
// return image;
// }
// return null;
//}
private IAGV GetAGVAt(Point worldPoint) private IAGV GetAGVAt(Point worldPoint)
{ {
if (_agvList == null) return null; if (_agvList == null) return null;
@@ -590,7 +692,7 @@ namespace AGVNavigationCore.Controls
}); });
} }
private void HandleSelectClick(MapNode hitNode, Point worldPoint) private void HandleSelectClick(NodeBase hitNode, Point worldPoint)
{ {
if (hitNode != null) if (hitNode != null)
{ {
@@ -605,8 +707,8 @@ namespace AGVNavigationCore.Controls
{ {
// 연결선을 클릭했을 때 삭제 확인 // 연결선을 클릭했을 때 삭제 확인
var (fromNode, toNode) = connection.Value; var (fromNode, toNode) = connection.Value;
string fromDisplay = !string.IsNullOrEmpty(fromNode.RfidId) ? fromNode.RfidId : fromNode.NodeId; string fromDisplay = fromNode.HasRfid() ? fromNode.RfidId.ToString("0000") : fromNode.Id;
string toDisplay = !string.IsNullOrEmpty(toNode.RfidId) ? toNode.RfidId : toNode.NodeId; string toDisplay = toNode.HasRfid() ? toNode.RfidId.ToString("0000") : toNode.Id;
var result = MessageBox.Show( var result = MessageBox.Show(
$"연결을 삭제하시겠습니까?\n\n{fromDisplay} ↔ {toDisplay}", $"연결을 삭제하시겠습니까?\n\n{fromDisplay} ↔ {toDisplay}",
@@ -617,13 +719,13 @@ namespace AGVNavigationCore.Controls
if (result == DialogResult.Yes) if (result == DialogResult.Yes)
{ {
// 단일 연결 삭제 (어느 방향에 저장되어 있는지 확인 후 삭제) // 단일 연결 삭제 (어느 방향에 저장되어 있는지 확인 후 삭제)
if (fromNode.ConnectedNodes.Contains(toNode.NodeId)) if (fromNode.ConnectedNodes.Contains(toNode.Id))
{ {
fromNode.RemoveConnection(toNode.NodeId); fromNode.RemoveConnection(toNode.Id);
} }
else if (toNode.ConnectedNodes.Contains(fromNode.NodeId)) else if (toNode.ConnectedNodes.Contains(fromNode.Id))
{ {
toNode.RemoveConnection(fromNode.NodeId); toNode.RemoveConnection(fromNode.Id);
} }
// 이벤트 발생 // 이벤트 발생
@@ -660,9 +762,8 @@ namespace AGVNavigationCore.Controls
var newNode = new MapNode var newNode = new MapNode
{ {
NodeId = newNodeId, Id = newNodeId,
Position = worldPoint, Position = worldPoint
Type = NodeType.Normal
}; };
_nodes.Add(newNode); _nodes.Add(newNode);
@@ -681,20 +782,22 @@ namespace AGVNavigationCore.Controls
worldPoint.Y = (worldPoint.Y / GRID_SIZE) * GRID_SIZE; worldPoint.Y = (worldPoint.Y / GRID_SIZE) * GRID_SIZE;
} }
// 고유한 NodeId 생성 // 고유한 NodeId 생성 (라벨도 ID 공유 권장)
string newNodeId = GenerateUniqueNodeId(); string newNodeId = GenerateUniqueNodeId();
var newNode = new MapNode var newLabel = new MapLabel
{ {
NodeId = newNodeId, Id = newNodeId,
Position = worldPoint, Position = worldPoint,
Type = NodeType.Label, Text = "New Label",
Name = "새 라벨" FontSize = 10,
FontFamily = "Arial"
}; };
_nodes.Add(newNode); if (_labels == null) _labels = new List<MapLabel>();
_labels.Add(newLabel);
NodeAdded?.Invoke(this, newNode); //NodeAdded?.Invoke(this, newNode); // TODO: 라벨 추가 이벤트 필요?
MapChanged?.Invoke(this, EventArgs.Empty); MapChanged?.Invoke(this, EventArgs.Empty);
Invalidate(); Invalidate();
} }
@@ -711,17 +814,17 @@ namespace AGVNavigationCore.Controls
// 고유한 NodeId 생성 // 고유한 NodeId 생성
string newNodeId = GenerateUniqueNodeId(); string newNodeId = GenerateUniqueNodeId();
var newNode = new MapNode var newImage = new MapImage
{ {
NodeId = newNodeId, Id = newNodeId,
Position = worldPoint, Position = worldPoint,
Type = NodeType.Image, Name = "New Image"
Name = "새 이미지"
}; };
_nodes.Add(newNode); if (_images == null) _images = new List<MapImage>();
_images.Add(newImage);
NodeAdded?.Invoke(this, newNode); //NodeAdded?.Invoke(this, newNode); // TODO: 이미지 추가 이벤트 필요?
MapChanged?.Invoke(this, EventArgs.Empty); MapChanged?.Invoke(this, EventArgs.Empty);
Invalidate(); Invalidate();
} }
@@ -739,7 +842,9 @@ namespace AGVNavigationCore.Controls
nodeId = $"N{counter:D3}"; nodeId = $"N{counter:D3}";
counter++; counter++;
} }
while (_nodes.Any(n => n.NodeId == nodeId)); while (_nodes.Any(n => n.Id == nodeId) ||
(_labels != null && _labels.Any(l => l.Id == nodeId)) ||
(_images != null && _images.Any(i => i.Id == nodeId)));
_nodeCounter = counter; _nodeCounter = counter;
return nodeId; return nodeId;
@@ -776,7 +881,7 @@ namespace AGVNavigationCore.Controls
// 연결된 모든 연결선도 제거 // 연결된 모든 연결선도 제거
foreach (var node in _nodes) foreach (var node in _nodes)
{ {
node.RemoveConnection(hitNode.NodeId); node.RemoveConnection(hitNode.Id);
} }
_nodes.Remove(hitNode); _nodes.Remove(hitNode);
@@ -792,13 +897,13 @@ namespace AGVNavigationCore.Controls
private void CreateConnection(MapNode fromNode, MapNode toNode) private void CreateConnection(MapNode fromNode, MapNode toNode)
{ {
// 중복 연결 체크 (양방향) // 중복 연결 체크 (양방향)
if (fromNode.ConnectedNodes.Contains(toNode.NodeId) || if (fromNode.ConnectedNodes.Contains(toNode.Id) ||
toNode.ConnectedNodes.Contains(fromNode.NodeId)) toNode.ConnectedNodes.Contains(fromNode.Id))
return; return;
// 양방향 연결 생성 (AGV가 양쪽 방향으로 이동 가능하도록) // 양방향 연결 생성 (AGV가 양쪽 방향으로 이동 가능하도록)
fromNode.AddConnection(toNode.NodeId); fromNode.AddConnection(toNode.Id);
toNode.AddConnection(fromNode.NodeId); toNode.AddConnection(fromNode.Id);
MapChanged?.Invoke(this, EventArgs.Empty); MapChanged?.Invoke(this, EventArgs.Empty);
} }
@@ -810,20 +915,26 @@ namespace AGVNavigationCore.Controls
return (float)Math.Sqrt(dx * dx + dy * dy); return (float)Math.Sqrt(dx * dx + dy * dy);
} }
private void ShowContextMenu(Point location, MapNode hitNode) private void ShowContextMenu(Point location, NodeBase hitItem)
{ {
_contextMenu.Items.Clear(); _contextMenu.Items.Clear();
if (hitNode != null) if (hitItem != null)
{ {
_contextMenu.Items.Add("노드 속성...", null, (s, e) => string typeName = "항목";
if (hitItem is MapNode) typeName = "노드";
else if (hitItem is MapLabel) typeName = "라벨";
else if (hitItem is MapImage) typeName = "이미지";
_contextMenu.Items.Add($"{typeName} 속성...", null, (s, e) =>
{ {
_selectedNode = hitNode; _selectedNode = hitItem;
_selectedNodes.Clear(); _selectedNodes.Clear();
_selectedNodes.Add(hitNode); _selectedNodes.Add(hitItem);
NodesSelected?.Invoke(this, _selectedNodes); NodesSelected?.Invoke(this, _selectedNodes);
Invalidate();
}); });
_contextMenu.Items.Add("노드 삭제", null, (s, e) => HandleDeleteClick(hitNode)); _contextMenu.Items.Add($"{typeName} 삭제", null, (s, e) => HandleDeleteClick(hitItem));
_contextMenu.Items.Add("-"); _contextMenu.Items.Add("-");
} }
@@ -848,13 +959,13 @@ namespace AGVNavigationCore.Controls
var (fromNode, toNode) = connection.Value; var (fromNode, toNode) = connection.Value;
// 단일 연결 삭제 (어느 방향에 저장되어 있는지 확인 후 삭제) // 단일 연결 삭제 (어느 방향에 저장되어 있는지 확인 후 삭제)
if (fromNode.ConnectedNodes.Contains(toNode.NodeId)) if (fromNode.ConnectedNodes.Contains(toNode.Id))
{ {
fromNode.RemoveConnection(toNode.NodeId); fromNode.RemoveConnection(toNode.Id);
} }
else if (toNode.ConnectedNodes.Contains(fromNode.NodeId)) else if (toNode.ConnectedNodes.Contains(fromNode.Id))
{ {
toNode.RemoveConnection(fromNode.NodeId); toNode.RemoveConnection(fromNode.Id);
} }
// 이벤트 발생 // 이벤트 발생
@@ -867,13 +978,14 @@ namespace AGVNavigationCore.Controls
private (MapNode From, MapNode To)? GetConnectionAt(Point worldPoint) private (MapNode From, MapNode To)? GetConnectionAt(Point worldPoint)
{ {
const int CONNECTION_HIT_TOLERANCE = 10; const int CONNECTION_HIT_TOLERANCE = 10;
if (_nodes == null) return null;
// 모든 연결선을 확인하여 클릭한 위치와 가장 가까운 연결선 찾기 // 모든 연결선을 확인하여 클릭한 위치와 가장 가까운 연결선 찾기
foreach (var fromNode in _nodes) foreach (var fromNode in _nodes)
{ {
foreach (var toNodeId in fromNode.ConnectedNodes) foreach (var toNodeId in fromNode.ConnectedNodes)
{ {
var toNode = _nodes.FirstOrDefault(n => n.NodeId == toNodeId); var toNode = _nodes.FirstOrDefault(n => n.Id == toNodeId);
if (toNode != null) if (toNode != null)
{ {
// 연결선과 클릭 위치 간의 거리 계산 // 연결선과 클릭 위치 간의 거리 계산
@@ -889,6 +1001,49 @@ namespace AGVNavigationCore.Controls
return null; return null;
} }
private void HandleDeleteClick(NodeBase item)
{
if (item == null) return;
if (item is MapNode hitNode)
{
// 연결된 모든 연결선도 제거
foreach (var node in _nodes)
{
node.RemoveConnection(hitNode.Id);
}
_nodes.Remove(hitNode);
if (_selectedNode == hitNode)
_selectedNode = null;
NodeDeleted?.Invoke(this, hitNode);
}
else if (item is MapLabel label)
{
if (_labels != null) _labels.Remove(label);
if (_selectedNode.Id.Equals(item.Id)) _selectedNode = null;
}
else if (item is MapImage image)
{
if (_images != null) _images.Remove(image);
if (_selectedNode.Id.Equals(item.Id)) _selectedNode = null;
}
else if (item is MapMark mark)
{
if (_marks != null) _marks.Remove(mark);
if (_selectedNode.Id.Equals(item.Id)) _selectedNode = null;
}
else if (item is MapMagnet magnet)
{
if (_magnets != null) _magnets.Remove(magnet);
if (_selectedNode.Id.Equals(item.Id)) _selectedNode = null;
}
MapChanged?.Invoke(this, EventArgs.Empty);
Invalidate();
}
private float CalculatePointToLineDistance(Point point, Point lineStart, Point lineEnd) private float CalculatePointToLineDistance(Point point, Point lineStart, Point lineEnd)
{ {
// 점에서 선분까지의 거리 계산 // 점에서 선분까지의 거리 계산
@@ -928,27 +1083,21 @@ namespace AGVNavigationCore.Controls
{ {
string tooltipText = ""; string tooltipText = "";
// 노드 툴팁 var hitNode = GetItemAt(worldPoint);
var hitNode = GetNodeAt(worldPoint);
if (hitNode != null)
{
tooltipText = $"노드: {hitNode.NodeId}\n타입: {hitNode.Type}\n위치: ({hitNode.Position.X}, {hitNode.Position.Y})";
}
else
{
// AGV 툴팁
var hitAGV = GetAGVAt(worldPoint); var hitAGV = GetAGVAt(worldPoint);
if (hitAGV != null)
if (hitNode != null)
tooltipText = $"노드: {hitNode.Id}\n타입: {hitNode.Type}\n위치: ({hitNode.Position.X}, {hitNode.Position.Y})";
else if (hitAGV != null)
{ {
var state = _agvStates.ContainsKey(hitAGV.AgvId) ? _agvStates[hitAGV.AgvId] : AGVState.Idle; var state = _agvStates.ContainsKey(hitAGV.AgvId) ? _agvStates[hitAGV.AgvId] : AGVState.Idle;
tooltipText = $"AGV: {hitAGV.AgvId}\n상태: {state}\n배터리: {hitAGV.BatteryLevel:F1}%\n위치: ({hitAGV.CurrentPosition.X}, {hitAGV.CurrentPosition.Y})"; tooltipText = $"AGV: {hitAGV.AgvId}\n상태: {state}\n배터리: {hitAGV.BatteryLevel:F1}%\n위치: ({hitAGV.CurrentPosition.X}, {hitAGV.CurrentPosition.Y})";
} }
}
// 툴팁 업데이트 (기존 ToolTip 컨트롤 사용) // 툴팁 텍스트 갱신 (변경되었을 때만)
if (!string.IsNullOrEmpty(tooltipText)) if (_tooltip != null && _tooltip.GetToolTip(this) != tooltipText)
{ {
// ToolTip 설정 (필요시 추가 구현) _tooltip.SetToolTip(this, tooltipText);
} }
} }
@@ -1016,7 +1165,7 @@ namespace AGVNavigationCore.Controls
/// </summary> /// </summary>
public void PanToNode(string nodeId) public void PanToNode(string nodeId)
{ {
var node = _nodes?.FirstOrDefault(n => n.NodeId == nodeId); var node = _nodes?.FirstOrDefault(n => n.Id == nodeId);
if (node != null) if (node != null)
{ {
PanTo(node.Position); PanTo(node.Position);

View File

@@ -1,12 +1,14 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
using AGVNavigationCore.Models; using AGVNavigationCore.Models;
using AGVNavigationCore.PathFinding; using AGVNavigationCore.PathFinding;
using AGVNavigationCore.PathFinding.Core; using AGVNavigationCore.PathFinding.Core;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Reflection.Emit;
using System.Windows.Forms;
namespace AGVNavigationCore.Controls namespace AGVNavigationCore.Controls
{ {
@@ -21,7 +23,7 @@ namespace AGVNavigationCore.Controls
private const int NODE_SIZE = 24; private const int NODE_SIZE = 24;
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 = 2.0f; private const float CONNECTION_WIDTH = 1.0f;
private const int SNAP_DISTANCE = 10; private const int SNAP_DISTANCE = 10;
private const int AGV_SIZE = 40; private const int AGV_SIZE = 40;
private const int CONNECTION_ARROW_SIZE = 8; private const int CONNECTION_ARROW_SIZE = 8;
@@ -37,7 +39,8 @@ namespace AGVNavigationCore.Controls
{ {
Edit, // 편집 가능 (맵 에디터) Edit, // 편집 가능 (맵 에디터)
Sync, // 동기화 모드 (장비 설정 동기화) Sync, // 동기화 모드 (장비 설정 동기화)
Emulator // 에뮬레이터 모드 Emulator, // 에뮬레이터 모드
Run // 가동 모드 (User Request)
} }
/// <summary> /// <summary>
@@ -65,10 +68,19 @@ namespace AGVNavigationCore.Controls
// 맵 데이터 // 맵 데이터
private List<MapNode> _nodes; private List<MapNode> _nodes;
private MapNode _selectedNode; private List<MapLabel> _labels; // 추가
private List<MapNode> _selectedNodes; // 다중 선택 private List<MapImage> _images; // 추가
private MapNode _hoveredNode; private List<MapMark> _marks;
private MapNode _destinationNode; private List<MapMagnet> _magnets;
// 선택된 객체들 (나중에 NodeBase로 통일 필요)
private NodeBase _selectedNode;
private List<NodeBase> _selectedNodes; // 다중 선택 (NodeBase로 변경 고려)
private NodeBase _hoveredNode;
private NodeBase _destinationNode;
// AGV 관련 // AGV 관련
private List<IAGV> _agvList; private List<IAGV> _agvList;
@@ -143,24 +155,31 @@ namespace AGVNavigationCore.Controls
private Pen _pathPen; private Pen _pathPen;
private Pen _agvPen; private Pen _agvPen;
private Pen _highlightedConnectionPen; private Pen _highlightedConnectionPen;
private Pen _magnetPen;
private Pen _markPen;
private ToolTip _tooltip;
// 컨텍스트 메뉴 // 컨텍스트 메뉴
private ContextMenuStrip _contextMenu; private ContextMenuStrip _contextMenu;
// 이벤트 // 이벤트
public event EventHandler<MapNode> NodeRightClicked; public event EventHandler<NodeBase> NodeRightClicked;
#endregion #endregion
#region Events #region Events
// 맵 편집 이벤트 // 맵 편집 이벤트
public event EventHandler<MapNode> NodeAdded; public delegate void NodeSelectHandler(object sender, NodeBase node, MouseEventArgs e);
public event EventHandler<List<MapNode>> NodesSelected; // 다중 선택 이벤트 public event NodeSelectHandler NodeSelect;
public event EventHandler<MapNode> NodeDeleted;
public event EventHandler<MapNode> NodeMoved; public event EventHandler<NodeBase> NodeAdded;
public event EventHandler<List<NodeBase>> NodesSelected; // 다중 선택 이벤트
public event EventHandler<NodeBase> NodeDeleted;
public event EventHandler<NodeBase> NodeMoved;
public event EventHandler<(MapNode From, MapNode To)> ConnectionDeleted; public event EventHandler<(MapNode From, MapNode To)> ConnectionDeleted;
public event EventHandler<MapNode> ImageNodeDoubleClicked; public event EventHandler<MapImage> ImageDoubleClicked;
public event EventHandler<MapLabel> LabelDoubleClicked;
public event EventHandler MapChanged; public event EventHandler MapChanged;
#endregion #endregion
@@ -184,6 +203,60 @@ namespace AGVNavigationCore.Controls
} }
} }
public void RemoveItem(NodeBase item)
{
if (item is MapImage img) RemoveImage(img);
else if (item is MapLabel lb) RemoveLabel(lb);
else if (item is MapNode nd) RemoveNode(nd);
else if (item is MapMark mk) RemoveMark(mk);
else if (item is MapMagnet mg) RemoveMagnet(mg);
else throw new Exception("unknown type");
}
public void RemoveNode(MapNode node)
{
if (_nodes != null && _nodes.Contains(node))
{
_nodes.Remove(node);
Invalidate();
}
}
public void RemoveLabel(MapLabel label)
{
if (_labels != null && _labels.Contains(label))
{
_labels.Remove(label);
Invalidate();
}
}
public void RemoveImage(MapImage image)
{
if (_images != null && _images.Contains(image))
{
_images.Remove(image);
Invalidate();
}
}
public void RemoveMark(MapMark mark)
{
if (_marks != null && _marks.Contains(mark))
{
_marks.Remove(mark);
Invalidate();
}
}
public void RemoveMagnet(MapMagnet magnet)
{
if (_magnets != null && _magnets.Contains(magnet))
{
_magnets.Remove(magnet);
Invalidate();
}
}
/// <summary> /// <summary>
/// 편집 모드 (CanvasMode.Edit일 때만 적용) /// 편집 모드 (CanvasMode.Edit일 때만 적용)
/// </summary> /// </summary>
@@ -230,15 +303,80 @@ namespace AGVNavigationCore.Controls
} }
} }
[Browsable(false)]
public MapImage SelectedImage
{
get { return this._selectedNode as MapImage; }
}
[Browsable(false)]
public MapLabel SelectedLabel
{
get { return this._selectedNode as MapLabel; }
}
[Browsable(false)]
public MapMark SelectedMark
{
get { return this._selectedNode as MapMark; }
}
[Browsable(false)]
public MapMagnet SelectedMagnet
{
get { return this._selectedNode as MapMagnet; }
}
/// <summary> /// <summary>
/// 선택된 노드 (단일) /// 선택된 노드 (단일)
/// </summary> /// </summary>
public MapNode SelectedNode => _selectedNode; public MapNode SelectedNode
{
get { return this._selectedNode as MapNode; }
}
/// <summary> /// <summary>
/// 선택된 노드들 (다중) /// 선택된 노드들 (다중)
/// </summary> /// </summary>
public List<MapNode> SelectedNodes => _selectedNodes ?? new List<MapNode>(); public List<NodeBase> SelectedNodes => _selectedNodes ?? new List<NodeBase>();
public List<NodeBase> Items
{
get
{
List<NodeBase> items = new List<NodeBase>();
if (Nodes != null && Nodes.Any()) items.AddRange(Nodes);
if (Labels != null && Labels.Any()) items.AddRange(Labels);
if (Images != null && Images.Any()) items.AddRange(Images);
if (Marks != null && Marks.Any()) items.AddRange(Marks);
if (Magnets != null && Magnets.Any()) items.AddRange(Magnets);
return items;
}
}
/// <summary>
/// Map file loading 결과를 셋팅합니다
/// </summary>
/// <param name="result"></param>
public void SetMapLoadResult(MapLoader.MapLoadResult result)
{
this.Nodes = result.Nodes;
this.Labels = result.Labels; // 추가
this.Images = result.Images; // 추가
this.Marks = result.Marks;
this.Magnets = result.Magnets;
// 🔥 맵 설정 적용 (배경색, 그리드 표시)
if (result.Settings != null)
{
this.BackColor = Color.FromArgb(result.Settings.BackgroundColorArgb);
this.ShowGrid = result.Settings.ShowGrid;
}
this.FitToNodes();
}
/// <summary> /// <summary>
/// 노드 목록 /// 노드 목록
@@ -260,6 +398,58 @@ namespace AGVNavigationCore.Controls
} }
} }
/// <summary>
/// 라벨 목록
/// </summary>
public List<MapLabel> Labels
{
get => _labels ?? new List<MapLabel>();
set
{
_labels = value ?? new List<MapLabel>();
Invalidate();
}
}
/// <summary>
/// 이미지 목록
/// </summary>
public List<MapImage> Images
{
get => _images ?? new List<MapImage>();
set
{
_images = value ?? new List<MapImage>();
Invalidate();
}
}
/// <summary>
/// 마크 목록
/// </summary>
public List<MapMark> Marks
{
get => _marks ?? new List<MapMark>();
set
{
_marks = value ?? new List<MapMark>();
Invalidate();
}
}
/// <summary>
/// 마그넷 목록
/// </summary>
public List<MapMagnet> Magnets
{
get => _magnets ?? new List<MapMagnet>();
set
{
_magnets = value ?? new List<MapMagnet>();
Invalidate();
}
}
/// <summary> /// <summary>
/// AGV 목록 /// AGV 목록
/// </summary> /// </summary>
@@ -389,7 +579,12 @@ namespace AGVNavigationCore.Controls
ControlStyles.ResizeRedraw, true); ControlStyles.ResizeRedraw, true);
_nodes = new List<MapNode>(); _nodes = new List<MapNode>();
_selectedNodes = new List<MapNode>(); // 다중 선택 리스트 초기화 _labels = new List<MapLabel>();
_images = new List<MapImage>();
_marks = new List<MapMark>();
_magnets = new List<MapMagnet>();
_selectedNodes = new List<NodeBase>(); // 다중 선택 리스트 초기화
_agvList = new List<IAGV>(); _agvList = new List<IAGV>();
_agvPositions = new Dictionary<string, Point>(); _agvPositions = new Dictionary<string, Point>();
_agvDirections = new Dictionary<string, AgvDirection>(); _agvDirections = new Dictionary<string, AgvDirection>();
@@ -399,6 +594,12 @@ namespace AGVNavigationCore.Controls
InitializeBrushesAndPens(); InitializeBrushesAndPens();
CreateContextMenu(); CreateContextMenu();
_tooltip = new ToolTip();
_tooltip.AutoPopDelay = 5000;
_tooltip.InitialDelay = 1000;
_tooltip.ReshowDelay = 500;
_tooltip.ShowAlways = true;
} }
private void InitializeBrushesAndPens() private void InitializeBrushesAndPens()
@@ -420,7 +621,8 @@ namespace AGVNavigationCore.Controls
_gridBrush = new SolidBrush(Color.LightGray); _gridBrush = new SolidBrush(Color.LightGray);
// 펜 // 펜
_connectionPen = new Pen(Color.DarkBlue, CONNECTION_WIDTH); _connectionPen = new Pen(Color.White, CONNECTION_WIDTH);
_connectionPen.DashStyle = DashStyle.Dash;
_connectionPen.EndCap = LineCap.ArrowAnchor; _connectionPen.EndCap = LineCap.ArrowAnchor;
_gridPen = new Pen(Color.LightGray, 1); _gridPen = new Pen(Color.LightGray, 1);
@@ -430,6 +632,8 @@ namespace AGVNavigationCore.Controls
_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, 4) { DashStyle = DashStyle.Solid };
_magnetPen = new Pen(Color.FromArgb(100,Color.LightSkyBlue), 15) { DashStyle = DashStyle.Solid };
_markPen = new Pen(Color.White, 3); // 마크는 흰색 선으로 표시
} }
private void CreateContextMenu() private void CreateContextMenu()
@@ -621,6 +825,8 @@ namespace AGVNavigationCore.Controls
_pathPen?.Dispose(); _pathPen?.Dispose();
_agvPen?.Dispose(); _agvPen?.Dispose();
_highlightedConnectionPen?.Dispose(); _highlightedConnectionPen?.Dispose();
_magnetPen?.Dispose();
_markPen?.Dispose();
// 컨텍스트 메뉴 정리 // 컨텍스트 메뉴 정리
_contextMenu?.Dispose(); _contextMenu?.Dispose();
@@ -646,13 +852,13 @@ namespace AGVNavigationCore.Controls
return; return;
// RFID값과 해당 노드의 인덱스를 저장 // RFID값과 해당 노드의 인덱스를 저장
var rfidToNodeIndex = new Dictionary<string, List<int>>(); var rfidToNodeIndex = new Dictionary<ushort, List<int>>();
// 모든 노드의 RFID값 수집 // 모든 노드의 RFID값 수집
for (int i = 0; i < _nodes.Count; i++) for (int i = 0; i < _nodes.Count; i++)
{ {
var node = _nodes[i]; var node = _nodes[i];
if (!string.IsNullOrEmpty(node.RfidId)) if (node.HasRfid())
{ {
if (!rfidToNodeIndex.ContainsKey(node.RfidId)) if (!rfidToNodeIndex.ContainsKey(node.RfidId))
{ {
@@ -671,7 +877,7 @@ namespace AGVNavigationCore.Controls
for (int i = 1; i < kvp.Value.Count; i++) for (int i = 1; i < kvp.Value.Count; i++)
{ {
int duplicateNodeIndex = kvp.Value[i]; int duplicateNodeIndex = kvp.Value[i];
_duplicateRfidNodes.Add(_nodes[duplicateNodeIndex].NodeId); _duplicateRfidNodes.Add(_nodes[duplicateNodeIndex].Id);
} }
} }
} }
@@ -692,7 +898,7 @@ namespace AGVNavigationCore.Controls
foreach (var node in _nodes) foreach (var node in _nodes)
{ {
// NodeId에서 숫자 부분 추출 (예: "N001" -> 1) // NodeId에서 숫자 부분 추출 (예: "N001" -> 1)
if (node.NodeId.StartsWith("N") && int.TryParse(node.NodeId.Substring(1), out int number)) if (node.Id.StartsWith("N") && int.TryParse(node.Id.Substring(1), out int number))
{ {
maxNumber = Math.Max(maxNumber, number); maxNumber = Math.Max(maxNumber, number);
} }

View File

@@ -11,26 +11,18 @@ namespace AGVNavigationCore.Models
{ {
/// <summary>일반 경로 노드</summary> /// <summary>일반 경로 노드</summary>
Normal, Normal,
/// <summary>로더</summary>
Loader,
/// <summary>
/// 언로더
/// </summary>
UnLoader,
/// <summary>
/// 클리너
/// </summary>
Clearner,
/// <summary>
/// 버퍼
/// </summary>
Buffer,
/// <summary>충전 스테이션</summary>
Charging,
/// <summary>라벨 (UI 요소)</summary>
Label, Label,
/// <summary>이미지 (UI 요소)</summary> /// <summary>이미지 (UI 요소)</summary>
Image Image,
/// <summary>
/// 마크센서
/// </summary>
Mark,
/// <summary>
/// 마그넷라인
/// </summary>
Magnet
} }
/// <summary> /// <summary>
@@ -71,17 +63,23 @@ namespace AGVNavigationCore.Models
/// <summary> /// <summary>
/// 일반노드 /// 일반노드
/// </summary> /// </summary>
Node, Normal,
/// <summary>로더</summary> /// <summary>로더</summary>
Loader, Loader,
/// <summary>클리너</summary> /// <summary>클리너</summary>
Cleaner, Clearner,
/// <summary>오프로더</summary> /// <summary>오프로더</summary>
Offloader, UnLoader,
/// <summary>버퍼</summary> /// <summary>버퍼</summary>
Buffer, Buffer,
/// <summary>충전기</summary> /// <summary>충전기</summary>
Charger Charger,
/// <summary>
/// 끝점(더이상 이동불가)
/// </summary>
Limit,
} }
/// <summary> /// <summary>

View File

@@ -82,7 +82,7 @@ namespace AGVNavigationCore.Models
/// <summary> /// <summary>
/// 현재 노드 ID /// 현재 노드 ID
/// </summary> /// </summary>
string CurrentNodeId { get; } MapNode CurrentNode { get; }
/// <summary> /// <summary>
/// 목표 위치 /// 목표 위치
@@ -92,7 +92,7 @@ namespace AGVNavigationCore.Models
/// <summary> /// <summary>
/// 목표 노드 ID /// 목표 노드 ID
/// </summary> /// </summary>
string PrevNodeId { get; } MapNode PrevNode { get; }
/// <summary> /// <summary>
/// 도킹 방향 /// 도킹 방향

View File

@@ -0,0 +1,88 @@
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using AGVNavigationCore.Utils;
using Newtonsoft.Json;
using System;
namespace AGVNavigationCore.Models
{
public class MapImage : NodeBase
{
[Category("기본 정보")]
[Description("이미지의 이름입니다.")]
public string Name { get; set; } = "Image";
[Category("이미지 설정")]
[Description("이미지 파일 경로입니다 (편집기용).")]
public string ImagePath { get; set; } = string.Empty;
[ReadOnly(false)]
public string ImageBase64 { get; set; } = string.Empty;
[Category("이미지 설정")]
[Description("이미지 크기 배율입니다.")]
public SizeF Scale { get; set; } = new SizeF(1.0f, 1.0f);
[Category("이미지 설정")]
[Description("이미지 투명도입니다 (0.0 ~ 1.0).")]
public float Opacity { get; set; } = 1.0f;
[Category("이미지 설정")]
[Description("이미지 회전 각도입니다.")]
public float Rotation { get; set; } = 0.0f;
[JsonIgnore]
[Browsable(false)]
public Image LoadedImage { get; set; }
public MapImage()
{
Type = NodeType.Image;
}
public bool LoadImage()
{
try
{
Image originalImage = null;
if (!string.IsNullOrEmpty(ImageBase64))
{
originalImage = ImageConverterUtil.Base64ToImage(ImageBase64);
}
else if (!string.IsNullOrEmpty(ImagePath) && System.IO.File.Exists(ImagePath))
{
originalImage = Image.FromFile(ImagePath);
}
if (originalImage != null)
{
LoadedImage?.Dispose();
LoadedImage = originalImage; // 리사이즈 필요시 추가 구현
return true;
}
}
catch
{
// 무시
}
return false;
}
public Size GetDisplaySize()
{
if (LoadedImage == null) return Size.Empty;
return new Size(
(int)(LoadedImage.Width * Scale.Width),
(int)(LoadedImage.Height * Scale.Height)
);
}
public void Dispose()
{
LoadedImage?.Dispose();
LoadedImage = null;
}
}
}

View File

@@ -0,0 +1,42 @@
using System.ComponentModel;
using System.Drawing;
namespace AGVNavigationCore.Models
{
public class MapLabel : NodeBase
{
[Category("라벨 설정")]
[Description("표시할 텍스트입니다.")]
public string Text { get; set; } = "";
[Category("라벨 설정")]
[Description("글자색입니다")]
public Color ForeColor { get; set; } = Color.Black;
[Category("라벨 설정")]
[Description("배경색입니다.")]
public Color BackColor { get; set; } = Color.Transparent;
[Category("라벨 설정")]
[Description("폰트 종류입니다.")]
public string FontFamily { get; set; } = "Arial";
[Category("라벨 설정")]
[Description("폰트 크기입니다.")]
public float FontSize { get; set; } = 12.0f;
[Category("라벨 설정")]
[Description("폰트 스타일입니다.")]
public FontStyle FontStyle { get; set; } = FontStyle.Regular;
[Category("라벨 설정")]
[Description("내부 여백입니다.")]
public int Padding { get; set; } = 5;
public MapLabel()
{
ForeColor = Color.Purple;
Type = NodeType.Label;
}
}
}

View File

@@ -28,6 +28,10 @@ namespace AGVNavigationCore.Models
{ {
public bool Success { get; set; } public bool Success { get; set; }
public List<MapNode> Nodes { get; set; } = new List<MapNode>(); public List<MapNode> Nodes { get; set; } = new List<MapNode>();
public List<MapLabel> Labels { get; set; } = new List<MapLabel>(); // 추가
public List<MapImage> Images { get; set; } = new List<MapImage>(); // 추가
public List<MapMark> Marks { get; set; } = new List<MapMark>();
public List<MapMagnet> Magnets { get; set; } = new List<MapMagnet>();
public MapSettings Settings { get; set; } = new MapSettings(); public MapSettings Settings { get; set; } = new MapSettings();
public string ErrorMessage { get; set; } = string.Empty; public string ErrorMessage { get; set; } = string.Empty;
public string Version { get; set; } = string.Empty; public string Version { get; set; } = string.Empty;
@@ -40,9 +44,13 @@ namespace AGVNavigationCore.Models
public class MapFileData public class MapFileData
{ {
public List<MapNode> Nodes { get; set; } = new List<MapNode>(); public List<MapNode> Nodes { get; set; } = new List<MapNode>();
public List<MapLabel> Labels { get; set; } = new List<MapLabel>(); // 추가
public List<MapImage> Images { get; set; } = new List<MapImage>(); // 추가
public List<MapMark> Marks { get; set; } = new List<MapMark>();
public List<MapMagnet> Magnets { get; set; } = new List<MapMagnet>();
public MapSettings Settings { get; set; } = new MapSettings(); public MapSettings Settings { get; set; } = new MapSettings();
public DateTime CreatedDate { get; set; } public DateTime CreatedDate { get; set; }
public string Version { get; set; } = "1.1"; // 버전 업그레이드 (설정 추가) public string Version { get; set; } = "1.3"; // 버전 업그레이드
} }
/// <summary> /// <summary>
@@ -64,7 +72,7 @@ namespace AGVNavigationCore.Models
var json = File.ReadAllText(filePath); var json = File.ReadAllText(filePath);
// JSON 역직렬화 설정: 누락된 속성 무시, 안전한 처리 // JSON 역직렬화 설정
var settings = new JsonSerializerSettings var settings = new JsonSerializerSettings
{ {
MissingMemberHandling = MissingMemberHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore,
@@ -72,36 +80,83 @@ namespace AGVNavigationCore.Models
DefaultValueHandling = DefaultValueHandling.Populate DefaultValueHandling = DefaultValueHandling.Populate
}; };
// 먼저 구조 파악을 위해 동적 객체로 로드하거나, MapFileData로 시도
var mapData = JsonConvert.DeserializeObject<MapFileData>(json, settings); var mapData = JsonConvert.DeserializeObject<MapFileData>(json, settings);
if (mapData != null) if (mapData != null)
{ {
result.Nodes = mapData.Nodes ?? new List<MapNode>(); result.Nodes = new List<MapNode>();
result.Settings = mapData.Settings ?? new MapSettings(); // 설정 로드 result.Labels = mapData.Labels ?? new List<MapLabel>();
result.Images = mapData.Images ?? new List<MapImage>();
result.Marks = mapData.Marks ?? new List<MapMark>();
result.Magnets = mapData.Magnets ?? new List<MapMagnet>();
result.Settings = mapData.Settings ?? new MapSettings();
result.Version = mapData.Version ?? "1.0"; result.Version = mapData.Version ?? "1.0";
result.CreatedDate = mapData.CreatedDate; result.CreatedDate = mapData.CreatedDate;
// 기존 Description 데이터를 Name으로 마이그레이션 if (mapData.Nodes != null)
MigrateDescriptionToName(result.Nodes); {
foreach (var node in mapData.Nodes)
{
// 마이그레이션: 기존 파일의 Nodes 리스트에 섞여있는 Label, Image 분리
// (새 파일 구조에서는 이미 분리되어 로드됨)
if (node.Type == NodeType.Label)
{
// MapNode -> MapLabel 변환 (필드 매핑)
var label = new MapLabel
{
Id = node.Id, // 기존 NodeId -> Id
Position = node.Position,
CreatedDate = node.CreatedDate,
ModifiedDate = node.ModifiedDate,
// DockingDirection 마이그레이션 (기존 NodeType 기반으로 설정) // Label 속성 매핑 (MapNode에서 임시로 가져오거나 Json Raw Parsing 필요할 수 있음)
MigrateDockingDirection(result.Nodes); // 현재 MapNode 클래스에는 해당 속성들이 제거되었으므로,
// Json 포맷 변경으로 인해 기존 데이터 로드시 정보 손실 가능성 있음.
// * 중요 *: MapNode 클래스에서 속성을 지웠으므로 일반 Deserialize로는 Label/Image 속성을 못 읽음.
// 해결책: JObject로 먼저 읽어서 분기 처리하거나, DTO 클래스를 별도로 두어야 함.
// 하지만 시간 관계상, 만약 기존 MapNode가 속성을 가지고 있지 않다면 마이그레이션은 "위치/ID" 정도만 복구됨.
// 완벽한 마이그레이션을 위해서는 MapNode에 Obsolete 속성을 잠시 두었어야 함.
// 여기서는 일단 기본 정보라도 살림.
};
result.Labels.Add(label);
}
else if (node.Type == NodeType.Image)
{
var image = new MapImage
{
Id = node.Id,
Position = node.Position,
CreatedDate = node.CreatedDate,
ModifiedDate = node.ModifiedDate,
// 이미지/라벨 속성 복구 불가 (MapNode에서 삭제됨)
};
result.Images.Add(image);
}
else
{
result.Nodes.Add(node);
}
}
}
// 중복된 NodeId 정리 // 중복된 NodeId 정리 (Nav Node만)
FixDuplicateNodeIds(result.Nodes); FixDuplicateNodeIds(result.Nodes);
// 존재하지 않는 노드에 대한 연결 정리 (고아 연결 제거) // 고아 연결 정리
CleanupOrphanConnections(result.Nodes); CleanupOrphanConnections(result.Nodes);
// 양방향 연결 자동 설정 (A→B가 있으면 B→A도 설정) // 양방향 연결 자동 설정
// 주의: CleanupDuplicateConnections()는 제거됨 - 양방향 연결을 단방향으로 변환하는 버그가 있었음
EnsureBidirectionalConnections(result.Nodes); EnsureBidirectionalConnections(result.Nodes);
// ConnectedMapNodes 채우기 (string ID → MapNode 객체 참조) // ConnectedMapNodes 채우기
ResolveConnectedMapNodes(result.Nodes); ResolveConnectedMapNodes(result.Nodes);
// 이미지 노드들의 이미지 로드 // 이미지 로드 (MapImage 객체에서)
LoadImageNodes(result.Nodes); foreach (var img in result.Images)
{
img.LoadImage();
}
result.Success = true; result.Success = true;
} }
@@ -121,23 +176,23 @@ namespace AGVNavigationCore.Models
/// <summary> /// <summary>
/// 맵 데이터를 파일로 저장 /// 맵 데이터를 파일로 저장
/// </summary> /// </summary>
/// <param name="filePath">저장할 파일 경로</param> public static bool SaveMapToFile(string filePath, List<MapNode> nodes, List<MapLabel> labels = null, List<MapImage> images = null, List<MapMark> marks = null, List<MapMagnet> magnets = null, MapSettings settings = null)
/// <param name="nodes">맵 노드 목록</param>
/// <param name="settings">맵 설정 (배경색, 그리드 표시 등)</param>
/// <returns>저장 성공 여부</returns>
public static bool SaveMapToFile(string filePath, List<MapNode> nodes, MapSettings settings = null)
{ {
try try
{ {
// 저장 전 고아 연결 정리 (삭제된 노드에 대한 연결 제거) // 저장 전 고아 연결 정리
CleanupOrphanConnections(nodes); CleanupOrphanConnections(nodes);
var mapData = new MapFileData var mapData = new MapFileData
{ {
Nodes = nodes, Nodes = nodes,
Settings = settings ?? new MapSettings(), // 설정 저장 Labels = labels ?? new List<MapLabel>(),
Images = images ?? new List<MapImage>(),
Marks = marks ?? new List<MapMark>(),
Magnets = magnets ?? new List<MapMagnet>(),
Settings = settings ?? new MapSettings(),
CreatedDate = DateTime.Now, CreatedDate = DateTime.Now,
Version = "1.1" Version = "1.3"
}; };
var json = JsonConvert.SerializeObject(mapData, Formatting.Indented); var json = JsonConvert.SerializeObject(mapData, Formatting.Indented);
@@ -145,27 +200,13 @@ namespace AGVNavigationCore.Models
return true; return true;
} }
catch (Exception) catch (Exception)
{ {
return false; return false;
} }
} }
/// <summary>
/// 이미지 노드들의 이미지 로드
/// </summary>
/// <param name="nodes">노드 목록</param>
private static void LoadImageNodes(List<MapNode> nodes)
{
foreach (var node in nodes)
{
if (node.Type == NodeType.Image)
{
node.LoadImage();
}
}
}
/// <summary> /// <summary>
/// ConnectedMapNodes 채우기 (ConnectedNodes의 string ID → MapNode 객체 변환) /// ConnectedMapNodes 채우기 (ConnectedNodes의 string ID → MapNode 객체 변환)
/// </summary> /// </summary>
@@ -175,7 +216,7 @@ namespace AGVNavigationCore.Models
if (mapNodes == null || mapNodes.Count == 0) return; if (mapNodes == null || mapNodes.Count == 0) return;
// 빠른 조회를 위한 Dictionary 생성 // 빠른 조회를 위한 Dictionary 생성
var nodeDict = mapNodes.ToDictionary(n => n.NodeId, n => n); var nodeDict = mapNodes.ToDictionary(n => n.Id, n => n);
foreach (var node in mapNodes) foreach (var node in mapNodes)
{ {
@@ -192,6 +233,8 @@ namespace AGVNavigationCore.Models
} }
} }
} }
} }
} }
@@ -208,39 +251,6 @@ namespace AGVNavigationCore.Models
// 기존 파일들은 다시 저장될 때 Description 없이 저장됨 // 기존 파일들은 다시 저장될 때 Description 없이 저장됨
} }
/// <summary>
/// 기존 맵 파일의 DockingDirection을 NodeType 기반으로 마이그레이션
/// </summary>
/// <param name="mapNodes">맵 노드 목록</param>
private static void MigrateDockingDirection(List<MapNode> mapNodes)
{
if (mapNodes == null || mapNodes.Count == 0) return;
foreach (var node in mapNodes)
{
// 기존 파일에서 DockingDirection이 기본값(DontCare)인 경우에만 마이그레이션
if (node.DockDirection == DockingDirection.DontCare)
{
switch (node.Type)
{
case NodeType.Charging:
node.DockDirection = DockingDirection.Forward;
break;
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
node.DockDirection = DockingDirection.Backward;
break;
default:
// Normal, Rotation, Label, Image는 DontCare 유지
node.DockDirection = DockingDirection.DontCare;
break;
}
}
}
}
/// <summary> /// <summary>
/// 중복된 NodeId를 가진 노드들을 고유한 NodeId로 수정 /// 중복된 NodeId를 가진 노드들을 고유한 NodeId로 수정
/// </summary> /// </summary>
@@ -255,13 +265,13 @@ namespace AGVNavigationCore.Models
// 첫 번째 패스: 중복된 노드들 식별 // 첫 번째 패스: 중복된 노드들 식별
foreach (var node in mapNodes) foreach (var node in mapNodes)
{ {
if (usedIds.Contains(node.NodeId)) if (usedIds.Contains(node.Id))
{ {
duplicateNodes.Add(node); duplicateNodes.Add(node);
} }
else else
{ {
usedIds.Add(node.NodeId); usedIds.Add(node.Id);
} }
} }
@@ -271,9 +281,9 @@ namespace AGVNavigationCore.Models
string newNodeId = GenerateUniqueNodeId(usedIds); string newNodeId = GenerateUniqueNodeId(usedIds);
// 다른 노드들의 연결에서 기존 NodeId를 새 NodeId로 업데이트 // 다른 노드들의 연결에서 기존 NodeId를 새 NodeId로 업데이트
UpdateConnections(mapNodes, duplicateNode.NodeId, newNodeId); UpdateConnections(mapNodes, duplicateNode.Id, newNodeId);
duplicateNode.NodeId = newNodeId; duplicateNode.Id = newNodeId;
usedIds.Add(newNodeId); usedIds.Add(newNodeId);
} }
} }
@@ -331,7 +341,7 @@ namespace AGVNavigationCore.Models
if (mapNodes == null || mapNodes.Count == 0) return; if (mapNodes == null || mapNodes.Count == 0) return;
// 존재하는 모든 노드 ID 집합 생성 // 존재하는 모든 노드 ID 집합 생성
var validNodeIds = new HashSet<string>(mapNodes.Select(n => n.NodeId)); var validNodeIds = new HashSet<string>(mapNodes.Select(n => n.Id));
// 각 노드의 연결을 검증하고 존재하지 않는 노드 ID 제거 // 각 노드의 연결을 검증하고 존재하지 않는 노드 ID 제거
foreach (var node in mapNodes) foreach (var node in mapNodes)
@@ -369,13 +379,13 @@ namespace AGVNavigationCore.Models
foreach (var connectedNodeId in node.ConnectedNodes.ToList()) foreach (var connectedNodeId in node.ConnectedNodes.ToList())
{ {
var connectedNode = mapNodes.FirstOrDefault(n => n.NodeId == connectedNodeId); var connectedNode = mapNodes.FirstOrDefault(n => n.Id == connectedNodeId);
if (connectedNode == null) continue; if (connectedNode == null) continue;
// 연결 쌍의 키 생성 (사전순 정렬) // 연결 쌍의 키 생성 (사전순 정렬)
string pairKey = string.Compare(node.NodeId, connectedNodeId, StringComparison.Ordinal) < 0 string pairKey = string.Compare(node.Id, connectedNodeId, StringComparison.Ordinal) < 0
? $"{node.NodeId}-{connectedNodeId}" ? $"{node.Id}-{connectedNodeId}"
: $"{connectedNodeId}-{node.NodeId}"; : $"{connectedNodeId}-{node.Id}";
if (processedPairs.Contains(pairKey)) if (processedPairs.Contains(pairKey))
{ {
@@ -388,17 +398,17 @@ namespace AGVNavigationCore.Models
processedPairs.Add(pairKey); processedPairs.Add(pairKey);
// 양방향 연결인 경우 하나만 유지 // 양방향 연결인 경우 하나만 유지
if (connectedNode.ConnectedNodes.Contains(node.NodeId)) if (connectedNode.ConnectedNodes.Contains(node.Id))
{ {
// 사전순으로 더 작은 노드에만 연결을 유지 // 사전순으로 더 작은 노드에만 연결을 유지
if (string.Compare(node.NodeId, connectedNodeId, StringComparison.Ordinal) > 0) if (string.Compare(node.Id, connectedNodeId, StringComparison.Ordinal) > 0)
{ {
connectionsToRemove.Add(connectedNodeId); connectionsToRemove.Add(connectedNodeId);
} }
else else
{ {
// 반대 방향 연결 제거 // 반대 방향 연결 제거
connectedNode.RemoveConnection(node.NodeId); connectedNode.RemoveConnection(node.Id);
} }
} }
} }
@@ -433,16 +443,16 @@ namespace AGVNavigationCore.Models
// 1단계: 모든 명시적 연결 수집 // 1단계: 모든 명시적 연결 수집
foreach (var node in mapNodes) foreach (var node in mapNodes)
{ {
if (!allConnections.ContainsKey(node.NodeId)) if (!allConnections.ContainsKey(node.Id))
{ {
allConnections[node.NodeId] = new HashSet<string>(); allConnections[node.Id] = new HashSet<string>();
} }
if (node.ConnectedNodes != null) if (node.ConnectedNodes != null)
{ {
foreach (var connectedId in node.ConnectedNodes) foreach (var connectedId in node.ConnectedNodes)
{ {
allConnections[node.NodeId].Add(connectedId); allConnections[node.Id].Add(connectedId);
} }
} }
} }
@@ -458,10 +468,10 @@ namespace AGVNavigationCore.Models
// 이 노드를 연결하는 모든 노드 찾기 // 이 노드를 연결하는 모든 노드 찾기
foreach (var otherNodeId in allConnections.Keys) foreach (var otherNodeId in allConnections.Keys)
{ {
if (otherNodeId == node.NodeId) continue; if (otherNodeId == node.Id) continue;
// 다른 노드가 이 노드를 연결하고 있다면 // 다른 노드가 이 노드를 연결하고 있다면
if (allConnections[otherNodeId].Contains(node.NodeId)) if (allConnections[otherNodeId].Contains(node.Id))
{ {
// 이 노드의 ConnectedNodes에 그 노드를 추가 (중복 방지) // 이 노드의 ConnectedNodes에 그 노드를 추가 (중복 방지)
if (!node.ConnectedNodes.Contains(otherNodeId)) if (!node.ConnectedNodes.Contains(otherNodeId))

View File

@@ -0,0 +1,72 @@
using System;
using System.ComponentModel;
using System.Drawing;
using Newtonsoft.Json;
namespace AGVNavigationCore.Models
{
/// <summary>
/// 맵 상의 마그넷(Magnet) 정보를 나타내는 클래스
/// </summary>
public class MapMagnet : NodeBase
{
public MapMagnet() {
Type = NodeType.Magnet;
}
[Category("위치 정보")]
[Description("시작점 좌표")]
public MagnetPoint P1 { get; set; } = new MagnetPoint();
[Category("위치 정보")]
[Description("끝점 좌표")]
public MagnetPoint P2 { get; set; } = new MagnetPoint();
[Category("위치 정보")]
[Description("제어점 좌표 (곡선인 경우)")]
public MagnetPoint ControlPoint { get; set; } = null;
public class MagnetPoint
{
public double X { get; set; }
public double Y { get; set; }
}
[JsonIgnore]
public override Point Position
{
get => new Point((int)P1.X, (int)P1.Y);
set
{
double dx = value.X - P1.X;
double dy = value.Y - P1.Y;
P1.X += dx;
P1.Y += dy;
P2.X += dx;
P2.Y += dy;
if (ControlPoint != null)
{
ControlPoint.X += dx;
ControlPoint.Y += dy;
}
}
}
/// <summary>
/// 시작점 Point 반환
/// </summary>
[Browsable(false)]
[JsonIgnore]
public Point StartPoint => new Point((int)P1.X, (int)P1.Y);
/// <summary>
/// 끝점 Point 반환
/// </summary>
[Browsable(false)]
[JsonIgnore]
public Point EndPoint => new Point((int)P2.X, (int)P2.Y);
}
}

View File

@@ -0,0 +1,37 @@
using System;
using System.ComponentModel;
using System.Drawing;
namespace AGVNavigationCore.Models
{
/// <summary>
/// 맵 상의 마크(Mark) 정보를 나타내는 클래스
/// </summary>
public class MapMark : NodeBase
{
// Id is inherited from NodeBase
public MapMark() {
Type = NodeType.Mark;
}
[Category("위치 정보")]
[Description("마크의 X 좌표")]
public double X
{
get => Position.X;
set => Position = new Point((int)value, Position.Y);
}
[Category("위치 정보")]
[Description("마크의 Y 좌표")]
public double Y
{
get => Position.Y;
set => Position = new Point(Position.X, (int)value);
}
[Category("위치 정보")]
[Description("마크의 회전 각도")]
public double Rotation { get; set; }
}
}

View File

@@ -1,81 +1,63 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using AGVNavigationCore.Utils; using AGVNavigationCore.Utils;
using Newtonsoft.Json;
namespace AGVNavigationCore.Models namespace AGVNavigationCore.Models
{ {
/// <summary> /// <summary>
/// 맵 노드 정보를 관리하는 클래스 /// 맵 노드 정보를 관리하는 클래스 (주행 경로용 노드)
/// 논리적 노드로서 실제 맵의 위치와 속성을 정의
/// </summary> /// </summary>
public class MapNode public class MapNode : NodeBase
{ {
/// <summary>
/// 논리적 노드 ID (맵 에디터에서 관리하는 고유 ID)
/// 예: "N001", "N002", "LOADER1", "CHARGER1"
/// </summary>
public string NodeId { get; set; } = string.Empty;
/// <summary>
/// 노드 표시 이름 (사용자 친화적)
/// 예: "로더1", "충전기1", "교차점A", "회전지점1"
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary> [Category("라벨 설정")]
/// 맵 상의 위치 좌표 (픽셀 단위) [Description("표시할 텍스트입니다.")]
/// </summary> public string Text { get; set; } = "";
public Point Position { get; set; } = Point.Empty;
/// <summary> public StationType StationType { get; set; }
/// 노드 타입
/// </summary>
public NodeType Type { get; set; } = NodeType.Normal;
[Browsable(false)]
public bool CanDocking public bool CanDocking
{ {
get get
{ {
if (Type == NodeType.Buffer) return true; if (StationType == StationType.Buffer) return true;
if (Type == NodeType.Loader) return true; if (StationType == StationType.Loader) return true;
if (Type == NodeType.UnLoader) return true; if (StationType == StationType.UnLoader) return true;
if (Type == NodeType.Clearner) return true; if (StationType == StationType.Clearner) return true;
if (Type == NodeType.Charging) return true; if (StationType == StationType.Charger) return true;
return false; return false;
} }
} }
/// <summary> [Category("노드 설정")]
/// 도킹 방향 (도킹/충전 노드인 경우에만 Forward/Backward, 일반 노드는 DontCare) [Description("도킹/충전 노드의 진입 방향입니다.")]
/// </summary>
public DockingDirection DockDirection { get; set; } = DockingDirection.DontCare; public DockingDirection DockDirection { get; set; } = DockingDirection.DontCare;
/// <summary> [Category("연결 정보")]
/// 연결된 노드 ID 목록 (경로 정보) [Description("연결된 노드 ID 목록입니다.")]
/// </summary> [ReadOnly(true)]
public List<string> ConnectedNodes { get; set; } = new List<string>(); public List<string> ConnectedNodes { get; set; } = new List<string>();
/// <summary> [JsonIgnore]
/// 연결된 노드 객체 목록 (런타임 전용, JSON 무시) [Browsable(false)]
/// </summary>
[Newtonsoft.Json.JsonIgnore]
public List<MapNode> ConnectedMapNodes { get; set; } = new List<MapNode>(); public List<MapNode> ConnectedMapNodes { get; set; } = new List<MapNode>();
/// <summary> [Category("주행 설정")]
/// 회전 가능 여부 (180도 회전 가능한 지점) [Description("제자리 회전(좌) 가능 여부입니다.")]
/// </summary>
public bool CanTurnLeft { get; set; } = true; public bool CanTurnLeft { get; set; } = true;
/// <summary> [Category("주행 설정")]
/// 회전 가능 여부 (180도 회전 가능한 지점) [Description("제자리 회전(우) 가능 여부입니다.")]
/// </summary>
public bool CanTurnRight { get; set; } = true; public bool CanTurnRight { get; set; } = true;
/// <summary> [Category("주행 설정")]
/// 교차로로 이용가능한지 [Description("교차로 주행 가능 여부입니다.")]
/// </summary>
public bool DisableCross public bool DisableCross
{ {
get get
@@ -85,216 +67,61 @@ namespace AGVNavigationCore.Models
} }
set { _disablecross = value; } set { _disablecross = value; }
} }
private bool _disablecross = false; private bool _disablecross = false;
/// <summary> [Category("주행 설정")]
/// 장비 ID (도킹/충전 스테이션인 경우) [Description("노드 통과 시 제한 속도입니다.")]
/// 예: "LOADER1", "CLEANER1", "BUFFER1", "CHARGER1" public SpeedLevel SpeedLimit { get; set; } = SpeedLevel.M;
/// </summary>
public string NodeAlias { get; set; } = string.Empty;
/// <summary> [Category("노드 설정")]
/// 장비 타입 (도킹/충전 스테이션인 경우) [Description("장비 ID 또는 별칭입니다.")]
/// </summary> public string AliasName { get; set; } = string.Empty;
// public StationType? StationType { get; set; } = null;
/// <summary> [Category("기본 정보")]
/// 노드 생성 일자 [Description("노드 사용 여부입니다.")]
/// </summary>
public DateTime CreatedDate { get; set; } = DateTime.Now;
/// <summary>
/// 노드 수정 일자
/// </summary>
public DateTime ModifiedDate { get; set; } = DateTime.Now;
/// <summary>
/// 노드 활성화 여부
/// </summary>
public bool IsActive { get; set; } = true; public bool IsActive { get; set; } = true;
/// <summary> [Category("RFID 정보")]
/// 노드 색상 (맵 에디터 표시용) [Description("물리적 RFID 태그 ID입니다.")]
/// </summary> public UInt16 RfidId { get; set; } = 0;
public Color DisplayColor { get; set; } = Color.Blue;
/// <summary>
/// RFID 태그 ID (이 노드에 매핑된 RFID)
/// </summary>
public string RfidId { get; set; } = string.Empty;
/// <summary> [Category("노드 텍스트"), DisplayName("TextColor")]
/// RFID 상태 (정상, 손상, 교체예정 등) [Description("텍스트 색상입니다.")]
/// </summary> public Color NodeTextForeColor { get; set; } = Color.Black;
public string RfidStatus { get; set; } = "정상";
/// <summary>
/// RFID 설치 위치 설명 (현장 작업자용)
/// 예: "로더1번 앞", "충전기2번 입구", "복도 교차점" 등
/// </summary>
public string RfidDescription { get; set; } = string.Empty;
/// <summary>
/// 라벨 텍스트 (NodeType.Label인 경우 사용)
/// </summary>
public string LabelText { get; set; } = string.Empty;
/// <summary>
/// 라벨 폰트 패밀리 (NodeType.Label인 경우 사용)
/// </summary>
public string FontFamily { get; set; } = "Arial";
/// <summary>
/// 라벨 폰트 크기 (NodeType.Label인 경우 사용)
/// </summary>
public float FontSize { get; set; } = 12.0f;
/// <summary>
/// 라벨 폰트 스타일 (NodeType.Label인 경우 사용)
/// </summary>
public FontStyle FontStyle { get; set; } = FontStyle.Regular;
/// <summary>
/// 텍스트 전경색 (모든 노드 타입에서 사용)
/// </summary>
public Color ForeColor { get; set; } = Color.Black;
/// <summary>
/// 라벨 배경색 (NodeType.Label인 경우 사용)
/// </summary>
public Color BackColor { get; set; } = Color.Transparent;
private float _textFontSize = 7.0f; private float _textFontSize = 7.0f;
[Category("노드 텍스트"), DisplayName("TextSize")]
/// <summary> [Description("일반 노드 텍스트의 크기입니다.")]
/// 텍스트 폰트 크기 (모든 노드 타입의 텍스트 표시에 사용, 픽셀 단위) public float NodeTextFontSize
/// 0 이하의 값이 설정되면 기본값 7.0f로 자동 설정
/// </summary>
public float TextFontSize
{ {
get => _textFontSize; get => _textFontSize;
set => _textFontSize = value > 0 ? value : 7.0f; set => _textFontSize = value > 0 ? value : 7.0f;
} }
/// <summary> public MapNode() : base()
/// 텍스트 볼드체 여부 (모든 노드 타입의 텍스트 표시에 사용)
/// </summary>
public bool TextFontBold { get; set; } = true;
/// <summary>
/// 노드 이름 말풍선 배경색 (하단에 표시되는 노드 이름의 배경색)
/// </summary>
public Color NameBubbleBackColor { get; set; } = Color.Gold;
/// <summary>
/// 노드 이름 말풍선 글자색 (하단에 표시되는 노드 이름의 글자색)
/// </summary>
public Color NameBubbleForeColor { get; set; } = Color.Black;
/// <summary>
/// 라벨 배경 표시 여부 (NodeType.Label인 경우 사용)
/// </summary>
public bool ShowBackground { get; set; } = false;
/// <summary>
/// 라벨 패딩 (NodeType.Label인 경우 사용, 픽셀 단위)
/// </summary>
public int Padding { get; set; } = 8;
/// <summary>
/// 이미지 파일 경로 (편집용, 저장시엔 사용되지 않음)
/// </summary>
[Newtonsoft.Json.JsonIgnore]
public string ImagePath { get; set; } = string.Empty;
/// <summary>
/// Base64 인코딩된 이미지 데이터 (JSON 저장용)
/// </summary>
public string ImageBase64 { get; set; } = string.Empty;
/// <summary>
/// 이미지 크기 배율 (NodeType.Image인 경우 사용)
/// </summary>
public SizeF Scale { get; set; } = new SizeF(1.0f, 1.0f);
/// <summary>
/// 이미지 투명도 (NodeType.Image인 경우 사용, 0.0~1.0)
/// </summary>
public float Opacity { get; set; } = 1.0f;
/// <summary>
/// 이미지 회전 각도 (NodeType.Image인 경우 사용, 도 단위)
/// </summary>
public float Rotation { get; set; } = 0.0f;
/// <summary>
/// 로딩된 이미지 (런타임에서만 사용, JSON 직렬화 제외)
/// </summary>
[Newtonsoft.Json.JsonIgnore]
public Image LoadedImage { get; set; }
/// <summary>
/// 기본 생성자
/// </summary>
public MapNode()
{ {
Type = NodeType.Normal;
} }
/// <summary> public MapNode(string nodeId, Point position, StationType type) : base(nodeId, position)
/// 매개변수 생성자
/// </summary>
/// <param name="nodeId">노드 ID</param>
/// <param name="name">노드 이름</param>
/// <param name="position">위치</param>
/// <param name="type">노드 타입</param>
public MapNode(string nodeId, string name, Point position, NodeType type)
{ {
NodeId = nodeId; Type = NodeType.Normal;
Name = name;
Position = position;
Type = type;
CreatedDate = DateTime.Now;
ModifiedDate = DateTime.Now;
// 타입별 기본 색상 설정
SetDefaultColorByType(type);
} }
[Category("기본 정보")]
/// <summary> [JsonIgnore]
/// 노드 타입에 따른 기본 색상 설정 [ReadOnly(true), Browsable(false)]
/// </summary> public bool isDockingNode
/// <param name="type">노드 타입</param>
public void SetDefaultColorByType(NodeType type)
{ {
switch (type) get
{ {
case NodeType.Normal: if (StationType == StationType.Charger || StationType == StationType.Buffer ||
DisplayColor = Color.Blue; StationType == StationType.Clearner || StationType == StationType.Loader ||
break; StationType == StationType.UnLoader) return true;
case NodeType.UnLoader: return false;
case NodeType.Clearner:
case NodeType.Buffer:
case NodeType.Loader:
DisplayColor = Color.Green;
break;
case NodeType.Charging:
DisplayColor = Color.Red;
break;
case NodeType.Label:
DisplayColor = Color.Purple;
break;
case NodeType.Image:
DisplayColor = Color.Brown;
break;
} }
} }
/// <summary>
/// 다른 노드와의 연결 추가
/// </summary>
/// <param name="nodeId">연결할 노드 ID</param>
public void AddConnection(string nodeId) public void AddConnection(string nodeId)
{ {
if (!ConnectedNodes.Contains(nodeId)) if (!ConnectedNodes.Contains(nodeId))
@@ -304,10 +131,6 @@ namespace AGVNavigationCore.Models
} }
} }
/// <summary>
/// 다른 노드와의 연결 제거
/// </summary>
/// <param name="nodeId">연결 해제할 노드 ID</param>
public void RemoveConnection(string nodeId) public void RemoveConnection(string nodeId)
{ {
if (ConnectedNodes.Remove(nodeId)) if (ConnectedNodes.Remove(nodeId))
@@ -316,290 +139,29 @@ namespace AGVNavigationCore.Models
} }
} }
///// <summary>
///// 도킹 스테이션 설정
///// </summary>
///// <param name="stationId">장비 ID</param>
///// <param name="stationType">장비 타입</param>
///// <param name="dockDirection">도킹 방향</param>
//public void SetDockingStation(string stationId, StationType stationType, DockingDirection dockDirection)
//{
// Type = NodeType.Docking;
// NodeAlias = stationId;
// DockDirection = dockDirection;
// SetDefaultColorByType(NodeType.Docking);
// ModifiedDate = DateTime.Now;
//}
/// <summary>
/// 충전 스테이션 설정
/// </summary>
/// <param name="stationId">충전기 ID</param>
public void SetChargingStation(string stationId) public void SetChargingStation(string stationId)
{ {
Type = NodeType.Charging; StationType = StationType.Charger;
NodeAlias = stationId; Id = stationId;
DockDirection = DockingDirection.Forward; // 충전기는 항상 전진 도킹 DockDirection = DockingDirection.Forward;
SetDefaultColorByType(NodeType.Charging);
ModifiedDate = DateTime.Now; ModifiedDate = DateTime.Now;
} }
/// <summary>
/// 문자열 표현
/// </summary>
public override string ToString() public override string ToString()
{ {
return $"{RfidId}({NodeId}): {Name} ({Type}) at ({Position.X}, {Position.Y})"; return $"RFID:{RfidId}(NODE:{Id}): AS:{AliasName} ({Type}) at ({Position.X}, {Position.Y})";
} }
/// <summary>
/// 리스트박스 표시용 텍스트 (노드ID - 설명 - RFID 순서)
/// </summary>
public string DisplayText
{
get
{
var displayText = NodeId;
if (!string.IsNullOrEmpty(Name))
{
displayText += $" - {Name}";
}
if (!string.IsNullOrEmpty(RfidId))
{
displayText += $" - [{RfidId}]";
}
return displayText;
}
}
/// <summary>
/// 노드 복사
/// </summary>
/// <returns>복사된 노드</returns>
public MapNode Clone()
{
var clone = new MapNode
{
NodeId = NodeId,
Name = Name,
Position = Position,
Type = Type,
DockDirection = DockDirection,
ConnectedNodes = new List<string>(ConnectedNodes),
CanTurnLeft = CanTurnLeft,
CanTurnRight = CanTurnRight,
DisableCross = DisableCross,
NodeAlias = NodeAlias,
CreatedDate = CreatedDate,
ModifiedDate = ModifiedDate,
IsActive = IsActive,
DisplayColor = DisplayColor,
RfidId = RfidId,
RfidStatus = RfidStatus,
RfidDescription = RfidDescription,
LabelText = LabelText,
FontFamily = FontFamily,
FontSize = FontSize,
FontStyle = FontStyle,
ForeColor = ForeColor,
BackColor = BackColor,
TextFontSize = TextFontSize,
TextFontBold = TextFontBold,
NameBubbleBackColor = NameBubbleBackColor,
NameBubbleForeColor = NameBubbleForeColor,
ShowBackground = ShowBackground,
Padding = Padding,
ImagePath = ImagePath,
ImageBase64 = ImageBase64,
Scale = Scale,
Opacity = Opacity,
Rotation = Rotation
};
return clone;
}
/// <summary>
/// 이미지 로드 (Base64 또는 파일 경로에서, 256x256 이상일 경우 자동 리사이즈)
/// </summary>
/// <returns>로드 성공 여부</returns>
public bool LoadImage()
{
if (Type != NodeType.Image) return false;
try
{
Image originalImage = null;
// 1. 먼저 Base64 데이터 시도
if (!string.IsNullOrEmpty(ImageBase64))
{
originalImage = ImageConverterUtil.Base64ToImage(ImageBase64);
}
// 2. Base64가 없으면 파일 경로에서 로드
else if (!string.IsNullOrEmpty(ImagePath) && System.IO.File.Exists(ImagePath))
{
originalImage = Image.FromFile(ImagePath);
}
if (originalImage != null)
{
LoadedImage?.Dispose();
// 이미지 크기 체크 및 리사이즈
if (originalImage.Width > 256 || originalImage.Height > 256)
{
LoadedImage = ResizeImage(originalImage, 256, 256);
originalImage.Dispose();
}
else
{
LoadedImage = originalImage;
}
return true;
}
}
catch (Exception)
{
// 이미지 로드 실패
}
return false;
}
/// <summary>
/// 이미지 리사이즈 (비율 유지)
/// </summary>
/// <param name="image">원본 이미지</param>
/// <param name="maxWidth">최대 너비</param>
/// <param name="maxHeight">최대 높이</param>
/// <returns>리사이즈된 이미지</returns>
private Image ResizeImage(Image image, int maxWidth, int maxHeight)
{
// 비율 계산
double ratioX = (double)maxWidth / image.Width;
double ratioY = (double)maxHeight / image.Height;
double ratio = Math.Min(ratioX, ratioY);
// 새로운 크기 계산
int newWidth = (int)(image.Width * ratio);
int newHeight = (int)(image.Height * ratio);
// 리사이즈된 이미지 생성
var resizedImage = new Bitmap(newWidth, newHeight);
using (var graphics = Graphics.FromImage(resizedImage))
{
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.DrawImage(image, 0, 0, newWidth, newHeight);
}
return resizedImage;
}
/// <summary>
/// 실제 표시될 크기 계산 (이미지 노드인 경우)
/// </summary>
/// <returns>실제 크기</returns>
public Size GetDisplaySize()
{
if (Type != NodeType.Image || LoadedImage == null) return Size.Empty;
return new Size(
(int)(LoadedImage.Width * Scale.Width),
(int)(LoadedImage.Height * Scale.Height)
);
}
/// <summary>
/// 파일 경로에서 이미지를 Base64로 변환하여 저장
/// </summary>
/// <param name="filePath">이미지 파일 경로</param>
/// <returns>변환 성공 여부</returns>
public bool ConvertImageToBase64(string filePath)
{
if (Type != NodeType.Image) return false;
try
{
if (!System.IO.File.Exists(filePath))
{
return false;
}
ImageBase64 = ImageConverterUtil.FileToBase64(filePath, System.Drawing.Imaging.ImageFormat.Png);
ImagePath = filePath; // 편집용으로 경로 유지
return !string.IsNullOrEmpty(ImageBase64);
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 리소스 정리
/// </summary>
public void Dispose()
{
LoadedImage?.Dispose();
LoadedImage = null;
}
/// <summary>
/// 경로 찾기에 사용 가능한 노드인지 확인
/// (라벨, 이미지 노드는 경로 찾기에서 제외)
/// </summary>
public bool IsNavigationNode() public bool IsNavigationNode()
{ {
return Type != NodeType.Label && Type != NodeType.Image && IsActive; // 이제 MapNode는 항상 내비게이션 노드임 (Label, Image 분리됨)
// 하지만 기존 로직 호환성을 위해 Active 체크만 유지
return IsActive;
} }
/// <summary>
/// RFID가 할당되어 있는지 확인
/// </summary>
public bool HasRfid() public bool HasRfid()
{ {
return !string.IsNullOrEmpty(RfidId); return RfidId > 0;
}
/// <summary>
/// RFID 정보 설정
/// </summary>
/// <param name="rfidId">RFID ID</param>
/// <param name="rfidDescription">설치 위치 설명</param>
/// <param name="rfidStatus">RFID 상태</param>
public void SetRfidInfo(string rfidId, string rfidDescription = "", string rfidStatus = "정상")
{
RfidId = rfidId;
RfidDescription = rfidDescription;
RfidStatus = rfidStatus;
ModifiedDate = DateTime.Now;
}
/// <summary>
/// RFID 정보 삭제
/// </summary>
public void ClearRfidInfo()
{
RfidId = string.Empty;
RfidDescription = string.Empty;
RfidStatus = "정상";
ModifiedDate = DateTime.Now;
}
/// <summary>
/// RFID 기반 표시 텍스트 (RFID ID 우선, 없으면 노드ID)
/// </summary>
public string GetRfidDisplayText()
{
return HasRfid() ? RfidId : NodeId;
} }
} }
} }

View File

@@ -0,0 +1,61 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using Newtonsoft.Json;
namespace AGVNavigationCore.Models
{
/// <summary>
/// 맵 상의 모든 객체의 최상위 기본 클래스
/// 위치, 선택 상태, 기본 식별자 등을 관리
/// </summary>
public abstract class NodeBase
{
[Category("기본 정보")]
[Description("객체의 고유 ID입니다.")]
[ReadOnly(true)]
public string Id { get; set; } = Guid.NewGuid().ToString();
[Category("기본 정보")]
public NodeType Type { protected set; get; } = NodeType.Normal;
[Category("기본 정보")]
[Description("객체의 좌표(X, Y)입니다.")]
public virtual Point Position { get; set; } = Point.Empty;
[Category("기본 정보")]
[Description("객체 생성 일자입니다.")]
[JsonIgnore]
[ReadOnly(true), Browsable(false)]
public DateTime CreatedDate { get; set; } = DateTime.Now;
[Category("기본 정보")]
[Description("객체 수정 일자입니다.")]
[JsonIgnore]
[ReadOnly(true), Browsable(false)]
public DateTime ModifiedDate { get; set; } = DateTime.Now;
[Browsable(false)]
[JsonIgnore]
public bool IsSelected { get; set; } = false;
[Browsable(false)]
[JsonIgnore]
public bool IsHovered { get; set; } = false;
public NodeBase()
{
}
public NodeBase(string id, Point position)
{
Id = id;
Position = position;
}
}
}

View File

@@ -146,7 +146,12 @@ namespace AGVNavigationCore.Models
/// <summary> /// <summary>
/// 현재 노드 ID /// 현재 노드 ID
/// </summary> /// </summary>
public string CurrentNodeId => _currentNode?.NodeId; public MapNode CurrentNode => _currentNode;
/// <summary>
/// 현재 노드 ID (CurrentNode.Id)
/// </summary>
public string CurrentNodeId => _currentNode?.Id;
/// <summary> /// <summary>
/// 이전 위치 /// 이전 위치
@@ -158,10 +163,6 @@ namespace AGVNavigationCore.Models
/// </summary> /// </summary>
public float BatteryLevel { get; set; } = 100.0f; public float BatteryLevel { get; set; } = 100.0f;
/// <summary>
/// 이전 노드 ID
/// </summary>
public string PrevNodeId => _prevNode?.NodeId;
/// <summary> /// <summary>
/// 이전 노드 /// 이전 노드
@@ -188,6 +189,11 @@ namespace AGVNavigationCore.Models
/// </summary> /// </summary>
public int DetectedRfidCount => _detectedRfids.Count; public int DetectedRfidCount => _detectedRfids.Count;
/// <summary>
/// 배터리 부족 경고 임계값 (%)
/// </summary>
public float LowBatteryThreshold { get; set; } = 20.0f;
#endregion #endregion
#region Constructor #region Constructor
@@ -262,9 +268,9 @@ namespace AGVNavigationCore.Models
BatteryLevel = Math.Max(0, Math.Min(100, percentage)); BatteryLevel = Math.Max(0, Math.Min(100, percentage));
// 배터리 부족 경고 // 배터리 부족 경고
if (BatteryLevel < 20.0f && _currentState != AGVState.Charging) if (BatteryLevel < LowBatteryThreshold && _currentState != AGVState.Charging)
{ {
OnError($"배터리 부족: {BatteryLevel:F1}%"); OnError($"배터리 부족: {BatteryLevel:F1}% (기준: {LowBatteryThreshold}%)");
} }
} }
@@ -274,6 +280,7 @@ namespace AGVNavigationCore.Models
public bool SetCurrentNodeMarkStop() public bool SetCurrentNodeMarkStop()
{ {
if (_currentNode == null) return false; if (_currentNode == null) return false;
if (_currentPath == null) return false;
var = _currentPath.DetailedPath.Where(t => t.IsPass == false).OrderBy(t => t.seq).FirstOrDefault(); var = _currentPath.DetailedPath.Where(t => t.IsPass == false).OrderBy(t => t.seq).FirstOrDefault();
if ( == null) return false; if ( == null) return false;
.IsPass = true; .IsPass = true;
@@ -309,7 +316,7 @@ namespace AGVNavigationCore.Models
MagnetPosition.S, MagnetPosition.S,
SpeedLevel.L, SpeedLevel.L,
eAGVCommandReason.NoPath, eAGVCommandReason.NoPath,
$"위치 확정 완료 (목적지 미설정) - 현재:{_currentNode?.NodeId ?? ""}" $"위치 확정 완료 (목적지 미설정) - 현재:{_currentNode?.Id ?? ""}"
); );
} }
@@ -318,7 +325,7 @@ namespace AGVNavigationCore.Models
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)
{ {
// 마지막 노드에 도착했는지 확인 (현재 노드가 마지막 노드와 같은지) // 마지막 노드에 도착했는지 확인 (현재 노드가 마지막 노드와 같은지)
if (_currentNode != null && _currentNode.NodeId == lastNode.NodeId) if (_currentNode != null && _currentNode.Id == lastNode.NodeId)
{ {
if (lastNode.IsPass) //이미완료되었다. if (lastNode.IsPass) //이미완료되었다.
{ {
@@ -327,7 +334,7 @@ namespace AGVNavigationCore.Models
MagnetPosition.S, MagnetPosition.S,
SpeedLevel.L, SpeedLevel.L,
eAGVCommandReason.Complete, eAGVCommandReason.Complete,
$"목적지 도착 - 최종:{_currentNode?.NodeId ?? ""}" $"목적지 도착 - 최종:{_currentNode?.Id ?? ""}"
); );
} }
else else
@@ -338,7 +345,7 @@ namespace AGVNavigationCore.Models
MagnetPosition.S, MagnetPosition.S,
SpeedLevel.L, SpeedLevel.L,
eAGVCommandReason.MarkStop, eAGVCommandReason.MarkStop,
$"목적지 도착 전(MarkStop) - 최종:{_currentNode?.NodeId ?? ""}" $"목적지 도착 전(MarkStop) - 최종:{_currentNode?.Id ?? ""}"
); );
} }
@@ -346,7 +353,7 @@ namespace AGVNavigationCore.Models
} }
// 4. 경로이탈 // 4. 경로이탈
var TargetNode = _currentPath.DetailedPath.Where(t => t.IsPass == false && t.NodeId.Equals(_currentNode.NodeId)).FirstOrDefault(); var TargetNode = _currentPath.DetailedPath.Where(t => t.IsPass == false && t.NodeId.Equals(_currentNode.Id)).FirstOrDefault();
if (TargetNode == null) if (TargetNode == null)
{ {
return new AGVCommand( return new AGVCommand(
@@ -354,11 +361,11 @@ namespace AGVNavigationCore.Models
MagnetPosition.S, MagnetPosition.S,
SpeedLevel.L, SpeedLevel.L,
eAGVCommandReason.PathOut, eAGVCommandReason.PathOut,
$"(재탐색요청)경로이탈 현재위치:{_currentNode.NodeId}" $"(재탐색요청)경로이탈 현재위치:{_currentNode.Id}"
); );
} }
return GetCommandFromPath(CurrentNodeId, "경로 실행 시작"); return GetCommandFromPath(CurrentNode, "경로 실행 시작");
} }
@@ -409,13 +416,13 @@ namespace AGVNavigationCore.Models
} }
_currentPath = path; _currentPath = path;
_remainingNodes = path.Path.Select(n => n.NodeId).ToList(); // MapNode → NodeId 변환 _remainingNodes = path.Path.Select(n => n.Id).ToList(); // MapNode → NodeId 변환
_currentNodeIndex = 0; _currentNodeIndex = 0;
// 경로 시작 노드가 현재 노드와 다른 경우 경고 // 경로 시작 노드가 현재 노드와 다른 경우 경고
if (_currentNode != null && _remainingNodes.Count > 0 && _remainingNodes[0] != _currentNode.NodeId) if (_currentNode != null && _remainingNodes.Count > 0 && _remainingNodes[0] != _currentNode.Id)
{ {
OnError($"경로 시작 노드({_remainingNodes[0]})와 현재 노드({_currentNode.NodeId})가 다릅니다."); OnError($"경로 시작 노드({_remainingNodes[0]})와 현재 노드({_currentNode.Id})가 다릅니다.");
} }
} }
@@ -550,7 +557,7 @@ namespace AGVNavigationCore.Models
public void SetPosition(MapNode node, AgvDirection motorDirection) public void SetPosition(MapNode node, AgvDirection motorDirection)
{ {
// 현재 위치를 이전 위치로 저장 (리프트 방향 계산용) // 현재 위치를 이전 위치로 저장 (리프트 방향 계산용)
if (_currentNode != null && _currentNode.NodeId != node.NodeId) if (_currentNode != null && _currentNode.Id != node.Id)
{ {
_prevPosition = _currentPosition; // 이전 위치 _prevPosition = _currentPosition; // 이전 위치
_prevNode = _currentNode; _prevNode = _currentNode;
@@ -569,9 +576,9 @@ namespace AGVNavigationCore.Models
_currentNode = node; _currentNode = node;
// 🔥 노드 ID를 RFID로 간주하여 감지 목록에 추가 (시뮬레이터용) // 🔥 노드 ID를 RFID로 간주하여 감지 목록에 추가 (시뮬레이터용)
if (!string.IsNullOrEmpty(node.NodeId) && !_detectedRfids.Contains(node.NodeId)) if (!string.IsNullOrEmpty(node.Id) && !_detectedRfids.Contains(node.Id))
{ {
_detectedRfids.Add(node.NodeId); _detectedRfids.Add(node.Id);
} }
// 🔥 RFID 2개 이상 감지 시 위치 확정 // 🔥 RFID 2개 이상 감지 시 위치 확정
@@ -583,9 +590,18 @@ namespace AGVNavigationCore.Models
//현재 경로값이 있는지 확인한다. //현재 경로값이 있는지 확인한다.
if (CurrentPath != null && CurrentPath.DetailedPath != null && CurrentPath.DetailedPath.Any()) if (CurrentPath != null && CurrentPath.DetailedPath != null && CurrentPath.DetailedPath.Any())
{ {
var item = CurrentPath.DetailedPath.FirstOrDefault(t => t.NodeId == node.NodeId && t.IsPass == false); var item = CurrentPath.DetailedPath.FirstOrDefault(t => t.NodeId == node.Id && t.IsPass == false);
if (item != null) if (item != null)
{ {
// [PathJump Check] 점프한 노드 개수 확인
// 현재 노드(item)보다 이전인데 아직 IsPass가 안 된 노드의 개수
int skippedCount = CurrentPath.DetailedPath.Count(t => t.seq < item.seq && t.IsPass == false);
if (skippedCount > 2)
{
OnError($"PathJump: {skippedCount}개의 노드를 건너뛰었습니다. (허용: 2개, 현재노드: {node.Id})");
return;
}
//item.IsPass = true; //item.IsPass = true;
//이전노드는 모두 지나친걸로 한다 //이전노드는 모두 지나친걸로 한다
CurrentPath.DetailedPath.Where(t => t.seq < item.seq).ToList().ForEach(t => t.IsPass = true); CurrentPath.DetailedPath.Where(t => t.seq < item.seq).ToList().ForEach(t => t.IsPass = true);
@@ -596,17 +612,6 @@ namespace AGVNavigationCore.Models
PositionChanged?.Invoke(this, (_currentPosition, _currentDirection, _currentNode)); PositionChanged?.Invoke(this, (_currentPosition, _currentDirection, _currentNode));
} }
/// <summary>
/// 현재 RFID 시뮬레이션 (현재 위치 기준)
/// </summary>
public string SimulateRfidReading(List<MapNode> mapNodes)
{
var closestNode = FindClosestNode(_currentPosition, mapNodes);
if (closestNode == null)
return null;
return closestNode.HasRfid() ? closestNode.RfidId : null;
}
#endregion #endregion
@@ -615,10 +620,11 @@ namespace AGVNavigationCore.Models
/// <summary> /// <summary>
/// 노드 ID를 RFID 값으로 변환 (NodeResolver 사용) /// 노드 ID를 RFID 값으로 변환 (NodeResolver 사용)
/// </summary> /// </summary>
public string GetRfidByNodeId(List<MapNode> _mapNodes, string nodeId) public ushort GetRfidByNodeId(List<MapNode> _mapNodes, string nodeId)
{ {
var node = _mapNodes?.FirstOrDefault(n => n.NodeId == nodeId); var node = _mapNodes?.FirstOrDefault(n => n.Id == nodeId);
return node?.HasRfid() == true ? node.RfidId : nodeId; if ((node?.HasRfid() ?? false) == false) return 0;
return node.RfidId;
} }
@@ -628,19 +634,16 @@ namespace AGVNavigationCore.Models
/// <summary> /// <summary>
/// DetailedPath에서 노드 정보를 찾아 AGVCommand 생성 /// DetailedPath에서 노드 정보를 찾아 AGVCommand 생성
/// </summary> /// </summary>
private AGVCommand GetCommandFromPath(string targetNodeId, string actionDescription) private AGVCommand GetCommandFromPath(MapNode targetNode, string actionDescription)
{ {
// DetailedPath가 없으면 기본 명령 반환 // DetailedPath가 없으면 기본 명령 반환
if (_currentPath == null || _currentPath.DetailedPath == null || _currentPath.DetailedPath.Count == 0) if (_currentPath == null || _currentPath.DetailedPath == null || _currentPath.DetailedPath.Count == 0)
{ {
var defaultMotor = _currentDirection == AgvDirection.Forward // [Refactor] Predict와 일관성 유지: 경로가 없으면 정지
? MotorCommand.Forward
: MotorCommand.Backward;
return new AGVCommand( return new AGVCommand(
defaultMotor, MotorCommand.Stop,
MagnetPosition.S, MagnetPosition.S,
SpeedLevel.M, SpeedLevel.L,
eAGVCommandReason.NoPath, eAGVCommandReason.NoPath,
$"{actionDescription} (DetailedPath 없음)" $"{actionDescription} (DetailedPath 없음)"
); );
@@ -648,7 +651,7 @@ namespace AGVNavigationCore.Models
// DetailedPath에서 targetNodeId에 해당하는 NodeMotorInfo 찾기 // DetailedPath에서 targetNodeId에 해당하는 NodeMotorInfo 찾기
// 지나가지 않은 경로를 찾는다 // 지나가지 않은 경로를 찾는다
var nodeInfo = _currentPath.DetailedPath.FirstOrDefault(n => n.NodeId == targetNodeId && n.IsPass == false); var nodeInfo = _currentPath.DetailedPath.FirstOrDefault(n => n.NodeId == targetNode.Id && n.IsPass == false);
if (nodeInfo == null) if (nodeInfo == null)
{ {
@@ -662,7 +665,7 @@ namespace AGVNavigationCore.Models
MagnetPosition.S, MagnetPosition.S,
SpeedLevel.M, SpeedLevel.M,
eAGVCommandReason.NoTarget, eAGVCommandReason.NoTarget,
$"{actionDescription} (노드 {targetNodeId} 정보 없음)" $"{actionDescription} (노드 {targetNode.Id} 정보 없음)"
); );
} }
@@ -697,17 +700,20 @@ namespace AGVNavigationCore.Models
break; break;
} }
// 속도 결정 (회전 노드면 저속, 일반 이동은 중속) // [Speed Control] NodeMotorInfo에 설정된 속도 사용
SpeedLevel speed = nodeInfo.CanRotate || nodeInfo.IsDirectionChangePoint // 단, 회전 구간 등에서 안전을 위해 강제 감속이 필요한 경우 로직 추가 가능
? SpeedLevel.L // 현재는 사용자 설정 우선
: SpeedLevel.M; SpeedLevel speed = nodeInfo.Speed;
// Optional: 회전 시 강제 감속 로직 (사용자 요청에 따라 주석 처리 또는 제거 가능)
// if (nodeInfo.CanRotate || nodeInfo.IsDirectionChangePoint) speed = SpeedLevel.L;
return new AGVCommand( return new AGVCommand(
motorCmd, motorCmd,
magnetPos, magnetPos,
speed, speed,
eAGVCommandReason.Normal, eAGVCommandReason.Normal,
$"{actionDescription} → {targetNodeId} (Motor:{motorCmd}, Magnet:{magnetPos})" $"{actionDescription} → {targetNode.Id} (Motor:{motorCmd}, Magnet:{magnetPos})"
); );
} }
@@ -864,21 +870,6 @@ namespace AGVNavigationCore.Models
} }
} }
private DockingDirection GetDockingDirection(NodeType nodeType)
{
switch (nodeType)
{
case NodeType.Charging:
return DockingDirection.Forward;
case NodeType.Loader:
case NodeType.UnLoader:
case NodeType.Clearner:
case NodeType.Buffer:
return DockingDirection.Backward;
default:
return DockingDirection.Forward;
}
}
private void OnError(string message) private void OnError(string message)
{ {

View File

@@ -59,7 +59,7 @@ namespace AGVNavigationCore.PathFinding.Analysis
if (node.IsNavigationNode()) if (node.IsNavigationNode())
{ {
var junctionInfo = AnalyzeNode(node); var junctionInfo = AnalyzeNode(node);
_junctions[node.NodeId] = junctionInfo; _junctions[node.Id] = junctionInfo;
} }
} }
} }
@@ -69,7 +69,7 @@ namespace AGVNavigationCore.PathFinding.Analysis
/// </summary> /// </summary>
private JunctionInfo AnalyzeNode(MapNode node) private JunctionInfo AnalyzeNode(MapNode node)
{ {
var junction = new JunctionInfo(node.NodeId); var junction = new JunctionInfo(node.Id);
// 양방향 연결을 고려하여 모든 연결된 노드 찾기 // 양방향 연결을 고려하여 모든 연결된 노드 찾기
var connectedNodes = GetAllConnectedNodes(node); var connectedNodes = GetAllConnectedNodes(node);
@@ -96,16 +96,16 @@ namespace AGVNavigationCore.PathFinding.Analysis
{ {
if (connectedNode != null) if (connectedNode != null)
{ {
connected.Add(connectedNode.NodeId); connected.Add(connectedNode.Id);
} }
} }
// 역방향 연결된 노드들 (다른 노드에서 이 노드로 연결) // 역방향 연결된 노드들 (다른 노드에서 이 노드로 연결)
foreach (var otherNode in _mapNodes) foreach (var otherNode in _mapNodes)
{ {
if (otherNode.NodeId != node.NodeId && otherNode.ConnectedMapNodes.Any(n => n.NodeId == node.NodeId)) if (otherNode.Id != node.Id && otherNode.ConnectedMapNodes.Any(n => n.Id == node.Id))
{ {
connected.Add(otherNode.NodeId); connected.Add(otherNode.Id);
} }
} }
@@ -124,7 +124,7 @@ namespace AGVNavigationCore.PathFinding.Analysis
foreach (var connectedId in connectedNodes) foreach (var connectedId in connectedNodes)
{ {
var connectedNode = _mapNodes.FirstOrDefault(n => n.NodeId == connectedId); var connectedNode = _mapNodes.FirstOrDefault(n => n.Id == connectedId);
if (connectedNode != null) if (connectedNode != null)
{ {
double angle = CalculateAngle(junctionNode.Position, connectedNode.Position); double angle = CalculateAngle(junctionNode.Position, connectedNode.Position);
@@ -226,9 +226,9 @@ namespace AGVNavigationCore.PathFinding.Analysis
return MagnetDirection.Straight; return MagnetDirection.Straight;
// 실제 각도 기반으로 마그넷 방향 계산 // 실제 각도 기반으로 마그넷 방향 계산
var fromNode = _mapNodes.FirstOrDefault(n => n.NodeId == fromNodeId); var fromNode = _mapNodes.FirstOrDefault(n => n.Id == fromNodeId);
var currentNode = _mapNodes.FirstOrDefault(n => n.NodeId == currentNodeId); var currentNode = _mapNodes.FirstOrDefault(n => n.Id == currentNodeId);
var toNode = _mapNodes.FirstOrDefault(n => n.NodeId == toNodeId); var toNode = _mapNodes.FirstOrDefault(n => n.Id == toNodeId);
if (fromNode == null || currentNode == null || toNode == null) if (fromNode == null || currentNode == null || toNode == null)
return MagnetDirection.Straight; return MagnetDirection.Straight;

View File

@@ -320,7 +320,7 @@ namespace AGVNavigationCore.PathFinding.Core
{ {
return DetailedPath.Select(n => n.NodeId).ToList(); return DetailedPath.Select(n => n.NodeId).ToList();
} }
return Path?.Select(n => n.NodeId).ToList() ?? new List<string>(); return Path?.Select(n => n.Id).ToList() ?? new List<string>();
} }
/// <summary> /// <summary>

View File

@@ -50,36 +50,36 @@ namespace AGVNavigationCore.PathFinding.Core
// 모든 네비게이션 노드를 PathNode로 변환하고 양방향 연결 생성 // 모든 네비게이션 노드를 PathNode로 변환하고 양방향 연결 생성
foreach (var mapNode in _mapNodes) foreach (var mapNode in _mapNodes)
{ {
_mapNodeLookup[mapNode.NodeId] = mapNode; // Add to lookup table _mapNodeLookup[mapNode.Id] = mapNode; // Add to lookup table
if (mapNode.IsNavigationNode()) if (mapNode.IsNavigationNode())
{ {
var pathNode = new PathNode(mapNode.NodeId, mapNode.Position); var pathNode = new PathNode(mapNode.Id, mapNode.Position);
_nodeMap[mapNode.NodeId] = pathNode; _nodeMap[mapNode.Id] = pathNode;
} }
} }
// 단일 연결을 양방향으로 확장 // 단일 연결을 양방향으로 확장
foreach (var mapNode in _mapNodes) foreach (var mapNode in _mapNodes)
{ {
if (mapNode.IsNavigationNode() && _nodeMap.ContainsKey(mapNode.NodeId)) if (mapNode.IsNavigationNode() && _nodeMap.ContainsKey(mapNode.Id))
{ {
var pathNode = _nodeMap[mapNode.NodeId]; var pathNode = _nodeMap[mapNode.Id];
foreach (var connectedNode in mapNode.ConnectedMapNodes) foreach (var connectedNode in mapNode.ConnectedMapNodes)
{ {
if (connectedNode != null && _nodeMap.ContainsKey(connectedNode.NodeId)) if (connectedNode != null && _nodeMap.ContainsKey(connectedNode.Id))
{ {
// 양방향 연결 생성 (단일 연결이 양방향을 의미) // 양방향 연결 생성 (단일 연결이 양방향을 의미)
if (!pathNode.ConnectedNodes.Contains(connectedNode.NodeId)) if (!pathNode.ConnectedNodes.Contains(connectedNode.Id))
{ {
pathNode.ConnectedNodes.Add(connectedNode.NodeId); pathNode.ConnectedNodes.Add(connectedNode.Id);
} }
var connectedPathNode = _nodeMap[connectedNode.NodeId]; var connectedPathNode = _nodeMap[connectedNode.Id];
if (!connectedPathNode.ConnectedNodes.Contains(mapNode.NodeId)) if (!connectedPathNode.ConnectedNodes.Contains(mapNode.Id))
{ {
connectedPathNode.ConnectedNodes.Add(mapNode.NodeId); connectedPathNode.ConnectedNodes.Add(mapNode.Id);
} }
} }
} }
@@ -371,8 +371,8 @@ namespace AGVNavigationCore.PathFinding.Core
var combinedDetailedPath = new List<NodeMotorInfo>(previousResult.DetailedPath ?? new List<NodeMotorInfo>()); var combinedDetailedPath = new List<NodeMotorInfo>(previousResult.DetailedPath ?? new List<NodeMotorInfo>());
// 이전 경로의 마지막 노드와 현재 경로의 시작 노드 비교 // 이전 경로의 마지막 노드와 현재 경로의 시작 노드 비교
string lastNodeOfPrevious = previousResult.Path[previousResult.Path.Count - 1].NodeId; string lastNodeOfPrevious = previousResult.Path[previousResult.Path.Count - 1].Id;
string firstNodeOfCurrent = currentResult.Path[0].NodeId; string firstNodeOfCurrent = currentResult.Path[0].Id;
if (lastNodeOfPrevious == firstNodeOfCurrent) if (lastNodeOfPrevious == firstNodeOfCurrent)
{ {
@@ -508,8 +508,8 @@ namespace AGVNavigationCore.PathFinding.Core
float totalDistance = 0; float totalDistance = 0;
for (int i = 0; i < path.Count - 1; i++) for (int i = 0; i < path.Count - 1; i++)
{ {
var nodeId1 = path[i].NodeId; var nodeId1 = path[i].Id;
var nodeId2 = path[i + 1].NodeId; var nodeId2 = path[i + 1].Id;
if (_nodeMap.ContainsKey(nodeId1) && _nodeMap.ContainsKey(nodeId2)) if (_nodeMap.ContainsKey(nodeId1) && _nodeMap.ContainsKey(nodeId2))
{ {
@@ -577,7 +577,7 @@ namespace AGVNavigationCore.PathFinding.Core
return null; return null;
// 교차로 노드 찾기 // 교차로 노드 찾기
var junctionNode = _mapNodes.FirstOrDefault(n => n.NodeId == junctionNodeId); var junctionNode = _mapNodes.FirstOrDefault(n => n.Id == junctionNodeId);
if (junctionNode == null || junctionNode.ConnectedNodes == null || junctionNode.ConnectedNodes.Count == 0) if (junctionNode == null || junctionNode.ConnectedNodes == null || junctionNode.ConnectedNodes.Count == 0)
return null; return null;
@@ -602,7 +602,7 @@ namespace AGVNavigationCore.PathFinding.Core
if (connectedNodeId == targetNodeId) continue; if (connectedNodeId == targetNodeId) continue;
// 조건 3, 4, 5: 존재하고, 활성 상태이고, 네비게이션 가능 // 조건 3, 4, 5: 존재하고, 활성 상태이고, 네비게이션 가능
var connectedNode = _mapNodes.FirstOrDefault(n => n.NodeId == connectedNodeId); var connectedNode = _mapNodes.FirstOrDefault(n => n.Id == connectedNodeId);
if (connectedNode != null && connectedNode.IsActive && connectedNode.IsNavigationNode()) if (connectedNode != null && connectedNode.IsActive && connectedNode.IsNavigationNode())
{ {
alternateNodes.Add(connectedNode); alternateNodes.Add(connectedNode);

View File

@@ -50,7 +50,7 @@ namespace AGVNavigationCore.PathFinding.Planning
n.DisableCross == false && n.DisableCross == false &&
n.ConnectedNodes.Count >= 3 && n.ConnectedNodes.Count >= 3 &&
n.ConnectedMapNodes.Where(t => t.CanDocking).Any() == false && n.ConnectedMapNodes.Where(t => t.CanDocking).Any() == false &&
n.NodeId != startNode.NodeId n.Id != startNode.Id
).ToList(); ).ToList();
// docking 포인트가 연결된 노드는 제거한다. // docking 포인트가 연결된 노드는 제거한다.
@@ -103,7 +103,7 @@ namespace AGVNavigationCore.PathFinding.Planning
pathNode.ConnectedNodes.Count >= 3 && pathNode.ConnectedNodes.Count >= 3 &&
pathNode.ConnectedMapNodes.Where(t => t.CanDocking).Any() == false) pathNode.ConnectedMapNodes.Where(t => t.CanDocking).Any() == false)
{ {
if (pathNode.NodeId.Equals(StartNode.NodeId) == false) if (pathNode.Id.Equals(StartNode.Id) == false)
return pathNode; return pathNode;
} }
} }
@@ -111,6 +111,12 @@ namespace AGVNavigationCore.PathFinding.Planning
return null; return null;
} }
public AGVPathResult FindPath(MapNode startNode, MapNode targetNode)
{
// 기본값으로 경로 탐색 (이전 위치 = 현재 위치, 방향 = 전진)
return FindPath(startNode, targetNode, startNode, AgvDirection.Forward, AgvDirection.Forward, false);
}
public AGVPathResult FindPath(MapNode startNode, MapNode targetNode, public AGVPathResult FindPath(MapNode startNode, MapNode targetNode,
MapNode prevNode, AgvDirection prevDirection, AgvDirection currentDirection, bool crossignore = false) MapNode prevNode, AgvDirection prevDirection, AgvDirection currentDirection, bool crossignore = false)
{ {
@@ -121,13 +127,17 @@ namespace AGVNavigationCore.PathFinding.Planning
return AGVPathResult.CreateFailure("목적지 노드가 null입니다.", 0, 0); return AGVPathResult.CreateFailure("목적지 노드가 null입니다.", 0, 0);
if (prevNode == null) if (prevNode == null)
return AGVPathResult.CreateFailure("이전위치 노드가 null입니다.", 0, 0); return AGVPathResult.CreateFailure("이전위치 노드가 null입니다.", 0, 0);
if (startNode.NodeId == targetNode.NodeId && targetNode.DockDirection.MatchAGVDirection(prevDirection)) if (targetNode.isDockingNode == false && targetNode.Type != NodeType.Normal)
return AGVPathResult.CreateFailure("이동 가능한 노드가 아닙니다", 0, 0);
var tnode = targetNode as MapNode;
if (startNode.Id == targetNode.Id && tnode.DockDirection.MatchAGVDirection(prevDirection))
return AGVPathResult.CreateSuccess(new List<MapNode> { startNode, startNode }, new List<AgvDirection>(), 0, 0); return AGVPathResult.CreateSuccess(new List<MapNode> { startNode, startNode }, new List<AgvDirection>(), 0, 0);
var ReverseDirection = (currentDirection == AgvDirection.Forward ? AgvDirection.Backward : AgvDirection.Forward); var ReverseDirection = (currentDirection == AgvDirection.Forward ? AgvDirection.Backward : AgvDirection.Forward);
//1.목적지까지의 최단거리 경로를 찾는다. //1.목적지까지의 최단거리 경로를 찾는다.
var pathResult = _basicPathfinder.FindPathAStar(startNode.NodeId, targetNode.NodeId); var pathResult = _basicPathfinder.FindPathAStar(startNode.Id, targetNode.Id);
pathResult.PrevNode = prevNode; pathResult.PrevNode = prevNode;
pathResult.PrevDirection = prevDirection; pathResult.PrevDirection = prevDirection;
if (!pathResult.Success || pathResult.Path == null || pathResult.Path.Count == 0) if (!pathResult.Success || pathResult.Path == null || pathResult.Path.Count == 0)
@@ -146,11 +156,11 @@ namespace AGVNavigationCore.PathFinding.Planning
//2.AGV방향과 목적지에 설정된 방향이 일치하면 그대로 진행하면된다.(목적지에 방향이 없는 경우에도 그대로 진행) //2.AGV방향과 목적지에 설정된 방향이 일치하면 그대로 진행하면된다.(목적지에 방향이 없는 경우에도 그대로 진행)
if (targetNode.DockDirection == DockingDirection.DontCare || if (tnode.DockDirection == DockingDirection.DontCare ||
(targetNode.DockDirection == DockingDirection.Forward && currentDirection == AgvDirection.Forward) || (tnode.DockDirection == DockingDirection.Forward && currentDirection == AgvDirection.Forward) ||
(targetNode.DockDirection == DockingDirection.Backward && currentDirection == AgvDirection.Backward)) (tnode.DockDirection == DockingDirection.Backward && currentDirection == AgvDirection.Backward))
{ {
if ((nextNodeForward?.NodeId ?? string.Empty) == pathResult.Path[1].NodeId) //예측경로와 다음진행방향 경로가 일치하면 해당 방향이 맞다 if ((nextNodeForward?.Id ?? string.Empty) == pathResult.Path[1].Id) //예측경로와 다음진행방향 경로가 일치하면 해당 방향이 맞다
{ {
MakeDetailData(pathResult, currentDirection); MakeDetailData(pathResult, currentDirection);
MakeMagnetDirection(pathResult); MakeMagnetDirection(pathResult);
@@ -177,10 +187,10 @@ namespace AGVNavigationCore.PathFinding.Planning
//뒤로 이동시 경로상의 처음 만나는 노드가 같다면 그 방향으로 이동하면 된다. //뒤로 이동시 경로상의 처음 만나는 노드가 같다면 그 방향으로 이동하면 된다.
// ⚠️ 단, 현재 방향과 목적지 도킹 방향이 일치해야 함! // ⚠️ 단, 현재 방향과 목적지 도킹 방향이 일치해야 함!
if (nextNodeBackward != null && pathResult.Path.Count > 1 && if (nextNodeBackward != null && pathResult.Path.Count > 1 &&
nextNodeBackward.NodeId == pathResult.Path[1].NodeId) // ✅ 추가: 현재도 Backward여야 함 nextNodeBackward.Id == pathResult.Path[1].Id) // ✅ 추가: 현재도 Backward여야 함
{ {
if (targetNode.DockDirection == DockingDirection.Forward && ReverseDirection == AgvDirection.Forward || if (tnode.DockDirection == DockingDirection.Forward && ReverseDirection == AgvDirection.Forward ||
targetNode.DockDirection == DockingDirection.Backward && ReverseDirection == AgvDirection.Backward) tnode.DockDirection == DockingDirection.Backward && ReverseDirection == AgvDirection.Backward)
{ {
MakeDetailData(pathResult, ReverseDirection); MakeDetailData(pathResult, ReverseDirection);
MakeMagnetDirection(pathResult); MakeMagnetDirection(pathResult);
@@ -191,12 +201,12 @@ namespace AGVNavigationCore.PathFinding.Planning
} }
if (nextNodeForward != null && pathResult.Path.Count > 1 && if (nextNodeForward != null && pathResult.Path.Count > 1 &&
nextNodeForward.NodeId == pathResult.Path[1].NodeId && nextNodeForward.Id == pathResult.Path[1].Id &&
targetNode.DockDirection == DockingDirection.Forward && tnode.DockDirection == DockingDirection.Forward &&
currentDirection == AgvDirection.Forward) // ✅ 추가: 현재도 Forward여야 함 currentDirection == AgvDirection.Forward) // ✅ 추가: 현재도 Forward여야 함
{ {
if (targetNode.DockDirection == DockingDirection.Forward && currentDirection == AgvDirection.Forward || if (tnode.DockDirection == DockingDirection.Forward && currentDirection == AgvDirection.Forward ||
targetNode.DockDirection == DockingDirection.Backward && currentDirection == AgvDirection.Backward) tnode.DockDirection == DockingDirection.Backward && currentDirection == AgvDirection.Backward)
{ {
MakeDetailData(pathResult, currentDirection); MakeDetailData(pathResult, currentDirection);
MakeMagnetDirection(pathResult); MakeMagnetDirection(pathResult);
@@ -221,7 +231,7 @@ namespace AGVNavigationCore.PathFinding.Planning
//진행방향으로 이동했을때 나오는 노드를 사용한다. //진행방향으로 이동했을때 나오는 노드를 사용한다.
if (nextNodeForward != null) if (nextNodeForward != null)
{ {
var Path0 = _basicPathfinder.FindPathAStar(startNode.NodeId, nextNodeForward.NodeId); var Path0 = _basicPathfinder.FindPathAStar(startNode.Id, nextNodeForward.Id);
Path0.PrevNode = prevNode; Path0.PrevNode = prevNode;
Path0.PrevDirection = prevDirection; Path0.PrevDirection = prevDirection;
MakeDetailData(Path0, prevDirection); MakeDetailData(Path0, prevDirection);
@@ -259,7 +269,7 @@ namespace AGVNavigationCore.PathFinding.Planning
JunctionInPath = FindNearestJunction(startNode); JunctionInPath = FindNearestJunction(startNode);
//종료노드로부터 가까운 교차로 검색 //종료노드로부터 가까운 교차로 검색
if (JunctionInPath == null) JunctionInPath = FindNearestJunction(targetNode); if (JunctionInPath == null) JunctionInPath = FindNearestJunction(tnode);
} }
if (JunctionInPath == null) if (JunctionInPath == null)
return AGVPathResult.CreateFailure("교차로가 없어 경로계산을 할 수 없습니다", 0, 0); return AGVPathResult.CreateFailure("교차로가 없어 경로계산을 할 수 없습니다", 0, 0);
@@ -267,7 +277,7 @@ namespace AGVNavigationCore.PathFinding.Planning
//경유지를 포함하여 경로를 다시 계산한다. //경유지를 포함하여 경로를 다시 계산한다.
//1.시작위치 - 교차로(여기까지는 현재 방향으로 그대로 이동을 한다) //1.시작위치 - 교차로(여기까지는 현재 방향으로 그대로 이동을 한다)
var path1 = _basicPathfinder.FindPathAStar(startNode.NodeId, JunctionInPath.NodeId); var path1 = _basicPathfinder.FindPathAStar(startNode.Id, JunctionInPath.Id);
path1.PrevNode = prevNode; path1.PrevNode = prevNode;
path1.PrevDirection = prevDirection; path1.PrevDirection = prevDirection;
@@ -278,7 +288,7 @@ namespace AGVNavigationCore.PathFinding.Planning
// ReverseCheck = false; //현재 진행 방향으로 이동해야한다 // ReverseCheck = false; //현재 진행 방향으로 이동해야한다
// MakeDetailData(path1, currentDirection); // path1의 상세 경로 정보 채우기 (모터 방향 설정) // MakeDetailData(path1, currentDirection); // path1의 상세 경로 정보 채우기 (모터 방향 설정)
//} //}
if (path1.Path.Count > 1 && nextNodeBackward != null && nextNodeBackward.NodeId.Equals(path1.Path[1].NodeId)) if (path1.Path.Count > 1 && nextNodeBackward != null && nextNodeBackward.Id.Equals(path1.Path[1].Id))
{ {
ReverseCheck = true; //현재 방향의 반대방향으로 이동해야한다 ReverseCheck = true; //현재 방향의 반대방향으로 이동해야한다
MakeDetailData(path1, ReverseDirection); // path1의 상세 경로 정보 채우기 (모터 방향 설정) MakeDetailData(path1, ReverseDirection); // path1의 상세 경로 정보 채우기 (모터 방향 설정)
@@ -295,7 +305,7 @@ namespace AGVNavigationCore.PathFinding.Planning
//2.교차로 - 종료위치 //2.교차로 - 종료위치
var path2 = _basicPathfinder.FindPathAStar(JunctionInPath.NodeId, targetNode.NodeId); var path2 = _basicPathfinder.FindPathAStar(JunctionInPath.Id, targetNode.Id);
path2.PrevNode = prevNode; path2.PrevNode = prevNode;
path2.PrevDirection = prevDirection; path2.PrevDirection = prevDirection;
@@ -315,9 +325,9 @@ namespace AGVNavigationCore.PathFinding.Planning
//3.방향전환을 위환 대체 노드찾기 //3.방향전환을 위환 대체 노드찾기
tempNode = _basicPathfinder.FindAlternateNodeForDirectionChange(JunctionInPath.NodeId, tempNode = _basicPathfinder.FindAlternateNodeForDirectionChange(JunctionInPath.Id,
path1.Path[path1.Path.Count - 2].NodeId, path1.Path[path1.Path.Count - 2].Id,
path2.Path[1].NodeId); path2.Path[1].Id);
//4. path1 + tempnode + path2 가 최종 위치가 된다. //4. path1 + tempnode + path2 가 최종 위치가 된다.
if (tempNode == null) if (tempNode == null)
@@ -332,7 +342,7 @@ namespace AGVNavigationCore.PathFinding.Planning
//if (tempNode != null) //if (tempNode != null)
{ {
// 교차로 → 대체노드 경로 계산 // 교차로 → 대체노드 경로 계산
var pathToTemp = _basicPathfinder.FindPathAStar(JunctionInPath.NodeId, tempNode.NodeId); var pathToTemp = _basicPathfinder.FindPathAStar(JunctionInPath.Id, tempNode.Id);
pathToTemp.PrevNode = JunctionInPath; pathToTemp.PrevNode = JunctionInPath;
pathToTemp.PrevDirection = (ReverseCheck ? ReverseDirection : currentDirection); pathToTemp.PrevDirection = (ReverseCheck ? ReverseDirection : currentDirection);
if (!pathToTemp.Success) if (!pathToTemp.Success)
@@ -348,7 +358,7 @@ namespace AGVNavigationCore.PathFinding.Planning
combinedResult = _basicPathfinder.CombineResults(combinedResult, pathToTemp); combinedResult = _basicPathfinder.CombineResults(combinedResult, pathToTemp);
// 대체노드 → 교차로 경로 계산 (역방향) // 대체노드 → 교차로 경로 계산 (역방향)
var pathFromTemp = _basicPathfinder.FindPathAStar(tempNode.NodeId, JunctionInPath.NodeId); var pathFromTemp = _basicPathfinder.FindPathAStar(tempNode.Id, JunctionInPath.Id);
pathFromTemp.PrevNode = JunctionInPath; pathFromTemp.PrevNode = JunctionInPath;
pathFromTemp.PrevDirection = (ReverseCheck ? ReverseDirection : currentDirection); pathFromTemp.PrevDirection = (ReverseCheck ? ReverseDirection : currentDirection);
if (!pathFromTemp.Success) if (!pathFromTemp.Success)
@@ -362,14 +372,14 @@ namespace AGVNavigationCore.PathFinding.Planning
//현재까지 노드에서 목적지까지의 방향이 일치하면 그대로 사용한다. //현재까지 노드에서 목적지까지의 방향이 일치하면 그대로 사용한다.
bool temp3ok = false; bool temp3ok = false;
var TempCheck3 = _basicPathfinder.FindPathAStar(combinedResult.Path.Last().NodeId, targetNode.NodeId); var TempCheck3 = _basicPathfinder.FindPathAStar(combinedResult.Path.Last().Id, targetNode.Id);
if (TempCheck3.Path.First().NodeId.Equals(combinedResult.Path.Last().NodeId)) if (TempCheck3.Path.First().Id.Equals(combinedResult.Path.Last().Id))
{ {
if (targetNode.DockDirection == DockingDirection.Forward && combinedResult.DetailedPath.Last().MotorDirection == AgvDirection.Forward) if (tnode.DockDirection == DockingDirection.Forward && combinedResult.DetailedPath.Last().MotorDirection == AgvDirection.Forward)
{ {
temp3ok = true; temp3ok = true;
} }
else if (targetNode.DockDirection == DockingDirection.Backward && combinedResult.DetailedPath.Last().MotorDirection == AgvDirection.Backward) else if (tnode.DockDirection == DockingDirection.Backward && combinedResult.DetailedPath.Last().MotorDirection == AgvDirection.Backward)
{ {
temp3ok = true; temp3ok = true;
} }
@@ -381,11 +391,11 @@ namespace AGVNavigationCore.PathFinding.Planning
if (temp3ok == false) if (temp3ok == false)
{ {
//목적지와 방향이 맞지 않다. 그러므로 대체노드를 추가로 더 찾아야한다. //목적지와 방향이 맞지 않다. 그러므로 대체노드를 추가로 더 찾아야한다.
var tempNode2 = _basicPathfinder.FindAlternateNodeForDirectionChange(JunctionInPath.NodeId, var tempNode2 = _basicPathfinder.FindAlternateNodeForDirectionChange(JunctionInPath.Id,
combinedResult.Path[combinedResult.Path.Count - 2].NodeId, combinedResult.Path[combinedResult.Path.Count - 2].Id,
path2.Path[1].NodeId); path2.Path[1].Id);
var pathToTemp2 = _basicPathfinder.FindPathAStar(JunctionInPath.NodeId, tempNode2.NodeId); var pathToTemp2 = _basicPathfinder.FindPathAStar(JunctionInPath.Id, tempNode2.Id);
if (ReverseCheck) MakeDetailData(pathToTemp2, currentDirection); if (ReverseCheck) MakeDetailData(pathToTemp2, currentDirection);
else MakeDetailData(pathToTemp2, ReverseDirection); else MakeDetailData(pathToTemp2, ReverseDirection);
@@ -400,7 +410,7 @@ namespace AGVNavigationCore.PathFinding.Planning
combinedResult.DetailedPath[combinedResult.DetailedPath.Count - 1].MotorDirection = currentDirection; combinedResult.DetailedPath[combinedResult.DetailedPath.Count - 1].MotorDirection = currentDirection;
} }
var pathToTemp3 = _basicPathfinder.FindPathAStar(tempNode2.NodeId, JunctionInPath.NodeId); var pathToTemp3 = _basicPathfinder.FindPathAStar(tempNode2.Id, JunctionInPath.Id);
if (ReverseCheck) MakeDetailData(pathToTemp3, ReverseDirection); if (ReverseCheck) MakeDetailData(pathToTemp3, ReverseDirection);
else MakeDetailData(pathToTemp3, currentDirection); else MakeDetailData(pathToTemp3, currentDirection);
@@ -420,12 +430,15 @@ namespace AGVNavigationCore.PathFinding.Planning
} }
/// <summary>
/// 이 작업후에 MakeMagnetDirection 를 추가로 실행 하세요
/// </summary>
/// <summary> /// <summary>
/// 이 작업후에 MakeMagnetDirection 를 추가로 실행 하세요 /// 이 작업후에 MakeMagnetDirection 를 추가로 실행 하세요
/// </summary> /// </summary>
/// <param name="path1"></param> /// <param name="path1"></param>
/// <param name="currentDirection"></param> /// <param name="currentDirection"></param>
private void MakeDetailData(AGVPathResult path1, AgvDirection currentDirection) public void MakeDetailData(AGVPathResult path1, AgvDirection currentDirection)
{ {
if (path1.Success && path1.Path != null && path1.Path.Count > 0) if (path1.Success && path1.Path != null && path1.Path.Count > 0)
{ {
@@ -433,9 +446,9 @@ namespace AGVNavigationCore.PathFinding.Planning
for (int i = 0; i < path1.Path.Count; i++) for (int i = 0; i < path1.Path.Count; i++)
{ {
var node = path1.Path[i]; var node = path1.Path[i];
string nodeId = node.NodeId; string nodeId = node.Id;
string RfidId = node.RfidId; var RfidId = node.RfidId;
string nextNodeId = (i + 1 < path1.Path.Count) ? path1.Path[i + 1].NodeId : null; string nextNodeId = (i + 1 < path1.Path.Count) ? path1.Path[i + 1].Id : null;
// 노드 정보 생성 (현재 방향 유지) // 노드 정보 생성 (현재 방향 유지)
var nodeInfo = new NodeMotorInfo(i + 1, var nodeInfo = new NodeMotorInfo(i + 1,
@@ -445,6 +458,13 @@ namespace AGVNavigationCore.PathFinding.Planning
MagnetDirection.Straight MagnetDirection.Straight
); );
// [Speed Control] MapNode의 속도 설정 적용
var mapNode = _mapNodes.FirstOrDefault(n => n.Id == nodeId);
if (mapNode != null)
{
nodeInfo.Speed = mapNode.SpeedLimit;
}
detailedPath1.Add(nodeInfo); detailedPath1.Add(nodeInfo);
} }
@@ -465,13 +485,13 @@ namespace AGVNavigationCore.PathFinding.Planning
for (int i = 0; i < path1.DetailedPath.Count; i++) for (int i = 0; i < path1.DetailedPath.Count; i++)
{ {
var detailPath = path1.DetailedPath[i]; var detailPath = path1.DetailedPath[i];
string nodeId = path1.Path[i].NodeId; string nodeId = path1.Path[i].Id;
string nextNodeId = (i + 1 < path1.Path.Count) ? path1.Path[i + 1].NodeId : null; string nextNodeId = (i + 1 < path1.Path.Count) ? path1.Path[i + 1].Id : null;
// 마그넷 방향 계산 (3개 이상 연결된 교차로에서만 좌/우 가중치 적용) // 마그넷 방향 계산 (3개 이상 연결된 교차로에서만 좌/우 가중치 적용)
if (i > 0 && nextNodeId != null) if (i > 0 && nextNodeId != null)
{ {
string prevNodeId = path1.Path[i - 1].NodeId; string prevNodeId = path1.Path[i - 1].Id;
if (path1.DetailedPath[i - 1].MotorDirection != detailPath.MotorDirection) if (path1.DetailedPath[i - 1].MotorDirection != detailPath.MotorDirection)
detailPath.MagnetDirection = MagnetDirection.Straight; detailPath.MagnetDirection = MagnetDirection.Straight;
else else

View File

@@ -91,14 +91,14 @@ namespace AGVNavigationCore.PathFinding.Planning
// 직접 경로에 갈림길이 포함된 경우 그 갈림길에서 방향 전환 // 직접 경로에 갈림길이 포함된 경우 그 갈림길에서 방향 전환
foreach (var node in directPath2.Path.Skip(1).Take(directPath2.Path.Count - 2)) // 시작과 끝 제외 foreach (var node in directPath2.Path.Skip(1).Take(directPath2.Path.Count - 2)) // 시작과 끝 제외
{ {
var junctionInfo = _junctionAnalyzer.GetJunctionInfo(node.NodeId); var junctionInfo = _junctionAnalyzer.GetJunctionInfo(node.Id);
if (junctionInfo != null && junctionInfo.IsJunction) if (junctionInfo != null && junctionInfo.IsJunction)
{ {
// 간단한 방향 전환: 직접 경로 사용하되 방향 전환 노드 표시 // 간단한 방향 전환: 직접 경로 사용하되 방향 전환 노드 표시
return DirectionChangePlan.CreateSuccess( return DirectionChangePlan.CreateSuccess(
directPath2.Path, directPath2.Path,
node.NodeId, node.Id,
$"갈림길 {node.NodeId}에서 방향 전환: {currentDirection} → {requiredDirection}" $"갈림길 {node.Id}에서 방향 전환: {currentDirection} → {requiredDirection}"
); );
} }
} }
@@ -165,14 +165,14 @@ namespace AGVNavigationCore.PathFinding.Planning
{ {
foreach (var node in directPath.Path.Skip(2)) // 시작점과 다음 노드는 제외 foreach (var node in directPath.Path.Skip(2)) // 시작점과 다음 노드는 제외
{ {
var junctionInfo = _junctionAnalyzer.GetJunctionInfo(node.NodeId); var junctionInfo = _junctionAnalyzer.GetJunctionInfo(node.Id);
if (junctionInfo != null && junctionInfo.IsJunction) if (junctionInfo != null && junctionInfo.IsJunction)
{ {
// 직진 경로상에서는 더 엄격한 조건 적용 // 직진 경로상에서는 더 엄격한 조건 적용
if (!suitableJunctions.Contains(node.NodeId) && if (!suitableJunctions.Contains(node.Id) &&
HasMultipleExitOptions(node.NodeId)) HasMultipleExitOptions(node.Id))
{ {
suitableJunctions.Add(node.NodeId); suitableJunctions.Add(node.Id);
} }
} }
} }
@@ -226,7 +226,7 @@ namespace AGVNavigationCore.PathFinding.Planning
/// </summary> /// </summary>
private List<string> GetAllConnectedNodes(string nodeId) private List<string> GetAllConnectedNodes(string nodeId)
{ {
var node = _mapNodes.FirstOrDefault(n => n.NodeId == nodeId); var node = _mapNodes.FirstOrDefault(n => n.Id == nodeId);
if (node == null) return new List<string>(); if (node == null) return new List<string>();
var connected = new HashSet<string>(); var connected = new HashSet<string>();
@@ -236,16 +236,16 @@ namespace AGVNavigationCore.PathFinding.Planning
{ {
if (connectedNode != null) if (connectedNode != null)
{ {
connected.Add(connectedNode.NodeId); connected.Add(connectedNode.Id);
} }
} }
// 역방향 연결 // 역방향 연결
foreach (var otherNode in _mapNodes) foreach (var otherNode in _mapNodes)
{ {
if (otherNode.NodeId != nodeId && otherNode.ConnectedMapNodes.Any(n => n.NodeId == nodeId)) if (otherNode.Id != nodeId && otherNode.ConnectedMapNodes.Any(n => n.Id == nodeId))
{ {
connected.Add(otherNode.NodeId); connected.Add(otherNode.Id);
} }
} }
@@ -293,7 +293,7 @@ namespace AGVNavigationCore.PathFinding.Planning
string actualDirectionChangeNode = FindActualDirectionChangeNode(changePath, junctionNodeId); string actualDirectionChangeNode = FindActualDirectionChangeNode(changePath, junctionNodeId);
string description = $"갈림길 {GetDisplayName(junctionNodeId)}를 통해 {GetDisplayName(actualDirectionChangeNode)}에서 방향 전환: {currentDirection} → {requiredDirection}"; string description = $"갈림길 {GetDisplayName(junctionNodeId)}를 통해 {GetDisplayName(actualDirectionChangeNode)}에서 방향 전환: {currentDirection} → {requiredDirection}";
System.Diagnostics.Debug.WriteLine($"[DirectionChangePlanner] ✅ 유효한 방향전환 경로: {string.Join(" ", changePath.Select(n => n.NodeId))}"); System.Diagnostics.Debug.WriteLine($"[DirectionChangePlanner] ✅ 유효한 방향전환 경로: {string.Join(" ", changePath.Select(n => n.Id))}");
return DirectionChangePlan.CreateSuccess(changePath, actualDirectionChangeNode, description); return DirectionChangePlan.CreateSuccess(changePath, actualDirectionChangeNode, description);
} }
@@ -319,7 +319,7 @@ namespace AGVNavigationCore.PathFinding.Planning
// 2. 인근 갈림길을 통한 우회인지, 직진 경로상 갈림길인지 판단 // 2. 인근 갈림길을 통한 우회인지, 직진 경로상 갈림길인지 판단
var directPath = _pathfinder.FindPathAStar(startNodeId, targetNodeId); var directPath = _pathfinder.FindPathAStar(startNodeId, targetNodeId);
bool isNearbyDetour = !directPath.Success || !directPath.Path.Any(n => n.NodeId == junctionNodeId); bool isNearbyDetour = !directPath.Success || !directPath.Path.Any(n => n.Id == junctionNodeId);
if (isNearbyDetour) if (isNearbyDetour)
{ {
@@ -376,17 +376,17 @@ namespace AGVNavigationCore.PathFinding.Planning
if (currentDirection != requiredDirection) if (currentDirection != requiredDirection)
{ {
string fromNodeId = toJunctionPath.Path.Count >= 2 ? string fromNodeId = toJunctionPath.Path.Count >= 2 ?
toJunctionPath.Path[toJunctionPath.Path.Count - 2].NodeId : startNodeId; toJunctionPath.Path[toJunctionPath.Path.Count - 2].Id : startNodeId;
var changeSequence = GenerateDirectionChangeSequence(junctionNodeId, fromNodeId, currentDirection, requiredDirection); var changeSequence = GenerateDirectionChangeSequence(junctionNodeId, fromNodeId, currentDirection, requiredDirection);
if (changeSequence.Count > 1) if (changeSequence.Count > 1)
{ {
fullPath.AddRange(changeSequence.Skip(1).Select(nodeId => _mapNodes.FirstOrDefault(n => n.NodeId == nodeId)).Where(n => n != null)); fullPath.AddRange(changeSequence.Skip(1).Select(nodeId => _mapNodes.FirstOrDefault(n => n.Id == nodeId)).Where(n => n != null));
} }
} }
// 3. 갈림길에서 목표점까지의 경로 // 3. 갈림길에서 목표점까지의 경로
string lastNode = fullPath.LastOrDefault()?.NodeId ?? junctionNodeId; string lastNode = fullPath.LastOrDefault()?.Id ?? junctionNodeId;
var fromJunctionPath = _pathfinder.FindPathAStar(lastNode, targetNodeId); var fromJunctionPath = _pathfinder.FindPathAStar(lastNode, targetNodeId);
if (fromJunctionPath.Success && fromJunctionPath.Path.Count > 1) if (fromJunctionPath.Success && fromJunctionPath.Path.Count > 1)
{ {
@@ -461,8 +461,8 @@ namespace AGVNavigationCore.PathFinding.Planning
// 왔던 길(excludeNodeId)를 제외한 노드 중에서 최적의 우회 노드 선택 // 왔던 길(excludeNodeId)를 제외한 노드 중에서 최적의 우회 노드 선택
// 우선순위: 1) 막다른 길이 아닌 노드 (우회 후 복귀 가능) 2) 직진방향 3) 목적지 방향 // 우선순위: 1) 막다른 길이 아닌 노드 (우회 후 복귀 가능) 2) 직진방향 3) 목적지 방향
var junctionNode = _mapNodes.FirstOrDefault(n => n.NodeId == junctionNodeId); var junctionNode = _mapNodes.FirstOrDefault(n => n.Id == junctionNodeId);
var fromNode = _mapNodes.FirstOrDefault(n => n.NodeId == excludeNodeId); var fromNode = _mapNodes.FirstOrDefault(n => n.Id == excludeNodeId);
if (junctionNode == null || fromNode == null) if (junctionNode == null || fromNode == null)
return availableNodes.FirstOrDefault(); return availableNodes.FirstOrDefault();
@@ -478,7 +478,7 @@ namespace AGVNavigationCore.PathFinding.Planning
{ {
if (nodeId == excludeNodeId) continue; // 왔던 길 제외 if (nodeId == excludeNodeId) continue; // 왔던 길 제외
var candidateNode = _mapNodes.FirstOrDefault(n => n.NodeId == nodeId); var candidateNode = _mapNodes.FirstOrDefault(n => n.Id == nodeId);
if (candidateNode == null) continue; if (candidateNode == null) continue;
// 갈림길에서 후보 노드로의 방향 벡터 계산 (junctionNode → candidateNode) // 갈림길에서 후보 노드로의 방향 벡터 계산 (junctionNode → candidateNode)
@@ -561,11 +561,11 @@ namespace AGVNavigationCore.PathFinding.Planning
return junctionNodeId; // 기본값으로 갈림길 반환 return junctionNodeId; // 기본값으로 갈림길 반환
// 갈림길이 두 번 나타나는 위치 찾기 // 갈림길이 두 번 나타나는 위치 찾기
int firstJunctionIndex = changePath.FindIndex(n => n.NodeId == junctionNodeId); int firstJunctionIndex = changePath.FindIndex(n => n.Id == junctionNodeId);
int lastJunctionIndex = -1; int lastJunctionIndex = -1;
for (int i = changePath.Count - 1; i >= 0; i--) for (int i = changePath.Count - 1; i >= 0; i--)
{ {
if (changePath[i].NodeId == junctionNodeId) if (changePath[i].Id == junctionNodeId)
{ {
lastJunctionIndex = i; lastJunctionIndex = i;
break; break;
@@ -577,7 +577,7 @@ namespace AGVNavigationCore.PathFinding.Planning
firstJunctionIndex != lastJunctionIndex && lastJunctionIndex - firstJunctionIndex == 2) firstJunctionIndex != lastJunctionIndex && lastJunctionIndex - firstJunctionIndex == 2)
{ {
// 첫 번째와 두 번째 갈림길 사이에 있는 노드가 실제 방향전환 노드 // 첫 번째와 두 번째 갈림길 사이에 있는 노드가 실제 방향전환 노드
string detourNode = changePath[firstJunctionIndex + 1].NodeId; string detourNode = changePath[firstJunctionIndex + 1].Id;
return detourNode; return detourNode;
} }
@@ -647,11 +647,11 @@ namespace AGVNavigationCore.PathFinding.Planning
} }
string errorMessage = $"되돌아가기 패턴 검출 ({backtrackingPatterns.Count}개): {string.Join(", ", issues)}"; string errorMessage = $"되돌아가기 패턴 검출 ({backtrackingPatterns.Count}개): {string.Join(", ", issues)}";
System.Diagnostics.Debug.WriteLine($"[PathValidation] ❌ 경로: {string.Join(" ", path.Select(n => n.NodeId))}"); System.Diagnostics.Debug.WriteLine($"[PathValidation] ❌ 경로: {string.Join(" ", path.Select(n => n.Id))}");
System.Diagnostics.Debug.WriteLine($"[PathValidation] ❌ 되돌아가기 패턴: {errorMessage}"); System.Diagnostics.Debug.WriteLine($"[PathValidation] ❌ 되돌아가기 패턴: {errorMessage}");
return PathValidationResult.CreateInvalidWithBacktracking( return PathValidationResult.CreateInvalidWithBacktracking(
path.Select(n => n.NodeId).ToList(), backtrackingPatterns, startNodeId, "", junctionNodeId, errorMessage); path.Select(n => n.Id).ToList(), backtrackingPatterns, startNodeId, "", junctionNodeId, errorMessage);
} }
// 2. 연속된 중복 노드 검증 // 2. 연속된 중복 노드 검증
@@ -670,13 +670,13 @@ namespace AGVNavigationCore.PathFinding.Planning
} }
// 4. 갈림길 포함 여부 검증 // 4. 갈림길 포함 여부 검증
if (!path.Any(n => n.NodeId == junctionNodeId)) if (!path.Any(n => n.Id == junctionNodeId))
{ {
return PathValidationResult.CreateInvalid(startNodeId, "", $"갈림길 {junctionNodeId}이 경로에 포함되지 않음"); return PathValidationResult.CreateInvalid(startNodeId, "", $"갈림길 {junctionNodeId}이 경로에 포함되지 않음");
} }
System.Diagnostics.Debug.WriteLine($"[PathValidation] ✅ 유효한 경로: {string.Join(" ", path.Select(n => n.NodeId))}"); System.Diagnostics.Debug.WriteLine($"[PathValidation] ✅ 유효한 경로: {string.Join(" ", path.Select(n => n.Id))}");
return PathValidationResult.CreateValid(path.Select(n => n.NodeId).ToList(), startNodeId, "", junctionNodeId); return PathValidationResult.CreateValid(path.Select(n => n.Id).ToList(), startNodeId, "", junctionNodeId);
} }
/// <summary> /// <summary>
@@ -688,9 +688,9 @@ namespace AGVNavigationCore.PathFinding.Planning
for (int i = 0; i < path.Count - 2; i++) for (int i = 0; i < path.Count - 2; i++)
{ {
string nodeA = path[i].NodeId; string nodeA = path[i].Id;
string nodeB = path[i + 1].NodeId; string nodeB = path[i + 1].Id;
string nodeC = path[i + 2].NodeId; string nodeC = path[i + 2].Id;
// A → B → A 패턴 검출 // A → B → A 패턴 검출
if (nodeA == nodeC && nodeA != nodeB) if (nodeA == nodeC && nodeA != nodeB)
@@ -712,9 +712,9 @@ namespace AGVNavigationCore.PathFinding.Planning
for (int i = 0; i < path.Count - 1; i++) for (int i = 0; i < path.Count - 1; i++)
{ {
if (path[i].NodeId == path[i + 1].NodeId) if (path[i].Id == path[i + 1].Id)
{ {
duplicates.Add(path[i].NodeId); duplicates.Add(path[i].Id);
} }
} }
@@ -728,12 +728,12 @@ namespace AGVNavigationCore.PathFinding.Planning
{ {
for (int i = 0; i < path.Count - 1; i++) for (int i = 0; i < path.Count - 1; i++)
{ {
string currentNode = path[i].NodeId; string currentNode = path[i].Id;
string nextNode = path[i + 1].NodeId; string nextNode = path[i + 1].Id;
// 두 노드간 직접 연결성 확인 (맵 노드의 ConnectedMapNodes 리스트 사용) // 두 노드간 직접 연결성 확인 (맵 노드의 ConnectedMapNodes 리스트 사용)
var currentMapNode = _mapNodes.FirstOrDefault(n => n.NodeId == currentNode); var currentMapNode = _mapNodes.FirstOrDefault(n => n.Id == currentNode);
if (currentMapNode == null || !currentMapNode.ConnectedMapNodes.Any(n => n.NodeId == nextNode)) if (currentMapNode == null || !currentMapNode.ConnectedMapNodes.Any(n => n.Id == nextNode))
{ {
return PathValidationResult.CreateInvalid(currentNode, nextNode, $"노드 {currentNode}와 {nextNode} 사이에 연결이 없음"); return PathValidationResult.CreateInvalid(currentNode, nextNode, $"노드 {currentNode}와 {nextNode} 사이에 연결이 없음");
} }
@@ -769,10 +769,10 @@ namespace AGVNavigationCore.PathFinding.Planning
/// <returns>표시할 이름</returns> /// <returns>표시할 이름</returns>
private string GetDisplayName(string nodeId) private string GetDisplayName(string nodeId)
{ {
var node = _mapNodes.FirstOrDefault(n => n.NodeId == nodeId); var node = _mapNodes.FirstOrDefault(n => n.Id == nodeId);
if (node != null && !string.IsNullOrEmpty(node.RfidId)) if (node != null && node.HasRfid())
{ {
return node.RfidId; return node.RfidId.ToString("0000");
} }
return $"({nodeId})"; return $"({nodeId})";
} }

View File

@@ -66,7 +66,7 @@ namespace AGVNavigationCore.PathFinding.Planning
// 연결된 노드 중 현재 노드가 아닌 것들만 필터링 // 연결된 노드 중 현재 노드가 아닌 것들만 필터링
var candidateNodes = allNodes.Where(n => var candidateNodes = allNodes.Where(n =>
connectedNodeIds.Contains(n.NodeId) && n.NodeId != currentNode.NodeId connectedNodeIds.Contains(n.Id) && n.Id != currentNode.Id
).ToList(); ).ToList();
if (candidateNodes.Count == 0) if (candidateNodes.Count == 0)
@@ -88,7 +88,7 @@ namespace AGVNavigationCore.PathFinding.Planning
if (movementLength < 0.001f) // 거의 이동하지 않음 if (movementLength < 0.001f) // 거의 이동하지 않음
{ {
return candidateNodes[0].NodeId; // 첫 번째 연결 노드 반환 return candidateNodes[0].Id; // 첫 번째 연결 노드 반환
} }
var normalizedMovement = new PointF( var normalizedMovement = new PointF(
@@ -138,7 +138,7 @@ namespace AGVNavigationCore.PathFinding.Planning
// 가장 높은 점수를 가진 노드 반환 // 가장 높은 점수를 가진 노드 반환
var bestCandidate = scoredCandidates.OrderByDescending(x => x.score).First(); var bestCandidate = scoredCandidates.OrderByDescending(x => x.score).First();
return bestCandidate.node.NodeId; return bestCandidate.node.Id;
} }
/// <summary> /// <summary>

View File

@@ -40,13 +40,18 @@ namespace AGVNavigationCore.PathFinding.Planning
/// <summary> /// <summary>
/// RFID Value /// RFID Value
/// </summary> /// </summary>
public string RfidId { get; set; } public ushort RfidId { get; set; }
/// <summary> /// <summary>
/// 해당 노드에서의 모터방향 /// 해당 노드에서의 모터방향
/// </summary> /// </summary>
public AgvDirection MotorDirection { get; set; } public AgvDirection MotorDirection { get; set; }
/// <summary>
/// 해당 노드에서의 제한 속도
/// </summary>
public SpeedLevel Speed { get; set; } = SpeedLevel.M;
/// <summary> /// <summary>
/// 마그넷 센서 방향 제어 (갈림길 처리용) /// 마그넷 센서 방향 제어 (갈림길 처리용)
/// </summary> /// </summary>
@@ -82,7 +87,7 @@ namespace AGVNavigationCore.PathFinding.Planning
/// </summary> /// </summary>
public string SpecialActionDescription { get; set; } public string SpecialActionDescription { get; set; }
public NodeMotorInfo(int seqno,string nodeId,string rfid, AgvDirection motorDirection, string nextNodeId = null, MagnetDirection magnetDirection = MagnetDirection.Straight) public NodeMotorInfo(int seqno,string nodeId,ushort rfid, AgvDirection motorDirection, string nextNodeId = null, MagnetDirection magnetDirection = MagnetDirection.Straight)
{ {
seq = seqno; seq = seqno;
NodeId = nodeId; NodeId = nodeId;
@@ -103,7 +108,7 @@ namespace AGVNavigationCore.PathFinding.Planning
/// </summary> /// </summary>
public override string ToString() public override string ToString()
{ {
var result = $"{RfidId}[{NodeId}]:{MotorDirection}"; var result = $"R{RfidId}[N{NodeId}]:{MotorDirection}";
// 마그넷 방향이 직진이 아닌 경우 표시 // 마그넷 방향이 직진이 아닌 경우 표시
if (MagnetDirection != MagnetDirection.Straight) if (MagnetDirection != MagnetDirection.Straight)

View File

@@ -76,7 +76,7 @@ namespace AGVNavigationCore.Utils
List<MapNode> candidateNodes = new List<MapNode>(); List<MapNode> candidateNodes = new List<MapNode>();
if (prevDirection == direction) if (prevDirection == direction)
{ {
candidateNodes = connectedMapNodes.Where(n => n.NodeId != prevNode.NodeId).ToList(); candidateNodes = connectedMapNodes.Where(n => n.Id != prevNode.Id).ToList();
} }
else else
{ {
@@ -112,9 +112,9 @@ namespace AGVNavigationCore.Utils
Console.WriteLine( Console.WriteLine(
$"\n[GetNextNodeByDirection] ========== 다음 노드 선택 시작 =========="); $"\n[GetNextNodeByDirection] ========== 다음 노드 선택 시작 ==========");
Console.WriteLine( Console.WriteLine(
$" 현재노드: {currentNode.RfidId}[{currentNode.NodeId}]({currentNode.Position.X:F1}, {currentNode.Position.Y:F1})"); $" 현재노드: {currentNode.RfidId}[{currentNode.Id}]({currentNode.Position.X:F1}, {currentNode.Position.Y:F1})");
Console.WriteLine( Console.WriteLine(
$" 이전노드: {prevNode.RfidId}[{prevNode.NodeId}]({prevNode.Position.X:F1}, {prevNode.Position.Y:F1})"); $" 이전노드: {prevNode.RfidId}[{prevNode.Id}]({prevNode.Position.X:F1}, {prevNode.Position.Y:F1})");
Console.WriteLine( Console.WriteLine(
$" 이동벡터: ({movementVector.X:F2}, {movementVector.Y:F2}) → 정규화: ({normalizedMovement.X:F3}, {normalizedMovement.Y:F3})"); $" 이동벡터: ({movementVector.X:F2}, {movementVector.Y:F2}) → 정규화: ({normalizedMovement.X:F3}, {normalizedMovement.Y:F3})");
Console.WriteLine( Console.WriteLine(
@@ -159,7 +159,7 @@ namespace AGVNavigationCore.Utils
} }
Console.WriteLine( Console.WriteLine(
$"\n [후보] {candidate.RfidId}[{candidate.NodeId}]({candidate.Position.X:F1}, {candidate.Position.Y:F1})"); $"\n [후보] {candidate.RfidId}[{candidate.Id}]({candidate.Position.X:F1}, {candidate.Position.Y:F1})");
Console.WriteLine( Console.WriteLine(
$" 벡터: ({toNextVector.X:F2}, {toNextVector.Y:F2}), 길이: {toNextLength:F2}"); $" 벡터: ({toNextVector.X:F2}, {toNextVector.Y:F2}), 길이: {toNextLength:F2}");
Console.WriteLine( Console.WriteLine(
@@ -204,7 +204,7 @@ namespace AGVNavigationCore.Utils
} }
Console.WriteLine( Console.WriteLine(
$"\n 최종선택: {bestNode?.RfidId ?? "null"}[{bestNode?.NodeId ?? "null"}] (점수: {bestScore:F4})"); $"\n 최종선택: {bestNode?.RfidId ?? 0}[{bestNode?.Id ?? "null"}] (점수: {bestScore:F4})");
Console.WriteLine( Console.WriteLine(
$"[GetNextNodeByDirection] ========== 다음 노드 선택 종료 ==========\n"); $"[GetNextNodeByDirection] ========== 다음 노드 선택 종료 ==========\n");
@@ -445,15 +445,15 @@ namespace AGVNavigationCore.Utils
// 선택 이유 생성 // 선택 이유 생성
if (prevMotorDirection.HasValue && direction == prevMotorDirection) if (prevMotorDirection.HasValue && direction == prevMotorDirection)
{ {
reason = $"모터 방향 일관성 유지 ({direction}) → {candidate.NodeId}"; reason = $"모터 방향 일관성 유지 ({direction}) → {candidate.Id}";
} }
else if (prevMotorDirection.HasValue) else if (prevMotorDirection.HasValue)
{ {
reason = $"모터 방향 변경 ({prevMotorDirection} → {direction}) → {candidate.NodeId}"; reason = $"모터 방향 변경 ({prevMotorDirection} → {direction}) → {candidate.Id}";
} }
else else
{ {
reason = $"방향 기반 선택 ({direction}) → {candidate.NodeId}"; reason = $"방향 기반 선택 ({direction}) → {candidate.Id}";
} }
} }
} }

View File

@@ -11,22 +11,22 @@ namespace AGVNavigationCore.Utils
{ {
/// <summary> /// <summary>
/// DirectionalPathfinder 테스트 클래스 /// DirectionalPathfinder 테스트 클래스
/// NewMap.agvmap을 로드하여 방향별 다음 노드를 검증 /// NewMap.json 로드하여 방향별 다음 노드를 검증
/// </summary> /// </summary>
public class DirectionalPathfinderTest public class DirectionalPathfinderTest
{ {
private List<MapNode> _allNodes; private List<MapNode> _allNodes;
private Dictionary<string, MapNode> _nodesByRfidId; private Dictionary<ushort, MapNode> _nodesByRfidId;
private AGVDirectionCalculator _calculator; private AGVDirectionCalculator _calculator;
public DirectionalPathfinderTest() public DirectionalPathfinderTest()
{ {
_nodesByRfidId = new Dictionary<string, MapNode>(); _nodesByRfidId = new Dictionary<ushort, MapNode>();
_calculator = new AGVDirectionCalculator(); _calculator = new AGVDirectionCalculator();
} }
/// <summary> /// <summary>
/// NewMap.agvmap 파일 로드 /// NewMap.json 파일 로드
/// </summary> /// </summary>
public bool LoadMapFile(string filePath) public bool LoadMapFile(string filePath)
{ {
@@ -52,7 +52,7 @@ namespace AGVNavigationCore.Utils
// RFID ID로 인덱싱 // RFID ID로 인덱싱
foreach (var node in _allNodes) foreach (var node in _allNodes)
{ {
if (!string.IsNullOrEmpty(node.RfidId)) if (node.HasRfid())
{ {
_nodesByRfidId[node.RfidId] = node; _nodesByRfidId[node.RfidId] = node;
} }
@@ -71,7 +71,7 @@ namespace AGVNavigationCore.Utils
/// <summary> /// <summary>
/// 테스트: RFID 번호로 노드를 찾고, 다음 노드를 계산 /// 테스트: RFID 번호로 노드를 찾고, 다음 노드를 계산
/// </summary> /// </summary>
public void TestDirectionalMovement(string previousRfidId, string currentRfidId, AgvDirection direction) public void TestDirectionalMovement(ushort previousRfidId, ushort currentRfidId, AgvDirection direction)
{ {
Console.WriteLine($"\n========================================"); Console.WriteLine($"\n========================================");
Console.WriteLine($"테스트: {previousRfidId} → {currentRfidId} (방향: {direction})"); Console.WriteLine($"테스트: {previousRfidId} → {currentRfidId} (방향: {direction})");
@@ -90,8 +90,8 @@ namespace AGVNavigationCore.Utils
return; return;
} }
Console.WriteLine($"이전 노드: {previousNode.NodeId} (RFID: {previousNode.RfidId}) - 위치: {previousNode.Position}"); Console.WriteLine($"이전 노드: {previousNode.Id} (RFID: {previousNode.RfidId}) - 위치: {previousNode.Position}");
Console.WriteLine($"현재 노드: {currentNode.NodeId} (RFID: {currentNode.RfidId}) - 위치: {currentNode.Position}"); Console.WriteLine($"현재 노드: {currentNode.Id} (RFID: {currentNode.RfidId}) - 위치: {currentNode.Position}");
Console.WriteLine($"이동 벡터: ({currentNode.Position.X - previousNode.Position.X}, " + Console.WriteLine($"이동 벡터: ({currentNode.Position.X - previousNode.Position.X}, " +
$"{currentNode.Position.Y - previousNode.Position.Y})"); $"{currentNode.Position.Y - previousNode.Position.Y})");
@@ -111,10 +111,10 @@ namespace AGVNavigationCore.Utils
} }
// 다음 노드 정보 출력 // 다음 노드 정보 출력
var nextNode = _allNodes.FirstOrDefault(n => n.NodeId == nextNodeId); var nextNode = _allNodes.FirstOrDefault(n => n.Id == nextNodeId);
if (nextNode != null) if (nextNode != null)
{ {
Console.WriteLine($"✓ 다음 노드: {nextNode.NodeId} (RFID: {nextNode.RfidId}) - 위치: {nextNode.Position}"); Console.WriteLine($"✓ 다음 노드: {nextNode.Id} (RFID: {nextNode.RfidId}) - 위치: {nextNode.Position}");
Console.WriteLine($" ├─ 노드 타입: {GetNodeTypeName(nextNode.Type)}"); Console.WriteLine($" ├─ 노드 타입: {GetNodeTypeName(nextNode.Type)}");
Console.WriteLine($" └─ 연결된 노드: {string.Join(", ", nextNode.ConnectedNodes)}"); Console.WriteLine($" └─ 연결된 노드: {string.Join(", ", nextNode.ConnectedNodes)}");
} }
@@ -132,7 +132,7 @@ namespace AGVNavigationCore.Utils
Console.WriteLine("\n========== 모든 노드 정보 =========="); Console.WriteLine("\n========== 모든 노드 정보 ==========");
foreach (var node in _allNodes.OrderBy(n => n.RfidId)) foreach (var node in _allNodes.OrderBy(n => n.RfidId))
{ {
Console.WriteLine($"{node.RfidId:D3} → {node.NodeId} ({GetNodeTypeName(node.Type)})"); Console.WriteLine($"{node.RfidId:D3} → {node.Id} ({GetNodeTypeName(node.Type)})");
Console.WriteLine($" 위치: {node.Position}, 연결: {string.Join(", ", node.ConnectedNodes)}"); Console.WriteLine($" 위치: {node.Position}, 연결: {string.Join(", ", node.ConnectedNodes)}");
} }
} }
@@ -140,7 +140,7 @@ namespace AGVNavigationCore.Utils
/// <summary> /// <summary>
/// 특정 RFID 노드의 상세 정보 출력 /// 특정 RFID 노드의 상세 정보 출력
/// </summary> /// </summary>
public void PrintNodeInfo(string rfidId) public void PrintNodeInfo(ushort rfidId)
{ {
if (!_nodesByRfidId.TryGetValue(rfidId, out var node)) if (!_nodesByRfidId.TryGetValue(rfidId, out var node))
{ {
@@ -149,8 +149,9 @@ namespace AGVNavigationCore.Utils
} }
Console.WriteLine($"\n========== RFID {rfidId} 상세 정보 =========="); Console.WriteLine($"\n========== RFID {rfidId} 상세 정보 ==========");
Console.WriteLine($"노드 ID: {node.NodeId}"); Console.WriteLine($"노드 ID: {node.Id}");
Console.WriteLine($"이름: {node.Name}"); Console.WriteLine($"RFID: {node.RfidId}");
Console.WriteLine($"ALIAS: {node.AliasName}");
Console.WriteLine($"위치: {node.Position}"); Console.WriteLine($"위치: {node.Position}");
Console.WriteLine($"타입: {GetNodeTypeName(node.Type)}"); Console.WriteLine($"타입: {GetNodeTypeName(node.Type)}");
Console.WriteLine($"TurnLeft/Right/교차로 : {(node.CanTurnLeft ? "O":"X")}/{(node.CanTurnRight ? "O" : "X")}/{(node.DisableCross ? "X" : "O")}"); Console.WriteLine($"TurnLeft/Right/교차로 : {(node.CanTurnLeft ? "O":"X")}/{(node.CanTurnRight ? "O" : "X")}/{(node.DisableCross ? "X" : "O")}");
@@ -165,7 +166,7 @@ namespace AGVNavigationCore.Utils
{ {
foreach (var connectedId in node.ConnectedNodes) foreach (var connectedId in node.ConnectedNodes)
{ {
var connectedNode = _allNodes.FirstOrDefault(n => n.NodeId == connectedId); var connectedNode = _allNodes.FirstOrDefault(n => n.Id == connectedId);
if (connectedNode != null) if (connectedNode != null)
{ {
Console.WriteLine($" → {connectedId} (RFID: {connectedNode.RfidId}) - 위치: {connectedNode.Position}"); Console.WriteLine($" → {connectedId} (RFID: {connectedNode.RfidId}) - 위치: {connectedNode.Position}");

View File

@@ -29,7 +29,7 @@ namespace AGVNavigationCore.Utils
return DockingValidationResult.CreateNotRequired(); return DockingValidationResult.CreateNotRequired();
} }
if (pathResult.DetailedPath.Any() == false && pathResult.Path.Any() && pathResult.Path.Count == 2 && if (pathResult.DetailedPath.Any() == false && pathResult.Path.Any() && pathResult.Path.Count == 2 &&
pathResult.Path[0].NodeId == pathResult.Path[1].NodeId) pathResult.Path[0].Id == pathResult.Path[1].Id)
{ {
System.Diagnostics.Debug.WriteLine($"[DockingValidator] 도킹 검증 불필요: 동일포인트"); System.Diagnostics.Debug.WriteLine($"[DockingValidator] 도킹 검증 불필요: 동일포인트");
return DockingValidationResult.CreateNotRequired(); return DockingValidationResult.CreateNotRequired();
@@ -44,7 +44,7 @@ namespace AGVNavigationCore.Utils
return DockingValidationResult.CreateNotRequired(); return DockingValidationResult.CreateNotRequired();
} }
System.Diagnostics.Debug.WriteLine($"[DockingValidator] 목적지 노드: {LastNode.NodeId} 타입:{LastNode.Type} ({(int)LastNode.Type})"); System.Diagnostics.Debug.WriteLine($"[DockingValidator] 목적지 노드: {LastNode.Id} 타입:{LastNode.Type} ({(int)LastNode.Type})");
//detail 경로 이동 예측 검증 //detail 경로 이동 예측 검증
for (int i = 0; i < pathResult.DetailedPath.Count - 1; i++) for (int i = 0; i < pathResult.DetailedPath.Count - 1; i++)
@@ -52,8 +52,8 @@ namespace AGVNavigationCore.Utils
var curNodeId = pathResult.DetailedPath[i].NodeId; var curNodeId = pathResult.DetailedPath[i].NodeId;
var nextNodeId = pathResult.DetailedPath[i + 1].NodeId; var nextNodeId = pathResult.DetailedPath[i + 1].NodeId;
var curNode = mapNodes?.FirstOrDefault(n => n.NodeId == curNodeId); var curNode = mapNodes?.FirstOrDefault(n => n.Id == curNodeId);
var nextNode = mapNodes?.FirstOrDefault(n => n.NodeId == nextNodeId); var nextNode = mapNodes?.FirstOrDefault(n => n.Id == nextNodeId);
if (curNode != null && nextNode != null) if (curNode != null && nextNode != null)
{ {
@@ -67,7 +67,7 @@ namespace AGVNavigationCore.Utils
else else
{ {
var prevNodeId = pathResult.DetailedPath[i - 1].NodeId; var prevNodeId = pathResult.DetailedPath[i - 1].NodeId;
prevNode = mapNodes?.FirstOrDefault(n => n.NodeId == prevNodeId); prevNode = mapNodes?.FirstOrDefault(n => n.Id == prevNodeId);
prevDir = pathResult.DetailedPath[i - 1].MotorDirection; prevDir = pathResult.DetailedPath[i - 1].MotorDirection;
} }
@@ -78,7 +78,7 @@ namespace AGVNavigationCore.Utils
Console.WriteLine( Console.WriteLine(
$"\n[ValidateDockingDirection] 경로 검증 단계 {i}:"); $"\n[ValidateDockingDirection] 경로 검증 단계 {i}:");
Console.WriteLine( Console.WriteLine(
$" 이전→현재→다음: {prevNode.NodeId}({prevNode.RfidId}) → {curNode.NodeId}({curNode.RfidId}) → {nextNode.NodeId}({nextNode.RfidId})"); $" 이전→현재→다음: {prevNode.Id}({prevNode.RfidId}) → {curNode.Id}({curNode.RfidId}) → {nextNode.Id}({nextNode.RfidId})");
Console.WriteLine( Console.WriteLine(
$" 현재 노드 위치: ({curNode.Position.X:F1}, {curNode.Position.Y:F1})"); $" 현재 노드 위치: ({curNode.Position.X:F1}, {curNode.Position.Y:F1})");
Console.WriteLine( Console.WriteLine(
@@ -96,16 +96,16 @@ namespace AGVNavigationCore.Utils
); );
Console.WriteLine( Console.WriteLine(
$" [예상] GetNextNodeByDirection 결과: {expectedNextNode?.NodeId ?? "null"}"); $" [예상] GetNextNodeByDirection 결과: {expectedNextNode?.Id ?? "null"}");
Console.WriteLine( Console.WriteLine(
$" [실제] DetailedPath 다음 노드: {nextNode.RfidId}[{nextNode.NodeId}]"); $" [실제] DetailedPath 다음 노드: {nextNode.RfidId}[{nextNode.Id}]");
if (expectedNextNode != null && !expectedNextNode.NodeId.Equals(nextNode.NodeId)) if (expectedNextNode != null && !expectedNextNode.Id.Equals(nextNode.Id))
{ {
string error = string error =
$"[DockingValidator] ⚠️ 경로 방향 불일치" + $"[DockingValidator] ⚠️ 경로 방향 불일치" +
$"\n현재={curNode.RfidId}[{curNodeId}] 이전={prevNode.RfidId}[{(prevNode?.NodeId ?? string.Empty)}] " + $"\n현재={curNode.RfidId}[{curNodeId}] 이전={prevNode.RfidId}[{(prevNode?.Id ?? string.Empty)}] " +
$"\n예상다음={expectedNextNode.RfidId}[{expectedNextNode.NodeId}] 실제다음={nextNode.RfidId}[{nextNodeId}]"; $"\n예상다음={expectedNextNode.RfidId}[{expectedNextNode.Id}] 실제다음={nextNode.RfidId}[{nextNodeId}]";
Console.WriteLine( Console.WriteLine(
$"[ValidateDockingDirection] ❌ 경로 방향 불일치 검출!"); $"[ValidateDockingDirection] ❌ 경로 방향 불일치 검출!");
Console.WriteLine( Console.WriteLine(
@@ -118,7 +118,7 @@ namespace AGVNavigationCore.Utils
$" 현재→실제: ({(nextNode.Position.X - curNode.Position.X):F2}, {(nextNode.Position.Y - curNode.Position.Y):F2})"); $" 현재→실제: ({(nextNode.Position.X - curNode.Position.X):F2}, {(nextNode.Position.Y - curNode.Position.Y):F2})");
Console.WriteLine($"[ValidateDockingDirection] 에러메시지: {error}"); Console.WriteLine($"[ValidateDockingDirection] 에러메시지: {error}");
return DockingValidationResult.CreateInvalid( return DockingValidationResult.CreateInvalid(
LastNode.NodeId, LastNode.Id,
LastNode.Type, LastNode.Type,
pathResult.DetailedPath[i].MotorDirection, pathResult.DetailedPath[i].MotorDirection,
pathResult.DetailedPath[i].MotorDirection, pathResult.DetailedPath[i].MotorDirection,
@@ -150,12 +150,12 @@ namespace AGVNavigationCore.Utils
System.Diagnostics.Debug.WriteLine($"[DockingValidator] 필요한 도킹 방향: {requiredDirection}"); System.Diagnostics.Debug.WriteLine($"[DockingValidator] 필요한 도킹 방향: {requiredDirection}");
var LastNodeInfo = pathResult.DetailedPath.Last(); var LastNodeInfo = pathResult.DetailedPath.Last();
if (LastNodeInfo.NodeId != LastNode.NodeId) if (LastNodeInfo.NodeId != LastNode.Id)
{ {
string error = $"마지막 노드의 도킹방향과 경로정보의 노드ID 불일치: 필요={LastNode.NodeId}, 계산됨={LastNodeInfo.NodeId }"; string error = $"마지막 노드의 도킹방향과 경로정보의 노드ID 불일치: 필요={LastNode.Id}, 계산됨={LastNodeInfo.NodeId }";
System.Diagnostics.Debug.WriteLine($"[DockingValidator] ❌ 도킹 검증 실패: {error}"); System.Diagnostics.Debug.WriteLine($"[DockingValidator] ❌ 도킹 검증 실패: {error}");
return DockingValidationResult.CreateInvalid( return DockingValidationResult.CreateInvalid(
LastNode.NodeId, LastNode.Id,
LastNode.Type, LastNode.Type,
requiredDirection, requiredDirection,
LastNodeInfo.MotorDirection, LastNodeInfo.MotorDirection,
@@ -167,7 +167,7 @@ namespace AGVNavigationCore.Utils
{ {
System.Diagnostics.Debug.WriteLine($"[DockingValidator] ✅ 도킹 검증 성공"); System.Diagnostics.Debug.WriteLine($"[DockingValidator] ✅ 도킹 검증 성공");
return DockingValidationResult.CreateValid( return DockingValidationResult.CreateValid(
LastNode.NodeId, LastNode.Id,
LastNode.Type, LastNode.Type,
requiredDirection, requiredDirection,
LastNodeInfo.MotorDirection); LastNodeInfo.MotorDirection);
@@ -177,7 +177,7 @@ namespace AGVNavigationCore.Utils
string error = $"도킹 방향 불일치: 필요={GetDirectionText(requiredDirection)}, 계산됨={GetDirectionText(LastNodeInfo.MotorDirection)}"; string error = $"도킹 방향 불일치: 필요={GetDirectionText(requiredDirection)}, 계산됨={GetDirectionText(LastNodeInfo.MotorDirection)}";
System.Diagnostics.Debug.WriteLine($"[DockingValidator] ❌ 도킹 검증 실패: {error}"); System.Diagnostics.Debug.WriteLine($"[DockingValidator] ❌ 도킹 검증 실패: {error}");
return DockingValidationResult.CreateInvalid( return DockingValidationResult.CreateInvalid(
LastNode.NodeId, LastNode.Id,
LastNode.Type, LastNode.Type,
requiredDirection, requiredDirection,
LastNodeInfo.MotorDirection, LastNodeInfo.MotorDirection,
@@ -264,7 +264,7 @@ namespace AGVNavigationCore.Utils
var deltaY = lastNode.Position.Y - secondLastNode.Position.Y; var deltaY = lastNode.Position.Y - secondLastNode.Position.Y;
var distance = Math.Sqrt(deltaX * deltaX + deltaY * deltaY); var distance = Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
System.Diagnostics.Debug.WriteLine($"[CalculateFinalDirection] 마지막 구간: {secondLastNode.NodeId} → {lastNode.NodeId}, 벡터: ({deltaX}, {deltaY}), 거리: {distance:F2}"); System.Diagnostics.Debug.WriteLine($"[CalculateFinalDirection] 마지막 구간: {secondLastNode.Id} → {lastNode.Id}, 벡터: ({deltaX}, {deltaY}), 거리: {distance:F2}");
// 이동 거리가 매우 작으면 현재 방향 유지 // 이동 거리가 매우 작으면 현재 방향 유지
if (distance < 1.0) if (distance < 1.0)

View File

@@ -28,10 +28,10 @@ namespace AGVNavigationCore.Utils
Console.WriteLine("================================================\n"); Console.WriteLine("================================================\n");
// 테스트 노드 생성 // 테스트 노드 생성
var node001 = new MapNode { NodeId = "N001", RfidId = "001", Position = new Point(65, 229), ConnectedNodes = new List<string> { "N002" } }; var node001 = new MapNode { Id = "N001", RfidId = 001, Position = new Point(65, 229), ConnectedNodes = new List<string> { "N002" } };
var node002 = new MapNode { NodeId = "N002", RfidId = "002", Position = new Point(206, 244), ConnectedNodes = new List<string> { "N001", "N003" } }; var node002 = new MapNode { Id = "N002", RfidId = 002, Position = new Point(206, 244), ConnectedNodes = new List<string> { "N001", "N003" } };
var node003 = new MapNode { NodeId = "N003", RfidId = "003", Position = new Point(278, 278), ConnectedNodes = new List<string> { "N002", "N004" } }; var node003 = new MapNode { Id = "N003", RfidId = 003, Position = new Point(278, 278), ConnectedNodes = new List<string> { "N002", "N004" } };
var node004 = new MapNode { NodeId = "N004", RfidId = "004", Position = new Point(380, 340), ConnectedNodes = new List<string> { "N003", "N022", "N031" } }; var node004 = new MapNode { Id = "N004", RfidId = 004, Position = new Point(380, 340), ConnectedNodes = new List<string> { "N003", "N022", "N031" } };
var allNodes = new List<MapNode> { node001, node002, node003, node004 }; var allNodes = new List<MapNode> { node001, node002, node003, node004 };
@@ -114,8 +114,8 @@ namespace AGVNavigationCore.Utils
AgvDirection motorDir = currentMotorDirection ?? direction; AgvDirection motorDir = currentMotorDirection ?? direction;
Console.WriteLine($"설명: {description}"); Console.WriteLine($"설명: {description}");
Console.WriteLine($"이전 위치: {prevPos} (RFID: {allNodes.First(n => n.Position == prevPos)?.RfidId ?? "?"})"); Console.WriteLine($"이전 위치: {prevPos} (RFID: {allNodes.First(n => n.Position == prevPos)?.RfidId.ToString("0000") ?? "?"})");
Console.WriteLine($"현재 노드: {currentNode.NodeId} (RFID: {currentNode.RfidId}) - 위치: {currentNode.Position}"); Console.WriteLine($"현재 노드: {currentNode.Id} (RFID: {currentNode.RfidId}) - 위치: {currentNode.Position}");
Console.WriteLine($"현재 모터 방향: {motorDir}"); Console.WriteLine($"현재 모터 방향: {motorDir}");
Console.WriteLine($"요청 방향: {direction}"); Console.WriteLine($"요청 방향: {direction}");
@@ -128,32 +128,32 @@ namespace AGVNavigationCore.Utils
Console.WriteLine($"이동 벡터: ({movementVector.X}, {movementVector.Y})"); Console.WriteLine($"이동 벡터: ({movementVector.X}, {movementVector.Y})");
// 각 후보 노드에 대한 점수 계산 // 각 후보 노드에 대한 점수 계산
Console.WriteLine($"\n현재 노드({currentNode.NodeId})의 ConnectedNodes: {string.Join(", ", currentNode.ConnectedNodes)}"); Console.WriteLine($"\n현재 노드({currentNode.Id})의 ConnectedNodes: {string.Join(", ", currentNode.ConnectedNodes)}");
Console.WriteLine($"가능한 다음 노드들:"); Console.WriteLine($"가능한 다음 노드들:");
var candidateNodes = allNodes.Where(n => var candidateNodes = allNodes.Where(n =>
currentNode.ConnectedNodes.Contains(n.NodeId) && n.NodeId != currentNode.NodeId currentNode.ConnectedNodes.Contains(n.Id) && n.Id != currentNode.Id
).ToList(); ).ToList();
foreach (var candidate in candidateNodes) foreach (var candidate in candidateNodes)
{ {
var score = CalculateScoreAndPrint(movementVector, currentNode.Position, candidate, direction); var score = CalculateScoreAndPrint(movementVector, currentNode.Position, candidate, direction);
string isExpected = (candidate.NodeId == expectedNextNode.NodeId) ? " ← 예상 노드" : ""; string isExpected = (candidate.Id == expectedNextNode.Id) ? " ← 예상 노드" : "";
Console.WriteLine($" {candidate.NodeId} (RFID: {candidate.RfidId}) - 위치: {candidate.Position} - 점수: {score:F1}{isExpected}"); Console.WriteLine($" {candidate.Id} (RFID: {candidate.RfidId}) - 위치: {candidate.Position} - 점수: {score:F1}{isExpected}");
} }
// 최고 점수 노드 선택 // 최고 점수 노드 선택
var bestCandidate = GetBestCandidate(movementVector, currentNode.Position, candidateNodes, direction); var bestCandidate = GetBestCandidate(movementVector, currentNode.Position, candidateNodes, direction);
Console.WriteLine($"\n✓ 선택된 노드: {bestCandidate.NodeId} (RFID: {bestCandidate.RfidId})"); Console.WriteLine($"\n✓ 선택된 노드: {bestCandidate.Id} (RFID: {bestCandidate.RfidId})");
if (bestCandidate.NodeId == expectedNextNode.NodeId) if (bestCandidate.Id == expectedNextNode.Id)
{ {
Console.WriteLine($"✅ 정답! ({expectedNodeIdStr})"); Console.WriteLine($"✅ 정답! ({expectedNodeIdStr})");
} }
else else
{ {
Console.WriteLine($"❌ 오답! 예상: {expectedNextNode.NodeId}, 실제: {bestCandidate.NodeId}"); Console.WriteLine($"❌ 오답! 예상: {expectedNextNode.Id}, 실제: {bestCandidate.Id}");
} }
} }

View File

@@ -63,14 +63,14 @@ namespace AGVNavigationCore.Utils
// 이전 노드 제외 (되돌아가는 방향 제외) // 이전 노드 제외 (되돌아가는 방향 제외)
if (previousNode != null) if (previousNode != null)
{ {
nextNodes = nextNodes.Where(n => n.NodeId != previousNode.NodeId).ToList(); nextNodes = nextNodes.Where(n => n.Id != previousNode.Id).ToList();
} }
if (nextNodes.Count == 1) if (nextNodes.Count == 1)
{ {
// 직선 경로: 다음 노드 방향으로 예측 // 직선 경로: 다음 노드 방향으로 예측
targetPosition = nextNodes.First().Position; targetPosition = nextNodes.First().Position;
calculationMethod = $"전진 경로 예측 ({currentNode.NodeId}→{nextNodes.First().NodeId})"; calculationMethod = $"전진 경로 예측 ({currentNode.Id}→{nextNodes.First().Id})";
} }
else if (nextNodes.Count > 1) else if (nextNodes.Count > 1)
{ {
@@ -268,7 +268,7 @@ namespace AGVNavigationCore.Utils
foreach (var nodeId in currentNode.ConnectedNodes) foreach (var nodeId in currentNode.ConnectedNodes)
{ {
var connectedNode = mapNodes.FirstOrDefault(n => n.NodeId == nodeId); var connectedNode = mapNodes.FirstOrDefault(n => n.Id == nodeId);
if (connectedNode != null) if (connectedNode != null)
{ {
connectedNodes.Add(connectedNode); connectedNodes.Add(connectedNode);

View File

@@ -14,7 +14,7 @@ namespace AGVNavigationCore.Utils
{ {
public void RunTests() public void RunTests()
{ {
string mapFilePath = @"C:\Data\Source\(5613#) ENIG AGV\Source\Cs_HMI\Data\NewMap.agvmap"; string mapFilePath = @"C:\Data\Source\(5613#) ENIG AGV\Source\Cs_HMI\Data\NewMap.json";
var tester = new DirectionalPathfinderTest(); var tester = new DirectionalPathfinderTest();
@@ -29,26 +29,26 @@ namespace AGVNavigationCore.Utils
tester.PrintAllNodes(); tester.PrintAllNodes();
// 테스트 시나리오 1: 001 → 002 → Forward (003 기대) // 테스트 시나리오 1: 001 → 002 → Forward (003 기대)
tester.PrintNodeInfo("001"); tester.PrintNodeInfo(001);
tester.PrintNodeInfo("002"); tester.PrintNodeInfo(002);
tester.TestDirectionalMovement("001", "002", AgvDirection.Forward); tester.TestDirectionalMovement(001, 002, AgvDirection.Forward);
// 테스트 시나리오 2: 002 → 001 → Backward (000 또는 이전 기대) // 테스트 시나리오 2: 002 → 001 → Backward (000 또는 이전 기대)
tester.TestDirectionalMovement("002", "001", AgvDirection.Backward); tester.TestDirectionalMovement(002, 001, AgvDirection.Backward);
// 테스트 시나리오 3: 002 → 003 → Forward // 테스트 시나리오 3: 002 → 003 → Forward
tester.PrintNodeInfo("003"); tester.PrintNodeInfo(003);
tester.TestDirectionalMovement("002", "003", AgvDirection.Forward); tester.TestDirectionalMovement(002, 003, AgvDirection.Forward);
// 테스트 시나리오 4: 003 → 004 → Forward // 테스트 시나리오 4: 003 → 004 → Forward
tester.PrintNodeInfo("004"); tester.PrintNodeInfo(004);
tester.TestDirectionalMovement("003", "004", AgvDirection.Forward); tester.TestDirectionalMovement(003, 004, AgvDirection.Forward);
// 테스트 시나리오 5: 003 → 004 → Right (030 기대) // 테스트 시나리오 5: 003 → 004 → Right (030 기대)
tester.TestDirectionalMovement("003", "004", AgvDirection.Right); tester.TestDirectionalMovement(003, 004, AgvDirection.Right);
// 테스트 시나리오 6: 004 → 003 → Backward // 테스트 시나리오 6: 004 → 003 → Backward
tester.TestDirectionalMovement("004", "003", AgvDirection.Backward); tester.TestDirectionalMovement(004, 003, AgvDirection.Backward);
Console.WriteLine("\n\n=== 테스트 완료 ==="); Console.WriteLine("\n\n=== 테스트 완료 ===");
} }

View File

@@ -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\</OutputPath> <OutputPath>..\..\..\..\..\..\Amkor\AGV4\Test\Simulator\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>

View File

@@ -108,7 +108,6 @@ namespace AGVSimulator.Forms
} }
private UnifiedAGVCanvas _simulatorCanvas; private UnifiedAGVCanvas _simulatorCanvas;
private List<MapNode> _mapNodes;
private AGVPathfinder _advancedPathfinder; private AGVPathfinder _advancedPathfinder;
private List<VirtualAGV> _agvList; private List<VirtualAGV> _agvList;
private SimulationState _simulationState; private SimulationState _simulationState;
@@ -158,7 +157,7 @@ namespace AGVSimulator.Forms
_config = SimulatorConfig.Load(); _config = SimulatorConfig.Load();
// 데이터 초기화 // 데이터 초기화
_mapNodes = new List<MapNode>();
_agvList = new List<VirtualAGV>(); _agvList = new List<VirtualAGV>();
_simulationState = new SimulationState(); _simulationState = new SimulationState();
_currentMapFilePath = string.Empty; _currentMapFilePath = string.Empty;
@@ -232,7 +231,7 @@ namespace AGVSimulator.Forms
{ {
using (var openDialog = new OpenFileDialog()) using (var openDialog = new OpenFileDialog())
{ {
openDialog.Filter = "AGV Map Files (*.agvmap)|*.agvmap|모든 파일 (*.*)|*.*"; openDialog.Filter = "AGV Map Files (*.json)|*.json|모든 파일 (*.*)|*.*";
openDialog.Title = "맵 파일 열기"; openDialog.Title = "맵 파일 열기";
if (openDialog.ShowDialog() == DialogResult.OK) if (openDialog.ShowDialog() == DialogResult.OK)
@@ -305,14 +304,14 @@ namespace AGVSimulator.Forms
private void OnAddAGV_Click(object sender, EventArgs e) private void OnAddAGV_Click(object sender, EventArgs e)
{ {
if (_mapNodes == null || _mapNodes.Count == 0) if (_simulatorCanvas.Nodes == null || _simulatorCanvas.Nodes.Count == 0)
{ {
MessageBox.Show("먼저 맵을 로드해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBox.Show("먼저 맵을 로드해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
return; return;
} }
var agvId = $"AGV{_agvList.Count + 1:D2}"; var agvId = $"AGV{_agvList.Count + 1:D2}";
var startPosition = _mapNodes.First().Position; // 첫 번째 노드에서 시작 var startPosition = _simulatorCanvas.Nodes.First().Position; // 첫 번째 노드에서 시작
var newAGV = new VirtualAGV(agvId, startPosition); var newAGV = new VirtualAGV(agvId, startPosition);
_agvList.Add(newAGV); _agvList.Add(newAGV);
@@ -396,7 +395,7 @@ namespace AGVSimulator.Forms
if (_advancedPathfinder == null) if (_advancedPathfinder == null)
{ {
_advancedPathfinder = new AGVPathfinder(_mapNodes); _advancedPathfinder = new AGVPathfinder(_simulatorCanvas.Nodes);
} }
// 현재 AGV 방향 가져오기 // 현재 AGV 방향 가져오기
@@ -420,14 +419,14 @@ namespace AGVSimulator.Forms
// 도킹 검증이 없는 경우 추가 검증 수행 // 도킹 검증이 없는 경우 추가 검증 수행
if (advancedResult.DockingValidation == null || !advancedResult.DockingValidation.IsValidationRequired) if (advancedResult.DockingValidation == null || !advancedResult.DockingValidation.IsValidationRequired)
{ {
advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _mapNodes); advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _simulatorCanvas.Nodes);
} }
//마지막대상이 버퍼라면 시퀀스처리를 해야한다 //마지막대상이 버퍼라면 시퀀스처리를 해야한다
if(targetNode.Type == NodeType.Buffer) if(targetNode.StationType == StationType.Buffer)
{ {
var lastDetailPath = advancedResult.DetailedPath.Last(); var lastDetailPath = advancedResult.DetailedPath.Last();
if(lastDetailPath.NodeId == targetNode.NodeId) //마지막노드 재확인 if(lastDetailPath.NodeId == targetNode.Id) //마지막노드 재확인
{ {
//버퍼에 도킹할때에는 마지막 노드에서 멈추고 시퀀스를 적용해야한다 //버퍼에 도킹할때에는 마지막 노드에서 멈추고 시퀀스를 적용해야한다
advancedResult.DetailedPath = advancedResult.DetailedPath.Take(advancedResult.DetailedPath.Count - 1).ToList(); advancedResult.DetailedPath = advancedResult.DetailedPath.Take(advancedResult.DetailedPath.Count - 1).ToList();
@@ -492,7 +491,7 @@ namespace AGVSimulator.Forms
} }
} }
private void OnTargetNodeSelected(object sender, List<MapNode> selectedNodes) private void OnTargetNodeSelected(object sender, List<NodeBase> selectedNodes)
{ {
try try
{ {
@@ -511,9 +510,9 @@ namespace AGVSimulator.Forms
if (selectedNode == null) return; if (selectedNode == null) return;
// 목적지를 선택된 노드로 설정 // 목적지를 선택된 노드로 설정
SetTargetNodeInCombo(selectedNode.NodeId); SetTargetNodeInCombo(selectedNode.Id);
var displayText = GetDisplayName(selectedNode.NodeId); var displayText = GetDisplayName(selectedNode.Id);
_statusLabel.Text = $"타겟계산 - 목적지: {displayText}"; _statusLabel.Text = $"타겟계산 - 목적지: {displayText}";
// 자동으로 경로 계산 수행 // 자동으로 경로 계산 수행
@@ -578,7 +577,7 @@ 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].ToString();
if (item.Contains($"[{closestNode.NodeId}]")) if (item.Contains($"[{closestNode.Id}]"))
{ {
_startNodeCombo.SelectedIndex = i; _startNodeCombo.SelectedIndex = i;
break; break;
@@ -598,13 +597,13 @@ namespace AGVSimulator.Forms
/// </summary> /// </summary>
private MapNode FindClosestNode(Point position) private MapNode FindClosestNode(Point position)
{ {
if (_mapNodes == null || _mapNodes.Count == 0) if (_simulatorCanvas.Nodes == null || _simulatorCanvas.Nodes.Count == 0)
return null; return null;
MapNode closestNode = null; MapNode closestNode = null;
double closestDistance = double.MaxValue; double closestDistance = double.MaxValue;
foreach (var node in _mapNodes) foreach (var node in _simulatorCanvas.Nodes)
{ {
var distance = Math.Sqrt(Math.Pow(node.Position.X - position.X, 2) + var distance = Math.Sqrt(Math.Pow(node.Position.X - position.X, 2) +
Math.Pow(node.Position.Y - position.Y, 2)); Math.Pow(node.Position.Y - position.Y, 2));
@@ -636,7 +635,7 @@ namespace AGVSimulator.Forms
/// <summary> /// <summary>
/// 맵 스캔 모드에서 RFID로부터 노드 생성 /// 맵 스캔 모드에서 RFID로부터 노드 생성
/// </summary> /// </summary>
private void CreateNodeFromRfidScan(string rfidId, VirtualAGV selectedAGV) private void CreateNodeFromRfidScan(ushort rfidId, VirtualAGV selectedAGV)
{ {
try try
{ {
@@ -645,11 +644,11 @@ namespace AGVSimulator.Forms
var currentDirection = directionItem?.Direction ?? AgvDirection.Forward; var currentDirection = directionItem?.Direction ?? AgvDirection.Forward;
// 중복 RFID 확인 // 중복 RFID 확인
var existingNode = _mapNodes?.FirstOrDefault(n => n.RfidId.Equals(rfidId, StringComparison.OrdinalIgnoreCase)); var existingNode = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.RfidId == rfidId);
if (existingNode != null) if (existingNode != null)
{ {
// 이미 존재하는 노드로 이동 // 이미 존재하는 노드로 이동
Program.WriteLine($"[맵 스캔] RFID '{rfidId}'는 이미 존재합니다 (노드: {existingNode.NodeId})"); Program.WriteLine($"[맵 스캔] RFID '{rfidId}'는 이미 존재합니다 (노드: {existingNode.Id})");
// 기존 노드로 AGV 위치 설정 // 기존 노드로 AGV 위치 설정
_simulatorCanvas.SetAGVPosition(selectedAGV.AgvId, existingNode, currentDirection); _simulatorCanvas.SetAGVPosition(selectedAGV.AgvId, existingNode, currentDirection);
@@ -659,7 +658,7 @@ namespace AGVSimulator.Forms
_lastNodeAddTime = DateTime.Now; _lastNodeAddTime = DateTime.Now;
_lastScanDirection = currentDirection; // 방향 업데이트 _lastScanDirection = currentDirection; // 방향 업데이트
_statusLabel.Text = $"기존 노드로 이동: {existingNode.NodeId} [{GetDirectionSymbol(currentDirection)}]"; _statusLabel.Text = $"기존 노드로 이동: {existingNode.Id} [{GetDirectionSymbol(currentDirection)}]";
_rfidTextBox.Text = ""; _rfidTextBox.Text = "";
return; return;
} }
@@ -720,28 +719,26 @@ namespace AGVSimulator.Forms
var newNodeId = $"{_scanNodeCounter:D3}"; var newNodeId = $"{_scanNodeCounter:D3}";
var newNode = new MapNode var newNode = new MapNode
{ {
NodeId = newNodeId, Id = newNodeId,
RfidId = rfidId, RfidId = rfidId,
Position = new Point(newX, newY), Position = new Point(newX, newY),
Type = NodeType.Normal, IsActive = true
IsActive = true,
Name = $"N{_scanNodeCounter}"
}; };
// 맵에 추가 // 맵에 추가
if (_mapNodes == null) if (_simulatorCanvas.Nodes == null)
_mapNodes = new List<MapNode>(); _simulatorCanvas.Nodes = new List<MapNode>();
_mapNodes.Add(newNode); _simulatorCanvas.Nodes.Add(newNode);
// 이전 노드와 연결 생성 // 이전 노드와 연결 생성
if (_lastScannedNode != null) if (_lastScannedNode != null)
{ {
// 양방향 연결 (ConnectedNodes에 추가 - JSON 저장됨) // 양방향 연결 (ConnectedNodes에 추가 - JSON 저장됨)
_lastScannedNode.AddConnection(newNode.NodeId); _lastScannedNode.AddConnection(newNode.Id);
newNode.AddConnection(_lastScannedNode.NodeId); newNode.AddConnection(_lastScannedNode.Id);
Program.WriteLine($"[맵 스캔] 연결 생성: {_lastScannedNode.NodeId} ↔ {newNode.NodeId}"); Program.WriteLine($"[맵 스캔] 연결 생성: {_lastScannedNode.Id} ↔ {newNode.Id}");
} }
// AGV 위치 설정 // AGV 위치 설정
@@ -749,10 +746,10 @@ namespace AGVSimulator.Forms
selectedAGV.SetPosition(newNode, currentDirection); selectedAGV.SetPosition(newNode, currentDirection);
// 캔버스 업데이트 // 캔버스 업데이트
_simulatorCanvas.Nodes = _mapNodes; _simulatorCanvas.Nodes = _simulatorCanvas.Nodes;
// 화면을 새 노드 위치로 이동 // 화면을 새 노드 위치로 이동
_simulatorCanvas.PanToNode(newNode.NodeId); _simulatorCanvas.PanToNode(newNode.Id);
_simulatorCanvas.Invalidate(); _simulatorCanvas.Invalidate();
// 상태 업데이트 // 상태 업데이트
@@ -764,10 +761,10 @@ namespace AGVSimulator.Forms
// UI 업데이트 // UI 업데이트
UpdateNodeComboBoxes(); UpdateNodeComboBoxes();
_statusLabel.Text = $"노드 생성: {newNode.NodeId} (RFID: {rfidId}) [{GetDirectionSymbol(currentDirection)}] - 총 {_mapNodes.Count}개"; _statusLabel.Text = $"노드 생성: {newNode.Id} (RFID: {rfidId}) [{GetDirectionSymbol(currentDirection)}] - 총 {_simulatorCanvas.Nodes.Count}개";
_rfidTextBox.Text = ""; _rfidTextBox.Text = "";
Program.WriteLine($"[맵 스캔] 노드 생성 완료: {newNode.NodeId} (RFID: {rfidId}) at ({newX}, {newY}), 방향: {currentDirection}"); Program.WriteLine($"[맵 스캔] 노드 생성 완료: {newNode.Id} (RFID: {rfidId}) at ({newX}, {newY}), 방향: {currentDirection}");
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -808,7 +805,7 @@ namespace AGVSimulator.Forms
if (agv.CurrentNodeId != null && agv.CurrentNodeId != _lastSentNodeId) if (agv.CurrentNodeId != null && agv.CurrentNodeId != _lastSentNodeId)
{ {
var rfid = GetRfidByNodeId(agv.CurrentNodeId); var rfid = GetRfidByNodeId(agv.CurrentNodeId);
if (!string.IsNullOrEmpty(rfid)) if (rfid > 0)
{ {
SendTag(rfid); SendTag(rfid);
_lastSentNodeId = agv.CurrentNodeId; _lastSentNodeId = agv.CurrentNodeId;
@@ -845,7 +842,7 @@ namespace AGVSimulator.Forms
// RFID 값 확인 // RFID 값 확인
var rfidId = _rfidTextBox.Text.Trim(); var rfidId = _rfidTextBox.Text.Trim();
if (string.IsNullOrEmpty(rfidId)) if (ushort.TryParse(rfidId,out ushort rfidvalue)==false)
{ {
MessageBox.Show("RFID 값을 입력해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBox.Show("RFID 값을 입력해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
return; return;
@@ -859,13 +856,13 @@ namespace AGVSimulator.Forms
// 맵 스캔 모드일 때: 노드 자동 생성 // 맵 스캔 모드일 때: 노드 자동 생성
if (_isMapScanMode) if (_isMapScanMode)
{ {
CreateNodeFromRfidScan(rfidId, selectedAGV); CreateNodeFromRfidScan(rfidvalue, selectedAGV);
this._simulatorCanvas.FitToNodes(); this._simulatorCanvas.FitToNodes();
return; return;
} }
// RFID에 해당하는 노드 직접 찾기 // RFID에 해당하는 노드 직접 찾기
var targetNode = _mapNodes?.FirstOrDefault(n => n.RfidId.Equals(rfidId, StringComparison.OrdinalIgnoreCase)); var targetNode = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.RfidId == rfidvalue);
if (targetNode == null) if (targetNode == null)
{ {
MessageBox.Show($"RFID '{rfidId}'에 해당하는 노드를 찾을 수 없습니다.\n\n사용 가능한 RFID 목록:\n{GetAvailableRfidList()}", MessageBox.Show($"RFID '{rfidId}'에 해당하는 노드를 찾을 수 없습니다.\n\n사용 가능한 RFID 목록:\n{GetAvailableRfidList()}",
@@ -875,15 +872,15 @@ namespace AGVSimulator.Forms
//이전위치와 동일한지 체크한다. //이전위치와 동일한지 체크한다.
if (selectedAGV.CurrentNodeId == targetNode.NodeId && selectedAGV.CurrentDirection == selectedDirection) if (selectedAGV.CurrentNodeId == targetNode.Id && selectedAGV.CurrentDirection == selectedDirection)
{ {
Program.WriteLine($"이전 노드위치와 모터의 방향이 동일하여 현재 위치 변경이 취소됩니다(NODE:{targetNode.NodeId},RFID:{targetNode.RfidId},DIR:{selectedDirection})"); Program.WriteLine($"이전 노드위치와 모터의 방향이 동일하여 현재 위치 변경이 취소됩니다(NODE:{targetNode.Id},RFID:{targetNode.RfidId},DIR:{selectedDirection})");
return; return;
} }
// 콘솔 출력 (상세한 리프트 방향 계산 과정) // 콘솔 출력 (상세한 리프트 방향 계산 과정)
Program.WriteLine($"[AGV-{selectedAGV.AgvId}] 위치 설정:"); Program.WriteLine($"[AGV-{selectedAGV.AgvId}] 위치 설정:");
Program.WriteLine($" RFID: {rfidId} → 노드: {targetNode.NodeId}"); Program.WriteLine($" RFID: {rfidId} → 노드: {targetNode.Id}");
Program.WriteLine($" 위치: ({targetNode.Position.X}, {targetNode.Position.Y})"); Program.WriteLine($" 위치: ({targetNode.Position.X}, {targetNode.Position.Y})");
Program.WriteLine($" 방향: {selectedDirectionItem?.DisplayText ?? ""} ({selectedDirection})"); Program.WriteLine($" 방향: {selectedDirectionItem?.DisplayText ?? ""} ({selectedDirection})");
@@ -912,14 +909,14 @@ namespace AGVSimulator.Forms
CalculateLiftDirectionDetailed(selectedAGV); CalculateLiftDirectionDetailed(selectedAGV);
Program.WriteLine(""); Program.WriteLine("");
_statusLabel.Text = $"{selectedAGV.AgvId} 위치를 RFID '{rfidId}' (노드: {targetNode.NodeId}), 방향: {selectedDirectionItem?.DisplayText ?? ""}로 설정했습니다."; _statusLabel.Text = $"{selectedAGV.AgvId} 위치를 RFID '{rfidId}' (노드: {targetNode.Id}), 방향: {selectedDirectionItem?.DisplayText ?? ""}로 설정했습니다.";
_rfidTextBox.Text = ""; // 입력 필드 초기화 _rfidTextBox.Text = ""; // 입력 필드 초기화
// 시뮬레이터 캔버스의 해당 노드로 이동 // 시뮬레이터 캔버스의 해당 노드로 이동
//_simulatorCanvas.PanToNode(targetNode.NodeId); //_simulatorCanvas.PanToNode(targetNode.NodeId);
// 시작 노드 콤보박스를 현재 위치로 자동 선택 // 시작 노드 콤보박스를 현재 위치로 자동 선택
SetStartNodeToCombo(targetNode.NodeId); SetStartNodeToCombo(targetNode.Id);
} }
/// <summary> /// <summary>
@@ -948,19 +945,18 @@ namespace AGVSimulator.Forms
private string GetAvailableRfidList() private string GetAvailableRfidList()
{ {
if (_mapNodes == null || _mapNodes.Count == 0) if (_simulatorCanvas.Nodes == null || _simulatorCanvas.Nodes.Count == 0)
return "매핑된 RFID가 없습니다."; return "매핑된 RFID가 없습니다.";
var nodesWithRfid = _mapNodes.Where(n => n.HasRfid()).ToList(); var nodesWithRfid = _simulatorCanvas.Nodes.Where(n => n.HasRfid()).ToList();
if (nodesWithRfid.Count == 0) if (nodesWithRfid.Count == 0)
return "RFID가 할당된 노드가 없습니다."; return "RFID가 할당된 노드가 없습니다.";
// 처음 10개의 RFID만 표시 (노드 이름 포함) // 처음 10개의 RFID만 표시 (노드 이름 포함)
var rfidList = nodesWithRfid.Take(10).Select(n => var rfidList = nodesWithRfid.Take(10).Select((Func<MapNode, string>)(n =>
{ {
var nodeNamePart = !string.IsNullOrEmpty(n.Name) ? $" {n.Name}" : ""; return $"- {n.RfidId} → {n.Id}";
return $"- {n.RfidId} → {n.NodeId}{nodeNamePart}"; }));
});
var result = string.Join("\n", rfidList); var result = string.Join("\n", rfidList);
if (nodesWithRfid.Count > 10) if (nodesWithRfid.Count > 10)
@@ -979,13 +975,13 @@ namespace AGVSimulator.Forms
{ {
Console.WriteLine($"Map File Load : {filePath}"); Console.WriteLine($"Map File Load : {filePath}");
_mapNodes = result.Nodes; _simulatorCanvas.Nodes = result.Nodes;
_currentMapFilePath = filePath; _currentMapFilePath = filePath;
// RFID 자동 할당 제거 - 에디터에서 설정한 값 그대로 사용 // RFID 자동 할당 제거 - 에디터에서 설정한 값 그대로 사용
// 시뮬레이터 캔버스에 맵 설정 // 시뮬레이터 캔버스에 맵 설정
_simulatorCanvas.Nodes = _mapNodes; _simulatorCanvas.SetMapLoadResult(result);//.Nodes = _simulatorCanvas.Nodes;
// 맵 설정 적용 (배경색, 그리드 표시) // 맵 설정 적용 (배경색, 그리드 표시)
if (result.Settings != null) if (result.Settings != null)
@@ -1053,15 +1049,14 @@ namespace AGVSimulator.Forms
_startNodeCombo.Items.Clear(); _startNodeCombo.Items.Clear();
_targetNodeCombo.Items.Clear(); _targetNodeCombo.Items.Clear();
if (_mapNodes != null) if (_simulatorCanvas.Nodes != null)
{ {
foreach (var node in _mapNodes) foreach (var node in _simulatorCanvas.Nodes)
{ {
if (node.IsActive && node.HasRfid()) if (node.IsActive && node.HasRfid())
{ {
// {rfid} - [{node}] {name} 형식으로 ComboBoxItem 생성 // {rfid} - [{node}] {name} 형식으로 ComboBoxItem 생성
var nodeNamePart = !string.IsNullOrEmpty(node.Name) ? $" {node.Name}" : ""; var displayText = $"{node.RfidId} - [{node.Id}]";
var displayText = $"{node.RfidId} - [{node.NodeId}]{nodeNamePart}";
var item = new ComboBoxItem<MapNode>(node, displayText); var item = new ComboBoxItem<MapNode>(node, displayText);
_startNodeCombo.Items.Add(item); _startNodeCombo.Items.Add(item);
@@ -1112,7 +1107,7 @@ namespace AGVSimulator.Forms
// RFID 위치 설정 관련 // RFID 위치 설정 관련
var hasSelectedAGV = _agvListCombo.SelectedItem != null; var hasSelectedAGV = _agvListCombo.SelectedItem != null;
var hasRfidNodes = _mapNodes != null && _mapNodes.Any(n => n.HasRfid()); var hasRfidNodes = _simulatorCanvas.Nodes != null && _simulatorCanvas.Nodes.Any(n => n.HasRfid());
_setPositionButton.Enabled = hasSelectedAGV && hasRfidNodes; _setPositionButton.Enabled = hasSelectedAGV && hasRfidNodes;
_rfidTextBox.Enabled = hasSelectedAGV && hasRfidNodes; _rfidTextBox.Enabled = hasSelectedAGV && hasRfidNodes;
@@ -1185,7 +1180,7 @@ namespace AGVSimulator.Forms
// 경로 예측 기반 LiftCalculator를 사용하여 리프트 방향 계산 // 경로 예측 기반 LiftCalculator를 사용하여 리프트 방향 계산
var liftInfo = AGVNavigationCore.Utils.LiftCalculator.CalculateLiftInfoWithPathPrediction( var liftInfo = AGVNavigationCore.Utils.LiftCalculator.CalculateLiftInfoWithPathPrediction(
currentPos, prevPos.Value, agv.CurrentDirection, _mapNodes); currentPos, prevPos.Value, agv.CurrentDirection, _simulatorCanvas.Nodes);
// 이동 각도 계산 (표시용) // 이동 각도 계산 (표시용)
var moveAngleRad = Math.Atan2(dy, dx); var moveAngleRad = Math.Atan2(dy, dx);
@@ -1229,7 +1224,7 @@ namespace AGVSimulator.Forms
// 경로 예측 기반 LiftCalculator를 사용하여 리프트 방향 계산 // 경로 예측 기반 LiftCalculator를 사용하여 리프트 방향 계산
var liftInfo = AGVNavigationCore.Utils.LiftCalculator.CalculateLiftInfoWithPathPrediction( var liftInfo = AGVNavigationCore.Utils.LiftCalculator.CalculateLiftInfoWithPathPrediction(
currentPos, targetPos.Value, agv.CurrentDirection, _mapNodes); currentPos, targetPos.Value, agv.CurrentDirection, _simulatorCanvas.Nodes);
// 도킹 방향 정보 추가 // 도킹 방향 정보 추가
string dockingInfo = dockingDirection == DockingDirection.Forward ? "전진도킹" : "후진도킹"; string dockingInfo = dockingDirection == DockingDirection.Forward ? "전진도킹" : "후진도킹";
@@ -1260,10 +1255,12 @@ namespace AGVSimulator.Forms
/// <summary> /// <summary>
/// 노드 ID를 RFID 값으로 변환 (NodeResolver 사용) /// 노드 ID를 RFID 값으로 변환 (NodeResolver 사용)
/// </summary> /// </summary>
private string GetRfidByNodeId(string nodeId) private ushort GetRfidByNodeId(string nodeId)
{ {
var node = _mapNodes?.FirstOrDefault(n => n.NodeId == nodeId); var node = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.Id == nodeId);
return node?.HasRfid() == true ? node.RfidId : nodeId; if (node == null) return 0;
if (node.HasRfid()) return node.RfidId;
else return 0;
} }
/// <summary> /// <summary>
@@ -1271,10 +1268,10 @@ namespace AGVSimulator.Forms
/// </summary> /// </summary>
private string GetDisplayName(string nodeId) private string GetDisplayName(string nodeId)
{ {
var node = _mapNodes?.FirstOrDefault(n => n.NodeId == nodeId); var node = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.Id == nodeId);
if (node != null && !string.IsNullOrEmpty(node.RfidId)) if (node != null && node.HasRfid())
{ {
return node.RfidId; return node.RfidId.ToString("0000");
} }
return $"({nodeId})"; return $"({nodeId})";
} }
@@ -1371,7 +1368,7 @@ 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) : "END"; var nextRfidId = info.NextNodeId != null ? GetRfidByNodeId(info.NextNodeId).ToString("0000") : "-END-";
var flags = new List<string>(); var flags = new List<string>();
if (info.CanRotate) flags.Add("회전가능"); if (info.CanRotate) flags.Add("회전가능");
@@ -1544,7 +1541,7 @@ namespace AGVSimulator.Forms
private async void toolStripButton1_Click(object sender, EventArgs e) private async void toolStripButton1_Click(object sender, EventArgs e)
{ {
// 맵과 AGV 확인 // 맵과 AGV 확인
if (_mapNodes == null || _mapNodes.Count == 0) if (_simulatorCanvas.Nodes == null || _simulatorCanvas.Nodes.Count == 0)
{ {
MessageBox.Show("맵 데이터가 없습니다. 먼저 맵을 로드해주세요.", "알림", MessageBox.Show("맵 데이터가 없습니다. 먼저 맵을 로드해주세요.", "알림",
MessageBoxButtons.OK, MessageBoxIcon.Warning); MessageBoxButtons.OK, MessageBoxIcon.Warning);
@@ -1560,8 +1557,7 @@ namespace AGVSimulator.Forms
} }
// 도킹 타겟 노드 찾기 // 도킹 타겟 노드 찾기
var dockingTargets = _mapNodes.Where(n => var dockingTargets = _simulatorCanvas.Nodes.Where(n => n.isDockingNode).ToList();
n.Type == NodeType.Charging || n.Type == NodeType.Loader || n.Type == NodeType.UnLoader || n.Type == NodeType.Clearner || n.Type == NodeType.Buffer).ToList();
if (dockingTargets.Count == 0) if (dockingTargets.Count == 0)
{ {
@@ -1622,13 +1618,9 @@ namespace AGVSimulator.Forms
/// </summary> /// </summary>
private string GetNodeDisplayName(MapNode node) private string GetNodeDisplayName(MapNode node)
{ {
if (node == null) if (node == null) return "-";
return "-"; if (node.HasRfid()) return node.RfidId.ToString("0000");
return $"({node.Id})";
if (!string.IsNullOrEmpty(node.RfidId))
return node.RfidId;
return $"({node.NodeId})";
} }
/// <summary> /// <summary>
@@ -1655,7 +1647,7 @@ namespace AGVSimulator.Forms
for (int i = 0; i < _targetNodeCombo.Items.Count; i++) for (int i = 0; i < _targetNodeCombo.Items.Count; i++)
{ {
var item = _targetNodeCombo.Items[i] as ComboBoxItem<MapNode>; var item = _targetNodeCombo.Items[i] as ComboBoxItem<MapNode>;
if (item?.Value?.NodeId == nodeId) if (item?.Value?.Id == nodeId)
{ {
_targetNodeCombo.SelectedIndex = i; _targetNodeCombo.SelectedIndex = i;
return; return;
@@ -1669,7 +1661,7 @@ namespace AGVSimulator.Forms
private PathTestLogItem CreateTestResultFromUI(MapNode prevNode, MapNode targetNode, private PathTestLogItem CreateTestResultFromUI(MapNode prevNode, MapNode targetNode,
string directionName, (bool result, string message) calcResult) string directionName, (bool result, string message) calcResult)
{ {
var currentNode = _mapNodes.FirstOrDefault(n => n.NodeId == var currentNode = _simulatorCanvas.Nodes.FirstOrDefault(n => n.Id ==
(_agvListCombo.SelectedItem as VirtualAGV)?.CurrentNodeId); (_agvListCombo.SelectedItem as VirtualAGV)?.CurrentNodeId);
var logItem = new PathTestLogItem var logItem = new PathTestLogItem
@@ -1678,7 +1670,7 @@ namespace AGVSimulator.Forms
MotorDirection = directionName, MotorDirection = directionName,
CurrentPosition = GetNodeDisplayName(currentNode), CurrentPosition = GetNodeDisplayName(currentNode),
TargetPosition = GetNodeDisplayName(targetNode), TargetPosition = GetNodeDisplayName(targetNode),
DockingPosition = targetNode.Type == NodeType.Charging ? "충전기" : "장비" DockingPosition = targetNode.StationType == StationType.Charger ? "충전기" : "장비"
}; };
if (calcResult.result) if (calcResult.result)
@@ -1688,7 +1680,7 @@ namespace AGVSimulator.Forms
if (currentPath != null && currentPath.Success) if (currentPath != null && currentPath.Success)
{ {
// 도킹 검증 // 도킹 검증
var dockingValidation = DockingValidator.ValidateDockingDirection(currentPath, _mapNodes); var dockingValidation = DockingValidator.ValidateDockingDirection(currentPath, _simulatorCanvas.Nodes);
if (dockingValidation.IsValid) if (dockingValidation.IsValid)
{ {
@@ -1729,7 +1721,7 @@ namespace AGVSimulator.Forms
var pairs = new List<(MapNode, MapNode)>(); var pairs = new List<(MapNode, MapNode)>();
var processedPairs = new HashSet<string>(); var processedPairs = new HashSet<string>();
foreach (var nodeA in _mapNodes) foreach (var nodeA in _simulatorCanvas.Nodes)
{ {
if (nodeA.ConnectedMapNodes == null || nodeA.ConnectedMapNodes.Count == 0) if (nodeA.ConnectedMapNodes == null || nodeA.ConnectedMapNodes.Count == 0)
continue; continue;
@@ -1741,8 +1733,8 @@ namespace AGVSimulator.Forms
continue; continue;
// 중복 방지 (A→B와 B→A를 같은 것으로 간주) // 중복 방지 (A→B와 B→A를 같은 것으로 간주)
var pairKey1 = $"{nodeA.NodeId}→{nodeB.NodeId}"; var pairKey1 = $"{nodeA.Id}→{nodeB.Id}";
var pairKey2 = $"{nodeB.NodeId}→{nodeA.NodeId}"; var pairKey2 = $"{nodeB.Id}→{nodeA.Id}";
if (nodeA.HasRfid() && nodeB.HasRfid() && !processedPairs.Contains(pairKey1) && !processedPairs.Contains(pairKey2)) if (nodeA.HasRfid() && nodeB.HasRfid() && !processedPairs.Contains(pairKey1) && !processedPairs.Contains(pairKey2))
{ {
@@ -1798,7 +1790,7 @@ namespace AGVSimulator.Forms
this.Invoke((MethodInvoker)delegate this.Invoke((MethodInvoker)delegate
{ {
// RFID 텍스트박스에 값 입력 // RFID 텍스트박스에 값 입력
_rfidTextBox.Text = nodeA.RfidId; _rfidTextBox.Text = nodeA.RfidId.ToString();
// 방향 콤보박스 선택 // 방향 콤보박스 선택
SetDirectionComboBox(direction); SetDirectionComboBox(direction);
@@ -1814,7 +1806,7 @@ namespace AGVSimulator.Forms
this.Invoke((MethodInvoker)delegate this.Invoke((MethodInvoker)delegate
{ {
// RFID 텍스트박스에 값 입력 // RFID 텍스트박스에 값 입력
_rfidTextBox.Text = nodeB.RfidId; _rfidTextBox.Text = nodeB.RfidId.ToString();
// 방향 콤보박스 선택 // 방향 콤보박스 선택
SetDirectionComboBox(direction); SetDirectionComboBox(direction);
@@ -1848,7 +1840,7 @@ namespace AGVSimulator.Forms
prb1.Value = (int)((double)currentTest / totalTests * 100); prb1.Value = (int)((double)currentTest / totalTests * 100);
// 목표 노드 콤보박스 선택 // 목표 노드 콤보박스 선택
SetTargetNodeComboBox(dockingTarget.NodeId); SetTargetNodeComboBox(dockingTarget.Id);
// 경로 계산 버튼 클릭 (실제 사용자 동작) // 경로 계산 버튼 클릭 (실제 사용자 동작)
var calcResult = CalcPath(); var calcResult = CalcPath();
@@ -1955,9 +1947,9 @@ namespace AGVSimulator.Forms
if (result == DialogResult.Yes) if (result == DialogResult.Yes)
{ {
// 기존 맵 데이터 삭제 // 기존 맵 데이터 삭제
_mapNodes?.Clear(); _simulatorCanvas.Nodes?.Clear();
_mapNodes = new List<MapNode>(); _simulatorCanvas.Nodes = new List<MapNode>();
_simulatorCanvas.Nodes = _mapNodes; _simulatorCanvas.Nodes = _simulatorCanvas.Nodes;
_currentMapFilePath = string.Empty; _currentMapFilePath = string.Empty;
UpdateNodeComboBoxes(); UpdateNodeComboBoxes();
_statusLabel.Text = "맵 초기화 완료 - 스캔 모드 시작"; _statusLabel.Text = "맵 초기화 완료 - 스캔 모드 시작";
@@ -1981,16 +1973,16 @@ namespace AGVSimulator.Forms
_isMapScanMode = false; _isMapScanMode = false;
btMakeMap.Text = "맵 생성"; btMakeMap.Text = "맵 생성";
btMakeMap.BackColor = SystemColors.Control; btMakeMap.BackColor = SystemColors.Control;
_statusLabel.Text = $"맵 스캔 완료 - {_mapNodes?.Count ?? 0}개 노드 생성됨"; _statusLabel.Text = $"맵 스캔 완료 - {_simulatorCanvas.Nodes?.Count ?? 0}개 노드 생성됨";
Program.WriteLine($"[맵 스캔] 스캔 모드 종료 - 총 {_mapNodes?.Count ?? 0}개 노드"); Program.WriteLine($"[맵 스캔] 스캔 모드 종료 - 총 {_simulatorCanvas.Nodes?.Count ?? 0}개 노드");
// 맵 저장 권장 // 맵 저장 권장
if (_mapNodes != null && _mapNodes.Count > 0) if (_simulatorCanvas.Nodes != null && _simulatorCanvas.Nodes.Count > 0)
{ {
var saveResult = MessageBox.Show( var saveResult = MessageBox.Show(
$"맵 스캔이 완료되었습니다.\n\n" + $"맵 스캔이 완료되었습니다.\n\n" +
$"생성된 노드: {_mapNodes.Count}개\n\n" + $"생성된 노드: {_simulatorCanvas.Nodes.Count}개\n\n" +
"맵을 저장하시겠습니까?", "맵을 저장하시겠습니까?",
"맵 저장", "맵 저장",
MessageBoxButtons.YesNo, MessageBoxButtons.YesNo,
@@ -2014,11 +2006,11 @@ namespace AGVSimulator.Forms
try try
{ {
// MapLoader의 표준 저장 메서드 사용 (AGVMapEditor와 동일한 형식) // MapLoader의 표준 저장 메서드 사용 (AGVMapEditor와 동일한 형식)
bool success = MapLoader.SaveMapToFile(filePath, _mapNodes); bool success = MapLoader.SaveMapToFile(filePath, _simulatorCanvas.Nodes);
if (success) if (success)
{ {
Program.WriteLine($"[맵 저장] 파일 저장 완료: {filePath} ({_mapNodes.Count}개 노드)"); Program.WriteLine($"[맵 저장] 파일 저장 완료: {filePath} ({_simulatorCanvas.Nodes.Count}개 노드)");
} }
else else
{ {
@@ -2035,7 +2027,7 @@ namespace AGVSimulator.Forms
private void btMapSaveAs_Click(object sender, EventArgs e) private void btMapSaveAs_Click(object sender, EventArgs e)
{ {
// 맵 데이터 확인 // 맵 데이터 확인
if (_mapNodes == null || _mapNodes.Count == 0) if (_simulatorCanvas.Nodes == null || _simulatorCanvas.Nodes.Count == 0)
{ {
MessageBox.Show("저장할 맵 데이터가 없습니다.", "알림", MessageBox.Show("저장할 맵 데이터가 없습니다.", "알림",
MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBoxButtons.OK, MessageBoxIcon.Information);
@@ -2044,9 +2036,9 @@ namespace AGVSimulator.Forms
using (var saveDialog = new SaveFileDialog()) using (var saveDialog = new SaveFileDialog())
{ {
saveDialog.Filter = "AGV Map Files (*.agvmap)|*.agvmap|모든 파일 (*.*)|*.*"; saveDialog.Filter = "AGV Map Files (*.json)|*.json|모든 파일 (*.*)|*.*";
saveDialog.Title = "맵 파일 저장"; saveDialog.Title = "맵 파일 저장";
saveDialog.DefaultExt = "agvmap"; saveDialog.DefaultExt = "json";
// 현재 파일이 있으면 기본 파일명으로 설정 // 현재 파일이 있으면 기본 파일명으로 설정
if (!string.IsNullOrEmpty(_currentMapFilePath)) if (!string.IsNullOrEmpty(_currentMapFilePath))
@@ -2057,7 +2049,7 @@ namespace AGVSimulator.Forms
else else
{ {
// 기본 파일명: 날짜_시간 형식 // 기본 파일명: 날짜_시간 형식
saveDialog.FileName = $"ScanMap_{DateTime.Now:yyyyMMdd_HHmmss}.agvmap"; saveDialog.FileName = $"ScanMap_{DateTime.Now:yyyyMMdd_HHmmss}.json";
} }
if (saveDialog.ShowDialog() == DialogResult.OK) if (saveDialog.ShowDialog() == DialogResult.OK)
@@ -2421,19 +2413,18 @@ namespace AGVSimulator.Forms
catch { } catch { }
} }
public void SendTag(string tagno) public void SendTag(ushort tagno)
{ {
if (_emulatorPort == null || !_emulatorPort.IsOpen) return; if (_emulatorPort == null || !_emulatorPort.IsOpen) return;
tagno = tagno.PadLeft(6, '0'); var tagnostr = tagno.ToString("000000");
if (tagno.Length > 6) tagno = tagno.Substring(0, 6);
var barr = new List<byte>(); var barr = new List<byte>();
barr.Add(0x02); barr.Add(0x02);
barr.Add((byte)'T'); barr.Add((byte)'T');
barr.Add((byte)'A'); barr.Add((byte)'A');
barr.Add((byte)'G'); barr.Add((byte)'G');
barr.AddRange(System.Text.Encoding.Default.GetBytes(tagno)); barr.AddRange(System.Text.Encoding.Default.GetBytes(tagnostr));
barr.Add((byte)'*'); barr.Add((byte)'*');
barr.Add((byte)'*'); barr.Add((byte)'*');
barr.Add(0x03); barr.Add(0x03);

View File

@@ -1,7 +1,7 @@
{ {
"Nodes": [ "Nodes": [
{ {
"NodeId": "N001", "Id": "N001",
"Name": "UNLOADER", "Name": "UNLOADER",
"Position": "65, 229", "Position": "65, 229",
"Type": 2, "Type": 2,
@@ -40,7 +40,7 @@
"DisplayText": "N001 - UNLOADER - [0001]" "DisplayText": "N001 - UNLOADER - [0001]"
}, },
{ {
"NodeId": "N002", "Id": "N002",
"Name": "N002", "Name": "N002",
"Position": "190, 230", "Position": "190, 230",
"Type": 0, "Type": 0,
@@ -80,7 +80,7 @@
"DisplayText": "N002 - N002 - [0002]" "DisplayText": "N002 - N002 - [0002]"
}, },
{ {
"NodeId": "N003", "Id": "N003",
"Name": "N003", "Name": "N003",
"Position": "296, 266", "Position": "296, 266",
"Type": 0, "Type": 0,
@@ -120,7 +120,7 @@
"DisplayText": "N003 - N003 - [0003]" "DisplayText": "N003 - N003 - [0003]"
}, },
{ {
"NodeId": "N004", "Id": "N004",
"Name": "N004", "Name": "N004",
"Position": "388, 330", "Position": "388, 330",
"Type": 0, "Type": 0,
@@ -162,7 +162,7 @@
"DisplayText": "N004 - N004 - [0004]" "DisplayText": "N004 - N004 - [0004]"
}, },
{ {
"NodeId": "N006", "Id": "N006",
"Name": "N006", "Name": "N006",
"Position": "530, 220", "Position": "530, 220",
"Type": 0, "Type": 0,
@@ -202,7 +202,7 @@
"DisplayText": "N006 - N006 - [0013]" "DisplayText": "N006 - N006 - [0013]"
}, },
{ {
"NodeId": "N007", "Id": "N007",
"Name": "N007", "Name": "N007",
"Position": "589, 184", "Position": "589, 184",
"Type": 0, "Type": 0,
@@ -242,7 +242,7 @@
"DisplayText": "N007 - N007 - [0014]" "DisplayText": "N007 - N007 - [0014]"
}, },
{ {
"NodeId": "N008", "Id": "N008",
"Name": "N008", "Name": "N008",
"Position": "282, 452", "Position": "282, 452",
"Type": 0, "Type": 0,
@@ -282,7 +282,7 @@
"DisplayText": "N008 - N008 - [0009]" "DisplayText": "N008 - N008 - [0009]"
}, },
{ {
"NodeId": "N009", "Id": "N009",
"Name": "N009", "Name": "N009",
"Position": "183, 465", "Position": "183, 465",
"Type": 0, "Type": 0,
@@ -322,7 +322,7 @@
"DisplayText": "N009 - N009 - [0010]" "DisplayText": "N009 - N009 - [0010]"
}, },
{ {
"NodeId": "N010", "Id": "N010",
"Name": "TOPS", "Name": "TOPS",
"Position": "52, 466", "Position": "52, 466",
"Type": 3, "Type": 3,
@@ -361,7 +361,7 @@
"DisplayText": "N010 - TOPS - [0011]" "DisplayText": "N010 - TOPS - [0011]"
}, },
{ {
"NodeId": "N011", "Id": "N011",
"Name": "N011", "Name": "N011",
"Position": "481, 399", "Position": "481, 399",
"Type": 0, "Type": 0,
@@ -402,7 +402,7 @@
"DisplayText": "N011 - N011 - [0005]" "DisplayText": "N011 - N011 - [0005]"
}, },
{ {
"NodeId": "N012", "Id": "N012",
"Name": "N012", "Name": "N012",
"Position": "559, 464", "Position": "559, 464",
"Type": 0, "Type": 0,
@@ -442,7 +442,7 @@
"DisplayText": "N012 - N012 - [0006]" "DisplayText": "N012 - N012 - [0006]"
}, },
{ {
"NodeId": "N013", "Id": "N013",
"Name": "N013", "Name": "N013",
"Position": "640, 513", "Position": "640, 513",
"Type": 0, "Type": 0,
@@ -482,7 +482,7 @@
"DisplayText": "N013 - N013 - [0007]" "DisplayText": "N013 - N013 - [0007]"
}, },
{ {
"NodeId": "N014", "Id": "N014",
"Name": "LOADER", "Name": "LOADER",
"Position": "728, 573", "Position": "728, 573",
"Type": 1, "Type": 1,
@@ -521,7 +521,7 @@
"DisplayText": "N014 - LOADER - [0008]" "DisplayText": "N014 - LOADER - [0008]"
}, },
{ {
"NodeId": "N019", "Id": "N019",
"Name": "CHARGER #2", "Name": "CHARGER #2",
"Position": "679, 199", "Position": "679, 199",
"Type": 5, "Type": 5,
@@ -560,7 +560,7 @@
"DisplayText": "N019 - CHARGER #2 - [0015]" "DisplayText": "N019 - CHARGER #2 - [0015]"
}, },
{ {
"NodeId": "N022", "Id": "N022",
"Name": "N022", "Name": "N022",
"Position": "461, 267", "Position": "461, 267",
"Type": 0, "Type": 0,
@@ -601,7 +601,7 @@
"DisplayText": "N022 - N022 - [0012]" "DisplayText": "N022 - N022 - [0012]"
}, },
{ {
"NodeId": "N023", "Id": "N023",
"Name": "N023", "Name": "N023",
"Position": "418, 206", "Position": "418, 206",
"Type": 0, "Type": 0,
@@ -641,7 +641,7 @@
"DisplayText": "N023 - N023 - [0016]" "DisplayText": "N023 - N023 - [0016]"
}, },
{ {
"NodeId": "N024", "Id": "N024",
"Name": "N024", "Name": "N024",
"Position": "476, 141", "Position": "476, 141",
"Type": 0, "Type": 0,
@@ -681,7 +681,7 @@
"DisplayText": "N024 - N024 - [0017]" "DisplayText": "N024 - N024 - [0017]"
}, },
{ {
"NodeId": "N025", "Id": "N025",
"Name": "N025", "Name": "N025",
"Position": "548, 99", "Position": "548, 99",
"Type": 0, "Type": 0,
@@ -721,7 +721,7 @@
"DisplayText": "N025 - N025 - [0018]" "DisplayText": "N025 - N025 - [0018]"
}, },
{ {
"NodeId": "N026", "Id": "N026",
"Name": "CHARGER #1", "Name": "CHARGER #1",
"Position": "670, 88", "Position": "670, 88",
"Type": 5, "Type": 5,
@@ -760,7 +760,7 @@
"DisplayText": "N026 - CHARGER #1 - [0019]" "DisplayText": "N026 - CHARGER #1 - [0019]"
}, },
{ {
"NodeId": "LBL001", "Id": "LBL001",
"Name": "Amkor Technology Korea", "Name": "Amkor Technology Korea",
"Position": "183, 103", "Position": "183, 103",
"Type": 6, "Type": 6,
@@ -797,7 +797,7 @@
"DisplayText": "LBL001 - Amkor Technology Korea" "DisplayText": "LBL001 - Amkor Technology Korea"
}, },
{ {
"NodeId": "IMG001", "Id": "IMG001",
"Name": "logo", "Name": "logo",
"Position": "633, 310", "Position": "633, 310",
"Type": 7, "Type": 7,
@@ -834,7 +834,7 @@
"DisplayText": "IMG001 - logo" "DisplayText": "IMG001 - logo"
}, },
{ {
"NodeId": "N015", "Id": "N015",
"Name": "", "Name": "",
"Position": "448, 476", "Position": "448, 476",
"Type": 0, "Type": 0,
@@ -874,7 +874,7 @@
"DisplayText": "N015 - [0037]" "DisplayText": "N015 - [0037]"
}, },
{ {
"NodeId": "N016", "Id": "N016",
"Name": "", "Name": "",
"Position": "425, 524", "Position": "425, 524",
"Type": 0, "Type": 0,
@@ -914,7 +914,7 @@
"DisplayText": "N016 - [0036]" "DisplayText": "N016 - [0036]"
}, },
{ {
"NodeId": "N017", "Id": "N017",
"Name": "", "Name": "",
"Position": "389, 559", "Position": "389, 559",
"Type": 0, "Type": 0,
@@ -954,7 +954,7 @@
"DisplayText": "N017 - [0035]" "DisplayText": "N017 - [0035]"
}, },
{ {
"NodeId": "N018", "Id": "N018",
"Name": "", "Name": "",
"Position": "315, 562", "Position": "315, 562",
"Type": 0, "Type": 0,
@@ -995,7 +995,7 @@
"DisplayText": "N018 - [0034]" "DisplayText": "N018 - [0034]"
}, },
{ {
"NodeId": "N005", "Id": "N005",
"Name": "", "Name": "",
"Position": "227, 560", "Position": "227, 560",
"Type": 0, "Type": 0,
@@ -1036,7 +1036,7 @@
"DisplayText": "N005 - [0033]" "DisplayText": "N005 - [0033]"
}, },
{ {
"NodeId": "N020", "Id": "N020",
"Name": "", "Name": "",
"Position": "142, 557", "Position": "142, 557",
"Type": 0, "Type": 0,
@@ -1077,7 +1077,7 @@
"DisplayText": "N020 - [0032]" "DisplayText": "N020 - [0032]"
}, },
{ {
"NodeId": "N021", "Id": "N021",
"Name": "", "Name": "",
"Position": "60, 559", "Position": "60, 559",
"Type": 0, "Type": 0,
@@ -1117,7 +1117,7 @@
"DisplayText": "N021 - [0031]" "DisplayText": "N021 - [0031]"
}, },
{ {
"NodeId": "N027", "Id": "N027",
"Name": "BUF1", "Name": "BUF1",
"Position": "61, 645", "Position": "61, 645",
"Type": 4, "Type": 4,
@@ -1156,7 +1156,7 @@
"DisplayText": "N027 - BUF1 - [0041]" "DisplayText": "N027 - BUF1 - [0041]"
}, },
{ {
"NodeId": "N028", "Id": "N028",
"Name": "BUF2", "Name": "BUF2",
"Position": "141, 643", "Position": "141, 643",
"Type": 4, "Type": 4,
@@ -1195,7 +1195,7 @@
"DisplayText": "N028 - BUF2 - [0040]" "DisplayText": "N028 - BUF2 - [0040]"
}, },
{ {
"NodeId": "N029", "Id": "N029",
"Name": "BUF3", "Name": "BUF3",
"Position": "229, 638", "Position": "229, 638",
"Type": 4, "Type": 4,
@@ -1234,7 +1234,7 @@
"DisplayText": "N029 - BUF3 - [0039]" "DisplayText": "N029 - BUF3 - [0039]"
}, },
{ {
"NodeId": "N030", "Id": "N030",
"Name": "BUF4", "Name": "BUF4",
"Position": "316, 638", "Position": "316, 638",
"Type": 4, "Type": 4,
@@ -1273,7 +1273,7 @@
"DisplayText": "N030 - BUF4 - [0038]" "DisplayText": "N030 - BUF4 - [0038]"
}, },
{ {
"NodeId": "N031", "Id": "N031",
"Name": "", "Name": "",
"Position": "337, 397", "Position": "337, 397",
"Type": 0, "Type": 0,

File diff suppressed because one or more lines are too long

1135
Cs_HMI/Data/NewMap_2.json Normal file

File diff suppressed because it is too large Load Diff

1005
Cs_HMI/Data/NewMap_3.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -186,6 +186,7 @@
<Compile Include="Device\BMS.cs" /> <Compile Include="Device\BMS.cs" />
<Compile Include="Device\BMSInformationEventArgs.cs" /> <Compile Include="Device\BMSInformationEventArgs.cs" />
<Compile Include="Device\CFlag.cs" /> <Compile Include="Device\CFlag.cs" />
<Compile Include="Device\BMSSerialComm.cs" />
<Compile Include="Device\xbee.cs"> <Compile Include="Device\xbee.cs">
<SubType>Component</SubType> <SubType>Component</SubType>
</Compile> </Compile>
@@ -196,6 +197,12 @@
<Compile Include="Dialog\fCounter.Designer.cs"> <Compile Include="Dialog\fCounter.Designer.cs">
<DependentUpon>fCounter.cs</DependentUpon> <DependentUpon>fCounter.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Dialog\fXbeeSetting.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Dialog\fXbeeSetting.Designer.cs">
<DependentUpon>fXbeeSetting.cs</DependentUpon>
</Compile>
<Compile Include="Dialog\fStateMachineDebug.cs"> <Compile Include="Dialog\fStateMachineDebug.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@@ -226,9 +233,6 @@
<Compile Include="Dialog\fSystem.Designer.cs"> <Compile Include="Dialog\fSystem.Designer.cs">
<DependentUpon>fSystem.cs</DependentUpon> <DependentUpon>fSystem.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Dialog\DriveDetector.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Dialog\fVolume.cs"> <Compile Include="Dialog\fVolume.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@@ -363,9 +367,6 @@
<Compile Include="StateMachine\_SPS.cs"> <Compile Include="StateMachine\_SPS.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
<Compile Include="Device\_DeviceManagement.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="StateMachine\_Loop.cs"> <Compile Include="StateMachine\_Loop.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@@ -423,6 +424,9 @@
<EmbeddedResource Include="Dialog\fCounter.resx"> <EmbeddedResource Include="Dialog\fCounter.resx">
<DependentUpon>fCounter.cs</DependentUpon> <DependentUpon>fCounter.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Dialog\fXbeeSetting.resx">
<DependentUpon>fXbeeSetting.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Dialog\fUpdateForm.resx"> <EmbeddedResource Include="Dialog\fUpdateForm.resx">
<DependentUpon>fUpdateForm.cs</DependentUpon> <DependentUpon>fUpdateForm.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>

View File

@@ -222,8 +222,6 @@ namespace Project
#region "Charge" #region "Charge"
[Browsable(false)] [Browsable(false)]
public int chargerpos { get; set; }
[Browsable(false)]
public int ChargetWaitSec { get; set; } public int ChargetWaitSec { get; set; }
[Browsable(false)] [Browsable(false)]
public int ChargeEmergencyLevel { get; set; } public int ChargeEmergencyLevel { get; set; }
@@ -240,6 +238,9 @@ namespace Project
[Browsable(false)] [Browsable(false)]
public int ChargeSearchTime { get; set; } public int ChargeSearchTime { get; set; }
[Category("Charge"), DisplayName("Low Battery Limit"), Description("배터리 부족 경고 알림 기준 (%)")]
public int BatteryLimit_Low { get; set; }
#endregion #endregion
#region "AGV" #region "AGV"
@@ -427,10 +428,10 @@ namespace Project
if (ChargeEmergencyLevel == 0) ChargeEmergencyLevel = 30; if (ChargeEmergencyLevel == 0) ChargeEmergencyLevel = 30;
if (interval_bms == 0) interval_bms = 10; if (interval_bms == 0) interval_bms = 10;
//충전은 10분간격으로 재시도 한다
if (ChargeRetryTerm == 0) ChargeRetryTerm = 600; if (ChargeRetryTerm == 0) ChargeRetryTerm = 600;
if (alarmSoundTerm == 0) alarmSoundTerm = 15; //기본 15초 if (alarmSoundTerm == 0) alarmSoundTerm = 15; //기본 15초
if (ChargeSearchTime == 0) ChargeSearchTime = 25; if (ChargeSearchTime == 0) ChargeSearchTime = 25;
if (BatteryLimit_Low == 0) BatteryLimit_Low = 20;
//최대 충전진행 시간(기본 1시간) //최대 충전진행 시간(기본 1시간)
if (ChargeMaxTime == 0) ChargeMaxTime = 3600; if (ChargeMaxTime == 0) ChargeMaxTime = 3600;
// if (interval_iostate == 0 || interval_iostate == 255) interval_iostate = 100; // if (interval_iostate == 0 || interval_iostate == 255) interval_iostate = 100;

View File

@@ -22,11 +22,31 @@ namespace Project
public Color SMSG_ShadowColor = Color.Transparent; public Color SMSG_ShadowColor = Color.Transparent;
public float SMSG_ProgressValue = 0; public float SMSG_ProgressValue = 0;
public string SMSG_Tag = string.Empty; public string SMSG_Tag = string.Empty;
//public event EventHandler SMSG_Update; /// <summary>
//public void UpdateStatusMessage() /// 이동대상위치(상차,하차,충전)
//{ /// </summary>
// SMSG_Update?.Invoke(null, null); private ePosition _targetPos = ePosition.NONE;
//} public string result_message = "";
public double result_progressmax = 0;
public double result_progressvalue = 0;
public DateTime StopMessageTimePLC = DateTime.Parse("1982-11-23");
public DateTime StopMessageTimeSWR = DateTime.Parse("1982-11-23");
public string StopMessagePLC = string.Empty;
public string StopMessageSWR = string.Empty;
private ePosition _currentpos = ePosition.NONE;
private string _currentposcw = string.Empty;
public ushort LastTAG { get; set; } = 0;
public ePosition NextPos = ePosition.NONE;
private char _comandKit { get; set; }
public string Memo;
public eResult ResultCode { get; set; }
public eECode ResultErrorCode;
public string ResultMessage { get; set; }
public Boolean isError { get; set; }
public int retry = 0;
public DateTime retryTime;
public Device.Socket.Message RecvMessage;
/// <summary> /// <summary>
/// 작업시작시간 /// 작업시작시간
/// </summary> /// </summary>
@@ -52,9 +72,6 @@ namespace Project
} }
} }
//public DateTime ChargeStartTime = DateTime.Parse("1982-11-23");
#region "AGV Status Value" #region "AGV Status Value"
public string PLC1_RawData { get; set; } public string PLC1_RawData { get; set; }
public string PLC2_RawData { get; set; } public string PLC2_RawData { get; set; }
@@ -62,25 +79,7 @@ namespace Project
#endregion #endregion
/// <summary>
/// 이동대상위치(상차,하차,충전)
/// </summary>
private ePosition _targetPos = ePosition.NONE;
public string result_message = "";
public double result_progressmax = 0;
public double result_progressvalue = 0;
public DateTime StopMessageTimePLC = DateTime.Parse("1982-11-23");
public DateTime StopMessageTimeSWR = DateTime.Parse("1982-11-23");
public string StopMessagePLC = string.Empty;
public string StopMessageSWR = string.Empty;
//public DateTime LastChar
//geTime = DateTime.Parse("1982-11-23");
public ePosition NextPos = ePosition.NONE;
public ePosition TargetPos public ePosition TargetPos
{ {
get get
@@ -94,7 +93,7 @@ namespace Project
} }
} }
private char _comandKit { get; set; }
public char CommandKit public char CommandKit
{ {
get get
@@ -110,27 +109,6 @@ namespace Project
} }
//private ePosition _currentPos = ePosition.NONE;
//public ePosition CurrentPos
//{
// get
// {
// return _currentPos;
// }
// set
// {
// if (_currentPos != value) //값이바뀔떄만 메세지 220628
// PUB.log.Add(string.Format("현재위치 설정:{0}->{1}", _currentPos, value));
// _currentPos = value;
// }
//}
private ePosition _currentpos = ePosition.NONE;
private string _currentposcw = string.Empty;
// public ePosition LastPos = ePosition.NONE;
public string LastTAG { get; set; } = string.Empty;
public ePosition CurrentPos public ePosition CurrentPos
{ {
get get
@@ -161,11 +139,7 @@ namespace Project
_currentposcw = value; _currentposcw = value;
} }
} }
public string Memo;
public eResult ResultCode { get; set; }
public eECode ResultErrorCode;
public string ResultMessage { get; set; }
#region "SetResultMessage" #region "SetResultMessage"
@@ -204,13 +178,6 @@ namespace Project
#endregion #endregion
public Boolean isError { get; set; }
public int retry = 0;
public DateTime retryTime;
public Device.Socket.Message RecvMessage;
public CResult() public CResult()
{ {
this.Clear(); this.Clear();

View File

@@ -9,12 +9,12 @@ using System.CodeDom;
namespace arDev namespace arDev
{ {
public class BMS : arRS232 public class BMS : BMSSerialComm
{ {
public BMS() public BMS()
{ {
MinRecvLength = 34;
} }
/// <summary> /// <summary>
@@ -55,28 +55,60 @@ namespace arDev
{ {
tempBuffer.Add(incomByte); tempBuffer.Add(incomByte);
var queylen = QueryIndex == 0 ? 34 : 23; if (tempBuffer.Count > 7)
if (tempBuffer.Count == queylen)
{ {
if (incomByte != 0x77) byte len = tempBuffer[3];
if (tempBuffer.Count >= 4 + len + 3) // Start+Reg+Status+Len + Data + Chk(2) + End
{
if (tempBuffer.Last() == 0x77)
{
//데이터가 맞게 수신됨
LastReceiveBuffer = tempBuffer.ToArray();
bComplete = true;
}
else
{ {
//종단기호가 맞지 않다. 이자료는 폐기한다. //종단기호가 맞지 않다. 이자료는 폐기한다.
var hexstr = string.Join(" ", tempBuffer.Select(t => t.ToString("X2"))); var hexstr = string.Join(" ", tempBuffer.Select(t => t.ToString("X2")));
RaiseMessage(MessageType.Error, $"discard : {hexstr}"); RaiseMessage(MessageType.Error, $"discard : {hexstr}");
tempBuffer.Clear(); tempBuffer.Clear();
} }
else
{
//데이터가 맞게 수신됨
LastReceiveBuffer = tempBuffer.ToArray();
bComplete = true;
}
findSTX = false; findSTX = false;
} }
else
{
//아직 모자르므로 대기한다
} }
// [22 - 12 - 27 14:32:49] open: True
//[22 - 12 - 27 14:32:49] Send: DD A5 03 00 FF FD 77 0D
//[22 - 12 - 27 14:32:50] Send: DD A5 03 00 FF FD 77 0D
//[22 - 12 - 27 14:32:50] Recv: 26.61v,81.4 %
//[22 - 12 - 27 14:32:50] Recv: DD 03 00 1B 0A 65 00 00 21 63 29 04 00 00 2C 92 00 00 00 00 00 00 28 51 03 08 02 0B 69 0B 66 FC 9C 77
//[22 - 12 - 27 14:32:50] Send: DD A5 03 00 FF FD 77 0D
//[22 - 12 - 27 14:32:51] Recv: 26.61v,81.4 %
//[22 - 12 - 27 14:32:51] Recv: DD 03 00 1B 0A 65 00 00 21 63 29 04 00 00 2C 92 00 00 00 00 00 00 28 51 03 08 02 0B 69 0B 66 FC 9C 77
//var queylen = QueryIndex == 0 ? 34 : 23;
//if (tempBuffer.Count == queylen)
//{
// if (incomByte != 0x77)
// {
// //종단기호가 맞지 않다. 이자료는 폐기한다.
// var hexstr = string.Join(" ", tempBuffer.Select(t => t.ToString("X2")));
// RaiseMessage(MessageType.Error, $"discard : {hexstr}");
// tempBuffer.Clear();
// }
// else
// {
// //데이터가 맞게 수신됨
// LastReceiveBuffer = tempBuffer.ToArray();
// bComplete = true;
// }
// findSTX = false;
//}
//else
//{
// //아직 모자르므로 대기한다
//}
} }
} }
} }
@@ -106,7 +138,7 @@ namespace arDev
else else
{ {
var rxstr = string.Join(" ", data.Select(t => t.ToString("X2"))); var rxstr = string.Join(" ", data.Select(t => t.ToString("X2")));
RaiseMessage(MessageType.Recv, $"Querh:{QueryIndex},Data:{rxstr}" ); RaiseMessage(MessageType.Recv, rxstr);
} }
if (QueryIndex == 0) if (QueryIndex == 0)
@@ -179,6 +211,13 @@ namespace arDev
batH = (UInt16)(batH | batL); batH = (UInt16)(batH | batL);
Current_Volt = (float)(batH / 100.0); Current_Volt = (float)(batH / 100.0);
//충방전전류
Int16 batHi = (Int16)LastReceiveBuffer[6];
Int16 batLi = (Int16)LastReceiveBuffer[7];
batHi = (Int16)(batHi << 8);
batHi = (Int16)(batHi | batLi);
Charge_Amp = (float)(batHi / 100.0);
//잔량확인 //잔량확인
batH = (UInt16)LastReceiveBuffer[8]; batH = (UInt16)LastReceiveBuffer[8];
batL = (UInt16)LastReceiveBuffer[9]; batL = (UInt16)LastReceiveBuffer[9];
@@ -224,107 +263,80 @@ namespace arDev
return false; return false;
} }
} }
/// <summary>
private bool _autocharge = false; /// 현재 충전중인지?
public Boolean AutoCharge /// </summary>
{ public bool IsCharging { get; private set; }
get { return _autocharge; } DateTime ChargeStart = DateTime.Now;
set { _autocharge = false; } DateTime ChargeEnd = DateTime.Now;
}
//public void ClearManualChargeCheckValue()
//{
// chk_timee = new DateTime(1982, 11, 23);
// chk_times = new DateTime(1982, 11, 23);
// chk_valuee = 0f;
// chk_values = 0f;
//}
void CheckManualCharge() void CheckManualCharge()
{ {
if (AutoCharge) //충방전전력이 1보다 크면 충전으로 한다.
if (Charge_Amp > 0.1)
{ {
if (chk_timee.Year != 1982) //기존에 충전상태가 OFF였다면 충전중으로 알려준다
if (IsCharging == false)
{ {
chk_timee = new DateTime(1982, 11, 23); IsCharging = true;
chk_valuee = 999f; ChargeStart = DateTime.Now;
} ChargeEnd = new DateTime(1982, 11, 23);
if (chk_times.Year != 1982)
{
chk_times = new DateTime(1982, 11, 23);
chk_values = 999f;
}
}
if (chk_times.Year == 1982)
{
chk_times = DateTime.Now;
chk_values = Current_Level;
}
else
{
if (chk_timee.Year == 1982)
{
if ((Current_Level - chk_values) >= 0.1)
{
//충전중이다
chk_timee = DateTime.Now;
chk_valuee = Current_Level;
try try
{ {
ChargeDetect?.Invoke(this, new ChargetDetectArgs(chk_times, chk_values, chk_timee, chk_valuee)); ChargeDetect?.Invoke(this, new ChargetDetectArgs(ChargeStart, true, Current_Level));
}
catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); }
}
else if ((Current_Level - chk_values) <= -0.1)
{
//방전중이다
if (chk_times.Year != 1982) chk_times = new DateTime(1982, 11, 23);
if (chk_timee.Year != 1982) chk_timee = new DateTime(1982, 11, 23);
}
else
{
//아직 변화가 없으니 종료일을 기록하지 않는다
}
}
else
{
//이미 종료일이 셋팅된 상태이다
if ((Current_Level - chk_valuee) >= 0.1)
{
//종료시간을 시작값에 넣는다
chk_times = chk_timee;
chk_values = chk_valuee;
chk_timee = DateTime.Now;
chk_valuee = Current_Level;
try
{
ChargeDetect?.Invoke(this, new ChargetDetectArgs(chk_times, chk_values, chk_timee, chk_valuee));
} }
catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); } catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); }
} }
else if ((Current_Level - chk_valuee) <= -0.1) else
{ {
//방전중이다 //충전상태가 유지되고 있다.
if (chk_times.Year != 1982) chk_times = new DateTime(1982, 11, 23); }
if (chk_timee.Year != 1982) chk_timee = new DateTime(1982, 11, 23);
} }
else else
{ {
//아직 변화가 없으니 종료일을 기록하지 않는다 //충전이해제되었다.. 단 바로 해제하지않고 1초정도 텀을 주고 OFF한다.
if (IsCharging)
{
if (ChargeEnd.Year == 1982)
{
ChargeEnd = DateTime.Now;
}
else
{
var ts = DateTime.Now - ChargeEnd;
if (ts.TotalSeconds > 2) //충전종료시그널후 2초후에 충전off를 알린다.
{
ChargeEnd = DateTime.Now;
IsCharging = false;
try
{
ChargeDetect?.Invoke(this, new ChargetDetectArgs(ChargeEnd, false, Current_Level));
}
catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); }
}
}
}
else
{
//방전상태가 유지되고 있다.
} }
} }
}
}
}
public DateTime chk_times { get; set; } = new DateTime(1982, 11, 23); public DateTime chk_times { get; set; } = new DateTime(1982, 11, 23);
public DateTime chk_timee { get; set; } = new DateTime(1982, 11, 23); public DateTime chk_timee { get; set; } = new DateTime(1982, 11, 23);
public float chk_values { get; set; } = 0f; public float chk_values { get; set; } = 0f;
public float chk_valuee { get; set; } = 0f; public float chk_valuee { get; set; } = 0f;
public float Charge_Amp { get; set; } = 0f;
public Int16 Charge_watt
{
get
{
return (Int16)((Charge_Amp) * Current_Volt);
}
}
/// <summary> /// <summary>
/// 전압 /// 전압
/// </summary> /// </summary>
@@ -407,7 +419,8 @@ namespace arDev
cmd.Add(0xFF); cmd.Add(0xFF);
cmd.Add(0xFD); cmd.Add(0xFD);
cmd.Add(0x77); cmd.Add(0x77);
cmd.Add(0x0D); //cmd.Add(0x0D);
//_device.DiscardInBuffer();
return WriteData(cmd.ToArray()); return WriteData(cmd.ToArray());
} }
@@ -423,7 +436,8 @@ namespace arDev
cmd.Add(0xFF); cmd.Add(0xFF);
cmd.Add(0xFC); cmd.Add(0xFC);
cmd.Add(0x77); cmd.Add(0x77);
cmd.Add(0x0D); //cmd.Add(0x0D);
//_device.DiscardInBuffer();
return WriteData(cmd.ToArray()); return WriteData(cmd.ToArray());
} }

View File

@@ -4,16 +4,14 @@ namespace arDev
{ {
public class ChargetDetectArgs : EventArgs public class ChargetDetectArgs : EventArgs
{ {
public DateTime times { get; set; } public DateTime time { get; set; }
public DateTime timee { get; set; } public float level { get; set; }
public float values { get; set; } public bool Detected { get; set; }
public float valuee { get; set; } public ChargetDetectArgs(DateTime times, bool detected, float values)
public ChargetDetectArgs(DateTime times, float values, DateTime timee, float valuee)
{ {
this.times = times; this.time = times;
this.times = timee; this.level = values;
this.values = values; this.Detected = detected;
this.valuee = valuee;
} }
} }
public class BMSInformationEventArgs : EventArgs public class BMSInformationEventArgs : EventArgs

View File

@@ -0,0 +1,607 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
namespace arDev
{
public abstract class BMSSerialComm : ISerialComm, IDisposable
{
protected System.IO.Ports.SerialPort _device;
protected ManualResetEvent _mre;
protected byte[] LastReceiveBuffer = new byte[] { };
/// <summary>
/// 최종 전송 메세지
/// </summary>
public byte[] lastSendBuffer = new byte[] { };
//public int ValidCheckTimeMSec { get; set; } = 5000;
protected List<byte> tempBuffer = new List<byte>();
protected Boolean findSTX = false;
public string ErrorMessage { get; set; }
public DateTime LastConnTime { get; set; }
public DateTime LastConnTryTime { get; set; }
public DateTime lastSendTime;
/// <summary>
/// 메세지 수신시 사용하는 내부버퍼
/// </summary>
protected List<byte> _buffer = new List<byte>();
/// <summary>
/// 데이터조회간격(초)
/// </summary>
public float ScanInterval { get; set; }
// public byte[] LastRecvData;
public string LastRecvString
{
get
{
if (LastReceiveBuffer == null) return String.Empty;
else return System.Text.Encoding.Default.GetString(LastReceiveBuffer);
}
}
/// <summary>
/// 마지막으로 데이터를 받은 시간
/// </summary>
public DateTime lastRecvTime;
public int WriteError = 0;
public string WriteErrorMessage = string.Empty;
public int WaitTimeout { get; set; } = 1000;
public int MinRecvLength { get; set; } = 1;
// Polling Thread related
protected Thread _recvThread;
protected volatile bool _isReading = false;
/// <summary>
/// 포트이름
/// </summary>
[Description("시리얼 포트 이름")]
[Category("설정"), DisplayName("Port Name")]
public string PortName
{
get
{
if (_device == null) return string.Empty;
else return _device.PortName;
}
set
{
if (this.IsOpen)
{
Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 포트이름을 변경할 수 없습니다", true));
}
else if (String.IsNullOrEmpty(value) == false)
_device.PortName = value;
else
{
Message?.Invoke(this, new MessageEventArgs("No PortName", true));
}
}
}
public int BaudRate
{
get
{
if (_device == null) return 0;
else return _device.BaudRate;
}
set
{
if (this.IsOpen)
{
Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 BaudRate(를) 변경할 수 없습니다", true));
}
else if (value != 0)
_device.BaudRate = value;
else Message?.Invoke(this, new MessageEventArgs("No baud rate", true));
}
}
public BMSSerialComm()
{
_device = new System.IO.Ports.SerialPort();
this.BaudRate = 9600;
ScanInterval = 10;
// _device.DataReceived += barcode_DataReceived; // Removed event handler
_device.ErrorReceived += this.barcode_ErrorReceived;
_device.WriteTimeout = 3000;
_device.ReadTimeout = 3000;
_device.ReadBufferSize = 8192;
_device.WriteBufferSize = 8192;
//_device.DiscardInBuffer();
//_device.DiscardOutBuffer();
ErrorMessage = string.Empty;
lastRecvTime = DateTime.Parse("1982-11-23");
LastConnTime = DateTime.Parse("1982-11-23");
LastConnTryTime = DateTime.Parse("1982-11-23");
lastRecvTime = DateTime.Parse("1982-11-23");
this._mre = new ManualResetEvent(true);
}
~BMSSerialComm()
{
Dispose(false);
}
// Flag: Has Dispose already been called?
bool disposed = false;
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
// Free any other managed objects here.
//
}
// Stop reading thread
_isReading = false;
// _device.DataReceived -= barcode_DataReceived; // Removed event handler
_device.ErrorReceived -= this.barcode_ErrorReceived;
if (_recvThread != null && _recvThread.IsAlive)
{
_recvThread.Join(500);
}
if (_device != null)
{
if (_device.IsOpen) _device.Close();
_device.Dispose();
}
// Free any unmanaged objects here.
//
disposed = true;
}
public Boolean Open()
{
try
{
if (_device.IsOpen == false)
{
_device.Open();
}
if (_device.IsOpen)
{
// Start polling thread
if (_isReading == false)
{
_isReading = true;
_recvThread = new Thread(ReadPort);
_recvThread.IsBackground = true;
_recvThread.Start();
}
return true;
}
return false;
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
Message.Invoke(this, new MessageEventArgs(ex.Message, true));
return false;
}
}
public string GetHexString(Byte[] input)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (byte b in input)
sb.Append(" " + b.ToString("X2"));
return sb.ToString();
}
/// <summary>
/// 포트가 열려있는지 확인
/// </summary>
[Description("현재 시리얼포트가 열려있는지 확인합니다")]
[Category("정보"), DisplayName("Port Open")]
public Boolean IsOpen
{
get
{
if (_device == null) return false;
return _device.IsOpen;
}
}
public virtual bool Close()
{
try
{
_isReading = false; // Stop thread loop
if (_recvThread != null && _recvThread.IsAlive)
{
if (!_recvThread.Join(500)) // Wait for thread to finish
{
// _recvThread.Abort(); // Avoid Abort if possible
}
}
if (_device != null && _device.IsOpen)
{
_device.DiscardInBuffer();
_device.DiscardOutBuffer();
_device.Close(); //dispose에서는 포트를 직접 클리어하지 않게 해뒀다.
return true;
}
else return false;
}
catch (Exception)
{
return false;
}
}
protected Boolean RaiseRecvData()
{
return RaiseRecvData(LastReceiveBuffer.ToArray(), false);
}
/// <summary>
/// 수신받은 메세지를 발생 시킵니다
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public virtual Boolean RaiseRecvData(byte[] Data, bool udpatelastbuffer)
{
//181206 - 최종수신 메세지 기록
lastRecvTime = DateTime.Now;
if (udpatelastbuffer && Data != null)
{
if (LastReceiveBuffer == null || LastReceiveBuffer.Length != Data.Length)
{
LastReceiveBuffer = new byte[Data.Length];
Array.Copy(Data, LastReceiveBuffer, Data.Length);
}
}
try
{
// UI update might need Invoke if this event handler updates UI directly,
// but usually the subscriber handles Invoke.
// Since we are running on a background thread now, subscribers must be aware.
Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage
if (ProcessRecvData(Data) == false)
{
//Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage
Message?.Invoke(this, new MessageEventArgs(this.ErrorMessage, true)); //errormessage
return false;
}
else
{
return true;
}
}
catch (Exception ex)
{
this.ErrorMessage = ex.Message;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
return false;
}
}
/// <summary>
/// 수신받은 자료를 처리한다
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public abstract bool ProcessRecvData(byte[] data);
#region "Internal Events"
void barcode_ErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
{
Message?.Invoke(this, new MessageEventArgs(e.ToString(), true));
}
byte[] buffer = new byte[] { };
// Replaced with ReadPort Loop
/*
void barcode_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
try
{
int ReadCount = _device.BytesToRead;
buffer = new byte[ReadCount];
_device.Read(buffer, 0, buffer.Length);
System.Text.StringBuilder LogMsg = new StringBuilder();
byte[] remainBuffer;
Repeat:
if (CustomParser(buffer, out remainBuffer))
{
//분석완료이므로 받은 데이터를 버퍼에 기록한다
if (LastReceiveBuffer == null || (LastReceiveBuffer.Length != tempBuffer.Count))
Array.Resize(ref LastReceiveBuffer, tempBuffer.Count);
Array.Copy(tempBuffer.ToArray(), LastReceiveBuffer, tempBuffer.Count);
tempBuffer.Clear();
//수신메세지발생
RaiseRecvData();
if (remainBuffer != null && remainBuffer.Length > 0)
{
//버퍼를 변경해서 다시 전송을 해준다.
Array.Resize(ref buffer, remainBuffer.Length);
Array.Copy(remainBuffer, buffer, remainBuffer.Length);
goto Repeat; //남은 버퍼가 있다면 진행을 해준다.
}
}
}
catch (Exception ex)
{
//if (IsOpen)
//{
// //_device.DiscardInBuffer();
// //_device.DiscardOutBuffer();
//}
ErrorMessage = ex.Message;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
}
}
*/
void ReadPort()
{
while (_isReading)
{
try
{
if (_device == null || !_device.IsOpen)
{
Thread.Sleep(100);
continue;
}
int readCount = _device.BytesToRead;
if (readCount > 0)
{
byte[] buffer = new byte[readCount];
_device.Read(buffer, 0, buffer.Length);
byte[] remainBuffer;
Repeat:
if (CustomParser(buffer, out remainBuffer))
{
//분석완료이므로 받은 데이터를 버퍼에 기록한다
if (LastReceiveBuffer == null || (LastReceiveBuffer.Length != tempBuffer.Count))
Array.Resize(ref LastReceiveBuffer, tempBuffer.Count);
Array.Copy(tempBuffer.ToArray(), LastReceiveBuffer, tempBuffer.Count);
tempBuffer.Clear();
//수신메세지발생
RaiseRecvData();
if (remainBuffer != null && remainBuffer.Length > 0)
{
//버퍼를 변경해서 다시 전송을 해준다.
buffer = new byte[remainBuffer.Length]; // Reallocate buffer for remaining data
Array.Copy(remainBuffer, buffer, remainBuffer.Length);
goto Repeat; //남은 버퍼가 있다면 진행을 해준다.
}
}
}
else
{
Thread.Sleep(20); // Data 없음, 대기
}
}
catch (Exception ex)
{
// Thread 상에서 Exception 발생 시 로그 남기고 계속 진행 여부 결정
// 여기서는 에러 메시지 발생시키고 Sleep
ErrorMessage = ex.Message;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
Thread.Sleep(1000);
}
}
}
#endregion
#region "External Events"
/// <summary>
/// 오류 및 기타 일반 메세지
/// </summary>
public event EventHandler<MessageEventArgs> Message;
#endregion
#region "Event Args"
/// <summary>
/// 데이터를 수신할떄 사용함(RAW 포함)
/// </summary>
public class ReceiveDataEventArgs : EventArgs
{
private byte[] _buffer = null;
/// <summary>
/// 바이트배열의 버퍼값
/// </summary>
public byte[] Value { get { return _buffer; } }
/// <summary>
/// 버퍼(바이트배열)의 데이터를 문자로 반환합니다.
/// </summary>
public string StrValue
{
get
{
//return string.Empty;
if (_buffer == null || _buffer.Length < 1) return string.Empty;
else return System.Text.Encoding.Default.GetString(_buffer);
}
}
public ReceiveDataEventArgs(byte[] buffer)
{
_buffer = buffer;
}
}
/// <summary>
/// 메세지를 강제 발생
/// </summary>
/// <param name="mt"></param>
/// <param name="message"></param>
protected virtual void RaiseMessage(MessageType mt, string message)
{
this.Message?.Invoke(this, new MessageEventArgs(mt, message));
}
public enum MessageType
{
Normal,
Error,
Send,
Recv,
}
public class MessageEventArgs : EventArgs
{
public MessageType MsgType { get; set; }
private string _message = string.Empty;
/// <summary>
/// Recv,Send,Normal,Error 모두 지원
/// </summary>
public string Message { get { return _message; } }
private byte[] _data = null;
/// <summary>
/// Recv,Send에서만 값이 존재 합니다
/// </summary>
public byte[] Data { get { return _data; } }
public MessageEventArgs(string Message, bool isError = false)
{
if (isError) MsgType = MessageType.Error;
else MsgType = MessageType.Normal;
_message = Message;
}
public MessageEventArgs(MessageType msgtype, string Message)
{
MsgType = msgtype;
_message = Message;
_data = System.Text.Encoding.Default.GetBytes(Message);
}
public MessageEventArgs(byte[] buffer, bool isRecv = true)
{
if (isRecv) MsgType = MessageType.Recv;
else MsgType = MessageType.Send;
_data = new byte[buffer.Length];
Array.Copy(buffer, _data, Data.Length);
_message = System.Text.Encoding.Default.GetString(_data);
}
}
#endregion
protected abstract bool CustomParser(byte[] buf, out byte[] remainBuffer);
/// <summary>
/// 포트가 열려있거나 데이터 수신시간이 없는경우 false를 반환합니다
/// </summary>
public Boolean IsValid
{
get
{
if (IsOpen == false) return false;
if (lastRecvTime.Year == 1982) return false;
var ts = DateTime.Now - lastRecvTime;
if (ts.TotalSeconds > (this.ScanInterval * 2.5)) return false;
return true;
}
}
protected bool WriteData(string cmd)
{
return WriteData(System.Text.Encoding.Default.GetBytes(cmd));
}
/// <summary>
/// 포트에 쓰기(barcode_DataReceived 이벤트로 메세지수신)
/// </summary>
protected Boolean WriteData(byte[] data)
{
Boolean bRet = false;
//171205 : 타임아웃시간추가
if (!_mre.WaitOne(WaitTimeout))
{
ErrorMessage = $"WriteData:MRE:WaitOne:TimeOut {WaitTimeout}ms";
this.Message?.Invoke(this, new MessageEventArgs(ErrorMessage, true));
return false;
}
_mre.Reset();
//Array.Resize(ref data, data.Length + 2);
try
{
lastSendTime = DateTime.Now;
if (lastSendBuffer == null) lastSendBuffer = new byte[data.Length]; //171113
else Array.Resize(ref lastSendBuffer, data.Length);
Array.Copy(data, lastSendBuffer, data.Length);
for (int i = 0; i < data.Length; i++)
_device.Write(data, i, 1);
//_device.Write(data, 0, data.Length);
//171113
this.Message?.Invoke(this, new MessageEventArgs(data, false));
bRet = true;
WriteError = 0;
WriteErrorMessage = string.Empty;
}
catch (Exception ex)
{
// this.isinit = false;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
bRet = false;
WriteError += 1; //연속쓰기오류횟수
WriteErrorMessage = ex.Message;
}
finally
{
_mre.Set();
}
return bRet;
}
}
}

View File

@@ -14,11 +14,11 @@ using System.Windows.Forms;
namespace Project.Device namespace Project.Device
{ {
public class Xbee : SerialPort public class Xbee : SerialPort, arDev.ISerialComm
{ {
public string buffer = string.Empty; public string buffer = string.Empty;
public System.Text.StringBuilder newbuffer = new StringBuilder(); public System.Text.StringBuilder newbuffer = new StringBuilder();
public string errorMessage = string.Empty; public string ErrorMessage { get; set; } = string.Empty;
public DateTime LastStatusSendTime { get; set; } = DateTime.Now; public DateTime LastStatusSendTime { get; set; } = DateTime.Now;
private EEProtocol proto; private EEProtocol proto;
@@ -38,6 +38,8 @@ namespace Project.Device
public Xbee() public Xbee()
{ {
this.WriteTimeout = 500;
this.ReadTimeout = 500;
this.DataReceived += Xbee_DataReceived; this.DataReceived += Xbee_DataReceived;
proto = new EEProtocol(); proto = new EEProtocol();
proto.OnDataReceived += Proto_OnDataReceived; proto.OnDataReceived += Proto_OnDataReceived;
@@ -65,11 +67,23 @@ namespace Project.Device
} }
catch (Exception ex) catch (Exception ex)
{ {
errorMessage = ex.Message; ErrorMessage = ex.Message;
return false; return false;
} }
} }
public new bool Close()
{
try
{
base.Close();
return true;
}
catch
{
return false;
}
}
public new bool Open() public new bool Open()
{ {
try try
@@ -79,8 +93,8 @@ namespace Project.Device
} }
catch (Exception ex) catch (Exception ex)
{ {
errorMessage = ex.Message; ErrorMessage = ex.Message;
PUB.logxbee.AddE(errorMessage); PUB.logxbee.AddE(ErrorMessage);
return false; return false;
} }
} }
@@ -92,19 +106,14 @@ namespace Project.Device
var cmd = e.ReceivedPacket.Command.ToString("X2"); var cmd = e.ReceivedPacket.Command.ToString("X2");
var id = e.ReceivedPacket.ID.ToString("X2"); var id = e.ReceivedPacket.ID.ToString("X2");
PUB.logxbee.Add("RX", $"{hexstrRaw}\nID:{id},CMD:{cmd},DATA:{hexstr}"); PUB.logxbee.Add("RX", $"{hexstrRaw}\nID:{id},CMD:{cmd},DATA:{hexstr}");
ProtocReceived?.Invoke(this, e); ProtocReceived?.Invoke(this, e);
} }
private void Proto_OnMessage(object sender, EEProtocol.MessageEventArgs e) private void Proto_OnMessage(object sender, EEProtocol.MessageEventArgs e)
{ {
MessageReceived?.Invoke(this, new MessageArgs(e.IsError, e.Message)); MessageReceived?.Invoke(this, new MessageArgs(e.IsError, e.Message));
} }
private void Xbee_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) private void Xbee_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{ {
var dev = sender as System.IO.Ports.SerialPort; var dev = sender as System.IO.Ports.SerialPort;
@@ -112,6 +121,7 @@ namespace Project.Device
dev.Read(buffer, 0, buffer.Length); dev.Read(buffer, 0, buffer.Length);
proto.ProcessReceivedData(buffer); proto.ProcessReceivedData(buffer);
} }
/// <summary> /// <summary>
/// 이동완료 신호 전송 /// 이동완료 신호 전송
/// </summary> /// </summary>
@@ -147,10 +157,12 @@ namespace Project.Device
byte cmd = (byte)ENIGProtocol.AGVCommandEH.Error; byte cmd = (byte)ENIGProtocol.AGVCommandEH.Error;
if (errormessage.Length > 30) errormessage = errormessage.Substring(0, 29); if (errormessage.Length > 30) errormessage = errormessage.Substring(0, 29);
var data = new byte[] { (byte)errcode }; var data = new List<byte>();
data.Add((byte)errcode);
var datamsg = System.Text.Encoding.Default.GetBytes(errormessage); var datamsg = System.Text.Encoding.Default.GetBytes(errormessage);
data.AddRange(datamsg);
var packet = proto.CreatePacket(id, cmd, data); var packet = proto.CreatePacket(id, cmd, data.ToArray());
Send(packet); Send(packet);
} }
@@ -166,12 +178,18 @@ namespace Project.Device
public bool CleanerInComplete { get; set; } public bool CleanerInComplete { get; set; }
public bool CleanerOutComplete { get; set; } public bool CleanerOutComplete { get; set; }
ManualResetEvent sendlock = new ManualResetEvent(true);
/// <summary> /// <summary>
/// AGV상태를 Xbee 로 전송한다 /// AGV상태를 Xbee 로 전송한다
/// </summary> /// </summary>
public void SendStatus() public void SendStatus()
{ {
if (this.IsOpen == false) return;
if (sendlock.WaitOne() == false) return;
sendlock.Reset();
/* /*
Mode[1] : 0=manual, 1=auto Mode[1] : 0=manual, 1=auto
RunSt[1] : 0=stop, 1=run, 2=error RunSt[1] : 0=stop, 1=run, 2=error
@@ -222,17 +240,17 @@ namespace Project.Device
data[5] = (byte)((VAR.BOOL[eVarBool.FLAG_CHARGEONA] || VAR.BOOL[eVarBool.FLAG_CHARGEONM]) ? 1 : 0); data[5] = (byte)((VAR.BOOL[eVarBool.FLAG_CHARGEONA] || VAR.BOOL[eVarBool.FLAG_CHARGEONM]) ? 1 : 0);
// CartSt // CartSt
if (PUB.AGV.signal.cart_detect1 && PUB.AGV.signal.cart_detect2) if (PUB.AGV.signal2.cart_detect1 && PUB.AGV.signal2.cart_detect2)
data[6] = 1; // 센서두개가 모두 감지되는 경우 data[6] = 1; // 센서두개가 모두 감지되는 경우
else if (PUB.AGV.signal.cart_detect1 == false && PUB.AGV.signal.cart_detect2 == false) else if (PUB.AGV.signal2.cart_detect1 == false && PUB.AGV.signal2.cart_detect2 == false)
data[6] = 0; // 센서두개가 모두 감지되지 않는 경우 data[6] = 0; // 센서두개가 모두 감지되지 않는 경우
else else
data[6] = 2; // 센서하나만 감지되는 경우 data[6] = 2; // 센서하나만 감지되는 경우
// LiftSt // LiftSt
if (PUB.AGV.signal.lift_up) if (PUB.AGV.signal1.lift_up)
data[7] = 1; // 위로 올라가는 경우 data[7] = 1; // 위로 올라가는 경우
else if (PUB.AGV.signal.lift_down) else if (PUB.AGV.signal1.lift_down)
data[7] = 0; // 아래로 내려가는 경우 data[7] = 0; // 아래로 내려가는 경우
else else
data[7] = 2; // unknown (기본값) data[7] = 2; // unknown (기본값)
@@ -246,14 +264,21 @@ namespace Project.Device
var cmd = (byte)ENIGProtocol.AGVCommandEH.Status; var cmd = (byte)ENIGProtocol.AGVCommandEH.Status;
var packet = proto.CreatePacket(PUB.setting.XBE_ID, cmd, data); var packet = proto.CreatePacket(PUB.setting.XBE_ID, cmd, data);
if (Send(packet)) if (Send(packet))
PUB.logxbee.AddI($"Send status {packet.Length} {packet.HexString()}"); PUB.logxbee.AddI($"Send status [O] : {packet.Length} {packet.HexString()}");
else
PUB.logxbee.AddE($"Send status [X] : {packet.Length} {packet.HexString()}");
LastStatusSendTime = DateTime.Now; LastStatusSendTime = DateTime.Now;
} }
catch (Exception ex) catch (Exception ex)
{ {
errorMessage = ex.Message; ErrorMessage = ex.Message;
PUB.logxbee.AddE(errorMessage); PUB.logxbee.AddE(ErrorMessage);
} }
finally
{
sendlock.Set();
}
} }

View File

@@ -1,311 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media.Animation;
using AR;
using arCtl;
using COMM;
using Project.StateMachine;
namespace Project
{
/// <summary>
/// 장치 연결 및 상태 전송을 담당하는 별도 태스크
/// </summary>
public partial class fMain
{
// 장치 관리 태스크 관련
private Task deviceManagementTask;
private CancellationTokenSource deviceManagementCts;
private volatile bool isDeviceManagementRunning = false;
/// <summary>
/// 장치 관리 태스크 시작 (IDLE 상태 진입 시 호출)
/// </summary>
public void StartDeviceManagementTask()
{
if (isDeviceManagementRunning)
{
PUB.log.Add("DeviceManagement", "이미 실행 중입니다.");
return;
}
isDeviceManagementRunning = true;
deviceManagementCts = new CancellationTokenSource();
deviceManagementTask = Task.Factory.StartNew(
() => DeviceManagementWorker(deviceManagementCts.Token),
deviceManagementCts.Token,
TaskCreationOptions.LongRunning,
TaskScheduler.Default
);
PUB.log.Add("DeviceManagement", "장치 관리 태스크 시작");
}
/// <summary>
/// 장치 관리 태스크 종료
/// File : /device/_DeviceManagement.cs
/// </summary>
public void StopDeviceManagementTask()
{
if (!isDeviceManagementRunning)
return;
isDeviceManagementRunning = false;
try
{
deviceManagementCts?.Cancel();
if (deviceManagementTask != null)
{
if (!deviceManagementTask.Wait(3000)) // 3초 대기
{
PUB.log.AddE("DeviceManagement:태스크 종료 대기 시간 초과");
}
}
}
catch (Exception ex)
{
PUB.log.AddE($"DeviceManagement 종료 중 오류: {ex.Message}");
}
finally
{
deviceManagementCts?.Dispose();
deviceManagementCts = null;
deviceManagementTask = null;
PUB.log.Add("DeviceManagement", "장치 관리 태스크 종료");
}
}
/// <summary>
/// 장치 관리 워커 (별도 태스크에서 실행)
/// - 장치 연결 관리 (AGV, XBee, BMS)
/// - 자동 상태 전송 (XBee, BMS)
/// </summary>
private void DeviceManagementWorker(CancellationToken cancellationToken)
{
PUB.log.Add("DeviceManagementWorker", "시작");
DateTime lastXbeStatusSendTime = DateTime.Now;
DateTime lastBmsQueryTime = DateTime.Now;
while (!cancellationToken.IsCancellationRequested && isDeviceManagementRunning)
{
try
{
// 상태머신이 IDLE 이상이고 CLOSING 미만일 때만 동작
if (PUB.sm.Step >= eSMStep.IDLE && PUB.sm.Step < eSMStep.CLOSING)
{
// ========== 1. 장치 연결 관리 ==========
ManageDeviceConnections();
// ========== 2. XBee 상태 전송 ==========
if (PUB.XBE != null && PUB.XBE.IsOpen)
{
var tsXbe = DateTime.Now - lastXbeStatusSendTime;
if (tsXbe.TotalSeconds >= PUB.setting.interval_xbe)
{
lastXbeStatusSendTime = DateTime.Now;
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
PUB.XBE.SendStatus();
}
catch (Exception ex)
{
PUB.log.AddE($"XBee SendStatus 오류: {ex.Message}");
}
});
}
}
// ========== 3. BMS 쿼리 및 배터리 경고 ==========
if (PUB.BMS != null && PUB.BMS.IsOpen)
{
var tsBms = DateTime.Now - lastBmsQueryTime;
if (tsBms.TotalSeconds >= PUB.setting.interval_bms)
{
lastBmsQueryTime = DateTime.Now;
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
PUB.BMS.SendQuery();
}
catch (Exception ex)
{
PUB.log.AddE($"BMS SendQuery 오류: {ex.Message}");
}
});
}
// 배터리 경고음
try
{
Update_BatteryWarnSpeak();
}
catch (Exception ex)
{
PUB.log.AddE($"BatteryWarnSpeak 오류: {ex.Message}");
}
}
}
}
catch (Exception ex)
{
PUB.log.AddE($"DeviceManagementWorker 오류: {ex.Message}");
}
// 1초 대기 (또는 취소 요청 시 즉시 종료)
try
{
Task.Delay(1000, cancellationToken).Wait();
}
catch (OperationCanceledException)
{
break;
}
}
PUB.log.Add("DeviceManagementWorker", "종료");
}
/// <summary>
/// 장치 연결 상태 관리
/// </summary>
private void ManageDeviceConnections()
{
try
{
// AGV 연결
ConnectSerialPort(PUB.AGV, PUB.setting.Port_AGV, PUB.setting.Baud_AGV,
eVarTime.LastConn_AGV, eVarTime.LastConnTry_AGV, eVarTime.LastRecv_AGV);
// XBee 연결
ConnectSerialPort(PUB.XBE, PUB.setting.Port_XBE, PUB.setting.Baud_XBE,
eVarTime.LastConn_XBE, eVarTime.LastConnTry_XBE, eVarTime.LastRecv_XBE);
// BMS 연결
if (PUB.BMS.IsOpen == false)
{
var ts = VAR.TIME.RUN(eVarTime.LastConn_BAT);
if (ts.TotalSeconds > 3)
{
PUB.log.Add($"BMS 연결 시도: {PUB.setting.Port_BAT}");
PUB.BMS.PortName = PUB.setting.Port_BAT;
if (PUB.BMS.Open())
PUB.log.AddI($"BMS 연결 완료({PUB.setting.Port_BAT})");
VAR.TIME.Update(eVarTime.LastConn_BAT);
VAR.TIME.Update(eVarTime.LastConnTry_BAT);
}
}
else if (PUB.BMS.IsValid == false)
{
var ts = VAR.TIME.RUN(eVarTime.LastConnTry_BAT);
if (ts.TotalSeconds > (PUB.setting.interval_bms * 2.5))
{
PUB.log.Add("BMS 자동 연결 해제 (응답 없음)");
PUB.BMS.Close();
VAR.TIME.Set(eVarTime.LastConn_BAT, DateTime.Now.AddSeconds(5));
}
}
}
catch (Exception ex)
{
PUB.log.AddE($"ManageDeviceConnections 오류: {ex.Message}");
}
}
/// <summary>
/// 시리얼 포트 연결 (arDev.arRS232)
/// </summary>
void ConnectSerialPort(arDev.arRS232 dev, string port, int baud, eVarTime conn, eVarTime conntry, eVarTime recvtime)
{
if (dev.IsOpen == false && port.isEmpty() == false)
{
var tsPLC = VAR.TIME.RUN(conntry);
if (tsPLC.TotalSeconds > 5)
{
VAR.TIME.Update(conntry);
try
{
VAR.TIME.Update(recvtime);
dev.PortName = port;
dev.BaudRate = baud;
if (dev.Open())
{
PUB.log.Add(port, $"[AGV:{port}:{baud}] 연결 완료");
}
else
{
var errmessage = dev.errorMessage;
PUB.log.AddE($"[AGV:{port}:{baud}] {errmessage}");
}
VAR.TIME.Update(conn);
VAR.TIME.Update(conntry);
}
catch (Exception ex)
{
PUB.log.AddE(ex.Message);
}
}
}
else if (dev.PortName.Equals(port) == false)
{
PUB.log.Add(port, $"포트 변경({dev.PortName}->{port})으로 연결 종료");
dev.Close();
VAR.TIME.Update(conntry);
}
}
/// <summary>
/// 시리얼 포트 연결 (Device.Xbee)
/// </summary>
void ConnectSerialPort(Device.Xbee dev, string port, int baud, eVarTime conn, eVarTime conntry, eVarTime recvtime)
{
if (dev.IsOpen == false && port.isEmpty() == false)
{
var tsPLC = VAR.TIME.RUN(conntry);
if (tsPLC.TotalSeconds > 5)
{
VAR.TIME.Update(conntry);
try
{
VAR.TIME.Update(recvtime);
dev.PortName = port;
dev.BaudRate = baud;
if (dev.Open())
{
PUB.log.Add(port, $"[XBEE:{port}:{baud}] 연결 완료");
}
else
{
var errmessage = dev.errorMessage;
PUB.log.AddE($"[XBEE:{port}:{baud}] {errmessage}");
}
VAR.TIME.Update(conn);
VAR.TIME.Update(conntry);
}
catch (Exception ex)
{
PUB.log.AddE(ex.Message);
}
}
}
else if (dev.PortName.Equals(port) == false)
{
PUB.log.Add(port, $"포트 변경({dev.PortName}->{port})으로 연결 종료");
dev.Close();
VAR.TIME[(int)conntry] = DateTime.Now;
}
}
}
}

View File

@@ -1,815 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms; // required for Message
using System.Runtime.InteropServices; // required for Marshal
using System.IO;
using Microsoft.Win32.SafeHandles;
// DriveDetector - rev. 1, Oct. 31 2007
namespace usbdetect
{
/// <summary>
/// Hidden Form which we use to receive Windows messages about flash drives
/// </summary>
internal class DetectorForm : Form
{
private Label label1;
private DriveDetector mDetector = null;
/// <summary>
/// Set up the hidden form.
/// </summary>
/// <param name="detector">DriveDetector object which will receive notification about USB drives, see WndProc</param>
public DetectorForm(DriveDetector detector)
{
mDetector = detector;
this.MinimizeBox = false;
this.MaximizeBox = false;
this.ShowInTaskbar = false;
this.ShowIcon = false;
this.FormBorderStyle = FormBorderStyle.None;
this.Load += new System.EventHandler(this.Load_Form);
this.Activated += new EventHandler(this.Form_Activated);
}
private void Load_Form(object sender, EventArgs e)
{
// We don't really need this, just to display the label in designer ...
InitializeComponent();
// Create really small form, invisible anyway.
this.Size = new System.Drawing.Size(5, 5);
}
private void Form_Activated(object sender, EventArgs e)
{
this.Visible = false;
}
/// <summary>
/// This function receives all the windows messages for this window (form).
/// We call the DriveDetector from here so that is can pick up the messages about
/// drives arrived and removed.
/// </summary>
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (mDetector != null)
{
mDetector.WndProc(ref m);
}
}
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(13, 30);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(377, 12);
this.label1.TabIndex = 0;
this.label1.Text = "This is invisible form. To see DriveDetector code click View Code";
//
// DetectorForm
//
this.ClientSize = new System.Drawing.Size(435, 80);
this.Controls.Add(this.label1);
this.Name = "DetectorForm";
this.ResumeLayout(false);
this.PerformLayout();
}
} // class DetectorForm
// Delegate for event handler to handle the device events
public delegate void DriveDetectorEventHandler(Object sender, DriveDetectorEventArgs e);
/// <summary>
/// Our class for passing in custom arguments to our event handlers
///
/// </summary>
public class DriveDetectorEventArgs : EventArgs
{
public DriveDetectorEventArgs()
{
Cancel = false;
Drive = "";
HookQueryRemove = false;
}
/// <summary>
/// Get/Set the value indicating that the event should be cancelled
/// Only in QueryRemove handler.
/// </summary>
public bool Cancel;
/// <summary>
/// Drive letter for the device which caused this event
/// </summary>
public string Drive;
/// <summary>
/// Set to true in your DeviceArrived event handler if you wish to receive the
/// QueryRemove event for this drive.
/// </summary>
public bool HookQueryRemove;
}
/// <summary>
/// Detects insertion or removal of removable drives.
/// Use it in 1 or 2 steps:
/// 1) Create instance of this class in your project and add handlers for the
/// DeviceArrived, DeviceRemoved and QueryRemove events.
/// AND (if you do not want drive detector to creaate a hidden form))
/// 2) Override WndProc in your form and call DriveDetector's WndProc from there.
/// If you do not want to do step 2, just use the DriveDetector constructor without arguments and
/// it will create its own invisible form to receive messages from Windows.
/// </summary>
class DriveDetector : IDisposable
{
/// <summary>
/// Events signalized to the client app.
/// Add handlers for these events in your form to be notified of removable device events
/// </summary>
public event DriveDetectorEventHandler DeviceArrived;
public event DriveDetectorEventHandler DeviceRemoved;
public event DriveDetectorEventHandler QueryRemove;
/// <summary>
/// The easiest way to use DriveDetector.
/// It will create hidden form for processing Windows messages about USB drives
/// You do not need to override WndProc in your form.
/// </summary>
public DriveDetector()
{
DetectorForm frm = new DetectorForm(this);
frm.Show(); // will be hidden immediatelly
Init(frm, null);
}
/// <summary>
/// Alternate constructor.
/// Pass in your Form and DriveDetector will not create hidden form.
/// </summary>
/// <param name="control">object which will receive Windows messages.
/// Pass "this" as this argument from your form class.</param>
public DriveDetector(Control control)
{
Init(control, null);
}
/// <summary>
/// Consructs DriveDetector object setting also path to file which should be opened
/// when registering for query remove.
/// </summary>
///<param name="control">object which will receive Windows messages.
/// Pass "this" as this argument from your form class.</param>
/// <param name="FileToOpen">Optional. Name of a file on the removable drive which should be opened.
/// If null, root directory of the drive will be opened. Opening a file is needed for us
/// to be able to register for the query remove message. TIP: For files use relative path without drive letter.
/// e.g. "SomeFolder\file_on_flash.txt"</param>
public DriveDetector(Control control, string FileToOpen)
{
Init(control, FileToOpen);
}
/// <summary>
/// init the DriveDetector object
/// </summary>
/// <param name="intPtr"></param>
private void Init(Control control, string fileToOpen)
{
mFileToOpen = fileToOpen;
mFileOnFlash = null;
mDeviceNotifyHandle = IntPtr.Zero;
mRecipientHandle = control.Handle;
mDirHandle = IntPtr.Zero; // handle to the root directory of the flash drive which we open
mCurrentDrive = "";
}
/// <summary>
/// Gets the value indicating whether the query remove event will be fired.
/// </summary>
public bool IsQueryHooked
{
get
{
if (mDeviceNotifyHandle == IntPtr.Zero)
return false;
else
return true;
}
}
/// <summary>
/// Gets letter of drive which is currently hooked. Empty string if none.
/// See also IsQueryHooked.
/// </summary>
public string HookedDrive
{
get
{
return mCurrentDrive;
}
}
/// <summary>
/// Gets the file stream for file which this class opened on a drive to be notified
/// about it's removal.
/// This will be null unless you specified a file to open (DriveDetector opens root directory of the flash drive)
/// </summary>
public FileStream OpenedFile
{
get
{
return mFileOnFlash;
}
}
/// <summary>
/// Hooks specified drive to receive a message when it is being removed.
/// This can be achieved also by setting e.HookQueryRemove to true in your
/// DeviceArrived event handler.
/// By default DriveDetector will open the root directory of the flash drive to obtain notification handle
/// from Windows (to learn when the drive is about to be removed).
/// </summary>
/// <param name="fileOnDrive">Drive letter or relative path to a file on the drive which should be
/// used to get a handle - required for registering to receive query remove messages.
/// If only drive letter is specified (e.g. "D:\\", root directory of the drive will be opened.</param>
/// <returns>true if hooked ok, false otherwise</returns>
public bool EnableQueryRemove(string fileOnDrive)
{
if (fileOnDrive == null || fileOnDrive.Length == 0)
throw new ArgumentException("Drive path must be supplied to register for Query remove.");
if ( fileOnDrive.Length == 2 && fileOnDrive[1] == ':' )
fileOnDrive += '\\'; // append "\\" if only drive letter with ":" was passed in.
if (mDeviceNotifyHandle != IntPtr.Zero)
{
// Unregister first...
RegisterForDeviceChange(false, null);
}
if (Path.GetFileName(fileOnDrive).Length == 0 ||!File.Exists(fileOnDrive))
mFileToOpen = null; // use root directory...
else
mFileToOpen = fileOnDrive;
RegisterQuery(Path.GetPathRoot(fileOnDrive));
if (mDeviceNotifyHandle == IntPtr.Zero)
return false; // failed to register
return true;
}
/// <summary>
/// Unhooks any currently hooked drive so that the query remove
/// message is not generated for it.
/// </summary>
public void DisableQueryRemove()
{
if (mDeviceNotifyHandle != IntPtr.Zero)
{
RegisterForDeviceChange(false, null);
}
}
/// <summary>
/// Unregister and close the file we may have opened on the removable drive.
/// Garbage collector will call this method.
/// </summary>
public void Dispose()
{
RegisterForDeviceChange(false, null);
}
#region WindowProc
/// <summary>
/// Message handler which must be called from client form.
/// Processes Windows messages and calls event handlers.
/// </summary>
/// <param name="m"></param>
public void WndProc(ref Message m)
{
int devType;
char c;
if (m.Msg == WM_DEVICECHANGE)
{
// WM_DEVICECHANGE can have several meanings depending on the WParam value...
switch (m.WParam.ToInt32())
{
//
// New device has just arrived
//
case DBT_DEVICEARRIVAL:
devType = Marshal.ReadInt32(m.LParam, 4);
if (devType == DBT_DEVTYP_VOLUME)
{
DEV_BROADCAST_VOLUME vol;
vol = (DEV_BROADCAST_VOLUME)
Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
// Get the drive letter
c = DriveMaskToLetter(vol.dbcv_unitmask);
//
// Call the client event handler
//
// We should create copy of the event before testing it and
// calling the delegate - if any
DriveDetectorEventHandler tempDeviceArrived = DeviceArrived;
if ( tempDeviceArrived != null )
{
DriveDetectorEventArgs e = new DriveDetectorEventArgs();
e.Drive = c + ":\\";
tempDeviceArrived(this, e);
// Register for query remove if requested
if (e.HookQueryRemove)
{
// If something is already hooked, unhook it now
if (mDeviceNotifyHandle != IntPtr.Zero)
{
RegisterForDeviceChange(false, null);
}
RegisterQuery(c + ":\\");
}
} // if has event handler
}
break;
//
// Device is about to be removed
// Any application can cancel the removal
//
case DBT_DEVICEQUERYREMOVE:
devType = Marshal.ReadInt32(m.LParam, 4);
if (devType == DBT_DEVTYP_HANDLE)
{
// TODO: we could get the handle for which this message is sent
// from vol.dbch_handle and compare it against a list of handles for
// which we have registered the query remove message (?)
//DEV_BROADCAST_HANDLE vol;
//vol = (DEV_BROADCAST_HANDLE)
// Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HANDLE));
// if ( vol.dbch_handle ....
//
// Call the event handler in client
//
DriveDetectorEventHandler tempQuery = QueryRemove;
if (tempQuery != null)
{
DriveDetectorEventArgs e = new DriveDetectorEventArgs();
e.Drive = mCurrentDrive; // drive which is hooked
tempQuery(this, e);
// If the client wants to cancel, let Windows know
if (e.Cancel)
{
m.Result = (IntPtr)BROADCAST_QUERY_DENY;
}
else
{
// Change 28.10.2007: Unregister the notification, this will
// close the handle to file or root directory also.
// We have to close it anyway to allow the removal so
// even if some other app cancels the removal we would not know about it...
RegisterForDeviceChange(false, null); // will also close the mFileOnFlash
}
}
}
break;
//
// Device has been removed
//
case DBT_DEVICEREMOVECOMPLETE:
devType = Marshal.ReadInt32(m.LParam, 4);
if (devType == DBT_DEVTYP_VOLUME)
{
devType = Marshal.ReadInt32(m.LParam, 4);
if (devType == DBT_DEVTYP_VOLUME)
{
DEV_BROADCAST_VOLUME vol;
vol = (DEV_BROADCAST_VOLUME)
Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
c = DriveMaskToLetter(vol.dbcv_unitmask);
//
// Call the client event handler
//
DriveDetectorEventHandler tempDeviceRemoved = DeviceRemoved;
if (tempDeviceRemoved != null)
{
DriveDetectorEventArgs e = new DriveDetectorEventArgs();
e.Drive = c + ":\\";
tempDeviceRemoved(this, e);
}
// TODO: we could unregister the notify handle here if we knew it is the
// right drive which has been just removed
//RegisterForDeviceChange(false, null);
}
}
break;
}
}
}
#endregion
#region Private Area
/// <summary>
/// New: 28.10.2007 - handle to root directory of flash drive which is opened
/// for device notification
/// </summary>
private IntPtr mDirHandle = IntPtr.Zero;
/// <summary>
/// Class which contains also handle to the file opened on the flash drive
/// </summary>
private FileStream mFileOnFlash = null;
/// <summary>
/// Name of the file to try to open on the removable drive for query remove registration
/// </summary>
private string mFileToOpen;
/// <summary>
/// Handle to file which we keep opened on the drive if query remove message is required by the client
/// </summary>
private IntPtr mDeviceNotifyHandle;
/// <summary>
/// Handle of the window which receives messages from Windows. This will be a form.
/// </summary>
private IntPtr mRecipientHandle;
/// <summary>
/// Drive which is currently hooked for query remove
/// </summary>
private string mCurrentDrive;
// Win32 constants
private const int DBT_DEVTYP_DEVICEINTERFACE = 5;
private const int DBT_DEVTYP_HANDLE = 6;
private const int BROADCAST_QUERY_DENY = 0x424D5144;
private const int WM_DEVICECHANGE = 0x0219;
private const int DBT_DEVICEARRIVAL = 0x8000; // system detected a new device
private const int DBT_DEVICEQUERYREMOVE = 0x8001; // Preparing to remove (any program can disable the removal)
private const int DBT_DEVICEREMOVECOMPLETE = 0x8004; // removed
private const int DBT_DEVTYP_VOLUME = 0x00000002; // drive type is logical volume
/// <summary>
/// Registers for receiving the query remove message for a given drive.
/// We need to open a handle on that drive and register with this handle.
/// Client can specify this file in mFileToOpen or we will open root directory of the drive
/// </summary>
/// <param name="drive">drive for which to register. </param>
private void RegisterQuery(string drive)
{
bool register = true;
if (mFileToOpen == null)
{
// Change 28.10.2007 - Open the root directory if no file specified - leave mFileToOpen null
// If client gave us no file, let's pick one on the drive...
//mFileToOpen = GetAnyFile(drive);
//if (mFileToOpen.Length == 0)
// return; // no file found on the flash drive
}
else
{
// Make sure the path in mFileToOpen contains valid drive
// If there is a drive letter in the path, it may be different from the actual
// letter assigned to the drive now. We will cut it off and merge the actual drive
// with the rest of the path.
if (mFileToOpen.Contains(":"))
{
string tmp = mFileToOpen.Substring(3);
string root = Path.GetPathRoot(drive);
mFileToOpen = Path.Combine(root, tmp);
}
else
mFileToOpen = Path.Combine(drive, mFileToOpen);
}
try
{
//mFileOnFlash = new FileStream(mFileToOpen, FileMode.Open);
// Change 28.10.2007 - Open the root directory
if (mFileToOpen == null) // open root directory
mFileOnFlash = null;
else
mFileOnFlash = new FileStream(mFileToOpen, FileMode.Open);
}
catch (Exception)
{
// just do not register if the file could not be opened
register = false;
}
if (register)
{
//RegisterForDeviceChange(true, mFileOnFlash.SafeFileHandle);
//mCurrentDrive = drive;
// Change 28.10.2007 - Open the root directory
if (mFileOnFlash == null)
RegisterForDeviceChange(drive);
else
// old version
RegisterForDeviceChange(true, mFileOnFlash.SafeFileHandle);
mCurrentDrive = drive;
}
}
/// <summary>
/// New version which gets the handle automatically for specified directory
/// Only for registering! Unregister with the old version of this function...
/// </summary>
/// <param name="register"></param>
/// <param name="dirPath">e.g. C:\\dir</param>
private void RegisterForDeviceChange(string dirPath)
{
IntPtr handle = Native.OpenDirectory(dirPath);
if (handle == IntPtr.Zero)
{
mDeviceNotifyHandle = IntPtr.Zero;
return;
}
else
mDirHandle = handle; // save handle for closing it when unregistering
// Register for handle
DEV_BROADCAST_HANDLE data = new DEV_BROADCAST_HANDLE();
data.dbch_devicetype = DBT_DEVTYP_HANDLE;
data.dbch_reserved = 0;
data.dbch_nameoffset = 0;
//data.dbch_data = null;
//data.dbch_eventguid = 0;
data.dbch_handle = handle;
data.dbch_hdevnotify = (IntPtr)0;
int size = Marshal.SizeOf(data);
data.dbch_size = size;
IntPtr buffer = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(data, buffer, true);
mDeviceNotifyHandle = Native.RegisterDeviceNotification(mRecipientHandle, buffer, 0);
}
/// <summary>
/// Registers to be notified when the volume is about to be removed
/// This is requierd if you want to get the QUERY REMOVE messages
/// </summary>
/// <param name="register">true to register, false to unregister</param>
/// <param name="fileHandle">handle of a file opened on the removable drive</param>
private void RegisterForDeviceChange(bool register, SafeFileHandle fileHandle)
{
if (register)
{
// Register for handle
DEV_BROADCAST_HANDLE data = new DEV_BROADCAST_HANDLE();
data.dbch_devicetype = DBT_DEVTYP_HANDLE;
data.dbch_reserved = 0;
data.dbch_nameoffset = 0;
//data.dbch_data = null;
//data.dbch_eventguid = 0;
data.dbch_handle = fileHandle.DangerousGetHandle(); //Marshal. fileHandle;
data.dbch_hdevnotify = (IntPtr)0;
int size = Marshal.SizeOf(data);
data.dbch_size = size;
IntPtr buffer = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(data, buffer, true);
mDeviceNotifyHandle = Native.RegisterDeviceNotification(mRecipientHandle, buffer, 0);
}
else
{
// close the directory handle
if (mDirHandle != IntPtr.Zero)
{
Native.CloseDirectoryHandle(mDirHandle);
// string er = Marshal.GetLastWin32Error().ToString();
}
// unregister
if (mDeviceNotifyHandle != IntPtr.Zero)
{
Native.UnregisterDeviceNotification(mDeviceNotifyHandle);
}
mDeviceNotifyHandle = IntPtr.Zero;
mDirHandle = IntPtr.Zero;
mCurrentDrive = "";
if (mFileOnFlash != null)
{
mFileOnFlash.Close();
mFileOnFlash = null;
}
}
}
/// <summary>
/// Gets drive letter from a bit mask where bit 0 = A, bit 1 = B etc.
/// There can actually be more than one drive in the mask but we
/// just use the last one in this case.
/// </summary>
/// <param name="mask"></param>
/// <returns></returns>
private static char DriveMaskToLetter(int mask)
{
char letter;
string drives = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// 1 = A
// 2 = B
// 4 = C...
int cnt = 0;
int pom = mask / 2;
while (pom != 0)
{
// while there is any bit set in the mask
// shift it to the righ...
pom = pom / 2;
cnt++;
}
if (cnt < drives.Length)
letter = drives[cnt];
else
letter = '?';
return letter;
}
/* 28.10.2007 - no longer needed
/// <summary>
/// Searches for any file in a given path and returns its full path
/// </summary>
/// <param name="drive">drive to search</param>
/// <returns>path of the file or empty string</returns>
private string GetAnyFile(string drive)
{
string file = "";
// First try files in the root
string[] files = Directory.GetFiles(drive);
if (files.Length == 0)
{
// if no file in the root, search whole drive
files = Directory.GetFiles(drive, "*.*", SearchOption.AllDirectories);
}
if (files.Length > 0)
file = files[0]; // get the first file
// return empty string if no file found
return file;
}*/
#endregion
#region Native Win32 API
/// <summary>
/// WinAPI functions
/// </summary>
private class Native
{
// HDEVNOTIFY RegisterDeviceNotification(HANDLE hRecipient,LPVOID NotificationFilter,DWORD Flags);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, uint Flags);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern uint UnregisterDeviceNotification(IntPtr hHandle);
//
// CreateFile - MSDN
const uint GENERIC_READ = 0x80000000;
const uint OPEN_EXISTING = 3;
const uint FILE_SHARE_READ = 0x00000001;
const uint FILE_SHARE_WRITE = 0x00000002;
const uint FILE_ATTRIBUTE_NORMAL = 128;
const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
// should be "static extern unsafe"
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr CreateFile(
string FileName, // file name
uint DesiredAccess, // access mode
uint ShareMode, // share mode
uint SecurityAttributes, // Security Attributes
uint CreationDisposition, // how to create
uint FlagsAndAttributes, // file attributes
int hTemplateFile // handle to template file
);
[DllImport("kernel32", SetLastError = true)]
static extern bool CloseHandle(
IntPtr hObject // handle to object
);
/// <summary>
/// Opens a directory, returns it's handle or zero.
/// </summary>
/// <param name="dirPath">path to the directory, e.g. "C:\\dir"</param>
/// <returns>handle to the directory. Close it with CloseHandle().</returns>
static public IntPtr OpenDirectory(string dirPath)
{
// open the existing file for reading
IntPtr handle = CreateFile(
dirPath,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
0);
if ( handle == INVALID_HANDLE_VALUE)
return IntPtr.Zero;
else
return handle;
}
public static bool CloseDirectoryHandle(IntPtr handle)
{
return CloseHandle(handle);
}
}
// Structure with information for RegisterDeviceNotification.
[StructLayout(LayoutKind.Sequential)]
public struct DEV_BROADCAST_HANDLE
{
public int dbch_size;
public int dbch_devicetype;
public int dbch_reserved;
public IntPtr dbch_handle;
public IntPtr dbch_hdevnotify;
public Guid dbch_eventguid;
public long dbch_nameoffset;
//public byte[] dbch_data[1]; // = new byte[1];
public byte dbch_data;
public byte dbch_data1;
}
// Struct for parameters of the WM_DEVICECHANGE message
[StructLayout(LayoutKind.Sequential)]
public struct DEV_BROADCAST_VOLUME
{
public int dbcv_size;
public int dbcv_devicetype;
public int dbcv_reserved;
public int dbcv_unitmask;
}
#endregion
}
}

View File

@@ -31,16 +31,15 @@
this.rtsys = new arCtl.LogTextBox(); this.rtsys = new arCtl.LogTextBox();
this.rtTx = new arCtl.LogTextBox(); this.rtTx = new arCtl.LogTextBox();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.rtAGV = new arCtl.LogTextBox();
this.rtBMS = new arCtl.LogTextBox();
this.rtXbee = new arCtl.LogTextBox();
this.panel1 = new System.Windows.Forms.Panel(); this.panel1 = new System.Windows.Forms.Panel();
this.label1 = new System.Windows.Forms.Label(); this.rtXbee = new arCtl.LogTextBox();
this.titleXBEE = new System.Windows.Forms.Label();
this.panel2 = new System.Windows.Forms.Panel(); this.panel2 = new System.Windows.Forms.Panel();
this.panel3 = new System.Windows.Forms.Panel(); this.rtBMS = new arCtl.LogTextBox();
this.titleBMS = new System.Windows.Forms.Label();
this.panel4 = new System.Windows.Forms.Panel(); this.panel4 = new System.Windows.Forms.Panel();
this.label2 = new System.Windows.Forms.Label(); this.rtAGV = new arCtl.LogTextBox();
this.label3 = new System.Windows.Forms.Label(); this.titleAGV = new System.Windows.Forms.Label();
this.tableLayoutPanel1.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout();
this.panel1.SuspendLayout(); this.panel1.SuspendLayout();
this.panel2.SuspendLayout(); this.panel2.SuspendLayout();
@@ -99,8 +98,7 @@
this.tableLayoutPanel1.Controls.Add(this.rtsys, 0, 0); this.tableLayoutPanel1.Controls.Add(this.rtsys, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.rtTx, 2, 0); this.tableLayoutPanel1.Controls.Add(this.rtTx, 2, 0);
this.tableLayoutPanel1.Controls.Add(this.panel1, 1, 1); this.tableLayoutPanel1.Controls.Add(this.panel1, 1, 1);
this.tableLayoutPanel1.Controls.Add(this.panel2, 3, 1); this.tableLayoutPanel1.Controls.Add(this.panel2, 2, 1);
this.tableLayoutPanel1.Controls.Add(this.panel3, 2, 1);
this.tableLayoutPanel1.Controls.Add(this.panel4, 0, 1); this.tableLayoutPanel1.Controls.Add(this.panel4, 0, 1);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
@@ -111,45 +109,15 @@
this.tableLayoutPanel1.Size = new System.Drawing.Size(681, 495); this.tableLayoutPanel1.Size = new System.Drawing.Size(681, 495);
this.tableLayoutPanel1.TabIndex = 2; this.tableLayoutPanel1.TabIndex = 2;
// //
// rtAGV // panel1
// //
this.rtAGV.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); this.panel1.Controls.Add(this.rtXbee);
this.rtAGV.ColorList = new arCtl.sLogMessageColor[0]; this.panel1.Controls.Add(this.titleXBEE);
this.rtAGV.DateFormat = "mm:ss.fff"; this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.rtAGV.DefaultColor = System.Drawing.Color.LightGray; this.panel1.Location = new System.Drawing.Point(173, 349);
this.rtAGV.Dock = System.Windows.Forms.DockStyle.Fill; this.panel1.Name = "panel1";
this.rtAGV.EnableDisplayTimer = false; this.panel1.Size = new System.Drawing.Size(164, 143);
this.rtAGV.EnableGubunColor = true; this.panel1.TabIndex = 3;
this.rtAGV.Font = new System.Drawing.Font("맑은 고딕", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.rtAGV.ListFormat = "[{0}] {1}";
this.rtAGV.Location = new System.Drawing.Point(0, 14);
this.rtAGV.MaxListCount = ((ushort)(1000));
this.rtAGV.MaxTextLength = ((uint)(400000u));
this.rtAGV.MessageInterval = 50;
this.rtAGV.Name = "rtAGV";
this.rtAGV.Size = new System.Drawing.Size(164, 129);
this.rtAGV.TabIndex = 2;
this.rtAGV.Text = "";
//
// rtBMS
//
this.rtBMS.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.rtBMS.ColorList = new arCtl.sLogMessageColor[0];
this.rtBMS.DateFormat = "mm:ss.fff";
this.rtBMS.DefaultColor = System.Drawing.Color.LightGray;
this.rtBMS.Dock = System.Windows.Forms.DockStyle.Fill;
this.rtBMS.EnableDisplayTimer = false;
this.rtBMS.EnableGubunColor = true;
this.rtBMS.Font = new System.Drawing.Font("맑은 고딕", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.rtBMS.ListFormat = "[{0}] {1}";
this.rtBMS.Location = new System.Drawing.Point(0, 14);
this.rtBMS.MaxListCount = ((ushort)(1000));
this.rtBMS.MaxTextLength = ((uint)(400000u));
this.rtBMS.MessageInterval = 50;
this.rtBMS.Name = "rtBMS";
this.rtBMS.Size = new System.Drawing.Size(165, 129);
this.rtBMS.TabIndex = 2;
this.rtBMS.Text = "";
// //
// rtXbee // rtXbee
// //
@@ -171,73 +139,96 @@
this.rtXbee.TabIndex = 2; this.rtXbee.TabIndex = 2;
this.rtXbee.Text = ""; this.rtXbee.Text = "";
// //
// panel1 // titleXBEE
// //
this.panel1.Controls.Add(this.rtXbee); this.titleXBEE.Dock = System.Windows.Forms.DockStyle.Top;
this.panel1.Controls.Add(this.label1); this.titleXBEE.Location = new System.Drawing.Point(0, 0);
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; this.titleXBEE.Name = "titleXBEE";
this.panel1.Location = new System.Drawing.Point(173, 349); this.titleXBEE.Size = new System.Drawing.Size(164, 14);
this.panel1.Name = "panel1"; this.titleXBEE.TabIndex = 0;
this.panel1.Size = new System.Drawing.Size(164, 143); this.titleXBEE.Text = "MC ID";
this.panel1.TabIndex = 3; this.titleXBEE.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// label1
//
this.label1.Dock = System.Windows.Forms.DockStyle.Top;
this.label1.Location = new System.Drawing.Point(0, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(164, 14);
this.label1.TabIndex = 0;
this.label1.Text = "MC ID";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
// //
// panel2 // panel2
// //
this.tableLayoutPanel1.SetColumnSpan(this.panel2, 2);
this.panel2.Controls.Add(this.rtBMS); this.panel2.Controls.Add(this.rtBMS);
this.panel2.Controls.Add(this.label3); this.panel2.Controls.Add(this.titleBMS);
this.panel2.Dock = System.Windows.Forms.DockStyle.Fill; this.panel2.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel2.Location = new System.Drawing.Point(513, 349); this.panel2.Location = new System.Drawing.Point(343, 349);
this.panel2.Name = "panel2"; this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(165, 143); this.panel2.Size = new System.Drawing.Size(335, 143);
this.panel2.TabIndex = 4; this.panel2.TabIndex = 4;
// //
// panel3 // rtBMS
// //
this.panel3.Dock = System.Windows.Forms.DockStyle.Fill; this.rtBMS.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.panel3.Location = new System.Drawing.Point(343, 349); this.rtBMS.ColorList = new arCtl.sLogMessageColor[0];
this.panel3.Name = "panel3"; this.rtBMS.DateFormat = "mm:ss.fff";
this.panel3.Size = new System.Drawing.Size(164, 143); this.rtBMS.DefaultColor = System.Drawing.Color.LightGray;
this.panel3.TabIndex = 5; this.rtBMS.Dock = System.Windows.Forms.DockStyle.Fill;
this.rtBMS.EnableDisplayTimer = false;
this.rtBMS.EnableGubunColor = true;
this.rtBMS.Font = new System.Drawing.Font("맑은 고딕", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.rtBMS.ListFormat = "[{0}] {1}";
this.rtBMS.Location = new System.Drawing.Point(0, 14);
this.rtBMS.MaxListCount = ((ushort)(1000));
this.rtBMS.MaxTextLength = ((uint)(400000u));
this.rtBMS.MessageInterval = 50;
this.rtBMS.Name = "rtBMS";
this.rtBMS.Size = new System.Drawing.Size(335, 129);
this.rtBMS.TabIndex = 2;
this.rtBMS.Text = "";
//
// titleBMS
//
this.titleBMS.Dock = System.Windows.Forms.DockStyle.Top;
this.titleBMS.Location = new System.Drawing.Point(0, 0);
this.titleBMS.Name = "titleBMS";
this.titleBMS.Size = new System.Drawing.Size(335, 14);
this.titleBMS.TabIndex = 3;
this.titleBMS.Text = "BMS";
this.titleBMS.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
// //
// panel4 // panel4
// //
this.panel4.Controls.Add(this.rtAGV); this.panel4.Controls.Add(this.rtAGV);
this.panel4.Controls.Add(this.label2); this.panel4.Controls.Add(this.titleAGV);
this.panel4.Dock = System.Windows.Forms.DockStyle.Fill; this.panel4.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel4.Location = new System.Drawing.Point(3, 349); this.panel4.Location = new System.Drawing.Point(3, 349);
this.panel4.Name = "panel4"; this.panel4.Name = "panel4";
this.panel4.Size = new System.Drawing.Size(164, 143); this.panel4.Size = new System.Drawing.Size(164, 143);
this.panel4.TabIndex = 6; this.panel4.TabIndex = 6;
// //
// label2 // rtAGV
// //
this.label2.Dock = System.Windows.Forms.DockStyle.Top; this.rtAGV.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.label2.Location = new System.Drawing.Point(0, 0); this.rtAGV.ColorList = new arCtl.sLogMessageColor[0];
this.label2.Name = "label2"; this.rtAGV.DateFormat = "mm:ss.fff";
this.label2.Size = new System.Drawing.Size(164, 14); this.rtAGV.DefaultColor = System.Drawing.Color.LightGray;
this.label2.TabIndex = 3; this.rtAGV.Dock = System.Windows.Forms.DockStyle.Fill;
this.label2.Text = "AGV"; this.rtAGV.EnableDisplayTimer = false;
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.rtAGV.EnableGubunColor = true;
this.rtAGV.Font = new System.Drawing.Font("맑은 고딕", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.rtAGV.ListFormat = "[{0}] {1}";
this.rtAGV.Location = new System.Drawing.Point(0, 14);
this.rtAGV.MaxListCount = ((ushort)(1000));
this.rtAGV.MaxTextLength = ((uint)(400000u));
this.rtAGV.MessageInterval = 50;
this.rtAGV.Name = "rtAGV";
this.rtAGV.Size = new System.Drawing.Size(164, 129);
this.rtAGV.TabIndex = 2;
this.rtAGV.Text = "";
// //
// label3 // titleAGV
// //
this.label3.Dock = System.Windows.Forms.DockStyle.Top; this.titleAGV.Dock = System.Windows.Forms.DockStyle.Top;
this.label3.Location = new System.Drawing.Point(0, 0); this.titleAGV.Location = new System.Drawing.Point(0, 0);
this.label3.Name = "label3"; this.titleAGV.Name = "titleAGV";
this.label3.Size = new System.Drawing.Size(165, 14); this.titleAGV.Size = new System.Drawing.Size(164, 14);
this.label3.TabIndex = 3; this.titleAGV.TabIndex = 3;
this.label3.Text = "BMS"; this.titleAGV.Text = "AGV";
this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.titleAGV.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
// //
// fLog // fLog
// //
@@ -267,11 +258,10 @@
private arCtl.LogTextBox rtBMS; private arCtl.LogTextBox rtBMS;
private arCtl.LogTextBox rtXbee; private arCtl.LogTextBox rtXbee;
private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Label label1; private System.Windows.Forms.Label titleXBEE;
private System.Windows.Forms.Panel panel2; private System.Windows.Forms.Panel panel2;
private System.Windows.Forms.Panel panel3;
private System.Windows.Forms.Panel panel4; private System.Windows.Forms.Panel panel4;
private System.Windows.Forms.Label label3; private System.Windows.Forms.Label titleBMS;
private System.Windows.Forms.Label label2; private System.Windows.Forms.Label titleAGV;
} }
} }

View File

@@ -29,7 +29,9 @@ namespace Project.Dialog
private void fLog_Load(object sender, EventArgs e) private void fLog_Load(object sender, EventArgs e)
{ {
this.label1.Text = $"XBEE:{PUB.setting.XBE_ID}"; this.titleXBEE.Text = $"XBEE({PUB.setting.Port_XBE},ID:{PUB.setting.XBE_ID})";
this.titleAGV.Text = $"AGV({PUB.setting.Port_AGV}:{PUB.setting.Baud_AGV})";
this.titleBMS.Text = $"BMS({PUB.setting.Port_BAT}:{PUB.setting.Baud_BAT})";
var colorlist = new arCtl.sLogMessageColor[] var colorlist = new arCtl.sLogMessageColor[]
{ {
new arCtl.sLogMessageColor("NOR",Color.Black), new arCtl.sLogMessageColor("NOR",Color.Black),

View File

@@ -28,530 +28,307 @@
/// </summary> /// </summary>
private void InitializeComponent() private void InitializeComponent()
{ {
this.lbMsg = new arCtl.arLabel(); this.btOpenDir = new System.Windows.Forms.Button();
this.arLabel4 = new arCtl.arLabel(); this.arLabel4 = new System.Windows.Forms.Button();
this.arLabel9 = new arCtl.arLabel(); this.btShutdown = new System.Windows.Forms.Button();
this.arLabel10 = new arCtl.arLabel(); this.btRestart = new System.Windows.Forms.Button();
this.arLabel1 = new arCtl.arLabel(); this.btStartMenu = new System.Windows.Forms.Button();
this.arLabel6 = new arCtl.arLabel(); this.btTaskMgr = new System.Windows.Forms.Button();
this.arLabel2 = new arCtl.arLabel(); this.btProcessList = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label();
this.arLabel3 = new arCtl.arLabel(); this.btEmulator = new System.Windows.Forms.Button();
this.arLabel5 = new arCtl.arLabel(); this.btMakePatch = new System.Windows.Forms.Button();
this.arLabel7 = new arCtl.arLabel(); this.btAutoRestart = new System.Windows.Forms.Button();
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.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// lbMsg // btOpenDir
// //
this.lbMsg.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72))))); this.btOpenDir.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
this.lbMsg.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(18)))), ((int)(((byte)(18)))), ((int)(((byte)(18))))); this.btOpenDir.Cursor = System.Windows.Forms.Cursors.Hand;
this.lbMsg.BackgroundImagePadding = new System.Windows.Forms.Padding(0); this.btOpenDir.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbMsg.BorderColor = System.Drawing.Color.LightSkyBlue; this.btOpenDir.Font = new System.Drawing.Font("Consolas", 12F);
this.lbMsg.BorderColorOver = System.Drawing.Color.Red; this.btOpenDir.ForeColor = System.Drawing.Color.White;
this.lbMsg.BorderSize = new System.Windows.Forms.Padding(1); this.btOpenDir.Location = new System.Drawing.Point(5, 5);
this.lbMsg.ColorTheme = arCtl.arLabel.eColorTheme.Custom; this.btOpenDir.Margin = new System.Windows.Forms.Padding(5);
this.lbMsg.Cursor = System.Windows.Forms.Cursors.Hand; this.btOpenDir.Name = "btOpenDir";
this.lbMsg.Font = new System.Drawing.Font("Consolas", 12F); this.btOpenDir.Size = new System.Drawing.Size(201, 79);
this.lbMsg.ForeColor = System.Drawing.Color.White; this.btOpenDir.TabIndex = 2;
this.lbMsg.GradientEnable = true; this.btOpenDir.Text = "폴더열기";
this.lbMsg.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical; this.btOpenDir.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbMsg.GradientRepeatBG = true; this.btOpenDir.Click += new System.EventHandler(this.lbMsg_Click);
this.lbMsg.isButton = true;
this.lbMsg.Location = new System.Drawing.Point(9, 8);
this.lbMsg.Margin = new System.Windows.Forms.Padding(5);
this.lbMsg.MouseDownColor = System.Drawing.Color.Yellow;
this.lbMsg.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.lbMsg.msg = null;
this.lbMsg.Name = "lbMsg";
this.lbMsg.ProgressBorderColor = System.Drawing.Color.Black;
this.lbMsg.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.lbMsg.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.lbMsg.ProgressEnable = false;
this.lbMsg.ProgressFont = new System.Drawing.Font("Consolas", 10F);
this.lbMsg.ProgressForeColor = System.Drawing.Color.Black;
this.lbMsg.ProgressMax = 100F;
this.lbMsg.ProgressMin = 0F;
this.lbMsg.ProgressPadding = new System.Windows.Forms.Padding(0);
this.lbMsg.ProgressValue = 0F;
this.lbMsg.ShadowColor = System.Drawing.Color.Black;
this.lbMsg.Sign = "";
this.lbMsg.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.lbMsg.SignColor = System.Drawing.Color.Yellow;
this.lbMsg.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.lbMsg.Size = new System.Drawing.Size(165, 100);
this.lbMsg.TabIndex = 2;
this.lbMsg.Text = "폴더열기";
this.lbMsg.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbMsg.TextShadow = true;
this.lbMsg.TextVisible = true;
this.lbMsg.Click += new System.EventHandler(this.lbMsg_Click);
// //
// arLabel4 // arLabel4
// //
this.arLabel4.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72))))); this.arLabel4.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
this.arLabel4.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(18)))), ((int)(((byte)(18)))), ((int)(((byte)(18)))));
this.arLabel4.BackgroundImagePadding = new System.Windows.Forms.Padding(0);
this.arLabel4.BorderColor = System.Drawing.Color.LightSkyBlue;
this.arLabel4.BorderColorOver = System.Drawing.Color.Red;
this.arLabel4.BorderSize = new System.Windows.Forms.Padding(1);
this.arLabel4.ColorTheme = arCtl.arLabel.eColorTheme.Custom;
this.arLabel4.Cursor = System.Windows.Forms.Cursors.Hand; this.arLabel4.Cursor = System.Windows.Forms.Cursors.Hand;
this.arLabel4.Dock = System.Windows.Forms.DockStyle.Bottom;
this.arLabel4.Font = new System.Drawing.Font("Consolas", 12F); this.arLabel4.Font = new System.Drawing.Font("Consolas", 12F);
this.arLabel4.ForeColor = System.Drawing.Color.White; this.arLabel4.ForeColor = System.Drawing.Color.White;
this.arLabel4.GradientEnable = true; this.arLabel4.Location = new System.Drawing.Point(10, 412);
this.arLabel4.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
this.arLabel4.GradientRepeatBG = true;
this.arLabel4.isButton = true;
this.arLabel4.Location = new System.Drawing.Point(9, 387);
this.arLabel4.Margin = new System.Windows.Forms.Padding(5); this.arLabel4.Margin = new System.Windows.Forms.Padding(5);
this.arLabel4.MouseDownColor = System.Drawing.Color.Yellow;
this.arLabel4.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.arLabel4.msg = null;
this.arLabel4.Name = "arLabel4"; this.arLabel4.Name = "arLabel4";
this.arLabel4.ProgressBorderColor = System.Drawing.Color.Black; this.arLabel4.Size = new System.Drawing.Size(633, 51);
this.arLabel4.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arLabel4.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arLabel4.ProgressEnable = false;
this.arLabel4.ProgressFont = new System.Drawing.Font("Consolas", 10F);
this.arLabel4.ProgressForeColor = System.Drawing.Color.Black;
this.arLabel4.ProgressMax = 100F;
this.arLabel4.ProgressMin = 0F;
this.arLabel4.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arLabel4.ProgressValue = 0F;
this.arLabel4.ShadowColor = System.Drawing.Color.Black;
this.arLabel4.Sign = "";
this.arLabel4.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.arLabel4.SignColor = System.Drawing.Color.Yellow;
this.arLabel4.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.arLabel4.Size = new System.Drawing.Size(503, 51);
this.arLabel4.TabIndex = 2; this.arLabel4.TabIndex = 2;
this.arLabel4.Text = "닫기"; this.arLabel4.Text = "닫기";
this.arLabel4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.arLabel4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel4.TextShadow = true;
this.arLabel4.TextVisible = true;
this.arLabel4.Click += new System.EventHandler(this.arLabel4_Click); this.arLabel4.Click += new System.EventHandler(this.arLabel4_Click);
// //
// arLabel9 // btShutdown
// //
this.arLabel9.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72))))); this.btShutdown.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
this.arLabel9.BackColor2 = System.Drawing.Color.Red; this.btShutdown.Cursor = System.Windows.Forms.Cursors.Hand;
this.arLabel9.BackgroundImagePadding = new System.Windows.Forms.Padding(0); this.btShutdown.Dock = System.Windows.Forms.DockStyle.Fill;
this.arLabel9.BorderColor = System.Drawing.Color.LightSkyBlue; this.btShutdown.Font = new System.Drawing.Font("Consolas", 12F);
this.arLabel9.BorderColorOver = System.Drawing.Color.Red; this.btShutdown.ForeColor = System.Drawing.Color.White;
this.arLabel9.BorderSize = new System.Windows.Forms.Padding(1); this.btShutdown.Location = new System.Drawing.Point(5, 94);
this.arLabel9.ColorTheme = arCtl.arLabel.eColorTheme.Custom; this.btShutdown.Margin = new System.Windows.Forms.Padding(5);
this.arLabel9.Cursor = System.Windows.Forms.Cursors.Hand; this.btShutdown.Name = "btShutdown";
this.arLabel9.Font = new System.Drawing.Font("Consolas", 12F); this.btShutdown.Size = new System.Drawing.Size(201, 79);
this.arLabel9.ForeColor = System.Drawing.Color.White; this.btShutdown.TabIndex = 2;
this.arLabel9.GradientEnable = true; this.btShutdown.Text = "시스템 종료";
this.arLabel9.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical; this.btShutdown.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel9.GradientRepeatBG = true; this.btShutdown.Click += new System.EventHandler(this.arLabel9_Click);
this.arLabel9.isButton = true;
this.arLabel9.Location = new System.Drawing.Point(9, 112);
this.arLabel9.Margin = new System.Windows.Forms.Padding(5);
this.arLabel9.MouseDownColor = System.Drawing.Color.Yellow;
this.arLabel9.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.arLabel9.msg = null;
this.arLabel9.Name = "arLabel9";
this.arLabel9.ProgressBorderColor = System.Drawing.Color.Black;
this.arLabel9.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arLabel9.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arLabel9.ProgressEnable = false;
this.arLabel9.ProgressFont = new System.Drawing.Font("Consolas", 10F);
this.arLabel9.ProgressForeColor = System.Drawing.Color.Black;
this.arLabel9.ProgressMax = 100F;
this.arLabel9.ProgressMin = 0F;
this.arLabel9.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arLabel9.ProgressValue = 0F;
this.arLabel9.ShadowColor = System.Drawing.Color.Black;
this.arLabel9.Sign = "";
this.arLabel9.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.arLabel9.SignColor = System.Drawing.Color.Yellow;
this.arLabel9.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.arLabel9.Size = new System.Drawing.Size(165, 100);
this.arLabel9.TabIndex = 2;
this.arLabel9.Text = "시스템 종료";
this.arLabel9.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel9.TextShadow = true;
this.arLabel9.TextVisible = true;
this.arLabel9.Click += new System.EventHandler(this.arLabel9_Click);
// //
// arLabel10 // btRestart
// //
this.arLabel10.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72))))); this.btRestart.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
this.arLabel10.BackColor2 = System.Drawing.Color.DarkBlue; this.btRestart.Cursor = System.Windows.Forms.Cursors.Hand;
this.arLabel10.BackgroundImagePadding = new System.Windows.Forms.Padding(0); this.btRestart.Dock = System.Windows.Forms.DockStyle.Fill;
this.arLabel10.BorderColor = System.Drawing.Color.LightSkyBlue; this.btRestart.Font = new System.Drawing.Font("Consolas", 12F);
this.arLabel10.BorderColorOver = System.Drawing.Color.Red; this.btRestart.ForeColor = System.Drawing.Color.White;
this.arLabel10.BorderSize = new System.Windows.Forms.Padding(1); this.btRestart.Location = new System.Drawing.Point(216, 94);
this.arLabel10.ColorTheme = arCtl.arLabel.eColorTheme.Custom; this.btRestart.Margin = new System.Windows.Forms.Padding(5);
this.arLabel10.Cursor = System.Windows.Forms.Cursors.Hand; this.btRestart.Name = "btRestart";
this.arLabel10.Font = new System.Drawing.Font("Consolas", 12F); this.btRestart.Size = new System.Drawing.Size(201, 79);
this.arLabel10.ForeColor = System.Drawing.Color.White; this.btRestart.TabIndex = 2;
this.arLabel10.GradientEnable = true; this.btRestart.Text = "시스템 재시작";
this.arLabel10.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical; this.btRestart.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel10.GradientRepeatBG = true; this.btRestart.Click += new System.EventHandler(this.arLabel10_Click);
this.arLabel10.isButton = true;
this.arLabel10.Location = new System.Drawing.Point(178, 112);
this.arLabel10.Margin = new System.Windows.Forms.Padding(5);
this.arLabel10.MouseDownColor = System.Drawing.Color.Yellow;
this.arLabel10.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.arLabel10.msg = null;
this.arLabel10.Name = "arLabel10";
this.arLabel10.ProgressBorderColor = System.Drawing.Color.Black;
this.arLabel10.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arLabel10.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arLabel10.ProgressEnable = false;
this.arLabel10.ProgressFont = new System.Drawing.Font("Consolas", 10F);
this.arLabel10.ProgressForeColor = System.Drawing.Color.Black;
this.arLabel10.ProgressMax = 100F;
this.arLabel10.ProgressMin = 0F;
this.arLabel10.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arLabel10.ProgressValue = 0F;
this.arLabel10.ShadowColor = System.Drawing.Color.Black;
this.arLabel10.Sign = "";
this.arLabel10.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.arLabel10.SignColor = System.Drawing.Color.Yellow;
this.arLabel10.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.arLabel10.Size = new System.Drawing.Size(165, 100);
this.arLabel10.TabIndex = 2;
this.arLabel10.Text = "시스템 재시작";
this.arLabel10.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel10.TextShadow = true;
this.arLabel10.TextVisible = true;
this.arLabel10.Click += new System.EventHandler(this.arLabel10_Click);
// //
// arLabel1 // btStartMenu
// //
this.arLabel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72))))); this.btStartMenu.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
this.arLabel1.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(18)))), ((int)(((byte)(18)))), ((int)(((byte)(18))))); this.btStartMenu.Cursor = System.Windows.Forms.Cursors.Hand;
this.arLabel1.BackgroundImagePadding = new System.Windows.Forms.Padding(0); this.btStartMenu.Dock = System.Windows.Forms.DockStyle.Fill;
this.arLabel1.BorderColor = System.Drawing.Color.LightSkyBlue; this.btStartMenu.Font = new System.Drawing.Font("Consolas", 12F);
this.arLabel1.BorderColorOver = System.Drawing.Color.Red; this.btStartMenu.ForeColor = System.Drawing.Color.White;
this.arLabel1.BorderSize = new System.Windows.Forms.Padding(1); this.btStartMenu.Location = new System.Drawing.Point(427, 5);
this.arLabel1.ColorTheme = arCtl.arLabel.eColorTheme.Custom; this.btStartMenu.Margin = new System.Windows.Forms.Padding(5);
this.arLabel1.Cursor = System.Windows.Forms.Cursors.Hand; this.btStartMenu.Name = "btStartMenu";
this.arLabel1.Font = new System.Drawing.Font("Consolas", 12F); this.btStartMenu.Size = new System.Drawing.Size(201, 79);
this.arLabel1.ForeColor = System.Drawing.Color.White; this.btStartMenu.TabIndex = 2;
this.arLabel1.GradientEnable = true; this.btStartMenu.Text = "시작메뉴";
this.arLabel1.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical; this.btStartMenu.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel1.GradientRepeatBG = true; this.btStartMenu.Click += new System.EventHandler(this.arLabel1_Click);
this.arLabel1.isButton = true;
this.arLabel1.Location = new System.Drawing.Point(347, 8);
this.arLabel1.Margin = new System.Windows.Forms.Padding(5);
this.arLabel1.MouseDownColor = System.Drawing.Color.Yellow;
this.arLabel1.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.arLabel1.msg = null;
this.arLabel1.Name = "arLabel1";
this.arLabel1.ProgressBorderColor = System.Drawing.Color.Black;
this.arLabel1.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arLabel1.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arLabel1.ProgressEnable = false;
this.arLabel1.ProgressFont = new System.Drawing.Font("Consolas", 10F);
this.arLabel1.ProgressForeColor = System.Drawing.Color.Black;
this.arLabel1.ProgressMax = 100F;
this.arLabel1.ProgressMin = 0F;
this.arLabel1.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arLabel1.ProgressValue = 0F;
this.arLabel1.ShadowColor = System.Drawing.Color.Black;
this.arLabel1.Sign = "";
this.arLabel1.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.arLabel1.SignColor = System.Drawing.Color.Yellow;
this.arLabel1.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.arLabel1.Size = new System.Drawing.Size(165, 100);
this.arLabel1.TabIndex = 2;
this.arLabel1.Text = "시작메뉴";
this.arLabel1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel1.TextShadow = true;
this.arLabel1.TextVisible = true;
this.arLabel1.Click += new System.EventHandler(this.arLabel1_Click);
// //
// arLabel6 // btTaskMgr
// //
this.arLabel6.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72))))); this.btTaskMgr.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
this.arLabel6.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(18)))), ((int)(((byte)(18)))), ((int)(((byte)(18))))); this.btTaskMgr.Cursor = System.Windows.Forms.Cursors.Hand;
this.arLabel6.BackgroundImagePadding = new System.Windows.Forms.Padding(0); this.btTaskMgr.Dock = System.Windows.Forms.DockStyle.Fill;
this.arLabel6.BorderColor = System.Drawing.Color.LightSkyBlue; this.btTaskMgr.Font = new System.Drawing.Font("Consolas", 12F);
this.arLabel6.BorderColorOver = System.Drawing.Color.Red; this.btTaskMgr.ForeColor = System.Drawing.Color.White;
this.arLabel6.BorderSize = new System.Windows.Forms.Padding(1); this.btTaskMgr.Location = new System.Drawing.Point(216, 5);
this.arLabel6.ColorTheme = arCtl.arLabel.eColorTheme.Custom; this.btTaskMgr.Margin = new System.Windows.Forms.Padding(5);
this.arLabel6.Cursor = System.Windows.Forms.Cursors.Hand; this.btTaskMgr.Name = "btTaskMgr";
this.arLabel6.Font = new System.Drawing.Font("Consolas", 12F); this.btTaskMgr.Size = new System.Drawing.Size(201, 79);
this.arLabel6.ForeColor = System.Drawing.Color.White; this.btTaskMgr.TabIndex = 2;
this.arLabel6.GradientEnable = true; this.btTaskMgr.Text = "작업관리자";
this.arLabel6.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical; this.btTaskMgr.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel6.GradientRepeatBG = true; this.btTaskMgr.Click += new System.EventHandler(this.arLabel6_Click);
this.arLabel6.isButton = true;
this.arLabel6.Location = new System.Drawing.Point(178, 8);
this.arLabel6.Margin = new System.Windows.Forms.Padding(5);
this.arLabel6.MouseDownColor = System.Drawing.Color.Yellow;
this.arLabel6.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.arLabel6.msg = null;
this.arLabel6.Name = "arLabel6";
this.arLabel6.ProgressBorderColor = System.Drawing.Color.Black;
this.arLabel6.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arLabel6.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arLabel6.ProgressEnable = false;
this.arLabel6.ProgressFont = new System.Drawing.Font("Consolas", 10F);
this.arLabel6.ProgressForeColor = System.Drawing.Color.Black;
this.arLabel6.ProgressMax = 100F;
this.arLabel6.ProgressMin = 0F;
this.arLabel6.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arLabel6.ProgressValue = 0F;
this.arLabel6.ShadowColor = System.Drawing.Color.Black;
this.arLabel6.Sign = "";
this.arLabel6.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.arLabel6.SignColor = System.Drawing.Color.Yellow;
this.arLabel6.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.arLabel6.Size = new System.Drawing.Size(165, 100);
this.arLabel6.TabIndex = 2;
this.arLabel6.Text = "작업관리자";
this.arLabel6.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel6.TextShadow = true;
this.arLabel6.TextVisible = true;
this.arLabel6.Click += new System.EventHandler(this.arLabel6_Click);
// //
// arLabel2 // btProcessList
// //
this.arLabel2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72))))); this.btProcessList.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
this.arLabel2.BackColor2 = System.Drawing.Color.DarkBlue; this.btProcessList.Cursor = System.Windows.Forms.Cursors.Hand;
this.arLabel2.BackgroundImagePadding = new System.Windows.Forms.Padding(0); this.btProcessList.Dock = System.Windows.Forms.DockStyle.Fill;
this.arLabel2.BorderColor = System.Drawing.Color.LightSkyBlue; this.btProcessList.Font = new System.Drawing.Font("Consolas", 12F);
this.arLabel2.BorderColorOver = System.Drawing.Color.Red; this.btProcessList.ForeColor = System.Drawing.Color.White;
this.arLabel2.BorderSize = new System.Windows.Forms.Padding(1); this.btProcessList.Location = new System.Drawing.Point(427, 94);
this.arLabel2.ColorTheme = arCtl.arLabel.eColorTheme.Custom; this.btProcessList.Margin = new System.Windows.Forms.Padding(5);
this.arLabel2.Cursor = System.Windows.Forms.Cursors.Hand; this.btProcessList.Name = "btProcessList";
this.arLabel2.Font = new System.Drawing.Font("Consolas", 12F); this.btProcessList.Size = new System.Drawing.Size(201, 79);
this.arLabel2.ForeColor = System.Drawing.Color.White; this.btProcessList.TabIndex = 2;
this.arLabel2.GradientEnable = true; this.btProcessList.Text = "Process List";
this.arLabel2.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical; this.btProcessList.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel2.GradientRepeatBG = true; this.btProcessList.Click += new System.EventHandler(this.arLabel2_Click);
this.arLabel2.isButton = true;
this.arLabel2.Location = new System.Drawing.Point(347, 112);
this.arLabel2.Margin = new System.Windows.Forms.Padding(5);
this.arLabel2.MouseDownColor = System.Drawing.Color.Yellow;
this.arLabel2.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.arLabel2.msg = null;
this.arLabel2.Name = "arLabel2";
this.arLabel2.ProgressBorderColor = System.Drawing.Color.Black;
this.arLabel2.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arLabel2.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arLabel2.ProgressEnable = false;
this.arLabel2.ProgressFont = new System.Drawing.Font("Consolas", 10F);
this.arLabel2.ProgressForeColor = System.Drawing.Color.Black;
this.arLabel2.ProgressMax = 100F;
this.arLabel2.ProgressMin = 0F;
this.arLabel2.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arLabel2.ProgressValue = 0F;
this.arLabel2.ShadowColor = System.Drawing.Color.Black;
this.arLabel2.Sign = "";
this.arLabel2.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.arLabel2.SignColor = System.Drawing.Color.Yellow;
this.arLabel2.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.arLabel2.Size = new System.Drawing.Size(165, 100);
this.arLabel2.TabIndex = 2;
this.arLabel2.Text = "Process List";
this.arLabel2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel2.TextShadow = true;
this.arLabel2.TextVisible = true;
this.arLabel2.Click += new System.EventHandler(this.arLabel2_Click);
// //
// label1 // label1
// //
this.label1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.label1.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129))); this.label1.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label1.ForeColor = System.Drawing.Color.White; this.label1.ForeColor = System.Drawing.Color.White;
this.label1.Location = new System.Drawing.Point(9, 327); this.label1.Location = new System.Drawing.Point(10, 389);
this.label1.Name = "label1"; this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(515, 23); this.label1.Size = new System.Drawing.Size(633, 23);
this.label1.TabIndex = 3; this.label1.TabIndex = 3;
this.label1.Text = "label1"; this.label1.Text = "label1";
// //
// label2 // label2
// //
this.label2.Dock = System.Windows.Forms.DockStyle.Bottom;
this.label2.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129))); this.label2.Font = new System.Drawing.Font("맑은 고딕", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label2.ForeColor = System.Drawing.Color.White; this.label2.ForeColor = System.Drawing.Color.White;
this.label2.Location = new System.Drawing.Point(9, 359); this.label2.Location = new System.Drawing.Point(10, 366);
this.label2.Name = "label2"; this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(515, 23); this.label2.Size = new System.Drawing.Size(633, 23);
this.label2.TabIndex = 3; this.label2.TabIndex = 3;
this.label2.Text = "label1"; this.label2.Text = "label1";
// //
// arLabel3 // btEmulator
// //
this.arLabel3.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72))))); this.btEmulator.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
this.arLabel3.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(18)))), ((int)(((byte)(18)))), ((int)(((byte)(18))))); this.btEmulator.Cursor = System.Windows.Forms.Cursors.Hand;
this.arLabel3.BackgroundImagePadding = new System.Windows.Forms.Padding(0); this.btEmulator.Dock = System.Windows.Forms.DockStyle.Fill;
this.arLabel3.BorderColor = System.Drawing.Color.LightSkyBlue; this.btEmulator.Font = new System.Drawing.Font("Consolas", 12F);
this.arLabel3.BorderColorOver = System.Drawing.Color.Red; this.btEmulator.ForeColor = System.Drawing.Color.White;
this.arLabel3.BorderSize = new System.Windows.Forms.Padding(1); this.btEmulator.Location = new System.Drawing.Point(5, 183);
this.arLabel3.ColorTheme = arCtl.arLabel.eColorTheme.Custom; this.btEmulator.Margin = new System.Windows.Forms.Padding(5);
this.arLabel3.Cursor = System.Windows.Forms.Cursors.Hand; this.btEmulator.Name = "btEmulator";
this.arLabel3.Font = new System.Drawing.Font("Consolas", 12F); this.btEmulator.Size = new System.Drawing.Size(201, 79);
this.arLabel3.ForeColor = System.Drawing.Color.White; this.btEmulator.TabIndex = 4;
this.arLabel3.GradientEnable = true; this.btEmulator.Text = "Emulator";
this.arLabel3.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical; this.btEmulator.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel3.GradientRepeatBG = true; this.btEmulator.Click += new System.EventHandler(this.arLabel3_Click);
this.arLabel3.isButton = true;
this.arLabel3.Location = new System.Drawing.Point(9, 216);
this.arLabel3.Margin = new System.Windows.Forms.Padding(5);
this.arLabel3.MouseDownColor = System.Drawing.Color.Yellow;
this.arLabel3.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.arLabel3.msg = null;
this.arLabel3.Name = "arLabel3";
this.arLabel3.ProgressBorderColor = System.Drawing.Color.Black;
this.arLabel3.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arLabel3.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arLabel3.ProgressEnable = false;
this.arLabel3.ProgressFont = new System.Drawing.Font("Consolas", 10F);
this.arLabel3.ProgressForeColor = System.Drawing.Color.Black;
this.arLabel3.ProgressMax = 100F;
this.arLabel3.ProgressMin = 0F;
this.arLabel3.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arLabel3.ProgressValue = 0F;
this.arLabel3.ShadowColor = System.Drawing.Color.Black;
this.arLabel3.Sign = "";
this.arLabel3.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.arLabel3.SignColor = System.Drawing.Color.Yellow;
this.arLabel3.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.arLabel3.Size = new System.Drawing.Size(165, 100);
this.arLabel3.TabIndex = 4;
this.arLabel3.Text = "Emulator";
this.arLabel3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel3.TextShadow = true;
this.arLabel3.TextVisible = true;
this.arLabel3.Click += new System.EventHandler(this.arLabel3_Click);
// //
// arLabel5 // btMakePatch
// //
this.arLabel5.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72))))); this.btMakePatch.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
this.arLabel5.BackColor2 = System.Drawing.Color.Pink; this.btMakePatch.Cursor = System.Windows.Forms.Cursors.Hand;
this.arLabel5.BackgroundImagePadding = new System.Windows.Forms.Padding(0); this.btMakePatch.Dock = System.Windows.Forms.DockStyle.Fill;
this.arLabel5.BorderColor = System.Drawing.Color.LightSkyBlue; this.btMakePatch.Font = new System.Drawing.Font("Consolas", 12F);
this.arLabel5.BorderColorOver = System.Drawing.Color.Red; this.btMakePatch.ForeColor = System.Drawing.Color.White;
this.arLabel5.BorderSize = new System.Windows.Forms.Padding(1); this.btMakePatch.Location = new System.Drawing.Point(216, 183);
this.arLabel5.ColorTheme = arCtl.arLabel.eColorTheme.Custom; this.btMakePatch.Margin = new System.Windows.Forms.Padding(5);
this.arLabel5.Cursor = System.Windows.Forms.Cursors.Hand; this.btMakePatch.Name = "btMakePatch";
this.arLabel5.Font = new System.Drawing.Font("Consolas", 12F); this.btMakePatch.Size = new System.Drawing.Size(201, 79);
this.arLabel5.ForeColor = System.Drawing.Color.White; this.btMakePatch.TabIndex = 5;
this.arLabel5.GradientEnable = true; this.btMakePatch.Text = "패치파일 생성";
this.arLabel5.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical; this.btMakePatch.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel5.GradientRepeatBG = true; this.btMakePatch.Click += new System.EventHandler(this.arLabel5_Click);
this.arLabel5.isButton = true;
this.arLabel5.Location = new System.Drawing.Point(178, 216);
this.arLabel5.Margin = new System.Windows.Forms.Padding(5);
this.arLabel5.MouseDownColor = System.Drawing.Color.Yellow;
this.arLabel5.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.arLabel5.msg = null;
this.arLabel5.Name = "arLabel5";
this.arLabel5.ProgressBorderColor = System.Drawing.Color.Black;
this.arLabel5.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arLabel5.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arLabel5.ProgressEnable = false;
this.arLabel5.ProgressFont = new System.Drawing.Font("Consolas", 10F);
this.arLabel5.ProgressForeColor = System.Drawing.Color.Black;
this.arLabel5.ProgressMax = 100F;
this.arLabel5.ProgressMin = 0F;
this.arLabel5.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arLabel5.ProgressValue = 0F;
this.arLabel5.ShadowColor = System.Drawing.Color.Black;
this.arLabel5.Sign = "";
this.arLabel5.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.arLabel5.SignColor = System.Drawing.Color.Yellow;
this.arLabel5.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.arLabel5.Size = new System.Drawing.Size(165, 100);
this.arLabel5.TabIndex = 5;
this.arLabel5.Text = "패치파일 생성";
this.arLabel5.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel5.TextShadow = true;
this.arLabel5.TextVisible = true;
this.arLabel5.Click += new System.EventHandler(this.arLabel5_Click);
// //
// arLabel7 // btAutoRestart
// //
this.arLabel7.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72))))); this.btAutoRestart.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(72)))), ((int)(((byte)(72)))));
this.arLabel7.BackColor2 = System.Drawing.Color.DarkBlue; this.btAutoRestart.Cursor = System.Windows.Forms.Cursors.Hand;
this.arLabel7.BackgroundImagePadding = new System.Windows.Forms.Padding(0); this.btAutoRestart.Dock = System.Windows.Forms.DockStyle.Fill;
this.arLabel7.BorderColor = System.Drawing.Color.LightSkyBlue; this.btAutoRestart.Font = new System.Drawing.Font("Consolas", 12F);
this.arLabel7.BorderColorOver = System.Drawing.Color.Red; this.btAutoRestart.ForeColor = System.Drawing.Color.White;
this.arLabel7.BorderSize = new System.Windows.Forms.Padding(1); this.btAutoRestart.Location = new System.Drawing.Point(427, 183);
this.arLabel7.ColorTheme = arCtl.arLabel.eColorTheme.Custom; this.btAutoRestart.Margin = new System.Windows.Forms.Padding(5);
this.arLabel7.Cursor = System.Windows.Forms.Cursors.Hand; this.btAutoRestart.Name = "btAutoRestart";
this.arLabel7.Font = new System.Drawing.Font("Consolas", 12F); this.btAutoRestart.Size = new System.Drawing.Size(201, 79);
this.arLabel7.ForeColor = System.Drawing.Color.White; this.btAutoRestart.TabIndex = 6;
this.arLabel7.GradientEnable = true; this.btAutoRestart.Text = "자동 재시작";
this.arLabel7.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical; this.btAutoRestart.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.arLabel7.GradientRepeatBG = true; this.btAutoRestart.Click += new System.EventHandler(this.arLabel7_Click);
this.arLabel7.isButton = true; //
this.arLabel7.Location = new System.Drawing.Point(347, 216); // tableLayoutPanel1
this.arLabel7.Margin = new System.Windows.Forms.Padding(5); //
this.arLabel7.MouseDownColor = System.Drawing.Color.Yellow; this.tableLayoutPanel1.ColumnCount = 3;
this.arLabel7.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
this.arLabel7.msg = null; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
this.arLabel7.Name = "arLabel7"; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
this.arLabel7.ProgressBorderColor = System.Drawing.Color.Black; this.tableLayoutPanel1.Controls.Add(this.btOpenDir, 0, 0);
this.arLabel7.ProgressColor1 = System.Drawing.Color.LightSkyBlue; this.tableLayoutPanel1.Controls.Add(this.btAutoRestart, 2, 2);
this.arLabel7.ProgressColor2 = System.Drawing.Color.DeepSkyBlue; this.tableLayoutPanel1.Controls.Add(this.btTaskMgr, 1, 0);
this.arLabel7.ProgressEnable = false; this.tableLayoutPanel1.Controls.Add(this.btMakePatch, 1, 2);
this.arLabel7.ProgressFont = new System.Drawing.Font("Consolas", 10F); this.tableLayoutPanel1.Controls.Add(this.btStartMenu, 2, 0);
this.arLabel7.ProgressForeColor = System.Drawing.Color.Black; this.tableLayoutPanel1.Controls.Add(this.btEmulator, 0, 2);
this.arLabel7.ProgressMax = 100F; this.tableLayoutPanel1.Controls.Add(this.btShutdown, 0, 1);
this.arLabel7.ProgressMin = 0F; this.tableLayoutPanel1.Controls.Add(this.btRestart, 1, 1);
this.arLabel7.ProgressPadding = new System.Windows.Forms.Padding(0); this.tableLayoutPanel1.Controls.Add(this.btProcessList, 2, 1);
this.arLabel7.ProgressValue = 0F; this.tableLayoutPanel1.Controls.Add(this.button1, 0, 3);
this.arLabel7.ShadowColor = System.Drawing.Color.Black; this.tableLayoutPanel1.Controls.Add(this.button2, 1, 3);
this.arLabel7.Sign = ""; this.tableLayoutPanel1.Controls.Add(this.button3, 2, 3);
this.arLabel7.SignAlign = System.Drawing.ContentAlignment.BottomRight; this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.arLabel7.SignColor = System.Drawing.Color.Yellow; this.tableLayoutPanel1.Location = new System.Drawing.Point(10, 10);
this.arLabel7.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic); this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.arLabel7.Size = new System.Drawing.Size(165, 100); this.tableLayoutPanel1.RowCount = 4;
this.arLabel7.TabIndex = 6; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.arLabel7.Text = "자동 재시작"; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.arLabel7.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.arLabel7.TextShadow = true; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.arLabel7.TextVisible = true; this.tableLayoutPanel1.Size = new System.Drawing.Size(633, 356);
this.arLabel7.Click += new System.EventHandler(this.arLabel7_Click); this.tableLayoutPanel1.TabIndex = 7;
//
// button1
//
this.button1.Dock = System.Windows.Forms.DockStyle.Fill;
this.button1.Location = new System.Drawing.Point(3, 270);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(205, 83);
this.button1.TabIndex = 7;
this.button1.Text = "Map Editor";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click_1);
//
// button2
//
this.button2.Dock = System.Windows.Forms.DockStyle.Fill;
this.button2.Enabled = false;
this.button2.Location = new System.Drawing.Point(214, 270);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(205, 83);
this.button2.TabIndex = 7;
this.button2.Text = "---";
this.button2.UseVisualStyleBackColor = true;
//
// button3
//
this.button3.Dock = System.Windows.Forms.DockStyle.Fill;
this.button3.Enabled = false;
this.button3.Location = new System.Drawing.Point(425, 270);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(205, 83);
this.button3.TabIndex = 7;
this.button3.Text = "---";
this.button3.UseVisualStyleBackColor = true;
// //
// fSystem // fSystem
// //
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.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40))))); this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
this.ClientSize = new System.Drawing.Size(519, 451); this.ClientSize = new System.Drawing.Size(653, 473);
this.Controls.Add(this.arLabel7); this.Controls.Add(this.tableLayoutPanel1);
this.Controls.Add(this.arLabel5);
this.Controls.Add(this.arLabel3);
this.Controls.Add(this.label2); this.Controls.Add(this.label2);
this.Controls.Add(this.label1); this.Controls.Add(this.label1);
this.Controls.Add(this.arLabel4); this.Controls.Add(this.arLabel4);
this.Controls.Add(this.lbMsg);
this.Controls.Add(this.arLabel9);
this.Controls.Add(this.arLabel10);
this.Controls.Add(this.arLabel1);
this.Controls.Add(this.arLabel2);
this.Controls.Add(this.arLabel6);
this.MaximizeBox = false; this.MaximizeBox = false;
this.MinimizeBox = false; this.MinimizeBox = false;
this.Name = "fSystem"; this.Name = "fSystem";
this.Padding = new System.Windows.Forms.Padding(10);
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "fSystem"; this.Text = "fSystem";
this.Load += new System.EventHandler(this.fSystem_Load); this.Load += new System.EventHandler(this.fSystem_Load);
this.tableLayoutPanel1.ResumeLayout(false);
this.ResumeLayout(false); this.ResumeLayout(false);
} }
#endregion #endregion
private arCtl.arLabel lbMsg; private System.Windows.Forms.Button btOpenDir;
private arCtl.arLabel arLabel4; private System.Windows.Forms.Button arLabel4;
private arCtl.arLabel arLabel6; private System.Windows.Forms.Button btTaskMgr;
private arCtl.arLabel arLabel9; private System.Windows.Forms.Button btShutdown;
private arCtl.arLabel arLabel10; private System.Windows.Forms.Button btRestart;
private arCtl.arLabel arLabel1; private System.Windows.Forms.Button btStartMenu;
private arCtl.arLabel arLabel2; private System.Windows.Forms.Button btProcessList;
private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label2;
private arCtl.arLabel arLabel3; private System.Windows.Forms.Button btEmulator;
private arCtl.arLabel arLabel5; private System.Windows.Forms.Button btMakePatch;
private arCtl.arLabel arLabel7; private System.Windows.Forms.Button btAutoRestart;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Button button3;
} }
} }

View File

@@ -154,5 +154,13 @@ namespace Project.Dialog
{ {
PUB.SystemReboot(5,true); PUB.SystemReboot(5,true);
} }
private void button1_Click_1(object sender, EventArgs e)
{
//mapeditor
var fn = new System.IO.FileInfo(@".\test\AGVMapEditor.exe");
if (fn.Exists == false) return;
UTIL.RunExplorer(fn.FullName);
}
} }
} }

View File

@@ -0,0 +1,239 @@
namespace Project.Dialog
{
partial class fXbeeSetting
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.button1 = new System.Windows.Forms.Button();
this.tbBaud = new System.Windows.Forms.TextBox();
this.tbPortName = new System.Windows.Forms.ComboBox();
this.panel1 = new System.Windows.Forms.Panel();
this.button2 = new System.Windows.Forms.Button();
this.btmy = new System.Windows.Forms.Button();
this.tbmy = new System.Windows.Forms.TextBox();
this.btch = new System.Windows.Forms.Button();
this.tbch = new System.Windows.Forms.TextBox();
this.btpand = new System.Windows.Forms.Button();
this.tbpanid = new System.Windows.Forms.TextBox();
this.panel2 = new System.Windows.Forms.Panel();
this.rtXbee = new arCtl.LogTextBox();
this.serialPort1 = new System.IO.Ports.SerialPort(this.components);
this.panel1.SuspendLayout();
this.panel2.SuspendLayout();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(25, 45);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(227, 26);
this.button1.TabIndex = 0;
this.button1.Text = "open/close";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// tbBaud
//
this.tbBaud.Font = new System.Drawing.Font("굴림", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.tbBaud.Location = new System.Drawing.Point(152, 13);
this.tbBaud.Name = "tbBaud";
this.tbBaud.Size = new System.Drawing.Size(100, 26);
this.tbBaud.TabIndex = 2;
this.tbBaud.Text = "9600";
this.tbBaud.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// tbPortName
//
this.tbPortName.Font = new System.Drawing.Font("굴림", 12F);
this.tbPortName.FormattingEnabled = true;
this.tbPortName.Location = new System.Drawing.Point(25, 15);
this.tbPortName.Name = "tbPortName";
this.tbPortName.Size = new System.Drawing.Size(121, 24);
this.tbPortName.TabIndex = 3;
//
// panel1
//
this.panel1.Controls.Add(this.button2);
this.panel1.Controls.Add(this.btmy);
this.panel1.Controls.Add(this.tbmy);
this.panel1.Controls.Add(this.btch);
this.panel1.Controls.Add(this.tbch);
this.panel1.Controls.Add(this.btpand);
this.panel1.Controls.Add(this.tbpanid);
this.panel1.Controls.Add(this.tbPortName);
this.panel1.Controls.Add(this.button1);
this.panel1.Controls.Add(this.tbBaud);
this.panel1.Dock = System.Windows.Forms.DockStyle.Top;
this.panel1.Location = new System.Drawing.Point(0, 0);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(580, 118);
this.panel1.TabIndex = 4;
//
// button2
//
this.button2.Location = new System.Drawing.Point(293, 15);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(75, 89);
this.button2.TabIndex = 10;
this.button2.Text = "Read Setting";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// btmy
//
this.btmy.Location = new System.Drawing.Point(480, 78);
this.btmy.Name = "btmy";
this.btmy.Size = new System.Drawing.Size(86, 26);
this.btmy.TabIndex = 9;
this.btmy.Text = "My";
this.btmy.UseVisualStyleBackColor = true;
this.btmy.Click += new System.EventHandler(this.btmy_Click);
//
// tbmy
//
this.tbmy.Font = new System.Drawing.Font("굴림", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.tbmy.Location = new System.Drawing.Point(374, 78);
this.tbmy.Name = "tbmy";
this.tbmy.Size = new System.Drawing.Size(100, 26);
this.tbmy.TabIndex = 8;
this.tbmy.Text = "9600";
this.tbmy.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// btch
//
this.btch.Location = new System.Drawing.Point(480, 47);
this.btch.Name = "btch";
this.btch.Size = new System.Drawing.Size(86, 26);
this.btch.TabIndex = 7;
this.btch.Text = "Channel";
this.btch.UseVisualStyleBackColor = true;
this.btch.Click += new System.EventHandler(this.btch_Click);
//
// tbch
//
this.tbch.Font = new System.Drawing.Font("굴림", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.tbch.Location = new System.Drawing.Point(374, 47);
this.tbch.Name = "tbch";
this.tbch.Size = new System.Drawing.Size(100, 26);
this.tbch.TabIndex = 6;
this.tbch.Text = "9600";
this.tbch.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// btpand
//
this.btpand.Location = new System.Drawing.Point(480, 15);
this.btpand.Name = "btpand";
this.btpand.Size = new System.Drawing.Size(86, 26);
this.btpand.TabIndex = 5;
this.btpand.Text = "PanID";
this.btpand.UseVisualStyleBackColor = true;
this.btpand.Click += new System.EventHandler(this.btpand_Click);
//
// tbpanid
//
this.tbpanid.Font = new System.Drawing.Font("굴림", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.tbpanid.Location = new System.Drawing.Point(374, 15);
this.tbpanid.Name = "tbpanid";
this.tbpanid.Size = new System.Drawing.Size(100, 26);
this.tbpanid.TabIndex = 4;
this.tbpanid.Text = "9600";
this.tbpanid.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// panel2
//
this.panel2.Controls.Add(this.rtXbee);
this.panel2.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel2.Location = new System.Drawing.Point(0, 118);
this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(580, 338);
this.panel2.TabIndex = 5;
//
// rtXbee
//
this.rtXbee.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.rtXbee.ColorList = new arCtl.sLogMessageColor[0];
this.rtXbee.DateFormat = "mm:ss.fff";
this.rtXbee.DefaultColor = System.Drawing.Color.LightGray;
this.rtXbee.Dock = System.Windows.Forms.DockStyle.Fill;
this.rtXbee.EnableDisplayTimer = false;
this.rtXbee.EnableGubunColor = true;
this.rtXbee.Font = new System.Drawing.Font("맑은 고딕", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.rtXbee.ListFormat = "[{0}] {1}";
this.rtXbee.Location = new System.Drawing.Point(0, 0);
this.rtXbee.MaxListCount = ((ushort)(1000));
this.rtXbee.MaxTextLength = ((uint)(400000u));
this.rtXbee.MessageInterval = 50;
this.rtXbee.Name = "rtXbee";
this.rtXbee.Size = new System.Drawing.Size(580, 338);
this.rtXbee.TabIndex = 2;
this.rtXbee.Text = "";
//
// serialPort1
//
this.serialPort1.ReadTimeout = 1000;
this.serialPort1.WriteTimeout = 1000;
//
// fXbeeSetting
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(580, 456);
this.Controls.Add(this.panel2);
this.Controls.Add(this.panel1);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "fXbeeSetting";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Form1";
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.fXbeeSetting_FormClosed);
this.Load += new System.EventHandler(this.fXbeeSetting_Load);
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.panel2.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button button1;
private System.Windows.Forms.TextBox tbBaud;
private System.Windows.Forms.ComboBox tbPortName;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Panel panel2;
private arCtl.LogTextBox rtXbee;
private System.Windows.Forms.Button btpand;
private System.Windows.Forms.TextBox tbpanid;
private System.Windows.Forms.Button btmy;
private System.Windows.Forms.TextBox tbmy;
private System.Windows.Forms.Button btch;
private System.Windows.Forms.TextBox tbch;
private System.Windows.Forms.Button button2;
private System.IO.Ports.SerialPort serialPort1;
}
}

View File

@@ -0,0 +1,193 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using AR;
using COMM;
namespace Project.Dialog
{
public partial class fXbeeSetting : Form
{
public fXbeeSetting()
{
InitializeComponent();
VAR.BOOL[eVarBool.DISABLE_AUTOCONN_XBEE] = true;
PUB.XBE.Close();
this.serialPort1.DataReceived += SerialPort1_DataReceived;
}
private void fXbeeSetting_Load(object sender, EventArgs e)
{
this.tbPortName.Items.Clear();
foreach (var item in System.IO.Ports.SerialPort.GetPortNames())
{
this.tbPortName.Items.Add(item);
}
this.tbPortName.Text = PUB.setting.Port_XBE;
this.tbBaud.Text = PUB.setting.Baud_XBE.ToString();
}
void showlog(string Message)
{
if (rtXbee.Visible)
{
rtXbee.AddMsg(DateTime.Now, "NORMAL", Message);
}
}
void showlog(arCtl.LogTextBox rtRx, DateTime LogTime, string TypeStr, string Message)
{
if (rtRx.Visible)
{
rtRx.AddMsg(LogTime, TypeStr, Message);
}
}
private void button1_Click(object sender, EventArgs e)
{
if (this.serialPort1.IsOpen)
{
serialPort1.Close();
showlog("closed");
}
else
{
serialPort1.PortName = tbPortName.Text;
serialPort1.BaudRate = int.Parse(tbBaud.Text);
serialPort1.Open();
showlog("open");
}
}
private void fXbeeSetting_FormClosed(object sender, FormClosedEventArgs e)
{
VAR.BOOL[eVarBool.DISABLE_AUTOCONN_XBEE] = false;
}
private volatile bool isCommandExecuting = false;
private void SerialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
if (isCommandExecuting) return;
try
{
string data = serialPort1.ReadExisting();
var hexdata = System.Text.Encoding.Default.GetBytes(data);
var hexstr = string.Join(" ", hexdata.Select(t => t.ToString("X2")));
if (!string.IsNullOrEmpty(data))
{
this.BeginInvoke(new Action(() =>
{
showlog($"RxAsync: {hexstr}");
}));
}
}
catch { }
}
private string Cmd(string cmd, int timeout = 1000)
{
isCommandExecuting = true;
try
{
if (!serialPort1.IsOpen) return "Error: Port Closed";
serialPort1.DiscardInBuffer();
serialPort1.Write(cmd);
System.Threading.Thread.Sleep(20);
serialPort1.ReadTimeout = timeout;
string res = serialPort1.ReadTo("\r");
System.Threading.Thread.Sleep(20);
showlog($"Tx:{cmd.Trim()}, Rx:{res}");
//명령수신호 10ms 대기후 다음 명령을 전송
System.Threading.Thread.Sleep(20);
return res;
}
catch (Exception ex)
{
showlog($"Err: {ex.Message}");
return "Error";
}
finally
{
isCommandExecuting = false;
}
}
private void btpand_Click(object sender, EventArgs e)
{
//각명령마다 회신을 확인하고 다음명령을 실행해야함
//명령수신호 10ms 대기후 다음 명령을 전송
//명령을 설정하면 응답은 OK\d 형태로 입력된다.
var cmds = new string[] {
"+++",
$"ATID{tbpanid.Text}\r" ,
$"ATCN\r"};
foreach (var cmd in cmds)
{
if(!Cmd(cmd).Contains("OK"))
{
showlog("FAIL");
break;
}
}
}
private void btch_Click(object sender, EventArgs e)
{
var cmds = new string[] {
"+++",
$"ATCH{tbch.Text}\r" ,
$"ATCN\r"};
foreach (var cmd in cmds)
{
if (!Cmd(cmd).Contains("OK"))
{
showlog("FAIL");
break;
}
}
}
private void btmy_Click(object sender, EventArgs e)
{
var cmds = new string[] {
"+++",
$"ATMY{tbmy.Text}\r" ,
$"ATCN\r"};
foreach (var cmd in cmds)
{
if (!Cmd(cmd).Contains("OK"))
{
showlog("FAIL");
break;
}
}
}
private void button2_Click(object sender, EventArgs e)
{
//read all(id,ch,my)
if (Cmd("+++").Contains("OK"))
{
var id = Cmd("ATID\r");
var ch = Cmd("ATCH\r");
var my = Cmd("ATMY\r");
Cmd("ATCN\r");
this.BeginInvoke(new Action(() => {
this.tbpanid.Text = id;
this.tbch.Text = ch;
this.tbmy.Text = my;
}));
}
}
}
}

View File

@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="serialPort1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View File

@@ -31,7 +31,12 @@ namespace Project
public static bool AutRebootAlreay = false; public static bool AutRebootAlreay = false;
public static bool DriveSpeed = false; public static bool DriveSpeed = false;
public static AGVNavigationCore.Controls.UnifiedAGVCanvas _mapCanvas; public static AGVNavigationCore.Controls.UnifiedAGVCanvas _mapCanvas;
public static List<MapNode> _mapNodes; //public static List<MapNode> _mapNodes;
/// <summary>
/// 다음 작업 명령 (PickOn/PickOff)
/// </summary>
public static ENIGProtocol.AGVCommandHE NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
/// <summary> /// <summary>
/// 가상 AGV (시뮬레이션용) /// 가상 AGV (시뮬레이션용)
@@ -607,26 +612,30 @@ namespace Project
#region VirtualAGV #region VirtualAGV
public static MapNode FindByNodeID(string nodeidx) public static MapNode FindByNodeID(string nodeidx)
{ {
var _mapNodes = PUB._mapCanvas.Nodes;
if (_mapNodes == null || _mapNodes.Any() == false) return null; if (_mapNodes == null || _mapNodes.Any() == false) return null;
if (nodeidx.isEmpty()) return null; if (nodeidx.isEmpty()) return null;
return _mapNodes.Where(t => t.NodeId.Equals(nodeidx)).FirstOrDefault(); return _mapNodes.Where(t => t.Id.Equals(nodeidx)).FirstOrDefault();
} }
public static MapNode FindByRFID(string rfidValue) public static MapNode FindByRFID(string rfidValue)
{ {
var _mapNodes = PUB._mapCanvas.Nodes;
if (_mapNodes == null || _mapNodes.Any() == false) return null; if (_mapNodes == null || _mapNodes.Any() == false) return null;
if (rfidValue.isEmpty()) return null; if (rfidValue.isEmpty()) return null;
return _mapNodes.Where(t => t.RfidId.Equals(rfidValue)).FirstOrDefault(); return _mapNodes.Where(t => t.RfidId.Equals(rfidValue)).FirstOrDefault();
} }
public static List<MapNode> FindByNodeAlias(string alias) public static List<MapNode> FindByNodeAlias(string alias)
{ {
var _mapNodes = PUB._mapCanvas.Nodes;
if (_mapNodes == null || _mapNodes.Any() == false) return null; if (_mapNodes == null || _mapNodes.Any() == false) return null;
if (alias.isEmpty()) return null; if (alias.isEmpty()) return null;
var lst = _mapNodes.Where(t => t.NodeAlias.Equals(alias)); var lst = _mapNodes.Where(t => t.AliasName.Equals(alias));
if (lst.Any() == false) return null; if (lst.Any() == false) return null;
return lst.ToList(); return lst.ToList();
} }
public static List<MapNode> FindByNodeType(AGVNavigationCore.Models.MapNode type) public static List<MapNode> FindByNodeType(AGVNavigationCore.Models.MapNode type)
{ {
var _mapNodes = PUB._mapCanvas.Nodes;
if (_mapNodes == null || _mapNodes.Any() == false) return null; if (_mapNodes == null || _mapNodes.Any() == false) return null;
var lst = _mapNodes.Where(t => t.Type.Equals(type)); var lst = _mapNodes.Where(t => t.Type.Equals(type));
if (lst.Any() == false) return null; if (lst.Any() == false) return null;
@@ -638,8 +647,9 @@ namespace Project
/// <param name="rfidId">읽은 RFID ID</param> /// <param name="rfidId">읽은 RFID ID</param>
/// <param name="motorDirection">모터 방향 (Forward/Backward)</param> /// <param name="motorDirection">모터 방향 (Forward/Backward)</param>
/// <returns>업데이트 성공 여부</returns> /// <returns>업데이트 성공 여부</returns>
public static bool UpdateAGVFromRFID(string rfidId, AgvDirection motorDirection = AgvDirection.Forward) public static bool UpdateAGVFromRFID(ushort rfidId, AgvDirection motorDirection = AgvDirection.Forward)
{ {
var _mapNodes = PUB._mapCanvas.Nodes;
if (_virtualAGV == null || _mapNodes == null) return false; if (_virtualAGV == null || _mapNodes == null) return false;
// RFID에 해당하는 노드 찾기 // RFID에 해당하는 노드 찾기
@@ -649,7 +659,7 @@ namespace Project
_virtualAGV.SetPosition(node, motorDirection); _virtualAGV.SetPosition(node, motorDirection);
RefreshAGVCanvas(); RefreshAGVCanvas();
log.Add($"[AGV] RFID {rfidId} 감지 → 노드 {node.NodeId} 위치 업데이트 (방향: {motorDirection})"); log.Add($"[AGV] RFID {rfidId} 감지 → 노드 {node.Id} 위치 업데이트 (방향: {motorDirection})");
return true; return true;
} }
@@ -665,9 +675,10 @@ namespace Project
/// <returns>업데이트 성공 여부</returns> /// <returns>업데이트 성공 여부</returns>
public static bool UpdateAGVToNode(string nodeId, AgvDirection motorDirection = AgvDirection.Forward) public static bool UpdateAGVToNode(string nodeId, AgvDirection motorDirection = AgvDirection.Forward)
{ {
var _mapNodes = PUB._mapCanvas.Nodes;
if (_virtualAGV == null || _mapNodes == null) return false; if (_virtualAGV == null || _mapNodes == null) return false;
var node = _mapNodes.FirstOrDefault(n => n.NodeId == nodeId); var node = _mapNodes.FirstOrDefault(n => n.Id == nodeId);
if (node != null) if (node != null)
{ {
_virtualAGV.SetPosition(node, motorDirection); _virtualAGV.SetPosition(node, motorDirection);
@@ -724,7 +735,7 @@ namespace Project
{ {
if (_virtualAGV == null) return; if (_virtualAGV == null) return;
_virtualAGV.BatteryLevel = batteryLevel; _virtualAGV.SetBatteryLevel(batteryLevel);
RefreshAGVCanvas(); RefreshAGVCanvas();
} }
@@ -739,41 +750,6 @@ namespace Project
} }
} }
/// <summary>
/// 현재 AGV의 노드 ID 가져오기
/// </summary>
/// <returns>현재 노드 ID</returns>
public static string GetCurrentAGVNodeId()
{
return _virtualAGV?.CurrentNodeId ?? string.Empty;
}
/// <summary>
/// 현재 AGV 위치 가져오기
/// </summary>
/// <returns>현재 위치</returns>
public static Point GetCurrentAGVPosition()
{
return _virtualAGV?.CurrentPosition ?? Point.Empty;
}
/// <summary>
/// 현재 AGV 방향 가져오기
/// </summary>
/// <returns>현재 방향</returns>
public static AgvDirection GetCurrentAGVDirection()
{
return _virtualAGV?.CurrentDirection ?? AgvDirection.Forward;
}
/// <summary>
/// 현재 AGV 상태 가져오기
/// </summary>
/// <returns>현재 상태</returns>
public static AGVState GetCurrentAGVState()
{
return _virtualAGV?.CurrentState ?? AGVState.Idle;
}
#endregion #endregion

View File

@@ -19,20 +19,6 @@ namespace Project
private void _SM_RUN(Boolean isFirst, TimeSpan stepTime) private void _SM_RUN(Boolean isFirst, TimeSpan stepTime)
{ {
//중단기능이 동작이라면 처리하지 않는다.
if (PUB.sm.bPause)
{
System.Threading.Thread.Sleep(200);
return;
}
//가동불가 조건 확인
if (CheckStopCondition() == false)
{
PUB.sm.SetNewStep(eSMStep.IDLE);
return;
}
//HW 연결오류 //HW 연결오류
if (PUB.AGV.IsOpen == false) if (PUB.AGV.IsOpen == false)
{ {
@@ -41,17 +27,16 @@ namespace Project
return; return;
} }
//이머전시상태라면 stop 처리한다. //가동불가 조건 확인
if (PUB.AGV.error.Emergency && if (CheckStopCondition() == false) return;
PUB.AGV.system1.agv_stop == true &&
PUB.AGV.system1.stop_by_front_detect == false) //중단기능이 동작이라면 처리하지 않는다.
if (PUB.sm.bPause)
{ {
PUB.Speak(Lang.); System.Threading.Thread.Sleep(200);
PUB.sm.SetNewStep(eSMStep.IDLE);
return; return;
} }
//스텝이 변경되었다면? //스텝이 변경되었다면?
if (PUB.sm.RunStep != PUB.sm.RunStepNew) if (PUB.sm.RunStep != PUB.sm.RunStepNew)
{ {
@@ -123,12 +108,20 @@ namespace Project
//목적지가 BUFFER라면 버퍼투입대기위치까지 완료했다는 시그널을 보낸다. //목적지가 BUFFER라면 버퍼투입대기위치까지 완료했다는 시그널을 보낸다.
var target = PUB._virtualAGV.TargetNode; var target = PUB._virtualAGV.TargetNode;
PUB.log.Add($"목적지({target.RfidId}) 도착완료 타입:{target.Type}, 출발지:{PUB._virtualAGV.StartNode.RfidId}"); PUB.log.Add($"목적지({target.RfidId}) 도착완료 타입:{target.Type}, 출발지:{PUB._virtualAGV.StartNode.RfidId}");
if (target.Type == AGVNavigationCore.Models.NodeType.Buffer)
{
switch (target.StationType)
{
case AGVNavigationCore.Models.StationType.Buffer:
//현재위치가 마지막경로의 NODEID와 일치해야한다 //현재위치가 마지막경로의 NODEID와 일치해야한다
if (PUB._virtualAGV.CurrentPath == null)
{
PUB.log.AddAT("목적지 버퍼이동완료 했지만 상세경로가 없습니다");
PUB.XBE.BufferInComplete = false;
PUB.XBE.BufferOutComplete = false;
break;
}
var lastPath = PUB._virtualAGV.CurrentPath.DetailedPath.LastOrDefault(); var lastPath = PUB._virtualAGV.CurrentPath.DetailedPath.LastOrDefault();
if (lastPath.NodeId.Equals(PUB._virtualAGV.CurrentNodeId)) if (lastPath.NodeId.Equals(PUB._virtualAGV.CurrentNode.Id))
{ {
//버퍼진입전 노드에 도착완료했따 //버퍼진입전 노드에 도착완료했따
PUB.XBE.BufferInReady = true; PUB.XBE.BufferInReady = true;
@@ -143,29 +136,9 @@ namespace Project
} }
PUB.XBE.BufferInComplete = false; PUB.XBE.BufferInComplete = false;
PUB.XBE.BufferOutComplete = false; PUB.XBE.BufferOutComplete = false;
break;
} }
else if (target.Type == AGVNavigationCore.Models.NodeType.Charging)
{
}
else if (target.Type == AGVNavigationCore.Models.NodeType.Loader)
{
}
else if (target.Type == AGVNavigationCore.Models.NodeType.Clearner)
{
}
else if (target.Type == AGVNavigationCore.Models.NodeType.UnLoader)
{
}
else
{
//목적지다 다른 형태이다
}
PUB._virtualAGV.Turn = AGVNavigationCore.Models.AGVTurn.None; PUB._virtualAGV.Turn = AGVNavigationCore.Models.AGVTurn.None;
PUB.sm.SetNewRunStep(ERunStep.READY); PUB.sm.SetNewRunStep(ERunStep.READY);
} }
@@ -272,9 +245,21 @@ namespace Project
//도킹완료상태를 업데이트한다. //도킹완료상태를 업데이트한다.
PUB.XBE.LoaderInComplete = true; PUB.XBE.LoaderInComplete = true;
//로더아웃으로 자동 진행 합니다 //로더아웃으로 자동 진행하지 않음 (ACS 명령 대기)
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
{
PUB.sm.SetNewRunStep(ERunStep.READY);
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop; // Command consumed
}
else
{
// Legacy behavior or Goto command: Auto-exit?
// User said separation is key. Let's Stop here too or keep legacy for GOTO?
// Assuming GOTO might rely on this, but safer to STOP if we want strict separation.
// However, let's keep legacy behavior for GOTO if possible, but for PickOn/Off we STOP.
PUB.sm.ClearRunStep(); PUB.sm.ClearRunStep();
PUB.sm.SetNewRunStep(ERunStep.LOADER_OUT); PUB.sm.SetNewRunStep(ERunStep.LOADER_OUT);
}
return; return;
} }
break; break;
@@ -302,9 +287,17 @@ namespace Project
//도킹완료상태를 업데이트한다. //도킹완료상태를 업데이트한다.
PUB.XBE.UnloaderInComplete = true; PUB.XBE.UnloaderInComplete = true;
//언로더아웃으로 자동 진행 합니다 //언로더아웃으로 자동 진행하지 않음
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
{
PUB.sm.SetNewRunStep(ERunStep.READY);
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
}
else
{
PUB.sm.ClearRunStep(); PUB.sm.ClearRunStep();
PUB.sm.SetNewRunStep(ERunStep.UNLOADER_OUT); PUB.sm.SetNewRunStep(ERunStep.UNLOADER_OUT);
}
return; return;
} }
break; break;
@@ -332,9 +325,17 @@ namespace Project
//도킹완료상태를 업데이트한다. //도킹완료상태를 업데이트한다.
PUB.XBE.CleanerInComplete = true; PUB.XBE.CleanerInComplete = true;
//클리너아웃으로 자동 진행 합니다 //클리너아웃으로 자동 진행하지 않음
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
{
PUB.sm.SetNewRunStep(ERunStep.READY);
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
}
else
{
PUB.sm.ClearRunStep(); PUB.sm.ClearRunStep();
PUB.sm.SetNewRunStep(ERunStep.CLEANER_OUT); PUB.sm.SetNewRunStep(ERunStep.CLEANER_OUT);
}
return; return;
} }
break; break;
@@ -362,9 +363,17 @@ namespace Project
//도킹완료상태를 업데이트한다. //도킹완료상태를 업데이트한다.
PUB.XBE.BufferInComplete = true; PUB.XBE.BufferInComplete = true;
//버퍼아웃으로 자동 진행 합니다 //버퍼아웃으로 자동 진행하지 않음
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
{
PUB.sm.SetNewRunStep(ERunStep.READY);
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
}
else
{
PUB.sm.ClearRunStep(); PUB.sm.ClearRunStep();
PUB.sm.SetNewRunStep(ERunStep.BUFFER_OUT); PUB.sm.SetNewRunStep(ERunStep.BUFFER_OUT);
}
return; return;
} }
break; break;

View File

@@ -125,28 +125,27 @@ namespace Project
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//리프트를 내린다. // [PickOn/PickOff] 초기 리프트 동작
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN); var liftCmd = arDev.Narumi.LiftCommand.DN;
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
{
liftCmd = arDev.Narumi.LiftCommand.UP;
}
PUB.AGV.LiftControl(liftCmd);
VAR.TIME.Update(eVarTime.LastTurnCommandTime); VAR.TIME.Update(eVarTime.LastTurnCommandTime);
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
return false; return false;
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//리프트다운센서 확인한다. //리프트 센서 확인
var liftdown = true;
if (liftdown == false)
{
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime); var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
if (ts.TotalSeconds > 10) if (ts.TotalSeconds > 10)
{ {
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP); // Timebound check
PUB.log.AddE("리프트 하강이 확인되지 않습니다(10초)");
PUB.sm.SetNewRunStep(ERunStep.ERROR);
return false;
} }
} PUB.log.Add("리프트 동작 확인 완료");
PUB.log.Add("리프트 하강 완료");
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
return false; return false;
} }
@@ -160,7 +159,7 @@ namespace Project
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//저속이동 //저속이동 (후진 진입)
var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
{ {
Bunki = arDev.Narumi.eBunki.Strate, Bunki = arDev.Narumi.eBunki.Strate,
@@ -224,11 +223,43 @@ namespace Project
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//완료되었다. (ACS에 보내야함) // [Action] 진입 완료 후 리프트 동작 (Pick/Drop)
PUB.log.Add("버퍼투입완료"); PUB.log.Add("버퍼 진입 완료. 작업 수행(Lift Pick/Drop)");
var liftCmd = arDev.Narumi.LiftCommand.UP;
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
{
liftCmd = arDev.Narumi.LiftCommand.DN;
}
PUB.AGV.LiftControl(liftCmd);
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
return false; return false;
} }
else if (PUB.sm.RunStepSeq == idx++)
{
// 리프트 동작 대기
// TODO: 실제 센서 확인 로직 추가 필요
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
if (ts.TotalSeconds < 2) return false;
PUB.log.Add("작업(Pick/Drop) 완료. 대기 상태로 전환 (퇴출 명령 대기)");
PUB.sm.UpdateRunStepSeq();
return false;
}
else if (PUB.sm.RunStepSeq == idx++)
{
//완료되었다. (ACS에 보내야함)
PUB.log.Add("버퍼 진입 및 작업 완료");
PUB.sm.UpdateRunStepSeq();
return false;
}
// 작업을 마치고 설비 안에 멈춰있는 상태.
// ACS가 이 상태를 확인하고 NextWorkCmd로 퇴출(Out) 명령을 보내야 함.
PUB.AddEEDB($"버퍼작업완료({PUB.Result.TargetPos})");
return true;
PUB.AddEEDB($"버퍼투입완료({PUB.Result.TargetPos})"); PUB.AddEEDB($"버퍼투입완료({PUB.Result.TargetPos})");
return true; return true;

View File

@@ -54,8 +54,8 @@ namespace Project
// 1분 타임아웃 체크 // 1분 타임아웃 체크
if (stepTime.TotalMinutes >= 1) if (stepTime.TotalMinutes >= 1)
{ {
PUB.XBE.errorMessage = $"충전해제가 실패되었습니다(1분)"; PUB.XBE.ErrorMessage = $"충전해제가 실패되었습니다(1분)";
PUB.log.AddE(PUB.XBE.errorMessage); PUB.log.AddE(PUB.XBE.ErrorMessage);
PUB.sm.SetNewStep(eSMStep.IDLE); PUB.sm.SetNewStep(eSMStep.IDLE);
return false; return false;
} }

View File

@@ -43,28 +43,27 @@ namespace Project
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//리프트를 내린다. // [PickOn/PickOff] 초기 리프트 동작
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN); var liftCmd = arDev.Narumi.LiftCommand.DN;
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
{
liftCmd = arDev.Narumi.LiftCommand.UP;
}
PUB.AGV.LiftControl(liftCmd);
VAR.TIME.Update(eVarTime.LastTurnCommandTime); VAR.TIME.Update(eVarTime.LastTurnCommandTime);
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
return false; return false;
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//리프트다운센서 확인한다. //리프트 센서 확인
var liftdown = true;
if (liftdown == false)
{
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime); var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
if (ts.TotalSeconds > 10) if (ts.TotalSeconds > 10)
{ {
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP); // Timebound check
PUB.log.AddE("리프트 하강이 확인되지 않습니다(10초)");
PUB.sm.SetNewRunStep(ERunStep.ERROR);
return false;
} }
} PUB.log.Add("리프트 동작 확인 완료");
PUB.log.Add("리프트 하강 완료");
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
return false; return false;
} }
@@ -78,7 +77,7 @@ namespace Project
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//저속이동 //저속이동 (후진 진입)
var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
{ {
Bunki = arDev.Narumi.eBunki.Strate, Bunki = arDev.Narumi.eBunki.Strate,
@@ -141,14 +140,40 @@ namespace Project
return false; return false;
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{
// [Action] 진입 완료 후 리프트 동작 (Pick/Drop)
PUB.log.Add("클리너 진입 완료. 작업 수행(Lift Pick/Drop)");
var liftCmd = arDev.Narumi.LiftCommand.UP;
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
{
liftCmd = arDev.Narumi.LiftCommand.DN;
}
PUB.AGV.LiftControl(liftCmd);
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
PUB.sm.UpdateRunStepSeq();
return false;
}
else if (PUB.sm.RunStepSeq == idx++)
{
// 리프트 동작 대기
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
if (ts.TotalSeconds < 2) return false;
PUB.log.Add("작업(Pick/Drop) 완료. 대기 상태로 전환");
PUB.sm.UpdateRunStepSeq();
return false;
}
else if (PUB.sm.RunStepSeq == idx++)
{ {
//완료되었다. //완료되었다.
PUB.log.Add("클리너진입완료"); PUB.log.Add("클리너 진입 및 작업 완료");
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
return false; return false;
} }
PUB.AddEEDB($"클리너진입완료({PUB.Result.TargetPos})"); PUB.AddEEDB($"클리너작업완료({PUB.Result.TargetPos})");
return true; return true;
} }
} }

View File

@@ -29,6 +29,17 @@ namespace Project
} }
//이미 충전중이라면 바로 완료 처리한다 (사용자 요청)
if (VAR.BOOL[eVarBool.FLAG_CHARGEONA] == true || PUB.AGV.system1.Battery_charging == true)
{
if (isFirst)
{
PUB.log.Add("이미 충전 중이므로 충전 시퀀스를 종료하고 준비 상태로 전환합니다.");
PUB.sm.SetNewRunStep(ERunStep.READY);
}
return false;
}
//충전 상태가 OFF되어야 동작하게한다 //충전 상태가 OFF되어야 동작하게한다
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false) if (_SM_RUN_CHGOFF(isFirst, stepTime) == false)
return false; return false;
@@ -75,7 +86,7 @@ namespace Project
PUB._virtualAGV.TargetNode = targetnode; PUB._virtualAGV.TargetNode = targetnode;
VAR.TIME.Update(eVarTime.ChargeSearch); VAR.TIME.Update(eVarTime.ChargeSearch);
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
PUB.log.Add($"충전:대상위치 QC 시작"); PUB.log.Add($"충전기 위치로 이동 목표:{targetnode.RfidId}");
return false; return false;
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
@@ -83,7 +94,7 @@ namespace Project
//모션 전후진 제어 //모션 전후진 제어
if (UpdateMotionPositionForCharger("GOCHARGE #1") == true) if (UpdateMotionPositionForCharger("GOCHARGE #1") == true)
{ {
PUB.log.Add($"충전:충전기 검색 전 QC위치 확인 완료"); PUB.log.Add($"충전기 목표위치 이동 완료");
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
} }
else else
@@ -105,8 +116,6 @@ namespace Project
return false; return false;
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{
if (PUB.setting.chargerpos == 0) //down search
{ {
PUB.log.Add($"충전:충전기 검색을 위한 전진시작"); PUB.log.Add($"충전:충전기 검색을 위한 전진시작");
PUB.Speak(Lang.); PUB.Speak(Lang.);
@@ -120,33 +129,11 @@ namespace Project
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward); PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward);
//PUB.Result.TargetPos = ePosition.CHARGE; //PUB.Result.TargetPos = ePosition.CHARGE;
VAR.TIME.Update(eVarTime.ChargeSearch); VAR.TIME.Update(eVarTime.ChargeSearch);
}
else if (PUB.setting.chargerpos == 2) //up search
{
PUB.log.Add($"충전:충전기 검색을 위한 전진시작");
PUB.Speak(Lang.);
PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
{
Speed = arDev.Narumi.eMoveSpd.Low,
Bunki = arDev.Narumi.eBunki.Strate,
Direction = arDev.Narumi.eMoveDir.Backward,
PBSSensor = 1,
});
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);
//PUB.Result.TargetPos = ePosition.CHARGE;
VAR.TIME.Update(eVarTime.ChargeSearch);
}
else
{
PUB.log.Add($"충전기위치가 QC위치로 설정되어 있습니다");
}
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
return false; return false;
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{
if (PUB.setting.chargerpos != 1)
{ {
if (PUB.AGV.system1.agv_run) if (PUB.AGV.system1.agv_run)
{ {
@@ -165,13 +152,6 @@ namespace Project
PUB.log.Add($"충전:AGV기동확인 안됨, 롤백 다시 이동할 수 있게 함"); PUB.log.Add($"충전:AGV기동확인 안됨, 롤백 다시 이동할 수 있게 함");
} }
} }
}
else
{
VAR.TIME.Update(eVarTime.ChargeSearch);
PUB.sm.UpdateRunStepSeq();
}
return false; return false;
} }
@@ -259,7 +239,6 @@ namespace Project
} }
return false; return false;
} }
return true; return true;
} }
} }

View File

@@ -17,6 +17,7 @@ namespace Project
{ {
///명령어 재전송 간격(기본 2초) ///명령어 재전송 간격(기본 2초)
var CommandInterval = 2; var CommandInterval = 2;
var funcName = "_SM_RUN_GOTO";
//충전 상태가 OFF되어야 동작하게한다 //충전 상태가 OFF되어야 동작하게한다
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false) if (_SM_RUN_CHGOFF(isFirst, stepTime) == false)
@@ -29,269 +30,48 @@ namespace Project
VAR.TIME.Update(eVarTime.CheckGotoTargetSet); VAR.TIME.Update(eVarTime.CheckGotoTargetSet);
} }
//PUB._virtualAGV. //라이더멈춤이 설정되어있다면 음성으로 알려준다 200409
if (PUB.AGV.system1.stop_by_front_detect == true)
{
var tsSpeak = DateTime.Now - LastSpeakTime;
if (tsSpeak.TotalSeconds >= PUB.setting.alarmSoundTerm)
{
PUB.Speak(Lang.);
LastSpeakTime = DateTime.Now;
}
return false;
}
//목적지가 설정되었는지 체크한다. var idx = 1;
//Z if (PUB.mapctl.Manager.agv.TargetRFID.IsEmpty) if (PUB.sm.RunStepSeq == idx++)
// { {
// //최대 5초간 설정여부를 확인하고 if(PUB._virtualAGV.TargetNode == null)
// if (VAR.TIME.RUN(eVarTime.CheckGotoTargetSet).TotalSeconds > 5) {
// { PUB.log.Add($"대상노드가 없어 이동을할 수 없습니다");
// //실패시에는 READY로 전환한다. PUB.sm.SetNewRunStep(ERunStep.READY);
// PUB.sm.SetNewRunStep(ERunStep.READY); return false;
// PUB.Speak(Lang.목적지가없어대기상태로전환합니다); }
// } PUB.sm.UpdateRunStepSeq();
// return false; return false;
// } }
else if (PUB.sm.RunStepSeq == idx++)
//var idx = 1; {
//var BeforePredictIdx = -1; //모션 전후진 제어
//var predict = PUB.mapctl.Manager.PredictResult; if (UpdateMotionPositionForMark(funcName))
//if (PUB.sm.RunStepSeq == idx++) {
//{ PUB.AGV.AGVMoveStop(funcName);
// PUB.Speak(Lang.위치로이동합니다); PUB.sm.UpdateRunStepSeq();
// PUB.log.Add($"목적지 위치 이동시작({PUB.mapctl.Manager.agv.TargetRFID.Value})"); }
// VAR.TIME.Update(eVarTime.CheckGotoTargetSet); return false;
// VAR.TIME.Set(eVarTime.SendGotoCommand, DateTime.Now.AddDays(-1)); }
// PUB.sm.UpdateRunStepSeq(); else if (PUB.sm.RunStepSeq == idx++)
// return false; {
//} //QC까지 모두 완료되었다.(완전히 정차할때까지 기다린다)
//else if (PUB.sm.RunStepSeq == idx++) PUB.Speak(Lang., true);
//{ PUB.AddEEDB($"홈검색완료({PUB.Result.TargetPos})");
// //멈춰야하는경우 PUB.sm.UpdateRunStepSeq();
// if (predict.MoveState == AGVControl.AGVMoveState.Stop) return false;
// { }
// if (PUB.AGV.system1.agv_run)
// {
// if (VAR.TIME.RUN(eVarTime.SendGotoCommand).TotalSeconds > 2)
// {
// PUB.Speak("AGV Stop");
// PUB.AGV.AGVMoveStop("Predict", arDev.Narumi.eStopOpt.Stop);
// VAR.TIME.Update(eVarTime.SendGotoCommand);
// }
// return false;
// }
// else
// {
// //완료되었거나 턴을진행해야한다
// if (predict.ReasonCode == AGVControl.AGVActionReasonCode.Arrived ||
// predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnMove ||
// predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnPoint)
// {
// GotoTurnStep = 0;
// GotoTurnSetTime = DateTime.Now.AddDays(-1);
// PUB.sm.UpdateRunStepSeq();
// }
// return false;
// }
// }
// else //이동해야하는 경우
// {
// //속도와 방향이 불일치하는 경우 다시 설정한다 (속도: H,L,M,[S]
// AGVControl.AgvDir AGV_Direction = (AGVControl.AgvDir)PUB.AGV.data.Direction;
// AGVControl.AgvSpeed AGV_Speed = (AGVControl.AgvSpeed)PUB.AGV.data.Speed;
// AGVControl.AgvSts AGV_Sts = (AGVControl.AgvSts)PUB.AGV.data.Sts;
// //상태값이 바뀌었다면 전송을 해야한다
// if (predict.Direction != AGV_Direction || predict.MoveSpeed != AGV_Speed || predict.MoveDiv != AGV_Sts)
// {
// if (VAR.TIME.RUN(eVarTime.SendGotoCommand).TotalSeconds > CommandInterval)
// {
// arDev.Narumi.eBunki v_bunki = arDev.Narumi.eBunki.Strate;
// if (predict.MoveDiv == AGVControl.AgvSts.Straight) v_bunki = arDev.Narumi.eBunki.Strate;
// else if (predict.MoveDiv == AGVControl.AgvSts.Left) v_bunki = arDev.Narumi.eBunki.Left;
// else if (predict.MoveDiv == AGVControl.AgvSts.Right) v_bunki = arDev.Narumi.eBunki.Right;
// arDev.Narumi.eMoveDir v_dir = arDev.Narumi.eMoveDir.Backward;
// if (predict.Direction == AGVControl.AgvDir.Forward) v_dir = arDev.Narumi.eMoveDir.Forward;
// else if (predict.Direction == AGVControl.AgvDir.Backward) v_dir = arDev.Narumi.eMoveDir.Backward;
// arDev.Narumi.eMoveSpd v_spd = arDev.Narumi.eMoveSpd.Low;
// if (predict.MoveSpeed == AGVControl.AgvSpeed.Middle) v_spd = arDev.Narumi.eMoveSpd.Middle;
// else if (predict.MoveSpeed == AGVControl.AgvSpeed.High) v_spd = arDev.Narumi.eMoveSpd.High;
// else if (predict.MoveSpeed == AGVControl.AgvSpeed.Low) v_spd = arDev.Narumi.eMoveSpd.Low;
// //이동셋팅을 해준다
// PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
// {
// Bunki = v_bunki,
// Direction = v_dir,
// PBSSensor = 1,
// Speed = v_spd,
// });
// if (predict.MoveSpeed == AGVControl.AgvSpeed.MarkStop)
// {
// PUB.AGV.AGVMoveStop("Predict", arDev.Narumi.eStopOpt.Stop);
// }
// VAR.TIME.Update(eVarTime.SendGotoCommand);
// }
// return false;
// }
// //정지상태라면 이동 명령을 전달한다
// if (PUB.AGV.system1.agv_run == false)
// {
// if (VAR.TIME.RUN(eVarTime.SendGotoCommand).TotalSeconds > CommandInterval)
// {
// PUB.Speak("AGV Start");
// arDev.Narumi.eRunOpt v_dir = arDev.Narumi.eRunOpt.Backward;
// if (predict.Direction == AGVControl.AgvDir.Forward) v_dir = arDev.Narumi.eRunOpt.Forward;
// else if (predict.Direction == AGVControl.AgvDir.Backward) v_dir = arDev.Narumi.eRunOpt.Backward;
// PUB.AGV.AGVMoveRun(v_dir);
// VAR.TIME.Update(eVarTime.SendGotoCommand);
// }
// return false;
// }
// }
// //예측이 업데이트되지 않으면 오류 처리해야한다
// if (BeforePredictIdx == -1) BeforePredictIdx = (int)predict.Idx;
// else if (BeforePredictIdx != predict.Idx) //이전사용한 IDX와 다르다면 예측이 실행된 경우이다
// BeforePredictIdx = (int)predict.Idx;
// else
// {
// //5초이상 예측값이 업데이트되지 않으면 오류 처리한다.
// var tsPredict = DateTime.Now - predict.CreateTime;
// if (tsPredict.TotalSeconds > 5)
// {
// PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.PredictFix, Lang.예측값이계산되지않아이동을중단합니다);
// PUB.Speak(Lang.예측값이계산되지않아이동을중단합니다);
// PUB.sm.SetNewRunStep(ERunStep.READY);
// }
// }
// return false;
//}
//else if (PUB.sm.RunStepSeq == idx++)
//{
// if (predict.ReasonCode == AGVControl.AGVActionReasonCode.Arrived)
// {
// PUB.Speak(Lang.목적지이동이완료되었습니다);
// PUB.sm.SetNewRunStep(ERunStep.READY);
// PUB.sm.UpdateRunStepSeq();
// }
// else if (predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnMove ||
// predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnPoint)
// {
// //턴을 해야하는 경우이다
// //좌턴을 기본으로 진행하며, 좌턴이동 후 마크스탑을 입력한다
// if (GotoTurnStep == 0)
// {
// //턴을 한적이 없으므로 턴을 먼저 진행한다
// arDev.Narumi.eMoveDir moveDir = arDev.Narumi.eMoveDir.Backward;
// if (predict.Direction == AGVControl.AgvDir.Forward) moveDir = arDev.Narumi.eMoveDir.Forward;
// if (PUB.AGV.data.Sts != 'L' || PUB.AGV.data.Speed != 'L' || PUB.AGV.data.Direction != moveDir.ToString()[0])
// {
// //셋팅이 다르다면 3초간격으로 전송한다
// var tsTurnSet = DateTime.Now - GotoTurnSetTime;
// if (tsTurnSet.TotalSeconds > 3)
// {
// PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
// {
// Bunki = arDev.Narumi.eBunki.Left,
// Direction = moveDir,
// PBSSensor = 1,
// Speed = arDev.Narumi.eMoveSpd.Low,
// });
// GotoTurnSetTime = DateTime.Now;
// PUB.log.Add("Turn Bunki Set");
// }
// }
// else
// {
// PUB.mapctl.Manager.agv.CurrentRFID.TurnOK = false;
// PUB.mapctl.Manager.agv.CurrentRFID.TurnStart = DateTime.Now;
// PUB.sm.UpdateRunStepSeq(); //셋팅이 맞으니 다음스텝으로 진행한다
// GotoTurnStep += 1;
// }
// }
// }
// else PUB.sm.UpdateRunStepSeq();
// return false;
//}
//else if (PUB.sm.RunStepSeq == idx++)
//{
// //턴이완료되길 기다린다.
// if (predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnMove ||
// predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnPoint)
// {
// //(최소5초는 기다리고 판단한다)
// if (stepTime.TotalSeconds < 5) return false;
// //최대30초는 기다려준다
// if (stepTime.TotalSeconds > 30)
// {
// var ermsg = "Turn Timeout(30sec)";
// PUB.log.AddE(ermsg);
// PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.TurnTimeout, ermsg);
// PUB.sm.SetNewRunStep(ERunStep.READY);
// }
// //모션이 멈추었다면 턴이완료된것이다.
// if (PUB.AGV.system1.agv_stop)
// {
// if (PUB.AGV.system1.Mark1_check == false && PUB.AGV.system1.Mark2_check == false)
// {
// PUB.log.AddE($"Turn 완료이나 Mark 센서가 확인되지 않았습니다");
// }
// GotoTurnStep += 1;
// PUB.sm.UpdateRunStepSeq();
// }
// else
// {
// //아직 이동중이므로 대기한다
// }
// }
// else PUB.sm.UpdateRunStepSeq(); //기타사항은 다음으로 넘어간다
// return false;
//}
//else if (PUB.sm.RunStepSeq == idx++)
//{
// if (predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnMove ||
// predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnPoint)
// {
// if (GotoTurnStep < 2)
// {
// PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.TurnError, "턴시퀀스 완료 실패");
// PUB.log.AddE($"턴완료시퀀스가 2가아닙니다. 대기 상태로 강제 전환합니다");
// PUB.sm.SetNewRunStep(ERunStep.READY);
// }
// else
// {
// PUB.log.AddI("Turn Complete");
// }
// //방향전환용 턴이라면 이동기록을 추가해서 방향이 맞도록 처리해주자
// if (predict.ReasonCode == AGVControl.AGVActionReasonCode.NeedTurnMove)
// {
// var rfid = PUB.mapctl.Manager.agv.CurrentRFID;
// var lastHistory = PUB.mapctl.Manager.agv.MovementHistory.Last();
// //원래방향에서 반대로 처리한다
// var revDir = lastHistory.Direction == AGVControl.AgvDir.Backward ? AGVControl.AgvDir.Forward : AGVControl.AgvDir.Backward;
// PUB.mapctl.Manager.agv.AddToMovementHistory(rfid.Value, rfid.Location, revDir);
// }
// else
// {
// //이동용 RFID에서 턴명령이 들어있는경우였다
// PUB.mapctl.Manager.agv.CurrentRFID.TurnEnd = DateTime.Now;
// PUB.mapctl.Manager.agv.CurrentRFID.TurnOK = true;
// }
// PUB.sm.UpdateRunStepSeq();
// }
// else PUB.sm.UpdateRunStepSeq();
// return false;
//}
//좌턴이동명령 전송
//마크스탑전송
//마크스탑이 확인되면 나머지는 경로예측에 맡긴다
return true; return true;
} }

View File

@@ -43,28 +43,33 @@ namespace Project
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//리프트를 내린다. // [PickOn/PickOff] 초기 리프트 동작
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN); var liftCmd = arDev.Narumi.LiftCommand.DN; // Default PickOn (Get -> Go Under -> Down)
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
{
liftCmd = arDev.Narumi.LiftCommand.UP; // PickOff (Put -> Carry In -> Up)
}
PUB.AGV.LiftControl(liftCmd);
VAR.TIME.Update(eVarTime.LastTurnCommandTime); VAR.TIME.Update(eVarTime.LastTurnCommandTime);
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
return false; return false;
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//리프트다운센서 확인한다. //리프트 센서 확인 (Direction에 따라 다름)
var liftdown = true; // 센서 확인 로직이 주석처리되어 있거나 하드코딩되어 있었음 (버퍼 코드 참조) // TODO: UP 센서 확인 로직 추가 필요 시 구현. 현재는 시간체크만 유지하거나 DN확인만 있음.
if (liftdown == false) // 일단 기존 로직 유지하되, UP일 경우 스킵 고려
{
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime); var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
if (ts.TotalSeconds > 10) if (ts.TotalSeconds > 10)
{ {
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP); // Timebound check
PUB.log.AddE("리프트 하강이 확인되지 않습니다(10초)"); // PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP);
PUB.sm.SetNewRunStep(ERunStep.ERROR); // Warning only?
return false;
} }
}
PUB.log.Add("리프트 하강 완료"); PUB.log.Add("리프트 동작 확인 완료");
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
return false; return false;
} }
@@ -78,7 +83,7 @@ namespace Project
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//저속이동 //저속이동 (후진 진입)
var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
{ {
Bunki = arDev.Narumi.eBunki.Strate, Bunki = arDev.Narumi.eBunki.Strate,
@@ -141,14 +146,41 @@ namespace Project
return false; return false;
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{
// [Action] 진입 완료 후 리프트 동작 (Pick/Drop)
PUB.log.Add("로더 진입 완료. 작업 수행(Lift Pick/Drop)");
var liftCmd = arDev.Narumi.LiftCommand.UP; // Default PickOn (Lift Up to Pick)
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
{
liftCmd = arDev.Narumi.LiftCommand.DN; // PickOff (Lift Down to Drop)
}
PUB.AGV.LiftControl(liftCmd);
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
PUB.sm.UpdateRunStepSeq();
return false;
}
else if (PUB.sm.RunStepSeq == idx++)
{
// 리프트 동작 대기
// TODO: 센서 확인
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
if (ts.TotalSeconds < 2) return false; // 2초 대기
PUB.log.Add("작업(Pick/Drop) 완료. 대기 상태로 전환");
PUB.sm.UpdateRunStepSeq();
return false;
}
else if (PUB.sm.RunStepSeq == idx++)
{ {
//완료되었다. //완료되었다.
PUB.log.Add("로더진입완료"); PUB.log.Add("로더 진입 및 작업 완료");
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
return false; return false;
} }
PUB.AddEEDB($"로더진입완료({PUB.Result.TargetPos})"); PUB.AddEEDB($"로더작업완료({PUB.Result.TargetPos})");
return true; return true;
} }
} }

View File

@@ -13,8 +13,11 @@ namespace Project
public Boolean _SM_RUN_POSCHK(bool isFirst, TimeSpan stepTime) public Boolean _SM_RUN_POSCHK(bool isFirst, TimeSpan stepTime)
{ {
//현재위치가 설정되어있는지 확인한다, 현재위치값이 있는 경우 True 를 반환 //현재위치가 설정되어있는지 확인한다, 현재위치값이 있는 경우 True 를 반환
var currentnode = PUB.FindByNodeID(PUB._virtualAGV.CurrentNodeId); if (PUB._virtualAGV.CurrentNode != null && PUB._virtualAGV.PrevNode != null)
if (currentnode != null) return true; return true;
//최소2개의 노드정보가 있어야 진행가능하므로 prevNode 값이 있는지 확인한다.
//이동을 하지 않고있다면 전진을 진행한다 //이동을 하지 않고있다면 전진을 진행한다
if (PUB.AGV.system1.agv_run == false) if (PUB.AGV.system1.agv_run == false)
@@ -30,7 +33,7 @@ namespace Project
PBSSensor = 1, PBSSensor = 1,
Speed = arDev.Narumi.eMoveSpd.Low, Speed = arDev.Narumi.eMoveSpd.Low,
}); });
PUB.AGV.AGVMoveRun(); PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward);
VAR.TIME.Update(eVarTime.LastRunCommandTime); VAR.TIME.Update(eVarTime.LastRunCommandTime);
} }
} }

View File

@@ -24,8 +24,14 @@ namespace Project
if (PUB.AGV.IsOpen == false) if (PUB.AGV.IsOpen == false)
{ {
//agv connect //agv connect
ConnectSerialPort(PUB.AGV, PUB.setting.Port_AGV, PUB.setting.Baud_AGV, var rlt = ConnectSerialPort(PUB.AGV, PUB.setting.Port_AGV, PUB.setting.Baud_AGV,
eVarTime.LastConn_AGV, eVarTime.LastConnTry_AGV, eVarTime.LastRecv_AGV); eVarTime.LastConn_AGV, eVarTime.LastConnTry_AGV, eVarTime.LastRecv_AGV);
if (rlt == false)
{
//존재하지 않는 포트라면 sync를 벗어난다
PUB.log.AddE($"AGV포트({PUB.setting.Port_AGV}) 가 존재하지않아 SYNC를 중단합니다");
PUB.sm.SetNewStep(eSMStep.IDLE);
}
} }
else if (PUB.AGV.IsValid == true) else if (PUB.AGV.IsValid == true)
{ {

View File

@@ -43,28 +43,27 @@ namespace Project
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//리프트를 내린다. // [PickOn/PickOff] 초기 리프트 동작
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN); var liftCmd = arDev.Narumi.LiftCommand.DN;
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
{
liftCmd = arDev.Narumi.LiftCommand.UP;
}
PUB.AGV.LiftControl(liftCmd);
VAR.TIME.Update(eVarTime.LastTurnCommandTime); VAR.TIME.Update(eVarTime.LastTurnCommandTime);
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
return false; return false;
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//리프트다운센서 확인한다. //리프트 센서 확인
var liftdown = true;
if (liftdown == false)
{
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime); var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
if (ts.TotalSeconds > 10) if (ts.TotalSeconds > 10)
{ {
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP); // Timebound check
PUB.log.AddE("리프트 하강이 확인되지 않습니다(10초)");
PUB.sm.SetNewRunStep(ERunStep.ERROR);
return false;
} }
} PUB.log.Add("리프트 동작 확인 완료");
PUB.log.Add("리프트 하강 완료");
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
return false; return false;
} }
@@ -78,7 +77,7 @@ namespace Project
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//저속이동 //저속이동 (후진 진입)
var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
{ {
Bunki = arDev.Narumi.eBunki.Strate, Bunki = arDev.Narumi.eBunki.Strate,
@@ -141,14 +140,40 @@ namespace Project
return false; return false;
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{
// [Action] 진입 완료 후 리프트 동작 (Pick/Drop)
PUB.log.Add("언로더 진입 완료. 작업 수행(Lift Pick/Drop)");
var liftCmd = arDev.Narumi.LiftCommand.UP;
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
{
liftCmd = arDev.Narumi.LiftCommand.DN;
}
PUB.AGV.LiftControl(liftCmd);
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
PUB.sm.UpdateRunStepSeq();
return false;
}
else if (PUB.sm.RunStepSeq == idx++)
{
// 리프트 동작 대기
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
if (ts.TotalSeconds < 2) return false;
PUB.log.Add("작업(Pick/Drop) 완료. 대기 상태로 전환");
PUB.sm.UpdateRunStepSeq();
return false;
}
else if (PUB.sm.RunStepSeq == idx++)
{ {
//완료되었다. //완료되었다.
PUB.log.Add("언로더진입완료"); PUB.log.Add("언로더 진입 및 작업 완료");
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
return false; return false;
} }
PUB.AddEEDB($"언로더진입완료({PUB.Result.TargetPos})"); PUB.AddEEDB($"언로더작업완료({PUB.Result.TargetPos})");
return true; return true;
} }
} }

View File

@@ -17,9 +17,6 @@ namespace Project
{ {
PUB.bShutdown = true; PUB.bShutdown = true;
// 장치 관리 태스크 종료
StopDeviceManagementTask();
PUB.AddEEDB("프로그램 종료"); PUB.AddEEDB("프로그램 종료");
PUB.log.Add("Program Close"); PUB.log.Add("Program Close");
PUB.LogFlush(); PUB.LogFlush();

View File

@@ -6,6 +6,7 @@ using System.Text;
using Project.StateMachine; using Project.StateMachine;
using COMM; using COMM;
using AR; using AR;
using AGVNavigationCore.Utils;
namespace Project namespace Project
{ {
@@ -14,6 +15,26 @@ namespace Project
bool CheckStopCondition() bool CheckStopCondition()
{ {
//이머전시상태라면 stop 처리한다.
if (PUB.AGV.error.Emergency &&
PUB.AGV.system1.agv_stop == true &&
PUB.AGV.system1.stop_by_front_detect == false)
{
PUB.Speak(Lang.);
PUB.sm.SetNewStep(eSMStep.IDLE);
return false;
}
//수동충전상태라면 이동하지 못한다
if (VAR.BOOL[eVarBool.FLAG_CHARGEONM])
{
PUB.Speak("수동 충전중이라 사용할 수 없습니다");
PUB.sm.SetNewStep(eSMStep.IDLE);
return false;
}
return true; return true;
} }
@@ -50,7 +71,7 @@ namespace Project
if (_SM_RUN_POSCHK(false, new TimeSpan()) == false) return false; if (_SM_RUN_POSCHK(false, new TimeSpan()) == false) return false;
//현재위치노드 오류 //현재위치노드 오류
var currentNode = PUB.FindByNodeID(PUB._virtualAGV.CurrentNodeId); var currentNode = PUB.FindByNodeID(PUB._virtualAGV.CurrentNode.Id);
if (currentNode == null) if (currentNode == null)
{ {
PUB.log.AddE($"현재위치노드가 없습니다"); PUB.log.AddE($"현재위치노드가 없습니다");
@@ -60,7 +81,7 @@ namespace Project
//시작노드값이 없다면 현재위치를 노드로 결정한다 //시작노드값이 없다면 현재위치를 노드로 결정한다
if (PUB._virtualAGV.StartNode == null) if (PUB._virtualAGV.StartNode == null)
PUB._virtualAGV.StartNode = PUB.FindByNodeID(PUB._virtualAGV.CurrentNodeId); PUB._virtualAGV.StartNode = PUB.FindByNodeID(PUB._virtualAGV.CurrentNode.Id);
//시작노드가없다면 오류 //시작노드가없다면 오류
if (PUB._virtualAGV.StartNode == null) if (PUB._virtualAGV.StartNode == null)
@@ -81,7 +102,7 @@ namespace Project
//경로 생성(경로정보가 없거나 현재노드가 경로에 없는경우) //경로 생성(경로정보가 없거나 현재노드가 경로에 없는경우)
if (PUB._virtualAGV.CurrentPath == null || if (PUB._virtualAGV.CurrentPath == null ||
PUB._virtualAGV.CurrentPath.DetailedPath.Any() == false || PUB._virtualAGV.CurrentPath.DetailedPath.Any() == false ||
PUB._virtualAGV.CurrentPath.DetailedPath.Where(t => t.NodeId.Equals(currentNode.NodeId)).Any() == false) PUB._virtualAGV.CurrentPath.DetailedPath.Where(t => t.NodeId.Equals(currentNode.Id)).Any() == false)
{ {
if (PUB.AGV.system1.agv_run) if (PUB.AGV.system1.agv_run)
{ {
@@ -96,12 +117,37 @@ namespace Project
PUB.sm.SetNewRunStep(ERunStep.READY); PUB.sm.SetNewRunStep(ERunStep.READY);
return false; return false;
} }
else
{
PUB._mapCanvas.CurrentPath = PathResult.result;
PUB._virtualAGV.SetPath(PathResult.result);
}
PUB.log.AddI($"경로생성 {PUB._virtualAGV.StartNode.RfidId} -> {PUB._virtualAGV.TargetNode.RfidId}"); PUB.log.AddI($"경로생성 {PUB._virtualAGV.StartNode.RfidId} -> {PUB._virtualAGV.TargetNode.RfidId}");
} }
//경로에 대한 무결성 검증
if (CheckPathIntegrity(PUB._virtualAGV.CurrentPath) == false)
{
if (PUB.AGV.system1.agv_run)
{
PUB.AGV.AGVMoveStop("Path Integrity Fail");
}
PUB.log.AddE($"경로 무결성 오류");
PUB.sm.SetNewRunStep(ERunStep.READY);
return false;
}
//predict 를 이용하여 다음 이동을 모두 확인한다. //predict 를 이용하여 다음 이동을 모두 확인한다.
var nextAction = PUB._virtualAGV.Predict(); var nextAction = PUB._virtualAGV.Predict();
if(nextAction.Reason == AGVNavigationCore.Models.eAGVCommandReason.PathOut)
{
//경로이탈
PUB._virtualAGV.CurrentPath.DetailedPath.Clear();
return false;
}
var message = $"[다음 행동 예측]\n\n" + var message = $"[다음 행동 예측]\n\n" +
$"모터: {nextAction.Motor}\n" + $"모터: {nextAction.Motor}\n" +
@@ -112,7 +158,7 @@ namespace Project
$"현재 상태: {PUB._virtualAGV.CurrentState}\n" + $"현재 상태: {PUB._virtualAGV.CurrentState}\n" +
$"현재 방향: {PUB._virtualAGV.CurrentDirection}\n" + $"현재 방향: {PUB._virtualAGV.CurrentDirection}\n" +
$"위치 확정: {PUB._virtualAGV.IsPositionConfirmed} (RFID {PUB._virtualAGV.DetectedRfidCount}개)\n" + $"위치 확정: {PUB._virtualAGV.IsPositionConfirmed} (RFID {PUB._virtualAGV.DetectedRfidCount}개)\n" +
$"현재 노드: {PUB._virtualAGV.CurrentNodeId ?? ""}"; $"현재 노드: {PUB._virtualAGV.CurrentNode.Id ?? ""}";
//모터에서 정지를 요청했다 //모터에서 정지를 요청했다
if (nextAction.Motor == AGVNavigationCore.Models.MotorCommand.Stop) if (nextAction.Motor == AGVNavigationCore.Models.MotorCommand.Stop)
@@ -122,7 +168,7 @@ namespace Project
// 완료(Complete) 상태라면 MarkStop 전송 // 완료(Complete) 상태라면 MarkStop 전송
if (nextAction.Reason == AGVNavigationCore.Models.eAGVCommandReason.MarkStop) if (nextAction.Reason == AGVNavigationCore.Models.eAGVCommandReason.MarkStop)
{ {
PUB.log.Add("다음행동예측에서 MARK STOP이 확인되었습니다"); PUB.log.Add("다음행동예측에서 MARK STOP이 확인되었습니다 (자동정지시퀀스)");
PUB.AGV.AGVMoveStop(nextAction.Message, arDev.Narumi.eStopOpt.MarkStop); PUB.AGV.AGVMoveStop(nextAction.Message, arDev.Narumi.eStopOpt.MarkStop);
} }
else else
@@ -136,10 +182,11 @@ namespace Project
// 현재 노드가 타겟 노드와 같고, 위치가 확정된 상태라면 도착으로 간주 // 현재 노드가 타겟 노드와 같고, 위치가 확정된 상태라면 도착으로 간주
// 단, AGV가 실제로 멈췄는지 확인 (agv_run == false) // 단, AGV가 실제로 멈췄는지 확인 (agv_run == false)
if (PUB._virtualAGV.IsPositionConfirmed && if (PUB._virtualAGV.IsPositionConfirmed &&
PUB._virtualAGV.CurrentNodeId == PUB._virtualAGV.TargetNode.NodeId) PUB._virtualAGV.CurrentNode.Id == PUB._virtualAGV.TargetNode.Id)
{ {
if (PUB.AGV.system1.agv_run == false) if (PUB.AGV.system1.agv_run == false)
{ {
PUB.log.AddI($"목표 도착 및 정지 확인됨(MarkStop 완료). Node:{PUB._virtualAGV.CurrentNode.Id}");
return true; return true;
} }
} }
@@ -210,14 +257,14 @@ namespace Project
PBSSensor = 1, PBSSensor = 1,
Speed = arDev.Narumi.eMoveSpd.Low, Speed = arDev.Narumi.eMoveSpd.Low,
}); });
PUB.AGV.AGVMoveRun();// PUB.AGV.AGVMoveRun( arDev.Narumi.eRunOpt.Forward);//
tm_gocharge_command = DateTime.Now; tm_gocharge_command = DateTime.Now;
} }
} }
else else
{ {
//현재위치가 충전위치이고, 움직이지 않았다면 완료된 경우라 할수 있따 //현재위치가 충전위치이고, 움직이지 않았다면 완료된 경우라 할수 있따
if (PUB._virtualAGV.CurrentNodeId.Equals(PUB.setting.NodeMAP_RFID_Charger) && if (PUB._virtualAGV.CurrentNode.Id.Equals(PUB.setting.NodeMAP_RFID_Charger) &&
VAR.BOOL[eVarBool.MARK_SENSOR] == true) VAR.BOOL[eVarBool.MARK_SENSOR] == true)
{ {
PUB.log.AddI("충전위치 검색 완료"); PUB.log.AddI("충전위치 검색 완료");
@@ -231,7 +278,13 @@ namespace Project
PUB.AGV.error.Emergency == false && PUB.AGV.error.Emergency == false &&
PUB.AGV.system1.agv_run == false) PUB.AGV.system1.agv_run == false)
{ {
//PUB.PLC.Move(Device.PLC.Rundirection.Backward, "UpdateMotionPosition #1(" + sender + ")"); PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
{
Bunki = arDev.Narumi.eBunki.Strate,
Direction = arDev.Narumi.eMoveDir.Backward,
PBSSensor = 1,
Speed = arDev.Narumi.eMoveSpd.Low,
});
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);// PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);//
LastCommandTime = DateTime.Now; LastCommandTime = DateTime.Now;
} }
@@ -244,5 +297,36 @@ namespace Project
/// <summary>
/// 경로 무결성(도킹방향 등) 검증
/// </summary>
/// <param name="pathResult"></param>
/// <returns></returns>
private bool CheckPathIntegrity(AGVNavigationCore.PathFinding.Core.AGVPathResult pathResult)
{
if (pathResult == null) return false;
// CalcPath에서 이미 DockingValidator를 수행했을 수 있음.
// 만약 수행되지 않았다면 여기서 수행.
if (pathResult.DockingValidation == null)
{
pathResult.DockingValidation = AGVNavigationCore.Utils.DockingValidator.ValidateDockingDirection(pathResult, PUB._mapCanvas.Nodes);
}
// 검증 결과 확인
if (pathResult.DockingValidation != null && pathResult.DockingValidation.IsValidationRequired)
{
if (pathResult.DockingValidation.IsValid == false)
{
PUB.log.AddE($"[경로무결성오류] {pathResult.DockingValidation.ValidationError}");
return false;
}
}
return true;
}
}//cvass }//cvass
} }

View File

@@ -9,6 +9,7 @@ using Project.StateMachine;
using COMM; using COMM;
using AR; using AR;
using AGVNavigationCore.Models; using AGVNavigationCore.Models;
using AGVNavigationCore.Controls;
namespace Project namespace Project
{ {
@@ -17,22 +18,19 @@ namespace Project
{ {
private void AGV_Message(object sender, arDev.Narumi.MessageEventArgs e) private void AGV_Message(object sender, arDev.Narumi.MessageEventArgs e)
{ {
if (e.MsgType == arDev.arRS232.MessageType.Normal) if (e.MsgType == arDev.NarumiSerialComm.MessageType.Normal)
PUB.logagv.AddE(e.Message); PUB.logagv.AddE(e.Message);
else if (e.MsgType == arDev.arRS232.MessageType.Normal) else if (e.MsgType == arDev.NarumiSerialComm.MessageType.Normal)
PUB.logagv.Add(e.Message); PUB.logagv.Add(e.Message);
else if (e.MsgType == arDev.arRS232.MessageType.Recv) else if (e.MsgType == arDev.NarumiSerialComm.MessageType.Recv)
{ {
if (e.Message.Substring(1).StartsWith("STS") == false) if (e.Message.Substring(1).StartsWith("STS") == false)
PUB.logagv.Add("AGV-RX", e.Message); PUB.logagv.Add("AGV-RX", e.Message);
} }
else if (e.MsgType == arDev.arRS232.MessageType.Send) else if (e.MsgType == arDev.NarumiSerialComm.MessageType.Send)
PUB.logagv.Add("AGV-TX", e.Message); PUB.logagv.Add("AGV-TX", e.Message);
else else
{ {
PUB.logagv.Add(e.MsgType.ToString(), e.Message); PUB.logagv.Add(e.MsgType.ToString(), e.Message);
} }
} }
@@ -43,6 +41,7 @@ namespace Project
{ {
try try
{ {
VAR.TIME.Set(eVarTime.LastRecv_AGV, DateTime.Now);
//데이터 파싱 //데이터 파싱
switch (e.DataType) switch (e.DataType)
{ {
@@ -54,11 +53,29 @@ namespace Project
var agv_chg = PUB.AGV.system1.Battery_charging; var agv_chg = PUB.AGV.system1.Battery_charging;
var agv_stp = PUB.AGV.system1.agv_stop; var agv_stp = PUB.AGV.system1.agv_stop;
var agv_run = PUB.AGV.system1.agv_run; var agv_run = PUB.AGV.system1.agv_run;
var agv_mrk = PUB.AGV.signal.mark_sensor; var agv_mrk = PUB.AGV.signal1.mark_sensor;
//if (chg_run && PUB.AGV.system1.agv_run) PUB.Speak("이동을 시작 합니다"); //if (chg_run && PUB.AGV.system1.agv_run) PUB.Speak("이동을 시작 합니다");
VAR.BOOL[eVarBool.AGVDIR_BACK] = PUB.AGV.data.Direction == 'B'; VAR.BOOL[eVarBool.AGVDIR_BACK] = PUB.AGV.data.Direction == 'B';
var syncDir = PUB.AGV.data.Direction == 'B' ? AgvDirection.Backward : AgvDirection.Forward;
// [Sync] Update VirtualAGV Direction
if (PUB._virtualAGV != null)
{
if (PUB._virtualAGV.CurrentDirection != syncDir)
PUB.UpdateAGVDirection(syncDir);
}
// [Sync] Update VirtualAGV State
AGVState syncState = AGVState.Idle;
if (PUB.AGV.error.Value > 0) syncState = AGVState.Error;
else if (PUB.AGV.system1.Battery_charging) syncState = AGVState.Charging;
else if (PUB.AGV.system1.agv_run) syncState = AGVState.Moving;
if (PUB._virtualAGV != null && PUB._virtualAGV.GetCurrentState() != syncState)
PUB.UpdateAGVState(syncState);
if (VAR.BOOL[eVarBool.AGV_ERROR] != (agv_err > 0)) if (VAR.BOOL[eVarBool.AGV_ERROR] != (agv_err > 0))
{ {
@@ -89,8 +106,7 @@ namespace Project
PUB.log.Add($"충전상태전환 {agv_chg}"); PUB.log.Add($"충전상태전환 {agv_chg}");
VAR.BOOL[eVarBool.FLAG_CHARGEONA] = agv_chg; VAR.BOOL[eVarBool.FLAG_CHARGEONA] = agv_chg;
} }
//자동충전해제시 곧바로 수동 충전되는 경우가 있어 자동 상태를 BMS에 넣는다 230118
PUB.BMS.AutoCharge = agv_chg;
if (PUB.AGV.error.Charger_pos_error != VAR.BOOL[eVarBool.CHG_POSERR]) if (PUB.AGV.error.Charger_pos_error != VAR.BOOL[eVarBool.CHG_POSERR])
{ {
@@ -112,10 +128,10 @@ namespace Project
} }
//마크센서 상태가 변경이 되었다면 //마크센서 상태가 변경이 되었다면
if (VAR.BOOL[eVarBool.MARK_SENSOR] != PUB.AGV.signal.mark_sensor) if (VAR.BOOL[eVarBool.MARK_SENSOR] != PUB.AGV.signal1.mark_sensor)
{ {
PUB.logagv.Add($"MARK_SENSOR 변경({PUB.AGV.signal.mark_sensor})"); PUB.logagv.Add($"MARK_SENSOR 변경({PUB.AGV.signal1.mark_sensor})");
VAR.BOOL[eVarBool.MARK_SENSOR] = PUB.AGV.signal.mark_sensor; VAR.BOOL[eVarBool.MARK_SENSOR] = PUB.AGV.signal1.mark_sensor;
//AGV가 멈췄고 마크센서가 ON되었다면 마지막 RFID 위치가 확정된경우이다 //AGV가 멈췄고 마크센서가 ON되었다면 마지막 RFID 위치가 확정된경우이다
if (agv_stp && VAR.BOOL[eVarBool.MARK_SENSOR]) if (agv_stp && VAR.BOOL[eVarBool.MARK_SENSOR])
@@ -141,9 +157,9 @@ namespace Project
case arDev.Narumi.DataType.TAG: case arDev.Narumi.DataType.TAG:
{ {
//자동 실행 중이다. //자동 실행 중이다.
PUB.log.Add($"AGV 태그수신 : {PUB.AGV.data.TagNo}");
PUB.Result.LastTAG = PUB.AGV.data.TagNo.ToString();
PUB.Result.LastTAG = PUB.AGV.data.TagNo;//.ToString("0000");
PUB.log.Add($"AGV 태그수신 : {PUB.AGV.data.TagNo} LastTag:{PUB.Result.LastTAG}");
//POT/NOT 보면 일단 바로 멈추게한다 //POT/NOT 보면 일단 바로 멈추게한다
if (PUB.Result.CurrentPos == ePosition.POT || PUB.Result.CurrentPos == ePosition.NOT) if (PUB.Result.CurrentPos == ePosition.POT || PUB.Result.CurrentPos == ePosition.NOT)
{ {
@@ -153,29 +169,26 @@ namespace Project
} }
//virtual agv setting //virtual agv setting
var CurrentNode = PUB._mapNodes.FirstOrDefault(t => t.RfidId.Equals(PUB.Result.LastTAG, StringComparison.OrdinalIgnoreCase)); var CurrentNode = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId == PUB.Result.LastTAG);
if (CurrentNode == null) if (CurrentNode == null)
{ {
//없는 노드는 자동으로 추가한다 //없는 노드는 자동으로 추가한다
var newNodeId = $"AUTO_{PUB.Result.LastTAG}"; var newNodeId = $"AUTO_{PUB.Result.LastTAG}";
var newNode = new MapNode var newNode = new MapNode
{ {
NodeId = newNodeId, Id = newNodeId,
RfidId = PUB.Result.LastTAG, RfidId = PUB.Result.LastTAG,
Name = $"자동추가_{PUB.Result.LastTAG}",
Type = NodeType.Normal,
Position = new Point(100, 100), // 기본 위치 Position = new Point(100, 100), // 기본 위치
IsActive = true, IsActive = true,
DisplayColor = Color.Orange, // 자동 추가된 노드는 오렌지색으로 표시
CreatedDate = DateTime.Now, CreatedDate = DateTime.Now,
ModifiedDate = DateTime.Now ModifiedDate = DateTime.Now
}; };
// 맵 노드 리스트에 추가 // 맵 노드 리스트에 추가
PUB._mapNodes.Add(newNode); PUB._mapCanvas.Nodes.Add(newNode);
// 캔버스에 노드 반영 (재설정) // 캔버스에 노드 반영 (재설정)
PUB._mapCanvas.Nodes = PUB._mapNodes; PUB._mapCanvas.Nodes = PUB._mapCanvas.Nodes;
// 로그 기록 // 로그 기록
PUB.log.AddI($"RFID:{PUB.Result.LastTAG} 노드를 자동 추가했습니다 (NodeId: {newNodeId})"); PUB.log.AddI($"RFID:{PUB.Result.LastTAG} 노드를 자동 추가했습니다 (NodeId: {newNodeId})");
@@ -205,7 +218,7 @@ namespace Project
} }
//이 후 상황을 예측한다 //이 후 상황을 예측한다
if (PUB._mapCanvas != null) if (PUB._mapCanvas != null && PUB._virtualAGV != null)
{ {
var nextAction = PUB._virtualAGV.Predict(); var nextAction = PUB._virtualAGV.Predict();
var message = $"[다음 행동 예측]\n\n" + var message = $"[다음 행동 예측]\n\n" +
@@ -217,11 +230,10 @@ namespace Project
$"현재 상태: {PUB._virtualAGV.CurrentState}\n" + $"현재 상태: {PUB._virtualAGV.CurrentState}\n" +
$"현재 방향: {PUB._virtualAGV.CurrentDirection}\n" + $"현재 방향: {PUB._virtualAGV.CurrentDirection}\n" +
$"위치 확정: {PUB._virtualAGV.IsPositionConfirmed} (RFID {PUB._virtualAGV.DetectedRfidCount}개)\n" + $"위치 확정: {PUB._virtualAGV.IsPositionConfirmed} (RFID {PUB._virtualAGV.DetectedRfidCount}개)\n" +
$"현재 노드: {PUB._virtualAGV.CurrentNodeId ?? ""}"; $"현재 노드: {PUB._virtualAGV.CurrentNode.Id ?? ""}";
PUB._mapCanvas.PredictMessage = message; PUB._mapCanvas.PredictMessage = message;
} }
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -25,9 +25,12 @@ namespace Project
DateTime lastbmstime = DateTime.Now; DateTime lastbmstime = DateTime.Now;
private void Bms_Message(object sender, arDev.BMS.MessageEventArgs e) private void Bms_Message(object sender, arDev.BMS.MessageEventArgs e)
{ {
if (e.MsgType == arDev.arRS232.MessageType.Error) PUB.logbms.AddE( e.Message);
if (e.MsgType == arDev.BMSSerialComm.MessageType.Error) PUB.logbms.AddE(e.Message);
else else
{ {
VAR.TIME[eVarTime.LastRecv_BAT] = DateTime.Now;
var hexstr = e.Data.GetHexString().Trim(); var hexstr = e.Data.GetHexString().Trim();
bool addlog = false; bool addlog = false;
var logtimesec = 30; var logtimesec = 30;
@@ -134,34 +137,50 @@ namespace Project
} }
if (addlog) if (addlog)
PUB.logbms.Add("BMS:" + hexstr); {
//if (e.MsgType == arDev.arRS232.MessageType.Recv)
// PUB.logbms.Add("RX", e.Data.GetHexString());
//else if (e.MsgType == arDev.arRS232.MessageType.Send)
// PUB.logbms.Add("TX", e.Data.GetHexString());
//else
{
PUB.logbms.Add(e.MsgType.ToString(),e.Message);
}
}
} }
} }
private void BMS_ChargeDetect(object sender, arDev.ChargetDetectArgs e) private void BMS_ChargeDetect(object sender, arDev.ChargetDetectArgs e)
{ {
//자동충전중이아니고 멈춰있다면 수동 충전으로 전환한다 //자동충전중이아니고 멈춰있다면 수동 충전으로 전환한다
if (PUB.AGV.system1.Battery_charging == false && PUB.AGV.system1.agv_stop == true && VAR.BOOL[eVarBool.FLAG_CHARGEONM] == false) VAR.TIME[eVarTime.LastRecv_BAT] = DateTime.Now;
if (e.Detected == true) //충전이 감지되었다.
{ {
if (PUB.setting.DetectManualCharge) if (VAR.BOOL[eVarBool.FLAG_AUTORUN] == false && VAR.BOOL[eVarBool.FLAG_CHARGEONM] == false)
{ {
VAR.BOOL[eVarBool.FLAG_CHARGEONM] = true; VAR.BOOL[eVarBool.FLAG_CHARGEONM] = true;
PUB.Speak(Lang.); PUB.Speak(Lang.);
if (PUB.AGV.system1.agv_run == true) PUB.AGV.AGVMoveStop("수동충전감지");
} }
else
{
PUB.log.Add($"충전이 감지되었지만 메뉴얼 전환 비활성화됨");
} }
else PUB.logbms.AddI("Battery Charge Off");
} }
}
private void Bms_BMSDataReceive(object sender, EventArgs e) private void Bms_BMSDataReceive(object sender, EventArgs e)
{ {
VAR.TIME[eVarTime.LastRecv_BAT] = DateTime.Now;
//PUB.mapctl.Manager.agv.BatteryLevel = PUB.BMS.Current_Level; //PUB.mapctl.Manager.agv.BatteryLevel = PUB.BMS.Current_Level;
//PUB.mapctl.Manager.agv.BatteryTemp1 = PUB.BMS.Current_temp1; //PUB.mapctl.Manager.agv.BatteryTemp1 = PUB.BMS.Current_temp1;
//PUB.mapctl.Manager.agv.BatteryTemp2 = PUB.BMS.Current_temp2; //PUB.mapctl.Manager.agv.BatteryTemp2 = PUB.BMS.Current_temp2;
// [Sync] Update VirtualAGV Battery
PUB.UpdateAGVBattery(PUB.BMS.Current_Level);
if (PUB.BMS.Current_Level <= PUB.setting.ChargeStartLevel) if (PUB.BMS.Current_Level <= PUB.setting.ChargeStartLevel)
{ {
//배터리 레벨이 기준보다 낮다면 경고를 활성화 한다 //배터리 레벨이 기준보다 낮다면 경고를 활성화 한다
@@ -184,6 +203,7 @@ namespace Project
} }
private void BMS_BMSCellDataReceive(object sender, arDev.BMSCelvoltageEventArgs e) private void BMS_BMSCellDataReceive(object sender, arDev.BMSCelvoltageEventArgs e)
{ {
VAR.TIME[eVarTime.LastRecv_BAT] = DateTime.Now;
EEMStatus.MakeBMSInformation_Cell(); EEMStatus.MakeBMSInformation_Cell();
} }
} }

View File

@@ -76,7 +76,8 @@ namespace Project
} }
} }
string lockstep = string.Empty;
System.Threading.ManualResetEvent mreloop = new System.Threading.ManualResetEvent(true);
void sm_Running(object sender, StateMachine.StateMachine.RunningEventArgs e) void sm_Running(object sender, StateMachine.StateMachine.RunningEventArgs e)
{ {
@@ -89,6 +90,11 @@ namespace Project
} }
else PUB.sm.WaitFirstRun = false; else PUB.sm.WaitFirstRun = false;
if (mreloop.WaitOne(1) == false) return;
mreloop.Reset();
lockstep = e.Step.ToString();
//main loop //main loop
switch (e.Step) switch (e.Step)
{ {
@@ -154,8 +160,6 @@ namespace Project
UpdateStatusMessage("준비 완료", Color.Red, Color.Gold); UpdateStatusMessage("준비 완료", Color.Red, Color.Gold);
if (startuptime.Year == 1982) startuptime = DateTime.Now; if (startuptime.Year == 1982) startuptime = DateTime.Now;
// 장치 관리 태스크 시작 (IDLE 진입 시 한 번만)
StartDeviceManagementTask();
// 동기화 모드 종료 (혹시 남아있을 경우) // 동기화 모드 종료 (혹시 남아있을 경우)
if (PUB._mapCanvas != null) if (PUB._mapCanvas != null)
@@ -191,8 +195,11 @@ namespace Project
if (e.isFirst) if (e.isFirst)
{ {
// 동기화 완료 시 캔버스 모드 복귀 // 동기화 완료 시 캔버스 모드 복귀
this.Invoke(new Action(() => {
if (PUB._mapCanvas != null) if (PUB._mapCanvas != null)
PUB._mapCanvas.SetSyncStatus("설정 동기화", 0f, "환경설정 값으로 AGV컨트롤러를 설정 합니다"); PUB._mapCanvas.SetSyncStatus("설정 동기화", 0f, "환경설정 값으로 AGV컨트롤러를 설정 합니다");
}));
} }
if (_SM_RUN_SYNC(runStepisFirst, PUB.sm.GetRunSteptime)) if (_SM_RUN_SYNC(runStepisFirst, PUB.sm.GetRunSteptime))
{ {
@@ -207,7 +214,6 @@ namespace Project
PUB.Speak( Lang.); PUB.Speak( Lang.);
PUB.sm.SetNewStep(eSMStep.IDLE); PUB.sm.SetNewStep(eSMStep.IDLE);
return;
} }
break; break;
@@ -271,6 +277,8 @@ namespace Project
} }
break; break;
} }
mreloop.Set();
} }
void DeleteFile(string path) void DeleteFile(string path)

View File

@@ -21,22 +21,208 @@ namespace Project
{ {
DateTime chargesynctime = DateTime.Now; DateTime chargesynctime = DateTime.Now;
DateTime agvsendstarttime = DateTime.Now; DateTime agvsendstarttime = DateTime.Now;
DateTime lastXbeStatusSendTime = DateTime.Now;
DateTime lastBmsQueryTime = DateTime.Now;
object connectobj = new object();
void sm_SPS(object sender, EventArgs e) void sm_SPS(object sender, EventArgs e)
{ {
if (PUB.sm.Step < eSMStep.IDLE || PUB.sm.Step >= eSMStep.CLOSING) return; if (PUB.sm == null || PUB.sm.Step < eSMStep.IDLE || PUB.sm.Step >= eSMStep.CLOSING || PUB.bShutdown == true) return;
// SPS는 이제 간단한 작업만 수행 // SPS는 이제 간단한 작업만 수행
// 장치 연결 및 상태 전송은 별도 태스크(_DeviceManagement.cs)에서 처리 // 장치 연결 및 상태 전송은 별도 태스크(_DeviceManagement.cs)에서 처리
// 장치 연결이 별도로 존재할때 1회 수신 후 통신이 전체 먹통되는 증상이 있어 우선 복귀 251215
try try
{ {
// 여기에 SPS에서 처리해야 할 간단한 작업만 남김
// 현재는 비어있음 - 필요한 경우 추가 // ========== 1. 장치 연결 관리 ==========
// AGV 연결
lock (connectobj)
{
ConnectSerialPort(PUB.AGV, PUB.setting.Port_AGV, PUB.setting.Baud_AGV,
eVarTime.LastConn_AGV, eVarTime.LastConnTry_AGV, eVarTime.LastRecv_AGV);
}
// XBee 연결
lock (connectobj)
{
if (VAR.BOOL[eVarBool.DISABLE_AUTOCONN_XBEE] == false)
{
ConnectSerialPort(PUB.XBE, PUB.setting.Port_XBE, PUB.setting.Baud_XBE,
eVarTime.LastConn_XBE, eVarTime.LastConnTry_XBE, null);
}
}
// BMS 연결
lock (connectobj)
{
if (PUB.BMS.IsOpen == false)
{
var ts = VAR.TIME.RUN(eVarTime.LastConn_BAT);
if (ts.TotalSeconds > 3)
{
PUB.log.Add($"BMS 연결 시도: {PUB.setting.Port_BAT}");
PUB.BMS.PortName = PUB.setting.Port_BAT;
if (PUB.BMS.Open())
PUB.log.AddI($"BMS 연결 완료({PUB.setting.Port_BAT})");
VAR.TIME.Update(eVarTime.LastConn_BAT);
VAR.TIME.Update(eVarTime.LastConnTry_BAT);
}
}
else if (PUB.BMS.IsValid == false)
{
var ts = VAR.TIME.RUN(eVarTime.LastConnTry_BAT);
if (ts.TotalSeconds > (Math.Max(10, PUB.setting.interval_bms) * 2.5))
{
this.BeginInvoke(new Action(() =>
{
PUB.log.Add("BMS 자동 연결 해제 (응답 없음)");
PUB.BMS.Close();
}));
VAR.TIME.Set(eVarTime.LastConn_BAT, DateTime.Now.AddSeconds(5));
}
}
}
// ========== 2. XBee 상태 전송 ==========
if (PUB.XBE != null && PUB.XBE.IsOpen)
{
var tsXbe = DateTime.Now - lastXbeStatusSendTime;
if (tsXbe.TotalSeconds >= PUB.setting.interval_xbe)
{
lastXbeStatusSendTime = DateTime.Now;
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
PUB.XBE.SendStatus();
}
catch (Exception ex)
{
PUB.log.AddE($"XBee SendStatus 오류: {ex.Message}");
}
});
}
}
// ========== 3. BMS 쿼리 및 배터리 경고 ==========
if (PUB.BMS != null && PUB.BMS.IsOpen)
{
var tsBms = DateTime.Now - lastBmsQueryTime;
if (tsBms.TotalSeconds >= PUB.setting.interval_bms)
{
lastBmsQueryTime = DateTime.Now;
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
PUB.BMS.SendQuery();
}
catch (Exception ex)
{
PUB.log.AddE($"BMS SendQuery 오류: {ex.Message}");
}
});
}
// 배터리 경고음
try
{
Update_BatteryWarnSpeak();
}
catch (Exception ex)
{
PUB.log.AddE($"BatteryWarnSpeak 오류: {ex.Message}");
}
}
} }
catch (Exception ex) catch (Exception ex)
{ {
PUB.log.AddE($"sm_SPS Exception: {ex.Message}"); PUB.log.AddE($"sm_SPS Exception: {ex.Message}");
} }
} }
/// <summary>
/// 시리얼 포트 연결 (arDev.arRS232)
/// </summary>
bool ConnectSerialPort(arDev.ISerialComm dev, string port, int baud, eVarTime conn, eVarTime conntry, eVarTime? recvtime)
{
if (port.isEmpty()) return false;
if (dev.IsOpen == false && port.isEmpty() == false)
{
var tsPLC = VAR.TIME.RUN(conntry);
if (tsPLC.TotalSeconds > 5)
{
VAR.TIME.Update(conntry);
try
{
if (recvtime != null) VAR.TIME.Update(recvtime);
dev.PortName = port;
dev.BaudRate = baud;
PUB.log.Add($"Connect to {port}:{baud}");
if (dev.Open())
{
if (recvtime != null) VAR.TIME[recvtime] = DateTime.Now; //값을 수신한것처럼한다
PUB.log.Add(port, $"[{port}:{baud}] 연결 완료");
}
else
{
//존재하지 않는 포트라면 sync를 벗어난다
var ports = System.IO.Ports.SerialPort.GetPortNames().Select(t => t.ToLower()).ToList();
if (ports.Contains(PUB.setting.Port_AGV.ToLower()) == false)
{
return false;
}
else
{
var errmessage = dev.ErrorMessage;
PUB.log.AddE($"[AGV:{port}:{baud}] {errmessage}");
}
}
VAR.TIME.Update(conn);
VAR.TIME.Update(conntry);
}
catch (Exception ex)
{
PUB.log.AddE(ex.Message);
}
}
}
else if (dev.PortName.Equals(port) == false)
{
this.BeginInvoke(new Action(() =>
{
PUB.log.Add(port, $"포트 변경({dev.PortName}->{port})으로 연결 종료");
VAR.TIME.Set(conntry, DateTime.Now);
dev.Close();
}));
VAR.TIME.Update(conntry);
}
else if (dev.IsOpen && recvtime != null)
{
//연결은 되었으나 통신이 지난지 10초가 지났다면 자동종료한다
var tsRecv = VAR.TIME.RUN(recvtime);
var tsConn = VAR.TIME.RUN(conntry);
if (tsRecv.TotalSeconds > 30 && tsConn.TotalSeconds > 5)
{
this.BeginInvoke(new Action(() =>
{
PUB.log.Add($"{port} 자동 연결 해제 (응답 없음)");
dev.Close();
}));
VAR.TIME.Set(conntry, DateTime.Now);
}
}
return true;
}
} }
} }

View File

@@ -570,6 +570,10 @@ namespace Project
{ {
UpdateStatusMessage("AGV 연결실패", Color.Tomato, Color.Black); UpdateStatusMessage("AGV 연결실패", Color.Tomato, Color.Black);
} }
else if(PUB.AGV.IsValid==false)
{
UpdateStatusMessage("AGV 통신상태 불량", Color.Tomato, Color.Black);
}
else if (PUB.AGV.error.Emergency) else if (PUB.AGV.error.Emergency)
{ {
if (PUB.AGV.error.runerror_by_no_magent_line) if (PUB.AGV.error.runerror_by_no_magent_line)
@@ -581,6 +585,14 @@ namespace Project
UpdateStatusMessage("비상 정지", Color.Tomato, Color.Black); UpdateStatusMessage("비상 정지", Color.Tomato, Color.Black);
} }
} }
else if (PUB.BMS != null || PUB.BMS.IsOpen==false)
{
UpdateStatusMessage("BMS가 연결되지 않았습니다", Color.Tomato, Color.Black);
}
else if (PUB.BMS != null || PUB.BMS.IsValid == false)
{
UpdateStatusMessage("BMS 통신상태 불량", Color.Tomato, Color.Black);
}
//else if (PUB.PLC.IsOpen == false) //else if (PUB.PLC.IsOpen == false)
//{ //{
// UpdateStatusMessage(Lang.PLC연결실패, Color.Tomato, Color.Black); // UpdateStatusMessage(Lang.PLC연결실패, Color.Tomato, Color.Black);
@@ -649,7 +661,7 @@ namespace Project
stMsg = Lang.; stMsg = Lang.;
//else if (PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_EMG)) //else if (PUB.PLC.GetValueI(arDev.FakePLC.DIName.PINI_EMG))
// stMsg = Lang.비상정지신호가감지되었습니다; // stMsg = Lang.비상정지신호가감지되었습니다;
else if (PUB.AGV.signal.front_gate_out == true) else if (PUB.AGV.signal1.front_gate_out == true)
stMsg = Lang.; stMsg = Lang.;
else if (PUB.AGV.error.runerror_by_no_magent_line) else if (PUB.AGV.error.runerror_by_no_magent_line)
stMsg = "마그네틱 라인을 벗어났습니다"; stMsg = "마그네틱 라인을 벗어났습니다";

View File

@@ -8,6 +8,7 @@ using AGVNavigationCore.Utils;
using AR; using AR;
using arDev; using arDev;
using COMM; using COMM;
using Project.StateMachine;
namespace Project namespace Project
{ {
@@ -47,7 +48,9 @@ namespace Project
if (data.Length > 4) if (data.Length > 4)
{ {
var currTag = System.Text.Encoding.Default.GetString(data, 1, data.Length - 1); var currTag = System.Text.Encoding.Default.GetString(data, 1, data.Length - 1);
var node = PUB._mapNodes.FirstOrDefault(t => t.RfidId == currTag); if (ushort.TryParse(currTag, out ushort currtagValue))
{
var node = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId == currtagValue);
if (node == null) if (node == null)
{ {
PUB.log.AddE($"[{logPrefix}-SetCurrent] 노드정보를 찾을 수 없습니다 RFID:{currTag}"); PUB.log.AddE($"[{logPrefix}-SetCurrent] 노드정보를 찾을 수 없습니다 RFID:{currTag}");
@@ -56,27 +59,84 @@ namespace Project
} }
else else
{ {
PUB.log.AddI($"XBEE:현재위치설정:[{node.RfidId}]{node.NodeId}"); PUB.log.AddI($"XBEE:현재위치설정:[{node.RfidId}]{node.Id}");
} }
PUB._mapCanvas.SetAGVPosition(PUB.setting.MCID, node, PUB._virtualAGV.CurrentDirection); PUB._mapCanvas.SetAGVPosition(PUB.setting.MCID, node, PUB._virtualAGV.CurrentDirection);
PUB._virtualAGV.SetPosition(node, PUB._virtualAGV.CurrentDirection); PUB._virtualAGV.SetPosition(node, PUB._virtualAGV.CurrentDirection);
} }
else PUB.log.AddE($"[{logPrefix}-SetCurrent] TagString Value Errorr:{data}");
}
else PUB.log.AddE($"[{logPrefix}-SetCurrent] TagString Lenght Errorr:{data.Length}"); else PUB.log.AddE($"[{logPrefix}-SetCurrent] TagString Lenght Errorr:{data.Length}");
break; break;
case ENIGProtocol.AGVCommandHE.Goto: //move to tag case ENIGProtocol.AGVCommandHE.PickOn: // 110
if (data.Length > 4) case ENIGProtocol.AGVCommandHE.PickOff: // 111
{ {
var currTag = System.Text.Encoding.Default.GetString(data, 1, data.Length - 1); PUB.log.AddI($"XBEE:작업명령수신:{cmd}");
var targetNode = PUB._mapNodes.FirstOrDefault(t => t.RfidId == currTag);
// 현재 위치 확인 (TargetNode가 아닌 CurrentNode 기준)
var currNode = PUB._virtualAGV.CurrentNode;
if (currNode == null)
{
PUB.log.AddE($"[{logPrefix}-{cmd}] 현재 노드를 알 수 없습니다 NodeID:{PUB._virtualAGV.CurrentNode.Id}");
PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, "Unknown Node");
return;
}
PUB.NextWorkCmd = cmd;
ERunStep nextStep = ERunStep.READY;
switch (currNode.StationType)
{
case StationType.Loader: nextStep = ERunStep.LOADER_IN; break;
case StationType.UnLoader: nextStep = ERunStep.UNLOADER_IN; break;
case StationType.Buffer: nextStep = ERunStep.BUFFER_IN; break;
case StationType.Clearner: nextStep = ERunStep.CLEANER_IN; break;
default:
PUB.log.AddE($"[{logPrefix}-{cmd}] 해당 노드타입({currNode.Type})은 작업을 지원하지 않습니다.");
return;
}
PUB.log.AddI($"작업 시작: {nextStep} (Type: {cmd})");
PUB.sm.SetNewRunStep(nextStep);
}
break;
case ENIGProtocol.AGVCommandHE.Charger: // 112
{
PUB.log.AddI($"XBEE:충전명령수신");
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Charger;
PUB.sm.SetNewRunStep(ERunStep.GOCHARGE);
}
break;
case ENIGProtocol.AGVCommandHE.GotoAlias:
case ENIGProtocol.AGVCommandHE.Goto: //move to tag
var datalength = cmd == ENIGProtocol.AGVCommandHE.GotoAlias ? 2 : 1;
if (data.Length > datalength)
{
var currTag = System.Text.Encoding.Default.GetString(data, 1, data.Length - 1).Trim();
MapNode targetNode = null;
if(cmd == ENIGProtocol.AGVCommandHE.GotoAlias)
{
targetNode = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.AliasName == currTag);
}
else
{
if (ushort.TryParse(currTag, out ushort currtagvalue))
targetNode = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId == currtagvalue);
else PUB.log.Add($"targstring 이 숫자가 아니라서 대상을 설정할 수 없습니다 값:{currTag}");
}
if (targetNode != null)
{
//자동상태가아니라면 처리하지 않는다. //자동상태가아니라면 처리하지 않는다.
if (VAR.BOOL[eVarBool.FLAG_AUTORUN] == false) if (VAR.BOOL[eVarBool.FLAG_AUTORUN] == false)
{ {
PUB.log.AddE($"[{logPrefix}-Goto] 자동실행상태가 아닙니다"); PUB.log.AddE($"[{logPrefix}-Goto] 자동실행상태가 아닙니다");
PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.ManualMode, $"{currTag}"); PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.ManualMode, $"{currTag}");
return;
} }
//목적지 //목적지
@@ -89,40 +149,46 @@ namespace Project
} }
///출발지 ///출발지
var startNode = PUB._mapNodes.FirstOrDefault(t => t.RfidId == PUB._virtualAGV.CurrentNodeId); var startNode = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId == PUB._virtualAGV.CurrentNode.RfidId);
PUB._virtualAGV.StartNode = startNode; PUB._virtualAGV.StartNode = startNode;
if (startNode == null) if (startNode == null)
{ {
PUB.log.AddE($"[{logPrefix}-Goto] 시작노드가 없습니다(현재위치 없음) NodeID:{PUB._virtualAGV.CurrentNodeId}"); PUB.log.AddE($"[{logPrefix}-Goto] 시작노드가 없습니다(현재위치 없음) NodeID:{PUB._virtualAGV.CurrentNode.Id}");
}
if (startNode != null)
{
//시작위치가 존재한다면 경로를 예측한다.
var rltGoto = CalcPath(startNode, targetNode);
if (rltGoto.result == null)
{
PUB.log.AddE($"[{logPrefix}-Goto] 경로예측실패 {rltGoto.message}");
}
else
{
//경로예측을 화면에 표시해준다.
PUB._virtualAGV.SetPath(rltGoto.result);
var pathWithRfid = rltGoto.result.GetSimplePath().Select(nodeId => PUB._virtualAGV.GetRfidByNodeId(PUB._mapNodes, nodeId)).ToList();
PUB.log.Add($"경로예측결과:{pathWithRfid}");
}
} }
//대상이동으로 처리한다. //대상이동으로 처리한다.
if(PUB.sm.RunStep != ERunStep.GOTO)
{
PUB.sm.SetNewRunStep(StateMachine.ERunStep.GOTO); PUB.sm.SetNewRunStep(StateMachine.ERunStep.GOTO);
PUB.sm.ResetRunStepSeq();
}
//Move to //Move to
PUB.log.Add($"[{logPrefix}-Goto] {startNode.RfidId} -> {targetNode.RfidId}"); PUB.log.Add($"[{logPrefix}-{cmd}] {startNode.RfidId} -> {targetNode.RfidId}");
} }
else PUB.log.AddE($"[{logPrefix}-Goto] TagString Lenght Errorr:{data.Length}"); else PUB.log.AddE($"[{logPrefix}-{cmd}] 대상노드가 없습니다 {data}");
}
else PUB.log.AddE($"[{logPrefix}-{cmd}] Length Error:{data.Length}");
break; break;
case ENIGProtocol.AGVCommandHE.LTurn180:
PUB.log.Add($"[{logPrefix}-LTurn180]");
PUB.AGV.AGVMoveLeft180Turn();
break;
case ENIGProtocol.AGVCommandHE.RTurn180:
PUB.log.Add($"[{logPrefix}-RTurn180]");
PUB.AGV.AGVMoveRight180Turn();
break;
case ENIGProtocol.AGVCommandHE.LTurn:
PUB.log.Add($"[{logPrefix}-LTurn]");
PUB.AGV.AGVMoveManual(arDev.Narumi.ManulOpt.LT, arDev.Narumi.Speed.Low, arDev.Narumi.Sensor.AllOn);
break;
case ENIGProtocol.AGVCommandHE.RTurn:
PUB.log.Add($"[{logPrefix}-RTurn]");
PUB.AGV.AGVMoveManual(arDev.Narumi.ManulOpt.RT, arDev.Narumi.Speed.Low, arDev.Narumi.Sensor.AllOn);
break;
case ENIGProtocol.AGVCommandHE.Stop: //stop case ENIGProtocol.AGVCommandHE.Stop: //stop
PUB.log.Add($"[{logPrefix}-Stop]"); PUB.log.Add($"[{logPrefix}-Stop]");
PUB.AGV.AGVMoveStop("xbee"); PUB.AGV.AGVMoveStop("xbee");
@@ -156,6 +222,7 @@ namespace Project
var MotDirection = data[1]; //0=back, 1=forward var MotDirection = data[1]; //0=back, 1=forward
var MagDirection = data[2]; //0=straight, 1=left, 2=right var MagDirection = data[2]; //0=straight, 1=left, 2=right
var AutSpeed = data[3]; //0=slow, 1=normal, 2=fast var AutSpeed = data[3]; //0=slow, 1=normal, 2=fast
var Lidar = data[4]; //0=off, 1=on
var bunkidata = new arDev.Narumi.BunkiData(); var bunkidata = new arDev.Narumi.BunkiData();
@@ -172,9 +239,12 @@ namespace Project
else if (MagDirection == 1) bunkidata.Bunki = arDev.Narumi.eBunki.Left; else if (MagDirection == 1) bunkidata.Bunki = arDev.Narumi.eBunki.Left;
else bunkidata.Bunki = arDev.Narumi.eBunki.Strate; else bunkidata.Bunki = arDev.Narumi.eBunki.Strate;
if (Lidar == 0) bunkidata.PBSSensor = 0;
else bunkidata.PBSSensor = 2;
PUB.log.Add($"[{logPrefix}-AutoMove] DIR:{bunkidata.Direction}-{bunkidata.Bunki},SPD:{bunkidata.Speed}"); PUB.log.Add($"[{logPrefix}-AutoMove] DIR:{bunkidata.Direction}-{bunkidata.Bunki},SPD:{bunkidata.Speed}");
PUB.AGV.AGVMoveSet(bunkidata); PUB.AGV.AGVMoveSet(bunkidata);
PUB.AGV.AGVMoveRun(); PUB.AGV.AGVMoveRun((MotDirection == 0 ? arDev.Narumi.eRunOpt.Backward : arDev.Narumi.eRunOpt.Forward));
break; break;
case ENIGProtocol.AGVCommandHE.MarkStop: //Set MarkStop case ENIGProtocol.AGVCommandHE.MarkStop: //Set MarkStop
@@ -205,6 +275,10 @@ namespace Project
} }
break; break;
default:
PUB.logagv.AddE($"Unknown Command : {cmd} Sender:{e.ReceivedPacket.ID}, Target:{data[0]}");
PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.UnknownCommand, $"{cmd}");
break;
} }
} }
} }
@@ -218,7 +292,7 @@ namespace Project
AGVNavigationCore.PathFinding.Planning.AGVPathfinder _advancedPathfinder = null; AGVNavigationCore.PathFinding.Planning.AGVPathfinder _advancedPathfinder = null;
(AGVNavigationCore.PathFinding.Core.AGVPathResult result, string message) CalcPath(MapNode startNode, MapNode targetNode) (AGVNavigationCore.PathFinding.Core.AGVPathResult result, string message) CalcPath(MapNode startNode, MapNode targetNode)
{ {
var _mapNodes = PUB._mapNodes; var _mapNodes = PUB._mapCanvas.Nodes;
// 시작 RFID가 없으면 AGV 현재 위치로 설정 // 시작 RFID가 없으면 AGV 현재 위치로 설정
if (startNode == null || targetNode == null) if (startNode == null || targetNode == null)
return (null, "시작 RFID와 목표 RFID를 선택해주세요."); return (null, "시작 RFID와 목표 RFID를 선택해주세요.");
@@ -242,12 +316,25 @@ namespace Project
_simulatorCanvas.FitToNodes(); _simulatorCanvas.FitToNodes();
string Message = string.Empty; string Message = string.Empty;
if (advancedResult.Success) if (advancedResult != null && advancedResult.Success)
{ {
// 도킹 검증이 없는 경우 추가 검증 수행 // 도킹 검증이 없는 경우 추가 검증 수행
if (advancedResult.DockingValidation == null || !advancedResult.DockingValidation.IsValidationRequired) if (advancedResult.DockingValidation == null || !advancedResult.DockingValidation.IsValidationRequired)
advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _mapNodes); advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _mapNodes);
//마지막대상이 버퍼라면 시퀀스처리를 해야한다
if (targetNode.StationType == StationType.Buffer&& advancedResult.DetailedPath.Any())
{
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; _simulatorCanvas.CurrentPath = advancedResult;
//_pathLengthLabel.Text = $"경로 길이: {advancedResult.TotalDistance:F1}"; //_pathLengthLabel.Text = $"경로 길이: {advancedResult.TotalDistance:F1}";
//_statusLabel.Text = $"경로 계산 완료 ({advancedResult.CalculationTimeMs}ms)"; //_statusLabel.Text = $"경로 계산 완료 ({advancedResult.CalculationTimeMs}ms)";
@@ -262,12 +349,12 @@ namespace Project
//UpdateAdvancedPathDebugInfo(advancedResult); //UpdateAdvancedPathDebugInfo(advancedResult);
} }
else else if(advancedResult != null)
{ {
// 경로 실패시 디버깅 정보 초기화 // 경로 실패시 디버깅 정보 초기화
//_pathDebugLabel.Text = $"경로: 실패 - {advancedResult.ErrorMessage}"; //_pathDebugLabel.Text = $"경로: 실패 - {advancedResult.ErrorMessage}";
advancedResult = null;
Message = $"경로를 찾을 수 없습니다:\n{advancedResult.ErrorMessage}"; Message = $"경로를 찾을 수 없습니다:\n{advancedResult.ErrorMessage}";
advancedResult = null;
} }
return (advancedResult, Message); return (advancedResult, Message);

View File

@@ -30,17 +30,18 @@
{ {
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.richTextBox1 = new System.Windows.Forms.RichTextBox(); this.rtSystem0 = new System.Windows.Forms.RichTextBox();
this.richTextBox2 = new System.Windows.Forms.RichTextBox(); this.rtSystem1 = new System.Windows.Forms.RichTextBox();
this.richTextBox3 = new System.Windows.Forms.RichTextBox(); this.rtSignal1 = new System.Windows.Forms.RichTextBox();
this.richTextBox4 = new System.Windows.Forms.RichTextBox(); this.rtError = new System.Windows.Forms.RichTextBox();
this.label1 = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label();
this.timer1 = new System.Windows.Forms.Timer(this.components); this.timer1 = new System.Windows.Forms.Timer(this.components);
this.panel1 = new System.Windows.Forms.Panel(); this.panel1 = new System.Windows.Forms.Panel();
this.lbIP = new System.Windows.Forms.Label();
this.button7 = new System.Windows.Forms.Button(); this.button7 = new System.Windows.Forms.Button();
this.button6 = new System.Windows.Forms.Button(); this.button6 = new System.Windows.Forms.Button();
this.button5 = new System.Windows.Forms.Button(); this.button5 = new System.Windows.Forms.Button();
this.button17 = new System.Windows.Forms.Button();
this.lbIP = new System.Windows.Forms.Label();
this.button3 = new System.Windows.Forms.Button(); this.button3 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button(); this.button2 = new System.Windows.Forms.Button();
this.button1 = new System.Windows.Forms.Button(); this.button1 = new System.Windows.Forms.Button();
@@ -48,13 +49,16 @@
this.button4 = new System.Windows.Forms.Button(); this.button4 = new System.Windows.Forms.Button();
this.button9 = new System.Windows.Forms.Button(); this.button9 = new System.Windows.Forms.Button();
this.panel2 = new System.Windows.Forms.Panel(); this.panel2 = new System.Windows.Forms.Panel();
this.button16 = new System.Windows.Forms.Button(); this.lbPortName = new System.Windows.Forms.Label();
this.button10 = new System.Windows.Forms.Button(); this.button15 = new System.Windows.Forms.Button();
this.button14 = new System.Windows.Forms.Button();
this.button11 = new System.Windows.Forms.Button(); this.button11 = new System.Windows.Forms.Button();
this.button12 = new System.Windows.Forms.Button(); this.button12 = new System.Windows.Forms.Button();
this.button13 = new System.Windows.Forms.Button(); this.button13 = new System.Windows.Forms.Button();
this.button14 = new System.Windows.Forms.Button(); this.button10 = new System.Windows.Forms.Button();
this.button15 = new System.Windows.Forms.Button(); this.button16 = new System.Windows.Forms.Button();
this.rtData = new System.Windows.Forms.RichTextBox();
this.rtSignal2 = new System.Windows.Forms.RichTextBox();
this.tableLayoutPanel1.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout();
this.panel1.SuspendLayout(); this.panel1.SuspendLayout();
this.panel2.SuspendLayout(); this.panel2.SuspendLayout();
@@ -67,11 +71,13 @@
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.Controls.Add(this.richTextBox1, 0, 0); this.tableLayoutPanel1.Controls.Add(this.rtSignal2, 2, 1);
this.tableLayoutPanel1.Controls.Add(this.richTextBox2, 1, 0); this.tableLayoutPanel1.Controls.Add(this.rtSystem0, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.richTextBox3, 2, 0); this.tableLayoutPanel1.Controls.Add(this.rtSystem1, 1, 0);
this.tableLayoutPanel1.Controls.Add(this.richTextBox4, 3, 0); this.tableLayoutPanel1.Controls.Add(this.rtSignal1, 2, 0);
this.tableLayoutPanel1.Controls.Add(this.rtError, 3, 0);
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 2); this.tableLayoutPanel1.Controls.Add(this.label1, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.rtData, 3, 1);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 3); this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 3);
this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.Name = "tableLayoutPanel1";
@@ -82,45 +88,47 @@
this.tableLayoutPanel1.Size = new System.Drawing.Size(1050, 461); this.tableLayoutPanel1.Size = new System.Drawing.Size(1050, 461);
this.tableLayoutPanel1.TabIndex = 6; this.tableLayoutPanel1.TabIndex = 6;
// //
// richTextBox1 // rtSystem0
// //
this.richTextBox1.Dock = System.Windows.Forms.DockStyle.Fill; this.rtSystem0.Dock = System.Windows.Forms.DockStyle.Fill;
this.richTextBox1.Location = new System.Drawing.Point(3, 3); this.rtSystem0.Font = new System.Drawing.Font("Calibri", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(2)));
this.richTextBox1.Name = "richTextBox1"; this.rtSystem0.Location = new System.Drawing.Point(3, 3);
this.tableLayoutPanel1.SetRowSpan(this.richTextBox1, 2); this.rtSystem0.Name = "rtSystem0";
this.richTextBox1.Size = new System.Drawing.Size(256, 404); this.tableLayoutPanel1.SetRowSpan(this.rtSystem0, 2);
this.richTextBox1.TabIndex = 1; this.rtSystem0.Size = new System.Drawing.Size(256, 404);
this.richTextBox1.Text = ""; this.rtSystem0.TabIndex = 1;
this.rtSystem0.Text = "test2\ntest3\nteat\nasdfjalsdf\nasdjfklasdfj\nkalsdjfalksdjfa\nsdjfklasdjfklasjdf\n";
// //
// richTextBox2 // rtSystem1
// //
this.richTextBox2.Dock = System.Windows.Forms.DockStyle.Fill; this.rtSystem1.Dock = System.Windows.Forms.DockStyle.Fill;
this.richTextBox2.Location = new System.Drawing.Point(265, 3); this.rtSystem1.Font = new System.Drawing.Font("Calibri", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(2)));
this.richTextBox2.Name = "richTextBox2"; this.rtSystem1.Location = new System.Drawing.Point(265, 3);
this.tableLayoutPanel1.SetRowSpan(this.richTextBox2, 2); this.rtSystem1.Name = "rtSystem1";
this.richTextBox2.Size = new System.Drawing.Size(256, 404); this.tableLayoutPanel1.SetRowSpan(this.rtSystem1, 2);
this.richTextBox2.TabIndex = 1; this.rtSystem1.Size = new System.Drawing.Size(256, 404);
this.richTextBox2.Text = ""; this.rtSystem1.TabIndex = 1;
this.rtSystem1.Text = "test2\ntest3\nteat\nasdfjalsdf\nasdjfklasdfj\nkalsdjfalksdjfa\nsdjfklasdjfklasjdf\n";
// //
// richTextBox3 // rtSignal1
// //
this.richTextBox3.Dock = System.Windows.Forms.DockStyle.Fill; this.rtSignal1.Dock = System.Windows.Forms.DockStyle.Fill;
this.richTextBox3.Location = new System.Drawing.Point(527, 3); this.rtSignal1.Font = new System.Drawing.Font("Calibri", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(2)));
this.richTextBox3.Name = "richTextBox3"; this.rtSignal1.Location = new System.Drawing.Point(527, 3);
this.tableLayoutPanel1.SetRowSpan(this.richTextBox3, 2); this.rtSignal1.Name = "rtSignal1";
this.richTextBox3.Size = new System.Drawing.Size(256, 404); this.rtSignal1.Size = new System.Drawing.Size(256, 199);
this.richTextBox3.TabIndex = 1; this.rtSignal1.TabIndex = 1;
this.richTextBox3.Text = ""; this.rtSignal1.Text = "test2\ntest3\nteat\nasdfjalsdf\nasdjfklasdfj\nkalsdjfalksdjfa\nsdjfklasdjfklasjdf\n";
// //
// richTextBox4 // rtError
// //
this.richTextBox4.Dock = System.Windows.Forms.DockStyle.Fill; this.rtError.Dock = System.Windows.Forms.DockStyle.Fill;
this.richTextBox4.Location = new System.Drawing.Point(789, 3); this.rtError.Font = new System.Drawing.Font("Calibri", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(2)));
this.richTextBox4.Name = "richTextBox4"; this.rtError.Location = new System.Drawing.Point(789, 3);
this.tableLayoutPanel1.SetRowSpan(this.richTextBox4, 2); this.rtError.Name = "rtError";
this.richTextBox4.Size = new System.Drawing.Size(258, 404); this.rtError.Size = new System.Drawing.Size(258, 199);
this.richTextBox4.TabIndex = 1; this.rtError.TabIndex = 1;
this.richTextBox4.Text = ""; this.rtError.Text = "test2\ntest3\nteat\nasdfjalsdf\nasdjfklasdfj\nkalsdjfalksdjfa\nsdjfklasdjfklasjdf\n";
// //
// label1 // label1
// //
@@ -141,10 +149,11 @@
// //
// panel1 // panel1
// //
this.panel1.Controls.Add(this.lbIP);
this.panel1.Controls.Add(this.button7); this.panel1.Controls.Add(this.button7);
this.panel1.Controls.Add(this.button6); this.panel1.Controls.Add(this.button6);
this.panel1.Controls.Add(this.button5); this.panel1.Controls.Add(this.button5);
this.panel1.Controls.Add(this.button17);
this.panel1.Controls.Add(this.lbIP);
this.panel1.Controls.Add(this.button3); this.panel1.Controls.Add(this.button3);
this.panel1.Controls.Add(this.button2); this.panel1.Controls.Add(this.button2);
this.panel1.Controls.Add(this.button1); this.panel1.Controls.Add(this.button1);
@@ -156,22 +165,10 @@
this.panel1.Size = new System.Drawing.Size(1050, 58); this.panel1.Size = new System.Drawing.Size(1050, 58);
this.panel1.TabIndex = 7; this.panel1.TabIndex = 7;
// //
// lbIP
//
this.lbIP.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbIP.Font = new System.Drawing.Font("Tahoma", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.lbIP.ForeColor = System.Drawing.Color.White;
this.lbIP.Location = new System.Drawing.Point(252, 0);
this.lbIP.Name = "lbIP";
this.lbIP.Size = new System.Drawing.Size(398, 58);
this.lbIP.TabIndex = 8;
this.lbIP.Text = "000.000.000.000";
this.lbIP.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// button7 // button7
// //
this.button7.Dock = System.Windows.Forms.DockStyle.Right; this.button7.Dock = System.Windows.Forms.DockStyle.Right;
this.button7.Location = new System.Drawing.Point(650, 0); this.button7.Location = new System.Drawing.Point(570, 0);
this.button7.Name = "button7"; this.button7.Name = "button7";
this.button7.Size = new System.Drawing.Size(80, 58); this.button7.Size = new System.Drawing.Size(80, 58);
this.button7.TabIndex = 6; this.button7.TabIndex = 6;
@@ -182,7 +179,7 @@
// button6 // button6
// //
this.button6.Dock = System.Windows.Forms.DockStyle.Right; this.button6.Dock = System.Windows.Forms.DockStyle.Right;
this.button6.Location = new System.Drawing.Point(730, 0); this.button6.Location = new System.Drawing.Point(650, 0);
this.button6.Name = "button6"; this.button6.Name = "button6";
this.button6.Size = new System.Drawing.Size(80, 58); this.button6.Size = new System.Drawing.Size(80, 58);
this.button6.TabIndex = 5; this.button6.TabIndex = 5;
@@ -193,7 +190,7 @@
// button5 // button5
// //
this.button5.Dock = System.Windows.Forms.DockStyle.Right; this.button5.Dock = System.Windows.Forms.DockStyle.Right;
this.button5.Location = new System.Drawing.Point(810, 0); this.button5.Location = new System.Drawing.Point(730, 0);
this.button5.Name = "button5"; this.button5.Name = "button5";
this.button5.Size = new System.Drawing.Size(80, 58); this.button5.Size = new System.Drawing.Size(80, 58);
this.button5.TabIndex = 4; this.button5.TabIndex = 4;
@@ -201,6 +198,29 @@
this.button5.UseVisualStyleBackColor = true; this.button5.UseVisualStyleBackColor = true;
this.button5.Click += new System.EventHandler(this.button5_Click); this.button5.Click += new System.EventHandler(this.button5_Click);
// //
// button17
//
this.button17.Dock = System.Windows.Forms.DockStyle.Right;
this.button17.Location = new System.Drawing.Point(810, 0);
this.button17.Name = "button17";
this.button17.Size = new System.Drawing.Size(80, 58);
this.button17.TabIndex = 9;
this.button17.Text = "Run(Bwd)";
this.button17.UseVisualStyleBackColor = true;
this.button17.Click += new System.EventHandler(this.button17_Click);
//
// lbIP
//
this.lbIP.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbIP.Font = new System.Drawing.Font("Tahoma", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.lbIP.ForeColor = System.Drawing.Color.White;
this.lbIP.Location = new System.Drawing.Point(252, 0);
this.lbIP.Name = "lbIP";
this.lbIP.Size = new System.Drawing.Size(638, 58);
this.lbIP.TabIndex = 8;
this.lbIP.Text = "000.000.000.000";
this.lbIP.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// button3 // button3
// //
this.button3.Dock = System.Windows.Forms.DockStyle.Left; this.button3.Dock = System.Windows.Forms.DockStyle.Left;
@@ -241,7 +261,7 @@
this.button8.Name = "button8"; this.button8.Name = "button8";
this.button8.Size = new System.Drawing.Size(80, 58); this.button8.Size = new System.Drawing.Size(80, 58);
this.button8.TabIndex = 7; this.button8.TabIndex = 7;
this.button8.Text = "Run"; this.button8.Text = "Run(Fwd)";
this.button8.UseVisualStyleBackColor = true; this.button8.UseVisualStyleBackColor = true;
this.button8.Click += new System.EventHandler(this.button8_Click); this.button8.Click += new System.EventHandler(this.button8_Click);
// //
@@ -269,6 +289,7 @@
// //
// panel2 // panel2
// //
this.panel2.Controls.Add(this.lbPortName);
this.panel2.Controls.Add(this.button15); this.panel2.Controls.Add(this.button15);
this.panel2.Controls.Add(this.button14); this.panel2.Controls.Add(this.button14);
this.panel2.Controls.Add(this.button11); this.panel2.Controls.Add(this.button11);
@@ -283,27 +304,39 @@
this.panel2.Size = new System.Drawing.Size(1050, 58); this.panel2.Size = new System.Drawing.Size(1050, 58);
this.panel2.TabIndex = 8; this.panel2.TabIndex = 8;
// //
// button16 // lbPortName
// //
this.button16.Dock = System.Windows.Forms.DockStyle.Left; this.lbPortName.Dock = System.Windows.Forms.DockStyle.Fill;
this.button16.Location = new System.Drawing.Point(0, 0); this.lbPortName.Font = new System.Drawing.Font("Tahoma", 15F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.button16.Name = "button16"; this.lbPortName.ForeColor = System.Drawing.Color.White;
this.button16.Size = new System.Drawing.Size(162, 58); this.lbPortName.Location = new System.Drawing.Point(607, 0);
this.button16.TabIndex = 0; this.lbPortName.Name = "lbPortName";
this.button16.Text = "백턴유지시간"; this.lbPortName.Size = new System.Drawing.Size(203, 58);
this.button16.UseVisualStyleBackColor = true; this.lbPortName.TabIndex = 15;
this.button16.Click += new System.EventHandler(this.button16_Click); this.lbPortName.Text = "--";
this.lbPortName.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
// //
// button10 // button15
// //
this.button10.Dock = System.Windows.Forms.DockStyle.Left; this.button15.Dock = System.Windows.Forms.DockStyle.Left;
this.button10.Location = new System.Drawing.Point(162, 0); this.button15.Location = new System.Drawing.Point(523, 0);
this.button10.Name = "button10"; this.button15.Name = "button15";
this.button10.Size = new System.Drawing.Size(162, 58); this.button15.Size = new System.Drawing.Size(84, 58);
this.button10.TabIndex = 1; this.button15.TabIndex = 14;
this.button10.Text = "GateOut Off Time"; this.button15.Text = "Mag Off";
this.button10.UseVisualStyleBackColor = true; this.button15.UseVisualStyleBackColor = true;
this.button10.Click += new System.EventHandler(this.button10_Click); this.button15.Click += new System.EventHandler(this.button15_Click);
//
// button14
//
this.button14.Dock = System.Windows.Forms.DockStyle.Left;
this.button14.Location = new System.Drawing.Point(439, 0);
this.button14.Name = "button14";
this.button14.Size = new System.Drawing.Size(84, 58);
this.button14.TabIndex = 13;
this.button14.Text = "Mag On";
this.button14.UseVisualStyleBackColor = true;
this.button14.Click += new System.EventHandler(this.button14_Click);
// //
// button11 // button11
// //
@@ -338,27 +371,47 @@
this.button13.UseVisualStyleBackColor = true; this.button13.UseVisualStyleBackColor = true;
this.button13.Click += new System.EventHandler(this.button13_Click); this.button13.Click += new System.EventHandler(this.button13_Click);
// //
// button14 // button10
// //
this.button14.Dock = System.Windows.Forms.DockStyle.Left; this.button10.Dock = System.Windows.Forms.DockStyle.Left;
this.button14.Location = new System.Drawing.Point(439, 0); this.button10.Location = new System.Drawing.Point(162, 0);
this.button14.Name = "button14"; this.button10.Name = "button10";
this.button14.Size = new System.Drawing.Size(84, 58); this.button10.Size = new System.Drawing.Size(162, 58);
this.button14.TabIndex = 13; this.button10.TabIndex = 1;
this.button14.Text = "Mag On"; this.button10.Text = "GateOut Off Time";
this.button14.UseVisualStyleBackColor = true; this.button10.UseVisualStyleBackColor = true;
this.button14.Click += new System.EventHandler(this.button14_Click); this.button10.Click += new System.EventHandler(this.button10_Click);
// //
// button15 // button16
// //
this.button15.Dock = System.Windows.Forms.DockStyle.Left; this.button16.Dock = System.Windows.Forms.DockStyle.Left;
this.button15.Location = new System.Drawing.Point(523, 0); this.button16.Location = new System.Drawing.Point(0, 0);
this.button15.Name = "button15"; this.button16.Name = "button16";
this.button15.Size = new System.Drawing.Size(84, 58); this.button16.Size = new System.Drawing.Size(162, 58);
this.button15.TabIndex = 14; this.button16.TabIndex = 0;
this.button15.Text = "Mag Off"; this.button16.Text = "백턴유지시간";
this.button15.UseVisualStyleBackColor = true; this.button16.UseVisualStyleBackColor = true;
this.button15.Click += new System.EventHandler(this.button15_Click); this.button16.Click += new System.EventHandler(this.button16_Click);
//
// rtData
//
this.rtData.Dock = System.Windows.Forms.DockStyle.Fill;
this.rtData.Font = new System.Drawing.Font("Calibri", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(2)));
this.rtData.Location = new System.Drawing.Point(789, 208);
this.rtData.Name = "rtData";
this.rtData.Size = new System.Drawing.Size(258, 199);
this.rtData.TabIndex = 3;
this.rtData.Text = "test2\ntest3\nteat\nasdfjalsdf\nasdjfklasdfj\nkalsdjfalksdjfa\nsdjfklasdjfklasjdf\n";
//
// rtSignal2
//
this.rtSignal2.Dock = System.Windows.Forms.DockStyle.Fill;
this.rtSignal2.Font = new System.Drawing.Font("Calibri", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(2)));
this.rtSignal2.Location = new System.Drawing.Point(527, 208);
this.rtSignal2.Name = "rtSignal2";
this.rtSignal2.Size = new System.Drawing.Size(256, 199);
this.rtSignal2.TabIndex = 4;
this.rtSignal2.Text = "test2\ntest3\nteat\nasdfjalsdf\nasdjfklasdfj\nkalsdjfalksdjfa\nsdjfklasdjfklasjdf\n";
// //
// fAgv // fAgv
// //
@@ -385,10 +438,10 @@
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Timer timer1; private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.RichTextBox richTextBox1; private System.Windows.Forms.RichTextBox rtSystem0;
private System.Windows.Forms.RichTextBox richTextBox2; private System.Windows.Forms.RichTextBox rtSystem1;
private System.Windows.Forms.RichTextBox richTextBox3; private System.Windows.Forms.RichTextBox rtSignal1;
private System.Windows.Forms.RichTextBox richTextBox4; private System.Windows.Forms.RichTextBox rtError;
private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Button button1; private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2; private System.Windows.Forms.Button button2;
@@ -409,5 +462,9 @@
private System.Windows.Forms.Button button11; private System.Windows.Forms.Button button11;
private System.Windows.Forms.Button button12; private System.Windows.Forms.Button button12;
private System.Windows.Forms.Button button13; private System.Windows.Forms.Button button13;
private System.Windows.Forms.Label lbPortName;
private System.Windows.Forms.Button button17;
private System.Windows.Forms.RichTextBox rtData;
private System.Windows.Forms.RichTextBox rtSignal2;
} }
} }

View File

@@ -38,11 +38,13 @@ namespace Project.ViewForm
lbIP.Text = PUB.IP; lbIP.Text = PUB.IP;
label1.Text = PUB.AGV.LastSTS; label1.Text = PUB.AGV.LastSTS;
richTextBox1.Rtf = PUB.AGV.system0.ToRtfString(); rtSystem0.Rtf = PUB.AGV.system0.ToRtfString();
richTextBox2.Rtf = PUB.AGV.system1.ToRtfString(); rtSystem1.Rtf = PUB.AGV.system1.ToRtfString();
richTextBox3.Rtf = CombineRtfStrings(PUB.AGV.signal.ToRtfString(), PUB.AGV.data.ToRtfString()); rtSignal1.Rtf = PUB.AGV.signal1.ToRtfString();
richTextBox4.Rtf = PUB.AGV.error.ToRtfString(); rtSignal2.Rtf = PUB.AGV.signal2.ToRtfString();
rtData.Rtf = PUB.AGV.data.ToRtfString();
rtError.Rtf = PUB.AGV.error.ToRtfString();
lbPortName.Text = $"AGV:{PUB.setting.Port_AGV}\nBMS:{PUB.setting.Port_BAT}";
timer1.Start(); timer1.Start();
} }
@@ -133,7 +135,7 @@ namespace Project.ViewForm
private void button8_Click(object sender, EventArgs e) private void button8_Click(object sender, EventArgs e)
{ {
PUB.AGV.AGVMoveRun(); PUB.AGV.AGVMoveRun( arDev.Narumi.eRunOpt.Forward);
} }
private void button9_Click(object sender, EventArgs e) private void button9_Click(object sender, EventArgs e)
@@ -190,5 +192,10 @@ namespace Project.ViewForm
{ {
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP); PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP);
} }
private void button17_Click(object sender, EventArgs e)
{
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);
}
} }
} }

View File

@@ -30,150 +30,122 @@ namespace Project.ViewForm
InitializeMapCanvas(); InitializeMapCanvas();
//PUB.mapctl = new AGVControl.MapControl();
//PUB.mapctl.Dock = DockStyle.Fill;
//PUB.mapctl.Visible = true;
//PUB.mapctl.Font = this.panel1.Font;
//PUB.mapctl.BackColor = Color.FromArgb(32, 32, 32);
//this.panel1.Controls.Add(PUB.mapctl);
} }
private void InitializeMapCanvas() private void InitializeMapCanvas()
{ {
PUB._mapCanvas = new AGVNavigationCore.Controls.UnifiedAGVCanvas(); PUB._mapCanvas.NodeSelect += OnNodeSelected;;
PUB._mapCanvas.Dock = DockStyle.Fill;
PUB._mapCanvas.ShowGrid = false;
PUB._mapCanvas.BackColor = Color.FromArgb(32, 32, 32);
PUB._mapCanvas.ForeColor = Color.White;
// RfidMappings 제거 - MapNode에 통합됨
// 이벤트 연결
//PUB._mapCanvas.NodeAdded += OnNodeAdded;
//PUB._mapCanvas.NodeSelected += OnNodeSelected;
//PUB._mapCanvas.NodeMoved += OnNodeMoved;
//PUB._mapCanvas.NodeDeleted += OnNodeDeleted;
//PUB._mapCanvas.ConnectionDeleted += OnConnectionDeleted;
//PUB._mapCanvas.ImageNodeDoubleClicked += OnImageNodeDoubleClicked;
//PUB._mapCanvas.MapChanged += OnMapChanged;
// 스플리터 패널에 맵 캔버스 추가 // 스플리터 패널에 맵 캔버스 추가
panel1.Controls.Add(PUB._mapCanvas); panel1.Controls.Add(PUB._mapCanvas);
}
// 툴바 버튼 이벤트 연결
//WireToolbarButtonEvents(); private void OnNodeSelected(object sender, NodeBase node, MouseEventArgs e)
{
if (node == null) return;
var mapnode = node as MapNode;
if (mapnode == null) return;
// [Run Mode] Left Click: AGV Operation
if (PUB._mapCanvas.Mode == AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Run && e.Button == MouseButtons.Left)
{
HandleRunModeClick(mapnode);
return;
}
if (e.Button != MouseButtons.Right) return;
// 도킹 가능한 노드인지 또는 작업 노드인지 확인
if (mapnode.isDockingNode == false) return;
ContextMenuStrip menu = new ContextMenuStrip();
// PickOn
var pickOn = new ToolStripMenuItem("Pick On (Move & Pick)");
pickOn.Click += (s, args) => ExecuteManualCommand(mapnode, ENIGProtocol.AGVCommandHE.PickOn);
menu.Items.Add(pickOn);
// PickOff
var pickOff = new ToolStripMenuItem("Pick Off (Move & Drop)");
pickOff.Click += (s, args) => ExecuteManualCommand(mapnode, ENIGProtocol.AGVCommandHE.PickOff);
menu.Items.Add(pickOff);
// Charge
if (mapnode.StationType == StationType.Charger)
{
var charge = new ToolStripMenuItem("Charge (Move & Charge)");
charge.Click += (s, args) => ExecuteManualCommand(mapnode, ENIGProtocol.AGVCommandHE.Charger);
menu.Items.Add(charge);
}
menu.Show(Cursor.Position);
} }
private void ExecuteManualCommand(MapNode targetNode, ENIGProtocol.AGVCommandHE cmd)
{
if (PUB._virtualAGV.CurrentNode == null)
{
MessageBox.Show("AGV의 현재 위치를 알 수 없습니다.");
return;
}
if (PUB.sm.Step == eSMStep.IDLE)
{
if (MessageBox.Show("현재 대기상태가 아닙니다. 강제로 실행하시겠습니까?", "Warning", MessageBoxButtons.YesNo) == DialogResult.No) return;
}
if (targetNode.isDockingNode == false)
{
UTIL.MsgE("이동 가능한 노드가 아닙니다");
}
// 1. 경로 생성
var pathFinder = new AGVNavigationCore.PathFinding.Planning.AGVPathfinder(PUB._mapCanvas.Nodes);
// 현재위치에서 목표위치까지
var result = pathFinder.FindPath(PUB._virtualAGV.CurrentNode, targetNode);
if (!result.Success || result.Path == null || result.Path.Count == 0)
{
MessageBox.Show("경로를 찾을 수 없습니다.");
return;
}
// 2. 상태 설정
// 2. 상태 설정
if (targetNode is MapNode mapno)
PUB.log.AddI($"[Manual Command] {cmd} to {mapno.RfidId}({targetNode.Id})");
else
PUB.log.AddI($"[Manual Command] {cmd} to ({targetNode.Id})");
// FindPathResult contains DetailedPath already.
PUB._virtualAGV.SetPath(result);
PUB._virtualAGV.TargetNode = targetNode as MapNode;
// 3. 작업 설정
PUB.NextWorkCmd = cmd;
// 4. 실행
PUB.sm.SetNewRunStep(ERunStep.GOTO); // GOTO -> Arrive -> _IN sequence execution
}
private void fAuto_Load(object sender, EventArgs e) private void fAuto_Load(object sender, EventArgs e)
{ {
ctlAuto1.dev_agv = PUB.AGV; ctlAuto1.dev_agv = PUB.AGV;
// ctlAuto1.dev_plc = PUB.PLC;
ctlAuto1.dev_bms = PUB.BMS; ctlAuto1.dev_bms = PUB.BMS;
ctlAuto1.dev_xbe = PUB.XBE; ctlAuto1.dev_xbe = PUB.XBE;
PUB.AGV.DataReceive += AGV_DataReceive; PUB.AGV.DataReceive += AGV_DataReceive;
//auto load
var mapPath = new System.IO.DirectoryInfo("route");
if (mapPath.Exists == false) mapPath.Create();
//맵파일로딩
if (PUB.setting.LastMapFile.isEmpty()) PUB.setting.LastMapFile = System.IO.Path.Combine(mapPath.FullName, "default.agvmap");
System.IO.FileInfo filePath = new System.IO.FileInfo(PUB.setting.LastMapFile);
if (filePath.Exists == false) filePath = new System.IO.FileInfo(System.IO.Path.Combine(mapPath.FullName,"default.agvmap"));
if(filePath.Exists==false) //그래도없다면 맵폴더에서 파일을 찾아본다.
{
var files = mapPath.GetFiles("*.agvmap");
if (files.Any()) filePath = files[0];
}
if (filePath.Exists)
{
var result = MapLoader.LoadMapFromFile(filePath.FullName);
if (result.Success)
{
if (PUB._mapNodes == null) PUB._mapNodes = new List<MapNode>();
else PUB._mapNodes.Clear();
PUB._mapNodes.AddRange(result.Nodes);
// 맵 캔버스에 데이터 설정
PUB._mapCanvas.Nodes = PUB._mapNodes;
PUB._mapCanvas.MapFileName = filePath.FullName;
// 🔥 맵 설정 적용 (배경색, 그리드 표시)
if (result.Settings != null)
{
PUB._mapCanvas.BackColor = System.Drawing.Color.FromArgb(result.Settings.BackgroundColorArgb);
PUB._mapCanvas.ShowGrid = result.Settings.ShowGrid;
}
// 🔥 가상 AGV 초기화 (첫 노드 위치에 생성)
if (PUB._virtualAGV == null && PUB._mapNodes.Count > 0)
{
var startNode = PUB._mapNodes.FirstOrDefault(n => n.IsNavigationNode());
if (startNode != null)
{
PUB._virtualAGV = new VirtualAGV(PUB.setting.MCID, startNode.Position, AgvDirection.Forward);
PUB._virtualAGV.SetPosition(startNode, AgvDirection.Forward);
// 캔버스에 AGV 리스트 설정
var agvList = new System.Collections.Generic.List<AGVNavigationCore.Controls.IAGV> { PUB._virtualAGV };
PUB._mapCanvas.AGVList = agvList;
PUB.log.Add($"가상 AGV 생성: {startNode.NodeId} 위치");
}
}
else if (PUB._virtualAGV != null)
{
// 기존 AGV가 있으면 캔버스에 다시 연결
var agvList = new System.Collections.Generic.List<AGVNavigationCore.Controls.IAGV> { PUB._virtualAGV };
PUB._mapCanvas.AGVList = agvList;
}
// 맵 로드 후 자동으로 맵에 맞춤
PUB._mapCanvas.FitToNodes();
PUB.log.Add($"맵 파일 로드 완료: {filePath.Name}, 노드 수: {result.Nodes.Count}");
}
else
{
PUB.log.Add($"맵 파일 로딩 실패: {result.ErrorMessage}");
MessageBox.Show($"맵 파일 로딩 실패: {result.ErrorMessage}", "오류",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
PUB.log.Add($"맵 파일을 찾을 수 없습니다: {filePath.FullName}");
}
//var fn = string.Empty;
//if (files.Any() == false)
//{
// fn = AR.UTIL.MakePath("sample.route");
//}
//else if (files.Count() == 1)
//{
// fn = files.First().FullName;
//}
//if (fn.isEmpty() == false)
//{
// var fi = new System.IO.FileInfo(AR.UTIL.CurrentPath + "\\sample.route");
// if (fi.Exists)
// {
// PUB.log.Add($"autoload : {fi.FullName}");
// var rlt = PUB.mapctl.LoadFromFile(fi.FullName, out string errmsg);
// if (rlt == false) AR.UTIL.MsgE(errmsg);
// }
//}
this.timer1.Start(); this.timer1.Start();
// Set Run Mode
PUB._mapCanvas.Mode = AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Run;
} }
private void AGV_DataReceive(object sender, arDev.Narumi.DataEventArgs e) private void AGV_DataReceive(object sender, arDev.Narumi.DataEventArgs e)
{ {
@@ -210,6 +182,9 @@ namespace Project.ViewForm
PUB.sm.StepChanged -= Sm_StepChanged; PUB.sm.StepChanged -= Sm_StepChanged;
this.ctlAuto1.ButtonClick -= CtlAuto1_ButtonClick; this.ctlAuto1.ButtonClick -= CtlAuto1_ButtonClick;
PUB.AGV.DataReceive -= AGV_DataReceive; PUB.AGV.DataReceive -= AGV_DataReceive;
// Reset Mode to Edit
PUB._mapCanvas.Mode = AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Edit;
} }
bool tmrun = false; bool tmrun = false;
@@ -217,7 +192,15 @@ namespace Project.ViewForm
private void fAuto_VisibleChanged(object sender, EventArgs e) private void fAuto_VisibleChanged(object sender, EventArgs e)
{ {
this.timer1.Enabled = this.Visible; this.timer1.Enabled = this.Visible;
if (timer1.Enabled) timer1.Start(); if (timer1.Enabled)
{
timer1.Start();
// 화면이 보일 때 Run 모드로 강제 설정
if (PUB._mapCanvas.Mode != AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Run)
{
PUB._mapCanvas.Mode = AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Run;
}
}
else timer1.Stop(); else timer1.Stop();
} }
@@ -252,5 +235,49 @@ namespace Project.ViewForm
//tmrun = false; //tmrun = false;
} }
private void HandleRunModeClick(MapNode targetNode)
{
if (targetNode == null) return;
ENIGProtocol.AGVCommandHE targetCmd = ENIGProtocol.AGVCommandHE.Goto;
string confirmMsg = "";
if (targetNode.StationType == StationType.Charger)
{
if (MessageBox.Show($"[{targetNode.Id}] 충전기로 이동하여 충전을 진행하시겠습니까?", "작업 확인", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
targetCmd = ENIGProtocol.AGVCommandHE.Charger;
ExecuteManualCommand(targetNode, targetCmd);
}
return;
}
else if (targetNode.isDockingNode)
{
// Loader, Unloader, Buffer, Cleaner - Pick/Drop Selection
ContextMenuStrip menu = new ContextMenuStrip();
var pickOn = new ToolStripMenuItem("Pick On (Move & Pick)");
pickOn.Click += (s, args) => ExecuteManualCommand(targetNode, ENIGProtocol.AGVCommandHE.PickOn);
menu.Items.Add(pickOn);
var pickOff = new ToolStripMenuItem("Pick Off (Move & Drop)");
pickOff.Click += (s, args) => ExecuteManualCommand(targetNode, ENIGProtocol.AGVCommandHE.PickOff);
menu.Items.Add(pickOff);
menu.Show(Cursor.Position);
return;
}
else
{
// Normal Node
if (MessageBox.Show($"[{targetNode.Id}] 노드로 이동하시겠습니까?", "이동 확인", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
targetCmd = ENIGProtocol.AGVCommandHE.Goto;
ExecuteManualCommand(targetNode, targetCmd);
}
return;
}
}
} }
} }

View File

@@ -64,7 +64,7 @@ namespace Project.ViewForm
{ {
timer1.Stop(); timer1.Stop();
this.arLabel1.Text = PUB.BMS.Current_Level.ToString("N1") + "%"; this.arLabel1.Text = PUB.BMS.Current_Level.ToString("N1") + "%";
this.arLabel1.Sign = PUB.BMS.Current_Volt.ToString() + "v"; this.arLabel1.Sign = $"{PUB.BMS.Current_Volt}v, {PUB.BMS.Charge_watt}w";// PUB.BMS.Current_Volt.ToString() + "v";
this.cv1.Text = PUB.BMS.CellVoltage[0].ToString("N3") + "v"; this.cv1.Text = PUB.BMS.CellVoltage[0].ToString("N3") + "v";
this.cv2.Text = PUB.BMS.CellVoltage[1].ToString("N3") + "v"; this.cv2.Text = PUB.BMS.CellVoltage[1].ToString("N3") + "v";
this.cv3.Text = PUB.BMS.CellVoltage[2].ToString("N3") + "v"; this.cv3.Text = PUB.BMS.CellVoltage[2].ToString("N3") + "v";

View File

@@ -29,34 +29,42 @@
private void InitializeComponent() private void InitializeComponent()
{ {
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.listView1 = new System.Windows.Forms.ListView();
this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.listView2 = new System.Windows.Forms.ListView();
this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.columnHeader4 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.listView3 = new System.Windows.Forms.ListView();
this.columnHeader5 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.columnHeader6 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.listView4 = new System.Windows.Forms.ListView();
this.columnHeader7 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.columnHeader8 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.timer1 = new System.Windows.Forms.Timer(this.components); this.timer1 = new System.Windows.Forms.Timer(this.components);
this.dv1 = new System.Windows.Forms.DataGridView();
this.Column1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Column2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dv2 = new System.Windows.Forms.DataGridView();
this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dv3 = new System.Windows.Forms.DataGridView();
this.dataGridViewTextBoxColumn3 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn4 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dv4 = new System.Windows.Forms.DataGridView();
this.dataGridViewTextBoxColumn5 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn6 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.tableLayoutPanel1.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dv1)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.dv2)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.dv3)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.dv4)).BeginInit();
this.SuspendLayout(); this.SuspendLayout();
// //
// tableLayoutPanel1 // tableLayoutPanel1
// //
this.tableLayoutPanel1.ColumnCount = 4; this.tableLayoutPanel1.ColumnCount = 4;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 30F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 30F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20F));
this.tableLayoutPanel1.Controls.Add(this.listView1, 0, 0); this.tableLayoutPanel1.Controls.Add(this.dv4, 2, 1);
this.tableLayoutPanel1.Controls.Add(this.listView2, 1, 0); this.tableLayoutPanel1.Controls.Add(this.dv3, 2, 0);
this.tableLayoutPanel1.Controls.Add(this.listView3, 2, 0); this.tableLayoutPanel1.Controls.Add(this.dv2, 1, 0);
this.tableLayoutPanel1.Controls.Add(this.listView4, 3, 0); this.tableLayoutPanel1.Controls.Add(this.dv1, 0, 0);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 3); this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 3);
this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.Name = "tableLayoutPanel1";
@@ -66,92 +74,167 @@
this.tableLayoutPanel1.Size = new System.Drawing.Size(1050, 577); this.tableLayoutPanel1.Size = new System.Drawing.Size(1050, 577);
this.tableLayoutPanel1.TabIndex = 6; this.tableLayoutPanel1.TabIndex = 6;
// //
// listView1
//
this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.columnHeader1,
this.columnHeader2});
this.listView1.Dock = System.Windows.Forms.DockStyle.Fill;
this.listView1.HideSelection = false;
this.listView1.Location = new System.Drawing.Point(3, 3);
this.listView1.Name = "listView1";
this.tableLayoutPanel1.SetRowSpan(this.listView1, 2);
this.listView1.Size = new System.Drawing.Size(256, 571);
this.listView1.TabIndex = 0;
this.listView1.UseCompatibleStateImageBehavior = false;
this.listView1.View = System.Windows.Forms.View.Details;
this.listView1.SelectedIndexChanged += new System.EventHandler(this.listView1_SelectedIndexChanged);
//
// columnHeader1
//
this.columnHeader1.Width = 120;
//
// columnHeader2
//
this.columnHeader2.Width = 120;
//
// listView2
//
this.listView2.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.columnHeader3,
this.columnHeader4});
this.listView2.Dock = System.Windows.Forms.DockStyle.Fill;
this.listView2.HideSelection = false;
this.listView2.Location = new System.Drawing.Point(265, 3);
this.listView2.Name = "listView2";
this.tableLayoutPanel1.SetRowSpan(this.listView2, 2);
this.listView2.Size = new System.Drawing.Size(256, 571);
this.listView2.TabIndex = 0;
this.listView2.UseCompatibleStateImageBehavior = false;
this.listView2.View = System.Windows.Forms.View.Details;
//
// columnHeader3
//
this.columnHeader3.Width = 120;
//
// listView3
//
this.listView3.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.columnHeader5,
this.columnHeader6});
this.listView3.Dock = System.Windows.Forms.DockStyle.Fill;
this.listView3.HideSelection = false;
this.listView3.Location = new System.Drawing.Point(527, 3);
this.listView3.Name = "listView3";
this.tableLayoutPanel1.SetRowSpan(this.listView3, 2);
this.listView3.Size = new System.Drawing.Size(256, 571);
this.listView3.TabIndex = 0;
this.listView3.UseCompatibleStateImageBehavior = false;
this.listView3.View = System.Windows.Forms.View.Details;
//
// columnHeader5
//
this.columnHeader5.Width = 120;
//
// listView4
//
this.listView4.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.columnHeader7,
this.columnHeader8});
this.listView4.Dock = System.Windows.Forms.DockStyle.Fill;
this.listView4.HideSelection = false;
this.listView4.Location = new System.Drawing.Point(789, 3);
this.listView4.Name = "listView4";
this.tableLayoutPanel1.SetRowSpan(this.listView4, 2);
this.listView4.Size = new System.Drawing.Size(258, 571);
this.listView4.TabIndex = 0;
this.listView4.UseCompatibleStateImageBehavior = false;
this.listView4.View = System.Windows.Forms.View.Details;
//
// columnHeader7
//
this.columnHeader7.Width = 120;
//
// timer1 // timer1
// //
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);
// //
// dv1
//
this.dv1.AllowUserToAddRows = false;
this.dv1.AllowUserToDeleteRows = false;
this.dv1.AllowUserToResizeRows = false;
this.dv1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dv1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.Column1,
this.Column2});
dataGridViewCellStyle4.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle4.BackColor = System.Drawing.SystemColors.Window;
dataGridViewCellStyle4.Font = new System.Drawing.Font("Calibri", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
dataGridViewCellStyle4.ForeColor = System.Drawing.SystemColors.ControlText;
dataGridViewCellStyle4.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dataGridViewCellStyle4.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle4.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
this.dv1.DefaultCellStyle = dataGridViewCellStyle4;
this.dv1.Dock = System.Windows.Forms.DockStyle.Fill;
this.dv1.Location = new System.Drawing.Point(3, 3);
this.dv1.Name = "dv1";
this.dv1.ReadOnly = true;
this.dv1.RowHeadersVisible = false;
this.tableLayoutPanel1.SetRowSpan(this.dv1, 2);
this.dv1.RowTemplate.Height = 23;
this.dv1.Size = new System.Drawing.Size(309, 571);
this.dv1.TabIndex = 1;
//
// Column1
//
this.Column1.HeaderText = "Column1";
this.Column1.Name = "Column1";
this.Column1.ReadOnly = true;
//
// Column2
//
this.Column2.HeaderText = "Column2";
this.Column2.Name = "Column2";
this.Column2.ReadOnly = true;
//
// dv2
//
this.dv2.AllowUserToAddRows = false;
this.dv2.AllowUserToDeleteRows = false;
this.dv2.AllowUserToResizeRows = false;
this.dv2.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dv2.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.dataGridViewTextBoxColumn1,
this.dataGridViewTextBoxColumn2});
dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle3.BackColor = System.Drawing.SystemColors.Window;
dataGridViewCellStyle3.Font = new System.Drawing.Font("Calibri", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
dataGridViewCellStyle3.ForeColor = System.Drawing.SystemColors.ControlText;
dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
this.dv2.DefaultCellStyle = dataGridViewCellStyle3;
this.dv2.Dock = System.Windows.Forms.DockStyle.Fill;
this.dv2.Location = new System.Drawing.Point(318, 3);
this.dv2.Name = "dv2";
this.dv2.ReadOnly = true;
this.dv2.RowHeadersVisible = false;
this.tableLayoutPanel1.SetRowSpan(this.dv2, 2);
this.dv2.RowTemplate.Height = 23;
this.dv2.Size = new System.Drawing.Size(309, 571);
this.dv2.TabIndex = 2;
//
// dataGridViewTextBoxColumn1
//
this.dataGridViewTextBoxColumn1.HeaderText = "Column1";
this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1";
this.dataGridViewTextBoxColumn1.ReadOnly = true;
//
// dataGridViewTextBoxColumn2
//
this.dataGridViewTextBoxColumn2.HeaderText = "Column2";
this.dataGridViewTextBoxColumn2.Name = "dataGridViewTextBoxColumn2";
this.dataGridViewTextBoxColumn2.ReadOnly = true;
//
// dv3
//
this.dv3.AllowUserToAddRows = false;
this.dv3.AllowUserToDeleteRows = false;
this.dv3.AllowUserToResizeRows = false;
this.dv3.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dv3.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.dataGridViewTextBoxColumn3,
this.dataGridViewTextBoxColumn4});
this.tableLayoutPanel1.SetColumnSpan(this.dv3, 2);
dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Window;
dataGridViewCellStyle2.Font = new System.Drawing.Font("Calibri", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
this.dv3.DefaultCellStyle = dataGridViewCellStyle2;
this.dv3.Dock = System.Windows.Forms.DockStyle.Fill;
this.dv3.Location = new System.Drawing.Point(633, 3);
this.dv3.Name = "dv3";
this.dv3.ReadOnly = true;
this.dv3.RowHeadersVisible = false;
this.dv3.RowTemplate.Height = 23;
this.dv3.Size = new System.Drawing.Size(414, 282);
this.dv3.TabIndex = 3;
//
// dataGridViewTextBoxColumn3
//
this.dataGridViewTextBoxColumn3.HeaderText = "Column1";
this.dataGridViewTextBoxColumn3.Name = "dataGridViewTextBoxColumn3";
this.dataGridViewTextBoxColumn3.ReadOnly = true;
//
// dataGridViewTextBoxColumn4
//
this.dataGridViewTextBoxColumn4.HeaderText = "Column2";
this.dataGridViewTextBoxColumn4.Name = "dataGridViewTextBoxColumn4";
this.dataGridViewTextBoxColumn4.ReadOnly = true;
//
// dv4
//
this.dv4.AllowUserToAddRows = false;
this.dv4.AllowUserToDeleteRows = false;
this.dv4.AllowUserToResizeRows = false;
this.dv4.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dv4.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.dataGridViewTextBoxColumn5,
this.dataGridViewTextBoxColumn6});
this.tableLayoutPanel1.SetColumnSpan(this.dv4, 2);
dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Window;
dataGridViewCellStyle1.Font = new System.Drawing.Font("Calibri", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.ControlText;
dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
this.dv4.DefaultCellStyle = dataGridViewCellStyle1;
this.dv4.Dock = System.Windows.Forms.DockStyle.Fill;
this.dv4.Location = new System.Drawing.Point(633, 291);
this.dv4.Name = "dv4";
this.dv4.ReadOnly = true;
this.dv4.RowHeadersVisible = false;
this.dv4.RowTemplate.Height = 23;
this.dv4.Size = new System.Drawing.Size(414, 283);
this.dv4.TabIndex = 4;
//
// dataGridViewTextBoxColumn5
//
this.dataGridViewTextBoxColumn5.HeaderText = "Column1";
this.dataGridViewTextBoxColumn5.Name = "dataGridViewTextBoxColumn5";
this.dataGridViewTextBoxColumn5.ReadOnly = true;
//
// dataGridViewTextBoxColumn6
//
this.dataGridViewTextBoxColumn6.HeaderText = "Column2";
this.dataGridViewTextBoxColumn6.Name = "dataGridViewTextBoxColumn6";
this.dataGridViewTextBoxColumn6.ReadOnly = true;
//
// fFlag // fFlag
// //
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
@@ -165,6 +248,10 @@
this.Load += new System.EventHandler(this.fFlag_Load); this.Load += new System.EventHandler(this.fFlag_Load);
this.VisibleChanged += new System.EventHandler(this.fFlag_VisibleChanged); this.VisibleChanged += new System.EventHandler(this.fFlag_VisibleChanged);
this.tableLayoutPanel1.ResumeLayout(false); this.tableLayoutPanel1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.dv1)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.dv2)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.dv3)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.dv4)).EndInit();
this.ResumeLayout(false); this.ResumeLayout(false);
} }
@@ -173,17 +260,17 @@
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Timer timer1; private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.ListView listView1; private System.Windows.Forms.DataGridView dv4;
private System.Windows.Forms.ListView listView2; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn5;
private System.Windows.Forms.ListView listView3; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn6;
private System.Windows.Forms.ListView listView4; private System.Windows.Forms.DataGridView dv3;
private System.Windows.Forms.ColumnHeader columnHeader1; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn3;
private System.Windows.Forms.ColumnHeader columnHeader2; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn4;
private System.Windows.Forms.ColumnHeader columnHeader3; private System.Windows.Forms.DataGridView dv2;
private System.Windows.Forms.ColumnHeader columnHeader4; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1;
private System.Windows.Forms.ColumnHeader columnHeader5; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2;
private System.Windows.Forms.ColumnHeader columnHeader6; private System.Windows.Forms.DataGridView dv1;
private System.Windows.Forms.ColumnHeader columnHeader7; private System.Windows.Forms.DataGridViewTextBoxColumn Column1;
private System.Windows.Forms.ColumnHeader columnHeader8; private System.Windows.Forms.DataGridViewTextBoxColumn Column2;
} }
} }

View File

@@ -30,16 +30,18 @@ namespace Project.ViewForm
void MakeControl() void MakeControl()
{ {
ListView[] lvs = new ListView[] { listView1, listView2, listView3, listView4 }; DataGridView[] dvs = new DataGridView[] { dv1, dv2, dv3, dv4 };
foreach (var lv in lvs)
foreach(var lv in dvs)
{ {
lv.Columns.Clear(); lv.Columns.Clear();
lv.Columns.Add("Idx"); lv.Columns.Add("idx", "*");
lv.Columns.Add("Title"); lv.Columns.Add("Title", "Title");
lv.Columns.Add("Value"); lv.Columns.Add("Value", "Value");
lv.Columns[0].Width = 25; lv.Columns[0].Width = 25;
lv.Columns[1].Width = 150; lv.Columns[1].Width = 150;
lv.Columns[2].Width = 100; lv.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
} }
Array valuelist; Array valuelist;
@@ -47,38 +49,33 @@ namespace Project.ViewForm
foreach (var item in valuelist) foreach (var item in valuelist)
{ {
var v = (COMM.eVarBool)item; var v = (COMM.eVarBool)item;
var lv = listView1.Items.Add($"{(int)v}"); dv1.Rows.Add($"{(int)v}", item, "--");
lv.SubItems.Add($"{item}");
lv.SubItems.Add("--");
} }
valuelist = Enum.GetValues(typeof(COMM.eVarInt32)); valuelist = Enum.GetValues(typeof(COMM.eVarInt32));
foreach (var item in valuelist) foreach (var item in valuelist)
{ {
var v = (COMM.eVarInt32)item; var v = (COMM.eVarInt32)item;
var lv = listView2.Items.Add($"{(int)v}"); dv2.Rows.Add($"{(int)v}", item, "--");
lv.SubItems.Add($"{item}");
lv.SubItems.Add("--");
} }
valuelist = Enum.GetValues(typeof(COMM.eVarString)); valuelist = Enum.GetValues(typeof(COMM.eVarString));
foreach (var item in valuelist) foreach (var item in valuelist)
{ {
var v = (COMM.eVarString)item; var v = (COMM.eVarString)item;
var lv = listView3.Items.Add($"{(int)v}"); dv3.Rows.Add($"{(int)v}", item, "--");
lv.SubItems.Add($"{item}");
lv.SubItems.Add("--");
} }
valuelist = Enum.GetValues(typeof(COMM.eVarTime)); valuelist = Enum.GetValues(typeof(COMM.eVarTime));
foreach (var item in valuelist) foreach (var item in valuelist)
{ {
var v = (COMM.eVarString)item; var v = (COMM.eVarString)item;
var lv = listView4.Items.Add($"{(int)v}"); dv4.Rows.Add($"{(int)v}", item, "--");
lv.SubItems.Add($"{item}");
lv.SubItems.Add("--");
} }
foreach (var dv in dvs)
dv.AutoResizeColumns();
} }
@@ -91,42 +88,32 @@ namespace Project.ViewForm
private void timer1_Tick(object sender, EventArgs e) private void timer1_Tick(object sender, EventArgs e)
{ {
timer1.Stop(); timer1.Stop();
listView1.SuspendLayout(); foreach(DataGridViewRow item in this.dv1.Rows)
foreach (ListViewItem item in listView1.Items)
{ {
var idx = int.Parse(item.SubItems[0].Text); var idx = int.Parse(item.Cells["idx"].Value.ToString());
var v = VAR.BOOL.Get(idx); var v = VAR.BOOL.Get(idx);
var desc = VAR.BOOL.GetCodeDesc(idx); item.Cells["value"].Value = v ? "O" : "X";
item.SubItems[2].Text = v ? "O" : "X";
} }
listView1.ResumeLayout();
listView2.SuspendLayout(); foreach (DataGridViewRow item in this.dv2.Rows)
foreach (ListViewItem item in listView2.Items)
{ {
var idx = int.Parse(item.SubItems[0].Text); var idx = int.Parse(item.Cells["idx"].Value.ToString());
var v = VAR.I32.Get(idx); var v = VAR.I32.Get(idx);
var desc = VAR.I32.GetCodeDesc(idx); item.Cells["value"].Value = v.ToString();
item.SubItems[2].Text = v.ToString();
} }
listView2.ResumeLayout();
listView3.SuspendLayout(); foreach (DataGridViewRow item in this.dv3.Rows)
foreach (ListViewItem item in listView3.Items)
{ {
var idx = int.Parse(item.SubItems[0].Text); var idx = int.Parse(item.Cells["idx"].Value.ToString());
var v = VAR.STR.Get(idx); var v = VAR.STR.Get(idx);
var desc = VAR.STR.GetCodeDesc(idx); item.Cells["value"].Value = v;
item.SubItems[2].Text = v;
} }
listView3.ResumeLayout(); foreach (DataGridViewRow item in this.dv4.Rows)
listView4.SuspendLayout();
foreach (ListViewItem item in listView4.Items)
{ {
var idx = int.Parse(item.SubItems[0].Text); var idx = int.Parse(item.Cells["idx"].Value.ToString());
var v = VAR.TIME.Get(idx); var v = VAR.TIME.Get(idx);
var desc = VAR.TIME.GetCodeDesc(idx); item.Cells["value"].Value = v.ToString("HH:mm:ss.fff");
item.SubItems[2].Text = v.ToString("HH:mm:ss.fff");
} }
listView4.ResumeLayout();
timer1.Start(); timer1.Start();
} }

View File

@@ -117,6 +117,30 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<metadata name="dataGridViewTextBoxColumn5.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="dataGridViewTextBoxColumn6.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="dataGridViewTextBoxColumn3.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="dataGridViewTextBoxColumn4.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="dataGridViewTextBoxColumn1.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="dataGridViewTextBoxColumn2.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="Column1.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="Column2.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="timer1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="timer1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value> <value>17, 17</value>
</metadata> </metadata>

View File

@@ -41,45 +41,45 @@
this.btErrReset = new System.Windows.Forms.Button(); this.btErrReset = new System.Windows.Forms.Button();
this.timer1 = new System.Windows.Forms.Timer(this.components); this.timer1 = new System.Windows.Forms.Timer(this.components);
this.panel2 = new System.Windows.Forms.Panel(); this.panel2 = new System.Windows.Forms.Panel();
this.btRight180 = new System.Windows.Forms.Button(); this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
this.btBack180 = new System.Windows.Forms.Button();
this.btLeft180 = new System.Windows.Forms.Button(); this.btLeft180 = new System.Windows.Forms.Button();
this.btRight180 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.button1 = new System.Windows.Forms.Button(); this.button1 = new System.Windows.Forms.Button();
this.groupBox1 = new System.Windows.Forms.GroupBox(); this.groupBox1 = new System.Windows.Forms.GroupBox();
this.radpbs2 = new AGVControl.MyRadioButton();
this.panel9 = new System.Windows.Forms.Panel(); this.panel9 = new System.Windows.Forms.Panel();
this.radpbs1 = new AGVControl.MyRadioButton();
this.panel3 = new System.Windows.Forms.Panel(); this.panel3 = new System.Windows.Forms.Panel();
this.radpbs0 = new AGVControl.MyRadioButton();
this.grpSpeed = new System.Windows.Forms.GroupBox(); this.grpSpeed = new System.Windows.Forms.GroupBox();
this.radspdh = new AGVControl.MyRadioButton();
this.panel8 = new System.Windows.Forms.Panel(); this.panel8 = new System.Windows.Forms.Panel();
this.radspdm = new AGVControl.MyRadioButton();
this.panel4 = new System.Windows.Forms.Panel(); this.panel4 = new System.Windows.Forms.Panel();
this.radspdl = new AGVControl.MyRadioButton();
this.grpBunki = new System.Windows.Forms.GroupBox(); this.grpBunki = new System.Windows.Forms.GroupBox();
this.radright = new AGVControl.MyRadioButton();
this.panel7 = new System.Windows.Forms.Panel(); this.panel7 = new System.Windows.Forms.Panel();
this.radstrai = new AGVControl.MyRadioButton();
this.panel5 = new System.Windows.Forms.Panel(); this.panel5 = new System.Windows.Forms.Panel();
this.radleft = new AGVControl.MyRadioButton();
this.groupBox2 = new System.Windows.Forms.GroupBox(); this.groupBox2 = new System.Windows.Forms.GroupBox();
this.arLabel1 = new arCtl.arLabel(); this.arLabel1 = new arCtl.arLabel();
this.panel12 = new System.Windows.Forms.Panel(); this.panel12 = new System.Windows.Forms.Panel();
this.panel6 = new System.Windows.Forms.Panel();
this.label1 = new System.Windows.Forms.Label();
this.radpbs2 = new AGVControl.MyRadioButton();
this.radpbs1 = new AGVControl.MyRadioButton();
this.radpbs0 = new AGVControl.MyRadioButton();
this.radspdh = new AGVControl.MyRadioButton();
this.radspdm = new AGVControl.MyRadioButton();
this.radspdl = new AGVControl.MyRadioButton();
this.radright = new AGVControl.MyRadioButton();
this.radstrai = new AGVControl.MyRadioButton();
this.radleft = new AGVControl.MyRadioButton();
this.radbackward = new AGVControl.MyRadioButton(); this.radbackward = new AGVControl.MyRadioButton();
this.panel6 = new System.Windows.Forms.Panel();
this.radforward = new AGVControl.MyRadioButton(); this.radforward = new AGVControl.MyRadioButton();
this.label1 = new System.Windows.Forms.Label();
this.guideSensor1 = new Narumi.UC.GuideSensor(); this.guideSensor1 = new Narumi.UC.GuideSensor();
this.btBack180 = new System.Windows.Forms.Button();
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
this.button2 = new System.Windows.Forms.Button();
this.tableLayoutPanel1.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout();
this.panel2.SuspendLayout(); this.panel2.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
this.groupBox1.SuspendLayout(); this.groupBox1.SuspendLayout();
this.grpSpeed.SuspendLayout(); this.grpSpeed.SuspendLayout();
this.grpBunki.SuspendLayout(); this.grpBunki.SuspendLayout();
this.groupBox2.SuspendLayout(); this.groupBox2.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// tableLayoutPanel1 // tableLayoutPanel1
@@ -124,7 +124,7 @@
this.btStart.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal; this.btStart.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.btStart.GradientRepeatBG = false; this.btStart.GradientRepeatBG = false;
this.btStart.isButton = true; this.btStart.isButton = true;
this.btStart.Location = new System.Drawing.Point(204, 201); this.btStart.Location = new System.Drawing.Point(204, 200);
this.btStart.MouseDownColor = System.Drawing.Color.Yellow; this.btStart.MouseDownColor = System.Drawing.Color.Yellow;
this.btStart.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); this.btStart.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.btStart.msg = new string[] { this.btStart.msg = new string[] {
@@ -146,7 +146,7 @@
this.btStart.SignAlign = System.Drawing.ContentAlignment.BottomRight; this.btStart.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btStart.SignColor = System.Drawing.Color.Yellow; this.btStart.SignColor = System.Drawing.Color.Yellow;
this.btStart.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic); this.btStart.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btStart.Size = new System.Drawing.Size(195, 192); this.btStart.Size = new System.Drawing.Size(195, 191);
this.btStart.TabIndex = 6; this.btStart.TabIndex = 6;
this.btStart.Text = "RUN"; this.btStart.Text = "RUN";
this.btStart.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.btStart.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -171,7 +171,7 @@
this.btRight.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal; this.btRight.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.btRight.GradientRepeatBG = false; this.btRight.GradientRepeatBG = false;
this.btRight.isButton = true; this.btRight.isButton = true;
this.btRight.Location = new System.Drawing.Point(405, 201); this.btRight.Location = new System.Drawing.Point(405, 200);
this.btRight.MouseDownColor = System.Drawing.Color.Yellow; this.btRight.MouseDownColor = System.Drawing.Color.Yellow;
this.btRight.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); this.btRight.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.btRight.msg = null; this.btRight.msg = null;
@@ -191,7 +191,7 @@
this.btRight.SignAlign = System.Drawing.ContentAlignment.BottomRight; this.btRight.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btRight.SignColor = System.Drawing.Color.Yellow; this.btRight.SignColor = System.Drawing.Color.Yellow;
this.btRight.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic); this.btRight.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btRight.Size = new System.Drawing.Size(196, 192); this.btRight.Size = new System.Drawing.Size(196, 191);
this.btRight.TabIndex = 0; this.btRight.TabIndex = 0;
this.btRight.Text = "좌회전"; this.btRight.Text = "좌회전";
this.btRight.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.btRight.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -237,7 +237,7 @@
this.btBack.SignAlign = System.Drawing.ContentAlignment.BottomRight; this.btBack.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btBack.SignColor = System.Drawing.Color.Yellow; this.btBack.SignColor = System.Drawing.Color.Yellow;
this.btBack.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic); this.btBack.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btBack.Size = new System.Drawing.Size(195, 192); this.btBack.Size = new System.Drawing.Size(195, 191);
this.btBack.TabIndex = 0; this.btBack.TabIndex = 0;
this.btBack.Text = "후진"; this.btBack.Text = "후진";
this.btBack.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.btBack.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -262,7 +262,7 @@
this.btForward.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal; this.btForward.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.btForward.GradientRepeatBG = false; this.btForward.GradientRepeatBG = false;
this.btForward.isButton = true; this.btForward.isButton = true;
this.btForward.Location = new System.Drawing.Point(204, 399); this.btForward.Location = new System.Drawing.Point(204, 397);
this.btForward.MouseDownColor = System.Drawing.Color.Yellow; this.btForward.MouseDownColor = System.Drawing.Color.Yellow;
this.btForward.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); this.btForward.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.btForward.msg = null; this.btForward.msg = null;
@@ -282,7 +282,7 @@
this.btForward.SignAlign = System.Drawing.ContentAlignment.BottomRight; this.btForward.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btForward.SignColor = System.Drawing.Color.Yellow; this.btForward.SignColor = System.Drawing.Color.Yellow;
this.btForward.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic); this.btForward.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btForward.Size = new System.Drawing.Size(195, 192); this.btForward.Size = new System.Drawing.Size(195, 194);
this.btForward.TabIndex = 0; this.btForward.TabIndex = 0;
this.btForward.Text = "전진"; this.btForward.Text = "전진";
this.btForward.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.btForward.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -307,7 +307,7 @@
this.btLeft.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal; this.btLeft.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.btLeft.GradientRepeatBG = false; this.btLeft.GradientRepeatBG = false;
this.btLeft.isButton = true; this.btLeft.isButton = true;
this.btLeft.Location = new System.Drawing.Point(3, 201); this.btLeft.Location = new System.Drawing.Point(3, 200);
this.btLeft.MouseDownColor = System.Drawing.Color.Yellow; this.btLeft.MouseDownColor = System.Drawing.Color.Yellow;
this.btLeft.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); this.btLeft.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.btLeft.msg = null; this.btLeft.msg = null;
@@ -327,7 +327,7 @@
this.btLeft.SignAlign = System.Drawing.ContentAlignment.BottomRight; this.btLeft.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btLeft.SignColor = System.Drawing.Color.Yellow; this.btLeft.SignColor = System.Drawing.Color.Yellow;
this.btLeft.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic); this.btLeft.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btLeft.Size = new System.Drawing.Size(195, 192); this.btLeft.Size = new System.Drawing.Size(195, 191);
this.btLeft.TabIndex = 0; this.btLeft.TabIndex = 0;
this.btLeft.Text = "우회전"; this.btLeft.Text = "우회전";
this.btLeft.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.btLeft.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -341,7 +341,7 @@
this.btMarkStop.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129))); this.btMarkStop.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.btMarkStop.Location = new System.Drawing.Point(3, 3); this.btMarkStop.Location = new System.Drawing.Point(3, 3);
this.btMarkStop.Name = "btMarkStop"; this.btMarkStop.Name = "btMarkStop";
this.btMarkStop.Size = new System.Drawing.Size(195, 192); this.btMarkStop.Size = new System.Drawing.Size(195, 191);
this.btMarkStop.TabIndex = 7; this.btMarkStop.TabIndex = 7;
this.btMarkStop.Text = "마크정지"; this.btMarkStop.Text = "마크정지";
this.btMarkStop.UseVisualStyleBackColor = true; this.btMarkStop.UseVisualStyleBackColor = true;
@@ -351,9 +351,9 @@
// //
this.btchargeOff.Dock = System.Windows.Forms.DockStyle.Fill; this.btchargeOff.Dock = System.Windows.Forms.DockStyle.Fill;
this.btchargeOff.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129))); this.btchargeOff.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.btchargeOff.Location = new System.Drawing.Point(405, 399); this.btchargeOff.Location = new System.Drawing.Point(405, 397);
this.btchargeOff.Name = "btchargeOff"; this.btchargeOff.Name = "btchargeOff";
this.btchargeOff.Size = new System.Drawing.Size(196, 192); this.btchargeOff.Size = new System.Drawing.Size(196, 194);
this.btchargeOff.TabIndex = 8; this.btchargeOff.TabIndex = 8;
this.btchargeOff.Text = "충전해제"; this.btchargeOff.Text = "충전해제";
this.btchargeOff.UseVisualStyleBackColor = true; this.btchargeOff.UseVisualStyleBackColor = true;
@@ -363,9 +363,9 @@
// //
this.btChargeOn.Dock = System.Windows.Forms.DockStyle.Fill; this.btChargeOn.Dock = System.Windows.Forms.DockStyle.Fill;
this.btChargeOn.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129))); this.btChargeOn.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.btChargeOn.Location = new System.Drawing.Point(3, 399); this.btChargeOn.Location = new System.Drawing.Point(3, 397);
this.btChargeOn.Name = "btChargeOn"; this.btChargeOn.Name = "btChargeOn";
this.btChargeOn.Size = new System.Drawing.Size(195, 192); this.btChargeOn.Size = new System.Drawing.Size(195, 194);
this.btChargeOn.TabIndex = 9; this.btChargeOn.TabIndex = 9;
this.btChargeOn.Text = "충전"; this.btChargeOn.Text = "충전";
this.btChargeOn.UseVisualStyleBackColor = true; this.btChargeOn.UseVisualStyleBackColor = true;
@@ -377,7 +377,7 @@
this.btErrReset.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129))); this.btErrReset.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.btErrReset.Location = new System.Drawing.Point(405, 3); this.btErrReset.Location = new System.Drawing.Point(405, 3);
this.btErrReset.Name = "btErrReset"; this.btErrReset.Name = "btErrReset";
this.btErrReset.Size = new System.Drawing.Size(196, 192); this.btErrReset.Size = new System.Drawing.Size(196, 191);
this.btErrReset.TabIndex = 10; this.btErrReset.TabIndex = 10;
this.btErrReset.Text = "오류소거"; this.btErrReset.Text = "오류소거";
this.btErrReset.UseVisualStyleBackColor = true; this.btErrReset.UseVisualStyleBackColor = true;
@@ -403,19 +403,38 @@
this.panel2.TabIndex = 9; this.panel2.TabIndex = 9;
this.panel2.Paint += new System.Windows.Forms.PaintEventHandler(this.panel2_Paint); this.panel2.Paint += new System.Windows.Forms.PaintEventHandler(this.panel2_Paint);
// //
// btRight180 // tableLayoutPanel2
// //
this.btRight180.BackColor = System.Drawing.Color.WhiteSmoke; this.tableLayoutPanel2.ColumnCount = 2;
this.btRight180.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.btRight180.Font = new System.Drawing.Font("맑은 고딕", 10F, System.Drawing.FontStyle.Bold); this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.btRight180.ForeColor = System.Drawing.Color.Black; this.tableLayoutPanel2.Controls.Add(this.btBack180, 1, 0);
this.btRight180.Location = new System.Drawing.Point(3, 62); this.tableLayoutPanel2.Controls.Add(this.btLeft180, 0, 0);
this.btRight180.Name = "btRight180"; this.tableLayoutPanel2.Controls.Add(this.btRight180, 0, 1);
this.btRight180.Size = new System.Drawing.Size(196, 53); this.tableLayoutPanel2.Controls.Add(this.button2, 1, 1);
this.btRight180.TabIndex = 6; this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
this.btRight180.Text = "180도 우회전"; this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 438);
this.btRight180.UseVisualStyleBackColor = false; this.tableLayoutPanel2.Name = "tableLayoutPanel2";
this.btRight180.Click += new System.EventHandler(this.btRight180_Click); this.tableLayoutPanel2.RowCount = 2;
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.Size = new System.Drawing.Size(404, 118);
this.tableLayoutPanel2.TabIndex = 12;
//
// btBack180
//
this.btBack180.BackColor = System.Drawing.Color.WhiteSmoke;
this.btBack180.Cursor = System.Windows.Forms.Cursors.Hand;
this.btBack180.Dock = System.Windows.Forms.DockStyle.Fill;
this.btBack180.Font = new System.Drawing.Font("맑은 고딕", 10F, System.Drawing.FontStyle.Bold);
this.btBack180.ForeColor = System.Drawing.Color.Black;
this.btBack180.Location = new System.Drawing.Point(205, 3);
this.btBack180.Name = "btBack180";
this.btBack180.Size = new System.Drawing.Size(196, 53);
this.btBack180.TabIndex = 15;
this.btBack180.Text = "자석 (ON)";
this.btBack180.UseVisualStyleBackColor = false;
this.btBack180.Click += new System.EventHandler(this.btBack180_Click);
// //
// btLeft180 // btLeft180
// //
@@ -431,6 +450,35 @@
this.btLeft180.UseVisualStyleBackColor = false; this.btLeft180.UseVisualStyleBackColor = false;
this.btLeft180.Click += new System.EventHandler(this.btLeft180_Click); this.btLeft180.Click += new System.EventHandler(this.btLeft180_Click);
// //
// btRight180
//
this.btRight180.BackColor = System.Drawing.Color.WhiteSmoke;
this.btRight180.Dock = System.Windows.Forms.DockStyle.Fill;
this.btRight180.Font = new System.Drawing.Font("맑은 고딕", 10F, System.Drawing.FontStyle.Bold);
this.btRight180.ForeColor = System.Drawing.Color.Black;
this.btRight180.Location = new System.Drawing.Point(3, 62);
this.btRight180.Name = "btRight180";
this.btRight180.Size = new System.Drawing.Size(196, 53);
this.btRight180.TabIndex = 6;
this.btRight180.Text = "180도 우회전";
this.btRight180.UseVisualStyleBackColor = false;
this.btRight180.Click += new System.EventHandler(this.btRight180_Click);
//
// button2
//
this.button2.BackColor = System.Drawing.Color.WhiteSmoke;
this.button2.Cursor = System.Windows.Forms.Cursors.Hand;
this.button2.Dock = System.Windows.Forms.DockStyle.Fill;
this.button2.Font = new System.Drawing.Font("맑은 고딕", 10F, System.Drawing.FontStyle.Bold);
this.button2.ForeColor = System.Drawing.Color.Black;
this.button2.Location = new System.Drawing.Point(205, 62);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(196, 53);
this.button2.TabIndex = 15;
this.button2.Text = "자석(OFF)";
this.button2.UseVisualStyleBackColor = false;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// button1 // button1
// //
this.button1.Dock = System.Windows.Forms.DockStyle.Top; this.button1.Dock = System.Windows.Forms.DockStyle.Top;
@@ -459,6 +507,25 @@
this.groupBox1.TabStop = false; this.groupBox1.TabStop = false;
this.groupBox1.Text = "근접센서(PBS)"; this.groupBox1.Text = "근접센서(PBS)";
// //
// radpbs2
//
this.radpbs2.BackColor = System.Drawing.Color.WhiteSmoke;
this.radpbs2.Bordercolor = System.Drawing.Color.DimGray;
this.radpbs2.BorderRadius = 7;
this.radpbs2.BorderSize = 2;
this.radpbs2.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radpbs2.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radpbs2.CheckWidth = 30;
this.radpbs2.Dock = System.Windows.Forms.DockStyle.Left;
this.radpbs2.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radpbs2.Location = new System.Drawing.Point(278, 19);
this.radpbs2.Name = "radpbs2";
this.radpbs2.Size = new System.Drawing.Size(119, 66);
this.radpbs2.TabIndex = 10;
this.radpbs2.Text = "On(2)";
this.radpbs2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radpbs2.UseVisualStyleBackColor = false;
//
// panel9 // panel9
// //
this.panel9.Dock = System.Windows.Forms.DockStyle.Left; this.panel9.Dock = System.Windows.Forms.DockStyle.Left;
@@ -467,6 +534,28 @@
this.panel9.Size = new System.Drawing.Size(15, 66); this.panel9.Size = new System.Drawing.Size(15, 66);
this.panel9.TabIndex = 12; this.panel9.TabIndex = 12;
// //
// radpbs1
//
this.radpbs1.BackColor = System.Drawing.Color.WhiteSmoke;
this.radpbs1.Bordercolor = System.Drawing.Color.DimGray;
this.radpbs1.BorderRadius = 7;
this.radpbs1.BorderSize = 2;
this.radpbs1.Checked = true;
this.radpbs1.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radpbs1.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radpbs1.CheckWidth = 30;
this.radpbs1.Dock = System.Windows.Forms.DockStyle.Left;
this.radpbs1.Font = new System.Drawing.Font("맑은 고딕", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radpbs1.ForeColor = System.Drawing.Color.Blue;
this.radpbs1.Location = new System.Drawing.Point(144, 19);
this.radpbs1.Name = "radpbs1";
this.radpbs1.Size = new System.Drawing.Size(119, 66);
this.radpbs1.TabIndex = 9;
this.radpbs1.TabStop = true;
this.radpbs1.Text = "On(1)";
this.radpbs1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radpbs1.UseVisualStyleBackColor = false;
//
// panel3 // panel3
// //
this.panel3.Dock = System.Windows.Forms.DockStyle.Left; this.panel3.Dock = System.Windows.Forms.DockStyle.Left;
@@ -475,6 +564,25 @@
this.panel3.Size = new System.Drawing.Size(15, 66); this.panel3.Size = new System.Drawing.Size(15, 66);
this.panel3.TabIndex = 11; this.panel3.TabIndex = 11;
// //
// radpbs0
//
this.radpbs0.BackColor = System.Drawing.Color.WhiteSmoke;
this.radpbs0.Bordercolor = System.Drawing.Color.DimGray;
this.radpbs0.BorderRadius = 7;
this.radpbs0.BorderSize = 2;
this.radpbs0.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radpbs0.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radpbs0.CheckWidth = 30;
this.radpbs0.Dock = System.Windows.Forms.DockStyle.Left;
this.radpbs0.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radpbs0.Location = new System.Drawing.Point(10, 19);
this.radpbs0.Name = "radpbs0";
this.radpbs0.Size = new System.Drawing.Size(119, 66);
this.radpbs0.TabIndex = 8;
this.radpbs0.Text = "Off(0)";
this.radpbs0.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radpbs0.UseVisualStyleBackColor = false;
//
// grpSpeed // grpSpeed
// //
this.grpSpeed.Controls.Add(this.radspdh); this.grpSpeed.Controls.Add(this.radspdh);
@@ -491,6 +599,25 @@
this.grpSpeed.TabStop = false; this.grpSpeed.TabStop = false;
this.grpSpeed.Text = "속도"; this.grpSpeed.Text = "속도";
// //
// radspdh
//
this.radspdh.BackColor = System.Drawing.Color.WhiteSmoke;
this.radspdh.Bordercolor = System.Drawing.Color.DimGray;
this.radspdh.BorderRadius = 7;
this.radspdh.BorderSize = 2;
this.radspdh.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radspdh.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radspdh.CheckWidth = 30;
this.radspdh.Dock = System.Windows.Forms.DockStyle.Left;
this.radspdh.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radspdh.Location = new System.Drawing.Point(278, 19);
this.radspdh.Name = "radspdh";
this.radspdh.Size = new System.Drawing.Size(119, 66);
this.radspdh.TabIndex = 2;
this.radspdh.Text = "고속";
this.radspdh.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radspdh.UseVisualStyleBackColor = false;
//
// panel8 // panel8
// //
this.panel8.Dock = System.Windows.Forms.DockStyle.Left; this.panel8.Dock = System.Windows.Forms.DockStyle.Left;
@@ -499,6 +626,28 @@
this.panel8.Size = new System.Drawing.Size(15, 66); this.panel8.Size = new System.Drawing.Size(15, 66);
this.panel8.TabIndex = 13; this.panel8.TabIndex = 13;
// //
// radspdm
//
this.radspdm.BackColor = System.Drawing.Color.WhiteSmoke;
this.radspdm.Bordercolor = System.Drawing.Color.DimGray;
this.radspdm.BorderRadius = 7;
this.radspdm.BorderSize = 2;
this.radspdm.Checked = true;
this.radspdm.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radspdm.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radspdm.CheckWidth = 30;
this.radspdm.Dock = System.Windows.Forms.DockStyle.Left;
this.radspdm.Font = new System.Drawing.Font("맑은 고딕", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radspdm.ForeColor = System.Drawing.Color.Blue;
this.radspdm.Location = new System.Drawing.Point(144, 19);
this.radspdm.Name = "radspdm";
this.radspdm.Size = new System.Drawing.Size(119, 66);
this.radspdm.TabIndex = 1;
this.radspdm.TabStop = true;
this.radspdm.Text = "중속";
this.radspdm.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radspdm.UseVisualStyleBackColor = false;
//
// panel4 // panel4
// //
this.panel4.Dock = System.Windows.Forms.DockStyle.Left; this.panel4.Dock = System.Windows.Forms.DockStyle.Left;
@@ -507,6 +656,25 @@
this.panel4.Size = new System.Drawing.Size(15, 66); this.panel4.Size = new System.Drawing.Size(15, 66);
this.panel4.TabIndex = 12; this.panel4.TabIndex = 12;
// //
// radspdl
//
this.radspdl.BackColor = System.Drawing.Color.WhiteSmoke;
this.radspdl.Bordercolor = System.Drawing.Color.DimGray;
this.radspdl.BorderRadius = 7;
this.radspdl.BorderSize = 2;
this.radspdl.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radspdl.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radspdl.CheckWidth = 30;
this.radspdl.Dock = System.Windows.Forms.DockStyle.Left;
this.radspdl.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radspdl.Location = new System.Drawing.Point(10, 19);
this.radspdl.Name = "radspdl";
this.radspdl.Size = new System.Drawing.Size(119, 66);
this.radspdl.TabIndex = 0;
this.radspdl.Text = "저속";
this.radspdl.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radspdl.UseVisualStyleBackColor = false;
//
// grpBunki // grpBunki
// //
this.grpBunki.Controls.Add(this.radright); this.grpBunki.Controls.Add(this.radright);
@@ -523,6 +691,25 @@
this.grpBunki.TabStop = false; this.grpBunki.TabStop = false;
this.grpBunki.Text = "분기"; this.grpBunki.Text = "분기";
// //
// radright
//
this.radright.BackColor = System.Drawing.Color.WhiteSmoke;
this.radright.Bordercolor = System.Drawing.Color.DimGray;
this.radright.BorderRadius = 7;
this.radright.BorderSize = 2;
this.radright.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radright.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radright.CheckWidth = 30;
this.radright.Dock = System.Windows.Forms.DockStyle.Left;
this.radright.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radright.Location = new System.Drawing.Point(278, 19);
this.radright.Name = "radright";
this.radright.Size = new System.Drawing.Size(119, 66);
this.radright.TabIndex = 4;
this.radright.Text = "우측";
this.radright.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radright.UseVisualStyleBackColor = false;
//
// panel7 // panel7
// //
this.panel7.Dock = System.Windows.Forms.DockStyle.Left; this.panel7.Dock = System.Windows.Forms.DockStyle.Left;
@@ -531,6 +718,28 @@
this.panel7.Size = new System.Drawing.Size(15, 66); this.panel7.Size = new System.Drawing.Size(15, 66);
this.panel7.TabIndex = 13; this.panel7.TabIndex = 13;
// //
// radstrai
//
this.radstrai.BackColor = System.Drawing.Color.WhiteSmoke;
this.radstrai.Bordercolor = System.Drawing.Color.DimGray;
this.radstrai.BorderRadius = 7;
this.radstrai.BorderSize = 2;
this.radstrai.Checked = true;
this.radstrai.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radstrai.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radstrai.CheckWidth = 30;
this.radstrai.Dock = System.Windows.Forms.DockStyle.Left;
this.radstrai.Font = new System.Drawing.Font("맑은 고딕", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radstrai.ForeColor = System.Drawing.Color.Blue;
this.radstrai.Location = new System.Drawing.Point(144, 19);
this.radstrai.Name = "radstrai";
this.radstrai.Size = new System.Drawing.Size(119, 66);
this.radstrai.TabIndex = 3;
this.radstrai.TabStop = true;
this.radstrai.Text = "직진";
this.radstrai.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radstrai.UseVisualStyleBackColor = false;
//
// panel5 // panel5
// //
this.panel5.Dock = System.Windows.Forms.DockStyle.Left; this.panel5.Dock = System.Windows.Forms.DockStyle.Left;
@@ -539,6 +748,25 @@
this.panel5.Size = new System.Drawing.Size(15, 66); this.panel5.Size = new System.Drawing.Size(15, 66);
this.panel5.TabIndex = 12; this.panel5.TabIndex = 12;
// //
// radleft
//
this.radleft.BackColor = System.Drawing.Color.WhiteSmoke;
this.radleft.Bordercolor = System.Drawing.Color.DimGray;
this.radleft.BorderRadius = 7;
this.radleft.BorderSize = 2;
this.radleft.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radleft.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radleft.CheckWidth = 30;
this.radleft.Dock = System.Windows.Forms.DockStyle.Left;
this.radleft.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radleft.Location = new System.Drawing.Point(10, 19);
this.radleft.Name = "radleft";
this.radleft.Size = new System.Drawing.Size(119, 66);
this.radleft.TabIndex = 5;
this.radleft.Text = "좌측";
this.radleft.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radleft.UseVisualStyleBackColor = false;
//
// groupBox2 // groupBox2
// //
this.groupBox2.Controls.Add(this.arLabel1); this.groupBox2.Controls.Add(this.arLabel1);
@@ -610,208 +838,6 @@
this.panel12.Size = new System.Drawing.Size(15, 66); this.panel12.Size = new System.Drawing.Size(15, 66);
this.panel12.TabIndex = 14; this.panel12.TabIndex = 14;
// //
// panel6
//
this.panel6.Dock = System.Windows.Forms.DockStyle.Left;
this.panel6.Location = new System.Drawing.Point(129, 19);
this.panel6.Name = "panel6";
this.panel6.Size = new System.Drawing.Size(15, 66);
this.panel6.TabIndex = 12;
//
// label1
//
this.label1.BackColor = System.Drawing.Color.Blue;
this.label1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.label1.Font = new System.Drawing.Font("맑은 고딕", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label1.ForeColor = System.Drawing.Color.White;
this.label1.Location = new System.Drawing.Point(607, 577);
this.label1.Name = "label1";
this.label1.Padding = new System.Windows.Forms.Padding(0, 3, 0, 3);
this.label1.Size = new System.Drawing.Size(404, 38);
this.label1.TabIndex = 9;
this.label1.Text = "----------";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// radpbs2
//
this.radpbs2.BackColor = System.Drawing.Color.WhiteSmoke;
this.radpbs2.Bordercolor = System.Drawing.Color.DimGray;
this.radpbs2.BorderRadius = 7;
this.radpbs2.BorderSize = 2;
this.radpbs2.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radpbs2.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radpbs2.CheckWidth = 30;
this.radpbs2.Dock = System.Windows.Forms.DockStyle.Left;
this.radpbs2.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radpbs2.Location = new System.Drawing.Point(278, 19);
this.radpbs2.Name = "radpbs2";
this.radpbs2.Size = new System.Drawing.Size(119, 66);
this.radpbs2.TabIndex = 10;
this.radpbs2.Text = "On(2)";
this.radpbs2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radpbs2.UseVisualStyleBackColor = false;
//
// radpbs1
//
this.radpbs1.BackColor = System.Drawing.Color.WhiteSmoke;
this.radpbs1.Bordercolor = System.Drawing.Color.DimGray;
this.radpbs1.BorderRadius = 7;
this.radpbs1.BorderSize = 2;
this.radpbs1.Checked = true;
this.radpbs1.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radpbs1.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radpbs1.CheckWidth = 30;
this.radpbs1.Dock = System.Windows.Forms.DockStyle.Left;
this.radpbs1.Font = new System.Drawing.Font("맑은 고딕", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radpbs1.ForeColor = System.Drawing.Color.Blue;
this.radpbs1.Location = new System.Drawing.Point(144, 19);
this.radpbs1.Name = "radpbs1";
this.radpbs1.Size = new System.Drawing.Size(119, 66);
this.radpbs1.TabIndex = 9;
this.radpbs1.TabStop = true;
this.radpbs1.Text = "On(1)";
this.radpbs1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radpbs1.UseVisualStyleBackColor = false;
//
// radpbs0
//
this.radpbs0.BackColor = System.Drawing.Color.WhiteSmoke;
this.radpbs0.Bordercolor = System.Drawing.Color.DimGray;
this.radpbs0.BorderRadius = 7;
this.radpbs0.BorderSize = 2;
this.radpbs0.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radpbs0.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radpbs0.CheckWidth = 30;
this.radpbs0.Dock = System.Windows.Forms.DockStyle.Left;
this.radpbs0.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radpbs0.Location = new System.Drawing.Point(10, 19);
this.radpbs0.Name = "radpbs0";
this.radpbs0.Size = new System.Drawing.Size(119, 66);
this.radpbs0.TabIndex = 8;
this.radpbs0.Text = "Off(0)";
this.radpbs0.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radpbs0.UseVisualStyleBackColor = false;
//
// radspdh
//
this.radspdh.BackColor = System.Drawing.Color.WhiteSmoke;
this.radspdh.Bordercolor = System.Drawing.Color.DimGray;
this.radspdh.BorderRadius = 7;
this.radspdh.BorderSize = 2;
this.radspdh.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radspdh.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radspdh.CheckWidth = 30;
this.radspdh.Dock = System.Windows.Forms.DockStyle.Left;
this.radspdh.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radspdh.Location = new System.Drawing.Point(278, 19);
this.radspdh.Name = "radspdh";
this.radspdh.Size = new System.Drawing.Size(119, 66);
this.radspdh.TabIndex = 2;
this.radspdh.Text = "고속";
this.radspdh.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radspdh.UseVisualStyleBackColor = false;
//
// radspdm
//
this.radspdm.BackColor = System.Drawing.Color.WhiteSmoke;
this.radspdm.Bordercolor = System.Drawing.Color.DimGray;
this.radspdm.BorderRadius = 7;
this.radspdm.BorderSize = 2;
this.radspdm.Checked = true;
this.radspdm.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radspdm.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radspdm.CheckWidth = 30;
this.radspdm.Dock = System.Windows.Forms.DockStyle.Left;
this.radspdm.Font = new System.Drawing.Font("맑은 고딕", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radspdm.ForeColor = System.Drawing.Color.Blue;
this.radspdm.Location = new System.Drawing.Point(144, 19);
this.radspdm.Name = "radspdm";
this.radspdm.Size = new System.Drawing.Size(119, 66);
this.radspdm.TabIndex = 1;
this.radspdm.TabStop = true;
this.radspdm.Text = "중속";
this.radspdm.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radspdm.UseVisualStyleBackColor = false;
//
// radspdl
//
this.radspdl.BackColor = System.Drawing.Color.WhiteSmoke;
this.radspdl.Bordercolor = System.Drawing.Color.DimGray;
this.radspdl.BorderRadius = 7;
this.radspdl.BorderSize = 2;
this.radspdl.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radspdl.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radspdl.CheckWidth = 30;
this.radspdl.Dock = System.Windows.Forms.DockStyle.Left;
this.radspdl.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radspdl.Location = new System.Drawing.Point(10, 19);
this.radspdl.Name = "radspdl";
this.radspdl.Size = new System.Drawing.Size(119, 66);
this.radspdl.TabIndex = 0;
this.radspdl.Text = "저속";
this.radspdl.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radspdl.UseVisualStyleBackColor = false;
//
// radright
//
this.radright.BackColor = System.Drawing.Color.WhiteSmoke;
this.radright.Bordercolor = System.Drawing.Color.DimGray;
this.radright.BorderRadius = 7;
this.radright.BorderSize = 2;
this.radright.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radright.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radright.CheckWidth = 30;
this.radright.Dock = System.Windows.Forms.DockStyle.Left;
this.radright.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radright.Location = new System.Drawing.Point(278, 19);
this.radright.Name = "radright";
this.radright.Size = new System.Drawing.Size(119, 66);
this.radright.TabIndex = 4;
this.radright.Text = "우측";
this.radright.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radright.UseVisualStyleBackColor = false;
//
// radstrai
//
this.radstrai.BackColor = System.Drawing.Color.WhiteSmoke;
this.radstrai.Bordercolor = System.Drawing.Color.DimGray;
this.radstrai.BorderRadius = 7;
this.radstrai.BorderSize = 2;
this.radstrai.Checked = true;
this.radstrai.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radstrai.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radstrai.CheckWidth = 30;
this.radstrai.Dock = System.Windows.Forms.DockStyle.Left;
this.radstrai.Font = new System.Drawing.Font("맑은 고딕", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radstrai.ForeColor = System.Drawing.Color.Blue;
this.radstrai.Location = new System.Drawing.Point(144, 19);
this.radstrai.Name = "radstrai";
this.radstrai.Size = new System.Drawing.Size(119, 66);
this.radstrai.TabIndex = 3;
this.radstrai.TabStop = true;
this.radstrai.Text = "직진";
this.radstrai.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radstrai.UseVisualStyleBackColor = false;
//
// radleft
//
this.radleft.BackColor = System.Drawing.Color.WhiteSmoke;
this.radleft.Bordercolor = System.Drawing.Color.DimGray;
this.radleft.BorderRadius = 7;
this.radleft.BorderSize = 2;
this.radleft.CheckOffColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.radleft.CheckOnColor = System.Drawing.Color.LimeGreen;
this.radleft.CheckWidth = 30;
this.radleft.Dock = System.Windows.Forms.DockStyle.Left;
this.radleft.Font = new System.Drawing.Font("맑은 고딕", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.radleft.Location = new System.Drawing.Point(10, 19);
this.radleft.Name = "radleft";
this.radleft.Size = new System.Drawing.Size(119, 66);
this.radleft.TabIndex = 5;
this.radleft.Text = "좌측";
this.radleft.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radleft.UseVisualStyleBackColor = false;
//
// radbackward // radbackward
// //
this.radbackward.BackColor = System.Drawing.Color.WhiteSmoke; this.radbackward.BackColor = System.Drawing.Color.WhiteSmoke;
@@ -831,6 +857,14 @@
this.radbackward.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.radbackward.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radbackward.UseVisualStyleBackColor = false; this.radbackward.UseVisualStyleBackColor = false;
// //
// panel6
//
this.panel6.Dock = System.Windows.Forms.DockStyle.Left;
this.panel6.Location = new System.Drawing.Point(129, 19);
this.panel6.Name = "panel6";
this.panel6.Size = new System.Drawing.Size(15, 66);
this.panel6.TabIndex = 12;
//
// radforward // radforward
// //
this.radforward.BackColor = System.Drawing.Color.WhiteSmoke; this.radforward.BackColor = System.Drawing.Color.WhiteSmoke;
@@ -853,6 +887,20 @@
this.radforward.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.radforward.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.radforward.UseVisualStyleBackColor = false; this.radforward.UseVisualStyleBackColor = false;
// //
// label1
//
this.label1.BackColor = System.Drawing.Color.Blue;
this.label1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.label1.Font = new System.Drawing.Font("맑은 고딕", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label1.ForeColor = System.Drawing.Color.White;
this.label1.Location = new System.Drawing.Point(607, 577);
this.label1.Name = "label1";
this.label1.Padding = new System.Windows.Forms.Padding(0, 3, 0, 3);
this.label1.Size = new System.Drawing.Size(404, 38);
this.label1.TabIndex = 9;
this.label1.Text = "----------";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// guideSensor1 // guideSensor1
// //
this.guideSensor1.Dock = System.Windows.Forms.DockStyle.Top; this.guideSensor1.Dock = System.Windows.Forms.DockStyle.Top;
@@ -865,54 +913,6 @@
this.guideSensor1.TabIndex = 8; this.guideSensor1.TabIndex = 8;
this.guideSensor1.Text = "guideSensor1"; this.guideSensor1.Text = "guideSensor1";
// //
// btBack180
//
this.btBack180.BackColor = System.Drawing.Color.WhiteSmoke;
this.btBack180.Cursor = System.Windows.Forms.Cursors.Hand;
this.btBack180.Dock = System.Windows.Forms.DockStyle.Fill;
this.btBack180.Font = new System.Drawing.Font("맑은 고딕", 10F, System.Drawing.FontStyle.Bold);
this.btBack180.ForeColor = System.Drawing.Color.Black;
this.btBack180.Location = new System.Drawing.Point(205, 3);
this.btBack180.Name = "btBack180";
this.btBack180.Size = new System.Drawing.Size(196, 53);
this.btBack180.TabIndex = 15;
this.btBack180.Text = "백후 180도 회전 (Left)";
this.btBack180.UseVisualStyleBackColor = false;
this.btBack180.Click += new System.EventHandler(this.btBack180_Click);
//
// tableLayoutPanel2
//
this.tableLayoutPanel2.ColumnCount = 2;
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.Controls.Add(this.btBack180, 1, 0);
this.tableLayoutPanel2.Controls.Add(this.btLeft180, 0, 0);
this.tableLayoutPanel2.Controls.Add(this.btRight180, 0, 1);
this.tableLayoutPanel2.Controls.Add(this.button2, 1, 1);
this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 438);
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
this.tableLayoutPanel2.RowCount = 2;
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.Size = new System.Drawing.Size(404, 118);
this.tableLayoutPanel2.TabIndex = 12;
//
// button2
//
this.button2.BackColor = System.Drawing.Color.WhiteSmoke;
this.button2.Cursor = System.Windows.Forms.Cursors.Hand;
this.button2.Dock = System.Windows.Forms.DockStyle.Fill;
this.button2.Font = new System.Drawing.Font("맑은 고딕", 10F, System.Drawing.FontStyle.Bold);
this.button2.ForeColor = System.Drawing.Color.Black;
this.button2.Location = new System.Drawing.Point(205, 62);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(196, 53);
this.button2.TabIndex = 15;
this.button2.Text = "백후 180도 회전(Right)";
this.button2.UseVisualStyleBackColor = false;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// fManual // fManual
// //
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
@@ -930,11 +930,11 @@
this.VisibleChanged += new System.EventHandler(this.fManual_VisibleChanged); this.VisibleChanged += new System.EventHandler(this.fManual_VisibleChanged);
this.tableLayoutPanel1.ResumeLayout(false); this.tableLayoutPanel1.ResumeLayout(false);
this.panel2.ResumeLayout(false); this.panel2.ResumeLayout(false);
this.tableLayoutPanel2.ResumeLayout(false);
this.groupBox1.ResumeLayout(false); this.groupBox1.ResumeLayout(false);
this.grpSpeed.ResumeLayout(false); this.grpSpeed.ResumeLayout(false);
this.grpBunki.ResumeLayout(false); this.grpBunki.ResumeLayout(false);
this.groupBox2.ResumeLayout(false); this.groupBox2.ResumeLayout(false);
this.tableLayoutPanel2.ResumeLayout(false);
this.ResumeLayout(false); this.ResumeLayout(false);
} }

View File

@@ -79,12 +79,19 @@ namespace Project.ViewForm
} }
if (PUB.CheckManualChargeMode() == false) return; if (PUB.CheckManualChargeMode() == false) return;
arDev.Narumi.Speed spd = arDev.Narumi.Speed.Low; arDev.Narumi.Speed spd = arDev.Narumi.Speed.Low;
if (radspdh.Checked) spd = arDev.Narumi.Speed.High; if (radspdh.Checked) spd = arDev.Narumi.Speed.High;
else if (radspdl.Checked) spd = arDev.Narumi.Speed.Mid; else if (radspdm.Checked) spd = arDev.Narumi.Speed.Mid;
arDev.Narumi.Sensor ss = arDev.Narumi.Sensor.PBSOff; arDev.Narumi.Sensor ss = arDev.Narumi.Sensor.PBSOff;
if (radpbs0.Checked) ss = arDev.Narumi.Sensor.PBSOn; if (radpbs0.Checked) ss = arDev.Narumi.Sensor.PBSOn;
PUB.AGV.AGVMoveManual(arDev.Narumi.ManulOpt.BS, spd, ss); PUB.AGV.AGVMoveManual(arDev.Narumi.ManulOpt.BS, spd, ss);
PUB.sm.SetNewStep(StateMachine.eSMStep.IDLE); PUB.sm.SetNewStep(StateMachine.eSMStep.IDLE);
// [Manual Safety] Clear previous auto-task state
PUB._virtualAGV.TargetNode = null;
PUB._virtualAGV.StopPath();
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop; // Clear ACS Command
PUB.sm.ClearRunStep(); // Clear RunStep sequence
} }
private void arLabel2_Click(object sender, EventArgs e) private void arLabel2_Click(object sender, EventArgs e)
@@ -103,11 +110,17 @@ namespace Project.ViewForm
if (PUB.CheckManualChargeMode() == false) return; if (PUB.CheckManualChargeMode() == false) return;
arDev.Narumi.Speed spd = arDev.Narumi.Speed.Low; arDev.Narumi.Speed spd = arDev.Narumi.Speed.Low;
if (radspdh.Checked) spd = arDev.Narumi.Speed.High; if (radspdh.Checked) spd = arDev.Narumi.Speed.High;
else if (radspdl.Checked) spd = arDev.Narumi.Speed.Mid; else if (radspdm.Checked) spd = arDev.Narumi.Speed.Mid;
arDev.Narumi.Sensor ss = arDev.Narumi.Sensor.PBSOff; arDev.Narumi.Sensor ss = arDev.Narumi.Sensor.PBSOff;
if (radpbs0.Checked) ss = arDev.Narumi.Sensor.PBSOn; if (radpbs0.Checked) ss = arDev.Narumi.Sensor.PBSOn;
PUB.AGV.AGVMoveManual(arDev.Narumi.ManulOpt.FS, spd, ss); PUB.AGV.AGVMoveManual(arDev.Narumi.ManulOpt.FS, spd, ss);
PUB.sm.SetNewStep(StateMachine.eSMStep.IDLE); PUB.sm.SetNewStep(StateMachine.eSMStep.IDLE);
// [Manual Safety] Clear previous auto-task state
PUB._virtualAGV.TargetNode = null;
PUB._virtualAGV.StopPath();
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
PUB.sm.ClearRunStep();
} }
private void arLabel3_Click(object sender, EventArgs e) private void arLabel3_Click(object sender, EventArgs e)
@@ -131,6 +144,12 @@ namespace Project.ViewForm
if (radpbs0.Checked) ss = arDev.Narumi.Sensor.PBSOn; if (radpbs0.Checked) ss = arDev.Narumi.Sensor.PBSOn;
PUB.AGV.AGVMoveManual(arDev.Narumi.ManulOpt.RT, spd, ss); PUB.AGV.AGVMoveManual(arDev.Narumi.ManulOpt.RT, spd, ss);
PUB.sm.SetNewStep(StateMachine.eSMStep.IDLE); PUB.sm.SetNewStep(StateMachine.eSMStep.IDLE);
// [Manual Safety] Clear previous auto-task state
PUB._virtualAGV.TargetNode = null;
PUB._virtualAGV.StopPath();
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
PUB.sm.ClearRunStep();
} }
private void arLabel4_Click(object sender, EventArgs e) private void arLabel4_Click(object sender, EventArgs e)
@@ -154,6 +173,12 @@ namespace Project.ViewForm
if (radpbs0.Checked) ss = arDev.Narumi.Sensor.PBSOn; if (radpbs0.Checked) ss = arDev.Narumi.Sensor.PBSOn;
PUB.AGV.AGVMoveManual(arDev.Narumi.ManulOpt.LT, spd, ss); PUB.AGV.AGVMoveManual(arDev.Narumi.ManulOpt.LT, spd, ss);
PUB.sm.SetNewStep(StateMachine.eSMStep.IDLE); PUB.sm.SetNewStep(StateMachine.eSMStep.IDLE);
// [Manual Safety] Clear previous auto-task state
PUB._virtualAGV.TargetNode = null;
PUB._virtualAGV.StopPath();
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
PUB.sm.ClearRunStep();
} }
private void arLabel11_Click(object sender, EventArgs e) private void arLabel11_Click(object sender, EventArgs e)
@@ -176,7 +201,7 @@ namespace Project.ViewForm
if (dlg != DialogResult.Yes) return; if (dlg != DialogResult.Yes) return;
var opt = makeopt(); var opt = makeopt();
PUB.AGV.AGVMoveSet(opt); PUB.AGV.AGVMoveSet(opt);
PUB.AGV.AGVMoveRun(); PUB.AGV.AGVMoveRun(opt.Direction == arDev.Narumi.eMoveDir.Forward ? arDev.Narumi.eRunOpt.Forward : arDev.Narumi.eRunOpt.Backward);
} }
else else
{ {
@@ -210,8 +235,8 @@ namespace Project.ViewForm
//마크정보 //마크정보
guideSensor1.SensorValue = PUB.AGV.data.guidesensor; guideSensor1.SensorValue = PUB.AGV.data.guidesensor;
guideSensor1.LMark = PUB.AGV.signal.mark_sensor_1; guideSensor1.LMark = PUB.AGV.signal1.mark_sensor_1;
guideSensor1.RMark = PUB.AGV.signal.mark_sensor_2; guideSensor1.RMark = PUB.AGV.signal1.mark_sensor_2;
guideSensor1.Invalidate(); guideSensor1.Invalidate();
if (PUB.AGV.system1.agv_run || PUB.AGV.system1.agv_run_manual) if (PUB.AGV.system1.agv_run || PUB.AGV.system1.agv_run_manual)
@@ -368,12 +393,12 @@ namespace Project.ViewForm
private void btBack180_Click(object sender, EventArgs e) private void btBack180_Click(object sender, EventArgs e)
{ {
//[STX] C T B 0 0 0 0 9 9 [ETX] //[STX] C T B 0 0 0 0 9 9 [ETX]
PUB.AGV.AGVMoveBack180Turn(true); PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.ON);// (;// (true);
} }
private void button2_Click(object sender, EventArgs e) private void button2_Click(object sender, EventArgs e)
{ {
PUB.AGV.AGVMoveBack180Turn(false); PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.OFF);
} }
} }
} }

View File

@@ -109,6 +109,7 @@ namespace Project
this.lbIDLE = new arCtl.arLabel(); this.lbIDLE = new arCtl.arLabel();
this.lbStStep = new arCtl.arLabel(); this.lbStStep = new arCtl.arLabel();
this.panTopMenu = new System.Windows.Forms.Panel(); this.panTopMenu = new System.Windows.Forms.Panel();
this.lbBat = new AGVControl.BatteryLevelGauge();
this.pictureBox1 = new System.Windows.Forms.PictureBox(); this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.cmDebug = new System.Windows.Forms.ContextMenuStrip(this.components); this.cmDebug = new System.Windows.Forms.ContextMenuStrip(this.components);
this.mapFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.mapFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -121,12 +122,12 @@ namespace Project
this.debugtestToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.debugtestToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.pandBottomDIO = new System.Windows.Forms.Panel(); this.pandBottomDIO = new System.Windows.Forms.Panel();
this.panel9 = new System.Windows.Forms.Panel(); this.panel9 = new System.Windows.Forms.Panel();
this.IOState = new arFrame.Control.GridView();
this.SSInfo = new arFrame.Control.GridView();
this.panDlg = new System.Windows.Forms.Panel(); this.panDlg = new System.Windows.Forms.Panel();
this.arPanel2 = new arCtl.arPanel(); this.arPanel2 = new arCtl.arPanel();
this.arPanel1 = new arCtl.arPanel(); this.arPanel1 = new arCtl.arPanel();
this.IOState = new arFrame.Control.GridView(); this.xbeeSettingToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SSInfo = new arFrame.Control.GridView();
this.lbBat = new AGVControl.BatteryLevelGauge();
this.panRight.SuspendLayout(); this.panRight.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout();
this.panel4.SuspendLayout(); this.panel4.SuspendLayout();
@@ -1622,6 +1623,26 @@ namespace Project
this.panTopMenu.Size = new System.Drawing.Size(1278, 50); this.panTopMenu.Size = new System.Drawing.Size(1278, 50);
this.panTopMenu.TabIndex = 134; this.panTopMenu.TabIndex = 134;
// //
// lbBat
//
this.lbBat.BorderColor = System.Drawing.Color.DimGray;
this.lbBat.CurA = 0F;
this.lbBat.Dock = System.Windows.Forms.DockStyle.Right;
this.lbBat.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbBat.ForeColor = System.Drawing.Color.Gray;
this.lbBat.IsOpen = true;
this.lbBat.Location = new System.Drawing.Point(830, 0);
this.lbBat.MaxA = 0F;
this.lbBat.Name = "lbBat";
this.lbBat.Padding = new System.Windows.Forms.Padding(0, 12, 0, 12);
this.lbBat.sign = "%";
this.lbBat.Size = new System.Drawing.Size(48, 50);
this.lbBat.TabIndex = 23;
this.lbBat.Text = "12";
this.lbBat.VLevel = 50F;
this.lbBat.Volt = 0F;
this.lbBat.Click += new System.EventHandler(this.lbBat_Click);
//
// pictureBox1 // pictureBox1
// //
this.pictureBox1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40))))); this.pictureBox1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
@@ -1652,9 +1673,10 @@ namespace Project
this.ToolStripMenuItem, this.ToolStripMenuItem,
this.demoListLotToolStripMenuItem, this.demoListLotToolStripMenuItem,
this.toolStripMenuItem5, this.toolStripMenuItem5,
this.refreshListToolStripMenuItem}); this.refreshListToolStripMenuItem,
this.xbeeSettingToolStripMenuItem});
this.cmDebug.Name = "cmVision"; this.cmDebug.Name = "cmVision";
this.cmDebug.Size = new System.Drawing.Size(229, 324); this.cmDebug.Size = new System.Drawing.Size(229, 368);
// //
// mapFileToolStripMenuItem // mapFileToolStripMenuItem
// //
@@ -1732,70 +1754,6 @@ namespace Project
this.panel9.Size = new System.Drawing.Size(1278, 35); this.panel9.Size = new System.Drawing.Size(1278, 35);
this.panel9.TabIndex = 0; this.panel9.TabIndex = 0;
// //
// panDlg
//
this.panDlg.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(64)))));
this.panDlg.Dock = System.Windows.Forms.DockStyle.Fill;
this.panDlg.Location = new System.Drawing.Point(1, 58);
this.panDlg.Margin = new System.Windows.Forms.Padding(0);
this.panDlg.Name = "panDlg";
this.panDlg.Size = new System.Drawing.Size(1014, 706);
this.panDlg.TabIndex = 146;
//
// arPanel2
//
this.arPanel2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(5)))), ((int)(((byte)(5)))), ((int)(((byte)(5)))));
this.arPanel2.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
this.arPanel2.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.arPanel2.BorderSize = new System.Windows.Forms.Padding(0, 0, 0, 5);
this.arPanel2.Dock = System.Windows.Forms.DockStyle.Top;
this.arPanel2.Font = new System.Drawing.Font("Consolas", 10F, System.Drawing.FontStyle.Italic);
this.arPanel2.ForeColor = System.Drawing.Color.Khaki;
this.arPanel2.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
this.arPanel2.GradientRepeatBG = false;
this.arPanel2.Location = new System.Drawing.Point(1, 55);
this.arPanel2.Name = "arPanel2";
this.arPanel2.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arPanel2.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arPanel2.ProgressMax = 100F;
this.arPanel2.ProgressMin = 0F;
this.arPanel2.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arPanel2.ProgressValue = 0F;
this.arPanel2.ShadowColor = System.Drawing.Color.Black;
this.arPanel2.ShowBorder = true;
this.arPanel2.Size = new System.Drawing.Size(1278, 3);
this.arPanel2.TabIndex = 145;
this.arPanel2.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.arPanel2.TextShadow = false;
this.arPanel2.UseProgressBar = false;
//
// arPanel1
//
this.arPanel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(5)))), ((int)(((byte)(5)))), ((int)(((byte)(5)))));
this.arPanel1.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
this.arPanel1.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.arPanel1.BorderSize = new System.Windows.Forms.Padding(0, 0, 0, 5);
this.arPanel1.Dock = System.Windows.Forms.DockStyle.Top;
this.arPanel1.Font = new System.Drawing.Font("Consolas", 10F, System.Drawing.FontStyle.Italic);
this.arPanel1.ForeColor = System.Drawing.Color.Khaki;
this.arPanel1.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
this.arPanel1.GradientRepeatBG = false;
this.arPanel1.Location = new System.Drawing.Point(1, 51);
this.arPanel1.Name = "arPanel1";
this.arPanel1.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arPanel1.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arPanel1.ProgressMax = 100F;
this.arPanel1.ProgressMin = 0F;
this.arPanel1.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arPanel1.ProgressValue = 0F;
this.arPanel1.ShadowColor = System.Drawing.Color.Black;
this.arPanel1.ShowBorder = true;
this.arPanel1.Size = new System.Drawing.Size(1278, 4);
this.arPanel1.TabIndex = 135;
this.arPanel1.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.arPanel1.TextShadow = false;
this.arPanel1.UseProgressBar = false;
//
// IOState // IOState
// //
this.IOState.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(50)))), ((int)(((byte)(50))))); this.IOState.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(50)))), ((int)(((byte)(50)))));
@@ -1948,25 +1906,76 @@ namespace Project
((ushort)(0))}; ((ushort)(0))};
this.SSInfo.Click += new System.EventHandler(this.SSInfo_Click); this.SSInfo.Click += new System.EventHandler(this.SSInfo_Click);
// //
// lbBat // panDlg
// //
this.lbBat.BorderColor = System.Drawing.Color.DimGray; this.panDlg.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(64)))));
this.lbBat.CurA = 0F; this.panDlg.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbBat.Dock = System.Windows.Forms.DockStyle.Right; this.panDlg.Location = new System.Drawing.Point(1, 58);
this.lbBat.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.panDlg.Margin = new System.Windows.Forms.Padding(0);
this.lbBat.ForeColor = System.Drawing.Color.Gray; this.panDlg.Name = "panDlg";
this.lbBat.IsOpen = true; this.panDlg.Size = new System.Drawing.Size(1014, 706);
this.lbBat.Location = new System.Drawing.Point(830, 0); this.panDlg.TabIndex = 146;
this.lbBat.MaxA = 0F; //
this.lbBat.Name = "lbBat"; // arPanel2
this.lbBat.Padding = new System.Windows.Forms.Padding(0, 12, 0, 12); //
this.lbBat.sign = "%"; this.arPanel2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(5)))), ((int)(((byte)(5)))), ((int)(((byte)(5)))));
this.lbBat.Size = new System.Drawing.Size(48, 50); this.arPanel2.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
this.lbBat.TabIndex = 23; this.arPanel2.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.lbBat.Text = "12"; this.arPanel2.BorderSize = new System.Windows.Forms.Padding(0, 0, 0, 5);
this.lbBat.VLevel = 50F; this.arPanel2.Dock = System.Windows.Forms.DockStyle.Top;
this.lbBat.Volt = 0F; this.arPanel2.Font = new System.Drawing.Font("Consolas", 10F, System.Drawing.FontStyle.Italic);
this.lbBat.Click += new System.EventHandler(this.lbBat_Click); this.arPanel2.ForeColor = System.Drawing.Color.Khaki;
this.arPanel2.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
this.arPanel2.GradientRepeatBG = false;
this.arPanel2.Location = new System.Drawing.Point(1, 55);
this.arPanel2.Name = "arPanel2";
this.arPanel2.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arPanel2.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arPanel2.ProgressMax = 100F;
this.arPanel2.ProgressMin = 0F;
this.arPanel2.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arPanel2.ProgressValue = 0F;
this.arPanel2.ShadowColor = System.Drawing.Color.Black;
this.arPanel2.ShowBorder = true;
this.arPanel2.Size = new System.Drawing.Size(1278, 3);
this.arPanel2.TabIndex = 145;
this.arPanel2.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.arPanel2.TextShadow = false;
this.arPanel2.UseProgressBar = false;
//
// arPanel1
//
this.arPanel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(5)))), ((int)(((byte)(5)))), ((int)(((byte)(5)))));
this.arPanel1.BackColor2 = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
this.arPanel1.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(25)))), ((int)(((byte)(25)))), ((int)(((byte)(25)))));
this.arPanel1.BorderSize = new System.Windows.Forms.Padding(0, 0, 0, 5);
this.arPanel1.Dock = System.Windows.Forms.DockStyle.Top;
this.arPanel1.Font = new System.Drawing.Font("Consolas", 10F, System.Drawing.FontStyle.Italic);
this.arPanel1.ForeColor = System.Drawing.Color.Khaki;
this.arPanel1.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
this.arPanel1.GradientRepeatBG = false;
this.arPanel1.Location = new System.Drawing.Point(1, 51);
this.arPanel1.Name = "arPanel1";
this.arPanel1.ProgressColor1 = System.Drawing.Color.LightSkyBlue;
this.arPanel1.ProgressColor2 = System.Drawing.Color.DeepSkyBlue;
this.arPanel1.ProgressMax = 100F;
this.arPanel1.ProgressMin = 0F;
this.arPanel1.ProgressPadding = new System.Windows.Forms.Padding(0);
this.arPanel1.ProgressValue = 0F;
this.arPanel1.ShadowColor = System.Drawing.Color.Black;
this.arPanel1.ShowBorder = true;
this.arPanel1.Size = new System.Drawing.Size(1278, 4);
this.arPanel1.TabIndex = 135;
this.arPanel1.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.arPanel1.TextShadow = false;
this.arPanel1.UseProgressBar = false;
//
// xbeeSettingToolStripMenuItem
//
this.xbeeSettingToolStripMenuItem.Name = "xbeeSettingToolStripMenuItem";
this.xbeeSettingToolStripMenuItem.Size = new System.Drawing.Size(228, 22);
this.xbeeSettingToolStripMenuItem.Text = "xbee setting";
this.xbeeSettingToolStripMenuItem.Click += new System.EventHandler(this.xbeeSettingToolStripMenuItem_Click);
// //
// fMain // fMain
// //
@@ -2089,6 +2098,7 @@ namespace Project
private ToolStripMenuItem saveToolStripMenuItem; private ToolStripMenuItem saveToolStripMenuItem;
private ToolStripMenuItem loadToolStripMenuItem; private ToolStripMenuItem loadToolStripMenuItem;
private ToolStripMenuItem editorToolStripMenuItem; private ToolStripMenuItem editorToolStripMenuItem;
private ToolStripMenuItem xbeeSettingToolStripMenuItem;
} }
} }

View File

@@ -33,7 +33,6 @@ namespace Project
bool remoteClose = false; bool remoteClose = false;
bool forceClose = false; bool forceClose = false;
readonly usbdetect.DriveDetector usbdet;
public fMain() public fMain()
{ {
InitializeComponent(); InitializeComponent();
@@ -57,9 +56,12 @@ namespace Project
if (DateTime.Now > PUB.LastInputTime) PUB.LastInputTime = DateTime.Now; if (DateTime.Now > PUB.LastInputTime) PUB.LastInputTime = DateTime.Now;
}; };
usbdet = new usbdetect.DriveDetector(this);
usbdet.DeviceArrived += Usbdet_DeviceArrived; PUB._mapCanvas = new AGVNavigationCore.Controls.UnifiedAGVCanvas();
usbdet.DeviceRemoved += Usbdet_DeviceRemoved; PUB._mapCanvas.Dock = DockStyle.Fill;
PUB._mapCanvas.ShowGrid = false;
PUB._mapCanvas.BackColor = Color.FromArgb(32, 32, 32);
PUB._mapCanvas.ForeColor = Color.White;
this.panTopMenu.MouseMove += LbTitle_MouseMove; this.panTopMenu.MouseMove += LbTitle_MouseMove;
this.panTopMenu.MouseUp += LbTitle_MouseUp; this.panTopMenu.MouseUp += LbTitle_MouseUp;
@@ -73,32 +75,6 @@ namespace Project
} }
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (usbdet != null)
{
usbdet.WndProc(ref m);
}
}
private void Usbdet_DeviceRemoved(object sender, usbdetect.DriveDetectorEventArgs e)
{
//throw new NotImplementedException();
Console.WriteLine(e.Drive);
}
private void Usbdet_DeviceArrived(object sender, usbdetect.DriveDetectorEventArgs e)
{
//throw new NotImplementedException();
using (var fUpdate = new Dialog.fUpdateForm(e.Drive))
if (fUpdate.ShowDialog() == DialogResult.Yes)
{
//종료한다
remoteClose = true;
this.Close();
}
}
private void __Closing(object sender, FormClosingEventArgs e) private void __Closing(object sender, FormClosingEventArgs e)
{ {
@@ -173,6 +149,9 @@ namespace Project
VAR.STR[eVarString.SWVersion] = Application.ProductVersion; VAR.STR[eVarString.SWVersion] = Application.ProductVersion;
AutoLoadMapData();
//SETTING H/W //SETTING H/W
this.IOState.ItemClick += gridView2_ItemClick; this.IOState.ItemClick += gridView2_ItemClick;
VAR.BOOL.PropertyChanged += BOOL_PropertyChanged; VAR.BOOL.PropertyChanged += BOOL_PropertyChanged;
@@ -281,6 +260,73 @@ namespace Project
PUB.AddEEDB("프로그램 시작"); PUB.AddEEDB("프로그램 시작");
} }
void AutoLoadMapData()
{
//auto load
var mapPath = new System.IO.DirectoryInfo("route");
if (mapPath.Exists == false) mapPath.Create();
//맵파일로딩
var basefile = System.IO.Path.Combine(mapPath.FullName, "default.json");
if (System.IO.File.Exists(basefile) == false)
if (PUB.setting.LastMapFile.isEmpty() == false) basefile = PUB.setting.LastMapFile;
System.IO.FileInfo filePath = new System.IO.FileInfo(basefile);
if (filePath.Exists == false) //그래도없다면 맵폴더에서 파일을 찾아본다.
{
var files = mapPath.GetFiles("*.json");
if (files.Any()) filePath = files[0];
}
if (filePath.Exists)
{
var result = MapLoader.LoadMapFromFile(filePath.FullName);
if (result.Success)
{
PUB._mapCanvas.SetMapLoadResult(result);
PUB._mapCanvas.MapFileName = filePath.FullName;
// 🔥 가상 AGV 초기화 (첫 노드 위치에 생성)
if (PUB._virtualAGV == null && PUB._mapCanvas.Nodes.Count > 0)
{
var startNode = PUB._mapCanvas.Nodes.FirstOrDefault(n => n.IsNavigationNode());
if (startNode != null)
{
PUB._virtualAGV = new VirtualAGV(PUB.setting.MCID, startNode.Position, AgvDirection.Forward);
PUB._virtualAGV.LowBatteryThreshold = PUB.setting.BatteryLimit_Low;
PUB._virtualAGV.SetPosition(startNode, AgvDirection.Forward);
// 캔버스에 AGV 리스트 설정
var agvList = new System.Collections.Generic.List<AGVNavigationCore.Controls.IAGV> { PUB._virtualAGV };
PUB._mapCanvas.AGVList = agvList;
PUB.log.Add($"가상 AGV 생성: {startNode.Id} 위치");
}
}
else if (PUB._virtualAGV != null)
{
PUB._virtualAGV.LowBatteryThreshold = PUB.setting.BatteryLimit_Low;
// 기존 AGV가 있으면 캔버스에 다시 연결
var agvList = new System.Collections.Generic.List<AGVNavigationCore.Controls.IAGV> { PUB._virtualAGV };
PUB._mapCanvas.AGVList = agvList;
}
PUB.log.Add($"맵 파일 로드 완료: {filePath.Name}, 노드 수: {result.Nodes.Count}");
}
else
{
PUB.log.Add($"맵 파일 로딩 실패: {result.ErrorMessage}");
MessageBox.Show($"맵 파일 로딩 실패: {result.ErrorMessage}", "오류",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
PUB.log.Add($"맵 파일을 찾을 수 없습니다: {filePath.FullName}");
}
}
#region "Mouse Form Move" #region "Mouse Form Move"
private Boolean fMove = false; private Boolean fMove = false;
@@ -736,6 +782,12 @@ namespace Project
{ {
if (VAR.BOOL[eVarBool.FLAG_CHARGEONM]) if (VAR.BOOL[eVarBool.FLAG_CHARGEONM])
{ {
if (PUB.BMS.IsValid && PUB.BMS.IsCharging)
{
UTIL.MsgE("현재 배터리에서 충전 상태가 감지되고 있어 해제할 수 없습니다");
return;
}
var dlg = UTIL.MsgQ("수동 충전을 해제 할까요?"); var dlg = UTIL.MsgQ("수동 충전을 해제 할까요?");
if (dlg != DialogResult.Yes) return; if (dlg != DialogResult.Yes) return;
VAR.BOOL[eVarBool.FLAG_CHARGEONM] = false; VAR.BOOL[eVarBool.FLAG_CHARGEONM] = false;
@@ -760,8 +812,8 @@ namespace Project
//mapsave //mapsave
using (var sd = new SaveFileDialog()) using (var sd = new SaveFileDialog())
{ {
sd.Filter = "AGV Map Files (*.agvmap)|*.agvmap|All Files (*.*)|*.*"; sd.Filter = "AGV Map Files (*.json)|*.json|All Files (*.*)|*.*";
sd.DefaultExt = "agvmap"; sd.DefaultExt = "json";
sd.FileName = PUB._mapCanvas.MapFileName; sd.FileName = PUB._mapCanvas.MapFileName;
if (sd.ShowDialog() == DialogResult.OK) if (sd.ShowDialog() == DialogResult.OK)
{ {
@@ -776,8 +828,8 @@ namespace Project
var od = new OpenFileDialog var od = new OpenFileDialog
{ {
Filter = "AGV Map Files (*.agvmap)|*.agvmap|All Files (*.*)|*.*", Filter = "AGV Map Files (*.json)|*.json|All Files (*.*)|*.*",
DefaultExt = "agvmap", DefaultExt = "json",
FileName = PUB._mapCanvas.MapFileName, FileName = PUB._mapCanvas.MapFileName,
}; };
@@ -801,20 +853,9 @@ namespace Project
if (result.Success) if (result.Success)
{ {
var _mapCanvas = PUB._mapCanvas; var _mapCanvas = PUB._mapCanvas;
PUB._mapNodes = result.Nodes; _mapCanvas.SetMapLoadResult(result);
PUB.log.Add($"Set _mapNodes"); PUB.log.Add($"Set _mapNodes");
// 맵 캔버스에 데이터 설정
_mapCanvas.Nodes = result.Nodes;
// RfidMappings 제거됨 - MapNode에 통합
// 🔥 맵 설정 적용 (배경색, 그리드 표시)
if (result.Settings != null)
{
_mapCanvas.BackColor = System.Drawing.Color.FromArgb(result.Settings.BackgroundColorArgb);
_mapCanvas.ShowGrid = result.Settings.ShowGrid;
}
// 설정에 마지막 맵 파일 경로 저장 // 설정에 마지막 맵 파일 경로 저장
PUB.setting.LastMapFile = filePath; PUB.setting.LastMapFile = filePath;
PUB.setting.Save(); PUB.setting.Save();
@@ -856,7 +897,7 @@ namespace Project
} }
var _mapCanvas = PUB._mapCanvas; var _mapCanvas = PUB._mapCanvas;
var _mapNodes = PUB._mapNodes;
// 🔥 현재 캔버스 설정을 맵 파일에 저장 // 🔥 현재 캔버스 설정을 맵 파일에 저장
var settings = new MapLoader.MapSettings var settings = new MapLoader.MapSettings
@@ -865,7 +906,11 @@ namespace Project
ShowGrid = _mapCanvas.ShowGrid ShowGrid = _mapCanvas.ShowGrid
}; };
if (MapLoader.SaveMapToFile(filePath, _mapNodes, settings)) if (MapLoader.SaveMapToFile(filePath,
_mapCanvas.Nodes, _mapCanvas.Labels,
_mapCanvas.Images, _mapCanvas.Marks,
_mapCanvas.Magnets,
settings))
{ {
// 설정에 마지막 맵 파일 경로 저장 // 설정에 마지막 맵 파일 경로 저장
PUB.setting.LastMapFile = filePath; PUB.setting.LastMapFile = filePath;
@@ -989,5 +1034,11 @@ namespace Project
MessageBoxButtons.OK, MessageBoxIcon.Error); MessageBoxButtons.OK, MessageBoxIcon.Error);
} }
} }
private void xbeeSettingToolStripMenuItem_Click(object sender, EventArgs e)
{
var f = new Dialog.fXbeeSetting();
f.Show();
}
} }
} }

View File

@@ -126,160 +126,163 @@
<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="btDebug.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="btDebug.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAA0lJREFUWEfV iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAA2xJREFUWEfV
ll+IVVUUxm/+Q0ECQUUwBzEiLKTQEPPJSHsrIqiHCHwIIiiZHiVhHtQiBPWhl9QiB5maLsZMQk9BNwyG l01IVFEUx1+fFEQQVASVRBFREYVGVKvCaldEYIsQWgQRmNgyClxkRQTZok0fRknYx1BoQaugiQKZRp0Z
21m/te+dGedVa6TpbUQpKitvrDt7Lues2TP3zNAIffBxD9/e+1vr7HP3WrtS+T8DWG30+rJDVXeq6jfA x2bbh5HtjMJIzZp+53oc38cd51Yk9Ic/955zz/mfM/e9d98b779GZ2fnDKGak4eurq7V8AnFvwtlLj5d
X0Z7Ns3PWxbU6/WHgCngOtCMtOcpG/Pz/3OMjIysa7VaK7xumo15vRSAE8Bhr+dhAYDnRORs3H5788nI /rdIJBJLKNoHX8OMUuZ9sqZh/w7t7e2z8/n8VDULEJ+sqfl7oPsGeFBNK6QAMbs6Ojou6vbLL+9VZvQy
ZvwMZ0TkUCrBPETkdeB4R1DVyyJyrjArB1XdA1wDWiU5Duz2PrMAzlvMjqCqn4jIYGFWBNAD3EoE6cbp NLK+09agH8QcIueUmuaa3sd5Rc0IWK8g4RXMO7IHlmt6BKxdlZpqmgLXaeCumgEQXAY/QVuhidifTCaX
LMu2eT8D8AXwcUcQkfeAemFWBPBBwrws3/d+BuAH++x54UXg99SZBoYSxmU5lPCz2vGHiLzQEUdHRzfH qkwArN2DTWqaLTmDI6FmAPjPQVsBF55VmQDwv4QNahrHXvgNRp5pfK3QJu7CVpUpAJ+cHYP86D3q8rzu
8/x8YfbMgoGEcVkOeD8LDNwNIWzyA4Oq+l2r1Xogr8fdaQB/JwIsxD9V9Y28l3lbDOCzvN5GlmWPA78B 7u6FOOV53q2uAvC3QJu4C1tUpgApjH84lUotUNco5B6ggWfcwVPUZUCw7E4ajkBbkWIcQu+wyhiIttRg
fbPaxMTEGmBjfF4fQnhWRI4B/fEo1oHvVfUr4FNVPQm8LCKP1mq1VYUAMy/TB/waQnjMj7Whqq/E8jqs 7ba6xsENs5aFr7BeXV4ul5uJPV/nc+i6kkZP4mtGSB7FBHzB/CHjDcbTjFXErIrH49ONiA+s1cMBdNao
qm8Bt+Pb/ARUgd4QwtN++yzRLMu2hBCeUNXXROQU8DUwISKXgFdF5IptvSWYXzsHIrI3Trbd8NvqOW1v KwgE9hMgx2sb8xrGz1B+zTsYg3UkbwlvnzTKD1iEfz151TRwntjHMMf8FuMBxkeMw7BK0+wgcJMGy26E
lNA9bc5wCOEpH29eAKcTRkuifRrv3xWq+qE3WiqtRHv/AprN5lYR2ZFnyeP3T6TXPQe8v8VsBxeRg4kF tzXMfjgQ8tkoMW00uFHLlAYJF3wCf0W5NCrrDpIu2cT+hGg1qqwdmUxmMdu+3E8SXR6/H0rbmp8tYX2p
Zdkb6fVStFNVqVarK7Mse9IaTp7xnzxnUZ72jzd63dO8vL/FtNj+a3QAPOONPBeRwAHv3xW1Wm1trg4k aYpj7LAkuLJOaVsrSe6JSi8Wi03jDt7ANlX4SWNyJ1sTx0hctdC25qdo+bWFUlNqm12wgcTtYaEwpbjQ
WTKBO+bl/bsituGQMFxsAubR4/3nRZZl+4EacC9WQPv1potJ4Ofo8a2q7vPxClDVd63pxPK5K2ofJUzL tuYnDWxTWXdwkMwieewcsNKxgS+ipbLuIFFewymfUISODYhGmcqWBtdmKwlx+BPKCSijTdi1gQ9QNJ4S
JnDRPOx2pKqfx4Z21MdtQ0Tetj6tqi/l9Xq9/iBwI2HeLYHJsbGxDXkv6wPWJYE383rFmkus/UcKAxGx u1nL2EHACQJHuGZyfK5T32XmNmGXBm6KBmM5cXcY5YV2XHwRUPQoi4ME7lOXAV+6c/G/gZECJRrozWaz
MP3ogyyQwM1Go/GI9zGo6juxN7S7bBsWON7159yIZtFoNLaralYigauWsF8/i9jif7Fu2xHtgli4pc4D 81TGAF8VHIJH1DUKebnglLO/Vl0B0JwcTG9hoMgEDbxPp9MrNT0A4o+xLu8G85Y1wKiF8q1f9F8OgstI
6/HxSj2WSGDcnv2FJgUR+RK40BHsgpG/iJQB8LDVc6M9+/GFEC8m/R1hwZK4DLBdut8x58W/ttbNMuq+ ThJTKFSkgefSsKZFoK/4j+TVqMs00IRj/Cu1COQdj7h8UmelWKiBHpmHP2hsQOMB8dfUNA00w8KHiAuI
hk4AAAAASUVORK5CYII= XyHnuVDm6naC1JKaanrmONbppEB2abJrFoHn/QK21s0ynEiLrwAAAABJRU5ErkJggg==
</value> </value>
</data> </data>
<data name="btOpenDir.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="btOpenDir.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAa9JREFUWEfl iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAb9JREFUWEft
ls8rhEEch/2IEoo/QKHcXJzEiaNyEA64cXLk5OAi/gInR04Or6IcnRy2t/b9fGbHbr0XkdoTYTlIUfbV lr8vBEEUxxchEST8ARIkOo1KqFwpUQgKdFRKKoVG+AtUSirFSkiuVCk2m9zt7e7dJtcIkVxFcK4QCYld
+/a+b3aalux+N2XquWzPNE/T7Mzb1PTvh+/77SSXSG5UYdKcV7dB8ohk8A1lAHPm3LoMkm+WBW2Ustns 3517e9ZkHGLfRuEln8zsvLf7/e7M7A/tP8rlckehUFgCG03IUHnygYsfgeALfMuy5uiUZAMXf5bEPqOa
gDm/5mFZqBolktc1ogBM/TagXjw5jtNqCyiT1CSPSTqSZDKZbjPgnuRo+Fs+n+8sFAq9kgRB0FIRAGAR y+UG6bTkQiHUjCq4/CU2ZnOK5H9sICkedF1vUxnwgQuOgc6JYRg9soFbMBaOlUqlLs/z+jgJgqD1gwGs
wDDJK8uWSfAMYCEJeHddt4PkoUWU5C4J0PFu3FgkSYpJwL7Wuic+hKYkBoDTKEAptRZet6bQAHaiAAAT yyIYQf8iGmOmBr2FyMCLaZqdaA/pOC1uIgMuzcYVHadFJTKw77puL9pwE8pFbGAJssKAbdtraDPxZErs
ANYtgigA5sOAcvi3IHlgCtJorYeigxcfwAtTEOYlugtInsRP8k8fpboAwI0uIgBbAEZMoQHsJTfhDIAV CANwMgnWpSQ70JwPDfjhY4H2IJ5MAyz7sNh4tAGL8WQKPIp3ATon9En+7kcpETD9ZnjjGjpbYFRVxMye
iyDNahSgte4nuWsRRMnlcmNRQBAEzUqpc1MQ5sP3/a40IHwYLJIkl9Hi8SEctAjSOGmAUmrWIogCYDMN MIDODAysSMk0WBUGsBEGcLArJdlxHGdcGMBGaMF74ExVxMgr9l13wwAGalIBN+dCPAys/5CigBud5DUN
ILltCtIopabTgPBFMgVpPM/r+7oDRVMQ5jFdPA54sEiSnFUEKKWWSb5aRAluPc8brwgIB8k286tVgvRL 0z+rKGAFN71J8uIp2JYLuMFNT5O8WIKsqoiTfD7fT/JiBipyATP3JF0PDNxJBdycknQ9sB7LGHySiri4
+C+NT9lLlLEeyHt7AAAAAElFTkSuQmCC xvRPkPR7INEu/7Vy0PgT/juhaW/ZS5SxdhC40QAAAABJRU5ErkJggg==
</value> </value>
</data> </data>
<data name="btMReset.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="btMReset.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAA99JREFUWEft iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAABAlJREFUWEft
V1uLHEUUHrxfHkRFhJBL16kxBoMYUUQIRkXFBy8gGC8PKl4I3laNF3TdqdP1GEFEQR9EQUSIYvIiKCJi V91vFFUU3wiC6AMBY0xIgb13ltJAjBoJMSF+BYkPIgkJH/ogxo8QP6ha1MDandn7qAkxmsiDoYkhJoUA
fDH+Ax/UB0126tSsYDTxEqPJjHw9Xb09Z3t2dpc85oOC6Tpffed09alzalqtE1gmBr51UteZy3qebhVn LyQYQwj1BfkPeFAeBLozszWh0PJR0XbX3296ZvbOfnVteewvOdmZc37nzJk7555zNzOP/4uqyTxQctWT
HhFHjwa2d3d9tmmw7YpTNf+4ITi7OeT0njD9IkyDMeNgzOlD8WaLXr9iRN++RJg+bXA2aezBWq23LGBr ZaO3Bq56L3D1+77n7C6Z7FPVvc88KLT7D991NvlF/WPg6b8g1RYyFhb1scCo58Vt7ghNbh0Cn6l7UCcy
henPmui/4uhLYXq6y3RDdO2NXU/rxZlrgjNPiqPPhOm/Gv9wcPRQ0ovT7QuWHFRk+0JNqC85fTDXaVvN RF8JMztwaRHothX0Hyz5Ofx+UvL05tDNrS8Z3Y1P8Zzvqn2w/QzbvxZ/wnf1OxIuE+Zzj3WcVOg5X1iB
04g+y8TRLrUb07PbV58pTHPQCp5u1OtGEJjuLZwOFx8StndoziQEZ+9BTqQgApOvBbRd8yvMdugiYfqn KkFR/zRSyDlibonQZLNI5KTlS8kP93Utwe8IpOIb/bLQm8P39BskivN44DnbxdQxUDOvw3dMYlQR08TX
JP4hnq7UHOA7v/G0fS+tPRcDv7UdiC67Fp+h2sWlBFBLuH5gc5u2JwS2U5MExdNaYdqtPsdYfgvJlEg4 kD6hNWK4oNeA8LcQbwVGbxBTCpfM+kVXD65aRuG1qFMI3ewLiDEhseIXap8AjHHBVXxPvSbqBqA+eoXX
TgvsbGYC05v4niGntyrB3Lz/41T7dGF6HZx5Pv3Q4HyRAHL7Tkk4JjNm3YjN2U5NYL8wHak9I/P3Vc/O MiCSXwXbKYvXlp9hMcUkbidRJwg81Y+l/J7fE1vycBKwqI5e7s0txvW35AidL/NHwklLiwSKzhEhTAX9
vDxcQ180OG8OAFVMmH4DITj6StslN280CDWOyNYVmltbJ+NUzPn1VB8HXqRztH5LfLahEnH2KW3/yWdn arWoIwSuU7ACXIPcs+5Z+VeTe1d9Oe2jzya6tDQmwC4Gww0SsHXOizoB3vI7K0BbwQ5y6VPdmVnAXTFi
TKiCafy8opIcvLk9iQQ2N6X5gb/uFLxRZPORyuRv0BPQA4Rpb20en2Mn1mDtqJdFIGzuKwX6+/2aVdW8 urUtowf00iiojcBke5IgrvOxqBP8abIPwdauC8ZyZVYt2TdqWxwExbdF1Oj9Ly7kG4WeOg6bXckXOBM4
X3VWw1sO4DxxyiAWcLC2clAcS3pcmF7rPX/h2fX5AsNKZV8VZ7fV58cG4LNNiRNc+3Jt1wGEGbsmzUe2 A3D9m6Xn5xikD30lzMxA8bwpASrXzMoVosbKrHhY9Cnhw4WSkSQaOPQVSoTQ1R9C/03588cfEVUN053K
D1QOJqFIJGfvj0w7hOlozcFeBAHnkc23tfkjSFj0AKxNOsLZzYmzWH0Zi1m/+jxhOrDgLReOWb31gDh6 OYTl3yuqCC0TwEOFgqaTe7oZx07A73dWxnqszluinhlRIbnOntDTX8F5MgmOZWcSfDg+z0VLf48FyxlA
JnHQwLS9haOhj0uvs86kt0AzanA2bryr9cWZr0vbr43JieLQIDRAMYG9aLfzpwBFJ9V4DBQlFCf87oNb XwmDFc6+EnPa9ZeWGDZdy+E8GgdpI8P1S09gS34aczjARF0Dt0b9dikXVqv4LTiMrIfMJANRUAvoDb+K
18aLFMUN9ty8XbdVGBsA0/eJE9k8gVxATYg5vZI4KMvFHNMOcEaVoW0/rrjObtb2AmMC2I2GorlAZHqs 7XrT4oShTwhpQTOhPRq3tV3AphP3eAqbEpsTryvkRkEFfBHopyJ7Uf0g6jRgbJ6Ap38XCka0+oi1wJ6A
EmU7pe0JaOWJF539RNsrqADSVh9GS9VcYEnt2JurhOmvUuvvRS809QDU5eEgLiiaPwmRzdbiPjHUOBad Vv11zGFbjnSwkSP0BCjuEwkXJytRpwFjswROcaAIJQU87IOYx8Ek6gZwlMc8FPNpUTcCBDuBeKknOFKF
uUtzRhDZXl8miuCNcI0a+RyOdqGx6HUauNAUN+P5XexHNs9pXiNCx1zcnd5wfvXM9CC2rhbIUXHm8+JE kkJH49iojYhxR2LdbXugASFJoO7wMMYDitA6Bj7FTvjekhhToat2iak50BxeIhES8I3wmxfnacExi4NF
eLMFfFxM0TvE0bPooupSeih6c+eol2WivJbvGdmNJQwkHALUeitGeVvaKUy/a2e10UMR6vnsar3+uAG9 6C3BA010Mq6tYgXJfCbm9vALam0p3/Oo3PJo9jYC3JVAlElsp1+iHYGjN/k8mHJ2IMH9nKLg2IfScXyG
Hp0QSTX8W2Yejky39Bxdir9tmn8Ck/A/N4AkP8Mw5xAAAAAASUVORK5CYII= HRJudpBj+ZAVtCNhwTFBCTN3yGlpEHLTflCdlCEDZZN9VtzuPzjrOQlZVFh2/C1T72JLvlp29RP82ya0
eXSITOY/N4AkP6QQbUkAAAAASUVORK5CYII=
</value> </value>
</data> </data>
<data name="btCapture.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="btCapture.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAkVJREFUWEft iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAkNJREFUWEft
ls+rjGEUxwcpCz8WJPnxD7j3rqRYSGRBLK4kkWyJLORPEJENdyULV8pm/Aeo2czined8zmtmMSiKxSBZ lU1LVFEYx6+F0KKXhRKR9QVSVxLoQqJoUdjCiAgl3CpKi/AjiGG0KVfRIiVoM34DC2Yzi7nz1sxiKkjQ
+32vV+d6JzNnnhnvcEfJPfWpt/M9z3O+8z7v88xTKi3EvxKVSmUZMKWqgf5RA64CS/34Pw5gGsgKMuXH xahEazVfp98znYnT47nTmRgHIg/8OM88/3PO87/n3HsmOG7/TIvH46cymcxsNptN0Ue1EJ5Bq5nWuMai
/zLK5fISERkXkT0e4FSkySBmVPW4nydn3Hp1NU/TdDvwMjLRqHihqtvmmler1RXA60jRqGk1m83lpfwV c1D2ZNZM82+xWOxkOp3ugpsaFhxTBf7EHjv10LUWdEktU/Zny+VyfUxaUYscJcsY7K0UTyQSZ0isqQHN
e9H4FsnNK9a7ZGsVEc7Zl6yqd7yWM5vvhrtG/jwbqRuI9Y4aUNUttjwictJpM8C1EMI602u12mrDni0H oFQsFk8Hsi0OUThw5BqK1A7krBzCY/pWtDdaM+ybr+GtYOJ9o3kjtZ0GyPXI8WBkVGl78DyVSl0QPQzD
XM9reprF6GsAeKyqV4B3Hbn3qroXWAVcAl51aC0RuZgkyUoR2We1kTl7GGSgB1U9EkLYBDz1WgdPkiTZ NkFiyaG9MGPsOZFEGoCP5J/Sf7NyG+Ru0Z+DJ7Bq8kIJs9PJZPIs/W1+b1haJLUMHIJxD3jKy8SftWbx
CByLaD0MY+B+lmWL87X2miexWhF5GNG6KGxARA4ZPt8PVT0IHPZ5T2EDwBrgdiQfRURuNRqNtT7vKWrg CROX6IdV3kk9BhbL5fIJxspZu3SbpIxlJ947tN/wNsBi9wSX5oI179Lf13mNtwFoh3mViwSzrwuFwnmX
a5Zli4q80jaq+sDG2FivubpCBjI7sYB7Pj+Acn7C+nwXhQ2o6g7grM/3Q1XPhBB2+rxnGAM3bI8Db70W ZuNrYJctbfHZ0iqs+U7mEO9qzcZ7B+TGol/Q+RrEzA3r0n7hbYAx/fSPdD4Kxk/wxVxzaTb1GHgp3zjx
4U3+629GND9vMQPAlxDCmKruBj5G9DYfgF3AhI2J6F0MY8B4lqbp+hDCVhGRiF6zI9xqgOcRvYdhDRgt V605WDdP/0rlD+FtAHZ4ok7G3iDeUprNJlyHbtgxuUjqMSB84W/7Ikau8kKmHXrIWj0yhnhJaU7qNSCU
YH9+9o+JyFEjTdPNllPVA+6IHsjvGGjzCLggIicMewbqkbqBzBkAJr3wF5ks1ev1DcDniDhqPtn3Mnct YMDc/Z34GBIoekVyrHUH3b6ia/I3Bqp8gCmKjwgSQ95o3lQMEAxqoYkMBvl8voNgWwnN4Lu8L3J0cgwT
s307zP/4PGAn5Omft9If127bOueByyPGekx0NV+I/zq+A9PgZ1seSUA0AAAAAElFTkSuQmCC JLz/xxuA3JDjleLVRkI+nUmYOWKkRrcpe9z++xYEPwDT4GdbjzYebAAAAABJRU5ErkJggg==
</value> </value>
</data> </data>
<data name="btShowManual.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="btShowManual.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAPJJREFUWEft iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAP5JREFUWEft
lj0KwkAQhVNY6n0Uj6BtagvxEFqJvYWnEL1EmiDKvEmXG9gK3kDZsIZk3Jj4s7iEffBB2Pl5r9iEBIGX lj0KwkAQhVNY6n0Uj6BtagvxEFqJvYWnEL1EmiBKfrrcIK3gDfSNTDZk2WQj65IQ9sFHZObtzCMkQc/J
lyti5j6A/Qt2ADYAJnEc9+S8OlM13aN65XyO8pLzAYAQwK0hVyKaPmaZeabODH1VhGX39wNkENESwEqe qTdKkmQax/G1gQs4gVUYhhM+JkQ16rGHvKoZX2gXHyuFhg/eLXlFUbTmoxR+QzXJ04TPR0tRUTJpQYg9
N+A3Ab7AeoC13le103qA3MBQK9VztS1AHS0PQESHwofnqW49gAt3wAfwAf4bwIXXsA4HAxDR0NBohSRJ rge53oL/BDDAeoAjoHl1M60HEAsUvUpfiIqSyYTOA+gYeAC8njdciw+PyjP8Z8AFcAG6DdCH11BHDwPg
BtI/iKKoA+Akmy1wVF7SP1Oapl1mXjDzVv5IGrgUlqpnWS+hd86Vh/T9SMw8BnDWjGTdy6up7uxqphcq ts0VRiukaTrjtaWCIBih+ZDNFrjTLl5bVZZlY/y/24EzjMVDVMcTFEPpt8oj4Jlb2sHrzISBSwzMmQWX
UCfvAAAAAElFTkSuQmCC nZx+lOd9AOxqphdZ7gKgAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="btTopMenu_Volume.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="btTopMenu_Volume.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAABGdBTUEAALGPC/xhBQAABGpJREFUWEe1 iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAABGdBTUEAALGPC/xhBQAABHFJREFUWEe1
WFtIdFUUnp8ur0U9dCGK6il6DrrwU1AP8Ue9DRFBvzhnrz0XFcX77R9BFARREVHqQRRRMQM172g+mIga mFtIXFcUhifk8pqSPORCSGn7VPIcaFpCA+lDSUjfpIRCI97vKN5vGUEUhEFFRGkfRBEVa0GtdzQ+WBE1
hL4o+ntDJAXvqaNkumJt5gznrLMbxxnng485Z1/W+mbvtfbluFxxIiUl5UUhxMdSyh+klF4ASBNCpAoh EPRF0XhDJAreo45Dra78a7tncs6e7ajjnAUfnnPW2mv97r32uYzNXwsODv4iPDz8+8jIyN9BVERERBzO
vjAM4w3ePinw+XzvAEAZAPwJANcAgFG4AQCNJJrbSRhCiPcB4JcYRPwf5wDgK273znC73c8DQBUA/KNx Q8BPYWFh92SYtRYdHf01CueBd+AQkA/mQQWLlsMDZ0j6AMn/BqeJOIlR8Fym89+CgoKuIVER+E8mvij/
Eg9/9fv9r3I/MSE1NfX18D/jRhPlrmEYD7m/qACAd8MxwI3dFy+EEN9wv1oAwGsAsK4xct+89Hq9n3H/ xMTE3Jbpz2chISF3kYD/M13ii7CCHnssy5zNMOgbwD2gSxgI9tEGv8hyvg3Bd8CcYbBVuKKiop7IsnrL
NpSVlT0LAL9rOieLhx6P5y2uIwIAKNV0ipuBQAALCwsd5VZKKX9zuVwPuBaXYRhvA0CId7iN+fn5ODU1 y8u7gsB/lYFWshEaGvqlLO9tCMhVBlyI2NhYyszM1Prc4B72BqUvHSswGBrtKwQ41QGnkZ6eToODg1RU
hVVVVY66iooKDIVC2Nra6qizUkr5HdfjklL+xBtGo5QSm5ub8ezsDAn9/f22ehJKv+Xl5XhycqLachsW VOTlKygoIKfTSTU1NV4+IxD1Usr4bLj4py74JBBPVVVVtLu7S2xtbW0mPwvlv/n5+bS9vS1ijX6F97jF
PnW73c9ExAQCgZcpyDQNtSwqKsKlpSUlxAQXtL29jePj4+j1erGyshLPz89VP27LpC3rhBB+3kBHGpW2 XJZSbDZM7U1cdClBJ5KVlUWTk5NCiNtUQUtLS9TX10doXCosLKS9vT0xzhhjxLTrcBKjC1LhWamtrSWX
tja8vLy0idEJys7OxvX1dRwYGFDvo6OjODs767BpUgjRbRU0yBsQ09LSsKamBmtra7GhoQE3Nja4jghM yyVlfDZVUHJyMs3NzVF7e7s47+npoZGREVOMEWhoknKEoA5dUFxcHBUXF1NJSQmVl5fT/Py8LO9tbkH1
QR0dHWr0MjMzsaCgQE1pMBjEvLw8vLq6wpycHIefMP8GgOdIzwMAONE0UE5ihSnI5/Ph3NwczszMqPeR 9fVi9hITEykjI0Msqd1up7S0NDo4OKCUlBSvOpKP4CrruYSDbYPDAxc5q7kF4QFMo6OjNDw8LM67u7up
kREcGxtTzysrK9jS0uLwY1II8QGl+pu8wiQZihXWKcvIyFAjU1JSooL64OBAlff29uLk5KTDj4WPKd0f t7dXHE9PT1N1dbUnvwom5iFv9fuqww0nOqsZlywhIUHMTE5Ojmjq9fV1cb2lpYUGBgY8cRpe8XZ/rHEI
aioSEkScnp7Grq4uFXfX19dq+inTlpeXHX4sDFK6P9JUJCyop6dHZRk9Hx0dqdGieNza2nL4sbA6aYIo /BXEDA0NUWNjo+i7w8NDsfy806ampkxxCnbe7s80DsFFBDU3N4tdxsebm5titrgfFxcXTXEKDssE8e7q
u4aGhtQzpXxxcTHW1dXh2tqaw49NULKmbGFhQQUwxZN1yubn5x1+LAwmJahpAby4uMCsrCxsbGxU6xGV 7OwUx7zls7OzqbS0lGZnZ01xCg7Llmx8fFw0MPeTccnGxsZMcQp2S5qab4D7+/uUlJREFRUV4n7E13kJ
0xQODg46/Fj4OGra05wvLi4qrq6ucg02mIJIDMUJxRC904jQYkrPu7u7WF1d7fBjUqU9AQAGeKWO9fX1 Ozo6PHEaXvnc9rzmExMTgpmZGVlab25BLIb7hHuIz3lG+GbKxysrK+RwODz5VcS2Z8NJu+rUUVZWRhsb
eHh4yLUomII2Nzexr69PZVdTU5Pax2jaSMj+/r7aSrjdMCMLIwnyaRpoSbFAi93NzY1WkEka3dPTU/Un G1KC2dyCFhYWqLW1VeyuyspK8RzjZWMha2tr4lGi5pV4bowsKNrg8An3At/sjo6OpJRjU3uIZ3dnZ0f8
SAQFc3t7u8OehT9Htg6Px/PSXTZXIh03dnZ2tII6Ozvx+PhYiaH34eFhJSjK6NCIfh0RFB6lH3mj20gH EyyCm7murs4Uo/CXEMOGl6QbuHDmhyvDrxvLy8tSjllQQ0MDbW1tCTF83tXVJQT5mB2e0RdSzrHh4h9q
MNo0KYu6u7sj5TRFVJeeno4TExO4t7eHubm5jv4WrtiOHwQ6SsZzQCPS2cfv99vKaLenUaKgpmfex0op 0GnwCxg/NHkXNTU1ea7zErEvPj6e+vv7aXV1lVJTU01jFaZNrx9s/CoJx7lf0Bh+98HnjekaP+15lrip
5bc2MSYAoJg3jpcU1KWlpY5yDce4jgho2OiMq+mULB7QOsh12EA3SwBY03S+b9Ld7FPuX4vwRTGZd7OQ +djoU8Hs/CplmA3ObDXYX7ipc3NztT6FXlne23jakOiNZpBVrIP7srze+MsSQbOGQVbB32Y/yrK+DcH8
I6tuQ/jCOKMxlij/8nq9n3B/MYFWTgCouMePDT2GYbzC/dwZhmG8J6XsivdzjBBiVkr5JbebMOgyKYR4 oWjlt5nTa1edZhjEH4zDhiSB4gO2/w+yzPkMg6+CAhCoHxua8TC/JdP7b0jyLaa4EQn9+jkGvTKC8T/L
AgB/AMC/3DHjKgA0SCk/4naSAgB4wTCMD4UQ31s+6aVIKT+nTzm8faz4DwnDvF8jQGuEAAAAAElFTkSu dIEz/phE8tco8hb8byyqYQaUQ8gjOdxaQ7HrEPgdBP6Gou6f9IJx/JR/ypFh5zSb7RMJw7xfBlQNxwAA
QmCC AABJRU5ErkJggg==
</value> </value>
</data> </data>
<data name="arLabel5.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="arLabel5.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAjpJREFUWEfV iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAlxJREFUWEfV
V7tu1UAQDb+CBBUiTSigAZE+SsTjCxAdCGo+ANrkK6DjkYiEd3v3nNnbkFBEfESSKxHpgi4atGutx2Nj l81u00AUhcOrIMGqgg0sYEMFe1QE5QlQd63oug/QbstT0B2UVgEKgW1+7GxoWSAeghKJSgGF70bHlpnc
uA4RRzqN53HG9nh2vLDwPyKEcInktoi8VQJ4DeCC9TsxkHxMclYSwCPrd2IQkRdOAU+t31wAcJPkBxG5 cYbW5edIn6LcuefesT0e263/Ur1e7/pgMHiZZdmB0e/327Cg4fMXzTdhUoUJbGj4/MVRP3cm8FTDzYiC
aG0k920BJHetn8ZqDgA3rK0TInKb5DQlPhaRB7PZ7IzaQghXSX53CpjGGC+rj/qSvE/yW7aRvGV1XGi1 DyjcodkVhUoR/1RtLg41XMq8xDvUuq9QmjA+xDhW4RP+P55MJhdsjOt/i9h3jVUZ53l+w3Isl/9r8K0Y
hXjJjyQ/O9ctI4B3zvVprycB4KUTPAi1d6xeAwDOkjy0wQNwMhqNzls9FyJy10kwL+9YnVZoE7W8x7+i g2UbmyubrQxhg3fwIYh55NR448THSWeCpF3H3Ai2dtQmLiZwkeTj0NwAo263e1lt6sVMV5wCZ+WRys+X
Diqr0YnUxV9sIkPFeqI49ooiEqxGJ2KMyzZJwQMAKzaG5GpX7+j4tjEV0mx/kr6AryR/2ASZnniGiKxZ LaLIdTwV1GqrdJq0ij+GhQJM2yIDL2cKZ7Sn0mnidrrjFRJfOKK7Si1FfAmia8e2b6XOSnv7lu6Az/Cj
/4I6O3SAPddRHmNcqgJJbjkBDQJATdHB715Hwa0qSJvEcfC4XlNzQHLDiWuw1pinXgDJTevQQqmpOSA5 MIZ4zQtxpPc8j7C9wzawZ7DJQV6TbTr7ffBMv0DzvixRkVd7OSrsyzK99dpOgse2LFGR8yTwuFhPWf6B
duI8blZBukzoeU7yGcm9lrMgc7WmWCCdI9Y/U3PuqoZqdS4wInLdSZB5qN1uY5L4keP/i52foUXPQRTT CRDYCxMiZLJERc4w8MTYk2V6BhZgg+AOHIH3LChYkm1G1LDniOcxrOYh7Fgv6ynbrFjNtyvGkGNb7Uot
+1Z2PvY/HkRagIh8sonm4Ju8T/QCgIdOknl5z+q40OYoNpkheRxjXLR6DegO5wQPxfdWr4G2lUz3/74r peZfg9yS2tswVOJGlINdb6P2tP/2RmQTwPTeK3ZKXltNlZ8vTue6U+SsrKp8vWxxkFy8yTTJCbvfVbWJ
Gckd53q/lUyhC2RRxEQXlGwDcK3lsJqGEK5kv7TUTLKt91KaodXq6Tgej89ZWzoxbQF71k9jNUfvO+8L i8ROYGySt2oTl1bzzG3ImjjgN+mVDF4FMSPtlcxE8rIZZBzRfEVDNsFFYt7DasytdlNpxUvNqBiDtJfS
kq9sAYP/mHRBdwengH/3a3bqP6dD4if8nu9RoGmrKgAAAABJRU5ErkJggg== QjZb2B0Oh5cUKkUxe2KGEzjScCnzWo3kI08VzV4EzW1vb/bDpE403HIm8Oc+zfQC8/c+TptTq/UT/J7v
URSJG9cAAAAASUVORK5CYII=
</value> </value>
</data> </data>
<data name="btClose.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="btClose.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAplJREFUWEft iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAsRJREFUWEft
V7tuFEEQdALkPHIgByIQfAE2GRAZCMCQmAyZDA74AhAPG/7AwIectNfVeyQXnY0TbEJMgu2Ah+rUc/Q2 lz9TU0EUxdOovWKv9Golo59AsRMr0ELQRjsHO4n4CWD8A/oNUD9IZpKQxCYVIg1gKTYChejvvDmbbN57
s3d7xojEJY20mqrumenpnpmdmtrHLlAUxREAp0TkkojM8Jt9UbenEJGTIvIEQBfAz5pWqurjoihORPtd IS8gY8OZOZPHuefe3b27+xJKpzgKqtXqhdXV1Sv1ev0OHNezNIdPBgw0Cl8xWAv+6cNmo9GYZzKXnXZ8
oyzLYwBeANjJDFjXqH0O4Gj0NxFU9TyAjeB8U1WXATxU1Zts9s2+zaDdEJFz0W8jiMhVAFvO2YqIzAI4 NJvNixR+A/ejgQZR3tdwxGWOBlZzgyLbLhq4g77C5xyfD0U/S9uxJ3Cbro253HAg8R4FdqNiX9Em+Txj
ELUJ5ABcA7Dq7LZU9UrUjoStfDi4iLzq9XoHo64O1AJY9JNoHAnb889u8HtR0xQAFtwk1htViiXcwEhV SwaKwSm4DkPeLpObsKUYSNDKO4Mz8Lt2u33W4YGQl7ylkA93C3fCe/49JJP4zKGhQf5sqAO3Ct0UjDpw
X3quiYOoAbDkFvPMc3+ApeayfdWHvSzLC5Zkd6pWv0GOGmpTX7/fPwTgo/ncGVmiVudptrOpn+XkMvx7 SRKdeGs5QZECaQ91lkM9FrNoOR8YRjGG074et53O3ETTIXtsKQPF5JHXUmltbe0c2jeomvuHXlEmoHse
bhI2ODlqqB2WoKrecFvxqGrpAOBDchCTTkTm3AA/ANwdwc17W6uOL8ar54aw4zXt/XLkibDKQSRyfdGO ZjtpWYVHYDjhv2FmEtIck0fezhWkkw+siy8tZ0Hwi0076UPHhGbQwwAH8KlDebEnDiXgb92OH443LPdC
APAuTbDdbh+OPPf4jAv/g8gnZAYcOzjBw8ot8HTkGUZeKklwK/IeIeTZsEeYTfI/HXkmyrSLwFzkPWom e2eD9n7Fcg+IxatMOpGn2d4D9E/2HFQqlfOWu2Dfrtmg9r+wnAHx9IADBxeIzdmnBV613AWD6kslGKYt
MMyJHETktpvAxcj//y3wSSgibyNPZAafJAnfp2hlk5DgfW6iJmU43PMMV9kOO4y+2uLEcxXwMZGiwFst 5yLVcjHT9jScE+rfttyFxGCQ2XIu+kygcybyQM6j4GesW5a7+O9bEB9CJvDRcg+IHecQfrYn/xAKBJs2
9e/xQdSqWjrwmPxHR/Ga+dzudrvHq1YB9pJJs130XLxocogaAK+TP1V96rksLNz+FbQQNU0B4L7z8ylO FbmGnT3PifVsh19GPxXHW7ecBa2ZdxFxyrIm9i9fRGXLWeg1ieEkXsUbUDX3Wq3WJYfygUm/ZMJslywn
rhZ8PITX0BJDGXV1oFZV3zj7b51O52zUjQSfUWESa6p6PVaHBzlLuLTng8FV9XLUNoJFYt05Y2M58WJp SH/R5CHtocb7UI9OLFjuD4xqd/wraNahoUHu86jOZpEFJOCcjJEQ/xpaVisdHgh5We2HKP9XrVa77nAx
8chm47f1DUrNh33ilUdw3/iSmfBZvs2Ea7znTWAlypVqZsDUyLXGltrfgkep/ZrNpF+z2uN1H2PwC9Ia UGCCxHgSG2j307cjhmI+cGHPk8HR7toyHNyJraiYqOukL5YyhadFPVtLrlrEzaFXnob2jYksUmyYn+V7
w16WomAQAAAAAElFTkSuQmCC TGyh8J4XAcV0RbXSRjRQmoqVB16140KvUgbSv2bjop77vl5PcShKpb/SGsNeH5IytgAAAABJRU5ErkJg
gg==
</value> </value>
</data> </data>
<data name="pictureBox1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="pictureBox1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABGdBTUEAALGPC/xhBQAAA5JJREFUaEPt iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABGdBTUEAALGPC/xhBQAAA5hJREFUaEPt
mn1TElEUxv12fZVm+g7NNNMokqQ1KouACBKjKIovwAJLaqRGgCkpooiYjpbmS5omyctpzjV2bK8Ku+7m mvtTElEUx/3v+lea6X9oppnGV5LV+EABUSRG8f3gvaZGagaakuIDFdPR0nykaZqv0/1eASmuAstursbX
UpyZ3z/sc5jz7N17OHeHhoZ6qDQe2h4/eOR4wtQSWLPQRwNesM+7YDg1XhNgrViz0AcxgoLARqgmwFrr +fyye45zvnDv2XN3KMhLq3poefzgke2J/i6BmmPlXwk3rBNt1DHbeydArag5Vv6VcBEB3uW+OwFqzRvR
RtRE3YjaqBtRAmvcCYaZ3luxzw9SeYhqjHjW/NASMMHi9uqtNPsNVC6iGiOd0zZIbq9BpVC9kSYfAyUo EnkjWiNvRA0axuxUM9x4I9aJVmGuZow4FjxU5jXS1Nr8jZR4aoT5mjFSNWSh8NoCpZPmjRS59HTB/qC1
kWK3j3aplaiJFXk174LxxAQxgWaeejqpvVETe0Qb6ILT3BkxkthKgWHGTmkqce9GRld9wEw5+D3QxvWA 3Q3htwE0beQ1Gy96Q/3cBMw8dVQJ9wfQ9B4p9dbSwfEhNxJanWUFW4VxN3HrRrrnXaQftHET0AupnpyL
N+OndJW4dyPtYSus7X0iJo7OTkAXMlGaarhXI/4NDjSsgV+N/qgHnIsjlK4aJBkxRhzQwhkpnnFGcC5V HmHsTdy6kQp/Ay1sfuYmdg/3SddnFMal41aNeJYlKnbXcBNQc8BB9qkuYWw6ZBkxjNqoTDKk8Ixhn868
X4gt7oRgcpqYKBSLoGEZSlMtkoxgC7wuTs5PQRcyUvqb0Pj1cH6RI7nRbALMEQelqRZZjWB0TNhhLM1S EAt7APrCQ9zE2fk5M6UXxmWCLCNogSLtHx2wpWEQ5ogo9lTT0ckxzw1EQ2RiH5AoLhMUNQJV9lupJ+IW
OULcKQ+Y3w7webqgGXzrQUpXLZKMNLEMuOZYnqE5P1kNjNXdLHSErVSOkBeTFtg6/Exydo/3ofW1mdKI 5iXTOesg07uWWBaRzmci15JPGJsJsowUsSXQNu5O0D7u4d8GNL8RpUq2gUV5ybwcMNPqzhees7G3ReVv
QZKRsbQPhpbHeXri/TAQY/m728x2QSBL55Vh1znQ+o28vnfWDa7kGKUTgyQj19Ho00OhWCCFTaQiYI05 TMK4TJFlpCfiovaZ3gT1Y83UEnTzoqASdy15o6l5cdxLEpV6DLFoosaRTmoL9whjM0WWERGFrmq2Yc94
KU2Z7mgfhNMxos0XCzeOHWKQzYgp4oDZzIfL4gp50N5SnManh4tCnmjD6ShYYn2URiyyGWGzQdAFTPzj Yf2zo9QQtAvjQF2gifyRII89ZTnXjR3ZoJgRI9uoI4sfL4s7O2Wf+PXFFTPTJywG8kcCZA42CeOyQTEj
Yn/nhsHkKKXDR8g26+Z1+IhhrlAnFtmMILiBNw92SIH734+u3cD42Zfjr0SDU+7LKQulkYKsRkZWvGAM 7qiPdGwMj8v6vpNaw90pcVhCFraU4sISQ+7fcdmimBGADbyyvc4L3PqxK9zAuPZ17xuPwZT7atCcEiMH
O/m73cpZ/pib2PUgPOe6+evYfrENC79HCrIaQXDjnuZ+kEITWyvAXJlkcSJ4n02Qa7n8T2gO3LyPxCK7 RY10zTnJ4LfzIqFyyfzH3ORm7fW5VBe7S7z9og0n/w+5KGoEYOMeHP/khYZW50ifNMliIvjAHnzQ8ekv
katnCwycpXCmwmuNvk6+s3HLM2CLD1D5UpHdCIKtuFS6PO15P06RwxDOYDgUlqPSb41YFDGCJ7mFzWVS KvHmvsnjKG4k+WwBYZbCTIV7ha6qRGeTZobZiNKSki8XxY0AtOKLi8vTnvPTID8MYQbDUBhXumdNtqhi
MM5SeHDScSY4PPtGPsvsbUL7m8q//mJQxIg3E4A2zsLffdzUeGAqBzPpINOBMO8uKGIEwTa7d3JACsc2 BCe5yZUZXjBmKRycdJKRdg6/82uLmytU8Tb90z8bVDHiXPSyA5KZFw1hU+PAFJd+wManA1GuXFQxAtBm
i0dYDGwE2mAXpb8rihm5/OEb5lehHNgIsCEI9XdFMSMIHpSwzV4NnJzLXUxOFDWCr0C53ydAjKWdNOin N/e3eeFoszjCQmgEpT62rAQ5uaCakcsHXwcvPlloBGgIopxcUM0IwEEJbTZZmJzjXUxJVDWCV6BS7AQI
xb8hqQZFjWB7bfIy/Ms1XcBMXo1SOhlQ1shGCBwLLv7lGq6Q8LpcKG7kb1E3ojbqRtTG/2Hkn/gLxz/z Ta9HqHoo+zckmaCqEbTXIqc+8XJN5zXxV6PC2BxR1wjDNtmWeLmGb0gUowSqG/lX5I1ojbwRrfF/GLkX
p5p6qCR+AaqNxnTXLPGFAAAAAElFTkSuQmCC P+G4Nz+qyUsTKij4DaqNxnRXLBhpAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<metadata name="cmDebug.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="cmDebug.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">

View File

@@ -103,7 +103,6 @@
this.valIntervalBMS = new AGVControl.ValueSelect(); this.valIntervalBMS = new AGVControl.ValueSelect();
this.valIntervalXBE = new AGVControl.ValueSelect(); this.valIntervalXBE = new AGVControl.ValueSelect();
this.tabPage3 = new System.Windows.Forms.TabPage(); this.tabPage3 = new System.Windows.Forms.TabPage();
this.cmbChargerPos = new System.Windows.Forms.ComboBox();
this.label58 = new System.Windows.Forms.Label(); this.label58 = new System.Windows.Forms.Label();
this.label19 = new System.Windows.Forms.Label(); this.label19 = new System.Windows.Forms.Label();
this.label44 = new System.Windows.Forms.Label(); this.label44 = new System.Windows.Forms.Label();
@@ -1562,7 +1561,6 @@
// tabPage3 // tabPage3
// //
this.tabPage3.BackColor = System.Drawing.Color.DarkSlateBlue; this.tabPage3.BackColor = System.Drawing.Color.DarkSlateBlue;
this.tabPage3.Controls.Add(this.cmbChargerPos);
this.tabPage3.Controls.Add(this.label58); this.tabPage3.Controls.Add(this.label58);
this.tabPage3.Controls.Add(this.label19); this.tabPage3.Controls.Add(this.label19);
this.tabPage3.Controls.Add(this.label44); this.tabPage3.Controls.Add(this.label44);
@@ -1600,20 +1598,6 @@
this.tabPage3.TabIndex = 4; this.tabPage3.TabIndex = 4;
this.tabPage3.Text = "충전"; this.tabPage3.Text = "충전";
// //
// cmbChargerPos
//
this.cmbChargerPos.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cmbChargerPos.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Bold);
this.cmbChargerPos.FormattingEnabled = true;
this.cmbChargerPos.Items.AddRange(new object[] {
"HOME 아래",
"HOME",
"HOME 위"});
this.cmbChargerPos.Location = new System.Drawing.Point(1004, 11);
this.cmbChargerPos.Name = "cmbChargerPos";
this.cmbChargerPos.Size = new System.Drawing.Size(217, 45);
this.cmbChargerPos.TabIndex = 69;
//
// label58 // label58
// //
this.label58.AutoSize = true; this.label58.AutoSize = true;
@@ -3611,7 +3595,6 @@
private AGVControl.ValueSelect vcTagF5B; private AGVControl.ValueSelect vcTagF5B;
private AGVControl.ValueSelect vcTagF4F5; private AGVControl.ValueSelect vcTagF4F5;
private System.Windows.Forms.Label label58; private System.Windows.Forms.Label label58;
private System.Windows.Forms.ComboBox cmbChargerPos;
private System.Windows.Forms.CheckBox chkClearPos; private System.Windows.Forms.CheckBox chkClearPos;
private System.Windows.Forms.Label label7; private System.Windows.Forms.Label label7;
private AGVControl.ValueSelect vcXBID; private AGVControl.ValueSelect vcXBID;

View File

@@ -52,26 +52,19 @@ namespace Project
{ {
//통신값 표시 //통신값 표시
//tbPortBMS.Text = Pub.setting.Port_BMS;
///tbPortPLC.Text = PUB.setting.Port_PLC;
tbPortAGV.Text = PUB.setting.Port_AGV; tbPortAGV.Text = PUB.setting.Port_AGV;
tbPortXBE.Text = PUB.setting.Port_XBE; tbPortXBE.Text = PUB.setting.Port_XBE;
tbportBMS.Text = PUB.setting.Port_BAT; tbportBMS.Text = PUB.setting.Port_BAT;
// tbBaudPLC.Text = PUB.setting.Baud_PLC.ToString();
tbBaudAGV.Text = PUB.setting.Baud_AGV.ToString(); tbBaudAGV.Text = PUB.setting.Baud_AGV.ToString();
tbBaudXBE.Text = PUB.setting.Baud_XBE.ToString(); tbBaudXBE.Text = PUB.setting.Baud_XBE.ToString();
tbBaudBAT.Text = PUB.setting.Baud_BAT.ToString(); tbBaudBAT.Text = PUB.setting.Baud_BAT.ToString();
//valueSelect1.Value = Pub.setting.interval_bms;
valIntervalXBE.Value = PUB.setting.interval_xbe; valIntervalXBE.Value = PUB.setting.interval_xbe;
vcpidDS.Value = PUB.setting.ZSpeed; vcpidDS.Value = PUB.setting.ZSpeed;
//valueSelect4.Value = PUB.setting.interval_iostate;
valIntervalBMS.Value = PUB.setting.interval_bms; valIntervalBMS.Value = PUB.setting.interval_bms;
tbChargerID.Value = PUB.setting.ChargerID; tbChargerID.Value = PUB.setting.ChargerID;
cmbChargerPos.SelectedIndex = PUB.setting.chargerpos;
vcSCK.Value = PUB.setting.SCK; vcSCK.Value = PUB.setting.SCK;
vcSSK.Value = PUB.setting.SSK; vcSSK.Value = PUB.setting.SSK;
vcSTT.Value = PUB.setting.STT; vcSTT.Value = PUB.setting.STT;
@@ -218,28 +211,19 @@ namespace Project
propertyGrid1.Invalidate(); propertyGrid1.Invalidate();
//통신정보저장 //통신정보저장
// Pub.setting.Port_BMS = tbPortBMS.Text;
PUB.setting.Port_XBE = tbPortXBE.Text; PUB.setting.Port_XBE = tbPortXBE.Text;
//PUB.setting.Port_PLC = tbPortPLC.Text;
PUB.setting.Port_AGV = tbPortAGV.Text; PUB.setting.Port_AGV = tbPortAGV.Text;
PUB.setting.Port_BAT = tbportBMS.Text; PUB.setting.Port_BAT = tbportBMS.Text;
// Pub.setting.Baud_bms = int.Parse(tbBaudBms.Text);
PUB.setting.Baud_XBE = int.Parse(tbBaudXBE.Text); PUB.setting.Baud_XBE = int.Parse(tbBaudXBE.Text);
//PUB.setting.Baud_PLC = int.Parse(tbBaudPLC.Text);
PUB.setting.Baud_AGV = int.Parse(tbBaudAGV.Text); PUB.setting.Baud_AGV = int.Parse(tbBaudAGV.Text);
PUB.setting.Baud_BAT = int.Parse(tbBaudBAT.Text); PUB.setting.Baud_BAT = int.Parse(tbBaudBAT.Text);
PUB.setting.ChargerID = (int)(tbChargerID.Value); PUB.setting.ChargerID = (int)(tbChargerID.Value);
//Pub.setting.interval_bms = (float)valueSelect1.Value;
PUB.setting.interval_bms = (int)valIntervalBMS.Value; PUB.setting.interval_bms = (int)valIntervalBMS.Value;
PUB.setting.interval_xbe = (float)valIntervalXBE.Value; PUB.setting.interval_xbe = (float)valIntervalXBE.Value;
PUB.setting.ZSpeed = (byte)vcpidDS.Value; PUB.setting.ZSpeed = (byte)vcpidDS.Value;
//PUB.setting.interval_iostate = (byte)valueSelect4.Value;
PUB.setting.chargerpos = this.cmbChargerPos.SelectedIndex;
PUB.setting.AutoModeOffAndClearPosition = this.chkClearPos.Checked; PUB.setting.AutoModeOffAndClearPosition = this.chkClearPos.Checked;
//PUB.setting.MotorUpTime = (byte)valueSelect5.Value;
//AGV정보 저장 //AGV정보 저장
PUB.setting.Enable_Speak = this.btSpeaker.ProgressValue != 0; PUB.setting.Enable_Speak = this.btSpeaker.ProgressValue != 0;
@@ -294,7 +278,6 @@ namespace Project
//시스템옵션 //시스템옵션
PUB.setting.ChargeMaxTime = (int)vcChargeMaxTime.Value; PUB.setting.ChargeMaxTime = (int)vcChargeMaxTime.Value;
//Pub.setting.ChargeIdleInterval = (int)nudChargeIdleInterval.Value;
PUB.setting.ChargeRetryTerm = (int)vcChargeRetryTerm.Value; PUB.setting.ChargeRetryTerm = (int)vcChargeRetryTerm.Value;
//초기화시간 //초기화시간

View File

@@ -172,14 +172,17 @@ namespace Project.StateMachine
var runningStartTime = DateTime.Now; var runningStartTime = DateTime.Now;
var runningTask = System.Threading.Tasks.Task.Run(() => var runningTask = System.Threading.Tasks.Task.Run(() =>
{ {
try
{ //try
//{
runningHandler(this, new RunningEventArgs(Step, firstRun, StepRunTime)); runningHandler(this, new RunningEventArgs(Step, firstRun, StepRunTime));
} //}
catch (Exception ex) //catch (Exception ex)
{ //{
RaiseMessage("SM-ERROR-RUNNING", $"Running Exception: {ex.Message}\n{ex.StackTrace}"); // RaiseMessage("SM-ERROR-RUNNING", $"Running Exception: {ex.Message}\n{ex.StackTrace}");
} //}
}); });
// 타임아웃 2초 // 타임아웃 2초
@@ -193,7 +196,8 @@ namespace Project.StateMachine
{ {
RaiseMessage("SM-ERROR-RUNNING", $"Running Exception: {ex.Message}\n{ex.StackTrace}"); RaiseMessage("SM-ERROR-RUNNING", $"Running Exception: {ex.Message}\n{ex.StackTrace}");
} }
} System.Threading.Thread.Sleep(1); }
System.Threading.Thread.Sleep(1);
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -252,11 +256,8 @@ namespace Project.StateMachine
if (handler != null) if (handler != null)
{ {
var args = new StepChangeEventArgs(OldStep, newstep_); var args = new StepChangeEventArgs(OldStep, newstep_);
System.Threading.ThreadPool.QueueUserWorkItem(_ =>
{
try { handler(this, args); } try { handler(this, args); }
catch { /* 이벤트 핸들러 예외 무시 */ } catch { /* 이벤트 핸들러 예외 무시 */ }
});
} }
} }
else else
@@ -374,11 +375,8 @@ namespace Project.StateMachine
if (handler != null) if (handler != null)
{ {
var args = new StepChangeEventArgs(ostep, _step); var args = new StepChangeEventArgs(ostep, _step);
System.Threading.ThreadPool.QueueUserWorkItem(_ =>
{
try { handler(this, args); } try { handler(this, args); }
catch { /* 이벤트 핸들러 예외 무시 */ } catch { /* 이벤트 핸들러 예외 무시 */ }
});
} }
} //171214 } //171214

View File

@@ -12,7 +12,7 @@ using AR;
namespace arDev namespace arDev
{ {
public partial class Narumi : arRS232 public partial class Narumi
{ {
public bool AGVMoveSet(BunkiData opt) public bool AGVMoveSet(BunkiData opt)
@@ -236,12 +236,12 @@ namespace arDev
case eAgvCmd.ChargeOf: case eAgvCmd.ChargeOf:
cmdString = $"CBT{param}O0003"; ///0003=충전대기시간 cmdString = $"CBT{param}O0003"; ///0003=충전대기시간
retval = AddCommand(cmdString); retval = AddCommand(cmdString);
RaiseMessage(arRS232.MessageType.Normal, "충전취소전송"); RaiseMessage(NarumiSerialComm.MessageType.Normal, "충전취소전송");
break; break;
case eAgvCmd.ChargeOn: case eAgvCmd.ChargeOn:
cmdString = $"CBT{param}I0003"; ///0003=충전대기시간 cmdString = $"CBT{param}I0003"; ///0003=충전대기시간
retval = AddCommand(cmdString); retval = AddCommand(cmdString);
RaiseMessage(arRS232.MessageType.Normal, "충전명령전송"); RaiseMessage(NarumiSerialComm.MessageType.Normal, "충전명령전송");
break; break;
case eAgvCmd.TurnLeft: case eAgvCmd.TurnLeft:

View File

@@ -0,0 +1,69 @@
using System;
using System.Linq;
using AR;
namespace arDev
{
public partial class Narumi
{
public class Dataframe
{
public byte STX { get; private set; }
public byte ETX { get; private set; }
public byte[] Data { get; private set; }
public string DataString { get; private set; }
public byte[] checksum { get; private set; } = new byte[2];
public bool Valid { get; private set; } = false;
public string Message { get; private set; } = string.Empty;
public byte[] Buffer { get; private set; }
public string Cmd { get; private set; } = string.Empty;
public bool Parse(byte[] data, int MinRecvLength = 0)
{
if (data == null || data.Any() == false)
{
this.Message = string.Format("수신 데이터가 없습니다");
return false;
}
else if (data.Length < 5)
{
this.Message = $"데이터의 길이가 5보다 작습니다 길이={data.Length}";
return false;
}
else if (MinRecvLength > 0 && data.Length < MinRecvLength)
{
this.Message = $"데이터의 길이가 {MinRecvLength}보다 작습니다 길이={data.Length}";
return false;
}
else if (data[0] != 0x02 || data[data.Length - 1] != 0x03)
{
this.Message = $"STX/ETX Error";
return false;
}
Buffer = new byte[data.Length];
Array.Copy(data, Buffer, data.Length);
STX = data[0];
ETX = data[data.Length - 1];
Array.Copy(data, data.Length - 3, checksum, 0, 2);
Data = new byte[data.Length - 4];
Array.Copy(data, 1, Data, 0, data.Length - 4);
if (data.Length > 2) Cmd = System.Text.Encoding.Default.GetString(Data, 0, 3);
this.DataString = System.Text.Encoding.Default.GetString(Data);
Valid = true;
return true;
}
public Dataframe(byte[] buffer = null, int minlen = 0)
{
if (buffer != null) Parse(buffer);
}
}
}
}

View File

@@ -47,8 +47,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="DataEventArgs.cs" /> <Compile Include="DataEventArgs.cs" />
<Compile Include="Dataframe.cs" />
<Compile Include="EnumData.cs" /> <Compile Include="EnumData.cs" />
<Compile Include="Command.cs" /> <Compile Include="Command.cs" />
<Compile Include="NarumiSerialComm.cs" />
<Compile Include="Structure\ErrorFlag.cs" /> <Compile Include="Structure\ErrorFlag.cs" />
<Compile Include="Narumi.cs" /> <Compile Include="Narumi.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />

View File

@@ -8,18 +8,17 @@ using System.Threading.Tasks;
using System.Collections; using System.Collections;
using COMM; using COMM;
using AR; using AR;
using System.Xml;
namespace arDev namespace arDev
{ {
public partial class Narumi : arRS232 public partial class Narumi : arDev.NarumiSerialComm
{ {
Hashtable SystemCheck, ErrorCheck; Hashtable SystemCheck, ErrorCheck;
private Queue Errlog; // 에러발생시 코드 임시 저장용(쓰레드 동기화용) private Queue Errlog; // 에러발생시 코드 임시 저장용(쓰레드 동기화용)
public int nBatteryNo { get; set; } = 0; public int nBatteryNo { get; set; } = 0;
public Narumi() public Narumi()
{ {
SystemCheck = new Hashtable(); SystemCheck = new Hashtable();
@@ -124,75 +123,18 @@ namespace arDev
return bComplete; return bComplete;
} }
public class Dataframe
{
public byte STX { get; private set; }
public byte ETX { get; private set; }
public byte[] Data { get; private set; }
public string DataString { get; private set; }
public byte[] checksum { get; private set; } = new byte[2];
public bool Valid { get; private set; } = false;
public string Message { get; private set; } = string.Empty;
public byte[] Buffer { get; private set; }
public string Cmd { get; private set; } = string.Empty;
public bool Parse(byte[] data, int MinRecvLength = 0)
{
if (data == null || data.Any() == false)
{
this.Message = string.Format("수신 데이터가 없습니다");
return false;
}
else if (data.Length < 5)
{
this.Message = $"데이터의 길이가 5보다 작습니다 길이={data.Length}";
return false;
}
else if (MinRecvLength > 0 && data.Length < MinRecvLength)
{
this.Message = $"데이터의 길이가 {MinRecvLength}보다 작습니다 길이={data.Length}";
return false;
}
else if (data[0] != 0x02 || data[data.Length - 1] != 0x03)
{
this.Message = $"STX/ETX Error";
return false;
}
Buffer = new byte[data.Length];
Array.Copy(data, Buffer, data.Length);
STX = data[0];
ETX = data[data.Length - 1];
Array.Copy(data, data.Length - 3, checksum, 0, 2);
Data = new byte[data.Length - 4];
Array.Copy(data, 1, Data, 0, data.Length - 4);
if (data.Length > 2) Cmd = System.Text.Encoding.Default.GetString(Data, 0, 3);
this.DataString = System.Text.Encoding.Default.GetString(Data);
Valid = true;
return true;
}
public Dataframe(byte[] buffer = null, int minlen = 0)
{
if (buffer != null) Parse(buffer);
}
}
public override bool ProcessRecvData(byte[] data) public override bool ProcessRecvData(byte[] data)
{ {
//LastReceiveBuffer //LastReceiveBuffer
var frame = new Dataframe(data, MinRecvLength); var frame = new Dataframe(data, MinRecvLength);
if (frame.Valid == false) if (frame.Valid == false)
{ {
RaiseMessage(arRS232.MessageType.Error, frame.Message); RaiseMessage(MessageType.Error, frame.Message);
return false; return false;
} }
else if (frame.DataString.StartsWith("$") == false && CheckSum(data) == false) else if (frame.DataString.StartsWith("$") == false && CheckSum(data) == false)
{ {
RaiseMessage(arRS232.MessageType.Error, "Checksum Error MSG=" + frame.DataString); RaiseMessage(MessageType.Error, "Checksum Error MSG=" + frame.DataString);
return false; return false;
} }
@@ -218,7 +160,7 @@ namespace arDev
// $로 시작되는 AGV 상태 표시 // $로 시작되는 AGV 상태 표시
//var text_Sts_Etc = Encoding.Default.GetString(bRcvData, 3, bRcvData.Length - 2).TrimStart(' '); //20210311 김정만 - SmartX FrameWork 사용 안함으로 주석처리 //var text_Sts_Etc = Encoding.Default.GetString(bRcvData, 3, bRcvData.Length - 2).TrimStart(' '); //20210311 김정만 - SmartX FrameWork 사용 안함으로 주석처리
//var sMessageOther = Encoding.Default.GetString(bRcvData, 3, bRcvData.Length - 2).TrimStart(' '); //var sMessageOther = Encoding.Default.GetString(bRcvData, 3, bRcvData.Length - 2).TrimStart(' ');
RaiseMessage(arRS232.MessageType.Normal, "$메세지수신:" + frame.DataString); RaiseMessage(MessageType.Normal, "$메세지수신:" + frame.DataString);
} }
else else
{ {
@@ -228,7 +170,7 @@ namespace arDev
} }
catch (Exception ex) catch (Exception ex)
{ {
RaiseMessage(arRS232.MessageType.Error, ex.Message); RaiseMessage(MessageType.Error, ex.Message);
retval = false; retval = false;
} }
} }
@@ -279,14 +221,15 @@ namespace arDev
public SystemFlag1 system1 = new SystemFlag1(); public SystemFlag1 system1 = new SystemFlag1();
public ErrorFlag error = new ErrorFlag(); public ErrorFlag error = new ErrorFlag();
public AgvData data = new AgvData(); public AgvData data = new AgvData();
public Signal signal = new Signal(); public Signal1 signal1 = new Signal1();
public Signal2 signal2 = new Signal2();
#region [] STS(AGV상태정보) #region [] STS(AGV상태정보)
public string LastSTS { get; set; } = string.Empty; public string LastSTS { get; set; } = string.Empty;
private void RevSTS(Dataframe frame) private void RevSTS(Dataframe frame)
{ {
LastSTS = frame.DataString; LastSTS = frame.DataString;
string rcvdNow = frame.DataString; string rcvdNow = frame.DataString.Replace("\0","");
byte[] bRcvData = frame.Buffer; byte[] bRcvData = frame.Buffer;
var encoding = System.Text.Encoding.Default; var encoding = System.Text.Encoding.Default;
try try
@@ -322,42 +265,15 @@ namespace arDev
data.guidesensor = int.Parse(rcvdNow.Substring(idx, 1)); idx += 1; //가이드 좌측부터 1~9 data.guidesensor = int.Parse(rcvdNow.Substring(idx, 1)); idx += 1; //가이드 좌측부터 1~9
nDataTemp = Convert.ToByte(rcvdNow.Substring(idx, 2), 16); nDataTemp = Convert.ToByte(rcvdNow.Substring(idx, 2), 16);
signal.SetValue(nDataTemp); signal1.SetValue(nDataTemp); idx += 2;
//data.Sts = encoding.GetString(bRcvData, 19, 3); //20210311 김정만 - SmartX FrameWork 사용 안함으로 주석처리 if(idx <= rcvdNow.Length-2)
{
//var Sts_cSpeed = encoding.GetString(bRcvData, 19, 1)[0]; nDataTemp = Convert.ToByte(rcvdNow.Substring(idx, 2), 16);
//var Sts_cDirection = encoding.GetString(bRcvData, 20, 1)[0]; signal2.SetValue(nDataTemp);
//var Sts_cFB = encoding.GetString(bRcvData, 21, 1)[0]; }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////가이드센서 정보 (22)//////////////////////////////////////////////////////////////////////////////////////////////////////////
//var Sts_nGuide = 0;
//if (bRcvData[22] > 47 && bRcvData[22] < 58) { Sts_nGuide = Convert.ToInt32(encoding.GetString(bRcvData, 22, 1)); }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////마크센서 & 포토센서 정보 (23~24)////////////////////////////////////////////////////////////////////////////////////////////////////
//nDataTemp = Convert.ToInt32(encoding.GetString(bRcvData, 23, 2), 16);
//data.Sts_bMark1 = Convert.ToBoolean(nDataTemp & 0x4);
//data.Sts_bMark2 = Convert.ToBoolean(nDataTemp & 0x8);
//data.Sts_bCargo = Convert.ToBoolean(nDataTemp & 0x10);
////포토센서
//if (Sts_cFB == 'F')
//{
// system.Sts_nSenser = Convert.ToInt32(encoding.GetString(bRcvData, 26, 1));
//}
//else if (Sts_cFB == 'B')
//{
// system.Sts_nSenser = Convert.ToInt32(encoding.GetString(bRcvData, 27, 1));
//}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//AGV 속도/분기/방향 (19~21)//////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
DataReceive?.Invoke(this, new DataEventArgs(DataType.STS)); DataReceive?.Invoke(this, new DataEventArgs(DataType.STS));
} }

View File

@@ -0,0 +1,609 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
namespace arDev
{
public abstract class NarumiSerialComm : ISerialComm, IDisposable
{
protected System.IO.Ports.SerialPort _device;
protected ManualResetEvent _mre;
protected byte[] LastReceiveBuffer = new byte[] { };
/// <summary>
/// 최종 전송 메세지
/// </summary>
public byte[] lastSendBuffer = new byte[] { };
//public int ValidCheckTimeMSec { get; set; } = 5000;
protected List<byte> tempBuffer = new List<byte>();
protected Boolean findSTX = false;
public string ErrorMessage { get; set; }
public DateTime LastConnTime { get; set; }
public DateTime LastConnTryTime { get; set; }
public DateTime lastSendTime;
/// <summary>
/// 메세지 수신시 사용하는 내부버퍼
/// </summary>
protected List<byte> _buffer = new List<byte>();
/// <summary>
/// 데이터조회간격(초)
/// </summary>
public float ScanInterval { get; set; }
// public byte[] LastRecvData;
public string LastRecvString
{
get
{
if (LastReceiveBuffer == null) return String.Empty;
else return System.Text.Encoding.Default.GetString(LastReceiveBuffer);
}
}
/// <summary>
/// 마지막으로 데이터를 받은 시간
/// </summary>
public DateTime lastRecvTime;
public int WriteError = 0;
public string WriteErrorMessage = string.Empty;
public int WaitTimeout { get; set; } = 1000;
public int MinRecvLength { get; set; } = 1;
// Polling Thread related
protected Thread _recvThread;
protected volatile bool _isReading = false;
/// <summary>
/// 포트이름
/// </summary>
[Description("시리얼 포트 이름")]
[Category("설정"), DisplayName("Port Name")]
public string PortName
{
get
{
if (_device == null) return string.Empty;
else return _device.PortName;
}
set
{
if (this.IsOpen)
{
Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 포트이름을 변경할 수 없습니다", true));
}
else if (String.IsNullOrEmpty(value) == false)
_device.PortName = value;
else
{
Message?.Invoke(this, new MessageEventArgs("No PortName", true));
}
}
}
public int BaudRate
{
get
{
if (_device == null) return 0;
else return _device.BaudRate;
}
set
{
if (this.IsOpen)
{
Message?.Invoke(this, new MessageEventArgs("포트가 열려있어 BaudRate(를) 변경할 수 없습니다", true));
}
else if (value != 0)
_device.BaudRate = value;
else Message?.Invoke(this, new MessageEventArgs("No baud rate", true));
}
}
public NarumiSerialComm()
{
_device = new System.IO.Ports.SerialPort();
this.BaudRate = 57600;
ScanInterval = 10;
// _device.DataReceived += barcode_DataReceived; // Removed event handler
_device.ErrorReceived += this.barcode_ErrorReceived;
_device.WriteTimeout = 3000;
_device.ReadTimeout = 3000;
_device.ReadBufferSize = 8192;
_device.WriteBufferSize = 8192;
//_device.DiscardInBuffer();
//_device.DiscardOutBuffer();
ErrorMessage = string.Empty;
lastRecvTime = DateTime.Parse("1982-11-23");
LastConnTime = DateTime.Parse("1982-11-23");
LastConnTryTime = DateTime.Parse("1982-11-23");
lastRecvTime = DateTime.Parse("1982-11-23");
this._mre = new ManualResetEvent(true);
}
~NarumiSerialComm()
{
Dispose(false);
}
// Flag: Has Dispose already been called?
bool disposed = false;
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
// Free any other managed objects here.
//
}
// Stop reading thread
_isReading = false;
// _device.DataReceived -= barcode_DataReceived; // Removed event handler
_device.ErrorReceived -= this.barcode_ErrorReceived;
if (_recvThread != null && _recvThread.IsAlive)
{
_recvThread.Join(500);
}
if (_device != null)
{
if (_device.IsOpen) _device.Close();
_device.Dispose();
}
// Free any unmanaged objects here.
//
disposed = true;
}
public Boolean Open()
{
try
{
if (_device.IsOpen == false)
{
_device.Open();
}
if (_device.IsOpen)
{
// Start polling thread
if (_isReading == false)
{
_isReading = true;
_recvThread = new Thread(ReadPort);
_recvThread.IsBackground = true;
_recvThread.Start();
}
return true;
}
return false;
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
Message.Invoke(this, new MessageEventArgs(ex.Message, true));
return false;
}
}
public string GetHexString(Byte[] input)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (byte b in input)
sb.Append(" " + b.ToString("X2"));
return sb.ToString();
}
/// <summary>
/// 포트가 열려있는지 확인
/// </summary>
[Description("현재 시리얼포트가 열려있는지 확인합니다")]
[Category("정보"), DisplayName("Port Open")]
public Boolean IsOpen
{
get
{
if (_device == null) return false;
return _device.IsOpen;
}
}
public virtual bool Close()
{
try
{
_isReading = false; // Stop thread loop
if (_recvThread != null && _recvThread.IsAlive)
{
if (!_recvThread.Join(500)) // Wait for thread to finish
{
// _recvThread.Abort(); // Avoid Abort if possible
}
}
if (_device != null && _device.IsOpen)
{
_device.DiscardInBuffer();
_device.DiscardOutBuffer();
_device.Close(); //dispose에서는 포트를 직접 클리어하지 않게 해뒀다.
return true;
}
else return false;
}
catch (Exception)
{
return false;
}
}
protected Boolean RaiseRecvData()
{
return RaiseRecvData(LastReceiveBuffer.ToArray(), false);
}
/// <summary>
/// 수신받은 메세지를 발생 시킵니다
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public virtual Boolean RaiseRecvData(byte[] Data, bool udpatelastbuffer)
{
//181206 - 최종수신 메세지 기록
lastRecvTime = DateTime.Now;
if (udpatelastbuffer && Data != null)
{
if (LastReceiveBuffer == null || LastReceiveBuffer.Length != Data.Length)
{
LastReceiveBuffer = new byte[Data.Length];
Array.Copy(Data, LastReceiveBuffer, Data.Length);
}
}
try
{
// UI update might need Invoke if this event handler updates UI directly,
// but usually the subscriber handles Invoke.
// Since we are running on a background thread now, subscribers must be aware.
Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage
if (ProcessRecvData(Data) == false)
{
//Message?.Invoke(this, new MessageEventArgs(Data, true)); //recvmessage
Message?.Invoke(this, new MessageEventArgs(this.ErrorMessage, true)); //errormessage
return false;
}
else
{
return true;
}
}
catch (Exception ex)
{
this.ErrorMessage = ex.Message;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
return false;
}
}
/// <summary>
/// 수신받은 자료를 처리한다
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public abstract bool ProcessRecvData(byte[] data);
#region "Internal Events"
void barcode_ErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
{
Message?.Invoke(this, new MessageEventArgs(e.ToString(), true));
}
byte[] buffer = new byte[] { };
// Replaced with ReadPort Loop
/*
void barcode_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
try
{
int ReadCount = _device.BytesToRead;
buffer = new byte[ReadCount];
_device.Read(buffer, 0, buffer.Length);
System.Text.StringBuilder LogMsg = new StringBuilder();
byte[] remainBuffer;
Repeat:
if (CustomParser(buffer, out remainBuffer))
{
//분석완료이므로 받은 데이터를 버퍼에 기록한다
if (LastReceiveBuffer == null || (LastReceiveBuffer.Length != tempBuffer.Count))
Array.Resize(ref LastReceiveBuffer, tempBuffer.Count);
Array.Copy(tempBuffer.ToArray(), LastReceiveBuffer, tempBuffer.Count);
tempBuffer.Clear();
//수신메세지발생
RaiseRecvData();
if (remainBuffer != null && remainBuffer.Length > 0)
{
//버퍼를 변경해서 다시 전송을 해준다.
Array.Resize(ref buffer, remainBuffer.Length);
Array.Copy(remainBuffer, buffer, remainBuffer.Length);
goto Repeat; //남은 버퍼가 있다면 진행을 해준다.
}
}
}
catch (Exception ex)
{
//if (IsOpen)
//{
// //_device.DiscardInBuffer();
// //_device.DiscardOutBuffer();
//}
ErrorMessage = ex.Message;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
}
}
*/
void ReadPort()
{
while (_isReading)
{
try
{
if (_device == null || !_device.IsOpen)
{
Thread.Sleep(100);
continue;
}
int readCount = _device.BytesToRead;
if (readCount > 0)
{
byte[] buffer = new byte[readCount];
_device.Read(buffer, 0, buffer.Length);
byte[] remainBuffer;
Repeat:
if (CustomParser(buffer, out remainBuffer))
{
//분석완료이므로 받은 데이터를 버퍼에 기록한다
if (LastReceiveBuffer == null || (LastReceiveBuffer.Length != tempBuffer.Count))
Array.Resize(ref LastReceiveBuffer, tempBuffer.Count);
Array.Copy(tempBuffer.ToArray(), LastReceiveBuffer, tempBuffer.Count);
tempBuffer.Clear();
//수신메세지발생
RaiseRecvData();
if (remainBuffer != null && remainBuffer.Length > 0)
{
//버퍼를 변경해서 다시 전송을 해준다.
buffer = new byte[remainBuffer.Length]; // Reallocate buffer for remaining data
Array.Copy(remainBuffer, buffer, remainBuffer.Length);
goto Repeat; //남은 버퍼가 있다면 진행을 해준다.
}
}
}
else
{
Thread.Sleep(20); // Data 없음, 대기
}
}
catch (Exception ex)
{
// Thread 상에서 Exception 발생 시 로그 남기고 계속 진행 여부 결정
// 여기서는 에러 메시지 발생시키고 Sleep
ErrorMessage = ex.Message;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
Thread.Sleep(1000);
}
}
}
#endregion
#region "External Events"
/// <summary>
/// 오류 및 기타 일반 메세지
/// </summary>
public event EventHandler<MessageEventArgs> Message;
#endregion
#region "Event Args"
/// <summary>
/// 데이터를 수신할떄 사용함(RAW 포함)
/// </summary>
public class ReceiveDataEventArgs : EventArgs
{
private byte[] _buffer = null;
/// <summary>
/// 바이트배열의 버퍼값
/// </summary>
public byte[] Value { get { return _buffer; } }
/// <summary>
/// 버퍼(바이트배열)의 데이터를 문자로 반환합니다.
/// </summary>
public string StrValue
{
get
{
//return string.Empty;
if (_buffer == null || _buffer.Length < 1) return string.Empty;
else return System.Text.Encoding.Default.GetString(_buffer);
}
}
public ReceiveDataEventArgs(byte[] buffer)
{
_buffer = buffer;
}
}
/// <summary>
/// 메세지를 강제 발생
/// </summary>
/// <param name="mt"></param>
/// <param name="message"></param>
protected virtual void RaiseMessage(MessageType mt, string message)
{
this.Message?.Invoke(this, new MessageEventArgs(mt, message));
}
public enum MessageType
{
Normal,
Error,
Send,
Recv,
}
public class MessageEventArgs : EventArgs
{
public MessageType MsgType { get; set; }
private string _message = string.Empty;
/// <summary>
/// Recv,Send,Normal,Error 모두 지원
/// </summary>
public string Message { get { return _message; } }
private byte[] _data = null;
/// <summary>
/// Recv,Send에서만 값이 존재 합니다
/// </summary>
public byte[] Data { get { return _data; } }
public MessageEventArgs(string Message, bool isError = false)
{
if (isError) MsgType = MessageType.Error;
else MsgType = MessageType.Normal;
_message = Message;
}
public MessageEventArgs(MessageType msgtype, string Message)
{
MsgType = msgtype;
_message = Message;
_data = System.Text.Encoding.Default.GetBytes(Message);
}
public MessageEventArgs(byte[] buffer, bool isRecv = true)
{
if (isRecv) MsgType = MessageType.Recv;
else MsgType = MessageType.Send;
_data = new byte[buffer.Length];
Array.Copy(buffer, _data, Data.Length);
_message = System.Text.Encoding.Default.GetString(_data);
}
}
#endregion
protected abstract bool CustomParser(byte[] buf, out byte[] remainBuffer);
/// <summary>
/// 포트가 열려있거나 데이터 수신시간이 없는경우 false를 반환합니다
/// </summary>
public Boolean IsValid
{
get
{
if (IsOpen == false) return false;
if (lastRecvTime.Year == 1982) return false;
var ts = DateTime.Now - lastRecvTime;
if (ts.TotalSeconds > (this.ScanInterval * 2.5)) return false;
return true;
}
}
protected bool WriteData(string cmd)
{
return WriteData(System.Text.Encoding.Default.GetBytes(cmd));
}
/// <summary>
/// 포트에 쓰기(barcode_DataReceived 이벤트로 메세지수신)
/// </summary>
protected Boolean WriteData(byte[] data)
{
Boolean bRet = false;
//171205 : 타임아웃시간추가
if (!_mre.WaitOne(WaitTimeout))
{
ErrorMessage = $"WriteData:MRE:WaitOne:TimeOut {WaitTimeout}ms";
this.Message?.Invoke(this, new MessageEventArgs(ErrorMessage, true));
return false;
}
_mre.Reset();
//Array.Resize(ref data, data.Length + 2);
try
{
lastSendTime = DateTime.Now;
if (lastSendBuffer == null) lastSendBuffer = new byte[data.Length]; //171113
else Array.Resize(ref lastSendBuffer, data.Length);
Array.Copy(data, lastSendBuffer, data.Length);
for (int i = 0; i < data.Length; i++)
_device.Write(data, i, 1);
//_device.Write(data, 0, data.Length);
//171113
this.Message?.Invoke(this, new MessageEventArgs(data, false));
bRet = true;
WriteError = 0;
WriteErrorMessage = string.Empty;
}
catch (Exception ex)
{
// this.isinit = false;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
bRet = false;
WriteError += 1; //연속쓰기오류횟수
WriteErrorMessage = ex.Message;
}
finally
{
_mre.Set();
}
return bRet;
}
}
}

View File

@@ -27,11 +27,23 @@ namespace arDev
/// <summary> /// <summary>
/// 기동시 자석 감지 에러 /// 기동시 자석 감지 에러
/// </summary> /// </summary>
runerror_by_no_magent_line, runerror_by_no_magent_line=5,
/// <summary> /// <summary>
/// 호출제어기 통신 오류 /// 호출제어기 통신 오류
/// </summary> /// </summary>
controller_comm_error = 11, controller_comm_error =6,
/// <summary>
/// 배터리 저전압
/// </summary>
battery_low_voltage=7,
spare08=8,
lift_timeout=9,
lift_driver_overcurrent=10,
lift_driver_emergency = 11,
/// <summary> /// <summary>
/// 도착경보기 통신 오류 /// 도착경보기 통신 오류
/// </summary> /// </summary>

View File

@@ -4,10 +4,26 @@ namespace arDev
{ {
public partial class Narumi public partial class Narumi
{ {
public enum eSignal1
public class Signal
{ {
private COMM.Flag _value { get; set; } = new COMM.Flag(16); front_gate_out = 0,
rear_gte_out,
mark_sensor_1,
mark_sensor_2,
lift_down_sensor,
lift_up_sensor,
magnet_relay,
charger_align_sensor,
}
public enum eSignal2
{
cart_detect1 = 0,
cart_detect2,
}
public class Signal1
{
private COMM.Flag _value { get; set; } = new COMM.Flag(8);
public void SetValue(Int16 value) { this._value.writeValue(value); } public void SetValue(Int16 value) { this._value.writeValue(value); }
public UInt16 Value public UInt16 Value
{ {
@@ -16,60 +32,37 @@ namespace arDev
return (UInt16)_value.Value; return (UInt16)_value.Value;
} }
} }
public enum eflag
{
front_gate_out = 0,
rear_sensor_out,
mark_sensor_1,
mark_sensor_2,
front_left_sensor,
front_right_sensor,
front_center_sensor,
charger_align_sensor,
lift_down,
lift_up,
magnet_on,
ChargetSensor,
cart_detect1,
cart_detect2,
Spare1,
Spare2
}
public bool GetValue(eflag idx) public bool GetValue(eSignal1 idx)
{ {
return _value.Get((int)idx); return _value.Get((int)idx);
} }
public bool GetChanged(eflag idx) public bool GetChanged(eSignal1 idx)
{ {
return _value.GetChanged((int)idx); return _value.GetChanged((int)idx);
} }
public Boolean front_gate_out { get { return GetValue(eflag.front_gate_out); } } public Boolean front_gate_out { get { return GetValue(eSignal1.front_gate_out); } }
public Boolean rear_sensor_out { get { return GetValue(eflag.rear_sensor_out); } } public Boolean rear_sensor_out { get { return GetValue(eSignal1.rear_gte_out); } }
public Boolean mark_sensor_1 { get { return GetValue(eflag.mark_sensor_1); } } public Boolean mark_sensor_1 { get { return GetValue(eSignal1.mark_sensor_1); } }
public Boolean mark_sensor_2 { get { return GetValue(eflag.mark_sensor_2); } } public Boolean mark_sensor_2 { get { return GetValue(eSignal1.mark_sensor_2); } }
public Boolean mark_sensor { get { return mark_sensor_1 || mark_sensor_2; } } public Boolean mark_sensor { get { return mark_sensor_1 || mark_sensor_2; } }
public Boolean front_left_sensor { get { return GetValue(eflag.front_left_sensor); } } public Boolean charger_align_sensor { get { return GetValue(eSignal1.charger_align_sensor); } }
public Boolean front_right_sensor { get { return GetValue(eflag.front_right_sensor); } } public Boolean lift_up { get { return GetValue(eSignal1.lift_up_sensor); } }
public Boolean front_center_sensor { get { return GetValue(eflag.front_center_sensor); } } public Boolean lift_down { get { return GetValue(eSignal1.lift_down_sensor); } }
public Boolean charger_align_sensor { get { return GetValue(eflag.charger_align_sensor); } } public Boolean magnet_on { get { return GetValue(eSignal1.magnet_relay); } }
public Boolean lift_up { get { return GetValue(eflag.lift_up); } }
public Boolean lift_down { get { return GetValue(eflag.lift_down); } }
public Boolean magnet_on { get { return GetValue(eflag.magnet_on); } }
public Boolean cart_detect1 { get { return GetValue(eflag.cart_detect1); } }
public Boolean cart_detect2 { get { return GetValue(eflag.cart_detect2); } }
public override string ToString() public override string ToString()
{ {
//모든사태값을 탭으로 구분하여 문자를 생성한다 //모든사태값을 탭으로 구분하여 문자를 생성한다
var sb = new System.Text.StringBuilder(); var sb = new System.Text.StringBuilder();
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
{ {
var def = Enum.IsDefined(typeof(eflag), i); var def = Enum.IsDefined(typeof(eSignal1), i);
if (def) if (def)
{ {
var flag = (eflag)i; var flag = (eSignal1)i;
var value = _value.Get(i); var value = _value.Get(i);
sb.AppendLine($"[{i:00}][{flag}] : {value}"); sb.AppendLine($"[{i:00}][{flag}] : {value}");
} }
@@ -86,10 +79,87 @@ namespace arDev
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
{ {
var def = Enum.IsDefined(typeof(eflag), i); var def = Enum.IsDefined(typeof(eSignal1), i);
if (def) if (def)
{ {
var flag = (eflag)i; var flag = (eSignal1)i;
var value = _value.Get(i);
string line = $"[{i:00}][{flag}] : {value}";
// : true가 포함된 줄은 파란색
if (value == true)
{
sb.AppendLine(@"\cf1 " + line + @"\cf0\line");
}
else
{
sb.AppendLine(line + @"\line");
}
}
}
sb.AppendLine("}");
return sb.ToString();
}
}
public class Signal2
{
private COMM.Flag _value { get; set; } = new COMM.Flag(8);
public void SetValue(Int16 value) { this._value.writeValue(value); }
public UInt16 Value
{
get
{
return (UInt16)_value.Value;
}
}
public bool GetValue(eSignal2 idx)
{
return _value.Get((int)idx);
}
public bool GetChanged(eSignal2 idx)
{
return _value.GetChanged((int)idx);
}
public Boolean cart_detect1 { get { return GetValue(eSignal2.cart_detect1); } }
public Boolean cart_detect2 { get { return GetValue(eSignal2.cart_detect2); } }
public override string ToString()
{
//모든사태값을 탭으로 구분하여 문자를 생성한다
var sb = new System.Text.StringBuilder();
for (int i = 0; i < 16; i++)
{
var def = Enum.IsDefined(typeof(eSignal2), i);
if(def)
{
var flag = (eSignal2)i;
var value = _value.Get(i);
sb.AppendLine($"[{i:00}][{flag}] : {value}");
}
}
return sb.ToString();
}
public string ToRtfString()
{
var sb = new System.Text.StringBuilder();
sb.AppendLine(@"{\rtf1\ansi\deff0");
sb.AppendLine(@"{\colortbl ;\red0\green0\blue255;}"); // Color 1 = Blue
for (int i = 0; i < 16; i++)
{
var def = Enum.IsDefined(typeof(eSignal2), i);
if (def)
{
var flag = (eSignal2)i;
var value = _value.Get(i); var value = _value.Get(i);
string line = $"[{i:00}][{flag}] : {value}"; string line = $"[{i:00}][{flag}] : {value}";

View File

@@ -44,6 +44,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Flag.cs" /> <Compile Include="Flag.cs" />
<Compile Include="ISerialComm.cs" />
<Compile Include="RS232.cs" /> <Compile Include="RS232.cs" />
<Compile Include="Var.cs" /> <Compile Include="Var.cs" />
<Compile Include="Enum.cs" /> <Compile Include="Enum.cs" />

View File

@@ -114,7 +114,7 @@ namespace COMM
WAIT_CHARGEACK, WAIT_CHARGEACK,
//agv area start ( 64 ~ 95) //agv area start ( 64 ~ 95)
DISABLE_AUTOCONN_XBEE,
//area start (96~127) //area start (96~127)

Some files were not shown because too many files have changed in this diff Show More