버퍼경로내의 오동작 확인 및 재시작 관련 코드 점검

This commit is contained in:
backuppc
2026-01-28 10:45:57 +09:00
parent b0e75b351a
commit ffa6c2fb23
7 changed files with 179 additions and 35 deletions

View File

@@ -289,10 +289,10 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PUB.cs" /> <Compile Include="PUB.cs" />
<Compile Include="CSetting.cs" /> <Compile Include="CSetting.cs" />
<Compile Include="StateMachine\Step\_SM_RUN_LOADER_IN.cs"> <Compile Include="StateMachine\Step\_SM_RUN_ENTER.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
<Compile Include="StateMachine\Step\_SM_RUN_LOADER_OUT.cs"> <Compile Include="StateMachine\Step\_SM_RUN_EXIT.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
<Compile Include="StateMachine\Step\_Util.cs"> <Compile Include="StateMachine\Step\_Util.cs">

View File

@@ -231,7 +231,7 @@ namespace Project
case ERunStep.CLEANER_IN: //클리너도킹 case ERunStep.CLEANER_IN: //클리너도킹
case ERunStep.UNLOADER_IN: //언로더도킹 case ERunStep.UNLOADER_IN: //언로더도킹
case ERunStep.LOADER_IN: //로더도킹 case ERunStep.LOADER_IN: //로더도킹
if (_SM_RUN_LOADER_IN(runStepisFirst, PUB.sm.GetRunSteptime)) if (_SM_RUN_ENTER(runStepisFirst, PUB.sm.GetRunSteptime))
{ {
PUB.Speak(Lang.); PUB.Speak(Lang.);
@@ -246,7 +246,7 @@ namespace Project
case ERunStep.CLEANER_OUT: //클리너아웃 case ERunStep.CLEANER_OUT: //클리너아웃
case ERunStep.UNLOADER_OUT: //언로더아웃 case ERunStep.UNLOADER_OUT: //언로더아웃
case ERunStep.LOADER_OUT: //로더아웃 case ERunStep.LOADER_OUT: //로더아웃
if (_SM_RUN_LOADER_OUT(runStepisFirst, PUB.sm.GetRunSteptime)) if (_SM_RUN_EXIT(runStepisFirst, PUB.sm.GetRunSteptime))
{ {
PUB.Speak(Lang.); PUB.Speak(Lang.);

View File

@@ -60,18 +60,52 @@ namespace Project
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
if (PUB._virtualAGV.Turn != AGVNavigationCore.Models.AGVTurn.L90) //이미 턴이 되어있는지 확인한다 (재진입 시 중복 실행 방지)
if (PUB._virtualAGV.Turn == AGVNavigationCore.Models.AGVTurn.L90)
{ {
PUB.log.Add($"[{funcname}] 이미 Left Turn 완료 상태입니다. 턴 명령을 건너뜁니다.");
PUB.sm.UpdateRunStepSeq();
}
else
{
//하드웨어 상태 확인
var turnState = PUB.AGV.TurnInformation?.State ?? arDev.eNarumiTurn.None;
if (turnState == arDev.eNarumiTurn.Left || turnState == arDev.eNarumiTurn.LeftIng)
{
//이미 좌회전 중이거나 완료된 하드웨어 상태
PUB.log.Add($"[{funcname}] 하드웨어 좌회전 상태 확인됨({turnState}). 명령을 건너뜁니다.");
PUB.sm.UpdateRunStepSeq();
}
else if (turnState == arDev.eNarumiTurn.Right || turnState == arDev.eNarumiTurn.RightIng)
{
//비정상 상태 (우회전 중?)
PUB.AGV.AGVMoveStop(funcname);
PUB.log.AddE($"[{funcname}] 턴 방향 불일치(Current:{turnState}). 우회전 상태에서 좌회전을 시도할 수 없습니다.");
PUB._mapCanvas.SetAlertMessage("Turn 방향 오류");
PUB.sm.SetNewRunStep(ERunStep.ERROR);
}
else
{
//정상 (None) -> 턴 명령 실행
PUB.AGV.AGVMoveLeft180Turn(); PUB.AGV.AGVMoveLeft180Turn();
PUB.log.Add("AGV Left Turn"); PUB.log.Add("AGV Left Turn");
VAR.TIME.Update(eVarTime.LastTurnCommandTime); VAR.TIME.Update(eVarTime.LastTurnCommandTime);
PUB.sm.UpdateRunStepSeq();
}
} }
PUB._mapCanvas.SetAlertMessage($"턴 진행 중"); PUB._mapCanvas.SetAlertMessage($"턴 진행 중");
PUB.sm.UpdateRunStepSeq(); //이미완료된상태이므로 다음으로 진행한다.
return false; return false;
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//이미 완료된 상태라면 대기 과정을 건너뛴다.
if (PUB._virtualAGV.Turn == AGVNavigationCore.Models.AGVTurn.L90)
{
PUB.sm.UpdateRunStepSeq();
return false;
}
//왼쪽턴이 완료되지 않은경우 //왼쪽턴이 완료되지 않은경우
if (PUB.AGV.TurnInformation.State != arDev.eNarumiTurn.Left) if (PUB.AGV.TurnInformation.State != arDev.eNarumiTurn.Left)
{ {
@@ -82,7 +116,7 @@ namespace Project
var overtime = 30; var overtime = 30;
if (PUB.AGV.TurnInformation.Runtime.TotalSeconds > overtime) if (PUB.AGV.TurnInformation.Runtime.TotalSeconds > overtime)
{ {
//5초동안 AGV까 움직이지 않았다면 오류 처리한다. //30초동안 AGV까 움직이지 않았다면 오류 처리한다.
PUB.AGV.AGVMoveStop($"[bufferin] {overtime}초이내 턴 감지 안됨"); PUB.AGV.AGVMoveStop($"[bufferin] {overtime}초이내 턴 감지 안됨");
PUB.log.AddE($"[{funcname}] {overtime}초이내 턴 감지 안됨"); PUB.log.AddE($"[{funcname}] {overtime}초이내 턴 감지 안됨");
PUB._mapCanvas.SetAlertMessage($"턴 완료 확인 불가(최대:{overtime}초)"); PUB._mapCanvas.SetAlertMessage($"턴 완료 확인 불가(최대:{overtime}초)");
@@ -148,6 +182,17 @@ namespace Project
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//저속이동 (후진 진입) //저속이동 (후진 진입)
// [Smart Restart] 재시작 시 안전 검사
// 이미 턴을 완료했고(L90), 현재 마크 센서가 감지된다면(ON),
// 이미 목적지(Mark 2)에 도착한 것으로 간주하여 후진을 생략한다.
if (PUB._virtualAGV.Turn == AGVNavigationCore.Models.AGVTurn.L90 && PUB.AGV.signal1.mark_sensor == true)
{
PUB.log.Add($"[{funcname}] 이미 목적지 도착 확인됨(Turn:L90, Sensor:ON). 후진 이동을 생략합니다.");
PUB.sm.UpdateRunStepSeq();
return false;
}
var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData var ret = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
{ {
Bunki = arDev.Narumi.eBunki.Strate, Bunki = arDev.Narumi.eBunki.Strate,

View File

@@ -116,16 +116,36 @@ namespace Project
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//마크스탑 //AGV구동을 확인하고 마크스탑을 설정한다.
PUB.AGV.AGVMoveStop("buffer out", arDev.Narumi.eStopOpt.MarkStop);
PUB.sm.UpdateRunStepSeq();
return false;
}
else if (PUB.sm.RunStepSeq == idx++)
{
//이동확인
if (PUB.AGV.system1.agv_run == false) if (PUB.AGV.system1.agv_run == false)
{ {
if (seqtime.TotalSeconds > 3)
{
//구동이확인되지 않으면 오류처리를 한다.
PUB.AGV.AGVMoveStop(funcname);
PUB.log.AddE($"[{funcname}] AGV 전진 구동이 확인되지 않습니다");
PUB._mapCanvas.SetAlertMessage("agv 전진실패");
PUB.sm.SetNewRunStep(ERunStep.ERROR);
}
return false;
}
//마크스탑설정
PUB.AGV.AGVMoveStop(funcname, arDev.Narumi.eStopOpt.MarkStop);
PUB.sm.UpdateRunStepSeq();
return false;
}
else if (PUB.sm.RunStepSeq == idx++)
{
//마크스탑신호가 3초이내로 들어와야 한다
if (PUB.AGV.data.Speed != 'S')
{
if (seqtime.TotalSeconds > 3)
{
PUB.AGV.AGVMoveStop(funcname);
PUB.log.AddE($"[{funcname}] MARK STOP신호가 확인되지 않습니다");
PUB._mapCanvas.SetAlertMessage("mark stop 신호 확인 안됨");
PUB.sm.SetNewRunStep(ERunStep.ERROR);
}
return false; return false;
} }
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
@@ -133,9 +153,16 @@ namespace Project
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//멈춤확인 //AGV가 멈출때까지 기다린다.
if (PUB.AGV.system1.agv_run == true) if (PUB.AGV.system1.agv_run == true)
{ {
if (seqtime.TotalSeconds > 15)
{
PUB.AGV.AGVMoveStop(funcname);
PUB.log.AddE($"[{funcname}] AGV가 멈추지 않아 강제종료 합니다");
PUB._mapCanvas.SetAlertMessage("agv가 멈추지 않아 강제 종료");
PUB.sm.SetNewRunStep(ERunStep.ERROR);
}
return false; return false;
} }
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
@@ -144,8 +171,38 @@ namespace Project
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
{ {
//우측으로 180도 턴 //우측으로 180도 턴
//이미 턴이 되어있는지 확인한다 (재진입 시 중복 실행 방지)
if (PUB._virtualAGV.Turn == AGVNavigationCore.Models.AGVTurn.R90)
{
PUB.log.Add($"[{funcname}] 이미 Right Turn 완료 상태입니다. 턴 명령을 건너뜁니다.");
PUB.sm.UpdateRunStepSeq();
}
else
{
//하드웨어 상태 확인
var turnState = PUB.AGV.TurnInformation?.State ?? arDev.eNarumiTurn.None;
if (turnState == arDev.eNarumiTurn.Right || turnState == arDev.eNarumiTurn.RightIng)
{
//이미 우회전 중이거나 완료된 하드웨어 상태
PUB.log.Add($"[{funcname}] 하드웨어 우회전 상태 확인됨({turnState}). 명령을 건너뜁니다.");
PUB.sm.UpdateRunStepSeq();
}
else if (turnState == arDev.eNarumiTurn.Left || turnState == arDev.eNarumiTurn.LeftIng)
{
//비정상 상태 (좌회전 중?)
PUB.AGV.AGVMoveStop(funcname);
PUB.log.AddE($"[{funcname}] 턴 방향 불일치(Current:{turnState}). 좌회전 상태에서 우회전을 시도할 수 없습니다.");
PUB._mapCanvas.SetAlertMessage("Turn 방향 오류");
PUB.sm.SetNewRunStep(ERunStep.ERROR);
}
else
{
//정상 (None) -> 턴 명령 실행
PUB.AGV.AGVMoveRight180Turn(); PUB.AGV.AGVMoveRight180Turn();
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
}
}
return false; return false;
} }
else if (PUB.sm.RunStepSeq == idx++) else if (PUB.sm.RunStepSeq == idx++)
@@ -153,6 +210,27 @@ namespace Project
//이동확인 //이동확인
if (PUB.AGV.system1.agv_run == false) if (PUB.AGV.system1.agv_run == false)
{ {
if (seqtime.TotalSeconds > 3)
{
//만약 이미 완료된 상태라서 건너뛰었다면 run이 안될 수 있다.
//하지만 위에서 건너뜀 -> UpdateRunStepSeq -> 바로 여기로 옴.
//건너뛴 경우(Turn == R90), Run 확인을 할 필요가 없으므로...
//로직 보완: 턴 상태가 이미 완료라면 이동확인도 스킵해야 함?
//아니면 위 단계에서 완료 상태면 '다음 다음' 단계로 점프?
//간단하게: Turn이 R90이면 그냥 통과시킴.
if (PUB._virtualAGV.Turn == AGVNavigationCore.Models.AGVTurn.R90)
{
//이미 완료된 상태이므로 이동 확인 패스
PUB.sm.UpdateRunStepSeq();
return false;
}
PUB.AGV.AGVMoveStop(funcname);
PUB.log.AddE($"[{funcname}] Turn 구동 실패");
PUB._mapCanvas.SetAlertMessage("Turn 구동 실패");
PUB.sm.SetNewRunStep(ERunStep.ERROR);
}
return false; return false;
} }
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
@@ -163,8 +241,23 @@ namespace Project
//멈춤확인 //멈춤확인
if (PUB.AGV.system1.agv_run == true) if (PUB.AGV.system1.agv_run == true)
{ {
if (seqtime.TotalSeconds > 25)
{
PUB.AGV.AGVMoveStop(funcname);
PUB.log.AddE($"[{funcname}] Turn 완료 실패 (Timeout)");
PUB._mapCanvas.SetAlertMessage("Turn 완료 실패");
PUB.sm.SetNewRunStep(ERunStep.ERROR);
}
return false; return false;
} }
//턴 완료 상태 업데이트
if (PUB._virtualAGV.Turn != AGVNavigationCore.Models.AGVTurn.R90)
{
PUB._virtualAGV.Turn = AGVNavigationCore.Models.AGVTurn.R90;
PUB.log.Add($"[{funcname}] Turn State Updated to R90");
}
PUB.sm.UpdateRunStepSeq(); PUB.sm.UpdateRunStepSeq();
return false; return false;
} }

View File

@@ -12,9 +12,9 @@ namespace Project
public partial class fMain public partial class fMain
{ {
/// <summary> /// <summary>
/// 로더 진입 /// 장비로 진입한다. -
/// </summary> /// </summary>
public Boolean _SM_RUN_LOADER_IN(bool isFirst, TimeSpan seqTime) public Boolean _SM_RUN_ENTER(bool isFirst, TimeSpan seqTime)
{ {
var idx = 1; var idx = 1;
var funcname = PUB.sm.RunStep.ToString(); var funcname = PUB.sm.RunStep.ToString();

View File

@@ -12,9 +12,9 @@ namespace Project
public partial class fMain public partial class fMain
{ {
/// <summary> /// <summary>
/// 로더 배출 /// 장비엣 빠젼나온다.
/// </summary> /// </summary>
public Boolean _SM_RUN_LOADER_OUT(bool isFirst, TimeSpan seqTime) public Boolean _SM_RUN_EXIT(bool isFirst, TimeSpan seqTime)
{ {
var idx = 1; var idx = 1;
var funcname = PUB.sm.RunStep.ToString(); var funcname = PUB.sm.RunStep.ToString();

View File

@@ -203,22 +203,28 @@ namespace Project
{ {
if (PUB.AGV.system1.agv_run == false) if (PUB.AGV.system1.agv_run == false)
{ {
//목적지도착완료시 // 경로가 존재한다면, 경로의 마지막 노드에 도착했는지 확인한다.
if (PUB._virtualAGV.CurrentPath != null && PUB._virtualAGV.CurrentPath.DetailedPath.Any())
{
var lastInfo = PUB._virtualAGV.CurrentPath.DetailedPath.Last();
// 위치와 방향이 모두 일치해야 완료된 것으로 본다.
if (PUB._virtualAGV.CurrentNode.Id == lastInfo.NodeId &&
PUB._virtualAGV.CurrentDirection == lastInfo.MotorDirection)
{
var node = PUB._mapCanvas.Nodes.Where(t => t.Id == PUB._virtualAGV.CurrentNodeId).FirstOrDefault();
var rfid = node?.ID2 ?? "(X)";
PUB.log.AddI($"목표 도착 및 정지 확인됨(MarkStop 완료) Node:{rfid}, Dir:{PUB._virtualAGV.CurrentDirection}");
return true;
}
}
else
{
// 경로 정보가 없다면 단순히 목적지 ID와 비교한다 (Fallback)
if (PUB._virtualAGV.CurrentNode.Id == PUB._virtualAGV.TargetNode.Id) if (PUB._virtualAGV.CurrentNode.Id == PUB._virtualAGV.TargetNode.Id)
{ {
var node = PUB._mapCanvas.Nodes.Where(t => t.Id == PUB._virtualAGV.CurrentNodeId).FirstOrDefault(); var node = PUB._mapCanvas.Nodes.Where(t => t.Id == PUB._virtualAGV.CurrentNodeId).FirstOrDefault();
var rfid = node?.ID2 ?? "(X)"; var rfid = node?.ID2 ?? "(X)";
PUB.log.AddI($"목표 도착 및 정지 확인됨(MarkStop 완료) Node:{rfid}"); PUB.log.AddI($"목표 도착 및 정지 확인됨(MarkStop 완료, No Path Info) Node:{rfid}");
return true;
}
//목적지가 버퍼라면 그 앞에 멈춘다
if (PUB._virtualAGV.TargetNode.StationType == AGVNavigationCore.Models.StationType.Buffer &&
PUB._virtualAGV.CurrentPath != null && PUB._virtualAGV.CurrentPath.DetailedPath.Any())
{
if (PUB._virtualAGV.CurrentNode.Id == PUB._virtualAGV.CurrentPath.DetailedPath.Last().NodeId)
{
PUB.log.AddI($"목표(버퍼) 도착 및 정지 확인됨(MarkStop 완료). Node:{PUB._virtualAGV.CurrentNodeID2}");
return true; return true;
} }
} }