NohDoyeon241104 5be34e612e feat: 본 레이저 단일 발진기 전환 - IO 채널 재정의 및 enum 단일화
레이저 발진기 교체(임시 Core/Ring 2대 -> 본 레이저 Max MFMC-2000/2000M 1대)에
따른 IO 재정의. 현장 적용 후 정상 동작 확인.

- ePrismaticDIAddress / ePrismaticDOAddress 를 본 레이저 38핀 커넥터 매뉴얼 기준으로
  재정의. 상태 신호 8개 -> 4개. 도면(엑셀) 라벨 오기도 함께 정정
  (Laser_FIRE -> Laser_MAIN_PWR_STATE)
- PAIX_PTOP / SeqAutoRunPTOP 의 중복 enum 정의 제거.
  WeldingCommon 의 PAIX_PrismaticScanner 를 IO 채널 번호의 단일 소스로 통일
- 발진 게이트를 Core/Ring_Control 2발 -> Laser_REMOTE 1발로 변경
  (ExecuteWelding ON/OFF, 정지 시 해제)
- DO 8 충돌 제거: Auto 시작 시 PCLBADReadyStatus(=8) write 삭제.
  본 레이저 배선에서 DO 8 = Laser_AC_KEY 라 그대로 두면 Auto 시작이
  레이저 AC KEY 릴레이를 켠다. LBAD 는 미등록 디바이스라 무의미
- GetLaserReady() 를 Laser_READY 단독 판정으로 변경
- 가이드빔 DO 를 EX_Red_Control -> Laser_GUIDE_BEAM_CONTROL 로 전환
- IO Board 에 설정 화면(UCIOInterfaceView) 연결
- IsLaserReady 표시를 DO Laser_REMOTE 되읽기로 확정 (260826 협의, 현장 미검증)

원복: 각 파일의 "260824 이전 원본" region 을 살리고 아래 블록을 제거하면 된다.
2026-08-26 11:45:40 +09:00

533 lines
21 KiB
C#

using Aurora;
using Aurora.Api;
using Aurora.Api.Manager;
using AuroraIO;
using AuroraLaser;
using AuroraLaser.Trumpf;
using AuroraLaserScanner;
using AuroraLBAD.Services;
using CoreUnit.Control.UserControl.Canvas;
using CoreUnit.Scanner.UserControl.ScannerCanvas;
using GUI;
using Hermes;
using MilCommon;
using MilUI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using VisionCommon;
using WeldingCommon;
using WeldingCommon.Alarm;
using WeldingCommon.Devices;
using WeldingCommon.Devices.Laser;
using WeldingCommon.Recipes;
using WeldingCommon.Sequences;
using WeldingCommon.Settings;
using WeldingCommon.Vision.WeldingObjectGenerator;
using static AuroraIO.AJINEXTEK.AjinIO.IOModuleInfo;
using static ILSOS_PrismaticRD_PTOP.Device.PLCControllerPTOP;
using static WeldingCommon.Devices.Laser.PAIX_PrismaticScanner;
using static WeldingCommon.Devices.PLCController;
namespace ILSOS_PrismaticRD_TDC.Sequences
{
internal class SeqTestRun
{
static readonly Logger Log = new Logger("SeqWaitWeldOrChangePage");
const string SEQ_ENTER = "[SeqWaitWeldOrChangePage] SEQ_ENTER";
const string SEQ_CHECK_INIT_CONDITION = "[SeqAutoRunLFW] Check Init Condition";
const string SEQ_CHECK_LOAD_COMPLETE = "[SeqAutoRunKS] Check Load Complete";
const string SEQ_GET_PLC_PAGE_CHANGE_REQ_ON = "[SeqWaitWeldOrChangePage] Get PLC Page Change Request On"; // PLC Page Change Request ON
const string SEQ_SET_PC_PAGE_CHANGE_ACK_ON = "[SeqWaitWeldOrChangePage] Set PC Page Change Ack On"; // PC Page Change Reqeust ACK ON
const string SEQ_GET_PLC_PAGE_CHANGE_REQ_OFF = "[SeqWaitWeldOrChangePage] Get PLC Page Change Request Off"; // PLC Page Change Request OFF
const string SEQ_SET_PC_PAGE_CHANGE_ACK_OFF = "[SeqWaitWeldOrChangePage] Set PC Page Change Ack Off"; // PC Page Change Reqeust OFF
const string SEQ_GET_PLC_WELD_REQ_ON = "[SeqWaitWeldOrChangePage] Get PLC Weld Request On"; // PLC Weld Request ON
const string SEQ_EXECUTE_WELDING = "[SeqAutoRunKS] Execute Welding";
const string SEQ_COMPLETE = "[SeqWaitWeldOrChangePage] SEQ_COMPLETE"; // 완료
private PLCController _PLC;
private Scanner _Scanner;
// 레이저 상태 변화
//private IODevice _IOLaser;
private LaserIOControl _IOLaser;
private static DataModel Models { get; set; }
private static List<IDrawObject> _UnpackDrawObject;
private SeqTestRun_InitParam _InitParam;
private SeqTestRun_OutParam _OutParam;
private eListNo _WeldingScannerListNo;
private List<IDrawObject> _SelectedObjectList;
private static short _PLCPageNo = 1;
private DateTime _AutoRunStartTime;
private DateTime _WeldingStartTime;
private TimeSpan _WeldingSpanTime;
private DateTime _WeldEndTime;
private TimeSpan _InterfaceSpanTime;
private DateTime _InterfaceStartTime;
const int PLC_TIMEOUT = 60;
private string _CurrentModelName;
private SeqTestRun _ModelItem;
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_CHECK_LOAD_COMPLETE, CheckLoadComplete);
// Page Change Request
sb.AddAction(SEQ_GET_PLC_PAGE_CHANGE_REQ_ON, GetPLCPageChangeRequestOn);
sb.AddAction(SEQ_SET_PC_PAGE_CHANGE_ACK_ON, SetPCPageChangeRequestACKOn);
sb.AddAction(SEQ_GET_PLC_PAGE_CHANGE_REQ_OFF, GetPLCPageChangeRequestOff);
sb.AddAction(SEQ_SET_PC_PAGE_CHANGE_ACK_OFF, SetPCPageChangeRequestACKOff);
// Weld Reqeust
// Weld Request I/F는 없어도 됨. 왜냐하면, PLC에서 Busy 신호만 보기 때문임.
sb.AddAction(SEQ_GET_PLC_WELD_REQ_ON, GetPLCWeldRequestOn);
sb.AddAction(SEQ_EXECUTE_WELDING, ExecuteWelding);
sb.AddAction(SEQ_COMPLETE, Complete);
sb.Flows(sb.Init > sb[SEQ_ENTER]
> sb[SEQ_CHECK_LOAD_COMPLETE]
> sb[SEQ_GET_PLC_PAGE_CHANGE_REQ_ON] > sb[SEQ_SET_PC_PAGE_CHANGE_ACK_ON]
> sb[SEQ_GET_PLC_PAGE_CHANGE_REQ_OFF] > sb[SEQ_SET_PC_PAGE_CHANGE_ACK_OFF]
> sb[SEQ_EXECUTE_WELDING]
> sb[SEQ_COMPLETE]
> sb.Final);
sb.OnForceError(OnForceError);
sb.OnStopped(OnStopped);
sb.Draw("SeqTestRun.pdf");
return sb;
}
private SequenceActionResult Sequence_Enter(SequenceContext context, string id, SequenceActionResult param)
{
Log.I("SeqTestRun Start");
Log.I("[SeqTestRun]Sequence_Enter");
InitDev(context);
if (!CheckDeviceReady(context))
{
Log.E("The devices are not active.");
return null;
}
if (InitSequenceVariable(context) == false)
{
return null;
}
_InitParam = param.Param as SeqTestRun_InitParam;
if (_InitParam.NeedPageNo)
_PLCPageNo = _InitParam.CurrentPageNo;
else
_PLCPageNo = 1;
// 레이저 Word 값을 위함
Models = _InitParam.Models;
return SequenceAction.Ok();
}
// 용접리스트1 확인
private SequenceActionResult CheckInitCondition(SequenceContext context, string id, SequenceActionResult param)
{
Log.I("[SeqTestRun]CheckInitCondition");
var startTime = DateTime.Now;
_WeldingScannerListNo = eListNo.LIST1;
return SequenceAction.Ok();
}
private SequenceActionResult CheckLoadComplete (SequenceContext context, string id, SequenceActionResult param)
{
Log.I("[SeqTestRun]CheckLoadComplete");
Status.SetWorkingPosition(eWorkingPosition.LOADING_CELL);
while (!context.Stopped)
{
if (_PLC.ReadBit((int)eBitInput.PLC_WELD_TRIGGER_REQ).data)
{
Log.I(string.Format($"[PLCInterface]GET PLC_WELD_TRIGGER_REQ True"));
_PLC.WriteBit((int)eBitOutput.PC_WELD_TRIGGER_ACK, true);
Log.I(string.Format($"[PLCInterface]SET PC_WELD_TRIGGER_ACK True"));
break;
}
_InterfaceSpanTime = DateTime.Now.Subtract(_InterfaceStartTime);
if (_InterfaceSpanTime.TotalMilliseconds > (PLC_TIMEOUT * 1000))
{
Log.E("TIMEOUT_PLC_WELD_TRIGGER_REQEUST");
_InterfaceStartTime = DateTime.Now;
}
Thread.Sleep(10);
}
return SequenceAction.Ok();
}
private SequenceActionResult GetPLCPageChangeRequestOn(SequenceContext context, string id, SequenceActionResult param)
{
Log.I("[SeqTestRun]GetPLCPageChangeRequestOn");
var res = context.RetryUntilSuccess(() => _PLC.ReadBit(eCommonBitInput.PLC_PAGE_CHANGE_REQ).data, _InitParam.TimeoutSec * 1000, interval: 10);
if (res == E2.TIMEOUT)
{
Log.FuncE("Get PLC Page Change Request On Timeout !");
return null;
}
Log.W(string.Format("PLC Page Change Request On"));
return SequenceAction.Ok();
}
private SequenceActionResult SetPCPageChangeRequestACKOn(SequenceContext context, string id, SequenceActionResult param)
{
Log.I("[SeqTestRun]SetPCPageChangeRequestACKOn");
if (_InitParam.NeedPageNo)
{
Thread.Sleep(100);
_PLCPageNo = _PLC.ReadWord(eCommonWordInput.PLC_PAGE_NO).data;
_PLC.WriteWord(eCommonWordOutput.PC_PAGE_NO, (short)_PLCPageNo);
Log.D($"When PageChangeReq is ON;{_PLCPageNo}");
#region Tact Time 절감을 위한 페이지 체인지 인터페이스 중 Program No 변경
// Unpack 관련 함수
_UnpackDrawObject?.Clear();
_UnpackDrawObject = new List<IDrawObject>();
if (Models.Layers.Count() > _PLCPageNo - 1) UnpackRecipes(Models.Layers[_PLCPageNo - 1].Objects);
else
{
Log.E($"[Page Change Req] Page No is not proper;{Models.Layers.Count()};{_PLCPageNo}");
return null;
}
int paramIndex = 0;
if (_UnpackDrawObject.Count > 0)
{
paramIndex = _UnpackDrawObject[0].ScannerParamID - 1;
}
else
{
paramIndex = 0;
Log.E("[Page Change Req] Draw Objects is zero");
}
_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은 오차 발생
// Ready 상태 변경 확인(Complete 상태에서 Laser Ready로 변환되기까지 시간이 걸림)
var res = context.RetryUntilSuccess(_IOLaser.GetLaserReady, _InitParam.TimeoutSec * 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));
// Ready 상태 변경 확인
res = context.RetryUntilSuccess(() => LaserReadyResponse(), _InitParam.TimeoutSec * 1000, 1);
if (res == E2.TIMEOUT)
{
Log.E(string.Format($"[Page Change Req] Laser Program Ready Timeout When LaserReadyResponse"));
return null;
}
Log.D(string.Format($"[Page Change Req] Laser Program Ready OK When ReadyResponse"));
#endregion
}
_PLC.WriteBit(eCommonBitOutput.PC_PAGE_CHANGE_COMPLETE, true);
Log.W(string.Format("Set PC Page Change Request ACK On"));
Log.I($"Welding List Space : {_Scanner.GetListSpace()}");
DateTime weldingTime = DateTime.Now;
_Scanner.ExecuteList(0);
//_Scanner.WaitForListTaskCompleted(_SystemParameter.WeldLinesTaskTimeOut);
//_MeasurementSpanTime = DateTime.Now.Subtract(weldingTime);
//Log.D(string.Format($"[ComponentTeachingRun] Component Welding Complete Time : {_MeasurementSpanTime.TotalMilliseconds}"));
return SequenceAction.Ok();
}
#region 레이저 값 변경을 위함
private int _CenterBeamPower;
private int _RingBeamPower;
private int _CenterRingRatio;
private static void UnpackRecipes(List<IDrawObject> 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<IDrawObject> subrecipes = subGroup.SubObjects as List<IDrawObject>;
UnpackRecipes(subrecipes);
}
else
{
_UnpackDrawObject.Add(recipes[i]);
}
}
}
private bool LaserReadyResponse()
{
//if (ApplicationExt.IsSimulation)
return true;
}
#endregion
private SequenceActionResult GetPLCPageChangeRequestOff(SequenceContext context, string id, SequenceActionResult param)
{
Log.I("[SeqTestRun]GetPLCPageChangeRequestOff");
//// 계속 페이지를 저장해줄 필요는 없음.
//var res = context.RetryUntilSuccess(UpdatePLCPageWord, _InitParam.TimeoutSec * 1000, interval: 10);
var res = context.RetryUntilSuccess(() => !_PLC.ReadBit(eCommonBitInput.PLC_PAGE_CHANGE_REQ).data, _InitParam.TimeoutSec * 1000, interval: 10);
if (res == E2.TIMEOUT)
{
Log.FuncE(string.Format("Get PLC Page Change Request OFF Timeout ! : {0}", eCommonBitInput.PLC_PAGE_CHANGE_REQ));
return null;
}
else Log.W(string.Format("Get PLC Page Change Request OFF Complete : {0}", eCommonBitInput.PLC_PAGE_CHANGE_REQ));
return SequenceAction.Ok();
}
private SequenceActionResult SetPCPageChangeRequestACKOff(SequenceContext context, string id, SequenceActionResult param)
{
Log.I("[SeqTestRun]SetPCPageChangeRequestACKOff");
if (_InitParam.NeedPageNo)
{
_PLCPageNo = _PLC.ReadWord(eCommonWordInput.PLC_PAGE_NO).data;
// PLC와 Request ON 시점에서 써주기로 하였으므로, 여기서는 굳이 써주지 않음.(확인용)
//_PLC.WriteWord(_InitParam.SetPageNoAddress, (short)_PLCPageNo, AuroraPLC.PLCBase.ePLCDevice.D);
Log.D($"When PageChangeReq is OFF;{_PLCPageNo}");
}
_PLC.WriteBit(eCommonBitOutput.PC_PAGE_CHANGE_COMPLETE, false);
Log.W(string.Format("Set PC Page Change Request ACK Off"));
return SequenceAction.Ok();
}
static DateTime WeldReqTime;
private SequenceActionResult GetPLCWeldRequestOn(SequenceContext context, string id, SequenceActionResult param)
{
Log.I("[SeqTestRun]GetPLCWeldRequestOn");
WeldReqTime = DateTime.Now;
context.RetryUntilSuccess(() => _PLC.ReadBit(eCommonBitInput.PLC_WELD_TRIGGER_REQ).data, _InitParam.TimeoutSec * 1000, interval: 10);
Log.W(string.Format("Get PLC Weld Request On"));
return SequenceAction.Ok();
}
private SequenceActionResult ExecuteWelding(SequenceContext context, string id, SequenceActionResult param)
{
Log.I("[SeqTestRun]ExecuteWelding");
Status.SetWorkingPosition(eWorkingPosition.EXECUTING_WELD);
Log.D(string.Format($"[TeachingRun] Welding Start"));
DateTime weldingTime = DateTime.Now;
_Scanner.ExecuteList(_WeldingScannerListNo);
_WeldingSpanTime = DateTime.Now.Subtract(weldingTime);
Log.D(string.Format($"[TeachingRun] Welding Complete Time : {_WeldingSpanTime.TotalMilliseconds}"));
double dExecuteListTime = _Scanner.GetTime();
Log.I(string.Format($"[TeachingRun] ExecuteList Time : {dExecuteListTime * 1000} msec"));
// OCT File Fomrat Record Test
//SaveOCTFileFormat();
// File Format Record Test
weldingTime = DateTime.Now;
//SaveScannerRecordFileForamt();
_WeldingSpanTime = DateTime.Now.Subtract(weldingTime);
Log.D(string.Format($"[TeachingRun] SaveScannerRecordFileForamt Time : {_WeldingSpanTime.TotalMilliseconds}"));
weldingTime = DateTime.Now;
_Scanner.JumpAbs(0, 0, eFunctionType.COMMAND);
//용접 완료 후 CorrectionFile Vision으로 원복
_Scanner.SelectCorrectionTable(1);
_WeldingSpanTime = DateTime.Now.Subtract(weldingTime);
Log.D(string.Format($"[TeachingRun] Change SelectCorrectionTable Time : {_WeldingSpanTime.TotalMilliseconds}"));
//int recipeNo = _RecipeManager.ActivatedRecipe().Id;
// 260824 EX_Red_Control → Laser_GUIDE_BEAM_CONTROL 로 명칭/채널 변경 (본 레이저)
// ※ 이 파일(SeqTestRun)은 미사용이며, _IOLaser 캐스팅이 항상 null 이라 실행되지 않는다.
// enum 심볼이 사라져 컴파일이 깨지므로 이름만 맞춰둔다.
_IOLaser._iOUnit.WriteDoBit(Convert.ToInt32(ePrismaticDOAddress.Laser_GUIDE_BEAM_CONTROL), true);
if()
_PLC.WriteBit((int)eBitOutput.PC_WELD_TRIGGER_ACK, false);
Log.I(string.Format($"[PLCInterface]SET PC_WELD_REQEUST_ACK False"));
_PLC.WriteBit((int)eBitOutput.PC_WELD_BUSY, true);
Log.I(string.Format($"[PLCInterface]SET PC_WELD_BUSY False"));
return SequenceAction.Ok();
}
private SequenceActionResult Complete(SequenceContext context, string id, SequenceActionResult param)
{
SeqTestRun_OutParam result = new SeqTestRun_OutParam() { PLCPageNo = (short)_PLCPageNo, Success = true, _WeldReqTime = WeldReqTime };
Log.D("CheckWeldingComplete Sequence Complete");
_PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE, true);
Log.I(string.Format($"[PLCInterface]SET PC_WELD_COMPLETE True"));
_PLC.WriteBit((int)eBitOutput.PC_WELD_BUSY, false);
Log.I(string.Format($"[PLCInterface]SET PC_WELD_BUSY False"));
return SequenceAction.Ok(result);
}
private void OnStopped(SequenceContext context)
{
Log.W("SequenceAction Ok : OnStopped");
}
private void OnForceError(SequenceContext context, string arg2)
{
Log.W("SequenceAction Ok : OnForceError");
}
private bool UpdatePLCPageWord()
{
if (_InitParam.NeedPageNo)
{
_PLCPageNo = _PLC.ReadWord(eCommonWordInput.PLC_PAGE_NO).data;
_PLC.WriteWord(eCommonWordOutput.PC_PAGE_NO, (short)_PLCPageNo);
}
return !_PLC.ReadBit(eCommonBitInput.PLC_PAGE_CHANGE_REQ).data;
}
static private bool pageChangeRequestBit = false;
static private bool weldRequestBit = false;
private bool CheckRequest()
{
pageChangeRequestBit = _PLC.ReadBit(eCommonBitInput.PLC_PAGE_CHANGE_REQ).data;
weldRequestBit = _PLC.ReadBit(eCommonBitInput.PLC_WELD_TRIGGER_REQ).data;
return (pageChangeRequestBit || weldRequestBit);
}
#region Device 및 상태 확인 함수 (시퀸스 공용)
private bool InitSequenceVariable(SequenceContext context)
{
int recipeNo = _PLC.ReadWord((int)eWordInput.PLC_PAGE_NO).data;
if (recipeNo == 1)
{
}
//_RecipeDrawObjects = _ScannerCanvasUserCtrl.Model.Layers[0].Objects;
//_WeldingScannerListNo = eListNo.LIST1;
return true;
}
private void InitDev(SequenceContext context)
{
//Device Init
//_IODevice = context.GetIOUnit();
_IOLaser = VisionEnv.AuroraEnv.DeviceManager[CommonDeviceIds.IODevice] as LaserIOControl;
_Scanner = context.GetLaserScanner();
_PLC = context.GetPLC();
}
private bool CheckDeviceReady(SequenceContext context)
{
List<string> deviceListToCheck = new List<string>() { CommonDeviceIds.PLCDevice };
bool isAllDeviceActivated = true;
foreach (string item in deviceListToCheck)
{
if (!context.Devices[item].IsActivated)
{
isAllDeviceActivated = isAllDeviceActivated && false;
}
}
return isAllDeviceActivated;
}
private bool CheckCondition(SequenceContext context)
{
if (CheckCondition_Interlock())
{
if (InitializeDeviceStatus(context))
{
return true;
}
}
return false;
}
private bool InitializeDeviceStatus(SequenceContext context)
{
// format 유지, ReceiveCellInfo 없음
return true;
}
private bool CheckCondition_Interlock()
{
// PLC, Data, IO 등 Interlock checking
return true;
}
#endregion
}
public class SeqTestRun_InitParam : InitParam
{
public short CurrentPageNo { get; set; }
public bool NeedPageNo { get; set; }
public DataModel Models { get; set; }
public int TimeoutSec { get; set; }
}
public class SeqTestRun_OutParam : OutParam
{
public bool IsComplete { get; set; }
public short PLCPageNo { get; set; }
public DateTime _WeldReqTime { get; set; }
}
}