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.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 SeqComponentTeachingRunForCleaner { 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_PrismaticCleaner _IO; private Laser _Laser; private ILaser _LaserDevice; private TruPulse _TruPulseLaser; 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 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); _TruPulseLaser = _LaserDevice as TruPulse; 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_PrismaticCleaner; } private bool CheckDeviceReady(SequenceContext context) { List deviceListToCheck = new List() { CommonDeviceIds.Scanner, CommonDeviceIds.IODevice }; 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; 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_PrismaticCleaner.ePrismaticDOAddress.LaserOnLamp, true); // Group: 펼치지 않고 단일 배치로 큐 삽입 → SeqUploadWeldingList에서 GroupRampingScannerParam 처리 // 비-Group: 기존처럼 재귀 펼침 후 ScannerParamID 동일한 것끼리 묶음 _DivideObjectsQueue = BuildDivideObjectsQueue(_SelectedObjectList); 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); var WaveForm = ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserProgramNo; var Power = ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower1; _TruPulseLaser.SetPulseWaveform((ushort)WaveForm); Log.I($"[ComponentTeachingRun] Set WaveForm : {WaveForm}"); var res = context.RetryUntilSuccess(() => _TruPulseLaser.GetPulseWaveform().waveform == (ushort)WaveForm, timeoutSeconds * 1000, 10); if (res == E2.TIMEOUT) { Log.E(string.Format($"[ComponentTeachingRun] WaveForm Setting Completed Timeout When Parameter Changes")); return null; } // 웨이브폼 변경하고 레이저 출사 안되는 현상이 있는데.. 리셋을 해줘야하나? 리셋하면 됨 //_TruPulseLaser.AlarmReset(); Log.D(string.Format($"[ComponentTeachingRun] Setting Complete WaveForm : {WaveForm}, BeamPower : {Power}W")); 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})")); Log.D(string.Format($"[ComponentTeachingRun] Check LaserReadyResponse")); // 만일 프로그램이 정상적으로 종료 및 변경 원할 경우, 레이저 스레드에서 Ready 상태로 만들어주고 있을 것임. // Ready 상태 변경 확인 var res = context.RetryUntilSuccess(() => _Laser.GetLaserReady(), timeoutSeconds * 1000, 1); if (res == E2.TIMEOUT) { Log.E(string.Format($"[ComponentTeachingRun] Laser Program Ready Timeout When LaserReadyResponse")); return null; } else if (res == E2.ABORTED) { 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); _Scanner.WaitForListTaskCompleted(_SystemParameter.WeldLinesTaskTimeOut); _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); } 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_PrismaticCleaner.ePrismaticDOAddress.LaserOnLamp, 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); } /// /// 선택된 객체 목록을 용접 순서 큐로 변환한다. /// - Group 객체 : 펼치지 않고 단일 배치 [group] 으로 큐에 삽입 /// → SeqUploadWeldingList 의 GroupRampingScannerParam 경로로 처리됨 /// - 비-Group 객체 : 재귀적으로 펼친 뒤 ScannerParamID 가 동일한 것끼리 묶어 삽입 /// → 기존 SplitBySerialize 동작과 동일 /// 처리 순서(원래 선택 순서)는 보장된다. /// private static Queue> BuildDivideObjectsQueue(List source) { var result = new Queue>(); if (source == null || source.Count == 0) return result; var flatBuffer = new List(); foreach (var obj in source) { if (obj is Group) { if (flatBuffer.Count > 0) { foreach (var chunk in SplitBySerialize(flatBuffer)) result.Enqueue(chunk); flatBuffer.Clear(); } result.Enqueue(new List { obj }); } else { FlattenIntoBuffer(obj, flatBuffer); } } if (flatBuffer.Count > 0) { foreach (var chunk in SplitBySerialize(flatBuffer)) result.Enqueue(chunk); } return result; } /// /// 비-Group 객체를 재귀적으로 펼쳐 buffer 에 추가한다. /// 최상위 Group 은 BuildDivideObjectsQueue 에서 처리하므로 여기서는 중첩 Group 만 해당. /// private static void FlattenIntoBuffer(IDrawObject obj, List buffer) { if (obj is Group group) { foreach (var sub in group.SubObjects) FlattenIntoBuffer(sub, buffer); } else { buffer.Add(obj); } } 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._iOUnit.WriteDoBit(Convert.ToInt32(ePDOAddress.LaserOnLampOn), false); Log.I("SequenceAction Ok : OnStopped"); } private void ForceLaserReady() { // 트럼프 레이저에서 PStartDyn, Prog_Stop을 감시하여 강제로 Ready 상태로 만드는 함수 if (_Laser.IsSimul) { return; } //// PSTART_STATICAL 에서는 필요 없어보임. //_LaserTruFiber.SetProgEnd(); // Laser 판넬 Off _IO._iOUnit.WriteDoBit(Convert.ToInt32(ePDOAddress.LaserOnLampOn), 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 { } }