using Aurora; using Aurora.Api; using Aurora.Api.Services; using Aurora.View; 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"; 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; // 26.04.06 NDY - MOTF용 ACS 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); 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); 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]); 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; } private SequenceActionResult Sequence_Enter(SequenceContext context, string id, SequenceActionResult param) { 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["ACS"] as ScannerACS; } catch { _ACS = null; Log.W("ACS 디바이스 취득 실패 (null 처리)"); } } private bool CheckDeviceReady(SequenceContext context) { List deviceListToCheck = new List() { CommonDeviceIds.Scanner, CommonDeviceIds.IODevice }; //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(); power.Value = ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower1 + ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower2; var velocity = Status.GetProcessingWeldingData().UploadProcessDatas.Where(data => data.DataName == $"Velocity_{SlotNo}_{_SequenceSetting.LastPageNo}").FirstOrDefault(); velocity.Value = ScannerCanvasUserCtrl.ScannerParams[paramIndex].MarkSpeed; // FDC 데이터 상신 _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 bool LaserReadyResponse() { if (ApplicationExt.IsSimulation) return true; // Ready 상태를 확인하기 위해서는 프로그램 파라메터가 최신 상태인지, Ready 상태인지 둘 다 확인해야 한다. return Convert.ToInt32(_LaserTruFiber.GetWriteInputWord(csWriteNodeNo.IW03)) == _CenterBeamPower && Convert.ToInt32(_LaserTruFiber.GetWriteInputWord(csWriteNodeNo.IW04)) == _RingBeamPower && Convert.ToInt32(_LaserTruFiber.GetWriteInputWord(csWriteNodeNo.IW05)) == _CenterRingRatio //&& _LaserTruFiber.GetProgramNo() == _BeforeProgramNo && _LaserTruFiber.GetLaserReady(); } 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 { } }