using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using Project.StateMachine; using COMM; using AR; namespace Project { public partial class fMain { DateTime LastSpeakTime = DateTime.Now; DateTime CoverControlTime = DateTime.Now; DateTime LastCommandTime = DateTime.Now; DateTime LastCommandTimeNextStop = DateTime.Now; bool runStepisFirst = false; private void _SM_RUN(Boolean isFirst, TimeSpan stepTime) { //HW 연결오류 if (PUB.AGV.IsOpen == false) { PUB.Result.SetResultMessage(eResult.Hardware, eECode.AGVCONN, eNextStep.ERROR); PUB.sm.SetNewStep(eSMStep.IDLE); return; } //가동불가 조건 확인 if (CheckStopCondition() == false) return; //중단기능이 동작이라면 처리하지 않는다. if (PUB.sm.bPause) { System.Threading.Thread.Sleep(200); return; } //스텝이 변경되었다면? if (PUB.sm.RunStep != PUB.sm.RunStepNew) { runStepisFirst = true; PUB.sm.ApplyRunStep(); } else runStepisFirst = false; //처음시작이라면 시작시간을 설정한다 if (isFirst) { if (PUB.sm.RunStep == ERunStep.READY) VAR.TIME.Update(eVarTime.ReadyStart); else VAR.TIME.Update(eVarTime.RunStart); } //자동모드에서 대기상태 (추가동작없음) if (PUB.sm.RunStep == ERunStep.READY) { _SM_RUN_READY(runStepisFirst, PUB.sm.GetRunSteptime); return; } //############################################# //## 이 후에는 모든 동작루틴이 온다 //############################################# //라이더 센서에서 멈춘경우 처리 if (PUB.AGV.system1.stop_by_front_detect == true) { var tsSpeak = DateTime.Now - LastSpeakTime; if (tsSpeak.TotalSeconds >= PUB.setting.alarmSoundTerm) { PUB.Speak(Lang.전방에물체가감지되었습니다); LastSpeakTime = DateTime.Now; } return; } //선로이탈감지 if (PUB.AGV.error.runerror_by_no_magent_line == true) { var tsSpeak = DateTime.Now - LastSpeakTime; if (tsSpeak.TotalSeconds >= PUB.setting.alarmSoundTerm) { PUB.Speak(Lang.선로를이탈했습니다); LastSpeakTime = DateTime.Now; } return; } //현재위치를 모르는 상태라면 이동하여 현재 위치를 찾는다 if (_SM_RUN_POSCHK(isFirst, stepTime) == false) return; //나머지 상황체크 switch (PUB.sm.RunStep) { case ERunStep.GOHOME: if (_SM_RUN_GOHOME(runStepisFirst, PUB.sm.GetRunSteptime) == true) { PUB.log.Add($"홈 이동이 완료되어 준비상태로 전환합니다"); PUB.sm.SetNewRunStep(ERunStep.READY); } break; case ERunStep.GOTO: //목적지까지 이동하는 경우 if (_SM_RUN_GOTO(runStepisFirst, PUB.sm.GetRunSteptime) == true) { //목적지가 BUFFER라면 버퍼투입대기위치까지 완료했다는 시그널을 보낸다. var target = PUB._virtualAGV.TargetNode; PUB.log.Add($"목적지({target.RfidId}) 도착완료 타입:{target.Type}, 출발지:{PUB._virtualAGV.StartNode.RfidId}"); switch (target.StationType) { case AGVNavigationCore.Models.StationType.Buffer: //현재위치가 마지막경로의 NODEID와 일치해야한다 if (PUB._virtualAGV.CurrentPath == null) { PUB.log.AddAT("목적지 버퍼이동완료 했지만 상세경로가 없습니다"); PUB.XBE.BufferInComplete = false; PUB.XBE.BufferOutComplete = false; break; } var lastPath = PUB._virtualAGV.CurrentPath.DetailedPath.LastOrDefault(); if (lastPath.NodeId.Equals(PUB._virtualAGV.CurrentNode.Id)) { //버퍼진입전 노드에 도착완료했따 PUB.XBE.BufferInReady = true; PUB.XBE.BufferReadyError = false; } else { //마지막위치가 아닌 다른 위치에 있으니 버퍼 작업을 할 수없다 PUB.log.AddAT("목적지 버퍼이동완료 했지만 마지막 노드가 아닙니다"); PUB.XBE.BufferInReady = false; PUB.XBE.BufferReadyError = true; } PUB.XBE.BufferInComplete = false; PUB.XBE.BufferOutComplete = false; break; } PUB._virtualAGV.Turn = AGVNavigationCore.Models.AGVTurn.None; PUB.sm.SetNewRunStep(ERunStep.READY); } break; case ERunStep.MARKSTOPB: //후진방향으로 마크스탑 case ERunStep.MARKSTOPF: //전진방향으로 마크스탑 //이동중이지 않다면 먼저 이동을 진행한다 var agvDir = PUB.sm.RunStep == ERunStep.MARKSTOPF ? arDev.Narumi.eRunOpt.Forward : arDev.Narumi.eRunOpt.Backward; PUB.AGV.AGVMoveRun(agvDir); //이동중이라면 마크스탑을 입력한다 PUB.AGV.AGVMoveStop("run-markstropb", arDev.Narumi.eStopOpt.MarkStop); //마크스탑신호를 확인한다.(최대 5초) //신호가 확인되지 않으면 오류로 정지한다 break; case ERunStep.GOCHARGE: //충전위치로 이동 if (runStepisFirst) { VAR.TIME[eVarTime.ChargeTry] = DateTime.Now; PUB.sm.ResetRunStepSeq(); PUB.log.Add("충전 명령 시작"); } else if (_SM_RUN_GOCHARGE(runStepisFirst, PUB.sm.GetRunSteptime)) { //230601 //if (PUB.Result != null && PUB.sm != null) // EEMStatus.AddEEDBSQL(PUB.sm.Step, PUB.sm.RunStep.ToString(), PUB.Result.TargetPos.ToString()); PUB.Speak(Lang.충전을시작합니다); PUB.sm.SetNewRunStep(ERunStep.CHARGECHECK); return; } break; case ERunStep.CHARGECHECK: //충전중 if (runStepisFirst) { VAR.TIME.Update(eVarTime.ChargeStart); LastCommandTime = DateTime.Now; } else if (_SM_RUN_GOCHARGECHECK(runStepisFirst, PUB.sm.GetRunSteptime)) { //충전상태가 활성화되었으므로 대기상태로 전환한다 PUB.sm.SetNewRunStep(ERunStep.READY); PUB.log.AddAT("충전상태 확인 완료로 인해 대기 합니다"); } break; case ERunStep.CHARGEOFF: if (runStepisFirst) { VAR.TIME.Update(eVarTime.ChargeEnd); LastCommandTime = DateTime.Now; } else { //충전 상태가 OFF되어야 동작하게한다 if (_SM_RUN_CHGOFF(isFirst, stepTime) == true) { //충전상태가 활성화되었으므로 대기상태로 전환한다 PUB.sm.ClearRunStep(); //충전기위치에서 OFF를 한 경우이다 if (PUB.Result.CurrentPos != ePosition.CHARGE || PUB.Result.TargetPos != ePosition.CHARGE) { PUB.Result.CurrentPos = ePosition.NONE; } else { PUB.Result.TargetPos = ePosition.QC; } PUB.Result.CurrentPosCW = "1"; PUB.sm.SetNewRunStep(ERunStep.READY); PUB.log.AddAT("충전 해제로 대기상태로 전환 합니다"); } } break; case ERunStep.LOADER_OUT: //로더아웃 if (_SM_RUN_LOADER_OUT(runStepisFirst, PUB.sm.GetRunSteptime)) { PUB.Speak(Lang.버퍼도킹해제완료); //도킹완료상태를 업데이트한다. PUB.XBE.LoaderInComplete = false; PUB.XBE.LoaderOutComplete = true; //대기상태로 전환 PUB.sm.SetNewRunStep(ERunStep.READY); return; } break; case ERunStep.LOADER_IN: //로더도킹 if (_SM_RUN_LOADER_IN(runStepisFirst, PUB.sm.GetRunSteptime)) { PUB.Speak(Lang.버퍼도킹이완료되었습니다); //도킹완료상태를 업데이트한다. PUB.XBE.LoaderInComplete = true; //로더아웃으로 자동 진행하지 않음 (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; case ERunStep.UNLOADER_OUT: //언로더아웃 if (_SM_RUN_UNLOADER_OUT(runStepisFirst, PUB.sm.GetRunSteptime)) { PUB.Speak(Lang.버퍼도킹해제완료); //도킹완료상태를 업데이트한다. PUB.XBE.UnloaderInComplete = false; PUB.XBE.UnloaderOutComplete = true; //대기상태로 전환 PUB.sm.SetNewRunStep(ERunStep.READY); return; } break; case ERunStep.UNLOADER_IN: //언로더도킹 if (_SM_RUN_UNLOADER_IN(runStepisFirst, PUB.sm.GetRunSteptime)) { PUB.Speak(Lang.버퍼도킹이완료되었습니다); //도킹완료상태를 업데이트한다. PUB.XBE.UnloaderInComplete = true; //언로더아웃으로 자동 진행하지 않음 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; case ERunStep.CLEANER_OUT: //클리너아웃 if (_SM_RUN_CLEANER_OUT(runStepisFirst, PUB.sm.GetRunSteptime)) { PUB.Speak(Lang.버퍼도킹해제완료); //도킹완료상태를 업데이트한다. PUB.XBE.CleanerInComplete = false; PUB.XBE.CleanerOutComplete = true; //대기상태로 전환 PUB.sm.SetNewRunStep(ERunStep.READY); return; } break; case ERunStep.CLEANER_IN: //클리너도킹 if (_SM_RUN_CLEANER_IN(runStepisFirst, PUB.sm.GetRunSteptime)) { PUB.Speak(Lang.버퍼도킹이완료되었습니다); //도킹완료상태를 업데이트한다. PUB.XBE.CleanerInComplete = true; //클리너아웃으로 자동 진행하지 않음 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; case ERunStep.BUFFER_OUT: //버퍼아웃 if (_SM_RUN_BUFFER_OUT(runStepisFirst, PUB.sm.GetRunSteptime)) { PUB.Speak(Lang.버퍼도킹해제완료); //도킹완료상태를 업데이트한다. PUB.XBE.BufferInComplete = false; PUB.XBE.BufferOutComplete = true; //대기상태로 전환 PUB.sm.SetNewRunStep(ERunStep.READY); return; } break; case ERunStep.BUFFER_IN: //버퍼도킹 if (_SM_RUN_BUFFER_IN(runStepisFirst, PUB.sm.GetRunSteptime)) { PUB.Speak(Lang.버퍼도킹이완료되었습니다); //도킹완료상태를 업데이트한다. PUB.XBE.BufferInComplete = true; //버퍼아웃으로 자동 진행하지 않음 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; } } }//cvass }