..
This commit is contained in:
@@ -263,7 +263,7 @@ namespace AGVNavigationCore.PathFinding.Core
|
||||
/// 경로의 노드 정보를 포함
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string GetDetailedPathInfo()
|
||||
public string GetDetailedPathInfo(bool shortmessage = false)
|
||||
{
|
||||
if (!Success)
|
||||
{
|
||||
@@ -272,7 +272,10 @@ namespace AGVNavigationCore.PathFinding.Core
|
||||
|
||||
var data = DetailedPath.Select(t =>
|
||||
{
|
||||
return $"{t.RfidId}[{t.NodeId}] {t.MotorDirection.ToString().Substring(0, 1)}-{t.MagnetDirection.ToString().Substring(0, 1)}";
|
||||
if (shortmessage)
|
||||
return $"{t.RfidId:00}{t.MotorDirection.ToString().Substring(0, 1)}{t.MagnetDirection.ToString().Substring(0, 1)}";
|
||||
else
|
||||
return $"{t.RfidId}[{t.NodeId}] {t.MotorDirection.ToString().Substring(0, 1)}-{t.MagnetDirection.ToString().Substring(0, 1)}";
|
||||
});
|
||||
return string.Join(" → ", data);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,11 @@ namespace AGVSimulator.Forms
|
||||
listItem.BackColor = Color.LightPink;
|
||||
}
|
||||
|
||||
var dockpos = item.DockingPosition ?? string.Empty;
|
||||
var targerpos = item.TargetPosition ?? string.Empty;
|
||||
if (dockpos.Equals("충전기") && targerpos.StartsWith("0015"))
|
||||
listItem.ForeColor = Color.DarkViolet;
|
||||
|
||||
_logListView.Items.Add(listItem);
|
||||
_logListView.EnsureVisible(_logListView.Items.Count - 1);
|
||||
}
|
||||
|
||||
@@ -1539,8 +1539,14 @@ namespace AGVSimulator.Forms
|
||||
private string GetNodeDisplayName(MapNode node)
|
||||
{
|
||||
if (node == null) return "-";
|
||||
if (node.HasRfid()) return node.RfidId.ToString("0000");
|
||||
return $"({node.Id})";
|
||||
var retval = "";
|
||||
if (node.HasRfid()) retval = node.RfidId.ToString("0000");
|
||||
else retval = $"({node.Id})";
|
||||
if (node.DockDirection == DockingDirection.Forward)
|
||||
retval += "(F)";
|
||||
else if (node.DockDirection == DockingDirection.Backward)
|
||||
retval += "(B)";
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1606,13 +1612,13 @@ namespace AGVSimulator.Forms
|
||||
{
|
||||
logItem.Success = true;
|
||||
logItem.Message = "성공";
|
||||
logItem.DetailedPath = currentPath.GetDetailedPathInfo();
|
||||
logItem.DetailedPath = currentPath.GetDetailedPathInfo(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
logItem.Success = false;
|
||||
logItem.Message = $"도킹 검증 실패: {dockingValidation.ValidationError}";
|
||||
logItem.DetailedPath = currentPath.GetDetailedPathInfo();
|
||||
logItem.DetailedPath = currentPath.GetDetailedPathInfo(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1694,9 +1700,9 @@ namespace AGVSimulator.Forms
|
||||
logForm.AppendLog("---");
|
||||
|
||||
// 각 연결된 노드 쌍에 대해 테스트
|
||||
foreach (var (nodeA, nodeB) in nodePairs)
|
||||
foreach (var (direction, directionName) in directions)
|
||||
{
|
||||
foreach (var (direction, directionName) in directions)
|
||||
foreach (var (nodeA, nodeB) in nodePairs)
|
||||
{
|
||||
// 취소 확인
|
||||
if (logForm.CancelRequested)
|
||||
@@ -1766,10 +1772,7 @@ namespace AGVSimulator.Forms
|
||||
var startNode = (_startNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
|
||||
var targetNode = (_targetNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
|
||||
var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
|
||||
var calcResult = CalcPath(startNode, targetNode, this._simulatorCanvas.Nodes,selectedAGV.PrevNode, selectedAGV.PrevDirection);
|
||||
|
||||
|
||||
|
||||
var calcResult = CalcPath(startNode, targetNode, this._simulatorCanvas.Nodes, selectedAGV.PrevNode, selectedAGV.PrevDirection);
|
||||
|
||||
//// 테스트 결과 생성
|
||||
testResult = CreateTestResultFromUI(nodeA, dockingTarget, directionName, calcResult);
|
||||
@@ -2111,9 +2114,13 @@ namespace AGVSimulator.Forms
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_emulatorPort != null && _emulatorPort.IsOpen)
|
||||
if (_emulatorPort != null)
|
||||
{
|
||||
_emulatorPort.Close();
|
||||
// 이벤트 핸들러 해제 (중복 호출 방지)
|
||||
_emulatorPort.DataReceived -= OnEmulatorDataReceived;
|
||||
|
||||
if (_emulatorPort.IsOpen)
|
||||
_emulatorPort.Close();
|
||||
}
|
||||
_isEmulatorConnected = false;
|
||||
_connectButton.Text = "Connect";
|
||||
@@ -2131,6 +2138,8 @@ namespace AGVSimulator.Forms
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_emulatorPort == null || !_emulatorPort.IsOpen) return;
|
||||
|
||||
string data = _emulatorPort.ReadExisting();
|
||||
_recvBuffer.Append(data);
|
||||
|
||||
@@ -2161,6 +2170,11 @@ namespace AGVSimulator.Forms
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Emulator Recv Error: {ex.Message}");
|
||||
// 수신 중 오류 발생 시 연결 해제 처리 (UI 스레드에서 실행)
|
||||
this.Invoke(new Action(() =>
|
||||
{
|
||||
if (_isEmulatorConnected) DisconnectEmulator();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2366,7 +2380,11 @@ namespace AGVSimulator.Forms
|
||||
|
||||
private void SendEmulatorStatus()
|
||||
{
|
||||
if (_emulatorPort == null || !_emulatorPort.IsOpen) return;
|
||||
if (_emulatorPort == null || !_emulatorPort.IsOpen)
|
||||
{
|
||||
if (_isEmulatorConnected) DisconnectEmulator();
|
||||
return;
|
||||
}
|
||||
|
||||
var agv = _agvList.FirstOrDefault();
|
||||
|
||||
@@ -2419,7 +2437,10 @@ namespace AGVSimulator.Forms
|
||||
{
|
||||
_emulatorPort.Write(barr, 0, barr.Length);
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
if (_isEmulatorConnected) DisconnectEmulator();
|
||||
}
|
||||
}
|
||||
|
||||
private string CalculateChecksum(string data)
|
||||
@@ -2545,7 +2566,7 @@ namespace AGVSimulator.Forms
|
||||
else
|
||||
{
|
||||
// 8. 적용
|
||||
|
||||
|
||||
ApplyResultToSimulator(rlt, selectedAGV);
|
||||
UpdateAdvancedPathDebugInfo(rlt);
|
||||
}
|
||||
@@ -2563,7 +2584,7 @@ namespace AGVSimulator.Forms
|
||||
|
||||
if (startNode == null || targetNode == null) return AGVPathResult.CreateFailure("시작/종료노드가 지정되지 않음");
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var pathFinder = new AGVPathfinder(nodes);
|
||||
@@ -2574,13 +2595,19 @@ namespace AGVSimulator.Forms
|
||||
AGVPathResult LimitPath = null;
|
||||
if (startNode.ConnectedMapNodes.Count == 1)
|
||||
{
|
||||
LimitPath = pathFinder.FindPathAStar(startNode, targetNode);
|
||||
for(int i = 0; i < LimitPath.Path.Count;i++)
|
||||
LimitPath = pathFinder.FindPathAStar(startNode, prevNode);
|
||||
for (int i = 0; i < LimitPath.Path.Count; i++)
|
||||
{
|
||||
var nodeinfo = LimitPath.Path[i];
|
||||
var dir = (prevDir == AgvDirection.Forward ? AgvDirection.Backward : AgvDirection.Forward);
|
||||
LimitPath.DetailedPath.Add(new NodeMotorInfo(i + 1, nodeinfo.Id, nodeinfo.RfidId, dir));
|
||||
}
|
||||
|
||||
//시작위치 및 방향을변경해준다
|
||||
var org_start = startNode;
|
||||
startNode = prevNode;
|
||||
prevNode = org_start;
|
||||
prevDir = (prevDir == AgvDirection.Forward ? AgvDirection.Backward : AgvDirection.Forward);
|
||||
}
|
||||
|
||||
|
||||
@@ -2599,9 +2626,11 @@ namespace AGVSimulator.Forms
|
||||
_simulatorCanvas.HighlightNodeId = null;
|
||||
|
||||
//버퍼구간내에 시작과 종료가 모두 포함되어있다
|
||||
bool fixpath = false;
|
||||
if (rlt.Path.Contains(startNode) && rlt.Path.Contains(targetNode))
|
||||
{
|
||||
Retval = CalcPathBufferToBuffer(pathFinder, startNode, targetNode, prevNode, prevDir, selectedAGV);
|
||||
fixpath = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2641,9 +2670,18 @@ namespace AGVSimulator.Forms
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//경로오류 검사
|
||||
if (Retval.Success == false) return Retval;
|
||||
|
||||
if (LimitPath != null)
|
||||
{
|
||||
Retval = CombinePaths(LimitPath, Retval);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//해당경로와 대상의 도킹포인트의 방향을 검사합니다
|
||||
if (targetNode.DockDirection != DockingDirection.DontCare)
|
||||
{
|
||||
@@ -2661,7 +2699,7 @@ namespace AGVSimulator.Forms
|
||||
//6[F][R] → 13[B][L] → 6[F][L] 이런경우를 찾아서 경로를 최적화한다.
|
||||
//위 예제에서는 6FR 13BL 이 제자리로 오는 경로이므로 6FL만 남기면된다.
|
||||
//단순히 ID만 같은게 아니라, 실제로 갔다가 되돌아오는 패턴(역방향)인지 확인해야 함.
|
||||
while (true)
|
||||
while (fixpath == false && true)
|
||||
{
|
||||
var updatecount = 0;
|
||||
for (int i = 0; i < Retval.DetailedPath.Count - 2; i++)
|
||||
@@ -2725,8 +2763,126 @@ namespace AGVSimulator.Forms
|
||||
|
||||
}
|
||||
|
||||
//6[F][R] → 13[B][L] → 6[F][L] 이런경우를 찾아서 경로를 최적화한다.
|
||||
//위 예제에서는 6FR 13BL 이 제자리로 오는 경로이므로 6FL만 남기면된다.
|
||||
//최종 목적지를 확인하여 도킹 노드라면 그 도킹노드방향과 모터방향을 체크한다.
|
||||
var lastnode = Retval.Path.Last();
|
||||
if (lastnode.StationType != StationType.Normal)
|
||||
{
|
||||
var lastnodePath = Retval.DetailedPath.Last();
|
||||
if (lastnode.DockDirection == DockingDirection.Forward && lastnodePath.MotorDirection != AgvDirection.Forward)
|
||||
return AGVPathResult.CreateFailure($"목적지의 모터방향({lastnode.DockDirection}) 불일치 경로방향({lastnodePath.MotorDirection}) {Retval.GetDetailedPathInfo(true)}");
|
||||
if (lastnode.DockDirection == DockingDirection.Backward && lastnodePath.MotorDirection != AgvDirection.Backward)
|
||||
return AGVPathResult.CreateFailure($"목적지의 모터방향({lastnode.DockDirection}) 불일치 경로방향({lastnodePath.MotorDirection}) {Retval.GetDetailedPathInfo(true)}");
|
||||
}
|
||||
|
||||
//모든 디테일데이터의 실제 위치를 기반으로 전체 노드를 추정한다
|
||||
for (int i = 0; i < Retval.DetailedPath.Count - 1; i++)
|
||||
{
|
||||
var pnode = (i == 0 ? prevNode : Retval.Path[i - 1]);
|
||||
var pdir = (i == 0 ? prevDir : Retval.DetailedPath[i - 1].MotorDirection);
|
||||
var cnode = Retval.Path[i];
|
||||
var nnode = Retval.Path[i + 1];
|
||||
|
||||
//연결된 노드에 다음노드 id가 포함되는지 확인한다
|
||||
if (cnode.ConnectedNodes.Contains(nnode.Id) == false)
|
||||
{
|
||||
return AGVPathResult.CreateFailure($"[{cnode.RfidId}] 노드에 연결되지 않은 [{nnode.RfidId}]노드가 지정됨 {Retval.GetDetailedPathInfo(true)}");
|
||||
}
|
||||
//왔던방향을 제외한 다른 방향에 다음노드가 있는지
|
||||
var detail = Retval.DetailedPath[i];
|
||||
|
||||
//연결된 모두를 찾아서 온곳
|
||||
bool existconnnode = false;
|
||||
|
||||
//왓던방향을고려해서 다음 노드를 찾아서 일치하는지 본다.
|
||||
var deltaX = nnode.Position.X - cnode.Position.X;
|
||||
var deltaY = nnode.Position.Y - cnode.Position.Y;
|
||||
if (Math.Abs(deltaX) > Math.Abs(deltaY))
|
||||
{
|
||||
//x축으로 많이 움직임
|
||||
if (nnode.Position.X > pnode.Position.X)
|
||||
{
|
||||
//장비가 오른쪽으로 움직였다
|
||||
if (pdir == detail.MotorDirection)
|
||||
{
|
||||
//왓던방향으로 게속 이동한다
|
||||
var nextpredictnode = cnode.ConnectedMapNodes.Where(t => t.Position.X > cnode.Position.X).FirstOrDefault();
|
||||
if (nextpredictnode.Id != nnode.Id)
|
||||
return AGVPathResult.CreateFailure($"[{cnode.RfidId}] 노드의 다음[{nnode.RfidId}]노드가 올바르지 않음 {Retval.GetDetailedPathInfo(true)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
//반대방향으로 이동한다
|
||||
var nextpredictnode = cnode.ConnectedMapNodes.Where(t => t.Position.X < cnode.Position.X).FirstOrDefault();
|
||||
if (nextpredictnode.Id != nnode.Id)
|
||||
return AGVPathResult.CreateFailure($"[{cnode.RfidId}] 노드의 다음[{nnode.RfidId}]노드가 올바르지 않음 {Retval.GetDetailedPathInfo(true)}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//장비가 왼쪽으로 움직였다
|
||||
if (pdir == detail.MotorDirection) //후진으로 왼쪽이동했으니 모니터가 왼쪽에 있다.
|
||||
{
|
||||
|
||||
//왓던방향으로 게속 이동한다
|
||||
var nextpredictnode = cnode.ConnectedMapNodes.Where(t => t.Position.X < cnode.Position.X).FirstOrDefault();
|
||||
if (nextpredictnode.Id != nnode.Id)
|
||||
return AGVPathResult.CreateFailure($"[{cnode.RfidId}] 노드의 다음[{nnode.RfidId}]노드가 올바르지 않음 {Retval.GetDetailedPathInfo(true)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
//반대방향으로 이동한다
|
||||
var nextpredictnode = cnode.ConnectedMapNodes.Where(t => t.Position.X > cnode.Position.X).FirstOrDefault();
|
||||
if (nextpredictnode.Id != nnode.Id)
|
||||
return AGVPathResult.CreateFailure($"[{cnode.RfidId}] 노드의 다음[{nnode.RfidId}]노드가 올바르지 않음 {Retval.GetDetailedPathInfo(true)}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//y축으로 많이 움직임
|
||||
if (nnode.Position.Y < pnode.Position.Y)
|
||||
{
|
||||
//장비가 위로 움직였다
|
||||
if (pdir == detail.MotorDirection)
|
||||
{
|
||||
|
||||
//왓던방향으로 게속 이동한다
|
||||
var nextpredictnode = cnode.ConnectedMapNodes.Where(t => t.Position.Y > cnode.Position.Y).FirstOrDefault();
|
||||
if (nextpredictnode.Id != nnode.Id)
|
||||
return AGVPathResult.CreateFailure($"[{cnode.RfidId}] 노드의 다음[{nnode.RfidId}]노드가 올바르지 않음 {Retval.GetDetailedPathInfo(true)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
//반대방향으로 이동한다
|
||||
var nextpredictnode = cnode.ConnectedMapNodes.Where(t => t.Position.Y < cnode.Position.Y).FirstOrDefault();
|
||||
if (nextpredictnode.Id != nnode.Id)
|
||||
return AGVPathResult.CreateFailure($"[{cnode.RfidId}] 노드의 다음[{nnode.RfidId}]노드가 올바르지 않음 {Retval.GetDetailedPathInfo(true)}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//장비가 아래로 움직였다
|
||||
if (pdir == detail.MotorDirection) //후진으로 아래로 이동했으니 모니터가 위에 있다.
|
||||
{
|
||||
|
||||
//왓던방향으로 게속 이동한다
|
||||
var nextpredictnode = cnode.ConnectedMapNodes.Where(t => t.Position.Y > cnode.Position.Y).Select(t => t.Id).ToList();
|
||||
if (nextpredictnode.Contains(nnode.Id) == false)
|
||||
return AGVPathResult.CreateFailure($"[{cnode.RfidId}] 노드의 다음[{nnode.RfidId}]노드가 올바르지 않음 {Retval.GetDetailedPathInfo(true)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
//반대방향으로 이동한다
|
||||
var nextpredictnode = cnode.ConnectedMapNodes.Where(t => t.Position.Y > cnode.Position.Y).FirstOrDefault();
|
||||
if (nextpredictnode.Id != nnode.Id)
|
||||
return AGVPathResult.CreateFailure($"[{cnode.RfidId}] 노드의 다음[{nnode.RfidId}]노드가 올바르지 않음 {Retval.GetDetailedPathInfo(true)}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//최종결과 반환
|
||||
@@ -2748,18 +2904,26 @@ namespace AGVSimulator.Forms
|
||||
// 이동 벡터 X 변화량
|
||||
// prev가 없거나 start와 같으면 이동 방향을 알 수 없음 -> 이 경우 보수적으로 기존 로직(Backward면 탈출) 따름
|
||||
int deltaX = 0;
|
||||
int deltaY = 0;
|
||||
if (prev == null) return AGVPathResult.CreateFailure("이전 노드 정보가 없습니다");
|
||||
else deltaX = start.Position.X - prev.Position.X;
|
||||
else
|
||||
{
|
||||
deltaX = start.Position.X - prev.Position.X;
|
||||
deltaY = -(start.Position.Y - prev.Position.Y);
|
||||
}
|
||||
|
||||
if (Math.Abs(deltaY) > Math.Abs(deltaX))
|
||||
deltaX = deltaY;
|
||||
|
||||
bool isMonitorLeft = false;
|
||||
|
||||
if (deltaX > 0) // 오른쪽(Forward)으로 이동해 옴 (예: 2 -> 4)
|
||||
if (deltaX > 0) // 오른쪽(Forward)으로 이동해 옴 (예: 2 -> 4), 위로 이동함
|
||||
{
|
||||
// 이동방향(Right) + 전진(F) => Monitor Right (Good)
|
||||
// 이동방향(Right) + 후진(B) => Monitor Left (Bad)
|
||||
isMonitorLeft = (prevDir == AgvDirection.Backward);
|
||||
}
|
||||
else if (deltaX < 0) // 왼쪽(Reverse)으로 이동해 옴 (예: 4 -> 2)
|
||||
else if (deltaX < 0) // 왼쪽(Reverse)으로 이동해 옴 (예: 4 -> 2), 아래로 이동함
|
||||
{
|
||||
// 이동방향(Left) + 전진(F) => Monitor Left (Bad)
|
||||
// 이동방향(Left) + 후진(B) => Monitor Right (Good)
|
||||
@@ -2775,8 +2939,22 @@ namespace AGVSimulator.Forms
|
||||
{
|
||||
// Monitor Left 상태 (방향 불일치) -> Gateway로 탈출
|
||||
var GateWayNode = FindNode(6);
|
||||
var escPath = pathfinder.FindBasicPath(start, GateWayNode, prev, prevDir);
|
||||
if (!escPath.Success) return AGVPathResult.CreateFailure("버퍼 탈출 경로 실패");
|
||||
var reverseDir = prevDir == AgvDirection.Backward ? AgvDirection.Forward : AgvDirection.Backward;
|
||||
|
||||
//왼족에서 오른조긍로 이동하면 prevdir 을 그대로 쓰는데.. 오른쪽에서 왼쪽으로 이동했다면 방향을 변경해야한다
|
||||
AGVPathResult escPath = null;
|
||||
if (start.Position.X > prev.Position.X)
|
||||
{
|
||||
escPath = pathfinder.FindBasicPath(start, GateWayNode, prev, prevDir);
|
||||
if (!escPath.Success) return AGVPathResult.CreateFailure("버퍼 탈출 경로 실패");
|
||||
}
|
||||
else
|
||||
{
|
||||
escPath = pathfinder.FindBasicPath(start, GateWayNode, prev, reverseDir);
|
||||
if (!escPath.Success) return AGVPathResult.CreateFailure("버퍼 탈출 경로 실패");
|
||||
}
|
||||
|
||||
|
||||
|
||||
var lastNode = escPath.Path.Last(); // Should be GW6
|
||||
var lastPrev = escPath.Path[escPath.Path.Count - 2];
|
||||
@@ -2805,9 +2983,96 @@ namespace AGVSimulator.Forms
|
||||
else
|
||||
{
|
||||
// Monitor Right 상태 (방향 일치) -> 직접 진입 또는 Overshoot
|
||||
|
||||
bool isTargetLeft = target.Position.X < start.Position.X;
|
||||
|
||||
if (isTargetLeft)
|
||||
if (target == start)
|
||||
{
|
||||
//시작위치랑 종료위치랑 같고, 방향도 같다면 이동할 필요는 없는 조건이다
|
||||
//오른쪽으로 한번더 이동해서그곳까지 이동한 후 역방향으로 MS 처리한다
|
||||
//PREV 가 아닌 다른 노드가 이동할 대상이다
|
||||
//현재위치에서 지정방향으로 이동한다.
|
||||
var list = new List<MapNode>();
|
||||
var retval = AGVPathResult.CreateSuccess(list, new List<AgvDirection>(), 0, 0);
|
||||
var resversedir = prevDir == AgvDirection.Backward ? AgvDirection.Forward : AgvDirection.Backward;
|
||||
|
||||
//현재위치에서 반드시 시작해야하낟.
|
||||
retval.Path.Add(target);
|
||||
|
||||
|
||||
|
||||
if (deltaX < 0)
|
||||
{
|
||||
//장비가 오른쪽에서 왼쪽으로 오는 경우와
|
||||
var nextNode = start.ConnectedMapNodes.Where(t => t.Id != prev.Id && t.StationType == StationType.Buffer).FirstOrDefault();
|
||||
if (nextNode != null)
|
||||
{
|
||||
//방향결정
|
||||
retval.DetailedPath.Add(new NodeMotorInfo(1, target.Id, target.RfidId, prevDir));
|
||||
|
||||
//다음노드로 이동을한다.
|
||||
retval.Path.Add(nextNode);
|
||||
var lastDefailt = retval.DetailedPath.Last();
|
||||
retval.DetailedPath.Add(new NodeMotorInfo(lastDefailt.seq + 1, nextNode.Id, nextNode.RfidId, AgvDirection.Forward)
|
||||
{
|
||||
Speed = SpeedLevel.M,
|
||||
IsPass = false,
|
||||
});
|
||||
|
||||
////다시원래노드로 이동을해야한다.(반대방향으로)
|
||||
retval.Path.Add(target);
|
||||
retval.DetailedPath.Add(new NodeMotorInfo((retval.DetailedPath.Max(t => t.seq) + 1), target.Id, target.RfidId, AgvDirection.Forward));
|
||||
|
||||
//최종목적지로 간다
|
||||
retval.Path.Add(target);
|
||||
retval.DetailedPath.Add(new NodeMotorInfo(retval.DetailedPath.Max(t => t.seq) + 1, target.Id, target.RfidId, AgvDirection.Backward));
|
||||
}
|
||||
else
|
||||
{
|
||||
//방향결정(다음노드가 없으니 반대방향으로 들어가야한다)
|
||||
retval.DetailedPath.Add(new NodeMotorInfo(1, target.Id, target.RfidId, resversedir));
|
||||
|
||||
//다음노드가 없으므로 이전노드로 처리를 해야한다.
|
||||
retval.Path.Add(prev);
|
||||
retval.DetailedPath.Add(new NodeMotorInfo(retval.DetailedPath.Last().seq + 1, prev.Id, prev.RfidId, prevDir)
|
||||
{
|
||||
Speed = SpeedLevel.M,
|
||||
IsPass = false,
|
||||
});
|
||||
|
||||
//최종목적지로 간다
|
||||
retval.Path.Add(target);
|
||||
retval.DetailedPath.Add(new NodeMotorInfo(retval.DetailedPath.Max(t => t.seq) + 1, target.Id, target.RfidId, prevDir));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//왼쪽에서 오른쪽으로 오는 경우가 서로 다르다
|
||||
retval.DetailedPath.Add(new NodeMotorInfo(1, target.Id, target.RfidId, prevDir));
|
||||
|
||||
var nextNode = start.ConnectedMapNodes.Where(t => t.Id != prev.Id && t.StationType == StationType.Buffer).FirstOrDefault();
|
||||
//한번더 이동을한다.(그리고는 반대로 저속 이동을 하게한다)
|
||||
retval.Path.Add(nextNode);
|
||||
var lastDefailt = retval.DetailedPath.Last();
|
||||
retval.DetailedPath.Add(new NodeMotorInfo(lastDefailt.seq + 1, nextNode.Id, nextNode.RfidId, AgvDirection.Backward)
|
||||
{
|
||||
Speed = SpeedLevel.L,
|
||||
IsPass = false,
|
||||
});
|
||||
|
||||
//최종목적지로 간다
|
||||
retval.Path.Add(target);
|
||||
retval.DetailedPath.Add(new NodeMotorInfo(retval.DetailedPath.Max(t => t.seq) + 1, target.Id, target.RfidId, AgvDirection.Backward));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return retval;
|
||||
}
|
||||
else if (isTargetLeft)
|
||||
{
|
||||
var directPath = pathfinder.FindBasicPath(start, target, prev, AgvDirection.Backward);
|
||||
ApplyResultToSimulator(directPath, agv);
|
||||
@@ -2823,14 +3088,22 @@ namespace AGVSimulator.Forms
|
||||
// return (false, "Overshoot 공간 부족");
|
||||
|
||||
var path1 = pathfinder.FindBasicPath(start, target, prev, AgvDirection.Forward);
|
||||
if (path1.Path.Count < 2) return AGVPathResult.CreateFailure("Overshoot 경로 생성 실패");
|
||||
if (path1.Path.Count < 2)
|
||||
{
|
||||
return AGVPathResult.CreateFailure("Overshoot 경로 생성 실패");
|
||||
}
|
||||
|
||||
//마지막위치는 제거한다. 바로 방향을 전환하기 위함이다.
|
||||
var last = path1.Path.Last();
|
||||
var lastD = path1.DetailedPath.Last();
|
||||
path1.Path.RemoveAt(path1.Path.Count - 1);
|
||||
path1.DetailedPath.RemoveAt(path1.DetailedPath.Count - 1);
|
||||
|
||||
//목표에서 방향바꿔 마크스탑을 해야한다
|
||||
path1.Path.Add(path1.Path.Last());
|
||||
path1.Path.Add(last);
|
||||
|
||||
//디테일은 방향바꿔서 추가 필요(속도는 저속처리해준다)
|
||||
var lastDefailt = path1.DetailedPath.Last();
|
||||
path1.DetailedPath.Add(new NodeMotorInfo(lastDefailt.seq + 1, lastDefailt.NodeId, lastDefailt.RfidId, AgvDirection.Backward)
|
||||
path1.DetailedPath.Add(new NodeMotorInfo(lastD.seq + 1, lastD.NodeId, lastD.RfidId, AgvDirection.Backward)
|
||||
{
|
||||
Speed = SpeedLevel.L,
|
||||
IsPass = false,
|
||||
@@ -2892,8 +3165,6 @@ namespace AGVSimulator.Forms
|
||||
//게이트웨이 진입 한 방향을 보고. 목적지와 도킹방향이 일치하는지 결정한다.
|
||||
var deltaX = GTNode.Position.X - PrevNode.Position.X;
|
||||
var isMonitorLeft = false;
|
||||
bool requiredDir = false;
|
||||
|
||||
|
||||
if (deltaX > 0) //게이트웨이가 우측에 있다
|
||||
{
|
||||
@@ -2932,7 +3203,8 @@ namespace AGVSimulator.Forms
|
||||
var motdir = targetNode.DockDirection == DockingDirection.Backward ? AgvDirection.Backward : AgvDirection.Forward;
|
||||
var pathtarget = pathFinder.FindBasicPath(GTNode, targetNode, PrevNode, motdir);
|
||||
|
||||
if (targetNode.DockDirection == DockingDirection.Backward && isMonitorLeft)
|
||||
if ((targetNode.DockDirection == DockingDirection.Backward && isMonitorLeft) ||
|
||||
(targetNode.DockDirection == DockingDirection.Forward && !isMonitorLeft))
|
||||
{
|
||||
//턴을 하는
|
||||
turnPatterns = GetTurnaroundPattern(GTNode, targetNode);
|
||||
@@ -2987,12 +3259,13 @@ namespace AGVSimulator.Forms
|
||||
private MapNode GetGatewayNode(MapNode node)
|
||||
{
|
||||
var rfid = 0;
|
||||
if (node.RfidId == 1) rfid = 10;
|
||||
if (node.RfidId == 15) rfid = 9;
|
||||
if (node.RfidId == 11) rfid = 6;
|
||||
if (node.RfidId == 8) rfid = 13;
|
||||
if (node.RfidId == 19) rfid = 13;
|
||||
if (node.StationType == StationType.Buffer) rfid = 6;
|
||||
if (node.StationType == StationType.UnLoader) rfid = 10; //unloader
|
||||
else if (node.StationType == StationType.Charger1) rfid = 9; //charger #1
|
||||
else if (node.StationType == StationType.Clearner) rfid = 6; //cleaner
|
||||
else if (node.StationType == StationType.Charger2) rfid = 13; //charger #2
|
||||
else if (node.StationType == StationType.Loader) rfid = 13; //loader
|
||||
else if (node.StationType == StationType.Buffer) rfid = 6;
|
||||
|
||||
if (rfid == 0) return null;
|
||||
return this._simulatorCanvas.Nodes.FirstOrDefault(t => t.RfidId == rfid);
|
||||
}
|
||||
@@ -3048,6 +3321,18 @@ namespace AGVSimulator.Forms
|
||||
var res = new AGVPathResult();
|
||||
res.Success = true;
|
||||
|
||||
var p1last = p1.DetailedPath.LastOrDefault();
|
||||
var p2fist = p2.DetailedPath.FirstOrDefault();
|
||||
|
||||
//p1의 마지막과 p2의 시작이 같다면 p1의 마지막을 제거한다.
|
||||
if (p1last != null && p2fist != null &&
|
||||
(p1last.NodeId == p2fist.NodeId && p1last.MotorDirection == p2fist.MotorDirection && p1last.MagnetDirection == p2fist.MagnetDirection))
|
||||
{
|
||||
p1.Path.RemoveAt(p1.Path.Count - 1);
|
||||
p1.DetailedPath.RemoveAt(p1.DetailedPath.Count - 1);
|
||||
}
|
||||
|
||||
|
||||
foreach (var item in p1.Path)
|
||||
{
|
||||
res.Path.Add(item);
|
||||
|
||||
Reference in New Issue
Block a user