diff --git a/Doc/ILSOSConfig/PLC_DeviceMap_Bit_Input_M_Device.csv b/Doc/ILSOSConfig/PLC_DeviceMap_Bit_Input_M_Device.csv index fffea2c..f2e8683 100644 --- a/Doc/ILSOSConfig/PLC_DeviceMap_Bit_Input_M_Device.csv +++ b/Doc/ILSOSConfig/PLC_DeviceMap_Bit_Input_M_Device.csv @@ -8,7 +8,7 @@ M70505,Heavy_ALStatus,Heavy_ALStatus M70506,Door Interlok,Door Interlok M70507,EMO Status,EMO Status M70508,Page_CHGRequest,Page_CHGRequest -M70509,Recipe_CHGRequest,Recipe_CHGRequest +M70509,Scout Enable,Scout Enable(상시 ON 시 용접 전/후 Scout) M70510,, M70511,, M70512,, diff --git a/Machines/ILSOS_PrismaticRD_PTOP/Device/PLC/PLCController.Address.cs b/Machines/ILSOS_PrismaticRD_PTOP/Device/PLC/PLCController.Address.cs index 55958fe..f0d4fb1 100644 --- a/Machines/ILSOS_PrismaticRD_PTOP/Device/PLC/PLCController.Address.cs +++ b/Machines/ILSOS_PrismaticRD_PTOP/Device/PLC/PLCController.Address.cs @@ -22,8 +22,9 @@ namespace ILSOS_PrismaticRD_PTOP.Device PLC_EMO_STATUS = 0x07, PLC_PAGE_CHANGE_REQ = 0x08, - // 260618: M70509 Recipe 변경 요청 (구 PLC_RMS_USED, RMS 미사용으로 폐기 후 재정의) - PLC_RECIPE_CHANGE_REQ = 0x09, + // 260622: M70509 = Scout Enable 플래그 (PLC가 상시 ON 유지 시 용접 전/후 Scout 수행) + // 구 PLC_RECIPE_CHANGE_REQ/RMS_USED 모두 미사용 → 재정의. Recipe 전환은 PAGE_CHANGE_REQ + RECIPE_NO 워드로 처리. + PLC_SCOUT_ENABLE = 0x09, // 0x0A ~ 0x0E // 260621: M70515 확정 = Weld_01 Ready (가변용접 시 PLC ON → Recipe/Page 확인 팝업 + Resume 트리거) PLC_WELD_READY_REQ = 0x0F, diff --git a/Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPTOP.cs b/Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPTOP.cs index eabd7b6..ffebc17 100644 --- a/Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPTOP.cs +++ b/Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPTOP.cs @@ -76,6 +76,23 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences const string SEQ_WRITE_COMPLETION_PLC_BIT = "[SeqAutoRunPTOP] Write Completion PLC Bit"; const string SEQ_COMPLETE = "[SeqAutoRunPTOP] SEQ_COMPLETE"; // 완료 + #region 260622: M70509(Scout Enable) 기반 용접 전/후 Scout + // 용접 전: M70516(Weld Trigger) 감지 후 M70509 ON이면 scout(pre) → 0.1s → 용접 + const string SEQ_PRE_SCOUT_CHECK = "[SeqAutoRunPTOP] Pre-Weld Scout Check"; // 분기: M70509 ON/OFF + const string SEQ_PRE_SCOUT_SET_PARAMS = "[SeqAutoRunPTOP] Pre-Weld Scout SetParams"; + const string SEQ_PRE_SCOUT_RUN = "[SeqAutoRunPTOP] Pre-Weld Scout Run"; // SeqScoutNormal 서브시퀀스 + const string SEQ_PRE_SCOUT_DONE = "[SeqAutoRunPTOP] Pre-Weld Scout Done"; // 0.1s 후 진행 + // 용접 후: 발진 완료 후 완료비트 송신 전 M70509 ON이면 scout(post) → 0.1s → 완료비트 + const string SEQ_POST_SCOUT_CHECK = "[SeqAutoRunPTOP] Post-Weld Scout Check"; + const string SEQ_POST_SCOUT_SET_PARAMS = "[SeqAutoRunPTOP] Post-Weld Scout SetParams"; + const string SEQ_POST_SCOUT_RUN = "[SeqAutoRunPTOP] Post-Weld Scout Run"; + const string SEQ_POST_SCOUT_DONE = "[SeqAutoRunPTOP] Post-Weld Scout Done"; // 0.1s 후 진행 + // 용접 완료 비트 송신(post-scout 분기 뒤로 분리): TriggerACK off / BUSY off / COMPLETE on + const string SEQ_WRITE_WELD_COMPLETE = "[SeqAutoRunPTOP] Write Weld Complete"; + + private const int SCOUT_PROCEED_DELAY_MS = 100; // scout 완료 후 진행 지연(0.1초) + #endregion + public enum ePrismaticDOAddress { CoreLaser_Control = 9, @@ -138,11 +155,26 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences sb.AddAction(SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ, GetUploadWeldingList_SeqResult); #endregion - // 260513 plc + // 260513 plc sb.AddAction(SEQ_CHECK_WELDING_START_READ_BIT, WeldingStartReadBit); + + // 260622: 용접 전 Scout (M70509 ON 시) + sb.AddDecision(SEQ_PRE_SCOUT_CHECK, PreScoutCheck); + sb.AddAction(SEQ_PRE_SCOUT_SET_PARAMS, PreScoutSetParams); + sb.AddSubsequence(SEQ_PRE_SCOUT_RUN, SequenceManager.GetSeqBuilder_SeqScoutNormal()); + sb.AddAction(SEQ_PRE_SCOUT_DONE, PreScoutDone); + sb.AddAction(SEQ_WAIT_WELDING_START, WaitWeldingStart); sb.AddAction(SEQ_EXECUTE_WELDING, ExecuteWelding); - sb.AddDecision(SEQ_CHECK_CYCLE_STOP, CheckCycleStop); // 체크페이지 + + // 260622: 용접 후 Scout (M70509 ON 시) → 완료비트 송신 + sb.AddDecision(SEQ_POST_SCOUT_CHECK, PostScoutCheck); + sb.AddAction(SEQ_POST_SCOUT_SET_PARAMS, PostScoutSetParams); + sb.AddSubsequence(SEQ_POST_SCOUT_RUN, SequenceManager.GetSeqBuilder_SeqScoutNormal()); + sb.AddAction(SEQ_POST_SCOUT_DONE, PostScoutDone); + sb.AddAction(SEQ_WRITE_WELD_COMPLETE, WriteWeldComplete); + + sb.AddDecision(SEQ_CHECK_CYCLE_STOP, CheckCycleStop); // 체크페이지 sb.AddAction(SEQ_SAVE_RESULT_DATA, SaveResultData); // 해당 공정의 정보를 기록 함. sb.AddAction(SEQ_WRITE_COMPLETION_PLC_BIT, WriteCompletionPLCBit); // 정상적으로 종료 되면, complete bit write @@ -161,16 +193,33 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences > sb[SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ] // 2 리스트 업로드 (서브시퀀스) - TeachingRun 노드 참고 > sb[SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ] // 3 업로드 결과 확인 - TeachingRun 노드 참고 #endregion - > sb[SEQ_CHECK_WELDING_START_READ_BIT] // while read bit + > sb[SEQ_CHECK_WELDING_START_READ_BIT] // while read bit (M70516 대기) + + > sb[SEQ_PRE_SCOUT_CHECK] // 260622: M70509 ON → pre scout + > sb[SEQ_PRE_SCOUT_SET_PARAMS] + > sb[SEQ_PRE_SCOUT_RUN] + > sb[SEQ_PRE_SCOUT_DONE] // 0.1s 후 용접 + > sb[SEQ_WAIT_WELDING_START] // 4 업로드 완료 PLC 완료 신호 및 BUSY 신호로 변경 - > sb[SEQ_EXECUTE_WELDING] // 5 Scanner 실행 (실제 용접) + > sb[SEQ_EXECUTE_WELDING] // 5 Scanner 실행 (실제 용접, 완료비트는 보류) + + > sb[SEQ_POST_SCOUT_CHECK] // 260622: M70509 ON → post scout + > sb[SEQ_POST_SCOUT_SET_PARAMS] + > sb[SEQ_POST_SCOUT_RUN] + > sb[SEQ_POST_SCOUT_DONE] // 0.1s 후 완료비트 + > sb[SEQ_WRITE_WELD_COMPLETE] // 용접 완료 비트 송신 → 다음 시퀀스 + > sb[SEQ_CHECK_CYCLE_STOP] > sb[SEQ_SAVE_RESULT_DATA] - > sb[SEQ_WRITE_COMPLETION_PLC_BIT] // PLC 비트 완료 신호 보내기 - > sb[SEQ_COMPLETE] // 다시 enter로 올리기 + > sb[SEQ_WRITE_COMPLETION_PLC_BIT] // PLC 비트 완료 신호 보내기 + > sb[SEQ_COMPLETE] // 다시 enter로 올리기 > sb.Final); + // 260622: Scout 스킵 분기 (M70509 OFF → 기존과 동일하게 용접만) + sb.Flows(sb[SEQ_PRE_SCOUT_CHECK] > sb[SEQ_WAIT_WELDING_START]); + sb.Flows(sb[SEQ_POST_SCOUT_CHECK] > sb[SEQ_WRITE_WELD_COMPLETE]); + sb.Flows(sb[SEQ_CHECK_CYCLE_STOP] > sb[SEQ_CHECK_PAGENO]); // 루프백: 매 사이클 Page Change bit(0x08) 대기부터 재확인 sb.Flows(sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ]); // 260621: Resume 시 핸드셰이크 스킵 sb.Flows(sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_COMPLETE] > sb.Final); @@ -787,21 +836,10 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences Log.D(string.Format($"[ExecuteWelding] Change SelectCorrectionTable Time : {_WeldingSpanTime.TotalMilliseconds}")); Thread.Sleep(500); - // 양극(P) 미사용 → 모든 페이지 음극 완료 비트로 응답 - _PLC.WriteBit((int)eBitOutput.PC_WELD_TRIGGER_ACK, false); - Log.I($"[ExecuteWelding] 용접완료 송신 - READY_ACK=false (PageNo:{plcPageNo})"); - Thread.Sleep(50); - _PLC.WriteBit((int)eBitOutput.PC_WELD_BUSY, false); - Log.I($"[ExecuteWelding] 용접완료 송신 - BUSY=false (PageNo:{plcPageNo})"); - Thread.Sleep(50); - _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE, true); - Log.I($"[ExecuteWelding] 용접완료 송신 - COMPLETE=true (PageNo:{plcPageNo})"); - Thread.Sleep(5); - - Status.SetWorkingPosition(eWorkingPosition.UNLOADING_CELL); #endregion - Log.I($"[ExecuteWelding] 완료 → CheckCycleStop 이동"); + // 260622: 완료비트(TriggerACK off/BUSY off/COMPLETE on) 송신은 post-scout(M70509) 분기 뒤로 분리 → WriteWeldComplete 노드 + Log.I($"[ExecuteWelding] 발진 완료 (완료비트 송신 보류) → PostScoutCheck 이동"); return SequenceAction.Ok(); } @@ -822,6 +860,91 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences return SEQ_CHECK_PAGENO; } + #region 260622: M70509(Scout Enable) 기반 용접 전/후 Scout + // 이미지 파일명 접미어: auto_{recipeNo}_{pageNo}_{pre|post} → 최종 {기존}_auto_{recipeNo}_{pageNo}_{phase}.PNG + private string ScoutSaveTag(string phase) => $"auto_{plcRecipeNo}_{plcPageNo}_{phase}"; + + private SeqScoutNormal_InitParam BuildScoutParams(string saveTag) + { + return new SeqScoutNormal_InitParam() + { + IsRealTimeScout = false, + ScoutSeqParams = new ScoutSeqParams(VisionRecipeManager.GetFovInfos()[0], eDisplayType.Machine_B), + PageIndex = _CurrentPageNo, // 현재 처리 중인 페이지(0-base) + SaveTag = saveTag, + }; + } + + // ── 용접 전 Scout (M70516 감지 후) ────────────────────────── + private string PreScoutCheck(SequenceContext context, string id, ICollection branches, SequenceActionResult param) + { + bool scout = _PLC.ReadBit(eBitInput.PLC_SCOUT_ENABLE).data; // M70509 + Log.I($"[PreScoutCheck] M70509(Scout Enable) = {scout}"); + // M70509 ON → pre scout / OFF → 기존과 동일하게 바로 용접 + return scout ? SEQ_PRE_SCOUT_SET_PARAMS : SEQ_WAIT_WELDING_START; + } + private SequenceActionResult PreScoutSetParams(SequenceContext context, string id, SequenceActionResult param) + { + Log.I("[PreScout] Scout 파라미터 구성(pre)"); + return SequenceAction.Ok(BuildScoutParams(ScoutSaveTag("pre"))); + } + private SequenceActionResult PreScoutDone(SequenceContext context, string id, SequenceActionResult param) + { + var result = param.Param as SeqScoutNormal_OutParam; + if (result == null || result.Success == false) + { + Log.E("[PreScout] Scout 실패 → 시퀀스 중단"); + return null; + } + Thread.Sleep(SCOUT_PROCEED_DELAY_MS); // 0.1초 후 용접 진행 + Log.I($"[PreScout] 완료 → {SCOUT_PROCEED_DELAY_MS}ms 후 WaitWeldingStart 이동"); + return SequenceAction.Ok(); + } + + // ── 용접 후 Scout (완료비트 송신 전) ──────────────────────── + private string PostScoutCheck(SequenceContext context, string id, ICollection branches, SequenceActionResult param) + { + bool scout = _PLC.ReadBit(eBitInput.PLC_SCOUT_ENABLE).data; // M70509 + Log.I($"[PostScoutCheck] M70509(Scout Enable) = {scout}"); + // M70509 ON → post scout / OFF → 바로 완료비트 송신 + return scout ? SEQ_POST_SCOUT_SET_PARAMS : SEQ_WRITE_WELD_COMPLETE; + } + private SequenceActionResult PostScoutSetParams(SequenceContext context, string id, SequenceActionResult param) + { + Log.I("[PostScout] Scout 파라미터 구성(post)"); + return SequenceAction.Ok(BuildScoutParams(ScoutSaveTag("post"))); + } + private SequenceActionResult PostScoutDone(SequenceContext context, string id, SequenceActionResult param) + { + var result = param.Param as SeqScoutNormal_OutParam; + if (result == null || result.Success == false) + { + Log.E("[PostScout] Scout 실패 → 시퀀스 중단"); + return null; + } + Thread.Sleep(SCOUT_PROCEED_DELAY_MS); // 0.1초 후 완료비트 송신 + Log.I($"[PostScout] 완료 → {SCOUT_PROCEED_DELAY_MS}ms 후 WriteWeldComplete 이동"); + return SequenceAction.Ok(); + } + + // 용접 완료 비트 송신(post-scout 분기 뒤로 분리). 음극 비트로 응답(양극 P 미사용). + private SequenceActionResult WriteWeldComplete(SequenceContext context, string id, SequenceActionResult param) + { + _PLC.WriteBit((int)eBitOutput.PC_WELD_TRIGGER_ACK, false); + Log.I($"[WriteWeldComplete] 용접완료 송신 - READY_ACK=false (PageNo:{plcPageNo})"); + Thread.Sleep(50); + _PLC.WriteBit((int)eBitOutput.PC_WELD_BUSY, false); + Log.I($"[WriteWeldComplete] 용접완료 송신 - BUSY=false (PageNo:{plcPageNo})"); + Thread.Sleep(50); + _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE, true); + Log.I($"[WriteWeldComplete] 용접완료 송신 - COMPLETE=true (PageNo:{plcPageNo})"); + Thread.Sleep(5); + Status.SetWorkingPosition(eWorkingPosition.UNLOADING_CELL); + Log.I($"[WriteWeldComplete] 완료 → CheckCycleStop 이동"); + return SequenceAction.Ok(); + } + #endregion + private SequenceActionResult SaveResultData(SequenceContext context, string id, SequenceActionResult param) { Log.I($"[SaveResultData] 용접 결과 데이터 저장 시작"); diff --git a/WeldingComponents/WeldingCommon/Sequences/Main/SeqScoutNormal.cs b/WeldingComponents/WeldingCommon/Sequences/Main/SeqScoutNormal.cs index e3208f5..b9ef73d 100644 --- a/WeldingComponents/WeldingCommon/Sequences/Main/SeqScoutNormal.cs +++ b/WeldingComponents/WeldingCommon/Sequences/Main/SeqScoutNormal.cs @@ -296,8 +296,14 @@ namespace WeldingCommon.Sequences //4.스캐너 필드 이미지 파일 저장 // TODO: Path 설정하는 것 따로 만들어서 넣기. 여기저기 흩어져 있음... + // 디렉터리는 기존 그대로(날짜 폴더). SaveTag 가 있으면 기존 파일명 뒤에 '_접미어' 추가. + // 기존: {_ScoutNo+1}_{HHmmss}.PNG + // SaveTag 적용: {_ScoutNo+1}_{HHmmss}_{SaveTag}.PNG (예: 1_143052_auto_11_2_pre.PNG) + string dateDir = $"{DateTime.Now.Year}_{DateTime.Now.Month}_{DateTime.Now.Day}"; + string baseName = $"{_ScoutNo + 1}_{DateTime.Now.ToString("HHmmss")}"; + string fileName = string.IsNullOrEmpty(_InitParam.SaveTag) ? $"{baseName}.PNG" : $"{baseName}_{_InitParam.SaveTag}.PNG"; string fieldScanImageFile = Path.Combine(ConfigurationManager.AppSettings.Get("DATA_PATH"), "_vision", "FieldScanImage", - string.Format($"{DateTime.Now.Year}_{DateTime.Now.Month}_{DateTime.Now.Day}/{_ScoutNo + 1}_{DateTime.Now.ToString("HHmmss")}.PNG")); + dateDir, fileName); _SystemParameter.ScannerFieldImageFile = fieldScanImageFile; //5.압축하여 저장 @@ -385,6 +391,10 @@ namespace WeldingCommon.Sequences // 기존: SeqScoutNormal 이 MainPage.selectedIndexNo(GUI 전역 상태)에만 의존 → 페이지 이동/자동운전 시 stale. // 변경: 호출부가 "현재 처리/표시 중인 페이지 번호"를 명시 주입. public int PageIndex { get; set; } = 0; + + // [추가 2026-06-22] 파일명 접미어. 디렉터리는 기존 날짜 폴더 그대로, 비면(기본) 기존 파일명 유지(하위호환). + // 적용 시: {기존파일명}_{SaveTag}.PNG (호출부에서 예: "auto_{recipeNo}_{pageNo}_pre" 전달) + public string SaveTag { get; set; } = ""; } public class SeqScoutNormal_OutParam : OutParam