..
This commit is contained in:
@@ -36,20 +36,20 @@ namespace AGVMapEditor.Forms
|
|||||||
{
|
{
|
||||||
public string FromNodeId { get; set; }
|
public string FromNodeId { get; set; }
|
||||||
public string FromNodeName { get; set; }
|
public string FromNodeName { get; set; }
|
||||||
public string FromRfidId { get; set; }
|
public ushort FromRfidId { get; set; }
|
||||||
public string ToNodeId { get; set; }
|
public string ToNodeId { get; set; }
|
||||||
public string ToNodeName { get; set; }
|
public string ToNodeName { get; set; }
|
||||||
public string ToRfidId { get; set; }
|
public ushort ToRfidId { get; set; }
|
||||||
public string ConnectionType { get; set; }
|
public string ConnectionType { get; set; }
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
// RFID가 있으면 RFID(노드이름), 없으면 NodeID(노드이름) 형태로 표시
|
// RFID가 있으면 RFID(노드이름), 없으면 NodeID(노드이름) 형태로 표시
|
||||||
string fromDisplay = !string.IsNullOrEmpty(FromRfidId)
|
string fromDisplay = FromRfidId > 0
|
||||||
? $"{FromRfidId}({FromNodeName})"
|
? $"{FromRfidId}({FromNodeName})"
|
||||||
: $"---({FromNodeId})";
|
: $"---({FromNodeId})";
|
||||||
|
|
||||||
string toDisplay = !string.IsNullOrEmpty(ToRfidId)
|
string toDisplay = ToRfidId > 0
|
||||||
? $"{ToRfidId}({ToNodeName})"
|
? $"{ToRfidId}({ToNodeName})"
|
||||||
: $"---({ToNodeId})";
|
: $"---({ToNodeId})";
|
||||||
|
|
||||||
@@ -420,7 +420,7 @@ namespace AGVMapEditor.Forms
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var rfidDisplay = (_selectedNode as MapNode)?.RfidId ?? "";
|
var rfidDisplay = (_selectedNode as MapNode)?.RfidId ?? 0;
|
||||||
var result = MessageBox.Show($"노드 {rfidDisplay}[{_selectedNode.Id}] 를 삭제하시겠습니까?\n연결된 RFID 매핑도 함께 삭제됩니다.",
|
var result = MessageBox.Show($"노드 {rfidDisplay}[{_selectedNode.Id}] 를 삭제하시겠습니까?\n연결된 RFID 매핑도 함께 삭제됩니다.",
|
||||||
"삭제 확인", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
"삭제 확인", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||||
|
|
||||||
@@ -1107,8 +1107,8 @@ namespace AGVMapEditor.Forms
|
|||||||
// RFID 값 변경시 중복 검사
|
// RFID 값 변경시 중복 검사
|
||||||
if (e.ChangedItem.PropertyDescriptor.Name == "RFID")
|
if (e.ChangedItem.PropertyDescriptor.Name == "RFID")
|
||||||
{
|
{
|
||||||
string newRfidValue = e.ChangedItem.Value?.ToString();
|
var newRfidValue = ushort.Parse(e.ChangedItem.Value?.ToString());
|
||||||
if (!string.IsNullOrEmpty(newRfidValue) && CheckRfidDuplicate(newRfidValue))
|
if (newRfidValue != 0 && CheckRfidDuplicate(newRfidValue))
|
||||||
{
|
{
|
||||||
// 중복된 RFID 값 발견
|
// 중복된 RFID 값 발견
|
||||||
MessageBox.Show($"RFID 값 '{newRfidValue}'이(가) 이미 다른 노드에서 사용 중입니다.\n입력값을 되돌립니다.",
|
MessageBox.Show($"RFID 값 '{newRfidValue}'이(가) 이미 다른 노드에서 사용 중입니다.\n입력값을 되돌립니다.",
|
||||||
@@ -1174,29 +1174,15 @@ namespace AGVMapEditor.Forms
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="rfidValue">검사할 RFID 값</param>
|
/// <param name="rfidValue">검사할 RFID 값</param>
|
||||||
/// <returns>중복되면 true, 아니면 false</returns>
|
/// <returns>중복되면 true, 아니면 false</returns>
|
||||||
private bool CheckRfidDuplicate(string rfidValue)
|
private bool CheckRfidDuplicate(ushort rfidValue)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(rfidValue) || this._mapCanvas.Nodes == null)
|
if (rfidValue == 0 || this._mapCanvas.Nodes == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// 현재 편집 중인 노드 제외하고 중복 검사
|
// 현재 편집 중인 노드 제외하고 중복 검사
|
||||||
string currentNodeId = null;
|
string currentNodeId = null;
|
||||||
var selectedObject = _propertyGrid.SelectedObject;
|
var selectedObject = _propertyGrid.SelectedObject;
|
||||||
|
|
||||||
// 다양한 PropertyWrapper 타입 처리
|
|
||||||
//if (selectedObject is NodePropertyWrapper nodeWrapper)
|
|
||||||
//{
|
|
||||||
// currentNodeId = nodeWrapper.WrappedNode?.NodeId;
|
|
||||||
//}
|
|
||||||
//else if (selectedObject is LabelNodePropertyWrapper labelWrapper)
|
|
||||||
//{
|
|
||||||
// currentNodeId = labelWrapper.WrappedNode?.NodeId;
|
|
||||||
//}
|
|
||||||
//else if (selectedObject is ImageNodePropertyWrapper imageWrapper)
|
|
||||||
//{
|
|
||||||
// currentNodeId = imageWrapper.WrappedNode?.NodeId;
|
|
||||||
//}
|
|
||||||
|
|
||||||
int duplicateCount = 0;
|
int duplicateCount = 0;
|
||||||
foreach (var node in this._mapCanvas.Nodes)
|
foreach (var node in this._mapCanvas.Nodes)
|
||||||
{
|
{
|
||||||
@@ -1205,7 +1191,7 @@ namespace AGVMapEditor.Forms
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// 같은 RFID 값을 가진 노드가 있는지 확인
|
// 같은 RFID 값을 가진 노드가 있는지 확인
|
||||||
if (!string.IsNullOrEmpty(node.RfidId) && node.RfidId.Equals(rfidValue, StringComparison.OrdinalIgnoreCase))
|
if (node.RfidId != 0 && node.RfidId == rfidValue)
|
||||||
{
|
{
|
||||||
duplicateCount++;
|
duplicateCount++;
|
||||||
break; // 하나라도 발견되면 중복
|
break; // 하나라도 발견되면 중복
|
||||||
|
|||||||
@@ -1140,7 +1140,8 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
|
|
||||||
// 위쪽에 표시할 이름 (노드의 Name 속성)
|
// 위쪽에 표시할 이름 (노드의 Name 속성)
|
||||||
string TopIDText = node.HasRfid() ? node.RfidId : $"[{node.Id}]";
|
string TopIDText = node.HasRfid() ? node.RfidId.ToString("0000") : $"[{node.Id}]";
|
||||||
|
|
||||||
// 아래쪽에 표시할 값 (RFID 우선, 없으면 노드ID)
|
// 아래쪽에 표시할 값 (RFID 우선, 없으면 노드ID)
|
||||||
string BottomLabelText = node.Text;
|
string BottomLabelText = node.Text;
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
|
|
||||||
if (hitNode == null) return;
|
if (hitNode == null) return;
|
||||||
|
|
||||||
if (hitNode.Type == NodeType.Normal)
|
if (hitNode.Type == NodeType.Normal)
|
||||||
{
|
{
|
||||||
HandleNormalNodeDoubleClick(hitNode as MapNode);
|
HandleNormalNodeDoubleClick(hitNode as MapNode);
|
||||||
}
|
}
|
||||||
@@ -146,15 +146,19 @@ namespace AGVNavigationCore.Controls
|
|||||||
private void HandleNormalNodeDoubleClick(MapNode node)
|
private void HandleNormalNodeDoubleClick(MapNode node)
|
||||||
{
|
{
|
||||||
// RFID 입력창 표시
|
// RFID 입력창 표시
|
||||||
string currentRfid = node.RfidId ?? "";
|
var currentRfid = node.RfidId;
|
||||||
string newRfid = Microsoft.VisualBasic.Interaction.InputBox(
|
string newRfid = Microsoft.VisualBasic.Interaction.InputBox(
|
||||||
$"노드 '{node.RfidId}[{node.Id}]'의 RFID를 입력하세요:",
|
$"노드 '{node.RfidId}[{node.Id}]'의 RFID를 입력하세요:",
|
||||||
"RFID 설정",
|
"RFID 설정",
|
||||||
currentRfid);
|
currentRfid.ToString());
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(newRfid) && newRfid != currentRfid)
|
if (ushort.TryParse(newRfid, out ushort newrfidvalue) == false) return;
|
||||||
|
if (newrfidvalue < 1) return;
|
||||||
|
|
||||||
|
|
||||||
|
if (newrfidvalue != currentRfid)
|
||||||
{
|
{
|
||||||
node.RfidId = newRfid.Trim();
|
node.RfidId = newrfidvalue;
|
||||||
MapChanged?.Invoke(this, EventArgs.Empty);
|
MapChanged?.Invoke(this, EventArgs.Empty);
|
||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
@@ -272,7 +276,7 @@ namespace AGVNavigationCore.Controls
|
|||||||
// 호버 업데이트
|
// 호버 업데이트
|
||||||
var newHoveredNode = GetItemAt(worldPoint);
|
var newHoveredNode = GetItemAt(worldPoint);
|
||||||
|
|
||||||
bool hoverChanged = (newHoveredNode != _hoveredNode) ;
|
bool hoverChanged = (newHoveredNode != _hoveredNode);
|
||||||
|
|
||||||
if (hoverChanged)
|
if (hoverChanged)
|
||||||
{
|
{
|
||||||
@@ -700,8 +704,8 @@ namespace AGVNavigationCore.Controls
|
|||||||
{
|
{
|
||||||
// 연결선을 클릭했을 때 삭제 확인
|
// 연결선을 클릭했을 때 삭제 확인
|
||||||
var (fromNode, toNode) = connection.Value;
|
var (fromNode, toNode) = connection.Value;
|
||||||
string fromDisplay = !string.IsNullOrEmpty(fromNode.RfidId) ? fromNode.RfidId : fromNode.Id;
|
string fromDisplay = fromNode.HasRfid() ? fromNode.RfidId.ToString("0000") : fromNode.Id;
|
||||||
string toDisplay = !string.IsNullOrEmpty(toNode.RfidId) ? toNode.RfidId : toNode.Id;
|
string toDisplay = toNode.HasRfid() ? toNode.RfidId.ToString("0000") : toNode.Id;
|
||||||
|
|
||||||
var result = MessageBox.Show(
|
var result = MessageBox.Show(
|
||||||
$"연결을 삭제하시겠습니까?\n\n{fromDisplay} ↔ {toDisplay}",
|
$"연결을 삭제하시겠습니까?\n\n{fromDisplay} ↔ {toDisplay}",
|
||||||
|
|||||||
@@ -851,13 +851,13 @@ namespace AGVNavigationCore.Controls
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// RFID값과 해당 노드의 인덱스를 저장
|
// RFID값과 해당 노드의 인덱스를 저장
|
||||||
var rfidToNodeIndex = new Dictionary<string, List<int>>();
|
var rfidToNodeIndex = new Dictionary<ushort, List<int>>();
|
||||||
|
|
||||||
// 모든 노드의 RFID값 수집
|
// 모든 노드의 RFID값 수집
|
||||||
for (int i = 0; i < _nodes.Count; i++)
|
for (int i = 0; i < _nodes.Count; i++)
|
||||||
{
|
{
|
||||||
var node = _nodes[i];
|
var node = _nodes[i];
|
||||||
if (!string.IsNullOrEmpty(node.RfidId))
|
if (node.HasRfid())
|
||||||
{
|
{
|
||||||
if (!rfidToNodeIndex.ContainsKey(node.RfidId))
|
if (!rfidToNodeIndex.ContainsKey(node.RfidId))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ namespace AGVNavigationCore.Models
|
|||||||
|
|
||||||
[Category("RFID 정보")]
|
[Category("RFID 정보")]
|
||||||
[Description("물리적 RFID 태그 ID입니다.")]
|
[Description("물리적 RFID 태그 ID입니다.")]
|
||||||
public string RfidId { get; set; } = string.Empty;
|
public UInt16 RfidId { get; set; } = 0;
|
||||||
|
|
||||||
|
|
||||||
[Category("노드 텍스트"), DisplayName("TextColor")]
|
[Category("노드 텍스트"), DisplayName("TextColor")]
|
||||||
@@ -161,7 +161,7 @@ namespace AGVNavigationCore.Models
|
|||||||
|
|
||||||
public bool HasRfid()
|
public bool HasRfid()
|
||||||
{
|
{
|
||||||
return !string.IsNullOrEmpty(RfidId);
|
return RfidId > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -611,17 +611,6 @@ namespace AGVNavigationCore.Models
|
|||||||
PositionChanged?.Invoke(this, (_currentPosition, _currentDirection, _currentNode));
|
PositionChanged?.Invoke(this, (_currentPosition, _currentDirection, _currentNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 현재 RFID 시뮬레이션 (현재 위치 기준)
|
|
||||||
/// </summary>
|
|
||||||
public string SimulateRfidReading(List<MapNode> mapNodes)
|
|
||||||
{
|
|
||||||
var closestNode = FindClosestNode(_currentPosition, mapNodes);
|
|
||||||
if (closestNode == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return closestNode.HasRfid() ? closestNode.RfidId : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -630,10 +619,11 @@ namespace AGVNavigationCore.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 노드 ID를 RFID 값으로 변환 (NodeResolver 사용)
|
/// 노드 ID를 RFID 값으로 변환 (NodeResolver 사용)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string GetRfidByNodeId(List<MapNode> _mapNodes, string nodeId)
|
public ushort GetRfidByNodeId(List<MapNode> _mapNodes, string nodeId)
|
||||||
{
|
{
|
||||||
var node = _mapNodes?.FirstOrDefault(n => n.Id == nodeId);
|
var node = _mapNodes?.FirstOrDefault(n => n.Id == nodeId);
|
||||||
return node?.HasRfid() == true ? node.RfidId : nodeId;
|
if ((node?.HasRfid() ?? false) == false) return 0;
|
||||||
|
return node.RfidId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -447,7 +447,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
{
|
{
|
||||||
var node = path1.Path[i];
|
var node = path1.Path[i];
|
||||||
string nodeId = node.Id;
|
string nodeId = node.Id;
|
||||||
string RfidId = node.RfidId;
|
var RfidId = node.RfidId;
|
||||||
string nextNodeId = (i + 1 < path1.Path.Count) ? path1.Path[i + 1].Id : null;
|
string nextNodeId = (i + 1 < path1.Path.Count) ? path1.Path[i + 1].Id : null;
|
||||||
|
|
||||||
// 노드 정보 생성 (현재 방향 유지)
|
// 노드 정보 생성 (현재 방향 유지)
|
||||||
|
|||||||
@@ -770,9 +770,9 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
private string GetDisplayName(string nodeId)
|
private string GetDisplayName(string nodeId)
|
||||||
{
|
{
|
||||||
var node = _mapNodes.FirstOrDefault(n => n.Id == nodeId);
|
var node = _mapNodes.FirstOrDefault(n => n.Id == nodeId);
|
||||||
if (node != null && !string.IsNullOrEmpty(node.RfidId))
|
if (node != null && node.HasRfid())
|
||||||
{
|
{
|
||||||
return node.RfidId;
|
return node.RfidId.ToString("0000");
|
||||||
}
|
}
|
||||||
return $"({nodeId})";
|
return $"({nodeId})";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// RFID Value
|
/// RFID Value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string RfidId { get; set; }
|
public ushort RfidId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 해당 노드에서의 모터방향
|
/// 해당 노드에서의 모터방향
|
||||||
@@ -87,7 +87,7 @@ namespace AGVNavigationCore.PathFinding.Planning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string SpecialActionDescription { get; set; }
|
public string SpecialActionDescription { get; set; }
|
||||||
|
|
||||||
public NodeMotorInfo(int seqno,string nodeId,string rfid, AgvDirection motorDirection, string nextNodeId = null, MagnetDirection magnetDirection = MagnetDirection.Straight)
|
public NodeMotorInfo(int seqno,string nodeId,ushort rfid, AgvDirection motorDirection, string nextNodeId = null, MagnetDirection magnetDirection = MagnetDirection.Straight)
|
||||||
{
|
{
|
||||||
seq = seqno;
|
seq = seqno;
|
||||||
NodeId = nodeId;
|
NodeId = nodeId;
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"\n 최종선택: {bestNode?.RfidId ?? "null"}[{bestNode?.Id ?? "null"}] (점수: {bestScore:F4})");
|
$"\n 최종선택: {bestNode?.RfidId ?? 0}[{bestNode?.Id ?? "null"}] (점수: {bestScore:F4})");
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"[GetNextNodeByDirection] ========== 다음 노드 선택 종료 ==========\n");
|
$"[GetNextNodeByDirection] ========== 다음 노드 선택 종료 ==========\n");
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ namespace AGVNavigationCore.Utils
|
|||||||
public class DirectionalPathfinderTest
|
public class DirectionalPathfinderTest
|
||||||
{
|
{
|
||||||
private List<MapNode> _allNodes;
|
private List<MapNode> _allNodes;
|
||||||
private Dictionary<string, MapNode> _nodesByRfidId;
|
private Dictionary<ushort, MapNode> _nodesByRfidId;
|
||||||
private AGVDirectionCalculator _calculator;
|
private AGVDirectionCalculator _calculator;
|
||||||
|
|
||||||
public DirectionalPathfinderTest()
|
public DirectionalPathfinderTest()
|
||||||
{
|
{
|
||||||
_nodesByRfidId = new Dictionary<string, MapNode>();
|
_nodesByRfidId = new Dictionary<ushort, MapNode>();
|
||||||
_calculator = new AGVDirectionCalculator();
|
_calculator = new AGVDirectionCalculator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
// RFID ID로 인덱싱
|
// RFID ID로 인덱싱
|
||||||
foreach (var node in _allNodes)
|
foreach (var node in _allNodes)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(node.RfidId))
|
if (node.HasRfid())
|
||||||
{
|
{
|
||||||
_nodesByRfidId[node.RfidId] = node;
|
_nodesByRfidId[node.RfidId] = node;
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 테스트: RFID 번호로 노드를 찾고, 다음 노드를 계산
|
/// 테스트: RFID 번호로 노드를 찾고, 다음 노드를 계산
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void TestDirectionalMovement(string previousRfidId, string currentRfidId, AgvDirection direction)
|
public void TestDirectionalMovement(ushort previousRfidId, ushort currentRfidId, AgvDirection direction)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"\n========================================");
|
Console.WriteLine($"\n========================================");
|
||||||
Console.WriteLine($"테스트: {previousRfidId} → {currentRfidId} (방향: {direction})");
|
Console.WriteLine($"테스트: {previousRfidId} → {currentRfidId} (방향: {direction})");
|
||||||
@@ -140,7 +140,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 특정 RFID 노드의 상세 정보 출력
|
/// 특정 RFID 노드의 상세 정보 출력
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void PrintNodeInfo(string rfidId)
|
public void PrintNodeInfo(ushort rfidId)
|
||||||
{
|
{
|
||||||
if (!_nodesByRfidId.TryGetValue(rfidId, out var node))
|
if (!_nodesByRfidId.TryGetValue(rfidId, out var node))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ namespace AGVNavigationCore.Utils
|
|||||||
Console.WriteLine("================================================\n");
|
Console.WriteLine("================================================\n");
|
||||||
|
|
||||||
// 테스트 노드 생성
|
// 테스트 노드 생성
|
||||||
var node001 = new MapNode { Id = "N001", RfidId = "001", Position = new Point(65, 229), ConnectedNodes = new List<string> { "N002" } };
|
var node001 = new MapNode { Id = "N001", RfidId = 001, Position = new Point(65, 229), ConnectedNodes = new List<string> { "N002" } };
|
||||||
var node002 = new MapNode { Id = "N002", RfidId = "002", Position = new Point(206, 244), ConnectedNodes = new List<string> { "N001", "N003" } };
|
var node002 = new MapNode { Id = "N002", RfidId = 002, Position = new Point(206, 244), ConnectedNodes = new List<string> { "N001", "N003" } };
|
||||||
var node003 = new MapNode { Id = "N003", RfidId = "003", Position = new Point(278, 278), ConnectedNodes = new List<string> { "N002", "N004" } };
|
var node003 = new MapNode { Id = "N003", RfidId = 003, Position = new Point(278, 278), ConnectedNodes = new List<string> { "N002", "N004" } };
|
||||||
var node004 = new MapNode { Id = "N004", RfidId = "004", Position = new Point(380, 340), ConnectedNodes = new List<string> { "N003", "N022", "N031" } };
|
var node004 = new MapNode { Id = "N004", RfidId = 004, Position = new Point(380, 340), ConnectedNodes = new List<string> { "N003", "N022", "N031" } };
|
||||||
|
|
||||||
var allNodes = new List<MapNode> { node001, node002, node003, node004 };
|
var allNodes = new List<MapNode> { node001, node002, node003, node004 };
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ namespace AGVNavigationCore.Utils
|
|||||||
AgvDirection motorDir = currentMotorDirection ?? direction;
|
AgvDirection motorDir = currentMotorDirection ?? direction;
|
||||||
|
|
||||||
Console.WriteLine($"설명: {description}");
|
Console.WriteLine($"설명: {description}");
|
||||||
Console.WriteLine($"이전 위치: {prevPos} (RFID: {allNodes.First(n => n.Position == prevPos)?.RfidId ?? "?"})");
|
Console.WriteLine($"이전 위치: {prevPos} (RFID: {allNodes.First(n => n.Position == prevPos)?.RfidId.ToString("0000") ?? "?"})");
|
||||||
Console.WriteLine($"현재 노드: {currentNode.Id} (RFID: {currentNode.RfidId}) - 위치: {currentNode.Position}");
|
Console.WriteLine($"현재 노드: {currentNode.Id} (RFID: {currentNode.RfidId}) - 위치: {currentNode.Position}");
|
||||||
Console.WriteLine($"현재 모터 방향: {motorDir}");
|
Console.WriteLine($"현재 모터 방향: {motorDir}");
|
||||||
Console.WriteLine($"요청 방향: {direction}");
|
Console.WriteLine($"요청 방향: {direction}");
|
||||||
|
|||||||
@@ -29,26 +29,26 @@ namespace AGVNavigationCore.Utils
|
|||||||
tester.PrintAllNodes();
|
tester.PrintAllNodes();
|
||||||
|
|
||||||
// 테스트 시나리오 1: 001 → 002 → Forward (003 기대)
|
// 테스트 시나리오 1: 001 → 002 → Forward (003 기대)
|
||||||
tester.PrintNodeInfo("001");
|
tester.PrintNodeInfo(001);
|
||||||
tester.PrintNodeInfo("002");
|
tester.PrintNodeInfo(002);
|
||||||
tester.TestDirectionalMovement("001", "002", AgvDirection.Forward);
|
tester.TestDirectionalMovement(001, 002, AgvDirection.Forward);
|
||||||
|
|
||||||
// 테스트 시나리오 2: 002 → 001 → Backward (000 또는 이전 기대)
|
// 테스트 시나리오 2: 002 → 001 → Backward (000 또는 이전 기대)
|
||||||
tester.TestDirectionalMovement("002", "001", AgvDirection.Backward);
|
tester.TestDirectionalMovement(002, 001, AgvDirection.Backward);
|
||||||
|
|
||||||
// 테스트 시나리오 3: 002 → 003 → Forward
|
// 테스트 시나리오 3: 002 → 003 → Forward
|
||||||
tester.PrintNodeInfo("003");
|
tester.PrintNodeInfo(003);
|
||||||
tester.TestDirectionalMovement("002", "003", AgvDirection.Forward);
|
tester.TestDirectionalMovement(002, 003, AgvDirection.Forward);
|
||||||
|
|
||||||
// 테스트 시나리오 4: 003 → 004 → Forward
|
// 테스트 시나리오 4: 003 → 004 → Forward
|
||||||
tester.PrintNodeInfo("004");
|
tester.PrintNodeInfo(004);
|
||||||
tester.TestDirectionalMovement("003", "004", AgvDirection.Forward);
|
tester.TestDirectionalMovement(003, 004, AgvDirection.Forward);
|
||||||
|
|
||||||
// 테스트 시나리오 5: 003 → 004 → Right (030 기대)
|
// 테스트 시나리오 5: 003 → 004 → Right (030 기대)
|
||||||
tester.TestDirectionalMovement("003", "004", AgvDirection.Right);
|
tester.TestDirectionalMovement(003, 004, AgvDirection.Right);
|
||||||
|
|
||||||
// 테스트 시나리오 6: 004 → 003 → Backward
|
// 테스트 시나리오 6: 004 → 003 → Backward
|
||||||
tester.TestDirectionalMovement("004", "003", AgvDirection.Backward);
|
tester.TestDirectionalMovement(004, 003, AgvDirection.Backward);
|
||||||
|
|
||||||
Console.WriteLine("\n\n=== 테스트 완료 ===");
|
Console.WriteLine("\n\n=== 테스트 완료 ===");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -635,7 +635,7 @@ namespace AGVSimulator.Forms
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 맵 스캔 모드에서 RFID로부터 노드 생성
|
/// 맵 스캔 모드에서 RFID로부터 노드 생성
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void CreateNodeFromRfidScan(string rfidId, VirtualAGV selectedAGV)
|
private void CreateNodeFromRfidScan(ushort rfidId, VirtualAGV selectedAGV)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -644,7 +644,7 @@ namespace AGVSimulator.Forms
|
|||||||
var currentDirection = directionItem?.Direction ?? AgvDirection.Forward;
|
var currentDirection = directionItem?.Direction ?? AgvDirection.Forward;
|
||||||
|
|
||||||
// 중복 RFID 확인
|
// 중복 RFID 확인
|
||||||
var existingNode = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.RfidId.Equals(rfidId, StringComparison.OrdinalIgnoreCase));
|
var existingNode = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.RfidId == rfidId);
|
||||||
if (existingNode != null)
|
if (existingNode != null)
|
||||||
{
|
{
|
||||||
// 이미 존재하는 노드로 이동
|
// 이미 존재하는 노드로 이동
|
||||||
@@ -805,7 +805,7 @@ namespace AGVSimulator.Forms
|
|||||||
if (agv.CurrentNodeId != null && agv.CurrentNodeId != _lastSentNodeId)
|
if (agv.CurrentNodeId != null && agv.CurrentNodeId != _lastSentNodeId)
|
||||||
{
|
{
|
||||||
var rfid = GetRfidByNodeId(agv.CurrentNodeId);
|
var rfid = GetRfidByNodeId(agv.CurrentNodeId);
|
||||||
if (!string.IsNullOrEmpty(rfid))
|
if (rfid > 0)
|
||||||
{
|
{
|
||||||
SendTag(rfid);
|
SendTag(rfid);
|
||||||
_lastSentNodeId = agv.CurrentNodeId;
|
_lastSentNodeId = agv.CurrentNodeId;
|
||||||
@@ -842,7 +842,7 @@ namespace AGVSimulator.Forms
|
|||||||
|
|
||||||
// RFID 값 확인
|
// RFID 값 확인
|
||||||
var rfidId = _rfidTextBox.Text.Trim();
|
var rfidId = _rfidTextBox.Text.Trim();
|
||||||
if (string.IsNullOrEmpty(rfidId))
|
if (ushort.TryParse(rfidId,out ushort rfidvalue)==false)
|
||||||
{
|
{
|
||||||
MessageBox.Show("RFID 값을 입력해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
MessageBox.Show("RFID 값을 입력해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
return;
|
return;
|
||||||
@@ -856,13 +856,13 @@ namespace AGVSimulator.Forms
|
|||||||
// 맵 스캔 모드일 때: 노드 자동 생성
|
// 맵 스캔 모드일 때: 노드 자동 생성
|
||||||
if (_isMapScanMode)
|
if (_isMapScanMode)
|
||||||
{
|
{
|
||||||
CreateNodeFromRfidScan(rfidId, selectedAGV);
|
CreateNodeFromRfidScan(rfidvalue, selectedAGV);
|
||||||
this._simulatorCanvas.FitToNodes();
|
this._simulatorCanvas.FitToNodes();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// RFID에 해당하는 노드 직접 찾기
|
// RFID에 해당하는 노드 직접 찾기
|
||||||
var targetNode = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.RfidId.Equals(rfidId, StringComparison.OrdinalIgnoreCase));
|
var targetNode = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.RfidId == rfidvalue);
|
||||||
if (targetNode == null)
|
if (targetNode == null)
|
||||||
{
|
{
|
||||||
MessageBox.Show($"RFID '{rfidId}'에 해당하는 노드를 찾을 수 없습니다.\n\n사용 가능한 RFID 목록:\n{GetAvailableRfidList()}",
|
MessageBox.Show($"RFID '{rfidId}'에 해당하는 노드를 찾을 수 없습니다.\n\n사용 가능한 RFID 목록:\n{GetAvailableRfidList()}",
|
||||||
@@ -1255,10 +1255,12 @@ namespace AGVSimulator.Forms
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 노드 ID를 RFID 값으로 변환 (NodeResolver 사용)
|
/// 노드 ID를 RFID 값으로 변환 (NodeResolver 사용)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string GetRfidByNodeId(string nodeId)
|
private ushort GetRfidByNodeId(string nodeId)
|
||||||
{
|
{
|
||||||
var node = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.Id == nodeId);
|
var node = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.Id == nodeId);
|
||||||
return node?.HasRfid() == true ? node.RfidId : nodeId;
|
if (node == null) return 0;
|
||||||
|
if (node.HasRfid()) return node.RfidId;
|
||||||
|
else return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1267,9 +1269,9 @@ namespace AGVSimulator.Forms
|
|||||||
private string GetDisplayName(string nodeId)
|
private string GetDisplayName(string nodeId)
|
||||||
{
|
{
|
||||||
var node = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.Id == nodeId);
|
var node = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.Id == nodeId);
|
||||||
if (node != null && !string.IsNullOrEmpty(node.RfidId))
|
if (node != null && node.HasRfid())
|
||||||
{
|
{
|
||||||
return node.RfidId;
|
return node.RfidId.ToString("0000");
|
||||||
}
|
}
|
||||||
return $"({nodeId})";
|
return $"({nodeId})";
|
||||||
}
|
}
|
||||||
@@ -1366,7 +1368,7 @@ namespace AGVSimulator.Forms
|
|||||||
{
|
{
|
||||||
var info = advancedResult.DetailedPath[i];
|
var info = advancedResult.DetailedPath[i];
|
||||||
var rfidId = GetRfidByNodeId(info.NodeId);
|
var rfidId = GetRfidByNodeId(info.NodeId);
|
||||||
var nextRfidId = info.NextNodeId != null ? GetRfidByNodeId(info.NextNodeId) : "END";
|
var nextRfidId = info.NextNodeId != null ? GetRfidByNodeId(info.NextNodeId).ToString("0000") : "-END-";
|
||||||
|
|
||||||
var flags = new List<string>();
|
var flags = new List<string>();
|
||||||
if (info.CanRotate) flags.Add("회전가능");
|
if (info.CanRotate) flags.Add("회전가능");
|
||||||
@@ -1616,12 +1618,8 @@ namespace AGVSimulator.Forms
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private string GetNodeDisplayName(MapNode node)
|
private string GetNodeDisplayName(MapNode node)
|
||||||
{
|
{
|
||||||
if (node == null)
|
if (node == null) return "-";
|
||||||
return "-";
|
if (node.HasRfid()) return node.RfidId.ToString("0000");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(node.RfidId))
|
|
||||||
return node.RfidId;
|
|
||||||
|
|
||||||
return $"({node.Id})";
|
return $"({node.Id})";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1792,7 +1790,7 @@ namespace AGVSimulator.Forms
|
|||||||
this.Invoke((MethodInvoker)delegate
|
this.Invoke((MethodInvoker)delegate
|
||||||
{
|
{
|
||||||
// RFID 텍스트박스에 값 입력
|
// RFID 텍스트박스에 값 입력
|
||||||
_rfidTextBox.Text = nodeA.RfidId;
|
_rfidTextBox.Text = nodeA.RfidId.ToString();
|
||||||
|
|
||||||
// 방향 콤보박스 선택
|
// 방향 콤보박스 선택
|
||||||
SetDirectionComboBox(direction);
|
SetDirectionComboBox(direction);
|
||||||
@@ -1808,7 +1806,7 @@ namespace AGVSimulator.Forms
|
|||||||
this.Invoke((MethodInvoker)delegate
|
this.Invoke((MethodInvoker)delegate
|
||||||
{
|
{
|
||||||
// RFID 텍스트박스에 값 입력
|
// RFID 텍스트박스에 값 입력
|
||||||
_rfidTextBox.Text = nodeB.RfidId;
|
_rfidTextBox.Text = nodeB.RfidId.ToString();
|
||||||
|
|
||||||
// 방향 콤보박스 선택
|
// 방향 콤보박스 선택
|
||||||
SetDirectionComboBox(direction);
|
SetDirectionComboBox(direction);
|
||||||
@@ -2415,19 +2413,18 @@ namespace AGVSimulator.Forms
|
|||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendTag(string tagno)
|
public void SendTag(ushort tagno)
|
||||||
{
|
{
|
||||||
if (_emulatorPort == null || !_emulatorPort.IsOpen) return;
|
if (_emulatorPort == null || !_emulatorPort.IsOpen) return;
|
||||||
|
|
||||||
tagno = tagno.PadLeft(6, '0');
|
var tagnostr = tagno.ToString("000000");
|
||||||
if (tagno.Length > 6) tagno = tagno.Substring(0, 6);
|
|
||||||
|
|
||||||
var barr = new List<byte>();
|
var barr = new List<byte>();
|
||||||
barr.Add(0x02);
|
barr.Add(0x02);
|
||||||
barr.Add((byte)'T');
|
barr.Add((byte)'T');
|
||||||
barr.Add((byte)'A');
|
barr.Add((byte)'A');
|
||||||
barr.Add((byte)'G');
|
barr.Add((byte)'G');
|
||||||
barr.AddRange(System.Text.Encoding.Default.GetBytes(tagno));
|
barr.AddRange(System.Text.Encoding.Default.GetBytes(tagnostr));
|
||||||
barr.Add((byte)'*');
|
barr.Add((byte)'*');
|
||||||
barr.Add((byte)'*');
|
barr.Add((byte)'*');
|
||||||
barr.Add(0x03);
|
barr.Add(0x03);
|
||||||
|
|||||||
@@ -226,9 +226,6 @@
|
|||||||
<Compile Include="Dialog\fSystem.Designer.cs">
|
<Compile Include="Dialog\fSystem.Designer.cs">
|
||||||
<DependentUpon>fSystem.cs</DependentUpon>
|
<DependentUpon>fSystem.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Dialog\DriveDetector.cs">
|
|
||||||
<SubType>Form</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Dialog\fVolume.cs">
|
<Compile Include="Dialog\fVolume.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -363,9 +360,6 @@
|
|||||||
<Compile Include="StateMachine\_SPS.cs">
|
<Compile Include="StateMachine\_SPS.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Device\_DeviceManagement.cs">
|
|
||||||
<SubType>Form</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="StateMachine\_Loop.cs">
|
<Compile Include="StateMachine\_Loop.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|||||||
@@ -22,11 +22,31 @@ namespace Project
|
|||||||
public Color SMSG_ShadowColor = Color.Transparent;
|
public Color SMSG_ShadowColor = Color.Transparent;
|
||||||
public float SMSG_ProgressValue = 0;
|
public float SMSG_ProgressValue = 0;
|
||||||
public string SMSG_Tag = string.Empty;
|
public string SMSG_Tag = string.Empty;
|
||||||
//public event EventHandler SMSG_Update;
|
/// <summary>
|
||||||
//public void UpdateStatusMessage()
|
/// 이동대상위치(상차,하차,충전)
|
||||||
//{
|
/// </summary>
|
||||||
// SMSG_Update?.Invoke(null, null);
|
private ePosition _targetPos = ePosition.NONE;
|
||||||
//}
|
public string result_message = "";
|
||||||
|
public double result_progressmax = 0;
|
||||||
|
public double result_progressvalue = 0;
|
||||||
|
public DateTime StopMessageTimePLC = DateTime.Parse("1982-11-23");
|
||||||
|
public DateTime StopMessageTimeSWR = DateTime.Parse("1982-11-23");
|
||||||
|
public string StopMessagePLC = string.Empty;
|
||||||
|
public string StopMessageSWR = string.Empty;
|
||||||
|
private ePosition _currentpos = ePosition.NONE;
|
||||||
|
private string _currentposcw = string.Empty;
|
||||||
|
public ushort LastTAG { get; set; } = 0;
|
||||||
|
public ePosition NextPos = ePosition.NONE;
|
||||||
|
private char _comandKit { get; set; }
|
||||||
|
public string Memo;
|
||||||
|
public eResult ResultCode { get; set; }
|
||||||
|
public eECode ResultErrorCode;
|
||||||
|
public string ResultMessage { get; set; }
|
||||||
|
public Boolean isError { get; set; }
|
||||||
|
public int retry = 0;
|
||||||
|
public DateTime retryTime;
|
||||||
|
public Device.Socket.Message RecvMessage;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 작업시작시간
|
/// 작업시작시간
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -52,9 +72,6 @@ namespace Project
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//public DateTime ChargeStartTime = DateTime.Parse("1982-11-23");
|
|
||||||
|
|
||||||
|
|
||||||
#region "AGV Status Value"
|
#region "AGV Status Value"
|
||||||
public string PLC1_RawData { get; set; }
|
public string PLC1_RawData { get; set; }
|
||||||
public string PLC2_RawData { get; set; }
|
public string PLC2_RawData { get; set; }
|
||||||
@@ -62,25 +79,7 @@ namespace Project
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 이동대상위치(상차,하차,충전)
|
|
||||||
/// </summary>
|
|
||||||
private ePosition _targetPos = ePosition.NONE;
|
|
||||||
|
|
||||||
|
|
||||||
public string result_message = "";
|
|
||||||
public double result_progressmax = 0;
|
|
||||||
public double result_progressvalue = 0;
|
|
||||||
|
|
||||||
|
|
||||||
public DateTime StopMessageTimePLC = DateTime.Parse("1982-11-23");
|
|
||||||
public DateTime StopMessageTimeSWR = DateTime.Parse("1982-11-23");
|
|
||||||
public string StopMessagePLC = string.Empty;
|
|
||||||
public string StopMessageSWR = string.Empty;
|
|
||||||
//public DateTime LastChar
|
|
||||||
//geTime = DateTime.Parse("1982-11-23");
|
|
||||||
|
|
||||||
public ePosition NextPos = ePosition.NONE;
|
|
||||||
public ePosition TargetPos
|
public ePosition TargetPos
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -94,7 +93,7 @@ namespace Project
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private char _comandKit { get; set; }
|
|
||||||
public char CommandKit
|
public char CommandKit
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -110,27 +109,6 @@ namespace Project
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//private ePosition _currentPos = ePosition.NONE;
|
|
||||||
//public ePosition CurrentPos
|
|
||||||
//{
|
|
||||||
// get
|
|
||||||
// {
|
|
||||||
// return _currentPos;
|
|
||||||
// }
|
|
||||||
// set
|
|
||||||
// {
|
|
||||||
// if (_currentPos != value) //값이바뀔떄만 메세지 220628
|
|
||||||
// PUB.log.Add(string.Format("현재위치 설정:{0}->{1}", _currentPos, value));
|
|
||||||
|
|
||||||
// _currentPos = value;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
private ePosition _currentpos = ePosition.NONE;
|
|
||||||
private string _currentposcw = string.Empty;
|
|
||||||
|
|
||||||
// public ePosition LastPos = ePosition.NONE;
|
|
||||||
public string LastTAG { get; set; } = string.Empty;
|
|
||||||
public ePosition CurrentPos
|
public ePosition CurrentPos
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -161,11 +139,7 @@ namespace Project
|
|||||||
_currentposcw = value;
|
_currentposcw = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public string Memo;
|
|
||||||
|
|
||||||
public eResult ResultCode { get; set; }
|
|
||||||
public eECode ResultErrorCode;
|
|
||||||
public string ResultMessage { get; set; }
|
|
||||||
|
|
||||||
#region "SetResultMessage"
|
#region "SetResultMessage"
|
||||||
|
|
||||||
@@ -204,13 +178,6 @@ namespace Project
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public Boolean isError { get; set; }
|
|
||||||
|
|
||||||
public int retry = 0;
|
|
||||||
public DateTime retryTime;
|
|
||||||
public Device.Socket.Message RecvMessage;
|
|
||||||
|
|
||||||
public CResult()
|
public CResult()
|
||||||
{
|
{
|
||||||
this.Clear();
|
this.Clear();
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ namespace Project.Device
|
|||||||
|
|
||||||
public Xbee()
|
public Xbee()
|
||||||
{
|
{
|
||||||
|
this.WriteTimeout = 500;
|
||||||
|
this.ReadTimeout = 500;
|
||||||
this.DataReceived += Xbee_DataReceived;
|
this.DataReceived += Xbee_DataReceived;
|
||||||
proto = new EEProtocol();
|
proto = new EEProtocol();
|
||||||
proto.OnDataReceived += Proto_OnDataReceived;
|
proto.OnDataReceived += Proto_OnDataReceived;
|
||||||
@@ -166,12 +168,18 @@ namespace Project.Device
|
|||||||
public bool CleanerInComplete { get; set; }
|
public bool CleanerInComplete { get; set; }
|
||||||
public bool CleanerOutComplete { get; set; }
|
public bool CleanerOutComplete { get; set; }
|
||||||
|
|
||||||
|
ManualResetEvent sendlock = new ManualResetEvent(true);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AGV상태를 Xbee 로 전송한다
|
/// AGV상태를 Xbee 로 전송한다
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SendStatus()
|
public void SendStatus()
|
||||||
{
|
{
|
||||||
|
if (this.IsOpen == false) return;
|
||||||
|
if ( sendlock.WaitOne() == false) return;
|
||||||
|
sendlock.Reset();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Mode[1] : 0=manual, 1=auto
|
Mode[1] : 0=manual, 1=auto
|
||||||
RunSt[1] : 0=stop, 1=run, 2=error
|
RunSt[1] : 0=stop, 1=run, 2=error
|
||||||
@@ -246,7 +254,9 @@ namespace Project.Device
|
|||||||
var cmd = (byte)ENIGProtocol.AGVCommandEH.Status;
|
var cmd = (byte)ENIGProtocol.AGVCommandEH.Status;
|
||||||
var packet = proto.CreatePacket(PUB.setting.XBE_ID, cmd, data);
|
var packet = proto.CreatePacket(PUB.setting.XBE_ID, cmd, data);
|
||||||
if (Send(packet))
|
if (Send(packet))
|
||||||
PUB.logxbee.AddI($"Send status {packet.Length} {packet.HexString()}");
|
PUB.logxbee.AddI($"Send status [O] : {packet.Length} {packet.HexString()}");
|
||||||
|
else
|
||||||
|
PUB.logxbee.AddE($"Send status [X] : {packet.Length} {packet.HexString()}");
|
||||||
LastStatusSendTime = DateTime.Now;
|
LastStatusSendTime = DateTime.Now;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -254,6 +264,11 @@ namespace Project.Device
|
|||||||
errorMessage = ex.Message;
|
errorMessage = ex.Message;
|
||||||
PUB.logxbee.AddE(errorMessage);
|
PUB.logxbee.AddE(errorMessage);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
sendlock.Set();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,323 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Media.Animation;
|
|
||||||
using AR;
|
|
||||||
using arCtl;
|
|
||||||
using COMM;
|
|
||||||
using Project.StateMachine;
|
|
||||||
|
|
||||||
namespace Project
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 장치 연결 및 상태 전송을 담당하는 별도 태스크
|
|
||||||
/// </summary>
|
|
||||||
public partial class fMain
|
|
||||||
{
|
|
||||||
// 장치 관리 태스크 관련
|
|
||||||
private Task deviceManagementTask;
|
|
||||||
private CancellationTokenSource deviceManagementCts;
|
|
||||||
private volatile bool isDeviceManagementRunning = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 장치 관리 태스크 시작 (IDLE 상태 진입 시 호출)
|
|
||||||
/// </summary>
|
|
||||||
public void StartDeviceManagementTask()
|
|
||||||
{
|
|
||||||
if (isDeviceManagementRunning)
|
|
||||||
{
|
|
||||||
PUB.log.Add("DeviceManagement", "이미 실행 중입니다.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isDeviceManagementRunning = true;
|
|
||||||
deviceManagementCts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
deviceManagementTask = Task.Factory.StartNew(
|
|
||||||
() => DeviceManagementWorker(deviceManagementCts.Token),
|
|
||||||
deviceManagementCts.Token,
|
|
||||||
TaskCreationOptions.LongRunning,
|
|
||||||
TaskScheduler.Default
|
|
||||||
);
|
|
||||||
|
|
||||||
PUB.log.Add("DeviceManagement", "장치 관리 태스크 시작");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 장치 관리 태스크 종료
|
|
||||||
/// File : /device/_DeviceManagement.cs
|
|
||||||
/// </summary>
|
|
||||||
public void StopDeviceManagementTask()
|
|
||||||
{
|
|
||||||
if (!isDeviceManagementRunning)
|
|
||||||
return;
|
|
||||||
|
|
||||||
isDeviceManagementRunning = false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
deviceManagementCts?.Cancel();
|
|
||||||
|
|
||||||
if (deviceManagementTask != null)
|
|
||||||
{
|
|
||||||
if (!deviceManagementTask.Wait(3000)) // 3초 대기
|
|
||||||
{
|
|
||||||
PUB.log.AddE("DeviceManagement:태스크 종료 대기 시간 초과");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE($"DeviceManagement 종료 중 오류: {ex.Message}");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
deviceManagementCts?.Dispose();
|
|
||||||
deviceManagementCts = null;
|
|
||||||
deviceManagementTask = null;
|
|
||||||
PUB.log.Add("DeviceManagement", "장치 관리 태스크 종료");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 장치 관리 워커 (별도 태스크에서 실행)
|
|
||||||
/// - 장치 연결 관리 (AGV, XBee, BMS)
|
|
||||||
/// - 자동 상태 전송 (XBee, BMS)
|
|
||||||
/// </summary>
|
|
||||||
private void DeviceManagementWorker(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
PUB.log.Add("DeviceManagementWorker", "시작");
|
|
||||||
|
|
||||||
DateTime lastXbeStatusSendTime = DateTime.Now;
|
|
||||||
DateTime lastBmsQueryTime = DateTime.Now;
|
|
||||||
|
|
||||||
while (!cancellationToken.IsCancellationRequested && isDeviceManagementRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 상태머신이 IDLE 이상이고 CLOSING 미만일 때만 동작
|
|
||||||
if (PUB.sm.Step >= eSMStep.IDLE && PUB.sm.Step < eSMStep.CLOSING)
|
|
||||||
{
|
|
||||||
// ========== 1. 장치 연결 관리 ==========
|
|
||||||
ManageDeviceConnections();
|
|
||||||
|
|
||||||
// ========== 2. XBee 상태 전송 ==========
|
|
||||||
if (PUB.XBE != null && PUB.XBE.IsOpen)
|
|
||||||
{
|
|
||||||
var tsXbe = DateTime.Now - lastXbeStatusSendTime;
|
|
||||||
if (tsXbe.TotalSeconds >= PUB.setting.interval_xbe)
|
|
||||||
{
|
|
||||||
lastXbeStatusSendTime = DateTime.Now;
|
|
||||||
ThreadPool.QueueUserWorkItem(_ =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
PUB.XBE.SendStatus();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE($"XBee SendStatus 오류: {ex.Message}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== 3. BMS 쿼리 및 배터리 경고 ==========
|
|
||||||
if (PUB.BMS != null && PUB.BMS.IsOpen)
|
|
||||||
{
|
|
||||||
var tsBms = DateTime.Now - lastBmsQueryTime;
|
|
||||||
if (tsBms.TotalSeconds >= PUB.setting.interval_bms)
|
|
||||||
{
|
|
||||||
lastBmsQueryTime = DateTime.Now;
|
|
||||||
ThreadPool.QueueUserWorkItem(_ =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
PUB.BMS.SendQuery();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE($"BMS SendQuery 오류: {ex.Message}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 배터리 경고음
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Update_BatteryWarnSpeak();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE($"BatteryWarnSpeak 오류: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE($"DeviceManagementWorker 오류: {ex.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1초 대기 (또는 취소 요청 시 즉시 종료)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Task.Delay(1000, cancellationToken).Wait();
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PUB.log.Add("DeviceManagementWorker", "종료");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 장치 연결 상태 관리
|
|
||||||
/// </summary>
|
|
||||||
private void ManageDeviceConnections()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// AGV 연결
|
|
||||||
ConnectSerialPort(PUB.AGV, PUB.setting.Port_AGV, PUB.setting.Baud_AGV,
|
|
||||||
eVarTime.LastConn_AGV, eVarTime.LastConnTry_AGV, eVarTime.LastRecv_AGV);
|
|
||||||
|
|
||||||
// XBee 연결
|
|
||||||
ConnectSerialPort(PUB.XBE, PUB.setting.Port_XBE, PUB.setting.Baud_XBE,
|
|
||||||
eVarTime.LastConn_XBE, eVarTime.LastConnTry_XBE, eVarTime.LastRecv_XBE);
|
|
||||||
|
|
||||||
// BMS 연결
|
|
||||||
if (PUB.BMS.IsOpen == false)
|
|
||||||
{
|
|
||||||
var ts = VAR.TIME.RUN(eVarTime.LastConn_BAT);
|
|
||||||
if (ts.TotalSeconds > 3)
|
|
||||||
{
|
|
||||||
PUB.log.Add($"BMS 연결 시도: {PUB.setting.Port_BAT}");
|
|
||||||
PUB.BMS.PortName = PUB.setting.Port_BAT;
|
|
||||||
if (PUB.BMS.Open())
|
|
||||||
PUB.log.AddI($"BMS 연결 완료({PUB.setting.Port_BAT})");
|
|
||||||
|
|
||||||
VAR.TIME.Update(eVarTime.LastConn_BAT);
|
|
||||||
VAR.TIME.Update(eVarTime.LastConnTry_BAT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (PUB.BMS.IsValid == false)
|
|
||||||
{
|
|
||||||
var ts = VAR.TIME.RUN(eVarTime.LastConnTry_BAT);
|
|
||||||
if (ts.TotalSeconds > (PUB.setting.interval_bms * 2.5))
|
|
||||||
{
|
|
||||||
PUB.log.Add("BMS 자동 연결 해제 (응답 없음)");
|
|
||||||
PUB.BMS.Close();
|
|
||||||
VAR.TIME.Set(eVarTime.LastConn_BAT, DateTime.Now.AddSeconds(5));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE($"ManageDeviceConnections 오류: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 시리얼 포트 연결 (arDev.arRS232)
|
|
||||||
/// </summary>
|
|
||||||
bool ConnectSerialPort(arDev.arRS232 dev, string port, int baud, eVarTime conn, eVarTime conntry, eVarTime recvtime)
|
|
||||||
{
|
|
||||||
if(port.isEmpty()) return false;
|
|
||||||
|
|
||||||
if (dev.IsOpen == false && port.isEmpty() == false)
|
|
||||||
{
|
|
||||||
var tsPLC = VAR.TIME.RUN(conntry);
|
|
||||||
if (tsPLC.TotalSeconds > 5)
|
|
||||||
{
|
|
||||||
VAR.TIME.Update(conntry);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
VAR.TIME.Update(recvtime);
|
|
||||||
dev.PortName = port;
|
|
||||||
dev.BaudRate = baud;
|
|
||||||
if (dev.Open())
|
|
||||||
{
|
|
||||||
PUB.log.Add(port, $"[AGV:{port}:{baud}] 연결 완료");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//존재하지 않는 포트라면 sync를 벗어난다
|
|
||||||
var ports = System.IO.Ports.SerialPort.GetPortNames().Select(t => t.ToLower()).ToList();
|
|
||||||
if (ports.Contains(PUB.setting.Port_AGV.ToLower()) == false)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var errmessage = dev.errorMessage;
|
|
||||||
PUB.log.AddE($"[AGV:{port}:{baud}] {errmessage}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VAR.TIME.Update(conn);
|
|
||||||
VAR.TIME.Update(conntry);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE(ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (dev.PortName.Equals(port) == false)
|
|
||||||
{
|
|
||||||
PUB.log.Add(port, $"포트 변경({dev.PortName}->{port})으로 연결 종료");
|
|
||||||
dev.Close();
|
|
||||||
VAR.TIME.Update(conntry);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 시리얼 포트 연결 (Device.Xbee)
|
|
||||||
/// </summary>
|
|
||||||
void ConnectSerialPort(Device.Xbee dev, string port, int baud, eVarTime conn, eVarTime conntry, eVarTime recvtime)
|
|
||||||
{
|
|
||||||
if (dev.IsOpen == false && port.isEmpty() == false)
|
|
||||||
{
|
|
||||||
var tsPLC = VAR.TIME.RUN(conntry);
|
|
||||||
if (tsPLC.TotalSeconds > 5)
|
|
||||||
{
|
|
||||||
VAR.TIME.Update(conntry);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
VAR.TIME.Update(recvtime);
|
|
||||||
dev.PortName = port;
|
|
||||||
dev.BaudRate = baud;
|
|
||||||
if (dev.Open())
|
|
||||||
{
|
|
||||||
PUB.log.Add(port, $"[XBEE:{port}:{baud}] 연결 완료");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var errmessage = dev.errorMessage;
|
|
||||||
PUB.log.AddE($"[XBEE:{port}:{baud}] {errmessage}");
|
|
||||||
}
|
|
||||||
VAR.TIME.Update(conn);
|
|
||||||
VAR.TIME.Update(conntry);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
PUB.log.AddE(ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (dev.PortName.Equals(port) == false)
|
|
||||||
{
|
|
||||||
PUB.log.Add(port, $"포트 변경({dev.PortName}->{port})으로 연결 종료");
|
|
||||||
dev.Close();
|
|
||||||
VAR.TIME[(int)conntry] = DateTime.Now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,815 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Windows.Forms; // required for Message
|
|
||||||
using System.Runtime.InteropServices; // required for Marshal
|
|
||||||
using System.IO;
|
|
||||||
using Microsoft.Win32.SafeHandles;
|
|
||||||
// DriveDetector - rev. 1, Oct. 31 2007
|
|
||||||
|
|
||||||
namespace usbdetect
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Hidden Form which we use to receive Windows messages about flash drives
|
|
||||||
/// </summary>
|
|
||||||
internal class DetectorForm : Form
|
|
||||||
{
|
|
||||||
private Label label1;
|
|
||||||
private DriveDetector mDetector = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set up the hidden form.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="detector">DriveDetector object which will receive notification about USB drives, see WndProc</param>
|
|
||||||
public DetectorForm(DriveDetector detector)
|
|
||||||
{
|
|
||||||
mDetector = detector;
|
|
||||||
this.MinimizeBox = false;
|
|
||||||
this.MaximizeBox = false;
|
|
||||||
this.ShowInTaskbar = false;
|
|
||||||
this.ShowIcon = false;
|
|
||||||
this.FormBorderStyle = FormBorderStyle.None;
|
|
||||||
this.Load += new System.EventHandler(this.Load_Form);
|
|
||||||
this.Activated += new EventHandler(this.Form_Activated);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Load_Form(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
// We don't really need this, just to display the label in designer ...
|
|
||||||
InitializeComponent();
|
|
||||||
|
|
||||||
// Create really small form, invisible anyway.
|
|
||||||
this.Size = new System.Drawing.Size(5, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Form_Activated(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
this.Visible = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This function receives all the windows messages for this window (form).
|
|
||||||
/// We call the DriveDetector from here so that is can pick up the messages about
|
|
||||||
/// drives arrived and removed.
|
|
||||||
/// </summary>
|
|
||||||
protected override void WndProc(ref Message m)
|
|
||||||
{
|
|
||||||
base.WndProc(ref m);
|
|
||||||
|
|
||||||
if (mDetector != null)
|
|
||||||
{
|
|
||||||
mDetector.WndProc(ref m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
this.label1 = new System.Windows.Forms.Label();
|
|
||||||
this.SuspendLayout();
|
|
||||||
//
|
|
||||||
// label1
|
|
||||||
//
|
|
||||||
this.label1.AutoSize = true;
|
|
||||||
this.label1.Location = new System.Drawing.Point(13, 30);
|
|
||||||
this.label1.Name = "label1";
|
|
||||||
this.label1.Size = new System.Drawing.Size(377, 12);
|
|
||||||
this.label1.TabIndex = 0;
|
|
||||||
this.label1.Text = "This is invisible form. To see DriveDetector code click View Code";
|
|
||||||
//
|
|
||||||
// DetectorForm
|
|
||||||
//
|
|
||||||
this.ClientSize = new System.Drawing.Size(435, 80);
|
|
||||||
this.Controls.Add(this.label1);
|
|
||||||
this.Name = "DetectorForm";
|
|
||||||
this.ResumeLayout(false);
|
|
||||||
this.PerformLayout();
|
|
||||||
|
|
||||||
}
|
|
||||||
} // class DetectorForm
|
|
||||||
|
|
||||||
|
|
||||||
// Delegate for event handler to handle the device events
|
|
||||||
public delegate void DriveDetectorEventHandler(Object sender, DriveDetectorEventArgs e);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Our class for passing in custom arguments to our event handlers
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class DriveDetectorEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
public DriveDetectorEventArgs()
|
|
||||||
{
|
|
||||||
Cancel = false;
|
|
||||||
Drive = "";
|
|
||||||
HookQueryRemove = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get/Set the value indicating that the event should be cancelled
|
|
||||||
/// Only in QueryRemove handler.
|
|
||||||
/// </summary>
|
|
||||||
public bool Cancel;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Drive letter for the device which caused this event
|
|
||||||
/// </summary>
|
|
||||||
public string Drive;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set to true in your DeviceArrived event handler if you wish to receive the
|
|
||||||
/// QueryRemove event for this drive.
|
|
||||||
/// </summary>
|
|
||||||
public bool HookQueryRemove;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Detects insertion or removal of removable drives.
|
|
||||||
/// Use it in 1 or 2 steps:
|
|
||||||
/// 1) Create instance of this class in your project and add handlers for the
|
|
||||||
/// DeviceArrived, DeviceRemoved and QueryRemove events.
|
|
||||||
/// AND (if you do not want drive detector to creaate a hidden form))
|
|
||||||
/// 2) Override WndProc in your form and call DriveDetector's WndProc from there.
|
|
||||||
/// If you do not want to do step 2, just use the DriveDetector constructor without arguments and
|
|
||||||
/// it will create its own invisible form to receive messages from Windows.
|
|
||||||
/// </summary>
|
|
||||||
class DriveDetector : IDisposable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Events signalized to the client app.
|
|
||||||
/// Add handlers for these events in your form to be notified of removable device events
|
|
||||||
/// </summary>
|
|
||||||
public event DriveDetectorEventHandler DeviceArrived;
|
|
||||||
public event DriveDetectorEventHandler DeviceRemoved;
|
|
||||||
public event DriveDetectorEventHandler QueryRemove;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The easiest way to use DriveDetector.
|
|
||||||
/// It will create hidden form for processing Windows messages about USB drives
|
|
||||||
/// You do not need to override WndProc in your form.
|
|
||||||
/// </summary>
|
|
||||||
public DriveDetector()
|
|
||||||
{
|
|
||||||
DetectorForm frm = new DetectorForm(this);
|
|
||||||
frm.Show(); // will be hidden immediatelly
|
|
||||||
Init(frm, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Alternate constructor.
|
|
||||||
/// Pass in your Form and DriveDetector will not create hidden form.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="control">object which will receive Windows messages.
|
|
||||||
/// Pass "this" as this argument from your form class.</param>
|
|
||||||
public DriveDetector(Control control)
|
|
||||||
{
|
|
||||||
Init(control, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Consructs DriveDetector object setting also path to file which should be opened
|
|
||||||
/// when registering for query remove.
|
|
||||||
/// </summary>
|
|
||||||
///<param name="control">object which will receive Windows messages.
|
|
||||||
/// Pass "this" as this argument from your form class.</param>
|
|
||||||
/// <param name="FileToOpen">Optional. Name of a file on the removable drive which should be opened.
|
|
||||||
/// If null, root directory of the drive will be opened. Opening a file is needed for us
|
|
||||||
/// to be able to register for the query remove message. TIP: For files use relative path without drive letter.
|
|
||||||
/// e.g. "SomeFolder\file_on_flash.txt"</param>
|
|
||||||
public DriveDetector(Control control, string FileToOpen)
|
|
||||||
{
|
|
||||||
Init(control, FileToOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// init the DriveDetector object
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="intPtr"></param>
|
|
||||||
private void Init(Control control, string fileToOpen)
|
|
||||||
{
|
|
||||||
mFileToOpen = fileToOpen;
|
|
||||||
mFileOnFlash = null;
|
|
||||||
mDeviceNotifyHandle = IntPtr.Zero;
|
|
||||||
mRecipientHandle = control.Handle;
|
|
||||||
mDirHandle = IntPtr.Zero; // handle to the root directory of the flash drive which we open
|
|
||||||
mCurrentDrive = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value indicating whether the query remove event will be fired.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsQueryHooked
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (mDeviceNotifyHandle == IntPtr.Zero)
|
|
||||||
return false;
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets letter of drive which is currently hooked. Empty string if none.
|
|
||||||
/// See also IsQueryHooked.
|
|
||||||
/// </summary>
|
|
||||||
public string HookedDrive
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return mCurrentDrive;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the file stream for file which this class opened on a drive to be notified
|
|
||||||
/// about it's removal.
|
|
||||||
/// This will be null unless you specified a file to open (DriveDetector opens root directory of the flash drive)
|
|
||||||
/// </summary>
|
|
||||||
public FileStream OpenedFile
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return mFileOnFlash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Hooks specified drive to receive a message when it is being removed.
|
|
||||||
/// This can be achieved also by setting e.HookQueryRemove to true in your
|
|
||||||
/// DeviceArrived event handler.
|
|
||||||
/// By default DriveDetector will open the root directory of the flash drive to obtain notification handle
|
|
||||||
/// from Windows (to learn when the drive is about to be removed).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="fileOnDrive">Drive letter or relative path to a file on the drive which should be
|
|
||||||
/// used to get a handle - required for registering to receive query remove messages.
|
|
||||||
/// If only drive letter is specified (e.g. "D:\\", root directory of the drive will be opened.</param>
|
|
||||||
/// <returns>true if hooked ok, false otherwise</returns>
|
|
||||||
public bool EnableQueryRemove(string fileOnDrive)
|
|
||||||
{
|
|
||||||
if (fileOnDrive == null || fileOnDrive.Length == 0)
|
|
||||||
throw new ArgumentException("Drive path must be supplied to register for Query remove.");
|
|
||||||
|
|
||||||
if ( fileOnDrive.Length == 2 && fileOnDrive[1] == ':' )
|
|
||||||
fileOnDrive += '\\'; // append "\\" if only drive letter with ":" was passed in.
|
|
||||||
|
|
||||||
if (mDeviceNotifyHandle != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
// Unregister first...
|
|
||||||
RegisterForDeviceChange(false, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Path.GetFileName(fileOnDrive).Length == 0 ||!File.Exists(fileOnDrive))
|
|
||||||
mFileToOpen = null; // use root directory...
|
|
||||||
else
|
|
||||||
mFileToOpen = fileOnDrive;
|
|
||||||
|
|
||||||
RegisterQuery(Path.GetPathRoot(fileOnDrive));
|
|
||||||
if (mDeviceNotifyHandle == IntPtr.Zero)
|
|
||||||
return false; // failed to register
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unhooks any currently hooked drive so that the query remove
|
|
||||||
/// message is not generated for it.
|
|
||||||
/// </summary>
|
|
||||||
public void DisableQueryRemove()
|
|
||||||
{
|
|
||||||
if (mDeviceNotifyHandle != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
RegisterForDeviceChange(false, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unregister and close the file we may have opened on the removable drive.
|
|
||||||
/// Garbage collector will call this method.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
RegisterForDeviceChange(false, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#region WindowProc
|
|
||||||
/// <summary>
|
|
||||||
/// Message handler which must be called from client form.
|
|
||||||
/// Processes Windows messages and calls event handlers.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="m"></param>
|
|
||||||
public void WndProc(ref Message m)
|
|
||||||
{
|
|
||||||
int devType;
|
|
||||||
char c;
|
|
||||||
|
|
||||||
if (m.Msg == WM_DEVICECHANGE)
|
|
||||||
{
|
|
||||||
// WM_DEVICECHANGE can have several meanings depending on the WParam value...
|
|
||||||
switch (m.WParam.ToInt32())
|
|
||||||
{
|
|
||||||
|
|
||||||
//
|
|
||||||
// New device has just arrived
|
|
||||||
//
|
|
||||||
case DBT_DEVICEARRIVAL:
|
|
||||||
|
|
||||||
devType = Marshal.ReadInt32(m.LParam, 4);
|
|
||||||
if (devType == DBT_DEVTYP_VOLUME)
|
|
||||||
{
|
|
||||||
DEV_BROADCAST_VOLUME vol;
|
|
||||||
vol = (DEV_BROADCAST_VOLUME)
|
|
||||||
Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
|
|
||||||
|
|
||||||
// Get the drive letter
|
|
||||||
c = DriveMaskToLetter(vol.dbcv_unitmask);
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Call the client event handler
|
|
||||||
//
|
|
||||||
// We should create copy of the event before testing it and
|
|
||||||
// calling the delegate - if any
|
|
||||||
DriveDetectorEventHandler tempDeviceArrived = DeviceArrived;
|
|
||||||
if ( tempDeviceArrived != null )
|
|
||||||
{
|
|
||||||
DriveDetectorEventArgs e = new DriveDetectorEventArgs();
|
|
||||||
e.Drive = c + ":\\";
|
|
||||||
tempDeviceArrived(this, e);
|
|
||||||
|
|
||||||
// Register for query remove if requested
|
|
||||||
if (e.HookQueryRemove)
|
|
||||||
{
|
|
||||||
// If something is already hooked, unhook it now
|
|
||||||
if (mDeviceNotifyHandle != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
RegisterForDeviceChange(false, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterQuery(c + ":\\");
|
|
||||||
}
|
|
||||||
} // if has event handler
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Device is about to be removed
|
|
||||||
// Any application can cancel the removal
|
|
||||||
//
|
|
||||||
case DBT_DEVICEQUERYREMOVE:
|
|
||||||
|
|
||||||
devType = Marshal.ReadInt32(m.LParam, 4);
|
|
||||||
if (devType == DBT_DEVTYP_HANDLE)
|
|
||||||
{
|
|
||||||
// TODO: we could get the handle for which this message is sent
|
|
||||||
// from vol.dbch_handle and compare it against a list of handles for
|
|
||||||
// which we have registered the query remove message (?)
|
|
||||||
//DEV_BROADCAST_HANDLE vol;
|
|
||||||
//vol = (DEV_BROADCAST_HANDLE)
|
|
||||||
// Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HANDLE));
|
|
||||||
// if ( vol.dbch_handle ....
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Call the event handler in client
|
|
||||||
//
|
|
||||||
DriveDetectorEventHandler tempQuery = QueryRemove;
|
|
||||||
if (tempQuery != null)
|
|
||||||
{
|
|
||||||
DriveDetectorEventArgs e = new DriveDetectorEventArgs();
|
|
||||||
e.Drive = mCurrentDrive; // drive which is hooked
|
|
||||||
tempQuery(this, e);
|
|
||||||
|
|
||||||
// If the client wants to cancel, let Windows know
|
|
||||||
if (e.Cancel)
|
|
||||||
{
|
|
||||||
m.Result = (IntPtr)BROADCAST_QUERY_DENY;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Change 28.10.2007: Unregister the notification, this will
|
|
||||||
// close the handle to file or root directory also.
|
|
||||||
// We have to close it anyway to allow the removal so
|
|
||||||
// even if some other app cancels the removal we would not know about it...
|
|
||||||
RegisterForDeviceChange(false, null); // will also close the mFileOnFlash
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Device has been removed
|
|
||||||
//
|
|
||||||
case DBT_DEVICEREMOVECOMPLETE:
|
|
||||||
|
|
||||||
devType = Marshal.ReadInt32(m.LParam, 4);
|
|
||||||
if (devType == DBT_DEVTYP_VOLUME)
|
|
||||||
{
|
|
||||||
devType = Marshal.ReadInt32(m.LParam, 4);
|
|
||||||
if (devType == DBT_DEVTYP_VOLUME)
|
|
||||||
{
|
|
||||||
DEV_BROADCAST_VOLUME vol;
|
|
||||||
vol = (DEV_BROADCAST_VOLUME)
|
|
||||||
Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
|
|
||||||
c = DriveMaskToLetter(vol.dbcv_unitmask);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Call the client event handler
|
|
||||||
//
|
|
||||||
DriveDetectorEventHandler tempDeviceRemoved = DeviceRemoved;
|
|
||||||
if (tempDeviceRemoved != null)
|
|
||||||
{
|
|
||||||
DriveDetectorEventArgs e = new DriveDetectorEventArgs();
|
|
||||||
e.Drive = c + ":\\";
|
|
||||||
tempDeviceRemoved(this, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: we could unregister the notify handle here if we knew it is the
|
|
||||||
// right drive which has been just removed
|
|
||||||
//RegisterForDeviceChange(false, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region Private Area
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// New: 28.10.2007 - handle to root directory of flash drive which is opened
|
|
||||||
/// for device notification
|
|
||||||
/// </summary>
|
|
||||||
private IntPtr mDirHandle = IntPtr.Zero;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Class which contains also handle to the file opened on the flash drive
|
|
||||||
/// </summary>
|
|
||||||
private FileStream mFileOnFlash = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Name of the file to try to open on the removable drive for query remove registration
|
|
||||||
/// </summary>
|
|
||||||
private string mFileToOpen;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handle to file which we keep opened on the drive if query remove message is required by the client
|
|
||||||
/// </summary>
|
|
||||||
private IntPtr mDeviceNotifyHandle;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handle of the window which receives messages from Windows. This will be a form.
|
|
||||||
/// </summary>
|
|
||||||
private IntPtr mRecipientHandle;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Drive which is currently hooked for query remove
|
|
||||||
/// </summary>
|
|
||||||
private string mCurrentDrive;
|
|
||||||
|
|
||||||
|
|
||||||
// Win32 constants
|
|
||||||
private const int DBT_DEVTYP_DEVICEINTERFACE = 5;
|
|
||||||
private const int DBT_DEVTYP_HANDLE = 6;
|
|
||||||
private const int BROADCAST_QUERY_DENY = 0x424D5144;
|
|
||||||
private const int WM_DEVICECHANGE = 0x0219;
|
|
||||||
private const int DBT_DEVICEARRIVAL = 0x8000; // system detected a new device
|
|
||||||
private const int DBT_DEVICEQUERYREMOVE = 0x8001; // Preparing to remove (any program can disable the removal)
|
|
||||||
private const int DBT_DEVICEREMOVECOMPLETE = 0x8004; // removed
|
|
||||||
private const int DBT_DEVTYP_VOLUME = 0x00000002; // drive type is logical volume
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers for receiving the query remove message for a given drive.
|
|
||||||
/// We need to open a handle on that drive and register with this handle.
|
|
||||||
/// Client can specify this file in mFileToOpen or we will open root directory of the drive
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="drive">drive for which to register. </param>
|
|
||||||
private void RegisterQuery(string drive)
|
|
||||||
{
|
|
||||||
bool register = true;
|
|
||||||
|
|
||||||
if (mFileToOpen == null)
|
|
||||||
{
|
|
||||||
// Change 28.10.2007 - Open the root directory if no file specified - leave mFileToOpen null
|
|
||||||
// If client gave us no file, let's pick one on the drive...
|
|
||||||
//mFileToOpen = GetAnyFile(drive);
|
|
||||||
//if (mFileToOpen.Length == 0)
|
|
||||||
// return; // no file found on the flash drive
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Make sure the path in mFileToOpen contains valid drive
|
|
||||||
// If there is a drive letter in the path, it may be different from the actual
|
|
||||||
// letter assigned to the drive now. We will cut it off and merge the actual drive
|
|
||||||
// with the rest of the path.
|
|
||||||
if (mFileToOpen.Contains(":"))
|
|
||||||
{
|
|
||||||
string tmp = mFileToOpen.Substring(3);
|
|
||||||
string root = Path.GetPathRoot(drive);
|
|
||||||
mFileToOpen = Path.Combine(root, tmp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
mFileToOpen = Path.Combine(drive, mFileToOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//mFileOnFlash = new FileStream(mFileToOpen, FileMode.Open);
|
|
||||||
// Change 28.10.2007 - Open the root directory
|
|
||||||
if (mFileToOpen == null) // open root directory
|
|
||||||
mFileOnFlash = null;
|
|
||||||
else
|
|
||||||
mFileOnFlash = new FileStream(mFileToOpen, FileMode.Open);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// just do not register if the file could not be opened
|
|
||||||
register = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (register)
|
|
||||||
{
|
|
||||||
//RegisterForDeviceChange(true, mFileOnFlash.SafeFileHandle);
|
|
||||||
//mCurrentDrive = drive;
|
|
||||||
// Change 28.10.2007 - Open the root directory
|
|
||||||
if (mFileOnFlash == null)
|
|
||||||
RegisterForDeviceChange(drive);
|
|
||||||
else
|
|
||||||
// old version
|
|
||||||
RegisterForDeviceChange(true, mFileOnFlash.SafeFileHandle);
|
|
||||||
|
|
||||||
mCurrentDrive = drive;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// New version which gets the handle automatically for specified directory
|
|
||||||
/// Only for registering! Unregister with the old version of this function...
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="register"></param>
|
|
||||||
/// <param name="dirPath">e.g. C:\\dir</param>
|
|
||||||
private void RegisterForDeviceChange(string dirPath)
|
|
||||||
{
|
|
||||||
IntPtr handle = Native.OpenDirectory(dirPath);
|
|
||||||
if (handle == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
mDeviceNotifyHandle = IntPtr.Zero;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
mDirHandle = handle; // save handle for closing it when unregistering
|
|
||||||
|
|
||||||
// Register for handle
|
|
||||||
DEV_BROADCAST_HANDLE data = new DEV_BROADCAST_HANDLE();
|
|
||||||
data.dbch_devicetype = DBT_DEVTYP_HANDLE;
|
|
||||||
data.dbch_reserved = 0;
|
|
||||||
data.dbch_nameoffset = 0;
|
|
||||||
//data.dbch_data = null;
|
|
||||||
//data.dbch_eventguid = 0;
|
|
||||||
data.dbch_handle = handle;
|
|
||||||
data.dbch_hdevnotify = (IntPtr)0;
|
|
||||||
int size = Marshal.SizeOf(data);
|
|
||||||
data.dbch_size = size;
|
|
||||||
IntPtr buffer = Marshal.AllocHGlobal(size);
|
|
||||||
Marshal.StructureToPtr(data, buffer, true);
|
|
||||||
|
|
||||||
mDeviceNotifyHandle = Native.RegisterDeviceNotification(mRecipientHandle, buffer, 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers to be notified when the volume is about to be removed
|
|
||||||
/// This is requierd if you want to get the QUERY REMOVE messages
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="register">true to register, false to unregister</param>
|
|
||||||
/// <param name="fileHandle">handle of a file opened on the removable drive</param>
|
|
||||||
private void RegisterForDeviceChange(bool register, SafeFileHandle fileHandle)
|
|
||||||
{
|
|
||||||
if (register)
|
|
||||||
{
|
|
||||||
// Register for handle
|
|
||||||
DEV_BROADCAST_HANDLE data = new DEV_BROADCAST_HANDLE();
|
|
||||||
data.dbch_devicetype = DBT_DEVTYP_HANDLE;
|
|
||||||
data.dbch_reserved = 0;
|
|
||||||
data.dbch_nameoffset = 0;
|
|
||||||
//data.dbch_data = null;
|
|
||||||
//data.dbch_eventguid = 0;
|
|
||||||
data.dbch_handle = fileHandle.DangerousGetHandle(); //Marshal. fileHandle;
|
|
||||||
data.dbch_hdevnotify = (IntPtr)0;
|
|
||||||
int size = Marshal.SizeOf(data);
|
|
||||||
data.dbch_size = size;
|
|
||||||
IntPtr buffer = Marshal.AllocHGlobal(size);
|
|
||||||
Marshal.StructureToPtr(data, buffer, true);
|
|
||||||
|
|
||||||
mDeviceNotifyHandle = Native.RegisterDeviceNotification(mRecipientHandle, buffer, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// close the directory handle
|
|
||||||
if (mDirHandle != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Native.CloseDirectoryHandle(mDirHandle);
|
|
||||||
// string er = Marshal.GetLastWin32Error().ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// unregister
|
|
||||||
if (mDeviceNotifyHandle != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Native.UnregisterDeviceNotification(mDeviceNotifyHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
mDeviceNotifyHandle = IntPtr.Zero;
|
|
||||||
mDirHandle = IntPtr.Zero;
|
|
||||||
|
|
||||||
mCurrentDrive = "";
|
|
||||||
if (mFileOnFlash != null)
|
|
||||||
{
|
|
||||||
mFileOnFlash.Close();
|
|
||||||
mFileOnFlash = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets drive letter from a bit mask where bit 0 = A, bit 1 = B etc.
|
|
||||||
/// There can actually be more than one drive in the mask but we
|
|
||||||
/// just use the last one in this case.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mask"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static char DriveMaskToLetter(int mask)
|
|
||||||
{
|
|
||||||
char letter;
|
|
||||||
string drives = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
||||||
// 1 = A
|
|
||||||
// 2 = B
|
|
||||||
// 4 = C...
|
|
||||||
int cnt = 0;
|
|
||||||
int pom = mask / 2;
|
|
||||||
while (pom != 0)
|
|
||||||
{
|
|
||||||
// while there is any bit set in the mask
|
|
||||||
// shift it to the righ...
|
|
||||||
pom = pom / 2;
|
|
||||||
cnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cnt < drives.Length)
|
|
||||||
letter = drives[cnt];
|
|
||||||
else
|
|
||||||
letter = '?';
|
|
||||||
|
|
||||||
return letter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 28.10.2007 - no longer needed
|
|
||||||
/// <summary>
|
|
||||||
/// Searches for any file in a given path and returns its full path
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="drive">drive to search</param>
|
|
||||||
/// <returns>path of the file or empty string</returns>
|
|
||||||
private string GetAnyFile(string drive)
|
|
||||||
{
|
|
||||||
string file = "";
|
|
||||||
// First try files in the root
|
|
||||||
string[] files = Directory.GetFiles(drive);
|
|
||||||
if (files.Length == 0)
|
|
||||||
{
|
|
||||||
// if no file in the root, search whole drive
|
|
||||||
files = Directory.GetFiles(drive, "*.*", SearchOption.AllDirectories);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.Length > 0)
|
|
||||||
file = files[0]; // get the first file
|
|
||||||
|
|
||||||
// return empty string if no file found
|
|
||||||
return file;
|
|
||||||
}*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
#region Native Win32 API
|
|
||||||
/// <summary>
|
|
||||||
/// WinAPI functions
|
|
||||||
/// </summary>
|
|
||||||
private class Native
|
|
||||||
{
|
|
||||||
// HDEVNOTIFY RegisterDeviceNotification(HANDLE hRecipient,LPVOID NotificationFilter,DWORD Flags);
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
||||||
public static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, uint Flags);
|
|
||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
||||||
public static extern uint UnregisterDeviceNotification(IntPtr hHandle);
|
|
||||||
|
|
||||||
//
|
|
||||||
// CreateFile - MSDN
|
|
||||||
const uint GENERIC_READ = 0x80000000;
|
|
||||||
const uint OPEN_EXISTING = 3;
|
|
||||||
const uint FILE_SHARE_READ = 0x00000001;
|
|
||||||
const uint FILE_SHARE_WRITE = 0x00000002;
|
|
||||||
const uint FILE_ATTRIBUTE_NORMAL = 128;
|
|
||||||
const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
|
|
||||||
static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
|
|
||||||
|
|
||||||
|
|
||||||
// should be "static extern unsafe"
|
|
||||||
[DllImport("kernel32", SetLastError = true)]
|
|
||||||
static extern IntPtr CreateFile(
|
|
||||||
string FileName, // file name
|
|
||||||
uint DesiredAccess, // access mode
|
|
||||||
uint ShareMode, // share mode
|
|
||||||
uint SecurityAttributes, // Security Attributes
|
|
||||||
uint CreationDisposition, // how to create
|
|
||||||
uint FlagsAndAttributes, // file attributes
|
|
||||||
int hTemplateFile // handle to template file
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
[DllImport("kernel32", SetLastError = true)]
|
|
||||||
static extern bool CloseHandle(
|
|
||||||
IntPtr hObject // handle to object
|
|
||||||
);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Opens a directory, returns it's handle or zero.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dirPath">path to the directory, e.g. "C:\\dir"</param>
|
|
||||||
/// <returns>handle to the directory. Close it with CloseHandle().</returns>
|
|
||||||
static public IntPtr OpenDirectory(string dirPath)
|
|
||||||
{
|
|
||||||
// open the existing file for reading
|
|
||||||
IntPtr handle = CreateFile(
|
|
||||||
dirPath,
|
|
||||||
GENERIC_READ,
|
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
||||||
0,
|
|
||||||
OPEN_EXISTING,
|
|
||||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
|
|
||||||
0);
|
|
||||||
|
|
||||||
if ( handle == INVALID_HANDLE_VALUE)
|
|
||||||
return IntPtr.Zero;
|
|
||||||
else
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static bool CloseDirectoryHandle(IntPtr handle)
|
|
||||||
{
|
|
||||||
return CloseHandle(handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Structure with information for RegisterDeviceNotification.
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct DEV_BROADCAST_HANDLE
|
|
||||||
{
|
|
||||||
public int dbch_size;
|
|
||||||
public int dbch_devicetype;
|
|
||||||
public int dbch_reserved;
|
|
||||||
public IntPtr dbch_handle;
|
|
||||||
public IntPtr dbch_hdevnotify;
|
|
||||||
public Guid dbch_eventguid;
|
|
||||||
public long dbch_nameoffset;
|
|
||||||
//public byte[] dbch_data[1]; // = new byte[1];
|
|
||||||
public byte dbch_data;
|
|
||||||
public byte dbch_data1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Struct for parameters of the WM_DEVICECHANGE message
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct DEV_BROADCAST_VOLUME
|
|
||||||
{
|
|
||||||
public int dbcv_size;
|
|
||||||
public int dbcv_devicetype;
|
|
||||||
public int dbcv_reserved;
|
|
||||||
public int dbcv_unitmask;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -647,7 +647,7 @@ namespace Project
|
|||||||
/// <param name="rfidId">읽은 RFID ID</param>
|
/// <param name="rfidId">읽은 RFID ID</param>
|
||||||
/// <param name="motorDirection">모터 방향 (Forward/Backward)</param>
|
/// <param name="motorDirection">모터 방향 (Forward/Backward)</param>
|
||||||
/// <returns>업데이트 성공 여부</returns>
|
/// <returns>업데이트 성공 여부</returns>
|
||||||
public static bool UpdateAGVFromRFID(string rfidId, AgvDirection motorDirection = AgvDirection.Forward)
|
public static bool UpdateAGVFromRFID(ushort rfidId, AgvDirection motorDirection = AgvDirection.Forward)
|
||||||
{
|
{
|
||||||
var _mapNodes = PUB._mapCanvas.Nodes;
|
var _mapNodes = PUB._mapCanvas.Nodes;
|
||||||
if (_virtualAGV == null || _mapNodes == null) return false;
|
if (_virtualAGV == null || _mapNodes == null) return false;
|
||||||
|
|||||||
@@ -17,9 +17,6 @@ namespace Project
|
|||||||
{
|
{
|
||||||
PUB.bShutdown = true;
|
PUB.bShutdown = true;
|
||||||
|
|
||||||
// 장치 관리 태스크 종료
|
|
||||||
StopDeviceManagementTask();
|
|
||||||
|
|
||||||
PUB.AddEEDB("프로그램 종료");
|
PUB.AddEEDB("프로그램 종료");
|
||||||
PUB.log.Add("Program Close");
|
PUB.log.Add("Program Close");
|
||||||
PUB.LogFlush();
|
PUB.LogFlush();
|
||||||
|
|||||||
@@ -19,10 +19,8 @@ namespace Project
|
|||||||
private void AGV_Message(object sender, arDev.Narumi.MessageEventArgs e)
|
private void AGV_Message(object sender, arDev.Narumi.MessageEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.MsgType == arDev.arRS232.MessageType.Normal)
|
if (e.MsgType == arDev.arRS232.MessageType.Normal)
|
||||||
|
|
||||||
PUB.logagv.AddE(e.Message);
|
PUB.logagv.AddE(e.Message);
|
||||||
else if (e.MsgType == arDev.arRS232.MessageType.Normal)
|
else if (e.MsgType == arDev.arRS232.MessageType.Normal)
|
||||||
|
|
||||||
PUB.logagv.Add(e.Message);
|
PUB.logagv.Add(e.Message);
|
||||||
else if (e.MsgType == arDev.arRS232.MessageType.Recv)
|
else if (e.MsgType == arDev.arRS232.MessageType.Recv)
|
||||||
{
|
{
|
||||||
@@ -33,7 +31,6 @@ namespace Project
|
|||||||
PUB.logagv.Add("AGV-TX", e.Message);
|
PUB.logagv.Add("AGV-TX", e.Message);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
PUB.logagv.Add(e.MsgType.ToString(), e.Message);
|
PUB.logagv.Add(e.MsgType.ToString(), e.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,6 +41,7 @@ namespace Project
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
VAR.TIME.Set(eVarTime.LastRecv_AGV, DateTime.Now);
|
||||||
//데이터 파싱
|
//데이터 파싱
|
||||||
switch (e.DataType)
|
switch (e.DataType)
|
||||||
{
|
{
|
||||||
@@ -161,7 +159,7 @@ namespace Project
|
|||||||
{
|
{
|
||||||
//자동 실행 중이다.
|
//자동 실행 중이다.
|
||||||
|
|
||||||
PUB.Result.LastTAG = PUB.AGV.data.TagNo.ToString("0000");
|
PUB.Result.LastTAG = PUB.AGV.data.TagNo;//.ToString("0000");
|
||||||
PUB.log.Add($"AGV 태그수신 : {PUB.AGV.data.TagNo} LastTag:{PUB.Result.LastTAG}");
|
PUB.log.Add($"AGV 태그수신 : {PUB.AGV.data.TagNo} LastTag:{PUB.Result.LastTAG}");
|
||||||
//POT/NOT 보면 일단 바로 멈추게한다
|
//POT/NOT 보면 일단 바로 멈추게한다
|
||||||
if (PUB.Result.CurrentPos == ePosition.POT || PUB.Result.CurrentPos == ePosition.NOT)
|
if (PUB.Result.CurrentPos == ePosition.POT || PUB.Result.CurrentPos == ePosition.NOT)
|
||||||
@@ -172,7 +170,7 @@ namespace Project
|
|||||||
}
|
}
|
||||||
|
|
||||||
//virtual agv setting
|
//virtual agv setting
|
||||||
var CurrentNode = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId.Equals(PUB.Result.LastTAG, StringComparison.OrdinalIgnoreCase));
|
var CurrentNode = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId == PUB.Result.LastTAG);
|
||||||
if (CurrentNode == null)
|
if (CurrentNode == null)
|
||||||
{
|
{
|
||||||
//없는 노드는 자동으로 추가한다
|
//없는 노드는 자동으로 추가한다
|
||||||
@@ -237,7 +235,6 @@ namespace Project
|
|||||||
|
|
||||||
PUB._mapCanvas.PredictMessage = message;
|
PUB._mapCanvas.PredictMessage = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -154,8 +154,6 @@ namespace Project
|
|||||||
UpdateStatusMessage("준비 완료", Color.Red, Color.Gold);
|
UpdateStatusMessage("준비 완료", Color.Red, Color.Gold);
|
||||||
if (startuptime.Year == 1982) startuptime = DateTime.Now;
|
if (startuptime.Year == 1982) startuptime = DateTime.Now;
|
||||||
|
|
||||||
// 장치 관리 태스크 시작 (IDLE 진입 시 한 번만)
|
|
||||||
StartDeviceManagementTask();
|
|
||||||
|
|
||||||
// 동기화 모드 종료 (혹시 남아있을 경우)
|
// 동기화 모드 종료 (혹시 남아있을 경우)
|
||||||
if (PUB._mapCanvas != null)
|
if (PUB._mapCanvas != null)
|
||||||
|
|||||||
@@ -21,22 +21,230 @@ namespace Project
|
|||||||
{
|
{
|
||||||
DateTime chargesynctime = DateTime.Now;
|
DateTime chargesynctime = DateTime.Now;
|
||||||
DateTime agvsendstarttime = DateTime.Now;
|
DateTime agvsendstarttime = DateTime.Now;
|
||||||
|
DateTime lastXbeStatusSendTime = DateTime.Now;
|
||||||
|
DateTime lastBmsQueryTime = DateTime.Now;
|
||||||
|
|
||||||
void sm_SPS(object sender, EventArgs e)
|
void sm_SPS(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (PUB.sm.Step < eSMStep.IDLE || PUB.sm.Step >= eSMStep.CLOSING) return;
|
if (PUB.sm == null || PUB.sm.Step < eSMStep.IDLE || PUB.sm.Step >= eSMStep.CLOSING || PUB.bShutdown == true) return;
|
||||||
|
|
||||||
// SPS는 이제 간단한 작업만 수행
|
// SPS는 이제 간단한 작업만 수행
|
||||||
// 장치 연결 및 상태 전송은 별도 태스크(_DeviceManagement.cs)에서 처리
|
// 장치 연결 및 상태 전송은 별도 태스크(_DeviceManagement.cs)에서 처리
|
||||||
|
// 장치 연결이 별도로 존재할때 1회 수신 후 통신이 전체 먹통되는 증상이 있어 우선 복귀 251215
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 여기에 SPS에서 처리해야 할 간단한 작업만 남김
|
|
||||||
// 현재는 비어있음 - 필요한 경우 추가
|
// ========== 1. 장치 연결 관리 ==========
|
||||||
|
// AGV 연결
|
||||||
|
ConnectSerialPort(PUB.AGV, PUB.setting.Port_AGV, PUB.setting.Baud_AGV,
|
||||||
|
eVarTime.LastConn_AGV, eVarTime.LastConnTry_AGV, eVarTime.LastRecv_AGV);
|
||||||
|
|
||||||
|
// XBee 연결
|
||||||
|
ConnectSerialPort(PUB.XBE, PUB.setting.Port_XBE, PUB.setting.Baud_XBE,
|
||||||
|
eVarTime.LastConn_XBE, eVarTime.LastConnTry_XBE, eVarTime.LastRecv_XBE);
|
||||||
|
|
||||||
|
// BMS 연결
|
||||||
|
if (PUB.BMS.IsOpen == false)
|
||||||
|
{
|
||||||
|
var ts = VAR.TIME.RUN(eVarTime.LastConn_BAT);
|
||||||
|
if (ts.TotalSeconds > 3)
|
||||||
|
{
|
||||||
|
PUB.log.Add($"BMS 연결 시도: {PUB.setting.Port_BAT}");
|
||||||
|
PUB.BMS.PortName = PUB.setting.Port_BAT;
|
||||||
|
if (PUB.BMS.Open())
|
||||||
|
PUB.log.AddI($"BMS 연결 완료({PUB.setting.Port_BAT})");
|
||||||
|
|
||||||
|
VAR.TIME.Update(eVarTime.LastConn_BAT);
|
||||||
|
VAR.TIME.Update(eVarTime.LastConnTry_BAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (PUB.BMS.IsValid == false)
|
||||||
|
{
|
||||||
|
var ts = VAR.TIME.RUN(eVarTime.LastConnTry_BAT);
|
||||||
|
if (ts.TotalSeconds > (PUB.setting.interval_bms * 2.5))
|
||||||
|
{
|
||||||
|
this.BeginInvoke(new Action(() => {
|
||||||
|
PUB.log.Add("BMS 자동 연결 해제 (응답 없음)");
|
||||||
|
PUB.BMS.Close();
|
||||||
|
}));
|
||||||
|
VAR.TIME.Set(eVarTime.LastConn_BAT, DateTime.Now.AddSeconds(5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 2. XBee 상태 전송 ==========
|
||||||
|
if (PUB.XBE != null && PUB.XBE.IsOpen)
|
||||||
|
{
|
||||||
|
var tsXbe = DateTime.Now - lastXbeStatusSendTime;
|
||||||
|
if (tsXbe.TotalSeconds >= PUB.setting.interval_xbe)
|
||||||
|
{
|
||||||
|
lastXbeStatusSendTime = DateTime.Now;
|
||||||
|
ThreadPool.QueueUserWorkItem(_ =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PUB.XBE.SendStatus();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PUB.log.AddE($"XBee SendStatus 오류: {ex.Message}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 3. BMS 쿼리 및 배터리 경고 ==========
|
||||||
|
if (PUB.BMS != null && PUB.BMS.IsOpen)
|
||||||
|
{
|
||||||
|
var tsBms = DateTime.Now - lastBmsQueryTime;
|
||||||
|
if (tsBms.TotalSeconds >= PUB.setting.interval_bms)
|
||||||
|
{
|
||||||
|
lastBmsQueryTime = DateTime.Now;
|
||||||
|
ThreadPool.QueueUserWorkItem(_ =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PUB.BMS.SendQuery();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PUB.log.AddE($"BMS SendQuery 오류: {ex.Message}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 배터리 경고음
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Update_BatteryWarnSpeak();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PUB.log.AddE($"BatteryWarnSpeak 오류: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
PUB.log.AddE($"sm_SPS Exception: {ex.Message}");
|
PUB.log.AddE($"sm_SPS Exception: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 시리얼 포트 연결 (arDev.arRS232)
|
||||||
|
/// </summary>
|
||||||
|
bool ConnectSerialPort(arDev.arRS232 dev, string port, int baud, eVarTime conn, eVarTime conntry, eVarTime recvtime)
|
||||||
|
{
|
||||||
|
if (port.isEmpty()) return false;
|
||||||
|
|
||||||
|
if (dev.IsOpen == false && port.isEmpty() == false)
|
||||||
|
{
|
||||||
|
var tsPLC = VAR.TIME.RUN(conntry);
|
||||||
|
if (tsPLC.TotalSeconds > 5)
|
||||||
|
{
|
||||||
|
VAR.TIME.Update(conntry);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
VAR.TIME.Update(recvtime);
|
||||||
|
dev.PortName = port;
|
||||||
|
dev.BaudRate = baud;
|
||||||
|
PUB.log.Add($"Connect to {port}:{baud}");
|
||||||
|
if (dev.Open())
|
||||||
|
{
|
||||||
|
PUB.log.Add(port, $"[AGV:{port}:{baud}] 연결 완료");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//존재하지 않는 포트라면 sync를 벗어난다
|
||||||
|
var ports = System.IO.Ports.SerialPort.GetPortNames().Select(t => t.ToLower()).ToList();
|
||||||
|
if (ports.Contains(PUB.setting.Port_AGV.ToLower()) == false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var errmessage = dev.errorMessage;
|
||||||
|
PUB.log.AddE($"[AGV:{port}:{baud}] {errmessage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VAR.TIME.Update(conn);
|
||||||
|
VAR.TIME.Update(conntry);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PUB.log.AddE(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dev.PortName.Equals(port) == false)
|
||||||
|
{
|
||||||
|
this.BeginInvoke(new Action(() => {
|
||||||
|
PUB.log.Add(port, $"포트 변경({dev.PortName}->{port})으로 연결 종료");
|
||||||
|
dev.Close();
|
||||||
|
}));
|
||||||
|
|
||||||
|
VAR.TIME.Update(conntry);
|
||||||
|
}
|
||||||
|
else if (dev.IsOpen)
|
||||||
|
{
|
||||||
|
//연결은 되었으나 통신이 지난지 10초가 지났다면 자동종료한다
|
||||||
|
var tsRecv = VAR.TIME.RUN(recvtime);
|
||||||
|
var tsConn = VAR.TIME.RUN(conntry);
|
||||||
|
if (tsRecv.TotalSeconds > 10 && tsConn.TotalSeconds > 5)
|
||||||
|
{
|
||||||
|
this.BeginInvoke(new Action(() => {
|
||||||
|
PUB.log.Add($"{port} 자동 연결 해제 (응답 없음)");
|
||||||
|
dev.Close();
|
||||||
|
}));
|
||||||
|
VAR.TIME.Set(conntry, DateTime.Now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 시리얼 포트 연결 (Device.Xbee)
|
||||||
|
/// </summary>
|
||||||
|
void ConnectSerialPort(Device.Xbee dev, string port, int baud, eVarTime conn, eVarTime conntry, eVarTime recvtime)
|
||||||
|
{
|
||||||
|
if (dev.IsOpen == false && port.isEmpty() == false)
|
||||||
|
{
|
||||||
|
var tsPLC = VAR.TIME.RUN(conntry);
|
||||||
|
if (tsPLC.TotalSeconds > 5)
|
||||||
|
{
|
||||||
|
VAR.TIME.Update(conntry);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
VAR.TIME.Update(recvtime);
|
||||||
|
dev.PortName = port;
|
||||||
|
dev.BaudRate = baud;
|
||||||
|
if (dev.Open())
|
||||||
|
{
|
||||||
|
PUB.log.Add(port, $"[XBEE:{port}:{baud}] 연결 완료");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var errmessage = dev.errorMessage;
|
||||||
|
PUB.log.AddE($"[XBEE:{port}:{baud}] {errmessage}");
|
||||||
|
}
|
||||||
|
VAR.TIME.Update(conn);
|
||||||
|
VAR.TIME.Update(conntry);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PUB.log.AddE(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dev.PortName.Equals(port) == false)
|
||||||
|
{
|
||||||
|
this.BeginInvoke(new Action(() => {
|
||||||
|
PUB.log.Add(port, $"포트 변경({dev.PortName}->{port})으로 연결 종료");
|
||||||
|
dev.Close();
|
||||||
|
}));
|
||||||
|
VAR.TIME[(int)conntry] = DateTime.Now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,20 +48,24 @@ namespace Project
|
|||||||
if (data.Length > 4)
|
if (data.Length > 4)
|
||||||
{
|
{
|
||||||
var currTag = System.Text.Encoding.Default.GetString(data, 1, data.Length - 1);
|
var currTag = System.Text.Encoding.Default.GetString(data, 1, data.Length - 1);
|
||||||
var node = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId == currTag);
|
if (ushort.TryParse(currTag, out ushort currtagValue))
|
||||||
if (node == null)
|
|
||||||
{
|
{
|
||||||
PUB.log.AddE($"[{logPrefix}-SetCurrent] 노드정보를 찾을 수 없습니다 RFID:{currTag}");
|
var node = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId == currtagValue);
|
||||||
PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, $"{currTag}");
|
if (node == null)
|
||||||
return;
|
{
|
||||||
}
|
PUB.log.AddE($"[{logPrefix}-SetCurrent] 노드정보를 찾을 수 없습니다 RFID:{currTag}");
|
||||||
else
|
PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, $"{currTag}");
|
||||||
{
|
return;
|
||||||
PUB.log.AddI($"XBEE:현재위치설정:[{node.RfidId}]{node.Id}");
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
PUB.log.AddI($"XBEE:현재위치설정:[{node.RfidId}]{node.Id}");
|
||||||
|
}
|
||||||
|
|
||||||
PUB._mapCanvas.SetAGVPosition(PUB.setting.MCID, node, PUB._virtualAGV.CurrentDirection);
|
PUB._mapCanvas.SetAGVPosition(PUB.setting.MCID, node, PUB._virtualAGV.CurrentDirection);
|
||||||
PUB._virtualAGV.SetPosition(node, PUB._virtualAGV.CurrentDirection);
|
PUB._virtualAGV.SetPosition(node, PUB._virtualAGV.CurrentDirection);
|
||||||
|
}
|
||||||
|
else PUB.log.AddE($"[{logPrefix}-SetCurrent] TagString Value Errorr:{data}");
|
||||||
}
|
}
|
||||||
else PUB.log.AddE($"[{logPrefix}-SetCurrent] TagString Lenght Errorr:{data.Length}");
|
else PUB.log.AddE($"[{logPrefix}-SetCurrent] TagString Lenght Errorr:{data.Length}");
|
||||||
break;
|
break;
|
||||||
@@ -111,58 +115,61 @@ namespace Project
|
|||||||
if (data.Length > 4)
|
if (data.Length > 4)
|
||||||
{
|
{
|
||||||
var currTag = System.Text.Encoding.Default.GetString(data, 1, data.Length - 1);
|
var currTag = System.Text.Encoding.Default.GetString(data, 1, data.Length - 1);
|
||||||
var targetNode = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId == currTag);
|
if(ushort.TryParse(currTag, out ushort currtagvalue))
|
||||||
|
|
||||||
|
|
||||||
//자동상태가아니라면 처리하지 않는다.
|
|
||||||
if (VAR.BOOL[eVarBool.FLAG_AUTORUN] == false)
|
|
||||||
{
|
{
|
||||||
PUB.log.AddE($"[{logPrefix}-Goto] 자동실행상태가 아닙니다");
|
var targetNode = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId == currtagvalue);
|
||||||
PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.ManualMode, $"{currTag}");
|
|
||||||
}
|
|
||||||
|
|
||||||
//목적지
|
|
||||||
PUB._virtualAGV.TargetNode = targetNode;
|
|
||||||
if (targetNode == null)
|
|
||||||
{
|
|
||||||
PUB.log.AddE($"[{logPrefix}-Goto] 노드정보를 찾을 수 없습니다 RFID:{currTag}");
|
|
||||||
PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, $"{currTag}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
///출발지
|
//자동상태가아니라면 처리하지 않는다.
|
||||||
var startNode = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId == PUB._virtualAGV.CurrentNode.Id);
|
if (VAR.BOOL[eVarBool.FLAG_AUTORUN] == false)
|
||||||
PUB._virtualAGV.StartNode = startNode;
|
|
||||||
if (startNode == null)
|
|
||||||
{
|
|
||||||
PUB.log.AddE($"[{logPrefix}-Goto] 시작노드가 없습니다(현재위치 없음) NodeID:{PUB._virtualAGV.CurrentNode.Id}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startNode != null)
|
|
||||||
{
|
|
||||||
//시작위치가 존재한다면 경로를 예측한다.
|
|
||||||
var rltGoto = CalcPath(startNode, targetNode);
|
|
||||||
if (rltGoto.result == null)
|
|
||||||
{
|
{
|
||||||
PUB.log.AddE($"[{logPrefix}-Goto] 경로예측실패 {rltGoto.message}");
|
PUB.log.AddE($"[{logPrefix}-Goto] 자동실행상태가 아닙니다");
|
||||||
|
PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.ManualMode, $"{currTag}");
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
//목적지
|
||||||
|
PUB._virtualAGV.TargetNode = targetNode;
|
||||||
|
if (targetNode == null)
|
||||||
{
|
{
|
||||||
//경로예측을 화면에 표시해준다.
|
PUB.log.AddE($"[{logPrefix}-Goto] 노드정보를 찾을 수 없습니다 RFID:{currTag}");
|
||||||
PUB._virtualAGV.SetPath(rltGoto.result);
|
PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, $"{currTag}");
|
||||||
var pathWithRfid = rltGoto.result.GetSimplePath().Select(nodeId => PUB._virtualAGV.GetRfidByNodeId(PUB._mapCanvas.Nodes, nodeId)).ToList();
|
return;
|
||||||
PUB.log.Add($"경로예측결과:{pathWithRfid}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///출발지
|
||||||
|
var startNode = PUB._mapCanvas.Nodes.FirstOrDefault(t => t.RfidId == PUB._virtualAGV.CurrentNode.RfidId);
|
||||||
|
PUB._virtualAGV.StartNode = startNode;
|
||||||
|
if (startNode == null)
|
||||||
|
{
|
||||||
|
PUB.log.AddE($"[{logPrefix}-Goto] 시작노드가 없습니다(현재위치 없음) NodeID:{PUB._virtualAGV.CurrentNode.Id}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startNode != null)
|
||||||
|
{
|
||||||
|
//시작위치가 존재한다면 경로를 예측한다.
|
||||||
|
var rltGoto = CalcPath(startNode, targetNode);
|
||||||
|
if (rltGoto.result == null)
|
||||||
|
{
|
||||||
|
PUB.log.AddE($"[{logPrefix}-Goto] 경로예측실패 {rltGoto.message}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//경로예측을 화면에 표시해준다.
|
||||||
|
PUB._virtualAGV.SetPath(rltGoto.result);
|
||||||
|
var pathWithRfid = rltGoto.result.GetSimplePath().Select(nodeId => PUB._virtualAGV.GetRfidByNodeId(PUB._mapCanvas.Nodes, nodeId)).ToList();
|
||||||
|
PUB.log.Add($"경로예측결과:{pathWithRfid}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//대상이동으로 처리한다.
|
||||||
|
PUB.sm.SetNewRunStep(StateMachine.ERunStep.GOTO);
|
||||||
|
|
||||||
|
//Move to
|
||||||
|
PUB.log.Add($"[{logPrefix}-Goto] {startNode.RfidId} -> {targetNode.RfidId}");
|
||||||
}
|
}
|
||||||
|
else PUB.log.AddE($"[{logPrefix}-Goto] TagString Value Error:{data}");
|
||||||
//대상이동으로 처리한다.
|
|
||||||
PUB.sm.SetNewRunStep(StateMachine.ERunStep.GOTO);
|
|
||||||
|
|
||||||
//Move to
|
|
||||||
PUB.log.Add($"[{logPrefix}-Goto] {startNode.RfidId} -> {targetNode.RfidId}");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else PUB.log.AddE($"[{logPrefix}-Goto] TagString Lenght Errorr:{data.Length}");
|
else PUB.log.AddE($"[{logPrefix}-Goto] TagString Lenght Error:{data.Length}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ENIGProtocol.AGVCommandHE.Stop: //stop
|
case ENIGProtocol.AGVCommandHE.Stop: //stop
|
||||||
|
|||||||
95
Cs_HMI/Project/ViewForm/fAgv.Designer.cs
generated
95
Cs_HMI/Project/ViewForm/fAgv.Designer.cs
generated
@@ -48,13 +48,14 @@
|
|||||||
this.button4 = new System.Windows.Forms.Button();
|
this.button4 = new System.Windows.Forms.Button();
|
||||||
this.button9 = new System.Windows.Forms.Button();
|
this.button9 = new System.Windows.Forms.Button();
|
||||||
this.panel2 = new System.Windows.Forms.Panel();
|
this.panel2 = new System.Windows.Forms.Panel();
|
||||||
this.button16 = new System.Windows.Forms.Button();
|
this.button15 = new System.Windows.Forms.Button();
|
||||||
this.button10 = new System.Windows.Forms.Button();
|
this.button14 = new System.Windows.Forms.Button();
|
||||||
this.button11 = new System.Windows.Forms.Button();
|
this.button11 = new System.Windows.Forms.Button();
|
||||||
this.button12 = new System.Windows.Forms.Button();
|
this.button12 = new System.Windows.Forms.Button();
|
||||||
this.button13 = new System.Windows.Forms.Button();
|
this.button13 = new System.Windows.Forms.Button();
|
||||||
this.button14 = new System.Windows.Forms.Button();
|
this.button10 = new System.Windows.Forms.Button();
|
||||||
this.button15 = new System.Windows.Forms.Button();
|
this.button16 = new System.Windows.Forms.Button();
|
||||||
|
this.lbPortName = new System.Windows.Forms.Label();
|
||||||
this.tableLayoutPanel1.SuspendLayout();
|
this.tableLayoutPanel1.SuspendLayout();
|
||||||
this.panel1.SuspendLayout();
|
this.panel1.SuspendLayout();
|
||||||
this.panel2.SuspendLayout();
|
this.panel2.SuspendLayout();
|
||||||
@@ -269,6 +270,7 @@
|
|||||||
//
|
//
|
||||||
// panel2
|
// panel2
|
||||||
//
|
//
|
||||||
|
this.panel2.Controls.Add(this.lbPortName);
|
||||||
this.panel2.Controls.Add(this.button15);
|
this.panel2.Controls.Add(this.button15);
|
||||||
this.panel2.Controls.Add(this.button14);
|
this.panel2.Controls.Add(this.button14);
|
||||||
this.panel2.Controls.Add(this.button11);
|
this.panel2.Controls.Add(this.button11);
|
||||||
@@ -283,27 +285,27 @@
|
|||||||
this.panel2.Size = new System.Drawing.Size(1050, 58);
|
this.panel2.Size = new System.Drawing.Size(1050, 58);
|
||||||
this.panel2.TabIndex = 8;
|
this.panel2.TabIndex = 8;
|
||||||
//
|
//
|
||||||
// button16
|
// button15
|
||||||
//
|
//
|
||||||
this.button16.Dock = System.Windows.Forms.DockStyle.Left;
|
this.button15.Dock = System.Windows.Forms.DockStyle.Left;
|
||||||
this.button16.Location = new System.Drawing.Point(0, 0);
|
this.button15.Location = new System.Drawing.Point(523, 0);
|
||||||
this.button16.Name = "button16";
|
this.button15.Name = "button15";
|
||||||
this.button16.Size = new System.Drawing.Size(162, 58);
|
this.button15.Size = new System.Drawing.Size(84, 58);
|
||||||
this.button16.TabIndex = 0;
|
this.button15.TabIndex = 14;
|
||||||
this.button16.Text = "백턴유지시간";
|
this.button15.Text = "Mag Off";
|
||||||
this.button16.UseVisualStyleBackColor = true;
|
this.button15.UseVisualStyleBackColor = true;
|
||||||
this.button16.Click += new System.EventHandler(this.button16_Click);
|
this.button15.Click += new System.EventHandler(this.button15_Click);
|
||||||
//
|
//
|
||||||
// button10
|
// button14
|
||||||
//
|
//
|
||||||
this.button10.Dock = System.Windows.Forms.DockStyle.Left;
|
this.button14.Dock = System.Windows.Forms.DockStyle.Left;
|
||||||
this.button10.Location = new System.Drawing.Point(162, 0);
|
this.button14.Location = new System.Drawing.Point(439, 0);
|
||||||
this.button10.Name = "button10";
|
this.button14.Name = "button14";
|
||||||
this.button10.Size = new System.Drawing.Size(162, 58);
|
this.button14.Size = new System.Drawing.Size(84, 58);
|
||||||
this.button10.TabIndex = 1;
|
this.button14.TabIndex = 13;
|
||||||
this.button10.Text = "GateOut Off Time";
|
this.button14.Text = "Mag On";
|
||||||
this.button10.UseVisualStyleBackColor = true;
|
this.button14.UseVisualStyleBackColor = true;
|
||||||
this.button10.Click += new System.EventHandler(this.button10_Click);
|
this.button14.Click += new System.EventHandler(this.button14_Click);
|
||||||
//
|
//
|
||||||
// button11
|
// button11
|
||||||
//
|
//
|
||||||
@@ -338,27 +340,39 @@
|
|||||||
this.button13.UseVisualStyleBackColor = true;
|
this.button13.UseVisualStyleBackColor = true;
|
||||||
this.button13.Click += new System.EventHandler(this.button13_Click);
|
this.button13.Click += new System.EventHandler(this.button13_Click);
|
||||||
//
|
//
|
||||||
// button14
|
// button10
|
||||||
//
|
//
|
||||||
this.button14.Dock = System.Windows.Forms.DockStyle.Left;
|
this.button10.Dock = System.Windows.Forms.DockStyle.Left;
|
||||||
this.button14.Location = new System.Drawing.Point(439, 0);
|
this.button10.Location = new System.Drawing.Point(162, 0);
|
||||||
this.button14.Name = "button14";
|
this.button10.Name = "button10";
|
||||||
this.button14.Size = new System.Drawing.Size(84, 58);
|
this.button10.Size = new System.Drawing.Size(162, 58);
|
||||||
this.button14.TabIndex = 13;
|
this.button10.TabIndex = 1;
|
||||||
this.button14.Text = "Mag On";
|
this.button10.Text = "GateOut Off Time";
|
||||||
this.button14.UseVisualStyleBackColor = true;
|
this.button10.UseVisualStyleBackColor = true;
|
||||||
this.button14.Click += new System.EventHandler(this.button14_Click);
|
this.button10.Click += new System.EventHandler(this.button10_Click);
|
||||||
//
|
//
|
||||||
// button15
|
// button16
|
||||||
//
|
//
|
||||||
this.button15.Dock = System.Windows.Forms.DockStyle.Left;
|
this.button16.Dock = System.Windows.Forms.DockStyle.Left;
|
||||||
this.button15.Location = new System.Drawing.Point(523, 0);
|
this.button16.Location = new System.Drawing.Point(0, 0);
|
||||||
this.button15.Name = "button15";
|
this.button16.Name = "button16";
|
||||||
this.button15.Size = new System.Drawing.Size(84, 58);
|
this.button16.Size = new System.Drawing.Size(162, 58);
|
||||||
this.button15.TabIndex = 14;
|
this.button16.TabIndex = 0;
|
||||||
this.button15.Text = "Mag Off";
|
this.button16.Text = "백턴유지시간";
|
||||||
this.button15.UseVisualStyleBackColor = true;
|
this.button16.UseVisualStyleBackColor = true;
|
||||||
this.button15.Click += new System.EventHandler(this.button15_Click);
|
this.button16.Click += new System.EventHandler(this.button16_Click);
|
||||||
|
//
|
||||||
|
// lbPortName
|
||||||
|
//
|
||||||
|
this.lbPortName.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.lbPortName.Font = new System.Drawing.Font("Tahoma", 15F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
|
this.lbPortName.ForeColor = System.Drawing.Color.White;
|
||||||
|
this.lbPortName.Location = new System.Drawing.Point(607, 0);
|
||||||
|
this.lbPortName.Name = "lbPortName";
|
||||||
|
this.lbPortName.Size = new System.Drawing.Size(203, 58);
|
||||||
|
this.lbPortName.TabIndex = 15;
|
||||||
|
this.lbPortName.Text = "--";
|
||||||
|
this.lbPortName.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
//
|
//
|
||||||
// fAgv
|
// fAgv
|
||||||
//
|
//
|
||||||
@@ -409,5 +423,6 @@
|
|||||||
private System.Windows.Forms.Button button11;
|
private System.Windows.Forms.Button button11;
|
||||||
private System.Windows.Forms.Button button12;
|
private System.Windows.Forms.Button button12;
|
||||||
private System.Windows.Forms.Button button13;
|
private System.Windows.Forms.Button button13;
|
||||||
|
private System.Windows.Forms.Label lbPortName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ namespace Project.ViewForm
|
|||||||
richTextBox2.Rtf = PUB.AGV.system1.ToRtfString();
|
richTextBox2.Rtf = PUB.AGV.system1.ToRtfString();
|
||||||
richTextBox3.Rtf = CombineRtfStrings(PUB.AGV.signal.ToRtfString(), PUB.AGV.data.ToRtfString());
|
richTextBox3.Rtf = CombineRtfStrings(PUB.AGV.signal.ToRtfString(), PUB.AGV.data.ToRtfString());
|
||||||
richTextBox4.Rtf = PUB.AGV.error.ToRtfString();
|
richTextBox4.Rtf = PUB.AGV.error.ToRtfString();
|
||||||
|
lbPortName.Text = $"AGV:{PUB.setting.Port_AGV}\nBMS:{PUB.setting.Port_BAT}";
|
||||||
timer1.Start();
|
timer1.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,28 +30,11 @@ namespace Project.ViewForm
|
|||||||
|
|
||||||
InitializeMapCanvas();
|
InitializeMapCanvas();
|
||||||
|
|
||||||
//PUB.mapctl = new AGVControl.MapControl();
|
|
||||||
//PUB.mapctl.Dock = DockStyle.Fill;
|
|
||||||
//PUB.mapctl.Visible = true;
|
|
||||||
//PUB.mapctl.Font = this.panel1.Font;
|
|
||||||
//PUB.mapctl.BackColor = Color.FromArgb(32, 32, 32);
|
|
||||||
//this.panel1.Controls.Add(PUB.mapctl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeMapCanvas()
|
private void InitializeMapCanvas()
|
||||||
{
|
{
|
||||||
// RfidMappings 제거 - MapNode에 통합됨
|
PUB._mapCanvas.NodeSelect += OnNodeSelected;;
|
||||||
|
|
||||||
// 이벤트 연결
|
|
||||||
//PUB._mapCanvas.NodeAdded += OnNodeAdded;
|
|
||||||
// 이벤트 연결
|
|
||||||
//PUB._mapCanvas.NodeAdded += OnNodeAdded;
|
|
||||||
PUB._mapCanvas.NodeSelect += OnNodeSelected;
|
|
||||||
//PUB._mapCanvas.NodeMoved += OnNodeMoved;
|
|
||||||
//PUB._mapCanvas.NodeDeleted += OnNodeDeleted;
|
|
||||||
//PUB._mapCanvas.ConnectionDeleted += OnConnectionDeleted;
|
|
||||||
//PUB._mapCanvas.ImageNodeDoubleClicked += OnImageNodeDoubleClicked;
|
|
||||||
//PUB._mapCanvas.MapChanged += OnMapChanged;
|
|
||||||
|
|
||||||
// 스플리터 패널에 맵 캔버스 추가
|
// 스플리터 패널에 맵 캔버스 추가
|
||||||
panel1.Controls.Add(PUB._mapCanvas);
|
panel1.Controls.Add(PUB._mapCanvas);
|
||||||
@@ -153,83 +136,6 @@ namespace Project.ViewForm
|
|||||||
PUB.AGV.DataReceive += AGV_DataReceive;
|
PUB.AGV.DataReceive += AGV_DataReceive;
|
||||||
|
|
||||||
|
|
||||||
//auto load
|
|
||||||
var mapPath = new System.IO.DirectoryInfo("route");
|
|
||||||
if (mapPath.Exists == false) mapPath.Create();
|
|
||||||
|
|
||||||
|
|
||||||
//맵파일로딩
|
|
||||||
if (PUB.setting.LastMapFile.isEmpty()) PUB.setting.LastMapFile = System.IO.Path.Combine(mapPath.FullName, "default.json");
|
|
||||||
System.IO.FileInfo filePath = new System.IO.FileInfo(PUB.setting.LastMapFile);
|
|
||||||
if (filePath.Exists == false) filePath = new System.IO.FileInfo(System.IO.Path.Combine(mapPath.FullName, "default.json"));
|
|
||||||
if (filePath.Exists == false) //그래도없다면 맵폴더에서 파일을 찾아본다.
|
|
||||||
{
|
|
||||||
var files = mapPath.GetFiles("*.json");
|
|
||||||
if (files.Any()) filePath = files[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filePath.Exists)
|
|
||||||
{
|
|
||||||
var result = MapLoader.LoadMapFromFile(filePath.FullName);
|
|
||||||
|
|
||||||
if (result.Success)
|
|
||||||
{
|
|
||||||
if (PUB._mapCanvas.Nodes == null) PUB._mapCanvas.Nodes = new List<MapNode>();
|
|
||||||
else PUB._mapCanvas.Nodes.Clear();
|
|
||||||
PUB._mapCanvas.Nodes.AddRange(result.Nodes);
|
|
||||||
|
|
||||||
// 맵 캔버스에 데이터 설정
|
|
||||||
PUB._mapCanvas.Nodes = PUB._mapCanvas.Nodes;
|
|
||||||
PUB._mapCanvas.MapFileName = filePath.FullName;
|
|
||||||
|
|
||||||
// 🔥 맵 설정 적용 (배경색, 그리드 표시)
|
|
||||||
if (result.Settings != null)
|
|
||||||
{
|
|
||||||
PUB._mapCanvas.BackColor = System.Drawing.Color.FromArgb(result.Settings.BackgroundColorArgb);
|
|
||||||
PUB._mapCanvas.ShowGrid = result.Settings.ShowGrid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 🔥 가상 AGV 초기화 (첫 노드 위치에 생성)
|
|
||||||
if (PUB._virtualAGV == null && PUB._mapCanvas.Nodes.Count > 0)
|
|
||||||
{
|
|
||||||
var startNode = PUB._mapCanvas.Nodes.FirstOrDefault(n => n.IsNavigationNode());
|
|
||||||
if (startNode != null)
|
|
||||||
{
|
|
||||||
PUB._virtualAGV = new VirtualAGV(PUB.setting.MCID, startNode.Position, AgvDirection.Forward);
|
|
||||||
PUB._virtualAGV.LowBatteryThreshold = PUB.setting.BatteryLimit_Low;
|
|
||||||
PUB._virtualAGV.SetPosition(startNode, AgvDirection.Forward);
|
|
||||||
|
|
||||||
// 캔버스에 AGV 리스트 설정
|
|
||||||
var agvList = new System.Collections.Generic.List<AGVNavigationCore.Controls.IAGV> { PUB._virtualAGV };
|
|
||||||
PUB._mapCanvas.AGVList = agvList;
|
|
||||||
|
|
||||||
PUB.log.Add($"가상 AGV 생성: {startNode.Id} 위치");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (PUB._virtualAGV != null)
|
|
||||||
{
|
|
||||||
PUB._virtualAGV.LowBatteryThreshold = PUB.setting.BatteryLimit_Low;
|
|
||||||
// 기존 AGV가 있으면 캔버스에 다시 연결
|
|
||||||
var agvList = new System.Collections.Generic.List<AGVNavigationCore.Controls.IAGV> { PUB._virtualAGV };
|
|
||||||
PUB._mapCanvas.AGVList = agvList;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 맵 로드 후 자동으로 맵에 맞춤
|
|
||||||
PUB._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;
|
//var fn = string.Empty;
|
||||||
//if (files.Any() == false)
|
//if (files.Any() == false)
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ namespace Project
|
|||||||
bool remoteClose = false;
|
bool remoteClose = false;
|
||||||
bool forceClose = false;
|
bool forceClose = false;
|
||||||
|
|
||||||
readonly usbdetect.DriveDetector usbdet;
|
|
||||||
public fMain()
|
public fMain()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -57,10 +56,6 @@ namespace Project
|
|||||||
|
|
||||||
if (DateTime.Now > PUB.LastInputTime) PUB.LastInputTime = DateTime.Now;
|
if (DateTime.Now > PUB.LastInputTime) PUB.LastInputTime = DateTime.Now;
|
||||||
};
|
};
|
||||||
usbdet = new usbdetect.DriveDetector(this);
|
|
||||||
usbdet.DeviceArrived += Usbdet_DeviceArrived;
|
|
||||||
usbdet.DeviceRemoved += Usbdet_DeviceRemoved;
|
|
||||||
|
|
||||||
|
|
||||||
PUB._mapCanvas = new AGVNavigationCore.Controls.UnifiedAGVCanvas();
|
PUB._mapCanvas = new AGVNavigationCore.Controls.UnifiedAGVCanvas();
|
||||||
PUB._mapCanvas.Dock = DockStyle.Fill;
|
PUB._mapCanvas.Dock = DockStyle.Fill;
|
||||||
@@ -68,8 +63,6 @@ namespace Project
|
|||||||
PUB._mapCanvas.BackColor = Color.FromArgb(32, 32, 32);
|
PUB._mapCanvas.BackColor = Color.FromArgb(32, 32, 32);
|
||||||
PUB._mapCanvas.ForeColor = Color.White;
|
PUB._mapCanvas.ForeColor = Color.White;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.panTopMenu.MouseMove += LbTitle_MouseMove;
|
this.panTopMenu.MouseMove += LbTitle_MouseMove;
|
||||||
this.panTopMenu.MouseUp += LbTitle_MouseUp;
|
this.panTopMenu.MouseUp += LbTitle_MouseUp;
|
||||||
this.panTopMenu.MouseDown += LbTitle_MouseDown;
|
this.panTopMenu.MouseDown += LbTitle_MouseDown;
|
||||||
@@ -82,32 +75,6 @@ namespace Project
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void WndProc(ref Message m)
|
|
||||||
{
|
|
||||||
base.WndProc(ref m);
|
|
||||||
if (usbdet != null)
|
|
||||||
{
|
|
||||||
usbdet.WndProc(ref m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Usbdet_DeviceRemoved(object sender, usbdetect.DriveDetectorEventArgs e)
|
|
||||||
{
|
|
||||||
//throw new NotImplementedException();
|
|
||||||
Console.WriteLine(e.Drive);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Usbdet_DeviceArrived(object sender, usbdetect.DriveDetectorEventArgs e)
|
|
||||||
{
|
|
||||||
//throw new NotImplementedException();
|
|
||||||
using (var fUpdate = new Dialog.fUpdateForm(e.Drive))
|
|
||||||
if (fUpdate.ShowDialog() == DialogResult.Yes)
|
|
||||||
{
|
|
||||||
//종료한다
|
|
||||||
remoteClose = true;
|
|
||||||
this.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void __Closing(object sender, FormClosingEventArgs e)
|
private void __Closing(object sender, FormClosingEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -182,6 +149,9 @@ namespace Project
|
|||||||
|
|
||||||
VAR.STR[eVarString.SWVersion] = Application.ProductVersion;
|
VAR.STR[eVarString.SWVersion] = Application.ProductVersion;
|
||||||
|
|
||||||
|
|
||||||
|
AutoLoadMapData();
|
||||||
|
|
||||||
//SETTING H/W
|
//SETTING H/W
|
||||||
this.IOState.ItemClick += gridView2_ItemClick;
|
this.IOState.ItemClick += gridView2_ItemClick;
|
||||||
VAR.BOOL.PropertyChanged += BOOL_PropertyChanged;
|
VAR.BOOL.PropertyChanged += BOOL_PropertyChanged;
|
||||||
@@ -290,6 +260,71 @@ namespace Project
|
|||||||
PUB.AddEEDB("프로그램 시작");
|
PUB.AddEEDB("프로그램 시작");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AutoLoadMapData()
|
||||||
|
{
|
||||||
|
//auto load
|
||||||
|
var mapPath = new System.IO.DirectoryInfo("route");
|
||||||
|
if (mapPath.Exists == false) mapPath.Create();
|
||||||
|
|
||||||
|
//맵파일로딩
|
||||||
|
if (PUB.setting.LastMapFile.isEmpty()) PUB.setting.LastMapFile = System.IO.Path.Combine(mapPath.FullName, "default.json");
|
||||||
|
System.IO.FileInfo filePath = new System.IO.FileInfo(PUB.setting.LastMapFile);
|
||||||
|
if (filePath.Exists == false) filePath = new System.IO.FileInfo(System.IO.Path.Combine(mapPath.FullName, "default.json"));
|
||||||
|
if (filePath.Exists == false) //그래도없다면 맵폴더에서 파일을 찾아본다.
|
||||||
|
{
|
||||||
|
var files = mapPath.GetFiles("*.json");
|
||||||
|
if (files.Any()) filePath = files[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filePath.Exists)
|
||||||
|
{
|
||||||
|
var result = MapLoader.LoadMapFromFile(filePath.FullName);
|
||||||
|
|
||||||
|
if (result.Success)
|
||||||
|
{
|
||||||
|
PUB._mapCanvas.SetMapLoadResult(result);
|
||||||
|
PUB._mapCanvas.MapFileName = filePath.FullName;
|
||||||
|
|
||||||
|
// 🔥 가상 AGV 초기화 (첫 노드 위치에 생성)
|
||||||
|
if (PUB._virtualAGV == null && PUB._mapCanvas.Nodes.Count > 0)
|
||||||
|
{
|
||||||
|
var startNode = PUB._mapCanvas.Nodes.FirstOrDefault(n => n.IsNavigationNode());
|
||||||
|
if (startNode != null)
|
||||||
|
{
|
||||||
|
PUB._virtualAGV = new VirtualAGV(PUB.setting.MCID, startNode.Position, AgvDirection.Forward);
|
||||||
|
PUB._virtualAGV.LowBatteryThreshold = PUB.setting.BatteryLimit_Low;
|
||||||
|
PUB._virtualAGV.SetPosition(startNode, AgvDirection.Forward);
|
||||||
|
|
||||||
|
// 캔버스에 AGV 리스트 설정
|
||||||
|
var agvList = new System.Collections.Generic.List<AGVNavigationCore.Controls.IAGV> { PUB._virtualAGV };
|
||||||
|
PUB._mapCanvas.AGVList = agvList;
|
||||||
|
|
||||||
|
PUB.log.Add($"가상 AGV 생성: {startNode.Id} 위치");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (PUB._virtualAGV != null)
|
||||||
|
{
|
||||||
|
PUB._virtualAGV.LowBatteryThreshold = PUB.setting.BatteryLimit_Low;
|
||||||
|
// 기존 AGV가 있으면 캔버스에 다시 연결
|
||||||
|
var agvList = new System.Collections.Generic.List<AGVNavigationCore.Controls.IAGV> { PUB._virtualAGV };
|
||||||
|
PUB._mapCanvas.AGVList = agvList;
|
||||||
|
}
|
||||||
|
PUB.log.Add($"맵 파일 로드 완료: {filePath.Name}, 노드 수: {result.Nodes.Count}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PUB.log.Add($"맵 파일 로딩 실패: {result.ErrorMessage}");
|
||||||
|
MessageBox.Show($"맵 파일 로딩 실패: {result.ErrorMessage}", "오류",
|
||||||
|
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PUB.log.Add($"맵 파일을 찾을 수 없습니다: {filePath.FullName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#region "Mouse Form Move"
|
#region "Mouse Form Move"
|
||||||
|
|
||||||
private Boolean fMove = false;
|
private Boolean fMove = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user