This commit is contained in:
backuppc
2025-12-15 17:34:43 +09:00
parent 9db88e5d6b
commit a7f938ff19
29 changed files with 535 additions and 1556 deletions

View File

@@ -36,20 +36,20 @@ namespace AGVMapEditor.Forms
{
public string FromNodeId { get; set; }
public string FromNodeName { get; set; }
public string FromRfidId { get; set; }
public ushort FromRfidId { get; set; }
public string ToNodeId { get; set; }
public string ToNodeName { get; set; }
public string ToRfidId { get; set; }
public ushort ToRfidId { get; set; }
public string ConnectionType { get; set; }
public override string ToString()
{
// RFID가 있으면 RFID(노드이름), 없으면 NodeID(노드이름) 형태로 표시
string fromDisplay = !string.IsNullOrEmpty(FromRfidId)
string fromDisplay = FromRfidId > 0
? $"{FromRfidId}({FromNodeName})"
: $"---({FromNodeId})";
string toDisplay = !string.IsNullOrEmpty(ToRfidId)
string toDisplay = ToRfidId > 0
? $"{ToRfidId}({ToNodeName})"
: $"---({ToNodeId})";
@@ -420,7 +420,7 @@ namespace AGVMapEditor.Forms
return;
}
var rfidDisplay = (_selectedNode as MapNode)?.RfidId ?? "";
var rfidDisplay = (_selectedNode as MapNode)?.RfidId ?? 0;
var result = MessageBox.Show($"노드 {rfidDisplay}[{_selectedNode.Id}] 를 삭제하시겠습니까?\n연결된 RFID 매핑도 함께 삭제됩니다.",
"삭제 확인", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
@@ -1107,8 +1107,8 @@ namespace AGVMapEditor.Forms
// RFID 값 변경시 중복 검사
if (e.ChangedItem.PropertyDescriptor.Name == "RFID")
{
string newRfidValue = e.ChangedItem.Value?.ToString();
if (!string.IsNullOrEmpty(newRfidValue) && CheckRfidDuplicate(newRfidValue))
var newRfidValue = ushort.Parse(e.ChangedItem.Value?.ToString());
if (newRfidValue != 0 && CheckRfidDuplicate(newRfidValue))
{
// 중복된 RFID 값 발견
MessageBox.Show($"RFID 값 '{newRfidValue}'이(가) 이미 다른 노드에서 사용 중입니다.\n입력값을 되돌립니다.",
@@ -1174,29 +1174,15 @@ namespace AGVMapEditor.Forms
/// </summary>
/// <param name="rfidValue">검사할 RFID 값</param>
/// <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;
// 현재 편집 중인 노드 제외하고 중복 검사
string currentNodeId = null;
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;
foreach (var node in this._mapCanvas.Nodes)
{
@@ -1205,7 +1191,7 @@ namespace AGVMapEditor.Forms
continue;
// 같은 RFID 값을 가진 노드가 있는지 확인
if (!string.IsNullOrEmpty(node.RfidId) && node.RfidId.Equals(rfidValue, StringComparison.OrdinalIgnoreCase))
if (node.RfidId != 0 && node.RfidId == rfidValue)
{
duplicateCount++;
break; // 하나라도 발견되면 중복

View File

@@ -1140,7 +1140,8 @@ namespace AGVNavigationCore.Controls
// 위쪽에 표시할 이름 (노드의 Name 속성)
string TopIDText = node.HasRfid() ? node.RfidId : $"[{node.Id}]";
string TopIDText = node.HasRfid() ? node.RfidId.ToString("0000") : $"[{node.Id}]";
// 아래쪽에 표시할 값 (RFID 우선, 없으면 노드ID)
string BottomLabelText = node.Text;

View File

@@ -121,7 +121,7 @@ namespace AGVNavigationCore.Controls
if (hitNode == null) return;
if (hitNode.Type == NodeType.Normal)
if (hitNode.Type == NodeType.Normal)
{
HandleNormalNodeDoubleClick(hitNode as MapNode);
}
@@ -146,15 +146,19 @@ namespace AGVNavigationCore.Controls
private void HandleNormalNodeDoubleClick(MapNode node)
{
// RFID 입력창 표시
string currentRfid = node.RfidId ?? "";
var currentRfid = node.RfidId;
string newRfid = Microsoft.VisualBasic.Interaction.InputBox(
$"노드 '{node.RfidId}[{node.Id}]'의 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);
Invalidate();
}
@@ -229,7 +233,7 @@ namespace AGVNavigationCore.Controls
return;
}
}
// 팬 시작 (좌클릭 - 모드에 따라)
@@ -272,7 +276,7 @@ namespace AGVNavigationCore.Controls
// 호버 업데이트
var newHoveredNode = GetItemAt(worldPoint);
bool hoverChanged = (newHoveredNode != _hoveredNode) ;
bool hoverChanged = (newHoveredNode != _hoveredNode);
if (hoverChanged)
{
@@ -700,8 +704,8 @@ namespace AGVNavigationCore.Controls
{
// 연결선을 클릭했을 때 삭제 확인
var (fromNode, toNode) = connection.Value;
string fromDisplay = !string.IsNullOrEmpty(fromNode.RfidId) ? fromNode.RfidId : fromNode.Id;
string toDisplay = !string.IsNullOrEmpty(toNode.RfidId) ? toNode.RfidId : toNode.Id;
string fromDisplay = fromNode.HasRfid() ? fromNode.RfidId.ToString("0000") : fromNode.Id;
string toDisplay = toNode.HasRfid() ? toNode.RfidId.ToString("0000") : toNode.Id;
var result = MessageBox.Show(
$"연결을 삭제하시겠습니까?\n\n{fromDisplay} ↔ {toDisplay}",

View File

@@ -851,13 +851,13 @@ namespace AGVNavigationCore.Controls
return;
// RFID값과 해당 노드의 인덱스를 저장
var rfidToNodeIndex = new Dictionary<string, List<int>>();
var rfidToNodeIndex = new Dictionary<ushort, List<int>>();
// 모든 노드의 RFID값 수집
for (int i = 0; i < _nodes.Count; i++)
{
var node = _nodes[i];
if (!string.IsNullOrEmpty(node.RfidId))
if (node.HasRfid())
{
if (!rfidToNodeIndex.ContainsKey(node.RfidId))
{

View File

@@ -83,7 +83,7 @@ namespace AGVNavigationCore.Models
[Category("RFID 정보")]
[Description("물리적 RFID 태그 ID입니다.")]
public string RfidId { get; set; } = string.Empty;
public UInt16 RfidId { get; set; } = 0;
[Category("노드 텍스트"), DisplayName("TextColor")]
@@ -161,7 +161,7 @@ namespace AGVNavigationCore.Models
public bool HasRfid()
{
return !string.IsNullOrEmpty(RfidId);
return RfidId > 0;
}
}
}

View File

@@ -611,18 +611,7 @@ namespace AGVNavigationCore.Models
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
@@ -630,10 +619,11 @@ namespace AGVNavigationCore.Models
/// <summary>
/// 노드 ID를 RFID 값으로 변환 (NodeResolver 사용)
/// </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);
return node?.HasRfid() == true ? node.RfidId : nodeId;
if ((node?.HasRfid() ?? false) == false) return 0;
return node.RfidId;
}

View File

@@ -447,7 +447,7 @@ namespace AGVNavigationCore.PathFinding.Planning
{
var node = path1.Path[i];
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;
// 노드 정보 생성 (현재 방향 유지)

View File

@@ -770,9 +770,9 @@ namespace AGVNavigationCore.PathFinding.Planning
private string GetDisplayName(string 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})";
}

View File

@@ -40,7 +40,7 @@ namespace AGVNavigationCore.PathFinding.Planning
/// <summary>
/// RFID Value
/// </summary>
public string RfidId { get; set; }
public ushort RfidId { get; set; }
/// <summary>
/// 해당 노드에서의 모터방향
@@ -87,7 +87,7 @@ namespace AGVNavigationCore.PathFinding.Planning
/// </summary>
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;
NodeId = nodeId;

View File

@@ -204,7 +204,7 @@ namespace AGVNavigationCore.Utils
}
Console.WriteLine(
$"\n 최종선택: {bestNode?.RfidId ?? "null"}[{bestNode?.Id ?? "null"}] (점수: {bestScore:F4})");
$"\n 최종선택: {bestNode?.RfidId ?? 0}[{bestNode?.Id ?? "null"}] (점수: {bestScore:F4})");
Console.WriteLine(
$"[GetNextNodeByDirection] ========== 다음 노드 선택 종료 ==========\n");

View File

@@ -16,12 +16,12 @@ namespace AGVNavigationCore.Utils
public class DirectionalPathfinderTest
{
private List<MapNode> _allNodes;
private Dictionary<string, MapNode> _nodesByRfidId;
private Dictionary<ushort, MapNode> _nodesByRfidId;
private AGVDirectionCalculator _calculator;
public DirectionalPathfinderTest()
{
_nodesByRfidId = new Dictionary<string, MapNode>();
_nodesByRfidId = new Dictionary<ushort, MapNode>();
_calculator = new AGVDirectionCalculator();
}
@@ -52,7 +52,7 @@ namespace AGVNavigationCore.Utils
// RFID ID로 인덱싱
foreach (var node in _allNodes)
{
if (!string.IsNullOrEmpty(node.RfidId))
if (node.HasRfid())
{
_nodesByRfidId[node.RfidId] = node;
}
@@ -71,7 +71,7 @@ namespace AGVNavigationCore.Utils
/// <summary>
/// 테스트: RFID 번호로 노드를 찾고, 다음 노드를 계산
/// </summary>
public void TestDirectionalMovement(string previousRfidId, string currentRfidId, AgvDirection direction)
public void TestDirectionalMovement(ushort previousRfidId, ushort currentRfidId, AgvDirection direction)
{
Console.WriteLine($"\n========================================");
Console.WriteLine($"테스트: {previousRfidId} → {currentRfidId} (방향: {direction})");
@@ -140,7 +140,7 @@ namespace AGVNavigationCore.Utils
/// <summary>
/// 특정 RFID 노드의 상세 정보 출력
/// </summary>
public void PrintNodeInfo(string rfidId)
public void PrintNodeInfo(ushort rfidId)
{
if (!_nodesByRfidId.TryGetValue(rfidId, out var node))
{

View File

@@ -28,10 +28,10 @@ namespace AGVNavigationCore.Utils
Console.WriteLine("================================================\n");
// 테스트 노드 생성
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 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 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 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 allNodes = new List<MapNode> { node001, node002, node003, node004 };
@@ -114,7 +114,7 @@ namespace AGVNavigationCore.Utils
AgvDirection motorDir = currentMotorDirection ?? direction;
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($"현재 모터 방향: {motorDir}");
Console.WriteLine($"요청 방향: {direction}");

View File

@@ -29,26 +29,26 @@ namespace AGVNavigationCore.Utils
tester.PrintAllNodes();
// 테스트 시나리오 1: 001 → 002 → Forward (003 기대)
tester.PrintNodeInfo("001");
tester.PrintNodeInfo("002");
tester.TestDirectionalMovement("001", "002", AgvDirection.Forward);
tester.PrintNodeInfo(001);
tester.PrintNodeInfo(002);
tester.TestDirectionalMovement(001, 002, AgvDirection.Forward);
// 테스트 시나리오 2: 002 → 001 → Backward (000 또는 이전 기대)
tester.TestDirectionalMovement("002", "001", AgvDirection.Backward);
tester.TestDirectionalMovement(002, 001, AgvDirection.Backward);
// 테스트 시나리오 3: 002 → 003 → Forward
tester.PrintNodeInfo("003");
tester.TestDirectionalMovement("002", "003", AgvDirection.Forward);
tester.PrintNodeInfo(003);
tester.TestDirectionalMovement(002, 003, AgvDirection.Forward);
// 테스트 시나리오 4: 003 → 004 → Forward
tester.PrintNodeInfo("004");
tester.TestDirectionalMovement("003", "004", AgvDirection.Forward);
tester.PrintNodeInfo(004);
tester.TestDirectionalMovement(003, 004, AgvDirection.Forward);
// 테스트 시나리오 5: 003 → 004 → Right (030 기대)
tester.TestDirectionalMovement("003", "004", AgvDirection.Right);
tester.TestDirectionalMovement(003, 004, AgvDirection.Right);
// 테스트 시나리오 6: 004 → 003 → Backward
tester.TestDirectionalMovement("004", "003", AgvDirection.Backward);
tester.TestDirectionalMovement(004, 003, AgvDirection.Backward);
Console.WriteLine("\n\n=== 테스트 완료 ===");
}

View File

@@ -635,7 +635,7 @@ namespace AGVSimulator.Forms
/// <summary>
/// 맵 스캔 모드에서 RFID로부터 노드 생성
/// </summary>
private void CreateNodeFromRfidScan(string rfidId, VirtualAGV selectedAGV)
private void CreateNodeFromRfidScan(ushort rfidId, VirtualAGV selectedAGV)
{
try
{
@@ -644,7 +644,7 @@ namespace AGVSimulator.Forms
var currentDirection = directionItem?.Direction ?? AgvDirection.Forward;
// 중복 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)
{
// 이미 존재하는 노드로 이동
@@ -805,7 +805,7 @@ namespace AGVSimulator.Forms
if (agv.CurrentNodeId != null && agv.CurrentNodeId != _lastSentNodeId)
{
var rfid = GetRfidByNodeId(agv.CurrentNodeId);
if (!string.IsNullOrEmpty(rfid))
if (rfid > 0)
{
SendTag(rfid);
_lastSentNodeId = agv.CurrentNodeId;
@@ -842,7 +842,7 @@ namespace AGVSimulator.Forms
// RFID 값 확인
var rfidId = _rfidTextBox.Text.Trim();
if (string.IsNullOrEmpty(rfidId))
if (ushort.TryParse(rfidId,out ushort rfidvalue)==false)
{
MessageBox.Show("RFID 값을 입력해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
@@ -856,13 +856,13 @@ namespace AGVSimulator.Forms
// 맵 스캔 모드일 때: 노드 자동 생성
if (_isMapScanMode)
{
CreateNodeFromRfidScan(rfidId, selectedAGV);
CreateNodeFromRfidScan(rfidvalue, selectedAGV);
this._simulatorCanvas.FitToNodes();
return;
}
// 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)
{
MessageBox.Show($"RFID '{rfidId}'에 해당하는 노드를 찾을 수 없습니다.\n\n사용 가능한 RFID 목록:\n{GetAvailableRfidList()}",
@@ -1255,10 +1255,12 @@ namespace AGVSimulator.Forms
/// <summary>
/// 노드 ID를 RFID 값으로 변환 (NodeResolver 사용)
/// </summary>
private string GetRfidByNodeId(string nodeId)
private ushort GetRfidByNodeId(string 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>
@@ -1267,9 +1269,9 @@ namespace AGVSimulator.Forms
private string GetDisplayName(string 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})";
}
@@ -1366,7 +1368,7 @@ namespace AGVSimulator.Forms
{
var info = advancedResult.DetailedPath[i];
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>();
if (info.CanRotate) flags.Add("회전가능");
@@ -1616,12 +1618,8 @@ namespace AGVSimulator.Forms
/// </summary>
private string GetNodeDisplayName(MapNode node)
{
if (node == null)
return "-";
if (!string.IsNullOrEmpty(node.RfidId))
return node.RfidId;
if (node == null) return "-";
if (node.HasRfid()) return node.RfidId.ToString("0000");
return $"({node.Id})";
}
@@ -1792,7 +1790,7 @@ namespace AGVSimulator.Forms
this.Invoke((MethodInvoker)delegate
{
// RFID 텍스트박스에 값 입력
_rfidTextBox.Text = nodeA.RfidId;
_rfidTextBox.Text = nodeA.RfidId.ToString();
// 방향 콤보박스 선택
SetDirectionComboBox(direction);
@@ -1808,7 +1806,7 @@ namespace AGVSimulator.Forms
this.Invoke((MethodInvoker)delegate
{
// RFID 텍스트박스에 값 입력
_rfidTextBox.Text = nodeB.RfidId;
_rfidTextBox.Text = nodeB.RfidId.ToString();
// 방향 콤보박스 선택
SetDirectionComboBox(direction);
@@ -2415,19 +2413,18 @@ namespace AGVSimulator.Forms
catch { }
}
public void SendTag(string tagno)
public void SendTag(ushort tagno)
{
if (_emulatorPort == null || !_emulatorPort.IsOpen) return;
tagno = tagno.PadLeft(6, '0');
if (tagno.Length > 6) tagno = tagno.Substring(0, 6);
var tagnostr = tagno.ToString("000000");
var barr = new List<byte>();
barr.Add(0x02);
barr.Add((byte)'T');
barr.Add((byte)'A');
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(0x03);