This commit is contained in:
backuppc
2025-11-10 14:43:47 +09:00
parent 68745f23bb
commit 6e54633c08
57 changed files with 4432 additions and 1018 deletions

View File

@@ -27,10 +27,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ENIGProtocol", "SubProject\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGV4", "Project\AGV4.csproj", "{D6B3880D-7D5C-44E2-B6A5-CF6D881A8A38}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVMapEditor", "AGVMapEditor\AGVMapEditor.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVSimulator", "AGVSimulator\AGVSimulator.csproj", "{B2C3D4E5-0000-0000-0000-000000000000}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "솔루션 항목", "솔루션 항목", "{2A3A057F-5D22-31FD-628C-DF5EF75AEF1E}"
ProjectSection(SolutionItems) = preProject
build.bat = build.bat
@@ -40,10 +36,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "솔루션 항목", "솔루
TODO.md = TODO.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVNavigationCore", "AGVNavigationCore\AGVNavigationCore.csproj", "{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Logic", "Logic", "{E5C75D32-5AD6-44DD-8F27-E32023206EBB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVNavigationCore", "AGVLogic\AGVNavigationCore\AGVNavigationCore.csproj", "{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVMapEditor", "AGVLogic\AGVMapEditor\AGVMapEditor.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AGVSimulator", "AGVLogic\AGVSimulator\AGVSimulator.csproj", "{B2C3D4E5-0000-0000-0000-000000000000}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test_Port", "TestProject\Test_Port\Test_Port.csproj", "{CCFA2CE7-A539-4ADC-B803-F759284C3463}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -174,6 +176,18 @@ Global
{D6B3880D-7D5C-44E2-B6A5-CF6D881A8A38}.Release|x64.Build.0 = Release|Any CPU
{D6B3880D-7D5C-44E2-B6A5-CF6D881A8A38}.Release|x86.ActiveCfg = Release|x86
{D6B3880D-7D5C-44E2-B6A5-CF6D881A8A38}.Release|x86.Build.0 = Release|x86
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Debug|x64.ActiveCfg = Debug|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Debug|x64.Build.0 = Debug|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Debug|x86.ActiveCfg = Debug|x86
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Debug|x86.Build.0 = Debug|x86
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Release|Any CPU.Build.0 = Release|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Release|x64.ActiveCfg = Release|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Release|x64.Build.0 = Release|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Release|x86.ActiveCfg = Release|x86
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Release|x86.Build.0 = Release|x86
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -198,18 +212,18 @@ Global
{B2C3D4E5-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|Any CPU
{B2C3D4E5-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|Any CPU
{B2C3D4E5-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Debug|x64.ActiveCfg = Debug|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Debug|x64.Build.0 = Debug|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Debug|x86.ActiveCfg = Debug|x86
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Debug|x86.Build.0 = Debug|x86
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Release|Any CPU.Build.0 = Release|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Release|x64.ActiveCfg = Release|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Release|x64.Build.0 = Release|Any CPU
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Release|x86.ActiveCfg = Release|x86
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C}.Release|x86.Build.0 = Release|x86
{CCFA2CE7-A539-4ADC-B803-F759284C3463}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CCFA2CE7-A539-4ADC-B803-F759284C3463}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CCFA2CE7-A539-4ADC-B803-F759284C3463}.Debug|x64.ActiveCfg = Debug|Any CPU
{CCFA2CE7-A539-4ADC-B803-F759284C3463}.Debug|x64.Build.0 = Debug|Any CPU
{CCFA2CE7-A539-4ADC-B803-F759284C3463}.Debug|x86.ActiveCfg = Debug|Any CPU
{CCFA2CE7-A539-4ADC-B803-F759284C3463}.Debug|x86.Build.0 = Debug|Any CPU
{CCFA2CE7-A539-4ADC-B803-F759284C3463}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CCFA2CE7-A539-4ADC-B803-F759284C3463}.Release|Any CPU.Build.0 = Release|Any CPU
{CCFA2CE7-A539-4ADC-B803-F759284C3463}.Release|x64.ActiveCfg = Release|Any CPU
{CCFA2CE7-A539-4ADC-B803-F759284C3463}.Release|x64.Build.0 = Release|Any CPU
{CCFA2CE7-A539-4ADC-B803-F759284C3463}.Release|x86.ActiveCfg = Release|Any CPU
{CCFA2CE7-A539-4ADC-B803-F759284C3463}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -222,9 +236,10 @@ Global
{14E8C9A5-013E-49BA-B435-EFEFC77DD623} = {C423C39A-44E7-4F09-B2F7-7943975FF948}
{EB77976F-4DE4-46A5-8B25-D07226204C32} = {7AF32085-E7A6-4D06-BA6E-C6B1EBAEA99A}
{9365803B-933D-4237-93C7-B502C855A71C} = {C423C39A-44E7-4F09-B2F7-7943975FF948}
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C} = {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}
{C5F7A8B2-8D3E-4A1B-9C6E-7F4D5E2A9B1C} = {E5C75D32-5AD6-44DD-8F27-E32023206EBB}
{CCFA2CE7-A539-4ADC-B803-F759284C3463} = {7AF32085-E7A6-4D06-BA6E-C6B1EBAEA99A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B5B1FD72-356F-4840-83E8-B070AC21C8D9}

View File

@@ -17,7 +17,7 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<OutputPath>..\..\..\..\..\..\Amkor\AGV4\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>

View File

@@ -111,6 +111,7 @@ namespace AGVMapEditor.Forms
// 이벤트 연결
_mapCanvas.NodeAdded += OnNodeAdded;
_mapCanvas.NodeSelected += OnNodeSelected;
_mapCanvas.NodesSelected += OnNodesSelected; // 다중 선택 이벤트
_mapCanvas.NodeMoved += OnNodeMoved;
_mapCanvas.NodeDeleted += OnNodeDeleted;
_mapCanvas.ConnectionDeleted += OnConnectionDeleted;
@@ -184,9 +185,47 @@ namespace AGVMapEditor.Forms
private void OnNodeSelected(object sender, MapNode node)
{
_selectedNode = node;
if (node == null)
{
// 빈 공간 클릭 시 캔버스 속성 표시
ShowCanvasProperties();
}
else
{
// 노드 클릭 시 노드 속성 표시
UpdateNodeProperties();
UpdateImageEditButton(); // 이미지 노드 선택 시 이미지 편집 버튼 활성화
}
}
private void OnNodesSelected(object sender, List<MapNode> nodes)
{
// 다중 선택 시 처리
if (nodes == null || nodes.Count == 0)
{
ShowCanvasProperties();
return;
}
if (nodes.Count == 1)
{
// 단일 선택은 기존 방식 사용
_selectedNode = nodes[0];
UpdateNodeProperties();
UpdateImageEditButton();
}
else
{
// 다중 선택: 상태바에 선택 개수 표시
toolStripStatusLabel1.Text = $"{nodes.Count}개 노드 선택됨 - PropertyGrid에서 공통 속성 일괄 변경 가능";
// 다중 선택 PropertyWrapper 표시
var multiWrapper = new MultiNodePropertyWrapper(nodes);
_propertyGrid.SelectedObject = multiWrapper;
_propertyGrid.Focus();
}
}
private void OnNodeMoved(object sender, MapNode node)
{
@@ -589,6 +628,13 @@ namespace AGVMapEditor.Forms
_mapCanvas.Nodes = _mapNodes;
// RfidMappings 제거됨 - MapNode에 통합
// 🔥 맵 설정 적용 (배경색, 그리드 표시)
if (result.Settings != null)
{
_mapCanvas.BackColor = System.Drawing.Color.FromArgb(result.Settings.BackgroundColorArgb);
_mapCanvas.ShowGrid = result.Settings.ShowGrid;
}
// 현재 파일 경로 업데이트
_currentMapFile = filePath;
_hasChanges = false;
@@ -614,7 +660,38 @@ namespace AGVMapEditor.Forms
private void SaveMapToFile(string filePath)
{
if (MapLoader.SaveMapToFile(filePath, _mapNodes))
// 🔥 백업 파일 생성 (기존 파일이 있을 경우)
if (File.Exists(filePath))
{
try
{
// 날짜시간 포함 백업 파일명 생성
var directory = Path.GetDirectoryName(filePath);
var fileNameWithoutExt = Path.GetFileNameWithoutExtension(filePath);
var extension = Path.GetExtension(filePath);
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
var backupFileName = $"{fileNameWithoutExt}_{timestamp}{extension}.bak";
var backupFilePath = Path.Combine(directory, backupFileName);
// 기존 파일을 백업 파일로 복사
File.Copy(filePath, backupFilePath, true);
}
catch (Exception ex)
{
// 백업 파일 생성 실패 시 경고만 표시하고 계속 진행
MessageBox.Show($"백업 파일 생성 실패: {ex.Message}\n원본 파일은 계속 저장됩니다.", "백업 경고",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
// 🔥 현재 캔버스 설정을 맵 파일에 저장
var settings = new MapLoader.MapSettings
{
BackgroundColorArgb = _mapCanvas.BackColor.ToArgb(),
ShowGrid = _mapCanvas.ShowGrid
};
if (MapLoader.SaveMapToFile(filePath, _mapNodes, settings))
{
// 현재 파일 경로 업데이트
_currentMapFile = filePath;
@@ -892,7 +969,7 @@ namespace AGVMapEditor.Forms
{
if (_selectedNode == null)
{
ClearNodeProperties();
ShowCanvasProperties();
return;
}
@@ -905,6 +982,16 @@ namespace AGVMapEditor.Forms
UpdateImageEditButton();
}
/// <summary>
/// 캔버스 속성 표시 (배경색 등)
/// </summary>
private void ShowCanvasProperties()
{
var canvasWrapper = new CanvasPropertyWrapper(_mapCanvas);
_propertyGrid.SelectedObject = canvasWrapper;
DisableImageEditButton();
}
private void ClearNodeProperties()
{
_propertyGrid.SelectedObject = null;
@@ -1010,6 +1097,9 @@ namespace AGVMapEditor.Forms
private void PropertyGrid_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
{
// 변경된 속성명 디버그 출력
System.Diagnostics.Debug.WriteLine($"[PropertyGrid] 속성 변경됨: {e.ChangedItem.PropertyDescriptor.Name}");
// RFID 값 변경시 중복 검사
if (e.ChangedItem.PropertyDescriptor.Name == "RFID")
{
@@ -1031,14 +1121,39 @@ namespace AGVMapEditor.Forms
_hasChanges = true;
UpdateTitle();
// 현재 선택된 노드를 기억
// 🔥 다중 선택 여부 확인 및 선택된 노드들 저장
bool isMultiSelect = _propertyGrid.SelectedObject is MultiNodePropertyWrapper;
List<MapNode> selectedNodes = null;
if (isMultiSelect)
{
// 캔버스에서 현재 선택된 노드들 가져오기
selectedNodes = new List<MapNode>(_mapCanvas.SelectedNodes);
System.Diagnostics.Debug.WriteLine($"[PropertyGrid] 다중 선택 노드 수: {selectedNodes.Count}");
}
// 현재 선택된 노드를 기억 (단일 선택인 경우만)
var currentSelectedNode = _selectedNode;
RefreshNodeList();
RefreshMapCanvas();
// 선택된 노드를 다시 선택
if (currentSelectedNode != null)
// 🔥 캔버스 강제 갱신 (bool 타입 속성 변경 시 특히 필요)
_mapCanvas.Invalidate();
_mapCanvas.Update();
// 🔥 다중 선택인 경우 MultiNodePropertyWrapper를 다시 생성하여 바인딩
if (isMultiSelect && selectedNodes != null && selectedNodes.Count > 0)
{
System.Diagnostics.Debug.WriteLine($"[PropertyGrid] MultiNodePropertyWrapper 재생성: {selectedNodes.Count}개");
var multiWrapper = new MultiNodePropertyWrapper(selectedNodes);
_propertyGrid.SelectedObject = multiWrapper;
}
// PropertyGrid 새로고침
_propertyGrid.Refresh();
// 🔥 단일 선택인 경우에만 노드를 다시 선택 (다중 선택은 캔버스에서 유지)
if (!isMultiSelect && currentSelectedNode != null)
{
var nodeIndex = _mapNodes.IndexOf(currentSelectedNode);
if (nodeIndex >= 0)
@@ -1162,5 +1277,96 @@ 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
}
}

View File

@@ -4,6 +4,7 @@ using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using AGVNavigationCore.Models;
using AGVNavigationCore.Controls;
namespace AGVMapEditor.Models
{
@@ -521,6 +522,84 @@ namespace AGVMapEditor.Models
}
}
[Category("표시")]
[DisplayName("노드 배경색")]
[Description("노드 배경 색상")]
public Color DisplayColor
{
get => _node.DisplayColor;
set
{
_node.DisplayColor = value;
_node.ModifiedDate = DateTime.Now;
}
}
[Category("표시")]
[DisplayName("글자 색상")]
[Description("노드 텍스트 색상 (NodeId, Name 등)")]
public Color ForeColor
{
get => _node.ForeColor;
set
{
_node.ForeColor = value;
_node.ModifiedDate = DateTime.Now;
}
}
[Category("표시")]
[DisplayName("글자 크기")]
[Description("노드 텍스트 크기 (픽셀)")]
public float TextFontSize
{
get => _node.TextFontSize;
set
{
_node.TextFontSize = Math.Max(5.0f, Math.Min(20.0f, value));
_node.ModifiedDate = DateTime.Now;
}
}
[Category("표시")]
[DisplayName("글자 굵게")]
[Description("노드 텍스트 볼드 표시")]
public bool TextFontBold
{
get => _node.TextFontBold;
set
{
_node.TextFontBold = value;
_node.ModifiedDate = DateTime.Now;
}
}
[Category("표시")]
[DisplayName("이름 말풍선 배경색")]
[Description("노드 이름 말풍선(하단 표시) 배경색")]
public Color NameBubbleBackColor
{
get => _node.NameBubbleBackColor;
set
{
_node.NameBubbleBackColor = value;
_node.ModifiedDate = DateTime.Now;
}
}
[Category("표시")]
[DisplayName("이름 말풍선 글자색")]
[Description("노드 이름 말풍선(하단 표시) 글자색")]
public Color NameBubbleForeColor
{
get => _node.NameBubbleForeColor;
set
{
_node.NameBubbleForeColor = value;
_node.ModifiedDate = DateTime.Now;
}
}
[Category("정보")]
[DisplayName("생성 일시")]
[Description("노드가 생성된 일시")]
@@ -540,5 +619,207 @@ namespace AGVMapEditor.Models
}
}
/// <summary>
/// 다중 노드 선택 시 공통 속성 편집용 래퍼
/// </summary>
public class MultiNodePropertyWrapper
{
private List<MapNode> _nodes;
public MultiNodePropertyWrapper(List<MapNode> nodes)
{
_nodes = nodes ?? new List<MapNode>();
}
[Category("다중 선택")]
[DisplayName("선택된 노드 수")]
[Description("현재 선택된 노드의 개수")]
[ReadOnly(true)]
public int SelectedCount => _nodes.Count;
[Category("표시")]
[DisplayName("노드 배경색")]
[Description("선택된 모든 노드의 배경색을 일괄 변경")]
public Color DisplayColor
{
get => _nodes.Count > 0 ? _nodes[0].DisplayColor : Color.Blue;
set
{
foreach (var node in _nodes)
{
node.DisplayColor = value;
node.ModifiedDate = DateTime.Now;
}
}
}
[Category("표시")]
[DisplayName("글자 색상")]
[Description("선택된 모든 노드의 글자 색상을 일괄 변경")]
public Color ForeColor
{
get => _nodes.Count > 0 ? _nodes[0].ForeColor : Color.Black;
set
{
foreach (var node in _nodes)
{
node.ForeColor = value;
node.ModifiedDate = DateTime.Now;
}
}
}
[Category("표시")]
[DisplayName("글자 크기")]
[Description("선택된 모든 노드의 글자 크기를 일괄 변경 (5~20 픽셀)")]
public float TextFontSize
{
get => _nodes.Count > 0 ? _nodes[0].TextFontSize : 7.0f;
set
{
var validValue = Math.Max(5.0f, Math.Min(20.0f, value));
System.Diagnostics.Debug.WriteLine($"[MultiNode] TextFontSize 변경 시작: {validValue}, 노드 수: {_nodes.Count}");
foreach (var node in _nodes)
{
System.Diagnostics.Debug.WriteLine($" - 노드 {node.NodeId}: {node.TextFontSize} → {validValue}");
node.TextFontSize = validValue;
node.ModifiedDate = DateTime.Now;
System.Diagnostics.Debug.WriteLine($" - 변경 후: {node.TextFontSize}");
}
System.Diagnostics.Debug.WriteLine($"[MultiNode] TextFontSize 변경 완료");
}
}
[Category("표시")]
[DisplayName("글자 굵게")]
[Description("선택된 모든 노드의 글자 굵기를 일괄 변경")]
public bool TextFontBold
{
get
{
var result = _nodes.Count > 0 ? _nodes[0].TextFontBold : true;
System.Diagnostics.Debug.WriteLine($"[MultiNode] TextFontBold GET: {result}");
return result;
}
set
{
System.Diagnostics.Debug.WriteLine($"[MultiNode] TextFontBold 변경 시작: {value}, 노드 수: {_nodes.Count}");
foreach (var node in _nodes)
{
System.Diagnostics.Debug.WriteLine($" - 노드 {node.NodeId}: {node.TextFontBold} → {value}");
node.TextFontBold = value;
node.ModifiedDate = DateTime.Now;
System.Diagnostics.Debug.WriteLine($" - 변경 후: {node.TextFontBold}");
}
System.Diagnostics.Debug.WriteLine($"[MultiNode] TextFontBold 변경 완료");
}
}
[Category("표시")]
[DisplayName("이름 말풍선 배경색")]
[Description("선택된 모든 노드의 이름 말풍선(하단 표시) 배경색을 일괄 변경")]
public Color NameBubbleBackColor
{
get => _nodes.Count > 0 ? _nodes[0].NameBubbleBackColor : Color.Gold;
set
{
foreach (var node in _nodes)
{
node.NameBubbleBackColor = value;
node.ModifiedDate = DateTime.Now;
}
}
}
[Category("표시")]
[DisplayName("이름 말풍선 글자색")]
[Description("선택된 모든 노드의 이름 말풍선(하단 표시) 글자색을 일괄 변경")]
public Color NameBubbleForeColor
{
get => _nodes.Count > 0 ? _nodes[0].NameBubbleForeColor : Color.Black;
set
{
foreach (var node in _nodes)
{
node.NameBubbleForeColor = value;
node.ModifiedDate = DateTime.Now;
}
}
}
[Category("고급")]
[DisplayName("활성화")]
[Description("선택된 모든 노드의 활성화 상태를 일괄 변경")]
public bool IsActive
{
get => _nodes.Count > 0 ? _nodes[0].IsActive : true;
set
{
foreach (var node in _nodes)
{
node.IsActive = value;
node.ModifiedDate = DateTime.Now;
}
}
}
[Category("정보")]
[DisplayName("안내")]
[Description("다중 선택 시 공통 속성만 변경할 수 있습니다")]
[ReadOnly(true)]
public string HelpText => "위 속성을 변경하면 선택된 모든 노드에 일괄 적용됩니다.";
}
/// <summary>
/// 캔버스 속성 래퍼 (배경색 등 캔버스 전체 속성 편집)
/// </summary>
public class CanvasPropertyWrapper
{
private UnifiedAGVCanvas _canvas;
public CanvasPropertyWrapper(UnifiedAGVCanvas canvas)
{
_canvas = canvas;
}
[Category("배경")]
[DisplayName("배경색")]
[Description("맵 캔버스 배경색")]
public Color BackgroundColor
{
get => _canvas.BackColor;
set
{
_canvas.BackColor = value;
_canvas.Invalidate();
}
}
[Category("그리드")]
[DisplayName("그리드 표시")]
[Description("그리드 표시 여부")]
public bool ShowGrid
{
get => _canvas.ShowGrid;
set
{
_canvas.ShowGrid = value;
_canvas.Invalidate();
}
}
[Category("정보")]
[DisplayName("설명")]
[Description("맵 캔버스 전체 속성")]
[ReadOnly(true)]
public string Description
{
get => "빈 공간을 클릭하면 캔버스 전체 속성을 편집할 수 있습니다.";
}
}
}

View File

@@ -26,6 +26,10 @@ namespace AGVNavigationCore.Controls
}
var g = e.Graphics;
// 🔥 배경색 그리기 (변환 행렬 적용 전에 전체 화면을 배경색으로 채움)
g.Clear(this.BackColor);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.High;
@@ -77,6 +81,7 @@ namespace AGVNavigationCore.Controls
}
// UI 정보 그리기 (변환 없이)
if (_showGrid)
DrawUIInfo(g);
}
@@ -689,8 +694,8 @@ namespace AGVNavigationCore.Controls
var pulseRect = new Rectangle(rect.X - 4, rect.Y - 4, rect.Width + 8, rect.Height + 8);
g.DrawEllipse(new Pen(Color.FromArgb(150, 0, 255, 255), 2) { DashStyle = DashStyle.Dash }, pulseRect);
}
// 선택된 노드 강조
else if (node == _selectedNode)
// 선택된 노드 강조 (단일 또는 다중)
else if (node == _selectedNode || (_selectedNodes != null && _selectedNodes.Contains(node)))
{
g.DrawEllipse(_selectedNodePen, rect);
}
@@ -938,19 +943,22 @@ namespace AGVNavigationCore.Controls
// 아래쪽에 표시할 값 (RFID 우선, 없으면 노드ID)
if (node.HasRfid())
{
// RFID가 있는 경우: 순수 RFID 값만 표시 (진한 색상)
// RFID가 있는 경우: 순수 RFID 값만 표시 (노드 전경색 사용)
displayText = node.RfidId;
textColor = Color.Black;
textColor = node.ForeColor;
}
else
{
// RFID가 없는 경우: 노드 ID 표시 (연한 색상)
// RFID가 없는 경우: 노드 ID 표시 (노드 전경색의 50% 투명도)
displayText = node.NodeId;
textColor = Color.Gray;
textColor = Color.FromArgb(128, node.ForeColor);
}
var font = new Font("Arial", 7, FontStyle.Bold);
var descFont = new Font("Arial", 8, FontStyle.Bold);
// 🔥 노드의 폰트 설정 사용 (0 이하일 경우 기본값 7.0f 사용)
var fontStyle = node.TextFontBold ? FontStyle.Bold : FontStyle.Regular;
var fontSize = node.TextFontSize > 0 ? node.TextFontSize : 7.0f;
var font = new Font("Arial", fontSize, fontStyle);
var descFont = new Font("Arial", fontSize + 1, fontStyle);
// 메인 텍스트 크기 측정
var textSize = g.MeasureString(displayText, font);
@@ -971,12 +979,8 @@ namespace AGVNavigationCore.Controls
// 설명 텍스트 그리기 (설명이 있는 경우에만)
if (!string.IsNullOrEmpty(descriptionText))
{
// 노드 이름 입력 여부에 따라 색상 구분
// 입력된 경우: 진한 색상 (잘 보이게)
// 기본값인 경우: 흐린 색상 (현재처럼)
Color descColor = string.IsNullOrEmpty(node.Name)
? Color.FromArgb(120, Color.Black) // 입력 안됨: 흐린 색상
: Color.FromArgb(200, Color.Black); // 입력됨: 진한 색상
// 🔥 노드의 말풍선 글자색 사용 (NameBubbleForeColor)
Color descColor = node.NameBubbleForeColor;
var rectpaddingx = 4;
var rectpaddingy = 2;
@@ -985,10 +989,10 @@ namespace AGVNavigationCore.Controls
(int)descSize.Width + rectpaddingx * 2,
(int)descSize.Height + rectpaddingy * 2);
// 라운드 사각형 그리기 (빨간 배경)
using (var backgroundBrush = new SolidBrush(Color.Gold))
// 라운드 사각형 그리기 (노드 이름 말풍선 배경색 사용)
using (var backgroundBrush = new SolidBrush(node.NameBubbleBackColor))
{
DrawRoundedRectangle(g, backgroundBrush, roundRect, 3); // 모서리 반지름 6px
DrawRoundedRectangle(g, backgroundBrush, roundRect, 3); // 모서리 반지름 3px
}
// 라운드 사각형 테두리 그리기 (진한 빨간색)
@@ -1287,26 +1291,19 @@ namespace AGVNavigationCore.Controls
private Brush GetNodeBrush(MapNode node)
{
// RFID가 없는 노드는 회색 계통으로 표시
// 🔥 노드의 DisplayColor를 배경색으로 사용
// RFID가 없는 노드는 DisplayColor를 50% 투명도로 표시
bool hasRfid = node.HasRfid();
switch (node.Type)
Color bgColor = node.DisplayColor;
// RFID가 없는 경우 투명도 50%
if (!hasRfid)
{
case NodeType.Normal:
return hasRfid ? _normalNodeBrush : new SolidBrush(Color.LightGray);
case NodeType.Rotation:
return hasRfid ? _rotationNodeBrush : new SolidBrush(Color.DarkGray);
case NodeType.Docking:
return hasRfid ? _dockingNodeBrush : new SolidBrush(Color.Gray);
case NodeType.Charging:
return hasRfid ? _chargingNodeBrush : new SolidBrush(Color.Silver);
case NodeType.Label:
return new SolidBrush(Color.Purple);
case NodeType.Image:
return new SolidBrush(Color.Brown);
default:
return hasRfid ? _normalNodeBrush : new SolidBrush(Color.LightGray);
bgColor = Color.FromArgb(128, bgColor);
}
return new SolidBrush(bgColor);
}
private void DrawAGVs(Graphics g)

View File

@@ -17,6 +17,54 @@ namespace AGVNavigationCore.Controls
var worldPoint = ScreenToWorld(e.Location);
var hitNode = GetNodeAt(worldPoint);
// 🔥 어떤 모드에서든 노드/빈 공간 클릭 시 선택 이벤트 발생 (속성창 업데이트)
bool ctrlPressed = (ModifierKeys & Keys.Control) == Keys.Control;
if (hitNode != null)
{
// 노드 클릭
if (ctrlPressed && _editMode == EditMode.Select)
{
// Ctrl+클릭: 다중 선택 토글
if (_selectedNodes.Contains(hitNode))
{
_selectedNodes.Remove(hitNode);
}
else
{
_selectedNodes.Add(hitNode);
}
// 마지막 선택된 노드 업데이트 (단일 참조용)
_selectedNode = _selectedNodes.Count > 0 ? _selectedNodes[_selectedNodes.Count - 1] : null;
// 다중 선택 이벤트만 발생 (OnNodesSelected에서 단일/다중 구분 처리)
NodesSelected?.Invoke(this, _selectedNodes);
Invalidate();
}
else
{
// 일반 클릭: 단일 선택
_selectedNode = hitNode;
_selectedNodes.Clear();
_selectedNodes.Add(hitNode);
// NodesSelected 이벤트만 발생 (OnNodesSelected에서 단일/다중 구분 처리)
NodesSelected?.Invoke(this, _selectedNodes);
Invalidate();
}
}
else if (_editMode == EditMode.Select)
{
// 빈 공간 클릭 (Select 모드에서만) - 선택 해제
_selectedNode = null;
_selectedNodes.Clear();
// NodesSelected 이벤트만 발생 (OnNodesSelected에서 빈 리스트 처리)
NodesSelected?.Invoke(this, _selectedNodes);
Invalidate();
}
switch (_editMode)
{
case EditMode.Select:
@@ -76,7 +124,10 @@ namespace AGVNavigationCore.Controls
default:
// 기본 동작: 노드 선택 이벤트 발생
NodeSelected?.Invoke(this, hitNode);
_selectedNode = hitNode;
_selectedNodes.Clear();
_selectedNodes.Add(hitNode);
NodesSelected?.Invoke(this, _selectedNodes);
break;
}
}
@@ -98,8 +149,11 @@ namespace AGVNavigationCore.Controls
Invalidate();
}
// 노드 선택 이벤트도 발생 (속성창 업데이트)
NodeSelected?.Invoke(this, node);
// 더블클릭 시 해당 노드 선택 (다중 선택 해제)
_selectedNode = node;
_selectedNodes.Clear();
_selectedNodes.Add(node);
NodesSelected?.Invoke(this, _selectedNodes);
}
private void HandleLabelNodeDoubleClick(MapNode node)
@@ -118,14 +172,20 @@ namespace AGVNavigationCore.Controls
Invalidate();
}
// 노드 선택 이벤트도 발생 (속성창 업데이트)
NodeSelected?.Invoke(this, node);
// 더블클릭 시 해당 노드 선택 (다중 선택 해제)
_selectedNode = node;
_selectedNodes.Clear();
_selectedNodes.Add(node);
NodesSelected?.Invoke(this, _selectedNodes);
}
private void HandleImageNodeDoubleClick(MapNode node)
{
// 이미지 노드 선택 이벤트만 발생 (MainForm에서 이미지 편집 버튼 활성화됨)
NodeSelected?.Invoke(this, node);
// 더블클릭 시 해당 노드 선택 (다중 선택 해제)
_selectedNode = node;
_selectedNodes.Clear();
_selectedNodes.Add(node);
NodesSelected?.Invoke(this, _selectedNodes);
// 이미지 편집 이벤트 발생 (MainForm에서 처리)
ImageNodeDoubleClicked?.Invoke(this, node);
@@ -519,13 +579,8 @@ namespace AGVNavigationCore.Controls
{
if (hitNode != null)
{
// 노드 선택
if (hitNode != _selectedNode)
{
_selectedNode = hitNode;
NodeSelected?.Invoke(this, hitNode);
Invalidate();
}
// 노드 선택은 위쪽 MouseClick에서 이미 처리됨 (NodesSelected 이벤트 발생)
// 여기서는 추가 처리 없음
}
else
{
@@ -565,10 +620,11 @@ namespace AGVNavigationCore.Controls
else
{
// 빈 공간 클릭 시 선택 해제
if (_selectedNode != null)
if (_selectedNode != null || _selectedNodes.Count > 0)
{
_selectedNode = null;
NodeSelected?.Invoke(this, null);
_selectedNodes.Clear();
NodesSelected?.Invoke(this, _selectedNodes);
Invalidate();
}
}
@@ -745,7 +801,13 @@ namespace AGVNavigationCore.Controls
if (hitNode != null)
{
_contextMenu.Items.Add("노드 속성...", null, (s, e) => NodeSelected?.Invoke(this, hitNode));
_contextMenu.Items.Add("노드 속성...", null, (s, e) =>
{
_selectedNode = hitNode;
_selectedNodes.Clear();
_selectedNodes.Add(hitNode);
NodesSelected?.Invoke(this, _selectedNodes);
});
_contextMenu.Items.Add("노드 삭제", null, (s, e) => HandleDeleteClick(hitNode));
_contextMenu.Items.Add("-");
}

View File

@@ -64,6 +64,7 @@ namespace AGVNavigationCore.Controls
// 맵 데이터
private List<MapNode> _nodes;
private MapNode _selectedNode;
private List<MapNode> _selectedNodes; // 다중 선택
private MapNode _hoveredNode;
private MapNode _destinationNode;
@@ -95,6 +96,11 @@ namespace AGVNavigationCore.Controls
private Point _connectionEndPoint;
private int _mouseMoveCounter = 0; // 디버그용: MouseMove 실행 횟수
// 영역 선택 관련
private bool _isAreaSelecting;
private Point _areaSelectStart;
private Point _areaSelectEnd;
// 그리드 및 줌 관련
private bool _showGrid = true;
private float _zoomFactor = 1.0f;
@@ -141,6 +147,7 @@ namespace AGVNavigationCore.Controls
// 맵 편집 이벤트
public event EventHandler<MapNode> NodeAdded;
public event EventHandler<MapNode> NodeSelected;
public event EventHandler<List<MapNode>> NodesSelected; // 다중 선택 이벤트
public event EventHandler<MapNode> NodeDeleted;
public event EventHandler<MapNode> NodeMoved;
public event EventHandler<(MapNode From, MapNode To)> ConnectionDeleted;
@@ -212,10 +219,15 @@ namespace AGVNavigationCore.Controls
}
/// <summary>
/// 선택된 노드
/// 선택된 노드 (단일)
/// </summary>
public MapNode SelectedNode => _selectedNode;
/// <summary>
/// 선택된 노드들 (다중)
/// </summary>
public List<MapNode> SelectedNodes => _selectedNodes ?? new List<MapNode>();
/// <summary>
/// 노드 목록
/// </summary>
@@ -365,6 +377,7 @@ namespace AGVNavigationCore.Controls
ControlStyles.ResizeRedraw, true);
_nodes = new List<MapNode>();
_selectedNodes = new List<MapNode>(); // 다중 선택 리스트 초기화
_agvList = new List<IAGV>();
_agvPositions = new Dictionary<string, Point>();
_agvDirections = new Dictionary<string, AgvDirection>();

View File

@@ -12,6 +12,15 @@ namespace AGVNavigationCore.Models
/// </summary>
public static class MapLoader
{
/// <summary>
/// 맵 설정 정보 (배경색, 그리드 표시 등)
/// </summary>
public class MapSettings
{
public int BackgroundColorArgb { get; set; } = System.Drawing.Color.White.ToArgb();
public bool ShowGrid { get; set; } = true;
}
/// <summary>
/// 맵 파일 로딩 결과
/// </summary>
@@ -19,6 +28,7 @@ namespace AGVNavigationCore.Models
{
public bool Success { get; set; }
public List<MapNode> Nodes { get; set; } = new List<MapNode>();
public MapSettings Settings { get; set; } = new MapSettings();
public string ErrorMessage { get; set; } = string.Empty;
public string Version { get; set; } = string.Empty;
public DateTime CreatedDate { get; set; }
@@ -30,8 +40,9 @@ namespace AGVNavigationCore.Models
public class MapFileData
{
public List<MapNode> Nodes { get; set; } = new List<MapNode>();
public MapSettings Settings { get; set; } = new MapSettings();
public DateTime CreatedDate { get; set; }
public string Version { get; set; } = "1.0";
public string Version { get; set; } = "1.1"; // 버전 업그레이드 (설정 추가)
}
/// <summary>
@@ -66,6 +77,7 @@ namespace AGVNavigationCore.Models
if (mapData != null)
{
result.Nodes = mapData.Nodes ?? new List<MapNode>();
result.Settings = mapData.Settings ?? new MapSettings(); // 설정 로드
result.Version = mapData.Version ?? "1.0";
result.CreatedDate = mapData.CreatedDate;
@@ -111,8 +123,9 @@ namespace AGVNavigationCore.Models
/// </summary>
/// <param name="filePath">저장할 파일 경로</param>
/// <param name="nodes">맵 노드 목록</param>
/// <param name="settings">맵 설정 (배경색, 그리드 표시 등)</param>
/// <returns>저장 성공 여부</returns>
public static bool SaveMapToFile(string filePath, List<MapNode> nodes)
public static bool SaveMapToFile(string filePath, List<MapNode> nodes, MapSettings settings = null)
{
try
{
@@ -122,8 +135,9 @@ namespace AGVNavigationCore.Models
var mapData = new MapFileData
{
Nodes = nodes,
Settings = settings ?? new MapSettings(), // 설정 저장
CreatedDate = DateTime.Now,
Version = "1.0"
Version = "1.1"
};
var json = JsonConvert.SerializeObject(mapData, Formatting.Indented);

View File

@@ -124,7 +124,7 @@ namespace AGVNavigationCore.Models
public FontStyle FontStyle { get; set; } = FontStyle.Regular;
/// <summary>
/// 라벨 전경색 (NodeType.Label인 경우 사용)
/// 텍스트 전경색 (모든 노드 타입에서 사용)
/// </summary>
public Color ForeColor { get; set; } = Color.Black;
@@ -133,6 +133,33 @@ namespace AGVNavigationCore.Models
/// </summary>
public Color BackColor { get; set; } = Color.Transparent;
private float _textFontSize = 7.0f;
/// <summary>
/// 텍스트 폰트 크기 (모든 노드 타입의 텍스트 표시에 사용, 픽셀 단위)
/// 0 이하의 값이 설정되면 기본값 7.0f로 자동 설정
/// </summary>
public float TextFontSize
{
get => _textFontSize;
set => _textFontSize = value > 0 ? value : 7.0f;
}
/// <summary>
/// 텍스트 볼드체 여부 (모든 노드 타입의 텍스트 표시에 사용)
/// </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>
@@ -347,6 +374,10 @@ namespace AGVNavigationCore.Models
FontStyle = FontStyle,
ForeColor = ForeColor,
BackColor = BackColor,
TextFontSize = TextFontSize,
TextFontBold = TextFontBold,
NameBubbleBackColor = NameBubbleBackColor,
NameBubbleForeColor = NameBubbleForeColor,
ShowBackground = ShowBackground,
Padding = Padding,
ImagePath = ImagePath,

View File

@@ -306,15 +306,15 @@ namespace AGVNavigationCore.Models
}
// 5. 방향체크
if(CurrentDirection != TargetNode.MotorDirection)
{
return new AGVCommand(
MotorCommand.Stop,
MagnetPosition.S,
SpeedLevel.L,
$"(재탐색요청)모터방향 불일치 현재위치:{_currentNode.NodeId}"
);
}
//if(CurrentDirection != TargetNode.MotorDirection)
//{
// return new AGVCommand(
// MotorCommand.Stop,
// MagnetPosition.S,
// SpeedLevel.L,
// $"(재탐색요청)모터방향 불일치 현재위치:{_currentNode.NodeId}"
// );
//}
//this.CurrentNodeId

View File

@@ -17,7 +17,7 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<OutputPath>..\..\..\..\..\..\Amkor\AGV4\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>

View File

@@ -51,6 +51,8 @@ namespace AGVSimulator.Forms
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.openMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.reloadMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
this.launchMapEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator();
@@ -65,7 +67,6 @@ namespace AGVSimulator.Forms
this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this._toolStrip = new System.Windows.Forms.ToolStrip();
this.openMapToolStripButton = new System.Windows.Forms.ToolStripButton();
this.reloadMapToolStripButton = new System.Windows.Forms.ToolStripButton();
this.launchMapEditorToolStripButton = new System.Windows.Forms.ToolStripButton();
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
@@ -79,6 +80,7 @@ namespace AGVSimulator.Forms
this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator();
this.toolStripButton1 = new System.Windows.Forms.ToolStripButton();
this.btPredict = new System.Windows.Forms.ToolStripButton();
this.btMakeMap = new System.Windows.Forms.ToolStripButton();
this._statusStrip = new System.Windows.Forms.StatusStrip();
this._statusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this._coordLabel = new System.Windows.Forms.ToolStripStatusLabel();
@@ -109,12 +111,12 @@ namespace AGVSimulator.Forms
this._addAgvButton = new System.Windows.Forms.Button();
this._agvListCombo = new System.Windows.Forms.ComboBox();
this._canvasPanel = new System.Windows.Forms.Panel();
this.lbPredict = new System.Windows.Forms.RichTextBox();
this._agvInfoPanel = new System.Windows.Forms.Panel();
this._pathDebugLabel = new System.Windows.Forms.TextBox();
this._agvInfoTitleLabel = new System.Windows.Forms.Label();
this._liftDirectionLabel = new System.Windows.Forms.Label();
this._motorDirectionLabel = new System.Windows.Forms.Label();
this.lbPredict = new System.Windows.Forms.RichTextBox();
this.timer1 = new System.Windows.Forms.Timer(this.components);
this._menuStrip.SuspendLayout();
this._toolStrip.SuspendLayout();
@@ -145,6 +147,8 @@ namespace AGVSimulator.Forms
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.openMapToolStripMenuItem,
this.reloadMapToolStripMenuItem,
this.SToolStripMenuItem,
this.ToolStripMenuItem,
this.toolStripSeparator1,
this.launchMapEditorToolStripMenuItem,
this.toolStripSeparator4,
@@ -169,6 +173,20 @@ namespace AGVSimulator.Forms
this.reloadMapToolStripMenuItem.Text = "맵 다시열기(&R)";
this.reloadMapToolStripMenuItem.Click += new System.EventHandler(this.OnReloadMap_Click);
//
// 맵저장SToolStripMenuItem
//
this.SToolStripMenuItem.Name = "맵저장SToolStripMenuItem";
this.SToolStripMenuItem.Size = new System.Drawing.Size(221, 22);
this.SToolStripMenuItem.Text = "맵 저장(&S)";
this.SToolStripMenuItem.Click += new System.EventHandler(this.SToolStripMenuItem_Click);
//
// 맵다른이름으로저장ToolStripMenuItem
//
this.ToolStripMenuItem.Name = "맵다른이름으로저장ToolStripMenuItem";
this.ToolStripMenuItem.Size = new System.Drawing.Size(221, 22);
this.ToolStripMenuItem.Text = "맵 다른 이름으로 저장";
this.ToolStripMenuItem.Click += new System.EventHandler(this.btMapSaveAs_Click);
//
// toolStripSeparator1
//
this.toolStripSeparator1.Name = "toolStripSeparator1";
@@ -272,7 +290,6 @@ namespace AGVSimulator.Forms
// _toolStrip
//
this._toolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.openMapToolStripButton,
this.reloadMapToolStripButton,
this.launchMapEditorToolStripButton,
this.toolStripSeparator2,
@@ -285,22 +302,14 @@ namespace AGVSimulator.Forms
this.resetZoomToolStripButton,
this.toolStripSeparator5,
this.toolStripButton1,
this.btPredict});
this.btPredict,
this.btMakeMap});
this._toolStrip.Location = new System.Drawing.Point(0, 24);
this._toolStrip.Name = "_toolStrip";
this._toolStrip.Size = new System.Drawing.Size(1034, 25);
this._toolStrip.TabIndex = 1;
this._toolStrip.Text = "toolStrip";
//
// openMapToolStripButton
//
this.openMapToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.openMapToolStripButton.Name = "openMapToolStripButton";
this.openMapToolStripButton.Size = new System.Drawing.Size(51, 22);
this.openMapToolStripButton.Text = "맵 열기";
this.openMapToolStripButton.ToolTipText = "맵 파일을 엽니다";
this.openMapToolStripButton.Click += new System.EventHandler(this.OnOpenMap_Click);
//
// reloadMapToolStripButton
//
this.reloadMapToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
@@ -406,6 +415,15 @@ namespace AGVSimulator.Forms
this.btPredict.Text = "다음 행동 예측";
this.btPredict.Click += new System.EventHandler(this.btPredict_Click);
//
// btMakeMap
//
this.btMakeMap.Image = ((System.Drawing.Image)(resources.GetObject("btMakeMap.Image")));
this.btMakeMap.ImageTransparentColor = System.Drawing.Color.Magenta;
this.btMakeMap.Name = "btMakeMap";
this.btMakeMap.Size = new System.Drawing.Size(63, 22);
this.btMakeMap.Text = "맵기록";
this.btMakeMap.Click += new System.EventHandler(this.btMakeMap_Click);
//
// _statusStrip
//
this._statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
@@ -701,6 +719,15 @@ namespace AGVSimulator.Forms
this._canvasPanel.Size = new System.Drawing.Size(801, 560);
this._canvasPanel.TabIndex = 4;
//
// lbPredict
//
this.lbPredict.Dock = System.Windows.Forms.DockStyle.Bottom;
this.lbPredict.Location = new System.Drawing.Point(0, 513);
this.lbPredict.Name = "lbPredict";
this.lbPredict.Size = new System.Drawing.Size(801, 47);
this.lbPredict.TabIndex = 0;
this.lbPredict.Text = "";
//
// _agvInfoPanel
//
this._agvInfoPanel.BackColor = System.Drawing.Color.LightBlue;
@@ -757,15 +784,6 @@ namespace AGVSimulator.Forms
this._motorDirectionLabel.TabIndex = 2;
this._motorDirectionLabel.Text = "모터 방향: -";
//
// lbPredict
//
this.lbPredict.Dock = System.Windows.Forms.DockStyle.Bottom;
this.lbPredict.Location = new System.Drawing.Point(0, 513);
this.lbPredict.Name = "lbPredict";
this.lbPredict.Size = new System.Drawing.Size(801, 47);
this.lbPredict.TabIndex = 0;
this.lbPredict.Text = "";
//
// timer1
//
this.timer1.Interval = 500;
@@ -825,7 +843,6 @@ namespace AGVSimulator.Forms
private System.Windows.Forms.ToolStripMenuItem helpToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem;
private System.Windows.Forms.ToolStrip _toolStrip;
private System.Windows.Forms.ToolStripButton openMapToolStripButton;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
private System.Windows.Forms.ToolStripButton startSimulationToolStripButton;
private System.Windows.Forms.ToolStripButton stopSimulationToolStripButton;
@@ -879,5 +896,8 @@ namespace AGVSimulator.Forms
private System.Windows.Forms.ToolStripButton btPredict;
private System.Windows.Forms.RichTextBox lbPredict;
private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.ToolStripButton btMakeMap;
private System.Windows.Forms.ToolStripMenuItem SToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItem;
}
}

View File

@@ -35,6 +35,13 @@ namespace AGVSimulator.Forms
private string _currentMapFilePath;
private bool _isTargetCalcMode; // 타겟계산 모드 상태
// 맵 스캔 모드 관련
private bool _isMapScanMode; // 맵 스캔 모드 상태
private DateTime _lastNodeAddTime; // 마지막 노드 추가 시간
private MapNode _lastScannedNode; // 마지막으로 스캔된 노드
private int _scanNodeCounter; // 스캔 노드 카운터
private AgvDirection _lastScanDirection; // 마지막 스캔 방향
// UI Controls - Designer에서 생성됨
#endregion
@@ -239,6 +246,7 @@ namespace AGVSimulator.Forms
UpdateUI();
_statusLabel.Text = $"{agvId} 추가됨";
_simulatorCanvas.FitToNodes();
}
private void OnRemoveAGV_Click(object sender, EventArgs e)
@@ -508,6 +516,165 @@ namespace AGVSimulator.Forms
return closestNode;
}
/// <summary>
/// 방향을 기호로 변환
/// </summary>
private string GetDirectionSymbol(AgvDirection direction)
{
switch (direction)
{
case AgvDirection.Forward: return "→";
case AgvDirection.Backward: return "←";
case AgvDirection.Left: return "↺";
case AgvDirection.Right: return "↻";
default: return "-";
}
}
/// <summary>
/// 맵 스캔 모드에서 RFID로부터 노드 생성
/// </summary>
private void CreateNodeFromRfidScan(string rfidId, VirtualAGV selectedAGV)
{
try
{
// 현재 선택된 방향 확인 (최상단에서 먼저 확인)
var directionItem = _directionCombo.SelectedItem as DirectionItem;
var currentDirection = directionItem?.Direction ?? AgvDirection.Forward;
// 중복 RFID 확인
var existingNode = _mapNodes?.FirstOrDefault(n => n.RfidId.Equals(rfidId, StringComparison.OrdinalIgnoreCase));
if (existingNode != null)
{
// 이미 존재하는 노드로 이동
Program.WriteLine($"[맵 스캔] RFID '{rfidId}'는 이미 존재합니다 (노드: {existingNode.NodeId})");
// 기존 노드로 AGV 위치 설정
_simulatorCanvas.SetAGVPosition(selectedAGV.AgvId, existingNode, currentDirection);
selectedAGV.SetPosition(existingNode, currentDirection);
_lastScannedNode = existingNode;
_lastNodeAddTime = DateTime.Now;
_lastScanDirection = currentDirection; // 방향 업데이트
_statusLabel.Text = $"기존 노드로 이동: {existingNode.NodeId} [{GetDirectionSymbol(currentDirection)}]";
_rfidTextBox.Text = "";
return;
}
// 새 노드 생성 위치 계산
int newX = 100; // 기본 시작 X 위치
int newY = 300; // 기본 시작 Y 위치
if (_lastScannedNode != null)
{
// 시간차 기반 X축 거리 계산
var timeDiff = (DateTime.Now - _lastNodeAddTime).TotalSeconds;
// 10초당 10px, 최소 50px, 최대 100px
int distanceX = Math.Max(50, Math.Min(100, (int)(timeDiff * 10)));
// 방향 전환 확인
bool directionChanged = (_lastScanDirection != currentDirection);
if (directionChanged)
{
// 방향이 바뀌면 Y축을 50px 증가시켜서 겹치지 않게 함
newY = _lastScannedNode.Position.Y + 50;
newX = _lastScannedNode.Position.X; // X는 같은 위치에서 시작
Program.WriteLine($"[맵 스캔] 방향 전환: {_lastScanDirection} → {currentDirection}, Y축 +50px");
}
else
{
// 방향이 같으면 Y축 유지
newY = _lastScannedNode.Position.Y;
// 모터 방향에 따라 X축 증가/감소
if (currentDirection == AgvDirection.Forward)
{
// 전진: X축 증가
newX = _lastScannedNode.Position.X + distanceX;
Program.WriteLine($"[맵 스캔] 전진 모드: X축 +{distanceX}px");
}
else if (currentDirection == AgvDirection.Backward)
{
// 후진: X축 감소
newX = _lastScannedNode.Position.X - distanceX;
Program.WriteLine($"[맵 스캔] 후진 모드: X축 -{distanceX}px");
}
else
{
// 그 외(회전 등): 기본적으로 전진 방향 사용
newX = _lastScannedNode.Position.X + distanceX;
Program.WriteLine($"[맵 스캔] 기타 방향({currentDirection}): X축 +{distanceX}px");
}
}
Program.WriteLine($"[맵 스캔] 시간차: {timeDiff:F1}초 → 거리: {distanceX}px");
}
// 새 노드 생성
var newNodeId = $"{_scanNodeCounter:D3}";
var newNode = new MapNode
{
NodeId = newNodeId,
RfidId = rfidId,
Position = new Point(newX, newY),
Type = NodeType.Normal,
IsActive = true,
Name = $"N{_scanNodeCounter}"
};
// 맵에 추가
if (_mapNodes == null)
_mapNodes = new List<MapNode>();
_mapNodes.Add(newNode);
// 이전 노드와 연결 생성
if (_lastScannedNode != null)
{
// 양방향 연결 (ConnectedNodes에 추가 - JSON 저장됨)
_lastScannedNode.AddConnection(newNode.NodeId);
newNode.AddConnection(_lastScannedNode.NodeId);
Program.WriteLine($"[맵 스캔] 연결 생성: {_lastScannedNode.NodeId} ↔ {newNode.NodeId}");
}
// AGV 위치 설정
_simulatorCanvas.SetAGVPosition(selectedAGV.AgvId, newNode, currentDirection);
selectedAGV.SetPosition(newNode, currentDirection);
// 캔버스 업데이트
_simulatorCanvas.Nodes = _mapNodes;
// 화면을 새 노드 위치로 이동
_simulatorCanvas.PanToNode(newNode.NodeId);
_simulatorCanvas.Invalidate();
// 상태 업데이트
_lastScannedNode = newNode;
_lastNodeAddTime = DateTime.Now;
_lastScanDirection = currentDirection; // 현재 방향 저장
_scanNodeCounter++;
// UI 업데이트
UpdateNodeComboBoxes();
_statusLabel.Text = $"노드 생성: {newNode.NodeId} (RFID: {rfidId}) [{GetDirectionSymbol(currentDirection)}] - 총 {_mapNodes.Count}개";
_rfidTextBox.Text = "";
Program.WriteLine($"[맵 스캔] 노드 생성 완료: {newNode.NodeId} (RFID: {rfidId}) at ({newX}, {newY}), 방향: {currentDirection}");
}
catch (Exception ex)
{
MessageBox.Show($"노드 생성 중 오류 발생:\n{ex.Message}", "오류",
MessageBoxButtons.OK, MessageBoxIcon.Error);
Program.WriteLine($"[맵 스캔 오류] {ex.Message}");
}
}
private void OnSetPosition_Click(object sender, EventArgs e)
{
SetAGVPositionByRfid();
@@ -562,6 +729,19 @@ namespace AGVSimulator.Forms
return;
}
// 선택된 방향 확인
var selectedDirectionItem = _directionCombo.SelectedItem as DirectionItem;
var selectedDirection = selectedDirectionItem?.Direction ?? AgvDirection.Forward;
// 맵 스캔 모드일 때: 노드 자동 생성
if (_isMapScanMode)
{
CreateNodeFromRfidScan(rfidId, selectedAGV);
this._simulatorCanvas.FitToNodes();
return;
}
// RFID에 해당하는 노드 직접 찾기
var targetNode = _mapNodes?.FirstOrDefault(n => n.RfidId.Equals(rfidId, StringComparison.OrdinalIgnoreCase));
if (targetNode == null)
@@ -571,9 +751,6 @@ namespace AGVSimulator.Forms
return;
}
// 선택된 방향 확인
var selectedDirectionItem = _directionCombo.SelectedItem as DirectionItem;
var selectedDirection = selectedDirectionItem?.Direction ?? AgvDirection.Forward;
//이전위치와 동일한지 체크한다.
if (selectedAGV.CurrentNodeId == targetNode.NodeId && selectedAGV.CurrentDirection == selectedDirection)
@@ -1625,6 +1802,184 @@ namespace AGVSimulator.Forms
var command = agv.Predict();
this.lbPredict.Text = $"Motor:{command.Motor},Magnet:{command.Magnet},Speed:{command.Speed} : {command.Reason}";
}
private void btMakeMap_Click(object sender, EventArgs e)
{
if (!_isMapScanMode)
{
// 스캔 모드 시작
var result = MessageBox.Show(
"맵 스캔 모드를 시작합니다.\n\n" +
"RFID를 입력하면 자동으로 맵 노드가 생성되고\n" +
"이전 노드와 연결됩니다.\n\n" +
"기존 맵 데이터를 삭제하고 시작하시겠습니까?\n\n" +
"예: 새 맵 시작\n" +
"아니오: 기존 맵에 추가",
"맵 스캔 모드",
MessageBoxButtons.YesNoCancel,
MessageBoxIcon.Question);
if (result == DialogResult.Cancel)
return;
if (result == DialogResult.Yes)
{
// 기존 맵 데이터 삭제
_mapNodes?.Clear();
_mapNodes = new List<MapNode>();
_simulatorCanvas.Nodes = _mapNodes;
_currentMapFilePath = string.Empty;
UpdateNodeComboBoxes();
_statusLabel.Text = "맵 초기화 완료 - 스캔 모드 시작";
}
// 스캔 모드 활성화
_isMapScanMode = true;
_lastNodeAddTime = DateTime.Now;
_lastScannedNode = null;
_scanNodeCounter = 1;
_lastScanDirection = AgvDirection.Forward; // 기본 방향은 전진
btMakeMap.Text = "스캔 중지";
btMakeMap.BackColor = Color.LightCoral;
_statusLabel.Text = "맵 스캔 모드: RFID를 입력하여 노드를 생성하세요";
Program.WriteLine("[맵 스캔] 스캔 모드 시작");
}
else
{
// 스캔 모드 종료
_isMapScanMode = false;
btMakeMap.Text = "맵 생성";
btMakeMap.BackColor = SystemColors.Control;
_statusLabel.Text = $"맵 스캔 완료 - {_mapNodes?.Count ?? 0}개 노드 생성됨";
Program.WriteLine($"[맵 스캔] 스캔 모드 종료 - 총 {_mapNodes?.Count ?? 0}개 노드");
// 맵 저장 권장
if (_mapNodes != null && _mapNodes.Count > 0)
{
var saveResult = MessageBox.Show(
$"맵 스캔이 완료되었습니다.\n\n" +
$"생성된 노드: {_mapNodes.Count}개\n\n" +
"맵을 저장하시겠습니까?",
"맵 저장",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (saveResult == DialogResult.Yes)
{
btMapSaveAs_Click(sender, e);
}
}
}
}
/// <summary>
/// 맵 데이터를 파일에 저장 (MapLoader 공통 저장 로직 사용)
/// </summary>
private void SaveMapToFile(string filePath)
{
try
{
// MapLoader의 표준 저장 메서드 사용 (AGVMapEditor와 동일한 형식)
bool success = MapLoader.SaveMapToFile(filePath, _mapNodes);
if (success)
{
Program.WriteLine($"[맵 저장] 파일 저장 완료: {filePath} ({_mapNodes.Count}개 노드)");
}
else
{
throw new InvalidOperationException("맵 저장에 실패했습니다.");
}
}
catch (Exception ex)
{
Program.WriteLine($"[맵 저장 오류] {ex.Message}");
throw;
}
}
private void btMapSaveAs_Click(object sender, EventArgs e)
{
// 맵 데이터 확인
if (_mapNodes == null || _mapNodes.Count == 0)
{
MessageBox.Show("저장할 맵 데이터가 없습니다.", "알림",
MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
using (var saveDialog = new SaveFileDialog())
{
saveDialog.Filter = "AGV Map Files (*.agvmap)|*.agvmap|모든 파일 (*.*)|*.*";
saveDialog.Title = "맵 파일 저장";
saveDialog.DefaultExt = "agvmap";
// 현재 파일이 있으면 기본 파일명으로 설정
if (!string.IsNullOrEmpty(_currentMapFilePath))
{
saveDialog.FileName = Path.GetFileName(_currentMapFilePath);
saveDialog.InitialDirectory = Path.GetDirectoryName(_currentMapFilePath);
}
else
{
// 기본 파일명: 날짜_시간 형식
saveDialog.FileName = $"ScanMap_{DateTime.Now:yyyyMMdd_HHmmss}.agvmap";
}
if (saveDialog.ShowDialog() == DialogResult.OK)
{
try
{
SaveMapToFile(saveDialog.FileName);
_currentMapFilePath = saveDialog.FileName;
// 설정에 마지막 맵 파일 경로 저장
_config.LastMapFilePath = _currentMapFilePath;
if (_config.AutoSave)
{
_config.Save();
}
_statusLabel.Text = $"맵 저장 완료: {Path.GetFileName(_currentMapFilePath)}";
MessageBox.Show($"맵이 저장되었습니다.\n\n파일: {_currentMapFilePath}", "저장 완료",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show($"맵 저장 중 오류 발생:\n{ex.Message}", "저장 오류",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
private void SToolStripMenuItem_Click(object sender, EventArgs e)
{
// 현재 맵 파일 경로가 있는 경우 해당 파일에 저장
if (string.IsNullOrEmpty(_currentMapFilePath))
{
// 경로가 없으면 다른 이름으로 저장 다이얼로그 표시
btMapSaveAs_Click(sender, e);
return;
}
try
{
SaveMapToFile(_currentMapFilePath);
_statusLabel.Text = $"맵 저장 완료: {Path.GetFileName(_currentMapFilePath)}";
MessageBox.Show($"맵이 저장되었습니다.\n\n파일: {_currentMapFilePath}", "저장 완료",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show($"맵 저장 중 오류 발생:\n{ex.Message}", "저장 오류",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
/// <summary>

View File

@@ -152,6 +152,21 @@
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
</value>
</data>
<data name="btMakeMap.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
</value>
</data>
<metadata name="_statusStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">

File diff suppressed because one or more lines are too long

View File

@@ -523,6 +523,10 @@
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AGVLogic\AGVNavigationCore\AGVNavigationCore.csproj">
<Project>{c5f7a8b2-8d3e-4a1b-9c6e-7f4d5e2a9b1c}</Project>
<Name>AGVNavigationCore</Name>
</ProjectReference>
<ProjectReference Include="..\StateMachine\StateMachine.csproj">
<Project>{bbc9bccf-6262-4355-9cc2-37ff678ac499}</Project>
<Name>StateMachine</Name>

View File

@@ -87,24 +87,26 @@ namespace Project
#region "Communication"
[Category("Commnunication Setting"), DisplayName("XBee PortName"), Editor(typeof(MyUITypeEditor), typeof(UITypeEditor))]
[Browsable(false), Category("Commnunication Setting"), DisplayName("XBee PortName"), Editor(typeof(MyUITypeEditor), typeof(UITypeEditor))]
public string Port_XBE { get; set; }
[Category("Commnunication Setting"), DisplayName("RFID PortName"), Editor(typeof(MyUITypeEditor), typeof(UITypeEditor))]
[Browsable(false), Category("Commnunication Setting"), DisplayName("RFID PortName"), Editor(typeof(MyUITypeEditor), typeof(UITypeEditor))]
public string Port_AGV { get; set; }
[Category("Commnunication Setting"), DisplayName("Xbee ID"), Editor(typeof(MyUITypeEditor), typeof(UITypeEditor))]
public byte XBE_ID { get; set; }
[Category("Commnunication Setting"), DisplayName("BMS PortName"), Editor(typeof(MyUITypeEditor), typeof(UITypeEditor))]
[Browsable(false), Category("Commnunication Setting"), DisplayName("BMS PortName"), Editor(typeof(MyUITypeEditor), typeof(UITypeEditor))]
public string Port_BAT { get; set; }
public int ChargerID { get; set; }
[Browsable(false)]
public int Baud_AGV { get; set; }
[Browsable(false)]
public int Baud_BAT { get; set; }
//public int Baud_PLC { get; set; }
[Browsable(false)]
public int Baud_XBE { get; set; }
//public int QueryInterval_BAT { get; set; }
@@ -219,6 +221,8 @@ namespace Project
#region "AGV"
public bool AutoModeOffAndClearPosition { get; set; }
[Browsable(false)]
public string musicfile { get; set; }
/// <summary>
@@ -235,8 +239,11 @@ namespace Project
//public double ChargeLimitLow { get; set; }
//public double ChargeLimitHigh { get; set; }
[Browsable(false)]
public string AGV_PANID { get; set; }
[Browsable(false)]
public string AGV_CHANNEL { get; set; }
[Browsable(false)]
public string AGV_ADDRESS { get; set; }
public int SCK { get; set; }
@@ -269,31 +276,19 @@ namespace Project
public int PID_DS { get; set; }
public double WheelSpeedCharge { get; set; }
//public byte MotorUpTime { get; set; }
//public double AlignSensorThreshold { get; set; }
// public byte BalanceThreshold { get; set; }
// public byte BalanceThresholdUp { get; set; }
// public byte MarkSensorThreshold { get; set; }
//public Boolean Opt_LidarStop { get; set; }
//public Boolean Opt_SlowUp { get; set; }
//public Boolean Opt_Magnet { get; set; }
public byte HomePositionValue { get; set; }
public byte HomeKitNo { get; set; }
//public Single interval_bms { get; set; }
public Single interval_xbe { get; set; }
public int interval_bms { get; set; }
//public byte interval_iostate { get; set; }
//public Boolean Enable_AutoZDnUp { get; set; }
public int doorSoundTerm { get; set; }
[Browsable(false)]
public int musicvol { get; set; }
public bool Enable_Music { get; set; }
#endregion
[Category("Report"),
[Category("Report"), Browsable(false),
Description("상태기록시 장비 식별코드(4자리)"),
DisplayName("M/C ID")]
public string MCID { get; set; }
@@ -368,10 +363,7 @@ namespace Project
if (ChargeStartLevel == 0) ChargeStartLevel = 85;
if (ChargeMaxLevel == 0) ChargeMaxLevel = 85;
if (ChargeEmergencyLevel == 0) ChargeEmergencyLevel = 30;
if (interval_bms == 0) interval_bms = 1000;
//충전은 대기상태 5분이 경과하면 진행한다
if (interval_bms == 0) interval_bms = 10;
//충전은 10분간격으로 재시도 한다
if (ChargeRetryTerm == 0) ChargeRetryTerm = 600;

View File

@@ -99,6 +99,114 @@ public static partial class EEMStatus
queryok = true;
}
/// <summary>
/// 배터리데이터수신시 값을 기록한다.
/// </summary>
public static void MakeBMSInformation_INFO()
{
if (PUB.BMS.Current_DataTime.Year == 1982 || LastBMSIFTime == PUB.BMS.Current_DataTime || PUB.setting.MCID.isEmpty()) return;
try
{
// BMS 데이터 취득 (실제 BMS 객체에서 데이터를 가져와야 함)
// 예시: var bms = Project.Device.BMS.Instance;
var mcid = Project.PUB.setting.MCID;
var timestr = PUB.BMS.Current_DataTime.ToString("yyyy-MM-dd HH:mm:ss");
// BMS 데이터 (실제 값으로 교체 필요)
var info_volt = PUB.BMS.Current_Volt;// "null"; // bms.Voltage
var info_current = PUB.BMS.Current_Amp;// "null"; // bms.Current
var info_capa = PUB.BMS.Current_MaxAmp;// "null"; // bms.Capacity
var info_level = PUB.BMS.Current_Level;// "null"; // bms.Level
var info_temp1 = PUB.BMS.Current_temp1;// "null"; // bms.Temp1
var info_temp2 = PUB.BMS.Current_temp2;// "null"; // bms.Temp2
var cell_volt1 = PUB.BMS.CellVoltage[0];// "null"; // bms.CellVolt1
var cell_volt2 = PUB.BMS.CellVoltage[1]; // bms.CellVolt2
var cell_volt3 = PUB.BMS.CellVoltage[2]; // bms.CellVolt3
var cell_volt4 = PUB.BMS.CellVoltage[3]; // bms.CellVolt4
var cell_volt5 = PUB.BMS.CellVoltage[4]; // bms.CellVolt5
var cell_volt6 = PUB.BMS.CellVoltage[5]; // bms.CellVolt6
var cell_volt7 = PUB.BMS.CellVoltage[6]; // bms.CellVolt7
var cell_volt8 = PUB.BMS.CellVoltage[7]; // bms.CellVolt8
// Status 폴더에 SQL 파일 생성
var path = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Status");
if (System.IO.Directory.Exists(path) == false)
System.IO.Directory.CreateDirectory(path);
var file = System.IO.Path.Combine(path, $"{DateTime.Now.ToString("HHmmssfff")}_BMS_INF.sql");
var sql = "insert into AGV_Shuttle_BMS(MCID,wdate,info_volt,info_current,info_capa,info_level,info_temp1,info_temp2) " +
"values('{0}','{1}',{2},{3},{4},{5},{6},{7})";
sql = string.Format(sql, mcid, timestr, info_volt, info_current, info_capa, info_level, info_temp1, info_temp2);
System.IO.File.WriteAllText(file, sql, System.Text.Encoding.Default);
LastBMSIFTime = DateTime.Now;
}
catch (Exception ex)
{
// 오류 로깅 (필요시 PUB.log.Add 등으로 처리)
Console.WriteLine($"MakeBMSInformation Error: {ex.Message}");
}
}
static DateTime LastBMSIFTime = DateTime.Now;
static DateTime LastBMSCVTime = DateTime.Now;
/// <summary>
/// 배터리데이터수신시 값을 기록한다.
/// </summary>
public static void MakeBMSInformation_Cell()
{
if (PUB.BMS.Current_CellTime.Year == 1982 || LastBMSCVTime == PUB.BMS.Current_CellTime || PUB.setting.MCID.isEmpty()) return;
try
{
// BMS 데이터 취득 (실제 BMS 객체에서 데이터를 가져와야 함)
// 예시: var bms = Project.Device.BMS.Instance;
var mcid = Project.PUB.setting.MCID;
var timestr = PUB.BMS.Current_CellTime.ToString("yyyy-MM-dd HH:mm:ss");
// BMS 데이터 (실제 값으로 교체 필요)
var info_volt = PUB.BMS.Current_Volt;// "null"; // bms.Voltage
var info_current = PUB.BMS.Current_Amp;// "null"; // bms.Current
var info_capa = PUB.BMS.Current_MaxAmp;// "null"; // bms.Capacity
var info_level = PUB.BMS.Current_Level;// "null"; // bms.Level
var info_temp1 = PUB.BMS.Current_temp1;// "null"; // bms.Temp1
var info_temp2 = PUB.BMS.Current_temp2;// "null"; // bms.Temp2
var cell_volt1 = PUB.BMS.CellVoltage[0];// "null"; // bms.CellVolt1
var cell_volt2 = PUB.BMS.CellVoltage[1]; // bms.CellVolt2
var cell_volt3 = PUB.BMS.CellVoltage[2]; // bms.CellVolt3
var cell_volt4 = PUB.BMS.CellVoltage[3]; // bms.CellVolt4
var cell_volt5 = PUB.BMS.CellVoltage[4]; // bms.CellVolt5
var cell_volt6 = PUB.BMS.CellVoltage[5]; // bms.CellVolt6
var cell_volt7 = PUB.BMS.CellVoltage[6]; // bms.CellVolt7
var cell_volt8 = PUB.BMS.CellVoltage[7]; // bms.CellVolt8
// Status 폴더에 SQL 파일 생성
var path = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Status");
if (System.IO.Directory.Exists(path) == false)
System.IO.Directory.CreateDirectory(path);
var file = System.IO.Path.Combine(path, $"{DateTime.Now.ToString("HHmmssfff")}_BMS_CV.sql");
var sql = "insert into AGV_Shuttle_BMS(MCID,wdate," +
"cell_volt1,cell_volt2,cell_volt3,cell_volt4,cell_volt5,cell_volt6,cell_volt7,cell_volt8) " +
"values('{0}','{1}',{2},{3},{4},{5},{6},{7},{8},{9})";
sql = string.Format(sql, mcid, timestr,
cell_volt1, cell_volt2, cell_volt3, cell_volt4,
cell_volt5, cell_volt6, cell_volt7, cell_volt8);
System.IO.File.WriteAllText(file, sql, System.Text.Encoding.Default);
LastBMSCVTime = DateTime.Now;
}
catch (Exception ex)
{
// 오류 로깅 (필요시 PUB.log.Add 등으로 처리)
Console.WriteLine($"MakeBMSInformation Error: {ex.Message}");
}
}
public static void UpdateStatusSQL(eSMStep status, bool _extrun = false, string remark = "")
{
var tsrun = DateTime.Now - StatusChecktime;

View File

@@ -12,6 +12,10 @@ using Microsoft.Speech.Synthesis;
using System.Threading.Tasks;
using System.Data.SqlClient;
using System.Linq;
using AGVNavigationCore.Models;
using AGVNavigationCore.Controls;
using System.Collections.Generic;
using System.Drawing;
namespace Project
{
@@ -22,7 +26,13 @@ namespace Project
public static bool Automodeonreboot = false;
public static bool AutRebootAlreay = false;
public static bool DriveSpeed = false;
public static AGVControl.MapControl mapctl;
public static AGVNavigationCore.Controls.UnifiedAGVCanvas _mapCanvas;
public static List<MapNode> _mapNodes;
/// <summary>
/// 가상 AGV (시뮬레이션용)
/// </summary>
public static VirtualAGV _virtualAGV;
#region "Hardware"
@@ -570,5 +580,140 @@ namespace Project
}
#region VirtualAGV
/// <summary>
/// RFID 읽기 시 해당 노드 위치로 AGV 업데이트
/// </summary>
/// <param name="rfidId">읽은 RFID ID</param>
/// <param name="motorDirection">모터 방향 (Forward/Backward)</param>
/// <returns>업데이트 성공 여부</returns>
public static bool UpdateAGVFromRFID(string rfidId, AgvDirection motorDirection = AgvDirection.Forward)
{
if (_virtualAGV == null || _mapNodes == null) return false;
// RFID에 해당하는 노드 찾기
var node = _mapNodes.FirstOrDefault(n => n.RfidId == rfidId);
if (node != null)
{
_virtualAGV.SetPosition(node, motorDirection);
RefreshAGVCanvas();
log.Add($"[AGV] RFID {rfidId} 감지 → 노드 {node.NodeId} 위치 업데이트 (방향: {motorDirection})");
return true;
}
log.Add($"[AGV] RFID {rfidId}에 해당하는 노드를 찾을 수 없음");
return false;
}
/// <summary>
/// 노드ID로 AGV 위치 업데이트
/// </summary>
/// <param name="nodeId">노드 ID</param>
/// <param name="motorDirection">모터 방향 (Forward/Backward)</param>
/// <returns>업데이트 성공 여부</returns>
public static bool UpdateAGVToNode(string nodeId, AgvDirection motorDirection = AgvDirection.Forward)
{
if (_virtualAGV == null || _mapNodes == null) return false;
var node = _mapNodes.FirstOrDefault(n => n.NodeId == nodeId);
if (node != null)
{
_virtualAGV.SetPosition(node, motorDirection);
RefreshAGVCanvas();
log.Add($"[AGV] 노드 {nodeId} 위치로 이동 (방향: {motorDirection})");
return true;
}
return false;
}
/// <summary>
/// AGV 방향 업데이트
/// </summary>
/// <param name="direction">새로운 방향</param>
public static void UpdateAGVDirection(AgvDirection direction)
{
if (_virtualAGV == null) return;
_virtualAGV.CurrentDirection = direction;
RefreshAGVCanvas();
}
/// <summary>
/// AGV 상태 업데이트
/// </summary>
/// <param name="state">새로운 상태</param>
public static void UpdateAGVState(AGVState state)
{
if (_virtualAGV == null) return;
_virtualAGV.CurrentState = state;
RefreshAGVCanvas();
}
/// <summary>
/// AGV 배터리 레벨 업데이트
/// </summary>
/// <param name="batteryLevel">배터리 레벨 (0.0 ~ 100.0)</param>
public static void UpdateAGVBattery(float batteryLevel)
{
if (_virtualAGV == null) return;
_virtualAGV.BatteryLevel = batteryLevel;
RefreshAGVCanvas();
}
/// <summary>
/// 맵 캔버스 강제 갱신 (AGV 위치 표시 업데이트)
/// </summary>
public static void RefreshAGVCanvas()
{
if (_mapCanvas != null && _mapCanvas.IsHandleCreated)
{
_mapCanvas.Invalidate();
}
}
/// <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
}
}

View File

@@ -5,11 +5,11 @@ using System.Runtime.InteropServices;
// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
// 이러한 특성 값을 변경하세요.
[assembly: AssemblyTitle("(OTP) 2D Reading System")]
[assembly: AssemblyTitle("ENIG Shuttle Narmi")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Amkor K4")]
[assembly: AssemblyProduct("(OTP) 2D Reading System")]
[assembly: AssemblyProduct("ENIG Shuttle Narmi")]
[assembly: AssemblyCopyright("Copyright ©Amkor-EET 2020")]
[assembly: AssemblyTrademark("EET")]
[assembly: AssemblyCulture("")]

View File

@@ -30,259 +30,259 @@ namespace Project
}
//목적지가 설정되었는지 체크한다.
if (PUB.mapctl.Manager.agv.TargetRFID.IsEmpty)
{
//최대 5초간 설정여부를 확인하고
if (VAR.TIME.RUN(eVarTime.CheckGotoTargetSet).TotalSeconds > 5)
{
//실패시에는 READY로 전환한다.
PUB.sm.SetNewRunStep(ERunStep.READY);
PUB.Speak(Lang.);
}
return false;
}
//Z if (PUB.mapctl.Manager.agv.TargetRFID.IsEmpty)
// {
// //최대 5초간 설정여부를 확인하고
// if (VAR.TIME.RUN(eVarTime.CheckGotoTargetSet).TotalSeconds > 5)
// {
// //실패시에는 READY로 전환한다.
// PUB.sm.SetNewRunStep(ERunStep.READY);
// PUB.Speak(Lang.목적지가없어대기상태로전환합니다);
// }
// return false;
// }
var idx = 1;
var BeforePredictIdx = -1;
var predict = PUB.mapctl.Manager.PredictResult;
if (PUB.sm.RunStepSeq == idx++)
{
PUB.Speak(Lang.);
PUB.log.Add($"목적지 위치 이동시작({PUB.mapctl.Manager.agv.TargetRFID.Value})");
VAR.TIME.Update(eVarTime.CheckGotoTargetSet);
VAR.TIME.Set(eVarTime.SendGotoCommand, DateTime.Now.AddDays(-1));
PUB.sm.UpdateRunStepSeq();
return false;
}
else if (PUB.sm.RunStepSeq == idx++)
{
//멈춰야하는경우
if (predict.MoveState == AGVControl.AGVMoveState.Stop)
{
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;
//var idx = 1;
//var BeforePredictIdx = -1;
//var predict = PUB.mapctl.Manager.PredictResult;
//if (PUB.sm.RunStepSeq == idx++)
//{
// PUB.Speak(Lang.위치로이동합니다);
// PUB.log.Add($"목적지 위치 이동시작({PUB.mapctl.Manager.agv.TargetRFID.Value})");
// VAR.TIME.Update(eVarTime.CheckGotoTargetSet);
// VAR.TIME.Set(eVarTime.SendGotoCommand, DateTime.Now.AddDays(-1));
// PUB.sm.UpdateRunStepSeq();
// return false;
//}
//else if (PUB.sm.RunStepSeq == idx++)
//{
// //멈춰야하는경우
// if (predict.MoveState == AGVControl.AGVMoveState.Stop)
// {
// 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;
// //상태값이 바뀌었다면 전송을 해야한다
// 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.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;
// 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,
});
// //이동셋팅을 해준다
// 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 (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");
// //정지상태라면 이동 명령을 전달한다
// 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;
// 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;
}
}
// 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);
}
}
// //예측이 업데이트되지 않으면 오류 처리해야한다
// 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;
// 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);
}
// //최대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 (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;
}
// //방향전환용 턴이라면 이동기록을 추가해서 방향이 맞도록 처리해주자
// 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;
}
// PUB.sm.UpdateRunStepSeq();
// }
// else PUB.sm.UpdateRunStepSeq();
// return false;
//}
//좌턴이동명령 전송

View File

@@ -56,33 +56,33 @@ namespace Project
VAR.BOOL[eVarBool.AGV_ERROR] = PUB.AGV.error.Value > 0;
VAR.BOOL[eVarBool.EMERGENCY] = PUB.AGV.error.Emergency;
//모터방향 입력
if (PUB.AGV.data.Direction == 'B')
PUB.mapctl.Manager.agv.Current_Motor_Direction = AGVControl.AgvDir.Backward;
else
PUB.mapctl.Manager.agv.Current_Motor_Direction = AGVControl.AgvDir.Forward;
////모터방향 입력
//if (PUB.AGV.data.Direction == 'B')
// PUB.mapctl.Manager.agv.Current_Motor_Direction = AGVControl.AgvDir.Backward;
//else
// PUB.mapctl.Manager.agv.Current_Motor_Direction = AGVControl.AgvDir.Forward;
//현재 속도
if (PUB.AGV.data.Speed == 'H')
PUB.mapctl.Manager.agv.CurrentSpeed = AGVControl.AgvSpeed.High;
else if (PUB.AGV.data.Speed == 'M')
PUB.mapctl.Manager.agv.CurrentSpeed = AGVControl.AgvSpeed.Middle;
else if (PUB.AGV.data.Speed == 'L')
PUB.mapctl.Manager.agv.CurrentSpeed = AGVControl.AgvSpeed.Low;
else if (PUB.AGV.data.Speed == 'S')
PUB.mapctl.Manager.agv.CurrentSpeed = AGVControl.AgvSpeed.MarkStop;
////현재 속도
//if (PUB.AGV.data.Speed == 'H')
// PUB.mapctl.Manager.agv.CurrentSpeed = AGVControl.AgvSpeed.High;
//else if (PUB.AGV.data.Speed == 'M')
// PUB.mapctl.Manager.agv.CurrentSpeed = AGVControl.AgvSpeed.Middle;
//else if (PUB.AGV.data.Speed == 'L')
// PUB.mapctl.Manager.agv.CurrentSpeed = AGVControl.AgvSpeed.Low;
//else if (PUB.AGV.data.Speed == 'S')
// PUB.mapctl.Manager.agv.CurrentSpeed = AGVControl.AgvSpeed.MarkStop;
//이동방향
if (PUB.AGV.data.Sts == 'S')
PUB.mapctl.Manager.agv.CurrentSTS = AGVControl.AgvSts.Straight;
else if (PUB.AGV.data.Sts == 'L')
PUB.mapctl.Manager.agv.CurrentSTS = AGVControl.AgvSts.Left;
else if (PUB.AGV.data.Sts == 'R')
PUB.mapctl.Manager.agv.CurrentSTS = AGVControl.AgvSts.Right;
////이동방향
//if (PUB.AGV.data.Sts == 'S')
// PUB.mapctl.Manager.agv.CurrentSTS = AGVControl.AgvSts.Straight;
//else if (PUB.AGV.data.Sts == 'L')
// PUB.mapctl.Manager.agv.CurrentSTS = AGVControl.AgvSts.Left;
//else if (PUB.AGV.data.Sts == 'R')
// PUB.mapctl.Manager.agv.CurrentSTS = AGVControl.AgvSts.Right;
PUB.mapctl.Manager.agv.IsMoving = PUB.AGV.system1.agv_run;
PUB.mapctl.Manager.agv.IsMarkCheck = PUB.AGV.system1.Mark1_check || PUB.AGV.system1.Mark2_check;
//PUB.mapctl.Manager.agv.IsMoving = PUB.AGV.system1.agv_run;
//PUB.mapctl.Manager.agv.IsMarkCheck = PUB.AGV.system1.Mark1_check || PUB.AGV.system1.Mark2_check;
if (PUB.AGV.signal.mark_sensor == false)
{
@@ -184,41 +184,18 @@ namespace Project
PUB.log.AddE(logEMsg);
}
//맵데이터에서 현재 위치를 찾는다
if (PUB.mapctl.SetCurrentPosition(PUB.AGV.data.TagNo) == false)
//virtual agv setting
var CurrentNode = PUB._mapNodes.FirstOrDefault(t => t.RfidId.Equals(PUB.Result.LastTAG, StringComparison.OrdinalIgnoreCase));
if (CurrentNode == null)
{
if (VAR.BOOL[eVarBool.FLAG_AUTORUN] && PUB.AGV.system1.agv_run)
PUB.AGV.AGVMoveStop("unknown tag no");
//존재하지 않는 태그가 읽히면 관련 오류를 표시한다.
}
else
{
//위치는 찾았다 해당 위치가 내 목적지라면 mark stop기능으로 전환한다
PUB.log.AddE($"RFID:{PUB.Result.LastTAG} 의 노드를 찾을 수 없습니다");
return;
}
////자동, 상하차 모드일때 RFID 가 타겟위치에 올때는 - 멈춤을 설정해준
//if (VAR.BOOL[eVarBool.FLAG_AUTORUN] == true &&
// PUB.Result.CurrentPos == PUB.Result.TargetPos &&
// PUB.Result.TargetPos != ePosition.NONE &&
// (PUB.sm.RunStep == ERunStep.GODOWN ||
// PUB.sm.RunStep == ERunStep.GOUP ||
// PUB.sm.RunStep == ERunStep.GOHOME ||
// PUB.sm.RunStep == ERunStep.GOCHARGE))
//{
// if (PUB.AGV.data.Sts == 'F' && dirForward == "0") //아래로 내려오고있음
// {
// PUB.AGV.AGVMoveStop("AGV_DataReceive", arDev.Narumi.eStopOpt.MarkStop);
// PUB.Speak( Lang.다음마크위치에서정지합니다);
// }
// else if (PUB.AGV.data.Sts == 'B' && dirForward == "1")
// {
// //VAR.BOOL[eVarBool.FLAG_NEXTSTOP_MARK] = true;
// PUB.AGV.AGVMoveStop("AGV_DataReceive", arDev.Narumi.eStopOpt.MarkStop);
// PUB.Speak(Lang.다음마크위치에서정지합니다);
// }
//}
//모터방향 확인해서 UI와 AGV클래스에 적용한
var MotDireciton = PUB.AGV.data.Direction == 'B' ? AGVNavigationCore.Models.AgvDirection.Backward : AGVNavigationCore.Models.AgvDirection.Forward;
PUB._virtualAGV.SetPosition(CurrentNode, MotDireciton);
PUB._mapCanvas.SetAGVPosition("AGV", CurrentNode, MotDireciton);
}
break;
case arDev.Narumi.DataType.ACK:
@@ -231,12 +208,8 @@ namespace Project
}
//이 후 상황을 예측한다
if (PUB.mapctl != null)
{
var rlt = PUB.mapctl.Manager.PredictNextAction();
if (rlt.Changed)
Console.WriteLine($"[new] predict idx:{rlt.Idx}");
}
var command = PUB._virtualAGV.Predict();
var preditMSG = $"Motor:{command.Motor},Magnet:{command.Magnet},Speed:{command.Speed} : {command.Reason}";
}
catch (Exception ex)
{

View File

@@ -25,7 +25,7 @@ namespace Project
DateTime lastbmstime = DateTime.Now;
private void Bms_Message(object sender, arDev.BMS.MessageEventArgs e)
{
if (e.MsgType == arRS232.MessageType.Error) PUB.logbms.AddE( e.Message);
if (e.MsgType == arDev.arRS232.MessageType.Error) PUB.logbms.AddE( e.Message);
else
{
var hexstr = e.Data.GetHexString().Trim();
@@ -159,9 +159,9 @@ namespace Project
private void Bms_BMSDataReceive(object sender, EventArgs e)
{
PUB.mapctl.Manager.agv.BatteryLevel = PUB.BMS.Current_Level;
PUB.mapctl.Manager.agv.BatteryTemp1 = PUB.BMS.Current_temp1;
PUB.mapctl.Manager.agv.BatteryTemp2 = PUB.BMS.Current_temp2;
//PUB.mapctl.Manager.agv.BatteryLevel = PUB.BMS.Current_Level;
//PUB.mapctl.Manager.agv.BatteryTemp1 = PUB.BMS.Current_temp1;
//PUB.mapctl.Manager.agv.BatteryTemp2 = PUB.BMS.Current_temp2;
if (PUB.BMS.Current_Level <= PUB.setting.ChargeStartLevel)
{
//배터리 레벨이 기준보다 낮다면 경고를 활성화 한다
@@ -180,6 +180,11 @@ namespace Project
PUB.log.AddAT("배터리 부족 경고 비활성화");
}
}
EEMStatus.MakeBMSInformation_INFO();
}
private void BMS_BMSCellDataReceive(object sender, arDev.BMSCelvoltageEventArgs e)
{
EEMStatus.MakeBMSInformation_Cell();
}
}
}

View File

@@ -118,10 +118,6 @@ namespace Project
PUB.BMS.PortName = PUB.setting.Port_BAT;
PUB.BMS.Open();
PUB.BMS.BMSDataReceive += Bms_BMSDataReceive;
PUB.BMS.Message += Bms_Message;
PUB.BMS.ChargeDetect += BMS_ChargeDetect;
VAR.TIME.Update(eVarTime.LastConn_BAT);
VAR.TIME.Update(eVarTime.LastConnTry_BAT);
}
@@ -132,9 +128,6 @@ namespace Project
if (ts.TotalSeconds > 10)
{
Console.WriteLine("bms auto disconnect");
PUB.BMS.BMSDataReceive -= Bms_BMSDataReceive;
PUB.BMS.Message -= Bms_Message;
PUB.BMS.ChargeDetect -= BMS_ChargeDetect;
PUB.BMS.Close();
VAR.TIME.Set(eVarTime.LastConn_BAT,DateTime.Now.AddSeconds(5));
}
@@ -159,7 +152,7 @@ namespace Project
{
if (PUB.BMS.lastSendTime.Year == 1982) PUB.BMS.lastSendTime = DateTime.Now.AddSeconds(1);
var ts = DateTime.Now - PUB.BMS.lastSendTime;
if (ts.TotalMilliseconds >= PUB.setting.interval_bms)
if (ts.TotalSeconds >= PUB.setting.interval_bms)
{
PUB.BMS.SendQuery();
}

View File

@@ -65,9 +65,6 @@ namespace Project
lbBat.CurA = PUB.BMS.Current_Amp;
lbBat.IsOpen = PUB.BMS.IsOpen;
if (PUB.mapctl != null)
PUB.mapctl.Invalidate();
//쓰레드로인해서 메인에서 진행하게한다. SPS는 메인쓰레드에서 진행 됨
//팝을 제거 혹은 표시하는 기능
if (PUB.popup.needShow) PUB.popup.showMessage();

View File

@@ -60,11 +60,11 @@ namespace Project
{
if (ushort.TryParse(targstr, out ushort tagno))
{
if (PUB.mapctl.SetCurrentPosition(tagno) == true)
{
PUB.log.AddI($"Set Position:{tagno}");
}
else PUB.log.AddE($"Position Set Error:{tagno}");
//if (PUB.mapctl.SetCurrentPosition(tagno) == true)
//{
// PUB.log.AddI($"Set Position:{tagno}");
//}
//else PUB.log.AddE($"Position Set Error:{tagno}");
}
else PUB.log.AddE($"Position Param(tagstr) Error:{dataStr}");
}
@@ -79,11 +79,11 @@ namespace Project
case ENIGProtocol.AGVCommandHE.Goto: //move to tag
if (uint.TryParse(dataStr, out uint tagno2))
{
var currPos = PUB.mapctl.Manager.agv.CurrentRFID;///.AGVMoveToRFID(;
if (PUB.mapctl.SetTargetPosition(tagno2))
PUB.log.AddI($"New Target {tagno2}");
else
PUB.log.AddE($"Path Error {tagno2}");
//var currPos = PUB.mapctl.Manager.agv.CurrentRFID;///.AGVMoveToRFID(;
//if (PUB.mapctl.SetTargetPosition(tagno2))
// PUB.log.AddI($"New Target {tagno2}");
//else
// PUB.log.AddE($"Path Error {tagno2}");
}
else PUB.log.AddE($"Path Param Error :{dataStr}");
break;

View File

@@ -10,6 +10,7 @@ using System.Windows.Forms;
using Project.StateMachine;
using COMM;
using AR;
using AGVNavigationCore.Models;
namespace Project.ViewForm
{
@@ -26,13 +27,46 @@ namespace Project.ViewForm
this.ctlAuto1.Scean = CtlAuto.eScean.Progress;
else
this.ctlAuto1.Scean = CtlAuto.eScean.Normal;
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);
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()
{
PUB._mapCanvas = new AGVNavigationCore.Controls.UnifiedAGVCanvas();
PUB._mapCanvas.Dock = DockStyle.Fill;
PUB._mapCanvas.ShowGrid = false;
PUB._mapCanvas.BackColor = Color.FromArgb(32,32,32);
PUB._mapCanvas.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);
// 툴바 버튼 이벤트 연결
//WireToolbarButtonEvents();
}
private void fAuto_Load(object sender, EventArgs e)
{
ctlAuto1.dev_agv = PUB.AGV;
@@ -47,25 +81,89 @@ namespace Project.ViewForm
var path = new System.IO.DirectoryInfo("route");
if (path.Exists == false) path.Create();
var files = path.GetFiles("*.route");
var fn = string.Empty;
if (files.Any() == false)
//맵파일로딩
var filePath = new System.IO.FileInfo(@".\route\NewMap.agvmap");
if (filePath.Exists)
{
fn = AR.UTIL.MakePath("sample.route");
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;
// 🔥 맵 설정 적용 (배경색, 그리드 표시)
if (result.Settings != null)
{
PUB._mapCanvas.BackColor = System.Drawing.Color.FromArgb(result.Settings.BackgroundColorArgb);
PUB._mapCanvas.ShowGrid = result.Settings.ShowGrid;
}
else if (files.Count() == 1)
// 🔥 가상 AGV 초기화 (첫 노드 위치에 생성)
if (PUB._virtualAGV == null && PUB._mapNodes.Count > 0)
{
fn = files.First().FullName;
}
if(fn.isEmpty()==false)
var startNode = PUB._mapNodes.FirstOrDefault(n => n.IsNavigationNode());
if (startNode != null)
{
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);
PUB._virtualAGV = new VirtualAGV("AGV-01", 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();
}

View File

@@ -294,7 +294,7 @@ namespace Project
this.btAutoRun.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.btAutoRun.GradientRepeatBG = false;
this.btAutoRun.isButton = true;
this.btAutoRun.Location = new System.Drawing.Point(0, 274);
this.btAutoRun.Location = new System.Drawing.Point(0, 288);
this.btAutoRun.Margin = new System.Windows.Forms.Padding(0);
this.btAutoRun.MouseDownColor = System.Drawing.Color.Empty;
this.btAutoRun.MouseOverColor = System.Drawing.Color.Empty;
@@ -315,7 +315,7 @@ namespace Project
this.btAutoRun.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btAutoRun.SignColor = System.Drawing.Color.Yellow;
this.btAutoRun.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btAutoRun.Size = new System.Drawing.Size(259, 139);
this.btAutoRun.Size = new System.Drawing.Size(259, 145);
this.btAutoRun.TabIndex = 22;
this.btAutoRun.Text = "수동";
this.btAutoRun.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -362,7 +362,7 @@ namespace Project
this.btChargeA.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btChargeA.SignColor = System.Drawing.Color.Yellow;
this.btChargeA.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btChargeA.Size = new System.Drawing.Size(143, 137);
this.btChargeA.Size = new System.Drawing.Size(143, 144);
this.btChargeA.TabIndex = 141;
this.btChargeA.Text = "자동충전";
this.btChargeA.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -388,7 +388,7 @@ namespace Project
this.lbTime.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
this.lbTime.GradientRepeatBG = false;
this.lbTime.isButton = false;
this.lbTime.Location = new System.Drawing.Point(5, 554);
this.lbTime.Location = new System.Drawing.Point(5, 574);
this.lbTime.Margin = new System.Windows.Forms.Padding(0);
this.lbTime.MouseDownColor = System.Drawing.Color.Yellow;
this.lbTime.MouseOverColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
@@ -416,7 +416,6 @@ namespace Project
this.lbTime.TextShadow = false;
this.lbTime.TextVisible = true;
this.toolTip1.SetToolTip(this.lbTime, "현재 시간");
this.lbTime.Click += new System.EventHandler(this.lbTime_Click);
//
// btDebug
//
@@ -978,7 +977,7 @@ namespace Project
this.panRight.Location = new System.Drawing.Point(1015, 146);
this.panRight.Name = "panRight";
this.panRight.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0);
this.panRight.Size = new System.Drawing.Size(264, 579);
this.panRight.Size = new System.Drawing.Size(264, 599);
this.panRight.TabIndex = 131;
//
// tableLayoutPanel1
@@ -997,7 +996,7 @@ namespace Project
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(259, 413);
this.tableLayoutPanel1.Size = new System.Drawing.Size(259, 433);
this.tableLayoutPanel1.TabIndex = 0;
//
// btHome
@@ -1039,7 +1038,7 @@ namespace Project
this.btHome.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btHome.SignColor = System.Drawing.Color.Yellow;
this.btHome.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btHome.Size = new System.Drawing.Size(116, 274);
this.btHome.Size = new System.Drawing.Size(116, 288);
this.btHome.TabIndex = 141;
this.btHome.Text = "홈";
this.btHome.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -1064,7 +1063,7 @@ namespace Project
this.btChargeM.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal;
this.btChargeM.GradientRepeatBG = false;
this.btChargeM.isButton = true;
this.btChargeM.Location = new System.Drawing.Point(116, 137);
this.btChargeM.Location = new System.Drawing.Point(116, 144);
this.btChargeM.Margin = new System.Windows.Forms.Padding(0);
this.btChargeM.MouseDownColor = System.Drawing.Color.Yellow;
this.btChargeM.MouseOverColor = System.Drawing.Color.Lime;
@@ -1085,7 +1084,7 @@ namespace Project
this.btChargeM.SignAlign = System.Drawing.ContentAlignment.BottomRight;
this.btChargeM.SignColor = System.Drawing.Color.Yellow;
this.btChargeM.SignFont = new System.Drawing.Font("Consolas", 7F, System.Drawing.FontStyle.Italic);
this.btChargeM.Size = new System.Drawing.Size(143, 137);
this.btChargeM.Size = new System.Drawing.Size(143, 144);
this.btChargeM.TabIndex = 141;
this.btChargeM.Text = "수동충전";
this.btChargeM.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
@@ -1494,7 +1493,7 @@ namespace Project
//
this.panel5.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(18)))), ((int)(((byte)(18)))), ((int)(((byte)(18)))));
this.panel5.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel5.Location = new System.Drawing.Point(5, 549);
this.panel5.Location = new System.Drawing.Point(5, 569);
this.panel5.Name = "panel5";
this.panel5.Size = new System.Drawing.Size(259, 5);
this.panel5.TabIndex = 142;
@@ -1716,10 +1715,10 @@ namespace Project
//
this.pandBottomDIO.Controls.Add(this.panel9);
this.pandBottomDIO.Dock = System.Windows.Forms.DockStyle.Bottom;
this.pandBottomDIO.Location = new System.Drawing.Point(1, 725);
this.pandBottomDIO.Location = new System.Drawing.Point(1, 745);
this.pandBottomDIO.Margin = new System.Windows.Forms.Padding(0);
this.pandBottomDIO.Name = "pandBottomDIO";
this.pandBottomDIO.Size = new System.Drawing.Size(1278, 55);
this.pandBottomDIO.Size = new System.Drawing.Size(1278, 35);
this.pandBottomDIO.TabIndex = 136;
//
// panel9
@@ -1730,7 +1729,7 @@ namespace Project
this.panel9.Location = new System.Drawing.Point(0, 0);
this.panel9.Margin = new System.Windows.Forms.Padding(0);
this.panel9.Name = "panel9";
this.panel9.Size = new System.Drawing.Size(1278, 55);
this.panel9.Size = new System.Drawing.Size(1278, 35);
this.panel9.TabIndex = 0;
//
// IOState
@@ -1797,7 +1796,7 @@ namespace Project
this.IOState.ShadowColor = System.Drawing.Color.Transparent;
this.IOState.showDebugInfo = false;
this.IOState.ShowIndexString = false;
this.IOState.Size = new System.Drawing.Size(1151, 55);
this.IOState.Size = new System.Drawing.Size(1151, 35);
this.IOState.TabIndex = 6;
this.IOState.Tags = null;
this.IOState.Text = "gridView2";
@@ -1870,7 +1869,7 @@ namespace Project
this.SSInfo.ShadowColor = System.Drawing.Color.Transparent;
this.SSInfo.showDebugInfo = false;
this.SSInfo.ShowIndexString = false;
this.SSInfo.Size = new System.Drawing.Size(127, 55);
this.SSInfo.Size = new System.Drawing.Size(127, 35);
this.SSInfo.TabIndex = 10;
this.SSInfo.Tags = null;
this.SSInfo.Text = "gridView3";
@@ -1892,7 +1891,7 @@ namespace Project
this.panDlg.Location = new System.Drawing.Point(1, 146);
this.panDlg.Margin = new System.Windows.Forms.Padding(0);
this.panDlg.Name = "panDlg";
this.panDlg.Size = new System.Drawing.Size(1014, 579);
this.panDlg.Size = new System.Drawing.Size(1014, 599);
this.panDlg.TabIndex = 146;
//
// arPanel2

View File

@@ -64,9 +64,6 @@ namespace Project
if (PUB.setting.FullScreen) this.WindowState = FormWindowState.Maximized;
lbTime.Click += (s1,e1) => {
PUB.mapctl.ShowDesign();
};
}
protected override void WndProc(ref Message m)
@@ -216,9 +213,10 @@ namespace Project
//배터리관리시스템
PUB.BMS = new arDev.BMS();
PUB.BMS.BMSDataReceive += Bms_BMSDataReceive;
PUB.BMS.BMSCellDataReceive += BMS_BMSCellDataReceive;
PUB.BMS.Message += Bms_Message;
PUB.BMS.ChargeDetect += BMS_ChargeDetect;
PUB.BMS.ScanInterval = PUB.setting.interval_bms;//
//디버그메세지 출력용 소켓
PUB.sock_debug = new Device.Socket();
@@ -243,6 +241,7 @@ namespace Project
PUB.sm.SPS += sm_SPS;
PUB.sm.Start();
tmDisplay.Tick += tmDisplay_Tick;
tmDisplay.Start(); //start Display
this.btDebug.Visible = PUB.setting.UseDebugMode;
@@ -263,6 +262,8 @@ namespace Project
}
#region "Mouse Form Move"
private Boolean fMove = false;
@@ -886,9 +887,5 @@ namespace Project
}
}
private void lbTime_Click(object sender, EventArgs e)
{
PUB.mapctl.ShowDesign();
}
}
}

View File

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

View File

@@ -1270,7 +1270,7 @@
this.label30.AutoSize = true;
this.label30.Font = new System.Drawing.Font("궁서체", 18F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label30.ForeColor = System.Drawing.Color.WhiteSmoke;
this.label30.Location = new System.Drawing.Point(946, 273);
this.label30.Location = new System.Drawing.Point(946, 188);
this.label30.Name = "label30";
this.label30.Size = new System.Drawing.Size(49, 24);
this.label30.TabIndex = 33;
@@ -1281,15 +1281,15 @@
this.label12.AutoSize = true;
this.label12.Font = new System.Drawing.Font("궁서체", 18F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label12.ForeColor = System.Drawing.Color.WhiteSmoke;
this.label12.Location = new System.Drawing.Point(946, 194);
this.label12.Location = new System.Drawing.Point(946, 109);
this.label12.Name = "label12";
this.label12.Size = new System.Drawing.Size(36, 24);
this.label12.Size = new System.Drawing.Size(49, 24);
this.label12.TabIndex = 33;
this.label12.Text = "ms";
this.label12.Text = "sec";
//
// button2
//
this.button2.Location = new System.Drawing.Point(383, 178);
this.button2.Location = new System.Drawing.Point(383, 93);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(89, 56);
this.button2.TabIndex = 28;
@@ -1309,7 +1309,7 @@
"46800",
"115200",
"250000"});
this.tbBaudBAT.Location = new System.Drawing.Point(478, 181);
this.tbBaudBAT.Location = new System.Drawing.Point(478, 96);
this.tbBaudBAT.Name = "tbBaudBAT";
this.tbBaudBAT.Size = new System.Drawing.Size(217, 51);
this.tbBaudBAT.TabIndex = 27;
@@ -1319,7 +1319,7 @@
//
this.tbportBMS.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.tbportBMS.Font = new System.Drawing.Font("궁서체", 32F, System.Drawing.FontStyle.Bold);
this.tbportBMS.Location = new System.Drawing.Point(161, 178);
this.tbportBMS.Location = new System.Drawing.Point(161, 93);
this.tbportBMS.Name = "tbportBMS";
this.tbportBMS.Size = new System.Drawing.Size(216, 56);
this.tbportBMS.TabIndex = 26;
@@ -1330,7 +1330,7 @@
this.label27.AutoSize = true;
this.label27.Font = new System.Drawing.Font("궁서체", 18F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label27.ForeColor = System.Drawing.Color.WhiteSmoke;
this.label27.Location = new System.Drawing.Point(38, 194);
this.label27.Location = new System.Drawing.Point(38, 109);
this.label27.Name = "label27";
this.label27.Size = new System.Drawing.Size(114, 24);
this.label27.TabIndex = 25;
@@ -1387,7 +1387,7 @@
//
// btSelXbee
//
this.btSelXbee.Location = new System.Drawing.Point(383, 257);
this.btSelXbee.Location = new System.Drawing.Point(383, 172);
this.btSelXbee.Name = "btSelXbee";
this.btSelXbee.Size = new System.Drawing.Size(89, 56);
this.btSelXbee.TabIndex = 13;
@@ -1407,7 +1407,7 @@
"46800",
"115200",
"250000"});
this.tbBaudXBE.Location = new System.Drawing.Point(478, 260);
this.tbBaudXBE.Location = new System.Drawing.Point(478, 175);
this.tbBaudXBE.Name = "tbBaudXBE";
this.tbBaudXBE.Size = new System.Drawing.Size(217, 51);
this.tbBaudXBE.TabIndex = 8;
@@ -1417,7 +1417,7 @@
//
this.tbPortXBE.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
this.tbPortXBE.Font = new System.Drawing.Font("궁서체", 32F, System.Drawing.FontStyle.Bold);
this.tbPortXBE.Location = new System.Drawing.Point(161, 257);
this.tbPortXBE.Location = new System.Drawing.Point(161, 172);
this.tbPortXBE.Name = "tbPortXBE";
this.tbPortXBE.Size = new System.Drawing.Size(216, 56);
this.tbPortXBE.TabIndex = 3;
@@ -1428,7 +1428,7 @@
this.label5.AutoSize = true;
this.label5.Font = new System.Drawing.Font("궁서체", 18F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label5.ForeColor = System.Drawing.Color.WhiteSmoke;
this.label5.Location = new System.Drawing.Point(38, 273);
this.label5.Location = new System.Drawing.Point(38, 188);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(114, 24);
this.label5.TabIndex = 1;
@@ -1444,7 +1444,7 @@
this.valIntervalBMS.FontSideButton = new System.Drawing.Font("Consolas", 15F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.valIntervalBMS.ForeColor = System.Drawing.Color.White;
this.valIntervalBMS.ForeColorButton = System.Drawing.Color.Black;
this.valIntervalBMS.Location = new System.Drawing.Point(701, 181);
this.valIntervalBMS.Location = new System.Drawing.Point(701, 96);
this.valIntervalBMS.MaxValue = 999999D;
this.valIntervalBMS.MinValue = 0D;
this.valIntervalBMS.Name = "valIntervalBMS";
@@ -1467,7 +1467,7 @@
this.valIntervalXBE.FontSideButton = new System.Drawing.Font("Consolas", 15F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.valIntervalXBE.ForeColor = System.Drawing.Color.White;
this.valIntervalXBE.ForeColorButton = System.Drawing.Color.Black;
this.valIntervalXBE.Location = new System.Drawing.Point(701, 260);
this.valIntervalXBE.Location = new System.Drawing.Point(701, 175);
this.valIntervalXBE.MaxValue = 100D;
this.valIntervalXBE.MinValue = 0D;
this.valIntervalXBE.Name = "valIntervalXBE";

View File

@@ -120,41 +120,40 @@
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="btClose.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAsRJREFUWEft
lz9TU0EUxdOovWKv9Golo59AsRMr0ELQRjsHO4n4CWD8A/oNUD9IZpKQxCYVIg1gKTYChejvvDmbbN57
IS8gY8OZOZPHuefe3b27+xJKpzgKqtXqhdXV1Sv1ev0OHNezNIdPBgw0Cl8xWAv+6cNmo9GYZzKXnXZ8
NJvNixR+A/ejgQZR3tdwxGWOBlZzgyLbLhq4g77C5xyfD0U/S9uxJ3Cbro253HAg8R4FdqNiX9Em+Txj
SwaKwSm4DkPeLpObsKUYSNDKO4Mz8Lt2u33W4YGQl7ylkA93C3fCe/49JJP4zKGhQf5sqAO3Ct0UjDpw
SRKdeGs5QZECaQ91lkM9FrNoOR8YRjGG074et53O3ETTIXtsKQPF5JHXUmltbe0c2jeomvuHXlEmoHse
ZjtpWYVHYDjhv2FmEtIck0fezhWkkw+siy8tZ0Hwi0076UPHhGbQwwAH8KlDebEnDiXgb92OH443LPdC
e2eD9n7Fcg+IxatMOpGn2d4D9E/2HFQqlfOWu2Dfrtmg9r+wnAHx9IADBxeIzdmnBV613AWD6kslGKYt
5yLVcjHT9jScE+rfttyFxGCQ2XIu+kygcybyQM6j4GesW5a7+O9bEB9CJvDRcg+IHecQfrYn/xAKBJs2
FbmGnT3PifVsh19GPxXHW7ecBa2ZdxFxyrIm9i9fRGXLWeg1ieEkXsUbUDX3Wq3WJYfygUm/ZMJslywn
SH/R5CHtocb7UI9OLFjuD4xqd/wraNahoUHu86jOZpEFJOCcjJEQ/xpaVisdHgh5We2HKP9XrVa77nAx
UGCCxHgSG2j307cjhmI+cGHPk8HR7toyHNyJraiYqOukL5YyhadFPVtLrlrEzaFXnob2jYksUmyYn+V7
TGyh8J4XAcV0RbXSRjRQmoqVB16140KvUgbSv2bjop77vl5PcShKpb/SGsNeH5IytgAAAABJRU5ErkJg
gg==
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAplJREFUWEft
V7tuFEEQdALkPHIgByIQfAE2GRAZCMCQmAyZDA74AhAPG/7AwIectNfVeyQXnY0TbEJMgu2Ah+rUc/Q2
s3d7xojEJY20mqrumenpnpmdmtrHLlAUxREAp0TkkojM8Jt9UbenEJGTIvIEQBfAz5pWqurjoihORPtd
oyzLYwBeANjJDFjXqH0O4Gj0NxFU9TyAjeB8U1WXATxU1Zts9s2+zaDdEJFz0W8jiMhVAFvO2YqIzAI4
ELUJ5ABcA7Dq7LZU9UrUjoStfDi4iLzq9XoHo64O1AJY9JNoHAnb889u8HtR0xQAFtwk1htViiXcwEhV
X3quiYOoAbDkFvPMc3+ApeayfdWHvSzLC5Zkd6pWv0GOGmpTX7/fPwTgo/ncGVmiVudptrOpn+XkMvx7
bhI2ODlqqB2WoKrecFvxqGrpAOBDchCTTkTm3AA/ANwdwc17W6uOL8ar54aw4zXt/XLkibDKQSRyfdGO
APAuTbDdbh+OPPf4jAv/g8gnZAYcOzjBw8ot8HTkGUZeKklwK/IeIeTZsEeYTfI/HXkmyrSLwFzkPWom
MMyJHETktpvAxcj//y3wSSgibyNPZAafJAnfp2hlk5DgfW6iJmU43PMMV9kOO4y+2uLEcxXwMZGiwFst
9e/xQdSqWjrwmPxHR/Ga+dzudrvHq1YB9pJJs130XLxocogaAK+TP1V96rksLNz+FbQQNU0B4L7z8ylO
rhZ8PITX0BJDGXV1oFZV3zj7b51O52zUjQSfUWESa6p6PVaHBzlLuLTng8FV9XLUNoJFYt05Y2M58WJp
8chm47f1DUrNh33ilUdw3/iSmfBZvs2Ea7znTWAlypVqZsDUyLXGltrfgkep/ZrNpF+z2uN1H2PwC9Ia
w16WomAQAAAAAElFTkSuQmCC
</value>
</data>
<data name="pictureBox1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABGdBTUEAALGPC/xhBQAAA5hJREFUaEPt
mvtTElEUx/3v+lea6X9oppnGV5LV+EABUSRG8f3gvaZGagaakuIDFdPR0nykaZqv0/1eASmuAstursbX
+fyye45zvnDv2XN3KMhLq3poefzgke2J/i6BmmPlXwk3rBNt1DHbeydArag5Vv6VcBEB3uW+OwFqzRvR
EnkjWiNvRA0axuxUM9x4I9aJVmGuZow4FjxU5jXS1Nr8jZR4aoT5mjFSNWSh8NoCpZPmjRS59HTB/qC1
3Q3htwE0beQ1Gy96Q/3cBMw8dVQJ9wfQ9B4p9dbSwfEhNxJanWUFW4VxN3HrRrrnXaQftHET0AupnpyL
HmHsTdy6kQp/Ay1sfuYmdg/3SddnFMal41aNeJYlKnbXcBNQc8BB9qkuYWw6ZBkxjNqoTDKk8Ixhn868
EAt7APrCQ9zE2fk5M6UXxmWCLCNogSLtHx2wpWEQ5ogo9lTT0ckxzw1EQ2RiH5AoLhMUNQJV9lupJ+IW
5iXTOesg07uWWBaRzmci15JPGJsJsowUsSXQNu5O0D7u4d8GNL8RpUq2gUV5ybwcMNPqzhees7G3ReVv
TMK4TJFlpCfiovaZ3gT1Y83UEnTzoqASdy15o6l5cdxLEpV6DLFoosaRTmoL9whjM0WWERGFrmq2Yc94
Yf2zo9QQtAvjQF2gifyRII89ZTnXjR3ZoJgRI9uoI4sfL4s7O2Wf+PXFFTPTJywG8kcCZA42CeOyQTEj
7qiPdGwMj8v6vpNaw90pcVhCFraU4sISQ+7fcdmimBGADbyyvc4L3PqxK9zAuPZ17xuPwZT7atCcEiMH
RY10zTnJ4LfzIqFyyfzH3ORm7fW5VBe7S7z9og0n/w+5KGoEYOMeHP/khYZW50ifNMliIvjAHnzQ8ekv
KvHmvsnjKG4k+WwBYZbCTIV7ha6qRGeTZobZiNKSki8XxY0AtOKLi8vTnvPTID8MYQbDUBhXumdNtqhi
BCe5yZUZXjBmKRycdJKRdg6/82uLmytU8Tb90z8bVDHiXPSyA5KZFw1hU+PAFJd+wManA1GuXFQxAtBm
N/e3eeFoszjCQmgEpT62rAQ5uaCakcsHXwcvPlloBGgIopxcUM0IwEEJbTZZmJzjXUxJVDWCV6BS7AQI
Ta9HqHoo+zckmaCqEbTXIqc+8XJN5zXxV6PC2BxR1wjDNtmWeLmGb0gUowSqG/lX5I1ojbwRrfF/GLkX
P+G4Nz+qyUsTKij4DaqNxnRXLBhpAAAAAElFTkSuQmCC
iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABGdBTUEAALGPC/xhBQAAA5JJREFUaEPt
mn1TElEUxv12fZVm+g7NNNMokqQ1KouACBKjKIovwAJLaqRGgCkpooiYjpbmS5omyctpzjV2bK8Ku+7m
UpyZ3z/sc5jz7N17OHeHhoZ6qDQe2h4/eOR4wtQSWLPQRwNesM+7YDg1XhNgrViz0AcxgoLARqgmwFrr
RtRE3YjaqBtRAmvcCYaZ3luxzw9SeYhqjHjW/NASMMHi9uqtNPsNVC6iGiOd0zZIbq9BpVC9kSYfAyUo
kWK3j3aplaiJFXk174LxxAQxgWaeejqpvVETe0Qb6ILT3BkxkthKgWHGTmkqce9GRld9wEw5+D3QxvWA
N+OndJW4dyPtYSus7X0iJo7OTkAXMlGaarhXI/4NDjSsgV+N/qgHnIsjlK4aJBkxRhzQwhkpnnFGcC5V
X4gt7oRgcpqYKBSLoGEZSlMtkoxgC7wuTs5PQRcyUvqb0Pj1cH6RI7nRbALMEQelqRZZjWB0TNhhLM1S
OULcKQ+Y3w7webqgGXzrQUpXLZKMNLEMuOZYnqE5P1kNjNXdLHSErVSOkBeTFtg6/Exydo/3ofW1mdKI
QZKRsbQPhpbHeXri/TAQY/m728x2QSBL55Vh1znQ+o28vnfWDa7kGKUTgyQj19Ho00OhWCCFTaQiYI05
KU2Z7mgfhNMxos0XCzeOHWKQzYgp4oDZzIfL4gp50N5SnManh4tCnmjD6ShYYn2URiyyGWGzQdAFTPzj
Yn/nhsHkKKXDR8g26+Z1+IhhrlAnFtmMILiBNw92SIH734+u3cD42Zfjr0SDU+7LKQulkYKsRkZWvGAM
O/m73cpZ/pib2PUgPOe6+evYfrENC79HCrIaQXDjnuZ+kEITWyvAXJlkcSJ4n02Qa7n8T2gO3LyPxCK7
katnCwycpXCmwmuNvk6+s3HLM2CLD1D5UpHdCIKtuFS6PO15P06RwxDOYDgUlqPSb41YFDGCJ7mFzWVS
MM5SeHDScSY4PPtGPsvsbUL7m8q//mJQxIg3E4A2zsLffdzUeGAqBzPpINOBMO8uKGIEwTa7d3JACsc2
i0dYDGwE2mAXpb8rihm5/OEb5lehHNgIsCEI9XdFMSMIHpSwzV4NnJzLXUxOFDWCr0C53ydAjKWdNOin
xb8hqQZFjWB7bfIy/Ms1XcBMXo1SOhlQ1shGCBwLLv7lGq6Q8LpcKG7kb1E3ojbqRtTG/2Hkn/gLxz/z
p5p6qCR+AaqNxnTXLPGFAAAAAElFTkSuQmCC
</value>
</data>
<metadata name="timer1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">

View File

@@ -48,5 +48,11 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="StateMachine.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AGVLogic\AGVNavigationCore\AGVNavigationCore.csproj">
<Project>{c5f7a8b2-8d3e-4a1b-9c6e-7f4d5e2a9b1c}</Project>
<Name>AGVNavigationCore</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -53,8 +53,8 @@ namespace arDev
else sb.Append("0S");
sb.Append("00");
sb.Append("0000"); //재기동시간
if (opt == eStopOpt.MarkStop)
VAR.BOOL[eVarBool.NEXTSTOP_MARK] = true;
//if (opt == eStopOpt.MarkStop)
// VAR.BOOL[eVarBool.NEXTSTOP_MARK] = true;
//동작중이면 이 멈춤 명령을 기록으로 남긴다 230116
if (this.system1.agv_run)

View File

@@ -11,15 +11,9 @@ namespace arDev
{
public class BMS : arRS232
{
/// <summary>
/// 데이터조회간격(초)
/// </summary>
public float ScanInterval { get; set; }
public BMS()
{
ScanInterval = 1000;
MinRecvLength = 34;
}
@@ -29,7 +23,6 @@ namespace arDev
public event EventHandler<BMSInformationEventArgs> BMSDataReceive;
public event EventHandler<BMSCelvoltageEventArgs> BMSCellDataReceive;
protected override bool CustomParser(byte[] buf, out byte[] remainBuffer)
{
List<byte> remain = new List<byte>();
@@ -91,10 +84,7 @@ namespace arDev
}
bool Recv0 = false;
int Recv0Cnt = 0;
bool Recv1 = false;
int Recv1Cnt = 0;
public event EventHandler<ChargetDetectArgs> ChargeDetect;
public override bool ProcessRecvData(byte[] data)
{
@@ -112,29 +102,19 @@ namespace arDev
return false;
}
if (VerifyChecksum(data) == false)
{
RaiseMessage(MessageType.Error, "Checksum error");
return false;
}
if (data[1] == 0x03)
if (QueryIndex == 0)
{
return ParseBMSInfo();
}
else if (data[1] == 0x04)
else
{
return ParseBMSCellVoltage();
}
else return false;
}
bool ParseBMSCellVoltage()
{
if (UseCmdLogging == true)
RaiseMessage(MessageType.Recv, ByteListToHexString(LastReceiveBuffer.ToList()));
//var i = 0;
var BatteryCell_Checksum = 0xffff;// - (LastReceiveBuffer[i + 3] + LastReceiveBuffer[i + 4] + LastReceiveBuffer[i + 5] + LastReceiveBuffer[i + 6] + LastReceiveBuffer[i + 7] + LastReceiveBuffer[i + 8] + LastReceiveBuffer[i + 9] + LastReceiveBuffer[i + 10] + LastReceiveBuffer[i + 11] + LastReceiveBuffer[i + 12] + LastReceiveBuffer[i + 13] + LastReceiveBuffer[i + 14] + LastReceiveBuffer[i + 15] + LastReceiveBuffer[i + 16] + LastReceiveBuffer[i + 17] + LastReceiveBuffer[i + 18] + LastReceiveBuffer[i + 19])) + 1;
for (int i = 3; i < 20; i++)
@@ -170,6 +150,7 @@ namespace arDev
try
{
BMSCellDataReceive?.Invoke(this, new BMSCelvoltageEventArgs(v1, v2, v3, v4, v5, v6, v7, v8));
Current_CellTime = DateTime.Now;
return true;
}
catch (Exception ex)
@@ -182,62 +163,8 @@ namespace arDev
}
public static bool VerifyChecksum(byte[] dataBytes)
{
// 길이 바이트 (byte[3])
byte length = dataBytes[3];
if (dataBytes[1] == 0x03)
{
// 데이터 바이트 (byte[4] ~ byte[30])
byte[] data = dataBytes.Skip(4).Take(27).ToArray();
// 길이와 데이터의 합을 계산
ushort sum = (ushort)(length + data.Sum(b => b));
// 반전시키고 높은 위치부터 낮은 위치로 더한 후 1을 더함
ushort checksum = (ushort)((~sum + 1) & 0xFFFF);
// 받은 체크섬 (byte[31] ~ byte[32])
ushort receivedChecksum = (ushort)((dataBytes[31] << 8) | dataBytes[32]);
//252 1: 64514
// 계산된 체크섬과 받은 체크섬을 비교
return checksum == receivedChecksum;
}
else if (dataBytes[1] == 0x04)
{
// 데이터 바이트 (byte[4] ~ byte[30])
byte[] data = dataBytes.Skip(4).Take(16).ToArray();
// 길이와 데이터의 합을 계산
ushort sum = (ushort)(length + data.Sum(b => b));
// 반전시키고 높은 위치부터 낮은 위치로 더한 후 1을 더함
ushort checksum = (ushort)((~sum + 1) & 0xFFFF);
// 받은 체크섬 (byte[31] ~ byte[32])
ushort receivedChecksum = (ushort)((dataBytes[20] << 8) | dataBytes[21]);
//252 1: 64514
// 계산된 체크섬과 받은 체크섬을 비교
return checksum == receivedChecksum;
}
return false;
}
bool ParseBMSInfo()
{
if(UseCmdLogging == true)
RaiseMessage(MessageType.Recv, ByteListToHexString(LastReceiveBuffer.ToList()));
//전압확인
UInt16 batH = (UInt16)LastReceiveBuffer[4];
UInt16 batL = (UInt16)LastReceiveBuffer[5];
@@ -265,7 +192,6 @@ namespace arDev
float levraw = (float)(Current_Amp / (Current_MaxAmp * 1.0));
Current_Level = (float)(levraw * 100.0);// (float)(map(remain, 0, total, 0, 100));
Current_LevelA = LastReceiveBuffer[23]; //<- 23번자료는 byte이므로 소수점이잇는 데이터로 직접 계산한다
Current_DataTime = DateTime.Now;
//250620 jwlee 추가
@@ -282,6 +208,7 @@ namespace arDev
try
{
BMSDataReceive?.Invoke(this, new BMSInformationEventArgs(Current_Volt, Current_Amp, Current_MaxAmp, Current_Level, Changed));
Current_DataTime = DateTime.Now;
return true;
}
catch (Exception ex)
@@ -424,13 +351,10 @@ namespace arDev
public int Current_MaxAmp { get; set; }
public DateTime Current_DataTime = DateTime.Parse("1982-11-23");
public DateTime Current_CellTime = DateTime.Parse("1982-11-23");
public int QueryIndex { get; set; } = 0;
public int CMDOutCnt { get; set; } = 3;
public bool UseCmdLogging { get; set; } = false;
/// <summary>
/// 상태읽기와 전압읽기명령을 반복합니다
/// </summary>
@@ -446,21 +370,9 @@ namespace arDev
return true;
}
else
{
if (Recv0Cnt > CMDOutCnt)
{
Recv0Cnt = 0;
RaiseMessage(MessageType.Error, "0 명령 Count Out");
QueryIndex = 1;
Recv1 = false;
return true;
}
else
{
return SendQuery_ReadStatue();
}
}
}
else
{
@@ -471,26 +383,14 @@ namespace arDev
return true;
}
else
{
if (Recv1Cnt > CMDOutCnt)
{
Recv1Cnt = 0;
RaiseMessage(MessageType.Error, "1 명령 Count Out");
QueryIndex = 0;
Recv0 = false;
return true;
}
else
{
return SendQuery_ReadCellvoltage();
}
}
}
}
public Boolean SendQuery_ReadStatue()
{
Recv0Cnt++;
Recv0 = false;
var cmd = new List<byte>();
cmd.Add(0xDD);
@@ -501,16 +401,12 @@ namespace arDev
cmd.Add(0xFD);
cmd.Add(0x77);
cmd.Add(0x0D);
if (UseCmdLogging == true)
RaiseMessage(MessageType.Normal, ByteListToHexString(cmd));
return WriteData(cmd.ToArray());
}
public Boolean SendQuery_ReadCellvoltage()
{
Recv1Cnt++;
Recv1 = false;
var cmd = new List<byte>();
cmd.Add(0xDD);
@@ -521,21 +417,8 @@ namespace arDev
cmd.Add(0xFC);
cmd.Add(0x77);
cmd.Add(0x0D);
if (UseCmdLogging == true)
RaiseMessage(MessageType.Normal, ByteListToHexString(cmd));
return WriteData(cmd.ToArray());
}
public string ByteListToHexString(List<byte> byteList)
{
StringBuilder hex = new StringBuilder(byteList.Count * 2);
foreach (byte b in byteList)
{
hex.AppendFormat("{0:X2} ", b);
}
return hex.ToString();
}
}
}

View File

@@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>arDevice</RootNamespace>
<AssemblyName>BMS</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
@@ -46,7 +46,12 @@
<Compile Include="BMSInformationEventArgs.cs" />
<Compile Include="BMS.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RS232.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CommData\CommData.csproj">
<Project>{14e8c9a5-013e-49ba-b435-efefc77dd623}</Project>
<Name>CommData</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
public abstract partial class arRS232 : IDisposable
{
@@ -15,7 +15,7 @@ public abstract partial class arRS232 : IDisposable
/// 최종 전송 메세지
/// </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; }
@@ -26,7 +26,10 @@ public abstract partial class arRS232 : IDisposable
/// 메세지 수신시 사용하는 내부버퍼
/// </summary>
protected List<byte> _buffer = new List<byte>();
/// <summary>
/// 데이터조회간격(초)
/// </summary>
public float ScanInterval { get; set; }
// public byte[] LastRecvData;
public string LastRecvString
@@ -98,7 +101,7 @@ public abstract partial class arRS232 : IDisposable
{
_device = new System.IO.Ports.SerialPort();
this.BaudRate = 9600;
ScanInterval = 10;
_device.DataReceived += barcode_DataReceived;
_device.ErrorReceived += this.barcode_ErrorReceived;
_device.WriteTimeout = 5000;
@@ -292,11 +295,11 @@ public abstract partial class arRS232 : IDisposable
}
catch (Exception ex)
{
if (IsOpen)
{
//_device.DiscardInBuffer();
//_device.DiscardOutBuffer();
}
//if (IsOpen)
//{
// //_device.DiscardInBuffer();
// //_device.DiscardOutBuffer();
//}
errorMessage = ex.Message;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
}
@@ -413,7 +416,7 @@ public abstract partial class arRS232 : IDisposable
protected abstract bool CustomParser(byte[] buf, out byte[] remainBuffer);
/// <summary>
/// 데이터수신시간이 설정값보다 x 2.5를 초과하면 false 반환
/// 포트가 열려있거나 데이터 수신시간이 없는경우 false 반환합니다
/// </summary>
public Boolean IsValid
{
@@ -422,10 +425,16 @@ public abstract partial class arRS232 : IDisposable
if (IsOpen == false) return false;
if (lastRecvTime.Year == 1982) return false;
var ts = DateTime.Now - lastRecvTime;
if (ts.TotalSeconds > 5) return false;
if (ts.TotalSeconds > (this.ScanInterval * 2.5)) return false;
return true;
}
}
protected string ByteListToHexString(List<byte> src)
{
return string.Join(" ", src.Select(t => t.ToString("X2")));
}
protected bool WriteData(string cmd)
{
return WriteData(System.Text.Encoding.Default.GetBytes(cmd));
@@ -483,3 +492,4 @@ public abstract partial class arRS232 : IDisposable
return bRet;
}
}

View File

@@ -16,7 +16,7 @@ namespace arDev
/// 최종 전송 메세지
/// </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; }
@@ -27,7 +27,10 @@ namespace arDev
/// 메세지 수신시 사용하는 내부버퍼
/// </summary>
protected List<byte> _buffer = new List<byte>();
/// <summary>
/// 데이터조회간격(초)
/// </summary>
public float ScanInterval { get; set; }
// public byte[] LastRecvData;
public string LastRecvString
@@ -99,7 +102,7 @@ namespace arDev
{
_device = new System.IO.Ports.SerialPort();
this.BaudRate = 9600;
ScanInterval = 10;
_device.DataReceived += barcode_DataReceived;
_device.ErrorReceived += this.barcode_ErrorReceived;
_device.WriteTimeout = 5000;
@@ -293,11 +296,11 @@ namespace arDev
}
catch (Exception ex)
{
if (IsOpen)
{
//_device.DiscardInBuffer();
//_device.DiscardOutBuffer();
}
//if (IsOpen)
//{
// //_device.DiscardInBuffer();
// //_device.DiscardOutBuffer();
//}
errorMessage = ex.Message;
this.Message?.Invoke(this, new MessageEventArgs(ex.Message, true));
}
@@ -414,7 +417,7 @@ namespace arDev
protected abstract bool CustomParser(byte[] buf, out byte[] remainBuffer);
/// <summary>
/// 데이터수신시간이 설정값보다 x 2.5를 초과하면 false 반환
/// 포트가 열려있거나 데이터 수신시간이 없는경우 false 반환합니다
/// </summary>
public Boolean IsValid
{
@@ -423,7 +426,7 @@ namespace arDev
if (IsOpen == false) return false;
if (lastRecvTime.Year == 1982) return false;
var ts = DateTime.Now - lastRecvTime;
if (ts.TotalSeconds > 5) return false;
if (ts.TotalSeconds > (this.ScanInterval * 2.5)) return false;
return true;
}
}

Submodule Cs_HMI/SubProject/EnigProtocol updated: 77a9d40662...070aa848c9

View File

@@ -102,7 +102,7 @@
//
// logTextBox1
//
this.logTextBox1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(24)))), ((int)(((byte)(24)))), ((int)(((byte)(24)))));
this.logTextBox1.BackColor = System.Drawing.Color.WhiteSmoke;
this.logTextBox1.ColorList = new arCtl.sLogMessageColor[0];
this.logTextBox1.DateFormat = "yy-MM-dd HH:mm:ss";
this.logTextBox1.DefaultColor = System.Drawing.Color.LightGray;

View File

@@ -18,7 +18,7 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\..\..\..\..\..\..\Amkor\AGV2\Test\BMS\</OutputPath>
<OutputPath>..\..\..\..\..\..\Amkor\AGV4\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>

View File

@@ -50,6 +50,9 @@
this.button14 = new System.Windows.Forms.Button();
this.button15 = new System.Windows.Forms.Button();
this.button16 = new System.Windows.Forms.Button();
this.button17 = new System.Windows.Forms.Button();
this.button18 = new System.Windows.Forms.Button();
this.button19 = new System.Windows.Forms.Button();
this.panel1 = new System.Windows.Forms.Panel();
this.rt0 = new System.Windows.Forms.RichTextBox();
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
@@ -58,9 +61,6 @@
this.rt3 = new System.Windows.Forms.RichTextBox();
this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel();
this.logTextBox2 = new arCtl.LogTextBox();
this.button17 = new System.Windows.Forms.Button();
this.button18 = new System.Windows.Forms.Button();
this.button19 = new System.Windows.Forms.Button();
this.tableLayoutPanel1.SuspendLayout();
this.panel1.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
@@ -130,7 +130,7 @@
//
// logTextBox1
//
this.logTextBox1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(24)))), ((int)(((byte)(24)))), ((int)(((byte)(24)))));
this.logTextBox1.BackColor = System.Drawing.Color.WhiteSmoke;
this.logTextBox1.ColorList = new arCtl.sLogMessageColor[0];
this.logTextBox1.DateFormat = "ss.fff";
this.logTextBox1.DefaultColor = System.Drawing.Color.LightGray;
@@ -338,6 +338,39 @@
this.button16.UseVisualStyleBackColor = true;
this.button16.Click += new System.EventHandler(this.button16_Click);
//
// button17
//
this.button17.Dock = System.Windows.Forms.DockStyle.Fill;
this.button17.Location = new System.Drawing.Point(3, 168);
this.button17.Name = "button17";
this.button17.Size = new System.Drawing.Size(130, 51);
this.button17.TabIndex = 17;
this.button17.Text = "Speed L";
this.button17.UseVisualStyleBackColor = true;
this.button17.Click += new System.EventHandler(this.button17_Click);
//
// button18
//
this.button18.Dock = System.Windows.Forms.DockStyle.Fill;
this.button18.Location = new System.Drawing.Point(139, 168);
this.button18.Name = "button18";
this.button18.Size = new System.Drawing.Size(130, 51);
this.button18.TabIndex = 17;
this.button18.Text = "Speed M";
this.button18.UseVisualStyleBackColor = true;
this.button18.Click += new System.EventHandler(this.button18_Click);
//
// button19
//
this.button19.Dock = System.Windows.Forms.DockStyle.Fill;
this.button19.Location = new System.Drawing.Point(275, 168);
this.button19.Name = "button19";
this.button19.Size = new System.Drawing.Size(130, 51);
this.button19.TabIndex = 17;
this.button19.Text = "Speed H";
this.button19.UseVisualStyleBackColor = true;
this.button19.Click += new System.EventHandler(this.button19_Click);
//
// panel1
//
this.panel1.Controls.Add(this.comboBox1);
@@ -423,7 +456,7 @@
//
// logTextBox2
//
this.logTextBox2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(24)))), ((int)(((byte)(24)))), ((int)(((byte)(24)))));
this.logTextBox2.BackColor = System.Drawing.Color.WhiteSmoke;
this.logTextBox2.ColorList = new arCtl.sLogMessageColor[0];
this.logTextBox2.DateFormat = "ss.fff";
this.logTextBox2.DefaultColor = System.Drawing.Color.LightGray;
@@ -441,39 +474,6 @@
this.logTextBox2.TabIndex = 4;
this.logTextBox2.Text = "";
//
// button17
//
this.button17.Dock = System.Windows.Forms.DockStyle.Fill;
this.button17.Location = new System.Drawing.Point(3, 168);
this.button17.Name = "button17";
this.button17.Size = new System.Drawing.Size(130, 51);
this.button17.TabIndex = 17;
this.button17.Text = "Speed L";
this.button17.UseVisualStyleBackColor = true;
this.button17.Click += new System.EventHandler(this.button17_Click);
//
// button18
//
this.button18.Dock = System.Windows.Forms.DockStyle.Fill;
this.button18.Location = new System.Drawing.Point(139, 168);
this.button18.Name = "button18";
this.button18.Size = new System.Drawing.Size(130, 51);
this.button18.TabIndex = 17;
this.button18.Text = "Speed M";
this.button18.UseVisualStyleBackColor = true;
this.button18.Click += new System.EventHandler(this.button18_Click);
//
// button19
//
this.button19.Dock = System.Windows.Forms.DockStyle.Fill;
this.button19.Location = new System.Drawing.Point(275, 168);
this.button19.Name = "button19";
this.button19.Size = new System.Drawing.Size(130, 51);
this.button19.TabIndex = 17;
this.button19.Text = "Speed H";
this.button19.UseVisualStyleBackColor = true;
this.button19.Click += new System.EventHandler(this.button19_Click);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);

View File

@@ -18,7 +18,7 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\..\..\..\..\..\..\Amkor\AGV2\</OutputPath>
<OutputPath>..\..\..\..\..\..\Amkor\AGV4\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
</configuration>

View File

@@ -0,0 +1,561 @@
using System;
using System.Linq;
using System.ComponentModel;
using System.Drawing.Design;
using System.Runtime.CompilerServices;
using System.Management;
using AR;
namespace Project
{
public class MyUITypeEditor : UITypeEditor
{
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
using (var f = new System.Windows.Forms.Form
{
WindowState = System.Windows.Forms.FormWindowState.Normal,
StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen,
Size = new System.Drawing.Size(800, 400),
MaximizeBox = false,
MinimizeBox = false,
Text = "Select Port",
FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog
})
{
var lst = new System.Windows.Forms.ListBox
{
Font = new System.Drawing.Font("Consolas", 15, System.Drawing.FontStyle.Bold),
Dock = System.Windows.Forms.DockStyle.Fill,
};
lst.DoubleClick += (s1, e1) =>
{
if (lst.SelectedItem != null) f.DialogResult = System.Windows.Forms.DialogResult.OK;
};
using (var searcher = new ManagementObjectSearcher("SELECT * FROM WIN32_SerialPort"))
{
var portnames = System.IO.Ports.SerialPort.GetPortNames().OrderBy(t => t);
var ports = searcher.Get().Cast<ManagementBaseObject>().ToList();
foreach (var port in portnames)
{
var desc = "";
var portInfo = ports.Where(t => t["DeviceId"].ToString() == port).FirstOrDefault();
if (portInfo != null) desc = portInfo["Caption"].ToString();
lst.Items.Add(string.Format("{0} - {1}", port, desc));
}
}
f.Controls.Add(lst);
if (f.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
var name = lst.SelectedItem.ToString().Split('-');
return name[0].Trim();
}
else
{
return base.EditValue(context, provider, value);
}
}
}
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
}
public class CSetting : AR.Setting
{
#region "log"
[Category("System Log"), DisplayName("Digital Input/Output"),
Description("입/출력 장치의 상태 변화를 기록 합니다.")]
public Boolean Log_IO { get; set; }
[Category("System Log"), DisplayName("Network Alive Check(Ping)"),
Description("서버와의 통신검사 로그를 기록 합니다")]
public Boolean Log_Ping { get; set; }
[Category("System Log"), DisplayName("S/M Step Change"),
Description("상태머신의 변화 상태를 기록 합니다.")]
public Boolean Log_StepChange { get; set; }
[Category("System Log"), DisplayName("Socket Recv Message"),
Description("서버 수신 메세지를 기록 합니다.")]
public Boolean LOg_SocketRecv { get; set; }
#endregion
#region "Communication"
[Category("Commnunication Setting"), DisplayName("XBee PortName"), Editor(typeof(MyUITypeEditor), typeof(UITypeEditor))]
public string Port_XBE { get; set; }
[Category("Commnunication Setting"), DisplayName("RFID PortName"), Editor(typeof(MyUITypeEditor), typeof(UITypeEditor))]
public string Port_AGV { get; set; }
[Category("Commnunication Setting"), DisplayName("Xbee ID"), Editor(typeof(MyUITypeEditor), typeof(UITypeEditor))]
public byte XBE_ID { get; set; }
[Category("Commnunication Setting"), DisplayName("BMS PortName"), Editor(typeof(MyUITypeEditor), typeof(UITypeEditor))]
public string Port_BAT { get; set; }
public int ChargerID { get; set; }
public int Baud_AGV { get; set; }
public int Baud_BAT { get; set; }
//public int Baud_PLC { get; set; }
public int Baud_XBE { get; set; }
//public int QueryInterval_BAT { get; set; }
#endregion
#region "Debug"
[Category("Developer Setting"), DisplayName("Listening Port"),
Description("서버와의 통신을 위한 대기 포트(TCP) - 기본값 : 7979")]
public int listenPort { get; set; }
[Category("Developer Setting")]
public Boolean UseDebugMode { get; set; }
#endregion
#region "Count Reset Setting"
[Category("Count Reset Setting"), DisplayName("A/M Clear Enable"),
Description("오전 초기화 여부\n작업수량을 초기화 하려면 True 로 설정하세요")]
public Boolean datetime_Check_1 { get; set; }
[Category("Count Reset Setting"), DisplayName("P/M Clear Enable"),
Description("오후 초기화 여부\n작업수량을 초기화 하려면 True 로 설정하세요")]
public Boolean datetime_Check_2 { get; set; }
[Category("Count Reset Setting"), DisplayName("A/M Clear Time(HH:mm)"),
Description("오전 초기화 시간(시:분)\n예) 09:00")]
public string datetime_Reset_1 { get; set; }
[Category("Count Reset Setting"), DisplayName("P/M Clear Time(HH:mm)"),
Description("오후 초기화 시간(시:분)\n예) 19:00")]
public string datetime_Reset_2 { get; set; }
#endregion
#region "general"
[Category("General Setting"), DisplayName("Enable Popup Message"),
Description("오류 발생시 별도의 메세지 창으로 표시합니다. 사용하지 않을 경우에는 우측 상단의 결과창의 메세지를 확인합니다.")]
public Boolean ShowPopUpMessage { get; set; }
[Category("General Setting"), DisplayName("Asset Number"),
Description("자산번호(서버에 값 전송 시 사용됩니다)")]
public string Asset { get; set; }
[Category("General Setting"),
Description("데이터 자동 소거 기간(일) 비활성=0")]
public int AutoDeleteDay { get; set; }
[Category("General Setting"),
Description("데이터 자동 소거 조건(남은용량 %)")]
public int AutoDeleteThreshold { get; set; }
[Category("General Setting"), PasswordPropertyText(true)]
public string Password_Setup { get; set; }
[Category("General Setting"), Browsable(false)]
public string Language { get; set; }
[Category("General Setting"), DisplayName("Full Screen Window State"),
Description("화면을 전체화면으로 사용 합니다.")]
public Boolean FullScreen { get; set; }
public bool DetectManualCharge { get; set; }
public Boolean StartLog { get; set; }
#endregion
#region "TAGS"
public int TAGPOT { get; set; }
public int TAGNOT { get; set; }
public int TAG_F2_F3 { get; set; }
public int TAG_QC_F1 { get; set; }
public int TAG_F1_F2 { get; set; }
public int TAG_F3_F4 { get; set; }
public int TAG_F4_F5 { get; set; }
public int TAG_QA_QC { get; set; }
public int TAGQAB { get; set; }
public int TAGQAA { get; set; }
public int TAGQCB { get; set; }
public int TAGQCA { get; set; }
public int TAGF1A { get; set; }
public int TAGF2A { get; set; }
public int TAGF3A { get; set; }
public int TAGF4A { get; set; }
public int TAGF5A { get; set; }
public int TAGF1B { get; set; }
public int TAGF2B { get; set; }
public int TAGF3B { get; set; }
public int TAGF4B { get; set; }
public int TAGF5B { get; set; }
#endregion
#region "Charge"
public int chargerpos { get; set; }
public int ChargetWaitSec { get; set; }
public int ChargeEmergencyLevel { get; set; }
public int ChargeMaxLevel { get; set; }
public int ChargeStartLevel { get; set; }
public Boolean Enable_AutoCharge { get; set; }
public int ChargeRetryTerm { get; set; }
//public int ChargeIdleInterval { get; set; }
public int ChargeMaxTime { get; set; }
public int ChargeSearchTime { get; set; }
#endregion
#region "AGV"
public bool AutoModeOffAndClearPosition { get; set; }
public string musicfile { get; set; }
/// <summary>
/// FVI로 가능 방향이 Backward 인가?
/// Forward 방향이라면 false 를 한다
/// </summary>
public Boolean AGV_Direction_FVI_Backward { get; set; }
public Boolean Enable_Speak { get; set; }
//public Boolean Disable_BMS { get; set; }
//public Boolean Log_BMS_Tx { get; set; }
//public Boolean Log_BMS_Rx { get; set; }
//public double ChargeLimitLow { get; set; }
//public double ChargeLimitHigh { get; set; }
public string AGV_PANID { get; set; }
public string AGV_CHANNEL { get; set; }
public string AGV_ADDRESS { get; set; }
public int SCK { get; set; }
public int SSK { get; set; }
public int STT { get; set; }
public int SAD { get; set; }
public byte ZSpeed { get; set; }
public int SPD_L { get; set; }
public int SPD_M { get; set; }
public int SPD_H { get; set; }
public int SPD_DRIVE { get; set; }
public int SPD_S { get; set; }
public int SPD_R { get; set; }
public int PID_PH { get; set; }
public int PID_PM { get; set; }
public int PID_PL { get; set; }
public int PID_IH { get; set; }
public int PID_IM { get; set; }
public int PID_IL { get; set; }
public int PID_DH { get; set; }
public int PID_DM { get; set; }
public int PID_DL { get; set; }
public int PID_PS { get; set; }
public int PID_IS { get; set; }
public int PID_DS { get; set; }
public double WheelSpeedCharge { get; set; }
public byte HomePositionValue { get; set; }
public byte HomeKitNo { get; set; }
public Single interval_xbe { get; set; }
public int interval_bms { get; set; }
public int doorSoundTerm { get; set; }
public int musicvol { get; set; }
public bool Enable_Music { get; set; }
#endregion
[Category("Report"),
Description("상태기록시 장비 식별코드(4자리)"),
DisplayName("M/C ID")]
public string MCID { get; set; }
[Category("Report"),
Description("작업 기록을 장비기술(EE) Database 에 기록 합니다. 원격으로 장비 현황을 모니터링 할 수 있습니다"),
DisplayName("SAVE EE-DB")]
public Boolean Save_EEDatabase { get; set; }
[Category("Report"), Description("상태값을 전송하는 간격(초)")]
public int StatusInterval { get; set; }
[Category("AutoReboot"),
DisplayName("자동재부팅시간"),
Description("기본값 14:00:00~14:05:00 , 재부팅은 장비가 대기(IDLE)일 때에만 동작하며 지정 시간 범위를 벗어나면 동작하지 않습니다")]
public String AutoRebootTimeStart { get; set; }
[Category("AutoReboot")]
public string AUtoRebootLastTime { get; set; }
public bool SetAutoModeOn { get; set; }
public override void AfterLoad()
{
if (StatusInterval < 10) StatusInterval = 300; //5분간격
if (SAD == 0) SAD = 999;
if (TAGF1A == 0)
{
TAGNOT = 9000;
TAGQAB = 9200;
TAGQCB = 9300;
TAGF1B = 9400;
TAGF2B = 9500;
TAGF3B = 9600;
TAGF4B = 9700;
TAGF5B = 9800;
TAGQAA = 9201;
TAGQCA = 9301;
TAGF1A = 9401;
TAGF2A = 9501;
TAGF3A = 9601;
TAGF4A = 9701;
TAGF5A = 9801;
TAGPOT = 9900;
TAG_QA_QC = 9250;
TAG_QC_F1 = 9350;
TAG_F1_F2 = 9450;
TAG_F2_F3 = 9550;
TAG_F3_F4 = 9650;
TAG_F4_F5 = 9750;
}
if(TAG_F4_F5 == 0)
{
TAG_F4_F5 = 9750;
TAGF5B = 9800;
TAGF5A = 9801;
}
if (SCK == 0) SCK = 10;
if (SSK == 0) SSK = 10;
if (STT == 0) STT = 30;
if (ChargerID == 0) ChargerID = 203;
if (ChargerID < 200) ChargerID += 200;
//자동충전요건
if (ChargetWaitSec == 0) ChargetWaitSec = 3;
if (ChargeStartLevel == 0) ChargeStartLevel = 85;
if (ChargeMaxLevel == 0) ChargeMaxLevel = 85;
if (ChargeEmergencyLevel == 0) ChargeEmergencyLevel = 30;
if (interval_bms == 0) interval_bms = 10;
//충전은 10분간격으로 재시도 한다
if (ChargeRetryTerm == 0) ChargeRetryTerm = 600;
if (doorSoundTerm == 0) doorSoundTerm = 15; //기본 15초
if (ChargeSearchTime == 0) ChargeSearchTime = 25;
//최대 충전진행 시간(기본 1시간)
if (ChargeMaxTime == 0) ChargeMaxTime = 3600;
// if (interval_iostate == 0 || interval_iostate == 255) interval_iostate = 100;
if (ZSpeed == 0) ZSpeed = 20;
if (interval_xbe == 0) interval_xbe = 5.0f;
if (HomePositionValue == 0) HomePositionValue = 4;
if (HomeKitNo == 0) HomeKitNo = 2;
if (datetime_Reset_1 == "") datetime_Reset_1 = "00:00";
if (datetime_Reset_2 == "") datetime_Reset_2 = "00:00";
if (SPD_H == 0)
{
SPD_DRIVE = 200;
SPD_H = 110;
SPD_M = 70;
SPD_L = 30;
SPD_S = 61;
SPD_R = 70;
}
if (PID_PH == 0)
{
PID_PH = 180; PID_PM = 240; PID_PL = 260;
PID_IH = 610; PID_IM = 640; PID_IL = 660;
PID_DH = 110; PID_DM = 140; PID_DL = 160;
PID_PS = 200; PID_IS = 620; PID_DS = 120;
}
if (WheelSpeedCharge == 0) WheelSpeedCharge = 10;
if (AutoDeleteThreshold == 0) AutoDeleteThreshold = 20;
if (Asset == "") Asset = "DEV_SPLIT";
if (listenPort == 0) listenPort = 7979;
if (Port_XBE == "") Port_XBE = "COM8";
if (Language.isEmpty()) Language = "Kor";
if (Password_Setup.isEmpty()) Password_Setup = "0000";
if (musicfile.isEmpty()) musicfile = UTIL.CurrentPath + "music.mp3";
if (musicvol == 0) musicvol = 50;
if (string.IsNullOrEmpty(Port_AGV)) Port_AGV = "COM1";
// if (string.IsNullOrEmpty(Port_PLC)) Port_PLC = "COM2";
if (string.IsNullOrEmpty(Port_XBE)) Port_XBE = "COM4";
if (string.IsNullOrEmpty(Port_BAT)) Port_BAT = "COM7";
if (Baud_AGV == 0) Baud_AGV = 57600;
//if (Baud_PLC == 0) Baud_PLC = 19200;
if (Baud_XBE == 0) Baud_XBE = 9600;
if (Baud_BAT == 0) Baud_BAT = 9600;
}
public override void AfterSave()
{
//throw new NotImplementedException();
}
public void CopyTo(CSetting dest)
{
//이곳의 모든 쓰기가능한 속성값을 대상에 써준다.
Type thClass = this.GetType();
foreach (var method in thClass.GetMethods())
{
//var parameters = method.GetParameters();
if (!method.Name.StartsWith("get_")) continue;
string keyname = method.Name.Substring(4);
string methodName = method.Name;
object odata = GetType().GetMethod(methodName).Invoke(this, null);
var wMethod = dest.GetType().GetMethod(Convert.ToString("set_") + keyname);
if (wMethod != null) wMethod.Invoke(dest, new object[] { odata });
}
}
}
public class CounterSetting : AR.Setting, INotifyPropertyChanged
{
public DateTime CountReset { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private int countUp1 = 0;
private int countUp2 = 0;
private int countUp3 = 0;
private int countUp4 = 0;
// private int countUp5 = 0;
private int countchgaerr = 0;
private int countchga = 0;
private int countchgm = 0;
//private int countdn = 0;
private int countqa = 0;
private int countqc = 0;
//메인카운터
public int CountUp1
{
get { return countUp1; }
set { if (value != countUp1) { countUp1 = value; NotifyPropertyChanged(); } }
}
public int CountUp2
{
get { return countUp2; }
set { if (value != countUp2) { countUp2 = value; NotifyPropertyChanged(); } }
}
public int CountUp3
{
get { return countUp3; }
set { if (value != countUp3) { countUp3 = value; NotifyPropertyChanged(); } }
}
public int CountUp4
{
get { return countUp4; }
set { if (value != countUp4) { countUp4 = value; NotifyPropertyChanged(); } }
}
//public int CountUp5
//{
// get { return countUp5; }
// set { if (value != countUp5) { countUp5 = value; NotifyPropertyChanged(); } }
//}
/// <summary>
/// 상차수량(FVI 1+2+3+4)
/// </summary>
public int CountUp
{
get
{
return CountUp1 + CountUp2 + CountUp3 + CountUp4;// + CountUp5;
}
}
public int CountChargeE
{
get { return this.countchgaerr; }
set { if (value != countchgaerr) { countchgaerr = value; NotifyPropertyChanged(); } }
}
public int CountChargeA
{
get { return this.countchga; }
set { if (value != countchga) { countchga = value; NotifyPropertyChanged(); } }
}
public int CountChargeM
{
get { return this.countchgm; }
set { if (value != countchgm) { countchgm = value; NotifyPropertyChanged(); } }
}
/// <summary>
/// 하차수량(QC+QA)
/// </summary>
public int CountDn
{
get { return this.countqa + this.countqc; }
//set { if (value != countdn) { countdn = value; NotifyPropertyChanged(); } }
}
public int CountQA
{
get { return this.countqa; }
set { if (value != countqa) { countqa = value; NotifyPropertyChanged(); } }
}
public int CountQC
{
get { return this.countqc; }
set { if (value != countqc) { countqc = value; NotifyPropertyChanged(); } }
}
public void CountClear()
{
CountUp1 = 0;
CountUp2 = 0;
CountUp3 = 0;
CountUp4 = 0;
// CountUp5 = 0;
CountQC = 0;
CountQA = 0;
//CountQa2 = 0;
CountReset = DateTime.Now;
PUB.log.Add("Count Clear");
this.Save();
}
public CounterSetting()
{
this.filename = AR.UTIL.CurrentPath + "counter.xml";
}
public override void AfterLoad()
{
if (CountReset == null) CountReset = DateTime.Parse("1982-11-23");
}
public override void AfterSave()
{
//throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,225 @@
namespace Project
{
partial class Form1
{
/// <summary>
/// 필수 디자이너 변수입니다.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 사용 중인 모든 리소스를 정리합니다.
/// </summary>
/// <param name="disposing">관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form
/// <summary>
/// 디자이너 지원에 필요한 메서드입니다.
/// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.cmbPortAGV = new System.Windows.Forms.ComboBox();
this.cmbPortBMS = new System.Windows.Forms.ComboBox();
this.tbBaudAGV = new System.Windows.Forms.TextBox();
this.tbBaudBMS = new System.Windows.Forms.TextBox();
this.btTestAGV = new System.Windows.Forms.Button();
this.btTestBMS = new System.Windows.Forms.Button();
this.rtLog = new System.Windows.Forms.RichTextBox();
this.btFindPort = new System.Windows.Forms.Button();
this.panel1 = new System.Windows.Forms.Panel();
this.toolStrip1 = new System.Windows.Forms.ToolStrip();
this.toolStripButton1 = new System.Windows.Forms.ToolStripButton();
this.toolStripButton2 = new System.Windows.Forms.ToolStripButton();
this.panel1.SuspendLayout();
this.toolStrip1.SuspendLayout();
this.SuspendLayout();
//
// label1
//
this.label1.Location = new System.Drawing.Point(16, 10);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(30, 20);
this.label1.TabIndex = 0;
this.label1.Text = "agv";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// label2
//
this.label2.Location = new System.Drawing.Point(16, 36);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(30, 20);
this.label2.TabIndex = 0;
this.label2.Text = "bms";
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// cmbPortAGV
//
this.cmbPortAGV.FormattingEnabled = true;
this.cmbPortAGV.Location = new System.Drawing.Point(60, 10);
this.cmbPortAGV.Name = "cmbPortAGV";
this.cmbPortAGV.Size = new System.Drawing.Size(121, 20);
this.cmbPortAGV.TabIndex = 1;
//
// cmbPortBMS
//
this.cmbPortBMS.FormattingEnabled = true;
this.cmbPortBMS.Location = new System.Drawing.Point(60, 36);
this.cmbPortBMS.Name = "cmbPortBMS";
this.cmbPortBMS.Size = new System.Drawing.Size(121, 20);
this.cmbPortBMS.TabIndex = 1;
//
// tbBaudAGV
//
this.tbBaudAGV.Location = new System.Drawing.Point(187, 10);
this.tbBaudAGV.Name = "tbBaudAGV";
this.tbBaudAGV.Size = new System.Drawing.Size(100, 21);
this.tbBaudAGV.TabIndex = 2;
//
// tbBaudBMS
//
this.tbBaudBMS.Location = new System.Drawing.Point(187, 36);
this.tbBaudBMS.Name = "tbBaudBMS";
this.tbBaudBMS.Size = new System.Drawing.Size(100, 21);
this.tbBaudBMS.TabIndex = 2;
//
// btTestAGV
//
this.btTestAGV.Location = new System.Drawing.Point(294, 8);
this.btTestAGV.Name = "btTestAGV";
this.btTestAGV.Size = new System.Drawing.Size(75, 24);
this.btTestAGV.TabIndex = 3;
this.btTestAGV.Text = "Test";
this.btTestAGV.UseVisualStyleBackColor = true;
this.btTestAGV.Click += new System.EventHandler(this.btTestAGV_Click);
//
// btTestBMS
//
this.btTestBMS.Location = new System.Drawing.Point(294, 34);
this.btTestBMS.Name = "btTestBMS";
this.btTestBMS.Size = new System.Drawing.Size(75, 24);
this.btTestBMS.TabIndex = 3;
this.btTestBMS.Text = "Test";
this.btTestBMS.UseVisualStyleBackColor = true;
this.btTestBMS.Click += new System.EventHandler(this.btTestBMS_Click);
//
// rtLog
//
this.rtLog.Dock = System.Windows.Forms.DockStyle.Fill;
this.rtLog.Location = new System.Drawing.Point(0, 92);
this.rtLog.Name = "rtLog";
this.rtLog.Size = new System.Drawing.Size(450, 215);
this.rtLog.TabIndex = 4;
this.rtLog.Text = "";
//
// btFindPort
//
this.btFindPort.Location = new System.Drawing.Point(375, 11);
this.btFindPort.Name = "btFindPort";
this.btFindPort.Size = new System.Drawing.Size(71, 47);
this.btFindPort.TabIndex = 5;
this.btFindPort.Text = "find port";
this.btFindPort.UseVisualStyleBackColor = true;
this.btFindPort.Click += new System.EventHandler(this.btFindPort_Click);
//
// panel1
//
this.panel1.Controls.Add(this.cmbPortAGV);
this.panel1.Controls.Add(this.btFindPort);
this.panel1.Controls.Add(this.label1);
this.panel1.Controls.Add(this.label2);
this.panel1.Controls.Add(this.btTestBMS);
this.panel1.Controls.Add(this.cmbPortBMS);
this.panel1.Controls.Add(this.tbBaudAGV);
this.panel1.Controls.Add(this.tbBaudBMS);
this.panel1.Controls.Add(this.btTestAGV);
this.panel1.Dock = System.Windows.Forms.DockStyle.Top;
this.panel1.Location = new System.Drawing.Point(0, 25);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(450, 67);
this.panel1.TabIndex = 6;
//
// toolStrip1
//
this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripButton1,
this.toolStripButton2});
this.toolStrip1.Location = new System.Drawing.Point(0, 0);
this.toolStrip1.Name = "toolStrip1";
this.toolStrip1.Size = new System.Drawing.Size(450, 25);
this.toolStrip1.TabIndex = 7;
this.toolStrip1.Text = "toolStrip1";
//
// toolStripButton1
//
this.toolStripButton1.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton1.Image")));
this.toolStripButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
this.toolStripButton1.Name = "toolStripButton1";
this.toolStripButton1.Size = new System.Drawing.Size(53, 22);
this.toolStripButton1.Text = "Load";
this.toolStripButton1.Click += new System.EventHandler(this.toolStripButton1_Click);
//
// toolStripButton2
//
this.toolStripButton2.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
this.toolStripButton2.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton2.Image")));
this.toolStripButton2.ImageTransparentColor = System.Drawing.Color.Magenta;
this.toolStripButton2.Name = "toolStripButton2";
this.toolStripButton2.Size = new System.Drawing.Size(52, 22);
this.toolStripButton2.Text = "Save";
this.toolStripButton2.Click += new System.EventHandler(this.toolStripButton2_Click);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(450, 307);
this.Controls.Add(this.rtLog);
this.Controls.Add(this.panel1);
this.Controls.Add(this.toolStrip1);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Form1";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "AGV Port Scanner";
this.Load += new System.EventHandler(this.Form1_Load);
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.toolStrip1.ResumeLayout(false);
this.toolStrip1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.ComboBox cmbPortAGV;
private System.Windows.Forms.ComboBox cmbPortBMS;
private System.Windows.Forms.TextBox tbBaudAGV;
private System.Windows.Forms.TextBox tbBaudBMS;
private System.Windows.Forms.Button btTestAGV;
private System.Windows.Forms.Button btTestBMS;
private System.Windows.Forms.RichTextBox rtLog;
private System.Windows.Forms.Button btFindPort;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.ToolStrip toolStrip1;
private System.Windows.Forms.ToolStripButton toolStripButton1;
private System.Windows.Forms.ToolStripButton toolStripButton2;
}
}

View File

@@ -0,0 +1,715 @@
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 System.IO.Ports;
using System.IO;
using AR;
namespace Project
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
PUB.init();
// 현재 설치되어있는 시리얼포트목록을 조회해서 cmbPort... 컨트롤에 미리 입력한다.
RefreshPortList();
// 기본 BaudRate 설정
tbBaudAGV.Text = "115200";
tbBaudBMS.Text = "9600";
}
/// <summary>
/// 시리얼 포트 목록을 새로고침하여 콤보박스에 추가
/// </summary>
private void RefreshPortList()
{
try
{
string[] ports = SerialPort.GetPortNames();
cmbPortAGV.Items.Clear();
cmbPortBMS.Items.Clear();
foreach (string port in ports)
{
cmbPortAGV.Items.Add(port);
cmbPortBMS.Items.Add(port);
}
addLog($"포트 목록 새로고침 완료: {ports.Length}개 포트 발견");
}
catch (Exception ex)
{
addLog($"포트 목록 조회 오류: {ex.Message}");
}
}
/// <summary>
/// 설정파일에서 데이터를 읽어서 컨트롤에 설정한다
/// </summary>
void LoadSetting()
{
try
{
// AGV 설정
if (!string.IsNullOrEmpty(PUB.setting.Port_AGV))
{
this.cmbPortAGV.Text = PUB.setting.Port_AGV;
}
tbBaudAGV.Text = PUB.setting.Baud_AGV.ToString();
// BMS 설정
if (!string.IsNullOrEmpty(PUB.setting.Port_BAT))
{
cmbPortBMS.Text = PUB.setting.Port_BAT;
}
tbBaudBMS.Text = PUB.setting.Baud_BAT.ToString();
addLog("설정 파일 로드 완료");
}
catch (Exception ex)
{
addLog($"설정 로드 오류: {ex.Message}");
}
}
/// <summary>
/// 현재 설정된 값을 확인하고 셋팅파일에 기록한다
/// </summary>
void SaveSetting()
{
try
{
// 현재 UI 값을 설정 객체에 반영
PUB.setting.Port_AGV = cmbPortAGV.SelectedItem?.ToString() ?? "";
PUB.setting.Baud_AGV = int.TryParse(tbBaudAGV.Text, out int agvBaud) ? agvBaud : 115200;
PUB.setting.Port_BAT = cmbPortBMS.SelectedItem?.ToString() ?? "";
PUB.setting.Baud_BAT = int.TryParse(tbBaudBMS.Text, out int bmsBaud) ? bmsBaud : 9600;
// 파일에 저장
PUB.setting.Save();
addLog("설정 파일 저장 완료");
}
catch (Exception ex)
{
addLog($"설정 저장 오류: {ex.Message}");
}
}
private void Form1_Load(object sender, EventArgs e)
{
LoadSetting();
}
private void btLoadSetting_Click(object sender, EventArgs e)
{
LoadSetting();
}
private void btSaveSetting_Click(object sender, EventArgs e)
{
SaveSetting();
}
private void btTestAGV_Click(object sender, EventArgs e)
{
// AGV 용 프로토콜을 전송해서 올바르게 피드백이 오는지 확인한다
// AGV Protocol: STX(0x02) + Data + ETX(0x03)
if (cmbPortAGV.SelectedItem == null)
{
addLog("[AGV] 포트를 선택해주세요");
return;
}
if (!int.TryParse(tbBaudAGV.Text, out int baudRate))
{
addLog("[AGV] BaudRate가 올바르지 않습니다");
return;
}
try
{
addLog($"[AGV] 테스트 시작: {cmbPortAGV.SelectedItem} @ {baudRate}bps");
using (arDev.Narumi port = new arDev.Narumi())
{
port.PortName = cmbPortAGV.SelectedItem.ToString();
port.BaudRate = baudRate;
port.Message += (s1, e1) =>{
addLog($"[AGV] 응답 수신: {e1.Message}");
};
port.Open();
addLog($"[AGV] 포트 열기 성공");
DateTime sendtime = DateTime.Now;
port.AGVMoveStop("test");//
addLog($"[AGV] 테스트 명령 전송:");
// 응답 대기
System.Threading.Thread.Sleep(1200);
if (port.lastRecvTime > sendtime)
{
// STX, ETX 확인
//if (response.Length >= 2 && response[0] == 0x02 && response[response.Length - 1] == 0x03)
//{
addLog($"[AGV] ✓ 통신 성공 - 올바른 프로토콜 응답");
//}
//else
//{
// addLog($"[AGV] ⚠ 응답은 받았으나 프로토콜 형식이 다름");
//}
}
else
{
addLog($"[AGV] ⚠ 응답 없음 - 포트는 열렸으나 장치 응답 없음");
}
port.Close();
addLog($"[AGV] 포트 닫기 완료");
}
}
catch (Exception ex)
{
addLog($"[AGV] ✗ 오류 발생: {ex.Message}");
}
}
private void btTestBMS_Click(object sender, EventArgs e)
{
// BMS 용 프로토콜을 전송해서 올바르게 피드백이 오는지 확인한다
// BMS Protocol: STX(0xDD) + Data(32 bytes) + ETX(0x77)
if (cmbPortBMS.SelectedItem == null)
{
addLog("[BMS] 포트를 선택해주세요");
return;
}
if (!int.TryParse(tbBaudBMS.Text, out int baudRate))
{
addLog("[BMS] BaudRate가 올바르지 않습니다");
return;
}
try
{
addLog($"[BMS] 테스트 시작: {cmbPortBMS.SelectedItem} @ {baudRate}bps");
using (SerialPort port = new SerialPort())
{
port.PortName = cmbPortBMS.SelectedItem.ToString();
port.BaudRate = baudRate;
port.DataBits = 8;
port.Parity = Parity.None;
port.StopBits = StopBits.One;
port.ReadTimeout = 3000;
port.WriteTimeout = 3000;
port.Open();
addLog($"[BMS] 포트 열기 성공");
// 간단한 테스트 명령 전송
var testCmd = new List<byte>();
testCmd.Add(0xDD);
testCmd.Add(0xA5);
testCmd.Add(0x03);
testCmd.Add(0x00);
testCmd.Add(0xFF);
testCmd.Add(0xFD);
testCmd.Add(0x77);
testCmd.Add(0x0D);
port.Write(testCmd.ToArray(), 0, testCmd.Count);//.Length);
addLog($"[BMS] 테스트 명령 전송: {BitConverter.ToString(testCmd.ToArray())}");
// 응답 대기
System.Threading.Thread.Sleep(1200);
if (port.BytesToRead > 0)
{
byte[] response = new byte[port.BytesToRead];
port.Read(response, 0, response.Length);
addLog($"[BMS] 응답 수신 ({response.Length} bytes): {BitConverter.ToString(response)}");
// STX, ETX 확인
if (response.Length >= 2 && response[0] == 0xDD && response[response.Length - 1] == 0x77)
{
addLog($"[BMS] ✓ 통신 성공 - 올바른 프로토콜 응답");
if (response.Length == 34 || response.Length == 23)
{
addLog($"[BMS] ✓ 데이터 길이 확인 ({response.Length} bytes)");
}
}
else
{
addLog($"[BMS] ⚠ 응답은 받았으나 프로토콜 형식이 다름");
}
}
else
{
addLog($"[BMS] ⚠ 응답 없음 - 포트는 열렸으나 장치 응답 없음");
}
port.Close();
addLog($"[BMS] 포트 닫기 완료");
}
}
catch (Exception ex)
{
addLog($"[BMS] ✗ 오류 발생: {ex.Message}");
}
}
void addLog(string msg)
{
// rtLog 컨트롤에 메세지를 추가하고, 추가된 메세지에 맞게 커서가 자동이동되게 한다
if (rtLog.InvokeRequired)
{
rtLog.Invoke(new Action(() => addLog(msg)));
}
else
{
string timestamp = DateTime.Now.ToString("HH:mm:ss.fff");
rtLog.AppendText($"[{timestamp}] {msg}\r\n");
rtLog.SelectionStart = rtLog.Text.Length;
rtLog.ScrollToCaret();
}
}
private async void btFindPort_Click(object sender, EventArgs e)
{
// 버튼 비활성화 (중복 실행 방지)
btFindPort.Enabled = false;
try
{
await Task.Run(() => FindPortsAsync());
}
finally
{
// 버튼 재활성화
btFindPort.Enabled = true;
}
}
private void FindPortsAsync()
{
// AGV와 BMS의 응답 포트를 병렬로 동시에 찾는다
addLog("===== 포트 자동 검색 시작 (병렬 모드) =====");
string[] ports = SerialPort.GetPortNames();
if (ports.Length == 0)
{
addLog("사용 가능한 포트가 없습니다");
return;
}
addLog($"총 {ports.Length}개 포트 검색 중...");
// 병렬 검색을 위한 변수
object lockObj = new object();
string foundAGVPort = null;
string foundBMSPort = null;
int agvBaud = 115200;
int bmsBaud = 9600;
bool bothFound = false;
// 현재 설정된 포트 (우선순위 표시용)
string currentAGVPort = null;
string currentBMSPort = null;
this.Invoke(new Action(() =>
{
currentAGVPort = cmbPortAGV.SelectedItem?.ToString();
currentBMSPort = cmbPortBMS.SelectedItem?.ToString();
}));
// 현재 설정 확인 로그
addLog($"현재 AGV 설정: {(string.IsNullOrEmpty(currentAGVPort) ? "( )" : currentAGVPort)}");
addLog($"현재 BMS 설정: {(string.IsNullOrEmpty(currentBMSPort) ? "( )" : currentBMSPort)}");
// === 1단계: 현재 설정된 포트 우선 테스트 ===
addLog("--- 1단계: 현재 설정 포트 우선 테스트 ---");
// AGV 현재 포트 우선 테스트
if (!string.IsNullOrEmpty(currentAGVPort) && ports.Contains(currentAGVPort))
{
addLog($"[우선] AGV 현재 포트 테스트: {currentAGVPort}");
for (int retry = 1; retry <= 3; retry++)
{
if (TestAGVPort(currentAGVPort, agvBaud))
{
foundAGVPort = currentAGVPort;
addLog($"✓ 현재 설정 포트에서 AGV 발견: {currentAGVPort}");
break;
}
if (retry < 3) System.Threading.Thread.Sleep(500);
}
}
// BMS 현재 포트 우선 테스트
if (!string.IsNullOrEmpty(currentBMSPort) && ports.Contains(currentBMSPort))
{
addLog($"[우선] BMS 현재 포트 테스트: {currentBMSPort}");
for (int retry = 1; retry <= 3; retry++)
{
if (TestBMSPort(currentBMSPort, bmsBaud))
{
foundBMSPort = currentBMSPort;
addLog($"✓ 현재 설정 포트에서 BMS 발견: {currentBMSPort}");
break;
}
if (retry < 3) System.Threading.Thread.Sleep(500);
}
}
// 둘 다 찾았으면 즉시 종료
if (foundAGVPort != null && foundBMSPort != null)
{
addLog("현재 설정 포트에서 모두 발견! 검색 종료");
ApplyFoundPorts(foundAGVPort, foundBMSPort, agvBaud, bmsBaud);
addLog("===== 포트 자동 검색 완료 =====");
return;
}
// === 2단계: 나머지 포트 병렬 검색 ===
if (foundAGVPort == null || foundBMSPort == null)
{
addLog("--- 2단계: 나머지 포트 병렬 검색 ---");
}
// 모든 포트를 병렬로 테스트 (단, 이미 테스트한 포트 제외)
List<Task> tasks = new List<Task>();
foreach (string portName in ports)
{
string port = portName; // 클로저 캡처용
// 이미 우선 테스트에서 체크한 포트는 제외
if (port == currentAGVPort || port == currentBMSPort)
continue;
Task task = Task.Run(() =>
{
// 이미 둘 다 찾았으면 즉시 종료
if (bothFound) return;
addLog($"[{port}] 검사 시작...");
// AGV 테스트 (3회 재시도)
for (int retry = 1; retry <= 3; retry++)
{
// 첫 번째 시도에서는 조기 종료하지 않고 전체 대기 시간 보장
if (retry > 1 && bothFound) return;
try
{
if (foundAGVPort == null && TestAGVPort(port, agvBaud))
{
lock (lockObj)
{
if (foundAGVPort == null)
{
foundAGVPort = port;
addLog($"[{port}] ✓ AGV 발견! (재시도: {retry}/3)");
// 둘 다 찾았는지 확인
if (foundBMSPort != null)
{
bothFound = true;
addLog("AGV와 BMS 모두 발견! 검색 종료");
}
}
}
break;
}
}
catch { }
if (retry < 3 && foundAGVPort == null && !bothFound)
{
System.Threading.Thread.Sleep(500);
}
}
// BMS 테스트 (3회 재시도)
for (int retry = 1; retry <= 3; retry++)
{
// 첫 번째 시도에서는 조기 종료하지 않고 전체 대기 시간 보장
if (retry > 1 && bothFound) return;
try
{
if (foundBMSPort == null && TestBMSPort(port, bmsBaud))
{
lock (lockObj)
{
if (foundBMSPort == null)
{
foundBMSPort = port;
addLog($"[{port}] ✓ BMS 발견! (재시도: {retry}/3)");
// 둘 다 찾았는지 확인
if (foundAGVPort != null)
{
bothFound = true;
addLog("AGV와 BMS 모두 발견! 검색 종료");
}
}
}
break;
}
}
catch { }
if (retry < 3 && foundBMSPort == null && !bothFound)
{
System.Threading.Thread.Sleep(500);
}
}
// 둘 다 실패한 경우에만 로그
if (!bothFound && foundAGVPort != port && foundBMSPort != port)
{
addLog($"[{port}] ✗ 응답 없음");
}
});
tasks.Add(task);
}
// 나머지 포트가 있을 경우만 대기
if (tasks.Count > 0)
{
addLog("나머지 포트 테스트 진행 중...");
// 주기적으로 체크하면서 둘 다 찾았으면 즉시 종료
while (!Task.WaitAll(tasks.ToArray(), 100))
{
if (bothFound)
{
addLog("검색 조기 종료 - 모든 포트 발견");
break;
}
}
}
// 결과 적용
ApplyFoundPorts(foundAGVPort, foundBMSPort, agvBaud, bmsBaud);
addLog("===== 포트 자동 검색 완료 =====");
}
/// <summary>
/// 발견된 포트를 UI에 적용
/// </summary>
private void ApplyFoundPorts(string agvPort, string bmsPort, int agvBaud, int bmsBaud)
{
if (agvPort != null)
{
this.Invoke(new Action(() =>
{
int idx = cmbPortAGV.Items.IndexOf(agvPort);
if (idx >= 0)
{
cmbPortAGV.SelectedIndex = idx;
tbBaudAGV.Text = agvBaud.ToString();
}
}));
addLog($"✓ AGV 포트: {agvPort} (Baud: {agvBaud})");
}
else
{
addLog("⚠ AGV 포트를 찾지 못했습니다");
}
if (bmsPort != null)
{
this.Invoke(new Action(() =>
{
int idx = cmbPortBMS.Items.IndexOf(bmsPort);
if (idx >= 0)
{
cmbPortBMS.SelectedIndex = idx;
tbBaudBMS.Text = bmsBaud.ToString();
}
}));
addLog($"✓ BMS 포트: {bmsPort} (Baud: {bmsBaud})");
}
else
{
addLog("⚠ BMS 포트를 찾지 못했습니다");
}
}
/// <summary>
/// AGV 포트인지 테스트 (btTestAGV_Click 코드 사용)
/// </summary>
private bool TestAGVPort(string portName, int baudRate)
{
try
{
using (arDev.Narumi port = new arDev.Narumi())
{
port.PortName = portName;
port.BaudRate = baudRate;
addLog($" >> AGV 테스트 시작: {portName} @ {baudRate}bps");
port.Open();
addLog($" >> AGV 포트 열기 성공: {portName}");
DateTime sendtime = DateTime.Now;
port.AGVMoveStop("test");
addLog($" >> AGV 명령 전송: {portName} (시간: {sendtime:HH:mm:ss.fff})");
// 응답 대기 (더 길게, 주기적으로 체크)
int maxWait = 2000; // 최대 2초 대기
int waitStep = 100;
int totalWait = 0;
while (totalWait < maxWait)
{
System.Threading.Thread.Sleep(waitStep);
totalWait += waitStep;
// lastRecvTime 확인
if (port.lastRecvTime > sendtime)
{
addLog($" >> ✓✓✓ AGV 응답 수신: {portName} (응답시간: {port.lastRecvTime:HH:mm:ss.fff}, 대기: {totalWait}ms)");
addLog($" >> 응답 데이터: {port.LastRecvString}");
port.Close();
return true;
}
// LastRecvString도 확인 (lastRecvTime이 업데이트 안 될 수도 있음)
if (!string.IsNullOrEmpty(port.LastRecvString))
{
addLog($" >> ✓✓✓ AGV 응답 감지 (문자열): {portName} (대기: {totalWait}ms)");
addLog($" >> 응답 데이터: {port.LastRecvString}");
port.Close();
return true;
}
}
addLog($" >> AGV 응답 없음: {portName} (최종 대기: {totalWait}ms, 마지막수신: {port.lastRecvTime:HH:mm:ss.fff})");
port.Close();
}
}
catch (Exception ex)
{
addLog($" >> AGV 테스트 오류: {portName} - {ex.Message}");
}
return false;
}
/// <summary>
/// BMS 포트인지 테스트 (btTestBMS_Click 코드 사용)
/// </summary>
private bool TestBMSPort(string portName, int baudRate)
{
try
{
using (SerialPort port = new SerialPort())
{
port.PortName = portName;
port.BaudRate = baudRate;
port.DataBits = 8;
port.Parity = Parity.None;
port.StopBits = StopBits.One;
port.ReadTimeout = 500;
port.WriteTimeout = 500;
addLog($" >> BMS 테스트 시작: {portName} @ {baudRate}bps");
port.Open();
addLog($" >> BMS 포트 열기 성공: {portName}");
// BMS 테스트 명령 전송 (테스트 버튼과 동일한 명령)
var testCmd = new List<byte>();
testCmd.Add(0xDD);
testCmd.Add(0xA5);
testCmd.Add(0x03);
testCmd.Add(0x00);
testCmd.Add(0xFF);
testCmd.Add(0xFD);
testCmd.Add(0x77);
testCmd.Add(0x0D);
port.Write(testCmd.ToArray(), 0, testCmd.Count);
addLog($" >> BMS 명령 전송: {portName} - {BitConverter.ToString(testCmd.ToArray())}");
// 응답 대기
System.Threading.Thread.Sleep(300);
if (port.BytesToRead > 0)
{
byte[] response = new byte[port.BytesToRead];
port.Read(response, 0, response.Length);
addLog($" >> BMS 응답 수신: {portName} ({response.Length} bytes) - {BitConverter.ToString(response)}");
// BMS 프로토콜 확인 (STX=0xDD, ETX=0x77)
if (response.Length >= 2 && response[0] == 0xDD && response[response.Length - 1] == 0x77)
{
addLog($" >> ✓✓✓ BMS 프로토콜 확인: {portName}");
port.Close();
return true;
}
else
{
addLog($" >> BMS 프로토콜 불일치: {portName}");
}
}
else
{
addLog($" >> BMS 응답 없음: {portName}");
}
port.Close();
}
}
catch (Exception ex)
{
addLog($" >> BMS 테스트 오류: {portName} - {ex.Message}");
}
return false;
}
private void toolStripButton1_Click(object sender, EventArgs e)
{
LoadSetting();
}
private void toolStripButton2_Click(object sender, EventArgs e)
{
SaveSetting();
}
}
}

View File

@@ -0,0 +1,154 @@
<?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="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="toolStripButton1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
</value>
</data>
</root>

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Project
{
public static class PUB
{
public static AR.Log log;
public static Project.CSetting setting;
public static void init()
{
log = new AR.Log();
setting = new Project.CSetting();
setting.Load();
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Project
{
static class Program
{
/// <summary>
/// 해당 응용 프로그램의 주 진입점입니다.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
// 이러한 특성 값을 변경하세요.
[assembly: AssemblyTitle("Test_Port")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Test_Port")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에
// 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면
// 해당 형식에 대해 ComVisible 특성을 true로 설정하세요.
[assembly: ComVisible(false)]
// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
[assembly: Guid("ccfa2ce7-a539-4adc-b803-f759284c3463")]
// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
//
// 주 버전
// 부 버전
// 빌드 번호
// 수정 버전
//
// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호가 자동으로
// 지정되도록 할 수 있습니다.
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 이 코드는 도구를 사용하여 생성되었습니다.
// 런타임 버전:4.0.30319.42000
//
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
// 이러한 변경 내용이 손실됩니다.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Project.Properties {
using System;
/// <summary>
/// 지역화된 문자열 등을 찾기 위한 강력한 형식의 리소스 클래스입니다.
/// </summary>
// 이 클래스는 ResGen 또는 Visual Studio와 같은 도구를 통해 StronglyTypedResourceBuilder
// 클래스에서 자동으로 생성되었습니다.
// 멤버를 추가하거나 제거하려면 .ResX 파일을 편집한 다음 /str 옵션을 사용하여 ResGen을
// 다시 실행하거나 VS 프로젝트를 다시 빌드하십시오.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// 이 클래스에서 사용하는 캐시된 ResourceManager 인스턴스를 반환합니다.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Project.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 이 강력한 형식의 리소스 클래스를 사용하여 모든 리소스 조회에 대한 현재 스레드의 CurrentUICulture
/// 속성을 재정의합니다.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@@ -0,0 +1,117 @@
<?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.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: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" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</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" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 이 코드는 도구를 사용하여 생성되었습니다.
// 런타임 버전:4.0.30319.42000
//
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
// 이러한 변경 내용이 손실됩니다.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Project.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{CCFA2CE7-A539-4ADC-B803-F759284C3463}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>Project</RootNamespace>
<AssemblyName>Test_Port</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\..\..\..\..\Amkor\AGV4\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="arCommUtil">
<HintPath>..\..\DLL\arCommUtil.dll</HintPath>
</Reference>
<Reference Include="arControl.Net4">
<HintPath>..\..\DLL\arControl.Net4.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Management" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="CSetting.cs" />
<Compile Include="Form1.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Form1.Designer.cs">
<DependentUpon>Form1.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PUB.cs" />
<EmbeddedResource Include="Form1.resx">
<DependentUpon>Form1.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\SubProject\AGV\NARUMI.csproj">
<Project>{8bae0eac-3d25-402f-9a65-2ba1ecfe28b7}</Project>
<Name>NARUMI</Name>
</ProjectReference>
<ProjectReference Include="..\..\SubProject\CommData\CommData.csproj">
<Project>{14e8c9a5-013e-49ba-b435-efefc77dd623}</Project>
<Name>CommData</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>