using Aurora; using Aurora.Api; using Aurora.Api.Devices; using Aurora.Api.Services; using Aurora.View; using AuroraACS; using AuroraIO; using AuroraLaser; using AuroraLaser.Ekspla; using AuroraLaser.Trumpf; using AuroraLaserScanner; using AuroraLBAD; using AuroraLBAD.Services; using AuroraOCT; using CoreUnit.Control.UserControl.Canvas; using CoreUnit.Scanner.UserControl.ScannerCanvas; using DB; using GUI; using Hermes; using ILSOS_PrismaticScanner.Device; using ILSOS_PrismaticScanner.Device.IO; using Matrox.MatroxImagingLibrary; using OpenCvSharp.Dnn; using System; using System.Collections.Generic; using System.Configuration; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.Remoting.Contexts; using System.Text; using System.Threading; using System.Threading.Tasks; using VisionCommon; using WeldingCommon; using WeldingCommon.Alarm; using WeldingCommon.Data; using WeldingCommon.Devices.ACS; // 26.04.06 using WeldingCommon.Sequences; using WeldingCommon.Settings; using WeldingCommon.Utils; using WeldingCommon.Views; using static AuroraIO.AJINEXTEK.AjinIO.IOModuleInfo; using static AuroraIO.Vendor.PAIX.PAIXIO; using static CoreUnit.Scanner.UserControl.ScannerCanvas.ScannerParam; using static ILSOS_PrismaticScanner.Device.PLCControllerPrismaticScanner; namespace ILSOS_PrismaticScanner.Sequences { public class SeqComponentTeachingRun { static readonly Logger Log = new Logger("SeqComponentTeachingRun"); const string SEQ_ENTER = "[SeqComponentTeachingRun] SEQ_ENTER"; // inputParam 으로 받자. const string SEQ_CHECK_INIT_CONDITION = "[SeqComponentTeachingRun] Check Init Condition"; // Check Init Condition const string SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ = "[SeqComponentTeachingRun] Set Sequence Param (UploadWeldingList Seq)"; const string SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ = "[SeqComponentTeachingRun] Process (UploadWeldingList Seq)"; const string SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ = "[SeqComponentTeachingRun] Get Sequence Result (UploadWeldingList Seq)"; const string SEQ_SET_BLOW_ON = "[SeqComponentTeachingRun] Set Blow On"; const string SEQ_SET_BLOW_OFF = "[SeqComponentTeachingRun] Set Blow Off"; const string SEQ_SAVE_RESULT_DATA = "[SeqComponentTeachingRun] Save Result Data"; const string SEQ_COMPLETE = "[SeqComponentTeachingRun] SEQ_COMPLETE"; // 완료 const string SEQ_UPDATE_COMPONENT_WELDING_PARAMETERS = "[SeqComponentTeachingRun] Update Component Welding Parameters"; const string SEQ_START_WELDING = "[SeqComponentTeachingRun] Start All Welding"; const string SEQ_DO_COMPONENT_WELDING = "[SeqComponentTeachingRun] Do Component Welding"; const string SEQ_CHECK_END_LIST = "[SeqComponentTeachingRun] Check End List"; const string SEQ_END_WELDING = "[SeqComponentTeachingRun] End All Welding"; #region 260408 NDY 노드 추가 // Builder()에 노드 추가 //const string SEQ_MOVE_STAGE_ORIGIN = "[SeqComponentTeachingRun] Move Stage To Origin"; //const string SEQ_ENCODER_RESET = "[SeqComponentTeachingRun] Encoder Reset"; #endregion const int timeoutSeconds = 10; static class ContextKey { public const string KEY_SEQUENCE_RESULT = "KEY_SEQUENCE_RESULT_SeqComponentTeachingRun"; public const string KEY_SEQUENCE_START_TIME = "KEY_SEQUENCE_START_TIME_SeqComponentTeachingRun"; } private SeqTeachingRun_InitParam _InitParam; private static Scanner _Scanner; private static PAIX_PrismaticScanner _IO; // 필드 선언 추가 (다른 필드들 옆에) // 변경 전 //private ScannerACS _ACS; // 변경 후 private IScannerACS _ACS; // 26.04.06 NDY - MOTF용 ACS Stage X 제어 private static ACSMotor _AxisX; // 26.04.08 NDY - MOTF용 Stage X 축 private Laser _Laser; private ILaser _LaserDevice; private LaserTruFiber _LaserTruFiber; private LaserPowerCalParam _LaserPowerCalParam; private PLCControllerPrismaticScanner _plc; private SystemParameter _SystemParameter; private ScannerCanvasUserCtrl _ScannerCanvasUserCtrl; //스캐너 동작관련 객체 선언 private static SequenceSetting _SequenceSetting; private eListNo _WeldingScannerListNo; private List _SelectedObjectList; private Queue> _DivideObjectsQueue; private List _TargetObjects; private static DateTime _SequenceCompleteTime; // 시퀸스 시간 private static TimeSpan _MeasurementSpanTime; //측정 시간 private static TimeSpan _ScannerSaveLogTime; private static DateTime _partialWeldingStartTime; private static List _FlattenRecipes; private static int _CountWeldingObject; // 트럼프 레시피에서 부분 용접 할 Ojbect의 개수 private static int _CurrentobjectIndex; private static int _BeforeObjectIndex; private static int _BeforeProgramNo; // 이전 트럼프 레이저 용접 번호 private static int _BeforeParameterNo; // private static int _CenterBeamPower; private static int _RingBeamPower; private static bool _LaserPowerControlByRatio = false; private static int _CenterRingRatio; // Center Ring의 파워 비율 private static double _LaserPowerRatio; // Laser에서 출력 파워 비율 public SequenceBuilder Builder() { var sb = new SequenceBuilder(1000, true); ////var sb = new SequenceBuilder(1000, false); // With log sb.AddAction(SEQ_ENTER, Sequence_Enter); sb.AddAction(SEQ_CHECK_INIT_CONDITION, CheckInitCondition); //분기 sb.AddAction(SEQ_SET_BLOW_ON, SetBlowOn); #region 26.04.08 추가된 노드 NDY // 26.04.08 필드 좌표 0,0으로 스테이지 이동 시퀸스 + EncoderReset 호출 //sb.AddAction(SEQ_MOVE_STAGE_ORIGIN, MoveStageToOrigin); //sb.AddAction(SEQ_ENCODER_RESET, EncoderReset); #endregion sb.AddAction(SEQ_START_WELDING, StartWelding); sb.AddDecision(SEQ_CHECK_END_LIST, CheckEndList); sb.AddAction(SEQ_UPDATE_COMPONENT_WELDING_PARAMETERS, UpdateComponentWeldingParameters); sb.AddAction(SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ, SetUploadWeldingList_SeqParams); sb.AddSubsequence(SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ, CommonSequenceManager.GetSeqBuilder_SeqUploadWeldingList()); sb.AddAction(SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ, GetUploadWeldingList_SeqResult); sb.AddAction(SEQ_DO_COMPONENT_WELDING, DoComponentWelding); sb.AddAction(SEQ_END_WELDING, EndWelding); sb.AddAction(SEQ_SET_BLOW_OFF, SetBlowOff); sb.AddAction(SEQ_SAVE_RESULT_DATA, SaveResultData); // 해당 공정의 정보를 기록 함. sb.AddAction(SEQ_COMPLETE, Complete); // 기존 흐름에서 SET_BLOW_ON → START_WELDING 사이에 끼워넣기 #region 기존 흐름 26.04.08 sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_SET_BLOW_ON] > sb[SEQ_START_WELDING] > sb[SEQ_CHECK_END_LIST]); #endregion #region 2개 노드 추가한 흐름 26.04.08 //sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_SET_BLOW_ON] // > sb[SEQ_MOVE_STAGE_ORIGIN] // ← 추가 // > sb[SEQ_ENCODER_RESET] // ← 추가 // > sb[SEQ_START_WELDING] > sb[SEQ_CHECK_END_LIST]); #endregion // 아래 두 흐름은 기존 그대로 유지 sb.Flows(sb[SEQ_CHECK_END_LIST] > sb[SEQ_UPDATE_COMPONENT_WELDING_PARAMETERS] > sb[SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_DO_COMPONENT_WELDING] > sb[SEQ_CHECK_END_LIST]); sb.Flows(sb[SEQ_CHECK_END_LIST] > sb[SEQ_END_WELDING] > sb[SEQ_SET_BLOW_OFF] > sb[SEQ_SAVE_RESULT_DATA] > sb[SEQ_COMPLETE] > sb.Final); sb.OnStopped(OnStopped); sb.OnForceError(OnForceError); //sb.Draw("SeqComponentTeachingRun.pdf"); return sb; } #region 26.04.08 NDY private SequenceActionResult EncoderReset(SequenceContext context, string id, SequenceActionResult param) { if (_ACS == null) { Log.W("[EncoderReset] ACS null - 스킵"); return SequenceAction.Ok(); } if (!_Scanner.Config.UseMarkingOnTheFly) { Log.W("[EncoderReset] MOTF 비활성화 - 스킵"); return SequenceAction.Ok(); } Log.I("[EncoderReset] 시작"); // SeqUploadWeldingList.MarkingOnTheFlyOn 패턴 _Scanner.SetFlyX(_Scanner.Config.MarkingOnTheFlyEncoderResolutionX); Log.I($"[EncoderReset] SetFlyX: {_Scanner.Config.MarkingOnTheFlyEncoderResolutionX}"); _Scanner.WaitForEncoderX(_Scanner.Config.MarkingStartEncoderPositionX); Log.I($"[EncoderReset] WaitForEncoderX: {_Scanner.Config.MarkingStartEncoderPositionX}"); _Scanner.StoreEncoder(0); _Scanner.SaveAndRestartTimer(); Log.I("[EncoderReset] 완료 - Stage(0) = Scanner(0) 동기화"); return SequenceAction.Ok(); } //SeqTeachingPosMoveStageA패턴 참고 - 기존 GUN 프로젝트 시퀸스 private SequenceActionResult MoveStageToOrigin(SequenceContext context, string id, SequenceActionResult param) { if (_AxisX == null) { Log.W("[MoveStageToOrigin] AxisX2 null - 스킵"); return SequenceAction.Ok(); } Log.I("[MoveStageToOrigin] Stage X → 0 이동 시작"); // SeqTeachingPosMoveStageA.MoveXYStage 패턴 _AxisX.SetTargetPos(0, TargetPosType.ABS); _AxisX.SetMoveVelocityImm(AuroraACS.Speed.M); _AxisX.AccelerationImm(AuroraACS.Speed.M); _AxisX.DecelerationImm(AuroraACS.Speed.M); _AxisX.JerkImm(AuroraACS.Speed.M); _AxisX.Move(); var res = context.RetryUntilSuccess( () => _AxisX.IsReachedTargetPosition(0), 30 * 1000, 20, true); if (res == E2.TIMEOUT) { Log.E("[MoveStageToOrigin] TIMEOUT"); return null; } if (res == E2.CANCELLED) { Log.E("[MoveStageToOrigin] CANCELLED"); return null; } Log.I("[MoveStageToOrigin] Stage X = 0 도달 완료"); return SequenceAction.Ok(); } #endregion private SequenceActionResult Sequence_Enter(SequenceContext context, string id, SequenceActionResult param) { Log.I("===== SeqTeachingRun Sequence_Enter 진입 ====="); // 임시 추가 Log.I("SeqTeachingRun Start"); _InitParam = param.Param as SeqTeachingRun_InitParam; _SystemParameter = context.GetSetting(CommonSettingIds.SystemParameter); //_Laser = context.GetLaser(); //_Laser.GetLaserDevice(ref _LaserDevice); //_LaserTruFiber = _LaserDevice as LaserTruFiber; //if (_plc == null) // _plc = context.Devices[DeviceIds.PLCDevice] as PLCControllerPrismaticScanner; // Laser 시퀀스 Running. 안 넣어줘도 됨. //_LaserTruFiber.SetIsSeqRunning(true); _LaserPowerCalParam = context.GetSetting(CommonSettingIds.LaserPowerCalParam);//우선 AuroraRecipe로 상속받아서 함수 사용하도록 함 //laserPowerCalParam 초기화(CSV 파일 불러오기) _LaserPowerCalParam.Initialize(_LaserPowerCalParam.PowerCalibrationCenterBeamCSVFile, _LaserPowerCalParam.PowerCalibrationRingBeamCSVFile); _SequenceSetting = context.Settings[CommonSettingIds.Sequence] as SequenceSetting; InitDev(context); if (!CheckDeviceReady(context)) { //Check Device AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_TeachingRun_DeviceNotReady); Log.E("The devices are not active."); return null; } if (InitSequenceVariable(context) == false) { AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_TeachingRun_InitVariableError); return null; } return SequenceAction.Ok(); } private void InitDev(SequenceContext context) { //Device Init _Scanner = context.GetLaserScanner(); //_IO = context.Devices[CommonDeviceIds.IODevice] as PAIX_PrismaticScanner; // 26.04.06 NDY 추가: ACS 디바이스 취득 (null-safe) // 변수 맵 수신 후 MOTFStart() 호출에 사용 // 변경 전 //try { _ACS = context.Devices[DeviceIds.ACS] as ScannerACS; } // 변경 후 try { _ACS = context.Devices[DeviceIds.ACS] as IScannerACS; } catch { _ACS = null; Log.W("ACS 디바이스 취득 실패 (null 처리)"); } //26.04.08 NDY 추가 _AxisX 가져오기 try { _AxisX = context.Devices[CommonDeviceIds.AxisX2] as ACSMotor; } catch { _AxisX = null; Log.W("AxisX2 취득 실패"); } } private bool CheckDeviceReady(SequenceContext context) { //List deviceListToCheck = new List() { CommonDeviceIds.Scanner, CommonDeviceIds.IODevice }; // 26.04.15 주석처리 아래 로 테스트 List deviceListToCheck = new List() { CommonDeviceIds.Scanner }; //List deviceListToCheck = new List() { CommonDeviceIds.Scanner, CommonDeviceIds.IODevice,DeviceIds.ACS }; 변수 맵 수신 후 ACS 주석 해제 위에 주석 처리 bool isAllDeviceActivated = true; foreach (string item in deviceListToCheck) { if (!context.Devices[item].IsActivated) { isAllDeviceActivated = isAllDeviceActivated && false; } } return isAllDeviceActivated; } private bool InitSequenceVariable(SequenceContext context) { var mainPage = CommonHelper.GetMainPage(); _ScannerCanvasUserCtrl = mainPage.ScannerCanvasCtrlTeaching; _WeldingScannerListNo = eListNo.LIST1; _SelectedObjectList = FindObjects(); return true; } private List FindObjects() { if (_InitParam.IsUseExternalObjects) { return _InitParam.ExternalObjects; } else if (_InitParam.IsObjectSelectAll) { return _ScannerCanvasUserCtrl.Model.ActiveLayer.Objects; } else { return _ScannerCanvasUserCtrl.Canvas.Model.SelectedObjects; } } private SequenceActionResult CheckInitCondition(SequenceContext context, string id, SequenceActionResult param) { if (!CheckCondition()) { AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_TeachingRun_CheckInitCondition); Log.E("Condition is not in state to start sequence."); context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, false); return null; } _CurrentobjectIndex = 0; _BeforeObjectIndex = 0; _CountWeldingObject = 0; _BeforeParameterNo = -1; _BeforeProgramNo = -1; _ScannerSaveLogTime = TimeSpan.Zero; _FlattenRecipes = new List(); // 레시피 초기화 UnpackRecipes(_SelectedObjectList); var startTime = DateTime.Now; context.PutValue(ContextKey.KEY_SEQUENCE_START_TIME, startTime); Log.D("ComponentTeachingRun Sequence CheckInitCondition"); return SequenceAction.Ok(); } private bool CheckCondition() { if (CheckCondition_Interlock()) { if (InitializeDeviceStatus()) { return true; } } return false; } private bool CheckCondition_Interlock() { //// PLC, Data, IO 등 Interlock checking bool boolian = AlarmManager.HasAlarmLevelHigh(); if (boolian) { Log.E($"Clear Heavy Alarms For Welding!"); MessageUtil.ShowWarningToast(string.Format("Clear Heavy Alarms For Welding!")); return false; } boolian = AlarmManager.IsPlcDoorOpen(); if (boolian) { Log.E($"PLC Door Opened!! Can't Weld Now."); MessageUtil.ShowWarningToast(string.Format("Close Door ! Clear PLC DoorInterlock!")); return false; } boolian = AlarmManager.IsPlcEmoAlarm(); if (boolian) { Log.E($"PLC EMO Pushed!! Can't Weld Now."); MessageUtil.ShowWarningToast(string.Format("Clear PLC EMO Status!")); return false; } return true; } private bool InitializeDeviceStatus() { //Vision Correction File 선택 _Scanner.SelectCorrectionTable(2); return true; } private SequenceActionResult SetBlowOn(SequenceContext context, string id, SequenceActionResult param) { if (_InitParam.UseBlow == false) { return SequenceAction.Ok(); } int intervalTime = _InitParam.IntervalTimeBlowOn; //_IO._iOUnit.WriteDoBit(Convert.ToInt32(ePDOAddress.SeamWeldingN2BlowSol), true); Thread.Sleep(intervalTime); Log.D(string.Format($"[ComponentTeachingRun] Blow ON Complete")); return SequenceAction.Ok(); } private void SaveScannerRecordFileForamt() { if (_Scanner.Config.UseRecordSignal) { DateTime startTime = DateTime.Now; _Scanner.SaveStackRecord("Normal"); _ScannerSaveLogTime += DateTime.Now.Subtract(startTime); } } private SequenceActionResult StartWelding(SequenceContext context, string id, SequenceActionResult param) { Log.D(string.Format($"[ComponentTeachingRun] Start Component Welding")); _SequenceCompleteTime = DateTime.Now; // Laser 판넬 On //_IO.WriteDoBit(PAIX_PrismaticScanner.ePrismaticDOAddress.CCCAPLaserOnLampOn, true); _DivideObjectsQueue = SplitBySerialize(_FlattenRecipes); Log.I($"Divided DrawObjects List Count[{_DivideObjectsQueue.Count}]"); return SequenceAction.Ok(); } // 펼쳐진 객체(_FlattenRecipes)를 여러개의 스캔 파라미터에 따라 여러개로 나눈다 public static Queue> SplitBySerialize(List source) { var result = new Queue>(); if (source == null || source.Count == 0) return result; List current = null; int? currentId = null; foreach (var item in source) { if (current == null || currentId.Value != item.ScannerParamID) { current = new List(); result.Enqueue(current); currentId = item.ScannerParamID; } current.Add(item); } return result; } private string CheckEndList(SequenceContext context, string id, ICollection branches, SequenceActionResult param) { if (_DivideObjectsQueue.Count == 0) return SEQ_END_WELDING; // 파일 저장 변수 초기화 if (_Scanner.Config.UseRecordSignal) { DateTime startTime = DateTime.Now; _Scanner.InitializeRecord(); _ScannerSaveLogTime += DateTime.Now.Subtract(startTime); } return SEQ_UPDATE_COMPONENT_WELDING_PARAMETERS; } private SequenceActionResult UpdateComponentWeldingParameters(SequenceContext context, string id, SequenceActionResult param) { _partialWeldingStartTime = DateTime.Now; //var currentDivideObject = _DivideObjectsQueue.Peek(); //int paramIndex = currentDivideObject[0].ScannerParamID - 1; //Log.I($"Remain Divided DrawObjects Count[{currentDivideObject.Count}] / ParameterNo[{paramIndex + 1}]"); //int SlotNo = _plc.ReadWord(eWordInput.PLC_CURRENT_SLOT_NO).data; //if (SlotNo <= 0) // SlotNo = 1; //// 결과 Data 를 업데이트 함. //var power = Status.GetProcessingWeldingData().UploadProcessDatas.Where(data => data.DataName == $"Power_{SlotNo}_{_SequenceSetting.LastPageNo}").FirstOrDefault(); //#region 26.04.08 NDY 캘리브레이션 노드 추가 테스트를 위해서 잠시 예외처리 코드 추가 //if (power != null) // power.Value = ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower1 // + ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower2; //#endregion //// 26.04.08 NDY 주석처리 - power null 예외 방지 (위 null 체크로 대체) ////power.Value = ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower1 + ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower2; //var velocity = Status.GetProcessingWeldingData().UploadProcessDatas.Where(data => data.DataName == $"Velocity_{SlotNo}_{_SequenceSetting.LastPageNo}").FirstOrDefault(); //#region 26.04.08 NDY velocity null에 대한 예외 처리 //if (velocity != null) // velocity.Value = ScannerCanvasUserCtrl.ScannerParams[paramIndex].MarkSpeed; //#endregion //// 26.04.08 NDY 주석처리 - velocity null 예외 방지 (위 null 체크로 대체) ////velocity.Value = ScannerCanvasUserCtrl.ScannerParams[paramIndex].MarkSpeed; //// 26.04.08 NDY - FDC 데이터 상신 -기존 코드 주석처리 ////_plc.UploadReportDatas(Status.GetProcessingWeldingData().UploadProcessDatas); //// 26.04.08 NDY - FDC 상신도 데이터 있을 때만 - 테스트 목적 예외 처리 추가 //if (Status.GetProcessingWeldingData().UploadProcessDatas.Count > 0) // _plc.UploadReportDatas(Status.GetProcessingWeldingData().UploadProcessDatas); //_CenterBeamPower = (int)ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower1; //_RingBeamPower = (int)ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower2; //_CenterRingRatio = 0; //if (_CenterBeamPower == 0 && _RingBeamPower == 0) // _CenterRingRatio = 100; //else // _CenterRingRatio = (int)Math.Round((double)(100 * _CenterBeamPower) / (double)(_CenterBeamPower + _RingBeamPower), 0); // Discrete 한 값이라서 Ring은 오차 발생 ////if (_Laser.Config.TruFiberType == eTruFiberType.TruFiberP) ////{ // _CenterBeamPower = _LaserPowerCalParam.CalibrateWattToWatt(_CenterBeamPower, (uint)LaserPowerCalParam.beamMode.Center); // _RingBeamPower = _LaserPowerCalParam.CalibrateWattToWatt(_RingBeamPower, (uint)LaserPowerCalParam.beamMode.Ring); ////} //// Ready 상태 변경 확인(Complete 상태에서 Laser Ready로 변환되기까지 시간이 걸림) //// 여기서는 왜 IW03과 Center를 비교하는 것인지.... 아직 값을 써주지 않았으므로 레이저 출사 가능 여부만 확인하면 되지 않을까. //var res = context.RetryUntilSuccess(_Laser.GetLaserReady, timeoutSeconds * 1000, 1); //if (res == E2.TIMEOUT) //{ // Log.E(string.Format($"[Page Change Req] Laser Program Ready Timeout When LaserReady")); // return null; //} //Log.D(string.Format($"[Page Change Req] Laser Program Ready OK")); //// Laser Program Parameters 설정(Laser Ready 상태는 용접 직전에 확인) //List<(int, int, bool)> datas = new List<(int, int, bool)>(); //datas.Add((csWriteNodeNo.IW03, _CenterBeamPower, true)); //datas.Add((csWriteNodeNo.IW04, _RingBeamPower, true)); //datas.Add((csWriteNodeNo.IW05, _CenterRingRatio, true)); //// 업데이트 될 경우, 프로그램 전환을 위한 프로그램 종료 시퀸스 진행됨. //if (_LaserTruFiber.UpdatesDatasIfChanged(datas)) //{ // // 프로그램이 종료 될 때까지 대기. // res = context.RetryUntilSuccess(_LaserTruFiber.GetProgramCompleted, timeoutSeconds * 1000, 10); // if (res == E2.TIMEOUT) // { // Log.E(string.Format($"[Page Change Req] Laser Program Completed Timeout When Parameter Changes")); // return null; // } // Log.D(string.Format($"[Page Change Req] Laser Program Completed OK When Data Updated")); //} return SequenceAction.Ok(); } private SequenceActionResult SetUploadWeldingList_SeqParams(SequenceContext context, string id, SequenceActionResult param) { Status.SetWorkingPosition(eWorkingPosition.UPLOADING_WELD_LIST); // TeachingRun에는 용접 시퀀스만 정의해서 Auto Run에서도 사용하므로 Offset 넣어줘야 함 SeqUploadWeldingList_InitParam seqParams = new SeqUploadWeldingList_InitParam() { NeedSetLaserTiming = _InitParam.NeedSetLaserTiming, IncludeListStartEnd = true, CellMeasureCenterOffsetX = _SystemParameter.CellPosMeasureCenterOffsetX + _InitParam.CellCenterOffsetX, CellMeasureCenterOffsetY = _SystemParameter.CellPosMeasureCenterOffsetY + _InitParam.CellCenterOffsetY, SelectedDrawObjectList = _DivideObjectsQueue.Peek(), WeldingScannerListNo = _WeldingScannerListNo, }; var _PageNo = _SequenceSetting.LastPageNo; Log.W($"page {_PageNo}({Status.GetProcessingWeldingData().CellId}) : Cell Offset Value Check ; X : {_SystemParameter.CellPosMeasureCenterOffsetX} + {_InitParam.CellCenterOffsetX} ; Y : {_SystemParameter.CellPosMeasureCenterOffsetY} + {_InitParam.CellCenterOffsetY}"); Log.D(string.Format($"[ComponentTeachingRun] Set UploadWelindList Seq Param")); return SequenceAction.Ok(seqParams); } private SequenceActionResult GetUploadWeldingList_SeqResult(SequenceContext context, string id, SequenceActionResult param) { Log.D(string.Format($"[ComponentTeachingRun] Get UploadWelindList Seq Param")); var resultOutParam = param.Param as SeqUploadWeldingList_OutParam; if (resultOutParam.Success == false) { Log.E(string.Format($"[ComponentTeachingRun] Failed UploadWeldingList Sequence")); return null; } return SequenceAction.Ok(); } private SequenceActionResult DoComponentWelding(SequenceContext context, string id, SequenceActionResult param) { _DivideObjectsQueue.Dequeue(); Log.I("UploadWeldingList Complete"); Log.D(string.Format($"[ComponentTeachingRun] Component Welding Start idx :({_BeforeObjectIndex} ~ {_BeforeObjectIndex + _CountWeldingObject - 1})")); #region PSTART_DYN 사용시 참고 //// PSTART_DYN에서는 레이저가 활성화되었는지 확인하여야 함. 하지만, PSTART_STATICALLY에서는 굳이 확인할 필요 없음. (이전에 확인해줄 것이므로) // var res = context.RetryUntilSuccess(_LaserTruFiber.GetProgramActive, timeoutSeconds * 1000, 1); //if (res == E2.TIMEOUT) //{ // Log.E(string.Format($"[ComponentTeachingRun] Laser Program Active Timeout")); // return null; //} #endregion //Log.D(string.Format($"[ComponentTeachingRun] Check LaserReadyResponse")); //// 만일 프로그램이 정상적으로 종료 및 변경 원할 경우, 레이저 스레드에서 Ready 상태로 만들어주고 있을 것임. //// Ready 상태 변경 확인 //var res = context.RetryUntilSuccess(LaserReadyResponse, timeoutSeconds * 1000, 10); //if (res == E2.TIMEOUT) //{ // Log.E(string.Format($"[ComponentTeachingRun] Laser Program Ready Timeout When LaserReadyResponse")); // return null; //} //Log.D(string.Format($"[ComponentTeachingRun] Laser Program Ready OK When ReadyResponse")); //Status.SetWorkingPosition(eWorkingPosition.EXECUTING_WELD); Log.I($"Welding List Space : {_Scanner.GetListSpace()}"); DateTime weldingTime = DateTime.Now; _Scanner.ExecuteList(_WeldingScannerListNo); // ── 26.04.06 NDY 추가: ACS Stage X 이동 ────────────── // MOTF: Scanner List 실행과 동시에 Stage X 이동 시작 // Stage X 엔코더가 트리거 → WaitForEncoderX 조건 도달 시 갈바노 마킹 // TODO: 변수 맵 수신 후 _ACS.MOTFStart()로 교체 // _ACS?.MOTFStart(); // ──────────────────────────────────────────────────── _Scanner.WaitForListTaskCompleted(_SystemParameter.WeldLinesTaskTimeOut); // ── 26.04.06 NDY 추가: ACS 완료 대기 ───────────────── // TODO: 변수 맵 수신 후 활성화 // while (!_ACS.CheckMOTFComp()) { Thread.Sleep(10); } // ──────────────────────────────────────────────────── _MeasurementSpanTime = DateTime.Now.Subtract(weldingTime); Log.D(string.Format($"[ComponentTeachingRun] Component Welding Complete Time : {_MeasurementSpanTime.TotalMilliseconds}")); #region PSTART_DYN 사용시 참고 //// 굳이 확인할 필요가 없을 듯 함.PSTAT_STATICALLY 활용시. //res = context.RetryUntilSuccess(_LaserTruFiber.GetProgramCompleted, timeoutSeconds * 1000, 1); //if (res == E2.TIMEOUT) //{ // Log.E(string.Format($"[ComponentTeachingRun] Laser Program Complete Timeout")); // return null; //} #endregion Log.D(string.Format($"[ComponentTeachingRun] Laser Program Complete OK")); if (_Scanner.Config.UseRecordSignal) { DateTime startTime = DateTime.Now; _Scanner.StackRecord(); _ScannerSaveLogTime += DateTime.Now.Subtract(startTime); } // excel 파일 작성 SaveScannerRecordFileForamt(); Log.D(string.Format($"[ComponentTeachingRun] SaveScannerRecordFileForamt Time : {_ScannerSaveLogTime.TotalMilliseconds}")); var PartialTactTime = DateTime.Now.Subtract(_partialWeldingStartTime); Log.D(string.Format($"[TactTime] Complete Partial Component Weld : {PartialTactTime.TotalMilliseconds}")); _ScannerCanvasUserCtrl.TimeLabelUpdate(_MeasurementSpanTime.TotalMilliseconds, PartialTactTime.TotalMilliseconds); //// 용접 끝난 후, 초기화 시킴 //var UploadDatas = Status.GetProcessingWeldingData().UploadProcessDatas; //foreach (var data in UploadDatas) // data.Clear(); //_plc.UploadReportDatas(UploadDatas); return SequenceAction.Ok(); } private SequenceActionResult EndWelding(SequenceContext context, string id, SequenceActionResult param) { _MeasurementSpanTime = DateTime.Now.Subtract(_SequenceCompleteTime); Log.D(string.Format($"[TactTime] Complete All Component Weld : {_MeasurementSpanTime.TotalMilliseconds}")); //Anlog 출력 0으로 변경, efunctiontype을 command로 변경해주면 list가 아니라 직접 변경됨 _Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.COMMAND); _Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.COMMAND); _Scanner.JumpAbs(0, 0, eFunctionType.COMMAND); // Laser 판넬 Off //_IO.WriteDoBit(PAIX_PrismaticScanner.ePrismaticDOAddress.CCCAPLaserOnLampOn, false); var mesurementTime = DateTime.Now; //용접 완료 후 CorrectionFile Vision으로 원복 _Scanner.SelectCorrectionTable(1); _MeasurementSpanTime = DateTime.Now.Subtract(mesurementTime); Log.D(string.Format($"[ComponentTeachingRun] Change SelectCorrectionTable Time : {_MeasurementSpanTime.TotalMilliseconds}")); return SequenceAction.Ok(); } private SequenceActionResult SetBlowOff(SequenceContext context, string id, SequenceActionResult param) { if (_InitParam.UseBlow == false) { return SequenceAction.Ok(); } int intervalTime = _InitParam.IntervalTimeBlowOff; Thread.Sleep(intervalTime); //_IO._iOUnit.WriteDoBit(Convert.ToInt32(ePDOAddress.SeamWeldingN2BlowSol), false); Log.D(string.Format($"[ComponentTeachingRun] Blow OFF Complete")); return SequenceAction.Ok(); } private SequenceActionResult SaveResultData(SequenceContext context, string id, SequenceActionResult param) { context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, true); return SequenceAction.Ok(); } private SequenceActionResult Complete(SequenceContext context, string id, SequenceActionResult param) { context.TryGetValue(ContextKey.KEY_SEQUENCE_RESULT, out bool seqResult); SeqTeachingRun_OutParam result = new SeqTeachingRun_OutParam() { Success = seqResult }; if (seqResult == false) { AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_TeachingRun_ResultFailed); } Log.D("ComponentTeachingRun Sequence Complete"); return SequenceAction.Ok(result); } private static void UnpackRecipes(List recipes) { // Recipe를 재귀적으로 탐색해서 List로 재구성하는 함수 if (recipes == null) return; for (int i = 0; i < recipes.Count; i++) { if (recipes[i] is Group) { Group subGroup = recipes[i] as Group; List subrecipes = subGroup.SubObjects as List; UnpackRecipes(subrecipes); } else { _FlattenRecipes.Add(recipes[i]); } } } private void OnForceError(SequenceContext context, string arg2) { // Scanner Log 저장 SaveScannerRecordFileForamt(); //List 실행 종료 _Scanner.StopExecution(); //Anlog 출력 0으로 변경, efunctiontype을 command로 변경해주면 list가 아니라 직접 변경됨 _Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.COMMAND); _Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.COMMAND); // TruFiber레이저 Ready로 변경 ForceLaserReady(); //Scanner 원점 복귀 // 복귀 시 Scanner Busy 상태일 수 있으므로 진행중인 작업이 끝날 때까지 대기 Thread.Sleep(10); _Scanner.JumpAbs(0, 0, eFunctionType.COMMAND); Thread.Sleep(10); Log.I("SequenceAction Ok : OnForceError"); } private void OnStopped(SequenceContext context) { // Scanner Log 저장 SaveScannerRecordFileForamt(); //List 실행 종료 _Scanner.StopExecution(); //Anlog 출력 0으로 변경, efunctiontype을 command로 변경해주면 list가 아니라 직접 변경됨 _Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.COMMAND); _Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.COMMAND); // TruFiber레이저 Ready로 변경 ForceLaserReady(); //Scanner 원점 복귀 // 복귀 시 Scanner Busy 상태일 수 있으므로 진행중인 작업이 끝날 때까지 대기 Thread.Sleep(10); _Scanner.JumpAbs(0, 0, eFunctionType.COMMAND); Thread.Sleep(10); // Laser 판넬 Off //_IO.WriteDoBit(PAIX_PrismaticScanner.ePrismaticDOAddress.CCCAPLaserOnLampOn, false); Log.I("SequenceAction Ok : OnStopped"); } private void ForceLaserReady() { // 트럼프 레이저에서 PStartDyn, Prog_Stop을 감시하여 강제로 Ready 상태로 만드는 함수 if (_Laser.IsSimul) { return; } //// PSTART_STATICAL 에서는 필요 없어보임. //_LaserTruFiber.SetProgEnd(); // Laser 판넬 Off //_IO.WriteDoBit(PAIX_PrismaticScanner.ePrismaticDOAddress.CCCAPLaserOnLampOn, false); Log.I("SequenceAction Ok : Set Laser Ready"); } } // TODO: UseStream 으로 TeachingRun 을 할 필요가 있을까?? 확인 필요 public class SeqComponentTeachingRun_InitParam : InitParam { public bool IsObjectSelectAll { get; set; } // All == true, Selected Object == false public bool UseStream { get; set; } public bool UseBlow { get; set; } public int IntervalTimeBlowOn { get; set; } public int IntervalTimeBlowOff { get; set; } public bool NeedSetLaserTiming { get; set; } // For SubSequence, SeqUploadWeldingList } public class SeqComponentTeachingRun_OutParam : OutParam { } }