NohDoyeon241104 aa5928ce3e refactor: PTOP 네임스페이스/클래스 정리(Scanner→PTOP) 및 머신 PTOP 단독화
- namespace/using ILSOS_PrismaticScanner → ILSOS_PrismaticRD_PTOP (전 .cs/.xaml)
- 파일/클래스 리네임 7개: AlarmCode/AlarmManager/PAIX/EquipmentSpecificModel→_PTOP,
  RMSHandlerForScanner→ForPTOP, SettingUploadDataListForScanner→ForPTOP,
  SeqAutoRunPrismaticScanner→SeqAutoRunPrismatic, PLCControllerPrismaticScanner→PLCControllerPTOP
- PAIX_PrismaticScanner: 머신 참조만 PAIX_PTOP로 변경, WeldingCommon 공유 참조(SeqTestRun)는 보존
- XAML 3종(x:Class/clr-namespace/code-behind) 일치, csproj Compile Include 갱신
- csproj 빌드 경로 TDC→PTOP(_out_prismatic_rd_ptop)
- sln에서 Scanner/Gun/CleanLaser 제거 및 해당 머신 소스 삭제 → Machines에 PTOP만
- 분석/작업일지 문서 추가 (참고본: 정리후(빌드완) 기준)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 11:28:19 +09:00

776 lines
34 KiB
C#

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_PrismaticRD_PTOP.Device;
using ILSOS_PrismaticRD_PTOP.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_PrismaticRD_PTOP.Device.PLCControllerPTOP;
namespace ILSOS_PrismaticRD_PTOP.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_PTOP _IO;
#warning to KSJ > _Laser 참조되는거 다 IOLaser로 변경해야함, 필요 없으면 지워도 무방
private Laser _Laser;
#warning to KSJ > _Laser 참조되는거 다 IOLaser로 변경해야함, 필요 없으면 지워도 무방
private ILaser _LaserDevice;
#warning to KSJ > _Laser 참조되는거 다 IOLaser로 변경해야함, 필요 없으면 지워도 무방
private LaserTruFiber _LaserTruFiber;
private LaserPowerCalParam _LaserPowerCalParam;
private PLCControllerPTOP _plc;
private SystemParameter _SystemParameter;
private ScannerCanvasUserCtrl _ScannerCanvasUserCtrl; //스캐너 동작관련 객체 선언
private static SequenceSetting _SequenceSetting;
private eListNo _WeldingScannerListNo;
private List<IDrawObject> _SelectedObjectList;
private Queue<List<IDrawObject>> _DivideObjectsQueue;
private List<IDrawObject> _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<SystemParameter>(CommonSettingIds.SystemParameter);
_Laser = context.GetLaser();
_Laser.GetLaserDevice(ref _LaserDevice);
//_LaserTruFiber = _LaserDevice as LaserTruFiber;
if (_plc == null)
_plc = context.Devices[DeviceIds.PLCDevice] as PLCControllerPTOP;
// Laser 시퀀스 Running. 안 넣어줘도 됨.
//_LaserTruFiber.SetIsSeqRunning(true);
_LaserPowerCalParam = context.GetSetting<LaserPowerCalParam>(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_PTOP;
}
private bool CheckDeviceReady(SequenceContext context)
{
List<string> deviceListToCheck = new List<string>() { 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<IDrawObject> 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;
}
#warning to KSJ > 지워도 무방해보임
//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_PTOP.ePrismaticDOAddress.CCCAPLaserOnLampOn, true);
// Group: 펼치지 않고 단일 배치로 큐 삽입 → SeqUploadWeldingList에서 GroupRampingScannerParam 처리
// 비-Group: 기존처럼 재귀 펼침 후 ScannerParamID 동일한 것끼리 묶음
_DivideObjectsQueue = BuildDivideObjectsQueue(_SelectedObjectList);
Log.I($"Divided DrawObjects List Count[{_DivideObjectsQueue.Count}]");
return SequenceAction.Ok();
}
// 펼쳐진 객체(_FlattenRecipes)를 여러개의 스캔 파라미터에 따라 여러개로 나눈다
public static Queue<List<IDrawObject>> SplitBySerialize(List<IDrawObject> source)
{
var result = new Queue<List<IDrawObject>>();
if (source == null || source.Count == 0)
return result;
List<IDrawObject> current = null;
int? currentId = null;
foreach (var item in source) {
if (current == null || currentId.Value != item.ScannerParamID) {
current = new List<IDrawObject>();
result.Enqueue(current);
currentId = item.ScannerParamID;
}
current.Add(item);
}
return result;
}
/// <summary>
/// 선택된 객체 목록을 용접 순서 큐로 변환한다.
/// - Group 객체 : 펼치지 않고 단일 배치 [group] 으로 큐에 삽입
/// → SeqUploadWeldingList 의 GroupRampingScannerParam 경로로 처리됨
/// - 비-Group 객체 : 재귀적으로 펼친 뒤 ScannerParamID 가 동일한 것끼리 묶어 삽입
/// → 기존 SplitBySerialize 동작과 동일
/// 처리 순서(원래 선택 순서)는 보장된다.
/// </summary>
private static Queue<List<IDrawObject>> BuildDivideObjectsQueue(List<IDrawObject> source)
{
var result = new Queue<List<IDrawObject>>();
if (source == null || source.Count == 0)
return result;
// 비-Group 객체를 모아뒀다가 한꺼번에 SplitBySerialize 하는 임시 버퍼
var flatBuffer = new List<IDrawObject>();
foreach (var obj in source) {
if (obj is Group) {
// 앞서 쌓인 비-Group 객체를 먼저 큐에 삽입 (순서 보장)
if (flatBuffer.Count > 0) {
foreach (var chunk in SplitBySerialize(flatBuffer))
result.Enqueue(chunk);
flatBuffer.Clear();
}
// Group 은 펼치지 않고 통째로 단일 배치
result.Enqueue(new List<IDrawObject> { obj });
} else {
// 비-Group 은 재귀 펼침 후 버퍼에 누적
FlattenIntoBuffer(obj, flatBuffer);
}
}
// 버퍼에 남은 비-Group 객체 처리
if (flatBuffer.Count > 0) {
foreach (var chunk in SplitBySerialize(flatBuffer))
result.Enqueue(chunk);
}
return result;
}
/// <summary>
/// 비-Group 객체를 재귀적으로 펼쳐 buffer 에 추가한다 (UnpackRecipes 대체).
/// 최상위 Group 은 BuildDivideObjectsQueue 에서 처리하므로 여기서는 중첩 Group 만 해당.
/// </summary>
private static void FlattenIntoBuffer(IDrawObject obj, List<IDrawObject> buffer)
{
if (obj is Group group) {
foreach (var sub in group.SubObjects)
FlattenIntoBuffer(sub, buffer);
} else {
buffer.Add(obj);
}
}
private string CheckEndList(SequenceContext context, string id, ICollection<string> 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은 오차 발생
// KSJ 레이저 IO 출사 테스트를 위한 주석
//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));
// 업데이트 될 경우, 프로그램 전환을 위한 프로그램 종료 시퀸스 진행됨.
// KSJ 레이저 IO 출사 테스트를 위한 주석
//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);
_Scanner.WaitForListTaskCompleted(1000);//(_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);
}
// 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_PTOP.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 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_PTOP.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_PTOP.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
{
}
}