diff --git a/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Core/AGVPathResult.cs b/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Core/AGVPathResult.cs index 6e3b9a7..b5fbc7e 100644 --- a/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Core/AGVPathResult.cs +++ b/Cs_HMI/AGVLogic/AGVNavigationCore/PathFinding/Core/AGVPathResult.cs @@ -263,7 +263,7 @@ namespace AGVNavigationCore.PathFinding.Core /// 경로의 노드 정보를 포함 /// /// - 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); } diff --git a/Cs_HMI/AGVLogic/AGVSimulator/Forms/ProgressLogForm.cs b/Cs_HMI/AGVLogic/AGVSimulator/Forms/ProgressLogForm.cs index eee4823..cf86d93 100644 --- a/Cs_HMI/AGVLogic/AGVSimulator/Forms/ProgressLogForm.cs +++ b/Cs_HMI/AGVLogic/AGVSimulator/Forms/ProgressLogForm.cs @@ -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); } diff --git a/Cs_HMI/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs b/Cs_HMI/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs index c17aec5..3d664bc 100644 --- a/Cs_HMI/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs +++ b/Cs_HMI/AGVLogic/AGVSimulator/Forms/SimulatorForm.cs @@ -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; } /// @@ -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)?.Value; var targetNode = (_targetNodeCombo.SelectedItem as ComboBoxItem)?.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(); + var retval = AGVPathResult.CreateSuccess(list, new List(), 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);