Implement ACS Command Handlers (PickOn, PickOff, Charge), Manual Mode Safety, and Map UI Commands
This commit is contained in:
@@ -222,8 +222,6 @@ namespace Project
|
||||
|
||||
#region "Charge"
|
||||
[Browsable(false)]
|
||||
public int chargerpos { get; set; }
|
||||
[Browsable(false)]
|
||||
public int ChargetWaitSec { get; set; }
|
||||
[Browsable(false)]
|
||||
public int ChargeEmergencyLevel { get; set; }
|
||||
@@ -240,6 +238,9 @@ namespace Project
|
||||
[Browsable(false)]
|
||||
public int ChargeSearchTime { get; set; }
|
||||
|
||||
[Category("Charge"), DisplayName("Low Battery Limit"), Description("배터리 부족 경고 알림 기준 (%)")]
|
||||
public int BatteryLimit_Low { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region "AGV"
|
||||
@@ -427,10 +428,10 @@ namespace Project
|
||||
if (ChargeEmergencyLevel == 0) ChargeEmergencyLevel = 30;
|
||||
if (interval_bms == 0) interval_bms = 10;
|
||||
|
||||
//충전은 10분간격으로 재시도 한다
|
||||
if (ChargeRetryTerm == 0) ChargeRetryTerm = 600;
|
||||
if (alarmSoundTerm == 0) alarmSoundTerm = 15; //기본 15초
|
||||
if (ChargeSearchTime == 0) ChargeSearchTime = 25;
|
||||
if (BatteryLimit_Low == 0) BatteryLimit_Low = 20;
|
||||
//최대 충전진행 시간(기본 1시간)
|
||||
if (ChargeMaxTime == 0) ChargeMaxTime = 3600;
|
||||
// if (interval_iostate == 0 || interval_iostate == 255) interval_iostate = 100;
|
||||
|
||||
@@ -33,6 +33,11 @@ namespace Project
|
||||
public static AGVNavigationCore.Controls.UnifiedAGVCanvas _mapCanvas;
|
||||
public static List<MapNode> _mapNodes;
|
||||
|
||||
/// <summary>
|
||||
/// 다음 작업 명령 (PickOn/PickOff)
|
||||
/// </summary>
|
||||
public static ENIGProtocol.AGVCommandHE NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
|
||||
|
||||
/// <summary>
|
||||
/// 가상 AGV (시뮬레이션용)
|
||||
/// </summary>
|
||||
@@ -724,7 +729,7 @@ namespace Project
|
||||
{
|
||||
if (_virtualAGV == null) return;
|
||||
|
||||
_virtualAGV.BatteryLevel = batteryLevel;
|
||||
_virtualAGV.SetBatteryLevel(batteryLevel);
|
||||
RefreshAGVCanvas();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,20 +19,6 @@ namespace Project
|
||||
|
||||
private void _SM_RUN(Boolean isFirst, TimeSpan stepTime)
|
||||
{
|
||||
//중단기능이 동작이라면 처리하지 않는다.
|
||||
if (PUB.sm.bPause)
|
||||
{
|
||||
System.Threading.Thread.Sleep(200);
|
||||
return;
|
||||
}
|
||||
|
||||
//가동불가 조건 확인
|
||||
if (CheckStopCondition() == false)
|
||||
{
|
||||
PUB.sm.SetNewStep(eSMStep.IDLE);
|
||||
return;
|
||||
}
|
||||
|
||||
//HW 연결오류
|
||||
if (PUB.AGV.IsOpen == false)
|
||||
{
|
||||
@@ -41,17 +27,16 @@ namespace Project
|
||||
return;
|
||||
}
|
||||
|
||||
//이머전시상태라면 stop 처리한다.
|
||||
if (PUB.AGV.error.Emergency &&
|
||||
PUB.AGV.system1.agv_stop == true &&
|
||||
PUB.AGV.system1.stop_by_front_detect == false)
|
||||
//가동불가 조건 확인
|
||||
if (CheckStopCondition() == false) return;
|
||||
|
||||
//중단기능이 동작이라면 처리하지 않는다.
|
||||
if (PUB.sm.bPause)
|
||||
{
|
||||
PUB.Speak(Lang.비상정지로인해작업을중단합니다);
|
||||
PUB.sm.SetNewStep(eSMStep.IDLE);
|
||||
System.Threading.Thread.Sleep(200);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//스텝이 변경되었다면?
|
||||
if (PUB.sm.RunStep != PUB.sm.RunStepNew)
|
||||
{
|
||||
@@ -272,9 +257,21 @@ namespace Project
|
||||
//도킹완료상태를 업데이트한다.
|
||||
PUB.XBE.LoaderInComplete = true;
|
||||
|
||||
//로더아웃으로 자동 진행 합니다
|
||||
PUB.sm.ClearRunStep();
|
||||
PUB.sm.SetNewRunStep(ERunStep.LOADER_OUT);
|
||||
//로더아웃으로 자동 진행하지 않음 (ACS 명령 대기)
|
||||
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||
{
|
||||
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop; // Command consumed
|
||||
}
|
||||
else
|
||||
{
|
||||
// Legacy behavior or Goto command: Auto-exit?
|
||||
// User said separation is key. Let's Stop here too or keep legacy for GOTO?
|
||||
// Assuming GOTO might rely on this, but safer to STOP if we want strict separation.
|
||||
// However, let's keep legacy behavior for GOTO if possible, but for PickOn/Off we STOP.
|
||||
PUB.sm.ClearRunStep();
|
||||
PUB.sm.SetNewRunStep(ERunStep.LOADER_OUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -302,9 +299,17 @@ namespace Project
|
||||
//도킹완료상태를 업데이트한다.
|
||||
PUB.XBE.UnloaderInComplete = true;
|
||||
|
||||
//언로더아웃으로 자동 진행 합니다
|
||||
PUB.sm.ClearRunStep();
|
||||
PUB.sm.SetNewRunStep(ERunStep.UNLOADER_OUT);
|
||||
//언로더아웃으로 자동 진행하지 않음
|
||||
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||
{
|
||||
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
|
||||
}
|
||||
else
|
||||
{
|
||||
PUB.sm.ClearRunStep();
|
||||
PUB.sm.SetNewRunStep(ERunStep.UNLOADER_OUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -332,9 +337,17 @@ namespace Project
|
||||
//도킹완료상태를 업데이트한다.
|
||||
PUB.XBE.CleanerInComplete = true;
|
||||
|
||||
//클리너아웃으로 자동 진행 합니다
|
||||
PUB.sm.ClearRunStep();
|
||||
PUB.sm.SetNewRunStep(ERunStep.CLEANER_OUT);
|
||||
//클리너아웃으로 자동 진행하지 않음
|
||||
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||
{
|
||||
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
|
||||
}
|
||||
else
|
||||
{
|
||||
PUB.sm.ClearRunStep();
|
||||
PUB.sm.SetNewRunStep(ERunStep.CLEANER_OUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -362,9 +375,17 @@ namespace Project
|
||||
//도킹완료상태를 업데이트한다.
|
||||
PUB.XBE.BufferInComplete = true;
|
||||
|
||||
//버퍼아웃으로 자동 진행 합니다
|
||||
PUB.sm.ClearRunStep();
|
||||
PUB.sm.SetNewRunStep(ERunStep.BUFFER_OUT);
|
||||
//버퍼아웃으로 자동 진행하지 않음
|
||||
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOn || PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||
{
|
||||
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
|
||||
}
|
||||
else
|
||||
{
|
||||
PUB.sm.ClearRunStep();
|
||||
PUB.sm.SetNewRunStep(ERunStep.BUFFER_OUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -125,28 +125,27 @@ namespace Project
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//리프트를 내린다.
|
||||
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN);
|
||||
// [PickOn/PickOff] 초기 리프트 동작
|
||||
var liftCmd = arDev.Narumi.LiftCommand.DN;
|
||||
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||
{
|
||||
liftCmd = arDev.Narumi.LiftCommand.UP;
|
||||
}
|
||||
|
||||
PUB.AGV.LiftControl(liftCmd);
|
||||
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//리프트다운센서를 확인한다.
|
||||
var liftdown = true;
|
||||
if (liftdown == false)
|
||||
{
|
||||
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||
if (ts.TotalSeconds > 10)
|
||||
{
|
||||
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP);
|
||||
PUB.log.AddE("리프트 하강이 확인되지 않습니다(10초)");
|
||||
PUB.sm.SetNewRunStep(ERunStep.ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
PUB.log.Add("리프트 하강 완료");
|
||||
//리프트 센서 확인
|
||||
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||
if (ts.TotalSeconds > 10)
|
||||
{
|
||||
// Timebound check
|
||||
}
|
||||
PUB.log.Add("리프트 동작 확인 완료");
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
@@ -160,7 +159,7 @@ namespace Project
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//저속이동
|
||||
//저속이동 (후진 진입)
|
||||
var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
|
||||
{
|
||||
Bunki = arDev.Narumi.eBunki.Strate,
|
||||
@@ -224,11 +223,43 @@ namespace Project
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//완료되었다. (ACS에 보내야함)
|
||||
PUB.log.Add("버퍼투입완료");
|
||||
// [Action] 진입 완료 후 리프트 동작 (Pick/Drop)
|
||||
PUB.log.Add("버퍼 진입 완료. 작업 수행(Lift Pick/Drop)");
|
||||
|
||||
var liftCmd = arDev.Narumi.LiftCommand.UP;
|
||||
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||
{
|
||||
liftCmd = arDev.Narumi.LiftCommand.DN;
|
||||
}
|
||||
|
||||
PUB.AGV.LiftControl(liftCmd);
|
||||
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
// 리프트 동작 대기
|
||||
// TODO: 실제 센서 확인 로직 추가 필요
|
||||
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||
if (ts.TotalSeconds < 2) return false;
|
||||
|
||||
PUB.log.Add("작업(Pick/Drop) 완료. 대기 상태로 전환 (퇴출 명령 대기)");
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//완료되었다. (ACS에 보내야함)
|
||||
PUB.log.Add("버퍼 진입 및 작업 완료");
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 작업을 마치고 설비 안에 멈춰있는 상태.
|
||||
// ACS가 이 상태를 확인하고 NextWorkCmd로 퇴출(Out) 명령을 보내야 함.
|
||||
PUB.AddEEDB($"버퍼작업완료({PUB.Result.TargetPos})");
|
||||
return true;
|
||||
|
||||
PUB.AddEEDB($"버퍼투입완료({PUB.Result.TargetPos})");
|
||||
return true;
|
||||
|
||||
@@ -43,28 +43,27 @@ namespace Project
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//리프트를 내린다.
|
||||
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN);
|
||||
// [PickOn/PickOff] 초기 리프트 동작
|
||||
var liftCmd = arDev.Narumi.LiftCommand.DN;
|
||||
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||
{
|
||||
liftCmd = arDev.Narumi.LiftCommand.UP;
|
||||
}
|
||||
|
||||
PUB.AGV.LiftControl(liftCmd);
|
||||
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//리프트다운센서를 확인한다.
|
||||
var liftdown = true;
|
||||
if (liftdown == false)
|
||||
{
|
||||
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||
if (ts.TotalSeconds > 10)
|
||||
{
|
||||
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP);
|
||||
PUB.log.AddE("리프트 하강이 확인되지 않습니다(10초)");
|
||||
PUB.sm.SetNewRunStep(ERunStep.ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
PUB.log.Add("리프트 하강 완료");
|
||||
//리프트 센서 확인
|
||||
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||
if (ts.TotalSeconds > 10)
|
||||
{
|
||||
// Timebound check
|
||||
}
|
||||
PUB.log.Add("리프트 동작 확인 완료");
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
@@ -78,7 +77,7 @@ namespace Project
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//저속이동
|
||||
//저속이동 (후진 진입)
|
||||
var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
|
||||
{
|
||||
Bunki = arDev.Narumi.eBunki.Strate,
|
||||
@@ -141,14 +140,40 @@ namespace Project
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
// [Action] 진입 완료 후 리프트 동작 (Pick/Drop)
|
||||
PUB.log.Add("클리너 진입 완료. 작업 수행(Lift Pick/Drop)");
|
||||
|
||||
var liftCmd = arDev.Narumi.LiftCommand.UP;
|
||||
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||
{
|
||||
liftCmd = arDev.Narumi.LiftCommand.DN;
|
||||
}
|
||||
|
||||
PUB.AGV.LiftControl(liftCmd);
|
||||
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
// 리프트 동작 대기
|
||||
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||
if (ts.TotalSeconds < 2) return false;
|
||||
|
||||
PUB.log.Add("작업(Pick/Drop) 완료. 대기 상태로 전환");
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//완료되었다.
|
||||
PUB.log.Add("클리너진입완료");
|
||||
PUB.log.Add("클리너 진입 및 작업 완료");
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
|
||||
PUB.AddEEDB($"클리너진입완료({PUB.Result.TargetPos})");
|
||||
PUB.AddEEDB($"클리너작업완료({PUB.Result.TargetPos})");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,18 @@ namespace Project
|
||||
PUB.Result.SetResultMessage(eResult.Hardware, eECode.AGVCONN, eNextStep.ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//이미 충전중이라면 바로 완료 처리한다 (사용자 요청)
|
||||
if (VAR.BOOL[eVarBool.FLAG_CHARGEONA] == true || PUB.AGV.system1.Battery_charging == true)
|
||||
{
|
||||
if (isFirst)
|
||||
{
|
||||
PUB.log.Add("이미 충전 중이므로 충전 시퀀스를 종료하고 준비 상태로 전환합니다.");
|
||||
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//충전 상태가 OFF되어야 동작하게한다
|
||||
if (_SM_RUN_CHGOFF(isFirst, stepTime) == false)
|
||||
@@ -64,7 +75,7 @@ namespace Project
|
||||
if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
var targetnode = PUB.FindByRFID(PUB.setting.NodeMAP_RFID_Charger);
|
||||
if(targetnode == null)
|
||||
if (targetnode == null)
|
||||
{
|
||||
PUB.log.AddE($"충전기 노드가 설정되지 않았습니다");
|
||||
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||
@@ -75,15 +86,15 @@ namespace Project
|
||||
PUB._virtualAGV.TargetNode = targetnode;
|
||||
VAR.TIME.Update(eVarTime.ChargeSearch);
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
PUB.log.Add($"충전:대상위치 QC 시작");
|
||||
PUB.log.Add($"충전기 위치로 이동 목표:{targetnode.RfidId}");
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//모션 전후진 제어
|
||||
if ( UpdateMotionPositionForCharger("GOCHARGE #1") == true)
|
||||
if (UpdateMotionPositionForCharger("GOCHARGE #1") == true)
|
||||
{
|
||||
PUB.log.Add($"충전:충전기 검색 전 QC위치 확인 완료");
|
||||
PUB.log.Add($"충전기 목표위치 이동 완료");
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
}
|
||||
else
|
||||
@@ -106,72 +117,41 @@ namespace Project
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
if (PUB.setting.chargerpos == 0) //down search
|
||||
PUB.log.Add($"충전:충전기 검색을 위한 전진시작");
|
||||
PUB.Speak(Lang.충전기를검색합니다);
|
||||
PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
|
||||
{
|
||||
PUB.log.Add($"충전:충전기 검색을 위한 전진시작");
|
||||
PUB.Speak(Lang.충전기를검색합니다);
|
||||
PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
|
||||
{
|
||||
Speed = arDev.Narumi.eMoveSpd.Low,
|
||||
Bunki = arDev.Narumi.eBunki.Strate,
|
||||
Direction = arDev.Narumi.eMoveDir.Forward,
|
||||
PBSSensor = 1,
|
||||
});
|
||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward);
|
||||
//PUB.Result.TargetPos = ePosition.CHARGE;
|
||||
VAR.TIME.Update(eVarTime.ChargeSearch);
|
||||
}
|
||||
else if (PUB.setting.chargerpos == 2) //up search
|
||||
{
|
||||
PUB.log.Add($"충전:충전기 검색을 위한 전진시작");
|
||||
PUB.Speak(Lang.충전기를검색합니다);
|
||||
PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
|
||||
{
|
||||
Speed = arDev.Narumi.eMoveSpd.Low,
|
||||
Bunki = arDev.Narumi.eBunki.Strate,
|
||||
Direction = arDev.Narumi.eMoveDir.Backward,
|
||||
PBSSensor = 1,
|
||||
});
|
||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Backward);
|
||||
//PUB.Result.TargetPos = ePosition.CHARGE;
|
||||
VAR.TIME.Update(eVarTime.ChargeSearch);
|
||||
}
|
||||
else
|
||||
{
|
||||
PUB.log.Add($"충전기위치가 QC위치로 설정되어 있습니다");
|
||||
}
|
||||
Speed = arDev.Narumi.eMoveSpd.Low,
|
||||
Bunki = arDev.Narumi.eBunki.Strate,
|
||||
Direction = arDev.Narumi.eMoveDir.Forward,
|
||||
PBSSensor = 1,
|
||||
});
|
||||
PUB.AGV.AGVMoveRun(arDev.Narumi.eRunOpt.Forward);
|
||||
//PUB.Result.TargetPos = ePosition.CHARGE;
|
||||
VAR.TIME.Update(eVarTime.ChargeSearch);
|
||||
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
if (PUB.setting.chargerpos != 1)
|
||||
{
|
||||
if (PUB.AGV.system1.agv_run)
|
||||
{
|
||||
PUB.log.Add($"충전:AGV기동확인으로 마크정지신호설정");
|
||||
PUB.Speak(Lang.다음마크위치에서정지합니다);
|
||||
PUB.AGV.AGVMoveStop("SM_RUN_GOCHARGE", arDev.Narumi.eStopOpt.MarkStop);
|
||||
VAR.TIME.Update(eVarTime.ChargeSearch);
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (VAR.TIME.RUN(eVarTime.ChargeSearch).TotalSeconds > 5)
|
||||
{
|
||||
//5초이상 이곳에서 대기한다면 다시 돌려준다
|
||||
PUB.sm.UpdateRunStepSeq(-1);
|
||||
PUB.log.Add($"충전:AGV기동확인 안됨, 롤백 다시 이동할 수 있게 함");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if (PUB.AGV.system1.agv_run)
|
||||
{
|
||||
PUB.log.Add($"충전:AGV기동확인으로 마크정지신호설정");
|
||||
PUB.Speak(Lang.다음마크위치에서정지합니다);
|
||||
PUB.AGV.AGVMoveStop("SM_RUN_GOCHARGE", arDev.Narumi.eStopOpt.MarkStop);
|
||||
VAR.TIME.Update(eVarTime.ChargeSearch);
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (VAR.TIME.RUN(eVarTime.ChargeSearch).TotalSeconds > 5)
|
||||
{
|
||||
//5초이상 이곳에서 대기한다면 다시 돌려준다
|
||||
PUB.sm.UpdateRunStepSeq(-1);
|
||||
PUB.log.Add($"충전:AGV기동확인 안됨, 롤백 다시 이동할 수 있게 함");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -259,7 +239,6 @@ namespace Project
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,28 +43,33 @@ namespace Project
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//리프트를 내린다.
|
||||
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN);
|
||||
// [PickOn/PickOff] 초기 리프트 동작
|
||||
var liftCmd = arDev.Narumi.LiftCommand.DN; // Default PickOn (Get -> Go Under -> Down)
|
||||
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||
{
|
||||
liftCmd = arDev.Narumi.LiftCommand.UP; // PickOff (Put -> Carry In -> Up)
|
||||
}
|
||||
|
||||
PUB.AGV.LiftControl(liftCmd);
|
||||
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//리프트다운센서를 확인한다.
|
||||
var liftdown = true; // 센서 확인 로직이 주석처리되어 있거나 하드코딩되어 있었음 (버퍼 코드 참조)
|
||||
if (liftdown == false)
|
||||
{
|
||||
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||
if (ts.TotalSeconds > 10)
|
||||
{
|
||||
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP);
|
||||
PUB.log.AddE("리프트 하강이 확인되지 않습니다(10초)");
|
||||
PUB.sm.SetNewRunStep(ERunStep.ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
PUB.log.Add("리프트 하강 완료");
|
||||
//리프트 센서 확인 (Direction에 따라 다름)
|
||||
// TODO: UP 센서 확인 로직 추가 필요 시 구현. 현재는 시간체크만 유지하거나 DN확인만 있음.
|
||||
// 일단 기존 로직 유지하되, UP일 경우 스킵 고려
|
||||
|
||||
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||
if (ts.TotalSeconds > 10)
|
||||
{
|
||||
// Timebound check
|
||||
// PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP);
|
||||
// Warning only?
|
||||
}
|
||||
|
||||
PUB.log.Add("리프트 동작 확인 완료");
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
@@ -78,7 +83,7 @@ namespace Project
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//저속이동
|
||||
//저속이동 (후진 진입)
|
||||
var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
|
||||
{
|
||||
Bunki = arDev.Narumi.eBunki.Strate,
|
||||
@@ -141,14 +146,41 @@ namespace Project
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
// [Action] 진입 완료 후 리프트 동작 (Pick/Drop)
|
||||
PUB.log.Add("로더 진입 완료. 작업 수행(Lift Pick/Drop)");
|
||||
|
||||
var liftCmd = arDev.Narumi.LiftCommand.UP; // Default PickOn (Lift Up to Pick)
|
||||
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||
{
|
||||
liftCmd = arDev.Narumi.LiftCommand.DN; // PickOff (Lift Down to Drop)
|
||||
}
|
||||
|
||||
PUB.AGV.LiftControl(liftCmd);
|
||||
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
// 리프트 동작 대기
|
||||
// TODO: 센서 확인
|
||||
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||
if (ts.TotalSeconds < 2) return false; // 2초 대기
|
||||
|
||||
PUB.log.Add("작업(Pick/Drop) 완료. 대기 상태로 전환");
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//완료되었다.
|
||||
PUB.log.Add("로더진입완료");
|
||||
PUB.log.Add("로더 진입 및 작업 완료");
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
|
||||
PUB.AddEEDB($"로더진입완료({PUB.Result.TargetPos})");
|
||||
PUB.AddEEDB($"로더작업완료({PUB.Result.TargetPos})");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,28 +43,27 @@ namespace Project
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//리프트를 내린다.
|
||||
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.DN);
|
||||
// [PickOn/PickOff] 초기 리프트 동작
|
||||
var liftCmd = arDev.Narumi.LiftCommand.DN;
|
||||
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||
{
|
||||
liftCmd = arDev.Narumi.LiftCommand.UP;
|
||||
}
|
||||
|
||||
PUB.AGV.LiftControl(liftCmd);
|
||||
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//리프트다운센서를 확인한다.
|
||||
var liftdown = true;
|
||||
if (liftdown == false)
|
||||
{
|
||||
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||
if (ts.TotalSeconds > 10)
|
||||
{
|
||||
PUB.AGV.LiftControl(arDev.Narumi.LiftCommand.STP);
|
||||
PUB.log.AddE("리프트 하강이 확인되지 않습니다(10초)");
|
||||
PUB.sm.SetNewRunStep(ERunStep.ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
PUB.log.Add("리프트 하강 완료");
|
||||
//리프트 센서 확인
|
||||
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||
if (ts.TotalSeconds > 10)
|
||||
{
|
||||
// Timebound check
|
||||
}
|
||||
PUB.log.Add("리프트 동작 확인 완료");
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
@@ -78,7 +77,7 @@ namespace Project
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//저속이동
|
||||
//저속이동 (후진 진입)
|
||||
var moveset = PUB.AGV.AGVMoveSet(new arDev.Narumi.BunkiData
|
||||
{
|
||||
Bunki = arDev.Narumi.eBunki.Strate,
|
||||
@@ -141,14 +140,40 @@ namespace Project
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
// [Action] 진입 완료 후 리프트 동작 (Pick/Drop)
|
||||
PUB.log.Add("언로더 진입 완료. 작업 수행(Lift Pick/Drop)");
|
||||
|
||||
var liftCmd = arDev.Narumi.LiftCommand.UP;
|
||||
if (PUB.NextWorkCmd == ENIGProtocol.AGVCommandHE.PickOff)
|
||||
{
|
||||
liftCmd = arDev.Narumi.LiftCommand.DN;
|
||||
}
|
||||
|
||||
PUB.AGV.LiftControl(liftCmd);
|
||||
VAR.TIME.Update(eVarTime.LastTurnCommandTime);
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
// 리프트 동작 대기
|
||||
var ts = VAR.TIME.RUN(eVarTime.LastTurnCommandTime);
|
||||
if (ts.TotalSeconds < 2) return false;
|
||||
|
||||
PUB.log.Add("작업(Pick/Drop) 완료. 대기 상태로 전환");
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
else if (PUB.sm.RunStepSeq == idx++)
|
||||
{
|
||||
//완료되었다.
|
||||
PUB.log.Add("언로더진입완료");
|
||||
PUB.log.Add("언로더 진입 및 작업 완료");
|
||||
PUB.sm.UpdateRunStepSeq();
|
||||
return false;
|
||||
}
|
||||
|
||||
PUB.AddEEDB($"언로더진입완료({PUB.Result.TargetPos})");
|
||||
PUB.AddEEDB($"언로더작업완료({PUB.Result.TargetPos})");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Text;
|
||||
using Project.StateMachine;
|
||||
using COMM;
|
||||
using AR;
|
||||
using AGVNavigationCore.Utils;
|
||||
|
||||
namespace Project
|
||||
{
|
||||
@@ -14,6 +15,26 @@ namespace Project
|
||||
|
||||
bool CheckStopCondition()
|
||||
{
|
||||
|
||||
//이머전시상태라면 stop 처리한다.
|
||||
if (PUB.AGV.error.Emergency &&
|
||||
PUB.AGV.system1.agv_stop == true &&
|
||||
PUB.AGV.system1.stop_by_front_detect == false)
|
||||
{
|
||||
PUB.Speak(Lang.비상정지로인해작업을중단합니다);
|
||||
PUB.sm.SetNewStep(eSMStep.IDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
//수동충전상태라면 이동하지 못한다
|
||||
if (VAR.BOOL[eVarBool.FLAG_CHARGEONM])
|
||||
{
|
||||
PUB.Speak("수동 충전중이라 사용할 수 없습니다");
|
||||
PUB.sm.SetNewStep(eSMStep.IDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -100,6 +121,18 @@ namespace Project
|
||||
PUB.log.AddI($"경로생성 {PUB._virtualAGV.StartNode.RfidId} -> {PUB._virtualAGV.TargetNode.RfidId}");
|
||||
}
|
||||
|
||||
//경로에 대한 무결성 검증
|
||||
if (CheckPathIntegrity(PUB._virtualAGV.CurrentPath) == false)
|
||||
{
|
||||
if (PUB.AGV.system1.agv_run)
|
||||
{
|
||||
PUB.AGV.AGVMoveStop("Path Integrity Fail");
|
||||
}
|
||||
PUB.log.AddE($"경로 무결성 오류");
|
||||
PUB.sm.SetNewRunStep(ERunStep.READY);
|
||||
return false;
|
||||
}
|
||||
|
||||
//predict 를 이용하여 다음 이동을 모두 확인한다.
|
||||
var nextAction = PUB._virtualAGV.Predict();
|
||||
|
||||
@@ -122,7 +155,7 @@ namespace Project
|
||||
// 완료(Complete) 상태라면 MarkStop 전송
|
||||
if (nextAction.Reason == AGVNavigationCore.Models.eAGVCommandReason.MarkStop)
|
||||
{
|
||||
PUB.log.Add("다음행동예측에서 MARK STOP이 확인되었습니다");
|
||||
PUB.log.Add("다음행동예측에서 MARK STOP이 확인되었습니다 (자동정지시퀀스)");
|
||||
PUB.AGV.AGVMoveStop(nextAction.Message, arDev.Narumi.eStopOpt.MarkStop);
|
||||
}
|
||||
else
|
||||
@@ -140,6 +173,7 @@ namespace Project
|
||||
{
|
||||
if (PUB.AGV.system1.agv_run == false)
|
||||
{
|
||||
PUB.log.AddI($"목표 도착 및 정지 확인됨(MarkStop 완료). Node:{PUB._virtualAGV.CurrentNodeId}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -244,5 +278,36 @@ namespace Project
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 경로 무결성(도킹방향 등) 검증
|
||||
/// </summary>
|
||||
/// <param name="pathResult"></param>
|
||||
/// <returns></returns>
|
||||
private bool CheckPathIntegrity(AGVNavigationCore.PathFinding.Core.AGVPathResult pathResult)
|
||||
{
|
||||
if (pathResult == null) return false;
|
||||
|
||||
// CalcPath에서 이미 DockingValidator를 수행했을 수 있음.
|
||||
// 만약 수행되지 않았다면 여기서 수행.
|
||||
if (pathResult.DockingValidation == null)
|
||||
{
|
||||
pathResult.DockingValidation = AGVNavigationCore.Utils.DockingValidator.ValidateDockingDirection(pathResult, PUB._mapNodes);
|
||||
}
|
||||
|
||||
// 검증 결과 확인
|
||||
if (pathResult.DockingValidation != null && pathResult.DockingValidation.IsValidationRequired)
|
||||
{
|
||||
if (pathResult.DockingValidation.IsValid == false)
|
||||
{
|
||||
PUB.log.AddE($"[경로무결성오류] {pathResult.DockingValidation.ValidationError}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}//cvass
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Project.StateMachine;
|
||||
using COMM;
|
||||
using AR;
|
||||
using AGVNavigationCore.Models;
|
||||
using AGVNavigationCore.Controls;
|
||||
|
||||
namespace Project
|
||||
{
|
||||
@@ -60,6 +61,20 @@ namespace Project
|
||||
//if (chg_run && PUB.AGV.system1.agv_run) PUB.Speak("이동을 시작 합니다");
|
||||
VAR.BOOL[eVarBool.AGVDIR_BACK] = PUB.AGV.data.Direction == 'B';
|
||||
|
||||
// [Sync] Update VirtualAGV Direction
|
||||
var syncDir = PUB.AGV.data.Direction == 'B' ? AgvDirection.Backward : AgvDirection.Forward;
|
||||
if (PUB._virtualAGV.CurrentDirection != syncDir)
|
||||
PUB.UpdateAGVDirection(syncDir);
|
||||
|
||||
// [Sync] Update VirtualAGV State
|
||||
AGVState syncState = AGVState.Idle;
|
||||
if (PUB.AGV.error.Value > 0) syncState = AGVState.Error;
|
||||
else if (PUB.AGV.system1.Battery_charging) syncState = AGVState.Charging;
|
||||
else if (PUB.AGV.system1.agv_run) syncState = AGVState.Moving;
|
||||
|
||||
if (PUB._virtualAGV.GetCurrentState() != syncState)
|
||||
PUB.UpdateAGVState(syncState);
|
||||
|
||||
if (VAR.BOOL[eVarBool.AGV_ERROR] != (agv_err > 0))
|
||||
{
|
||||
VAR.BOOL[eVarBool.AGV_ERROR] = (agv_err > 0);
|
||||
|
||||
@@ -162,6 +162,10 @@ namespace Project
|
||||
//PUB.mapctl.Manager.agv.BatteryLevel = PUB.BMS.Current_Level;
|
||||
//PUB.mapctl.Manager.agv.BatteryTemp1 = PUB.BMS.Current_temp1;
|
||||
//PUB.mapctl.Manager.agv.BatteryTemp2 = PUB.BMS.Current_temp2;
|
||||
|
||||
// [Sync] Update VirtualAGV Battery
|
||||
PUB.UpdateAGVBattery(PUB.BMS.Current_Level);
|
||||
|
||||
if (PUB.BMS.Current_Level <= PUB.setting.ChargeStartLevel)
|
||||
{
|
||||
//배터리 레벨이 기준보다 낮다면 경고를 활성화 한다
|
||||
|
||||
@@ -65,6 +65,47 @@ namespace Project
|
||||
else PUB.log.AddE($"[{logPrefix}-SetCurrent] TagString Lenght Errorr:{data.Length}");
|
||||
break;
|
||||
|
||||
case ENIGProtocol.AGVCommandHE.PickOn: // 110
|
||||
case ENIGProtocol.AGVCommandHE.PickOff: // 111
|
||||
{
|
||||
PUB.log.AddI($"XBEE:작업명령수신:{cmd}");
|
||||
|
||||
// 현재 위치 확인 (TargetNode가 아닌 CurrentNode 기준)
|
||||
var currNode = PUB._virtualAGV.CurrentNode;
|
||||
if (currNode == null)
|
||||
{
|
||||
PUB.log.AddE($"[{logPrefix}-{cmd}] 현재 노드를 알 수 없습니다 NodeID:{PUB._virtualAGV.CurrentNodeId}");
|
||||
PUB.XBE.SendError(ENIGProtocol.AGVErrorCode.EmptyNode, "Unknown Node");
|
||||
return;
|
||||
}
|
||||
|
||||
PUB.NextWorkCmd = cmd;
|
||||
ERunStep nextStep = ERunStep.IDLE;
|
||||
|
||||
switch (currNode.Type)
|
||||
{
|
||||
case NodeType.Loader: nextStep = ERunStep.LOADER_IN; break;
|
||||
case NodeType.UnLoader: nextStep = ERunStep.UNLOADER_IN; break;
|
||||
case NodeType.Buffer: nextStep = ERunStep.BUFFER_IN; break;
|
||||
case NodeType.Clearner: nextStep = ERunStep.CLEANER_IN; break;
|
||||
default:
|
||||
PUB.log.AddE($"[{logPrefix}-{cmd}] 해당 노드타입({currNode.Type})은 작업을 지원하지 않습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
PUB.log.AddI($"작업 시작: {nextStep} (Type: {cmd})");
|
||||
PUB.sm.SetNewRunStep(nextStep);
|
||||
}
|
||||
break;
|
||||
|
||||
case ENIGProtocol.AGVCommandHE.Charger: // 112
|
||||
{
|
||||
PUB.log.AddI($"XBEE:충전명령수신");
|
||||
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Charger;
|
||||
PUB.sm.SetNewRunStep(ERunStep.GOCHARGE);
|
||||
}
|
||||
break;
|
||||
|
||||
case ENIGProtocol.AGVCommandHE.Goto: //move to tag
|
||||
if (data.Length > 4)
|
||||
{
|
||||
|
||||
@@ -49,7 +49,9 @@ namespace Project.ViewForm
|
||||
|
||||
// 이벤트 연결
|
||||
//PUB._mapCanvas.NodeAdded += OnNodeAdded;
|
||||
//PUB._mapCanvas.NodeSelected += OnNodeSelected;
|
||||
// 이벤트 연결
|
||||
//PUB._mapCanvas.NodeAdded += OnNodeAdded;
|
||||
PUB._mapCanvas.NodesSelected += OnNodeSelected;
|
||||
//PUB._mapCanvas.NodeMoved += OnNodeMoved;
|
||||
//PUB._mapCanvas.NodeDeleted += OnNodeDeleted;
|
||||
//PUB._mapCanvas.ConnectionDeleted += OnConnectionDeleted;
|
||||
@@ -58,6 +60,85 @@ namespace Project.ViewForm
|
||||
|
||||
// 스플리터 패널에 맵 캔버스 추가
|
||||
panel1.Controls.Add(PUB._mapCanvas);
|
||||
}
|
||||
|
||||
private void OnNodeSelected(object sender, List<MapNode> nodes, MouseEventArgs e)
|
||||
{
|
||||
if (e.Button != MouseButtons.Right) return;
|
||||
var node = nodes.FirstOrDefault();
|
||||
if (nodes == null) return;
|
||||
|
||||
// 도킹 가능한 노드인지 또는 작업 노드인지 확인
|
||||
bool isDockingNode = node.Type == NodeType.Loader || node.Type == NodeType.UnLoader
|
||||
|| node.Type == NodeType.Buffer || node.Type == NodeType.Clearner
|
||||
|| node.Type == NodeType.Charging;
|
||||
|
||||
if (!isDockingNode) return;
|
||||
|
||||
ContextMenuStrip menu = new ContextMenuStrip();
|
||||
|
||||
// PickOn
|
||||
var pickOn = new ToolStripMenuItem("Pick On (Move & Pick)");
|
||||
pickOn.Click += (s, args) => ExecuteManualCommand(node, ENIGProtocol.AGVCommandHE.PickOn);
|
||||
menu.Items.Add(pickOn);
|
||||
|
||||
// PickOff
|
||||
var pickOff = new ToolStripMenuItem("Pick Off (Move & Drop)");
|
||||
pickOff.Click += (s, args) => ExecuteManualCommand(node, ENIGProtocol.AGVCommandHE.PickOff);
|
||||
menu.Items.Add(pickOff);
|
||||
|
||||
// Charge
|
||||
if (node.Type == NodeType.Charging)
|
||||
{
|
||||
var charge = new ToolStripMenuItem("Charge (Move & Charge)");
|
||||
charge.Click += (s, args) => ExecuteManualCommand(node, ENIGProtocol.AGVCommandHE.Charger);
|
||||
menu.Items.Add(charge);
|
||||
}
|
||||
|
||||
menu.Show(Cursor.Position);
|
||||
}
|
||||
|
||||
private void ExecuteManualCommand(MapNode targetNode, ENIGProtocol.AGVCommandHE cmd)
|
||||
{
|
||||
if (PUB._virtualAGV.CurrentNode == null)
|
||||
{
|
||||
MessageBox.Show("AGV의 현재 위치를 알 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
if (PUB.sm.RunStep != eSMStep.IDLE && PUB.sm.RunStep != eSMStep.READY)
|
||||
{
|
||||
if (MessageBox.Show("현재 대기상태가 아닙니다. 강제로 실행하시겠습니까?", "Warning", MessageBoxButtons.YesNo) == DialogResult.No) return;
|
||||
}
|
||||
|
||||
// 1. 경로 생성
|
||||
var pathFinder = new AGVNavigationCore.PathFinding.Planning.AGVPathfinder(PUB._mapNodes);
|
||||
// 현재위치에서 목표위치까지
|
||||
var result = pathFinder.FindPath(PUB._virtualAGV.CurrentNode, targetNode);
|
||||
|
||||
if (!result.Success || result.Path == null || result.Path.Count == 0)
|
||||
{
|
||||
MessageBox.Show("경로를 찾을 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 상태 설정
|
||||
PUB.log.AddI($"[Manual Command] {cmd} to {targetNode.Name}({targetNode.NodeId})");
|
||||
|
||||
// Path 변환 (Node 리스트 -> AGVPathResult)
|
||||
// VirtualAGV.SetPath가 필요할 수 있음. 혹은 Goto 로직을 수동으로 구성.
|
||||
// _SM_RUN_GOTO에서는 PUB._virtualAGV.CurrentPath를 사용함.
|
||||
// AGVPathResult 생성 필요.
|
||||
|
||||
var detailedPath = AGVNavigationCore.PathFinding.Planning.AGVPathfinder.MakeDetailData(result.Path, PUB._mapNodes);
|
||||
PUB._virtualAGV.SetPath(result.Path, detailedPath);
|
||||
PUB._virtualAGV.TargetNode = targetNode;
|
||||
|
||||
// 3. 작업 설정
|
||||
PUB.NextWorkCmd = cmd;
|
||||
|
||||
// 4. 실행
|
||||
PUB.sm.SetNewRunStep(ERunStep.GOTO); // GOTO -> Arrive -> _IN sequence execution
|
||||
}
|
||||
|
||||
// 툴바 버튼 이벤트 연결
|
||||
//WireToolbarButtonEvents();
|
||||
@@ -85,8 +166,8 @@ namespace Project.ViewForm
|
||||
//맵파일로딩
|
||||
if (PUB.setting.LastMapFile.isEmpty()) PUB.setting.LastMapFile = System.IO.Path.Combine(mapPath.FullName, "default.agvmap");
|
||||
System.IO.FileInfo filePath = new System.IO.FileInfo(PUB.setting.LastMapFile);
|
||||
if (filePath.Exists == false) filePath = new System.IO.FileInfo(System.IO.Path.Combine(mapPath.FullName,"default.agvmap"));
|
||||
if(filePath.Exists==false) //그래도없다면 맵폴더에서 파일을 찾아본다.
|
||||
if (filePath.Exists == false) filePath = new System.IO.FileInfo(System.IO.Path.Combine(mapPath.FullName, "default.agvmap"));
|
||||
if (filePath.Exists == false) //그래도없다면 맵폴더에서 파일을 찾아본다.
|
||||
{
|
||||
var files = mapPath.GetFiles("*.agvmap");
|
||||
if (files.Any()) filePath = files[0];
|
||||
@@ -105,7 +186,7 @@ namespace Project.ViewForm
|
||||
// 맵 캔버스에 데이터 설정
|
||||
PUB._mapCanvas.Nodes = PUB._mapNodes;
|
||||
PUB._mapCanvas.MapFileName = filePath.FullName;
|
||||
|
||||
|
||||
// 🔥 맵 설정 적용 (배경색, 그리드 표시)
|
||||
if (result.Settings != null)
|
||||
{
|
||||
@@ -120,6 +201,7 @@ namespace Project.ViewForm
|
||||
if (startNode != null)
|
||||
{
|
||||
PUB._virtualAGV = new VirtualAGV(PUB.setting.MCID, startNode.Position, AgvDirection.Forward);
|
||||
PUB._virtualAGV.LowBatteryThreshold = PUB.setting.BatteryLimit_Low;
|
||||
PUB._virtualAGV.SetPosition(startNode, AgvDirection.Forward);
|
||||
|
||||
// 캔버스에 AGV 리스트 설정
|
||||
@@ -131,6 +213,7 @@ namespace Project.ViewForm
|
||||
}
|
||||
else if (PUB._virtualAGV != null)
|
||||
{
|
||||
PUB._virtualAGV.LowBatteryThreshold = PUB.setting.BatteryLimit_Low;
|
||||
// 기존 AGV가 있으면 캔버스에 다시 연결
|
||||
var agvList = new System.Collections.Generic.List<AGVNavigationCore.Controls.IAGV> { PUB._virtualAGV };
|
||||
PUB._mapCanvas.AGVList = agvList;
|
||||
|
||||
@@ -85,6 +85,12 @@ namespace Project.ViewForm
|
||||
if (radpbs0.Checked) ss = arDev.Narumi.Sensor.PBSOn;
|
||||
PUB.AGV.AGVMoveManual(arDev.Narumi.ManulOpt.BS, spd, ss);
|
||||
PUB.sm.SetNewStep(StateMachine.eSMStep.IDLE);
|
||||
|
||||
// [Manual Safety] Clear previous auto-task state
|
||||
PUB._virtualAGV.TargetNode = null;
|
||||
PUB._virtualAGV.CurrentPath = null;
|
||||
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop; // Clear ACS Command
|
||||
PUB.sm.ClearRunStep(); // Clear RunStep sequence
|
||||
}
|
||||
|
||||
private void arLabel2_Click(object sender, EventArgs e)
|
||||
@@ -108,6 +114,12 @@ namespace Project.ViewForm
|
||||
if (radpbs0.Checked) ss = arDev.Narumi.Sensor.PBSOn;
|
||||
PUB.AGV.AGVMoveManual(arDev.Narumi.ManulOpt.FS, spd, ss);
|
||||
PUB.sm.SetNewStep(StateMachine.eSMStep.IDLE);
|
||||
|
||||
// [Manual Safety] Clear previous auto-task state
|
||||
PUB._virtualAGV.TargetNode = null;
|
||||
PUB._virtualAGV.CurrentPath = null;
|
||||
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
|
||||
PUB.sm.ClearRunStep();
|
||||
}
|
||||
|
||||
private void arLabel3_Click(object sender, EventArgs e)
|
||||
@@ -131,6 +143,12 @@ namespace Project.ViewForm
|
||||
if (radpbs0.Checked) ss = arDev.Narumi.Sensor.PBSOn;
|
||||
PUB.AGV.AGVMoveManual(arDev.Narumi.ManulOpt.RT, spd, ss);
|
||||
PUB.sm.SetNewStep(StateMachine.eSMStep.IDLE);
|
||||
|
||||
// [Manual Safety] Clear previous auto-task state
|
||||
PUB._virtualAGV.TargetNode = null;
|
||||
PUB._virtualAGV.CurrentPath = null;
|
||||
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
|
||||
PUB.sm.ClearRunStep();
|
||||
}
|
||||
|
||||
private void arLabel4_Click(object sender, EventArgs e)
|
||||
@@ -154,6 +172,12 @@ namespace Project.ViewForm
|
||||
if (radpbs0.Checked) ss = arDev.Narumi.Sensor.PBSOn;
|
||||
PUB.AGV.AGVMoveManual(arDev.Narumi.ManulOpt.LT, spd, ss);
|
||||
PUB.sm.SetNewStep(StateMachine.eSMStep.IDLE);
|
||||
|
||||
// [Manual Safety] Clear previous auto-task state
|
||||
PUB._virtualAGV.TargetNode = null;
|
||||
PUB._virtualAGV.CurrentPath = null;
|
||||
PUB.NextWorkCmd = ENIGProtocol.AGVCommandHE.Stop;
|
||||
PUB.sm.ClearRunStep();
|
||||
}
|
||||
|
||||
private void arLabel11_Click(object sender, EventArgs e)
|
||||
|
||||
17
Cs_HMI/Project/fSetup.Designer.cs
generated
17
Cs_HMI/Project/fSetup.Designer.cs
generated
@@ -103,7 +103,6 @@
|
||||
this.valIntervalBMS = new AGVControl.ValueSelect();
|
||||
this.valIntervalXBE = new AGVControl.ValueSelect();
|
||||
this.tabPage3 = new System.Windows.Forms.TabPage();
|
||||
this.cmbChargerPos = new System.Windows.Forms.ComboBox();
|
||||
this.label58 = new System.Windows.Forms.Label();
|
||||
this.label19 = new System.Windows.Forms.Label();
|
||||
this.label44 = new System.Windows.Forms.Label();
|
||||
@@ -1562,7 +1561,6 @@
|
||||
// tabPage3
|
||||
//
|
||||
this.tabPage3.BackColor = System.Drawing.Color.DarkSlateBlue;
|
||||
this.tabPage3.Controls.Add(this.cmbChargerPos);
|
||||
this.tabPage3.Controls.Add(this.label58);
|
||||
this.tabPage3.Controls.Add(this.label19);
|
||||
this.tabPage3.Controls.Add(this.label44);
|
||||
@@ -1600,20 +1598,6 @@
|
||||
this.tabPage3.TabIndex = 4;
|
||||
this.tabPage3.Text = "충전";
|
||||
//
|
||||
// cmbChargerPos
|
||||
//
|
||||
this.cmbChargerPos.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.cmbChargerPos.Font = new System.Drawing.Font("맑은 고딕", 20F, System.Drawing.FontStyle.Bold);
|
||||
this.cmbChargerPos.FormattingEnabled = true;
|
||||
this.cmbChargerPos.Items.AddRange(new object[] {
|
||||
"HOME 아래",
|
||||
"HOME",
|
||||
"HOME 위"});
|
||||
this.cmbChargerPos.Location = new System.Drawing.Point(1004, 11);
|
||||
this.cmbChargerPos.Name = "cmbChargerPos";
|
||||
this.cmbChargerPos.Size = new System.Drawing.Size(217, 45);
|
||||
this.cmbChargerPos.TabIndex = 69;
|
||||
//
|
||||
// label58
|
||||
//
|
||||
this.label58.AutoSize = true;
|
||||
@@ -3611,7 +3595,6 @@
|
||||
private AGVControl.ValueSelect vcTagF5B;
|
||||
private AGVControl.ValueSelect vcTagF4F5;
|
||||
private System.Windows.Forms.Label label58;
|
||||
private System.Windows.Forms.ComboBox cmbChargerPos;
|
||||
private System.Windows.Forms.CheckBox chkClearPos;
|
||||
private System.Windows.Forms.Label label7;
|
||||
private AGVControl.ValueSelect vcXBID;
|
||||
|
||||
@@ -52,26 +52,19 @@ namespace Project
|
||||
{
|
||||
|
||||
//통신값 표시
|
||||
//tbPortBMS.Text = Pub.setting.Port_BMS;
|
||||
///tbPortPLC.Text = PUB.setting.Port_PLC;
|
||||
tbPortAGV.Text = PUB.setting.Port_AGV;
|
||||
tbPortXBE.Text = PUB.setting.Port_XBE;
|
||||
tbportBMS.Text = PUB.setting.Port_BAT;
|
||||
|
||||
// tbBaudPLC.Text = PUB.setting.Baud_PLC.ToString();
|
||||
tbBaudAGV.Text = PUB.setting.Baud_AGV.ToString();
|
||||
tbBaudXBE.Text = PUB.setting.Baud_XBE.ToString();
|
||||
tbBaudBAT.Text = PUB.setting.Baud_BAT.ToString();
|
||||
|
||||
//valueSelect1.Value = Pub.setting.interval_bms;
|
||||
valIntervalXBE.Value = PUB.setting.interval_xbe;
|
||||
vcpidDS.Value = PUB.setting.ZSpeed;
|
||||
//valueSelect4.Value = PUB.setting.interval_iostate;
|
||||
valIntervalBMS.Value = PUB.setting.interval_bms;
|
||||
tbChargerID.Value = PUB.setting.ChargerID;
|
||||
|
||||
cmbChargerPos.SelectedIndex = PUB.setting.chargerpos;
|
||||
|
||||
vcSCK.Value = PUB.setting.SCK;
|
||||
vcSSK.Value = PUB.setting.SSK;
|
||||
vcSTT.Value = PUB.setting.STT;
|
||||
@@ -218,28 +211,19 @@ namespace Project
|
||||
propertyGrid1.Invalidate();
|
||||
|
||||
//통신정보저장
|
||||
// Pub.setting.Port_BMS = tbPortBMS.Text;
|
||||
PUB.setting.Port_XBE = tbPortXBE.Text;
|
||||
//PUB.setting.Port_PLC = tbPortPLC.Text;
|
||||
PUB.setting.Port_AGV = tbPortAGV.Text;
|
||||
PUB.setting.Port_BAT = tbportBMS.Text;
|
||||
|
||||
// Pub.setting.Baud_bms = int.Parse(tbBaudBms.Text);
|
||||
PUB.setting.Baud_XBE = int.Parse(tbBaudXBE.Text);
|
||||
//PUB.setting.Baud_PLC = int.Parse(tbBaudPLC.Text);
|
||||
PUB.setting.Baud_AGV = int.Parse(tbBaudAGV.Text);
|
||||
PUB.setting.Baud_BAT = int.Parse(tbBaudBAT.Text);
|
||||
|
||||
PUB.setting.ChargerID = (int)(tbChargerID.Value);
|
||||
|
||||
//Pub.setting.interval_bms = (float)valueSelect1.Value;
|
||||
PUB.setting.interval_bms = (int)valIntervalBMS.Value;
|
||||
PUB.setting.interval_xbe = (float)valIntervalXBE.Value;
|
||||
PUB.setting.ZSpeed = (byte)vcpidDS.Value;
|
||||
//PUB.setting.interval_iostate = (byte)valueSelect4.Value;
|
||||
PUB.setting.chargerpos = this.cmbChargerPos.SelectedIndex;
|
||||
PUB.setting.AutoModeOffAndClearPosition = this.chkClearPos.Checked;
|
||||
//PUB.setting.MotorUpTime = (byte)valueSelect5.Value;
|
||||
|
||||
//AGV정보 저장
|
||||
PUB.setting.Enable_Speak = this.btSpeaker.ProgressValue != 0;
|
||||
@@ -294,7 +278,6 @@ namespace Project
|
||||
|
||||
//시스템옵션
|
||||
PUB.setting.ChargeMaxTime = (int)vcChargeMaxTime.Value;
|
||||
//Pub.setting.ChargeIdleInterval = (int)nudChargeIdleInterval.Value;
|
||||
PUB.setting.ChargeRetryTerm = (int)vcChargeRetryTerm.Value;
|
||||
|
||||
//초기화시간
|
||||
|
||||
Reference in New Issue
Block a user