"feat:Enable-hover-highlight-and-refactor"

This commit is contained in:
2025-12-14 17:20:50 +09:00
parent 34b038c4be
commit 764fbbd204
48 changed files with 3980 additions and 2750 deletions

View File

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

View File

@@ -90,8 +90,8 @@ namespace AGVNavigationCore.Utils
return;
}
Console.WriteLine($"이전 노드: {previousNode.NodeId} (RFID: {previousNode.RfidId}) - 위치: {previousNode.Position}");
Console.WriteLine($"현재 노드: {currentNode.NodeId} (RFID: {currentNode.RfidId}) - 위치: {currentNode.Position}");
Console.WriteLine($"이전 노드: {previousNode.Id} (RFID: {previousNode.RfidId}) - 위치: {previousNode.Position}");
Console.WriteLine($"현재 노드: {currentNode.Id} (RFID: {currentNode.RfidId}) - 위치: {currentNode.Position}");
Console.WriteLine($"이동 벡터: ({currentNode.Position.X - previousNode.Position.X}, " +
$"{currentNode.Position.Y - previousNode.Position.Y})");
@@ -111,10 +111,10 @@ namespace AGVNavigationCore.Utils
}
// 다음 노드 정보 출력
var nextNode = _allNodes.FirstOrDefault(n => n.NodeId == nextNodeId);
var nextNode = _allNodes.FirstOrDefault(n => n.Id == nextNodeId);
if (nextNode != null)
{
Console.WriteLine($"✓ 다음 노드: {nextNode.NodeId} (RFID: {nextNode.RfidId}) - 위치: {nextNode.Position}");
Console.WriteLine($"✓ 다음 노드: {nextNode.Id} (RFID: {nextNode.RfidId}) - 위치: {nextNode.Position}");
Console.WriteLine($" ├─ 노드 타입: {GetNodeTypeName(nextNode.Type)}");
Console.WriteLine($" └─ 연결된 노드: {string.Join(", ", nextNode.ConnectedNodes)}");
}
@@ -132,7 +132,7 @@ namespace AGVNavigationCore.Utils
Console.WriteLine("\n========== 모든 노드 정보 ==========");
foreach (var node in _allNodes.OrderBy(n => n.RfidId))
{
Console.WriteLine($"{node.RfidId:D3} → {node.NodeId} ({GetNodeTypeName(node.Type)})");
Console.WriteLine($"{node.RfidId:D3} → {node.Id} ({GetNodeTypeName(node.Type)})");
Console.WriteLine($" 위치: {node.Position}, 연결: {string.Join(", ", node.ConnectedNodes)}");
}
}
@@ -149,8 +149,9 @@ namespace AGVNavigationCore.Utils
}
Console.WriteLine($"\n========== RFID {rfidId} 상세 정보 ==========");
Console.WriteLine($"노드 ID: {node.NodeId}");
Console.WriteLine($"이름: {node.Name}");
Console.WriteLine($"노드 ID: {node.Id}");
Console.WriteLine($"RFID: {node.RfidId}");
Console.WriteLine($"ALIAS: {node.AliasName}");
Console.WriteLine($"위치: {node.Position}");
Console.WriteLine($"타입: {GetNodeTypeName(node.Type)}");
Console.WriteLine($"TurnLeft/Right/교차로 : {(node.CanTurnLeft ? "O":"X")}/{(node.CanTurnRight ? "O" : "X")}/{(node.DisableCross ? "X" : "O")}");
@@ -165,7 +166,7 @@ namespace AGVNavigationCore.Utils
{
foreach (var connectedId in node.ConnectedNodes)
{
var connectedNode = _allNodes.FirstOrDefault(n => n.NodeId == connectedId);
var connectedNode = _allNodes.FirstOrDefault(n => n.Id == connectedId);
if (connectedNode != null)
{
Console.WriteLine($" → {connectedId} (RFID: {connectedNode.RfidId}) - 위치: {connectedNode.Position}");

View File

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

View File

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

View File

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