- [Line.cs] LargeMOTFPositionX/Y 프로퍼티 추가 ([XmlSerializable]) - [DrawingLayer.cs] LargeMOTFOffsetX/Y 프로퍼티 추가 ([XmlSerializable]) - [RecipeWeldingLines.cs] UseLargeMOTF, LargeMOTFPageCount 프로퍼티 추가 ([XmlSerializable]) - Load() 에 XmlUtil.ParseProperties() 추가하여 로드 시 반영 - [MainPage.cs] - UpdateLargeMOTFPosition() 추가: 저장 직전 Layer별 마지막 Line PF2를 LargeMOTFPositionX/Y에 자동 SET - OnRecipeReloaded 이벤트 추가: 레시피 로드 완료 시 콜백 - GenerateFlippedPages() 추가: EndSet 시 Page3(P2 Y플립), Page4(P1 Y플립) 자동 생성 - [UCScannerObjectTeachingViewModel.cs] - OnSaved(): UpdateLargeMOTFPosition, UseLargeMOTF/LargeMOTFPageCount 저장 직전 반영 - OnChanged(), Reloaded(): 레시피 로드 시 IsUseLargeMOTF, LargeMOTFPageCount UI 반영 - 생성자: OnRecipeReloaded 이벤트 등록하여 프로그램 시작 시 UI 반영 - [UCScannerObjectTeachingControlBoxViewModel.cs] - PerformCmdLargeMOTFEndSet(): LargeMOTFOffsetX 계산 후 GenerateFlippedPages() 호출 - OnRecipeReloaded 시 LargeMOTFOffsetX UI 반영 (Layer[1] 기준)
1076 lines
46 KiB
C#
1076 lines
46 KiB
C#
using Aurora;
|
|
using Aurora.Api;
|
|
using Aurora.Api.Devices;
|
|
using Aurora.Api.Manager;
|
|
using Aurora.View;
|
|
using AuroraCellTracker;
|
|
using AuroraIO;
|
|
using AuroraLaser;
|
|
using AuroraLaser.Trumpf;
|
|
using AuroraLaserScanner;
|
|
using AuroraLBAD;
|
|
using AuroraLight;
|
|
using AuroraOCT;
|
|
using AuroraPLC;
|
|
using CoreUnit.Control.UserControl.Canvas;
|
|
using CoreUnit.Scanner.UserControl.ScannerCanvas;
|
|
using DB;
|
|
using DB.Recipe;
|
|
using Hermes;
|
|
using Hermes.Functional.Option;
|
|
using ILSOS_PrismaticScanner.Device;
|
|
using ILSOS_PrismaticScanner.Models;
|
|
using Matrox.MatroxImagingLibrary;
|
|
using MilCommon;
|
|
using MilUI;
|
|
using OpenCvSharp;
|
|
using OpenCvSharp.Dnn;
|
|
using OpenCvSharp.WpfExtensions;
|
|
using System;
|
|
using System.CodeDom.Compiler;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Configuration;
|
|
using System.Data;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.IO.Pipes;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices.ComTypes;
|
|
using System.Runtime.Remoting.Contexts;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Forms.VisualStyles;
|
|
using System.Windows.Media.Imaging;
|
|
using VisionCommon;
|
|
using WeldingCommon;
|
|
using WeldingCommon.Alarm;
|
|
using WeldingCommon.Data;
|
|
using WeldingCommon.Devices;
|
|
using WeldingCommon.Devices.ACS;
|
|
using WeldingCommon.Models;
|
|
using WeldingCommon.Sequences;
|
|
using WeldingCommon.Sequences.Main;
|
|
using WeldingCommon.Settings;
|
|
using WeldingCommon.Utils;
|
|
using WeldingCommon.Views;
|
|
using WeldingCommon.Vision.WeldingObjectGenerator;
|
|
using static Aurora.Api.SequenceBuilder;
|
|
using static AuroraIO.AJINEXTEK.AjinIO.IOModuleInfo;
|
|
using static AuroraIO.Vendor.PAIX.PAIXIO;
|
|
using static ILSOS_PrismaticScanner.Device.PLCControllerPrismaticScanner;
|
|
using static WeldingCommon.Devices.PLCController;
|
|
using static WeldingCommon.Sequences.Main.SeqExecutePlcBitInterface_InitParam;
|
|
|
|
namespace ILSOS_PrismaticScanner.Sequences
|
|
{
|
|
/// <summary>
|
|
/// ACS 모션 + 갈바노 스캐너 MOTF 자동운전 시퀀스
|
|
/// 기존 SeqAutoRunPrismaticScanner 기반으로 작성
|
|
/// - 비전 보상 / 이미지 저장 제거 (MOTF 구조상 불필요)
|
|
/// - InitFly2D(엔코더 리셋) 노드 추가
|
|
/// - Object 리스트를 List1 / List2 로 분리하여 각각 MOTF 용접 수행
|
|
/// 작성일: 26.04.09 NDY
|
|
/// </summary>
|
|
class SeqAutoRunPrismaticScannerACS
|
|
{
|
|
static readonly Logger Log = new Logger("SeqAutoRunPrismaticScannerACS");
|
|
|
|
#region 시퀀스 노드 이름
|
|
// 기존 SeqAutoRunPrismaticScanner 노드명과 동일하게 유지
|
|
const string SEQ_ENTER = "SEQ_ENTER";
|
|
const string SEQ_CHECK_INIT_CONDITION = "Check Init Condition";
|
|
|
|
const string SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ = "Set Sequence Param (ExecutePlcPageChangeOrWeldRequestInterface Seq)";
|
|
const string SEQ_WAIT_CHANGE_PAGE_OR_WELD_REQUEST = "Process (Wait Page Change Request Or Weld Request)";
|
|
const string SEQ_GET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ = "Get Sequence Result (ExecutePlcPageChangeOrWeldRequestInterface Seq)";
|
|
|
|
const string SEQ_CHECK_WELDDING_RETRY = "Check Welding Retry";
|
|
|
|
const string SEQ_GENERATE_WELDING_OBJECT = "Generate Welding Object";
|
|
|
|
const string SEQ_SET_PARAMS_COMPONENT_TEACHINGRUN_SEQ = "Set Params Component Teachingrun Seq";
|
|
const string SEQ_DO_ID_COMPONENT_TEACHINGRUN_SEQ = "Process (Component Teachingrun Seq)";
|
|
const string SEQ_GET_RESULT_COMPONENT_TEACHINGRUN_SEQ = "Get Result Component Teachingrun Seq";
|
|
|
|
const string SEQ_GET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ = "Get Sequence Result (PLC Weld Complete Interface Seq)";
|
|
const string SEQ_DO_PLC_WELD_COMPLETE_INTERFACE_SEQ = "Process (PLC Weld Complete Interface Seq)";
|
|
const string SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ = "Set Sequence Param (PLC Weld Complete Interface Seq)";
|
|
|
|
const string SEQ_SAVE_RESULT_DATA = "Save Result Data";
|
|
const string SEQ_COMPLETE = "SEQ_COMPLETE";
|
|
|
|
#region 26.04.09 NDY - MOTF 추가 노드
|
|
// 기존 노드에 없던 ACS MOTF 전용 노드
|
|
// 노드명 뒤에 "ACS" 또는 "1"/"2" 붙여서 기존과 구분
|
|
|
|
// [MOTF Step 1] Linear 원점에서 엔코더 리셋
|
|
const string SEQ_RESET_ENCODER_ACS = "Reset Encoder ACS (InitFly2D)";
|
|
|
|
// [MOTF Step 3] 1번 Object 리스트 오프셋 이동
|
|
const string SEQ_MOVE_OBJECT_LIST1_ACS = "Move Object List1 ACS (Scanner Center Offset)";
|
|
|
|
// [MOTF Step 5] 2번 Object 리스트 세팅 및 용접
|
|
const string SEQ_SET_PARAMS_COMPONENT_TEACHINGRUN_SEQ2 = "Set Params Component Teachingrun Seq2";
|
|
const string SEQ_DO_ID_COMPONENT_TEACHINGRUN_SEQ2 = "Process (Component Teachingrun Seq2)";
|
|
const string SEQ_GET_RESULT_COMPONENT_TEACHINGRUN_SEQ2 = "Get Result Component Teachingrun Seq2";
|
|
|
|
// [MOTF Step 6] 2번 Object 리스트 오프셋 이동
|
|
const string SEQ_MOVE_OBJECT_LIST2_ACS = "Move Object List2 ACS (Scanner Center Offset)";
|
|
#endregion
|
|
|
|
#region 26.04.10 NDY - 시뮬레이션 우회 전용 노드
|
|
// PLC 서브시퀀스를 개발 환경에서 스킵하기 위한 노드
|
|
// 실제 장비에서는 이 노드를 타지 않음
|
|
const string SEQ_SKIP_PLC_WELD_COMPLETE_ACS = "Skip PLC WeldComplete ACS (Simulation)";
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
static class ContextKey
|
|
{
|
|
// 기존과 동일한 패턴, 클래스명만 ACS로 변경
|
|
public const string KEY_SEQUENCE_RESULT = "KEY_SEQUENCE_RESULT_SeqAutoRunPrismaticScannerACS";
|
|
public const string KEY_SEQUENCE_START_TIME = "KEY_SEQUENCE_START_TIME_SeqAutoRunPrismaticScannerACS";
|
|
}
|
|
|
|
#region 변수 정의
|
|
|
|
#region 26.04.10 NDY - 시뮬레이션 1사이클 테스트용
|
|
// true: 1사이클 후 루프 중단, false: 기존처럼 계속 루프
|
|
private bool _IsOneCycleTest = true;
|
|
#endregion
|
|
|
|
// 기존 SeqAutoRunPrismaticScanner 변수 그대로 유지
|
|
|
|
// 디바이스 정의
|
|
private Scanner _Scanner;
|
|
private Laser _Laser;
|
|
private PLCControllerPrismaticScanner _PLC;
|
|
private IODevice _IO;
|
|
private LightDevice _Light;
|
|
|
|
// 비전 정의
|
|
// MOTF에서는 비전 보상을 사용하지 않으나, 향후 확장 가능성을 위해 선언 유지
|
|
private CameraAgentBase _CameraAgent;
|
|
private ICameraDisplay _CameraDisplay;
|
|
private PatternAgentBase _PatternAgent;
|
|
private List<PatternInfo> _Patterns;
|
|
private PatternInfo _PatternInfo;
|
|
private PatternResult _PatternResult;
|
|
private FieldScanParams _FieldScanParams;
|
|
private object _GrabImage;
|
|
private MIL_ID _GrabImageMilId;
|
|
private int _RetryCount = 0;
|
|
private double _OffsetXfromModelFinder = 0;
|
|
private double _OffsetYfromModelFinder = 0;
|
|
|
|
// 시스템 및 용접 파라미터 정의
|
|
private SystemParameter _SystemParameter;
|
|
private static SequenceSetting _SequenceSetting;
|
|
private ScannerCanvasUserCtrl _ScannerCanvasUserCtrl;
|
|
private List<IDrawObject> _SelectedDrawObjectList;
|
|
private static List<IDrawObject> _UnpackDrawObject;
|
|
|
|
// InitParam 타입만 ACS용으로 변경, 나머지 구조 동일
|
|
private SeqAutoRunPrismaticScannerACS_InitParam _InitParam;
|
|
|
|
// 생산 데이터 정의
|
|
private uint _TodayProduct;
|
|
private uint _TotalProduct;
|
|
|
|
private DateTime _AutoRunStartTime;
|
|
private DateTime _WeldStartTime;
|
|
private DateTime _WeldEndTime;
|
|
private TimeSpan _WeldSpanTime;
|
|
private TimeSpan _InterfaceSpanTime;
|
|
private DateTime _InterfaceStartTime;
|
|
|
|
private string _LotId = "N/A";
|
|
private string _CellId = "N/A";
|
|
const int timeoutSeconds = 10;
|
|
|
|
private bool _FirstUpdateStep = true;
|
|
private bool _IsWeldingRetry = false;
|
|
private int _CurrentWeldingStep = 0;
|
|
private int _TotalWeldingStep = 0;
|
|
private int _PreviousTotalWeldingStep = 0;
|
|
private int _PageNo = 1;
|
|
private MIL_ID _ResultImageMilId;
|
|
private bool _SuccessVisionResult = false;
|
|
const int PLC_TIMEOUT = 60;
|
|
|
|
public string EVENT_TRIGGER_SAVE_FILE = "EventTriggerSaveFile";
|
|
|
|
// 공정 시간 측정
|
|
private static DateTime _WeldReqTime;
|
|
private static DateTime _WeldComTime;
|
|
|
|
private List<int> lightValues = new List<int> { 255, 255, 255, 255 };
|
|
private (double X, double Y) TargetObjectCenterPosition = (X: 0.0, Y: 0.0);
|
|
|
|
#region 26.04.09 NDY - ACS MOTF 추가 변수
|
|
// ACS 모션 컨트롤러 참조
|
|
// WeldingCommon/Devices/ACS/ScannerACS.cs 에 등록된 장치
|
|
// Stage X 축(AxisX2), Y1/Y2/Z 축 제어 및 엔코더 값 읽기에 사용
|
|
private ScannerACS _ScannerACS;
|
|
|
|
// MOTF용 Object 리스트 분리
|
|
// 이미지 기준:
|
|
// List1 - Page1 끝점 ~ Page2 시작점 (RM 용접, Virtual Line 포함)
|
|
// List2 - Page3 끝점 ~ Page4 시작점 (LM 용접, Virtual Line 포함)
|
|
// 기존 _SelectedDrawObjectList 명명 패턴 유지, 숫자로 구분
|
|
private List<IDrawObject> _SelectedDrawObjectList1;
|
|
private List<IDrawObject> _SelectedDrawObjectList2;
|
|
|
|
// 각 Object 리스트별 스캐너 오프셋
|
|
// 스캐너 중심(50, 0) ~ Linear 원점(0, 0) 간 거리만큼 Object 좌표를 이동
|
|
// → WaitForEncoderX가 RM(25,0) / LM(75,0) 시작점에서 정확히 트리거되도록 보장
|
|
// TODO: 이정인 선임 ACS 매핑 파일 수령 후 실제 수치로 확정 필요
|
|
private double _ObjectList1OffsetX = 0;
|
|
private double _ObjectList1OffsetY = 0;
|
|
private double _ObjectList2OffsetX = 0;
|
|
private double _ObjectList2OffsetY = 0;
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
public SequenceBuilder Builder()
|
|
{
|
|
var sb = new SequenceBuilder(1000, true);
|
|
|
|
// 기존 노드 등록 (SeqAutoRunPrismaticScanner와 동일 구조)
|
|
sb.AddAction(SEQ_ENTER, Sequence_Enter);
|
|
sb.AddDecision(SEQ_CHECK_INIT_CONDITION, CheckInitCondition);
|
|
|
|
#region 26.04.10 NDY - PLC WeldRequest : Action → Decision 변경
|
|
// 기존: sb.AddAction(SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ, ...)
|
|
// 변경 이유: 시뮬레이션 환경에서 서브시퀀스(SEQ_WAIT_CHANGE_PAGE_OR_WELD_REQUEST)를
|
|
// 타지 않도록 Decision 분기 처리가 필요
|
|
// 실제 장비: SEQ_WAIT_CHANGE_PAGE_OR_WELD_REQUEST → SEQ_GET_PARAMS → SEQ_CHECK_WELDDING_RETRY
|
|
// 시뮬레이션: 서브시퀀스 스킵 → 바로 SEQ_CHECK_WELDDING_RETRY
|
|
sb.AddDecision(SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ, SetPlcPageChangeOrWeldRequest_SeqParams);
|
|
#endregion
|
|
|
|
sb.AddSubsequence(SEQ_WAIT_CHANGE_PAGE_OR_WELD_REQUEST, SequenceManager.GetSeqBuilder_SeqWaitWeldOrChangePage());
|
|
sb.AddAction(SEQ_GET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ, GetPlcPageChangeOrWeldRequest_SeqParams);
|
|
|
|
sb.AddDecision(SEQ_CHECK_WELDDING_RETRY, CheckWeldingRetry);
|
|
sb.AddAction(SEQ_GENERATE_WELDING_OBJECT, GenerateWeldingObject);
|
|
|
|
// List1 용접 (RM)
|
|
sb.AddAction(SEQ_SET_PARAMS_COMPONENT_TEACHINGRUN_SEQ, SetComponentTeachingrun_SeqParams);
|
|
sb.AddSubsequence(SEQ_DO_ID_COMPONENT_TEACHINGRUN_SEQ, SequenceManager.GetSeqBuilder_SeqComponentTeachingRun());
|
|
sb.AddAction(SEQ_GET_RESULT_COMPONENT_TEACHINGRUN_SEQ, GetComponentTeachingrun_SeqResult);
|
|
|
|
// List2 용접 (LM)
|
|
sb.AddAction(SEQ_SET_PARAMS_COMPONENT_TEACHINGRUN_SEQ2, SetComponentTeachingrun_SeqParams2);
|
|
sb.AddSubsequence(SEQ_DO_ID_COMPONENT_TEACHINGRUN_SEQ2, SequenceManager.GetSeqBuilder_SeqComponentTeachingRun());
|
|
sb.AddAction(SEQ_GET_RESULT_COMPONENT_TEACHINGRUN_SEQ2, GetComponentTeachingrun_SeqResult2);
|
|
|
|
#region 26.04.10 NDY - PLC WeldComplete : Action → Decision 변경
|
|
// 기존: sb.AddAction(SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ, ...)
|
|
// 변경 이유: 시뮬레이션 환경에서 서브시퀀스(SEQ_DO_PLC_WELD_COMPLETE_INTERFACE_SEQ)를
|
|
// 타지 않도록 Decision 분기 처리가 필요
|
|
// 실제 장비: SEQ_DO_PLC_WELD_COMPLETE → SEQ_GET_PARAMS → SEQ_SAVE_RESULT_DATA
|
|
// 시뮬레이션: SEQ_SKIP_PLC_WELD_COMPLETE_ACS → SEQ_SAVE_RESULT_DATA
|
|
sb.AddDecision(SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ, SetPlcWeldCompleteInterface_SeqParams);
|
|
sb.AddAction(SEQ_SKIP_PLC_WELD_COMPLETE_ACS, SkipPlcWeldComplete);
|
|
#endregion
|
|
|
|
sb.AddSubsequence(SEQ_DO_PLC_WELD_COMPLETE_INTERFACE_SEQ, SequenceManager.GetSeqBuilder_SeqExecutePlcBitInterface());
|
|
sb.AddAction(SEQ_GET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ, GetPlcWeldCompleteInterface_SeqParams);
|
|
|
|
sb.AddAction(SEQ_SAVE_RESULT_DATA, SaveResultData);
|
|
sb.AddAction(SEQ_COMPLETE, Complete);
|
|
|
|
#region 26.04.09 NDY - MOTF 추가 노드 등록
|
|
sb.AddAction(SEQ_RESET_ENCODER_ACS, ResetEncoder);
|
|
sb.AddAction(SEQ_MOVE_OBJECT_LIST1_ACS, MoveObjectList1);
|
|
sb.AddAction(SEQ_MOVE_OBJECT_LIST2_ACS, MoveObjectList2);
|
|
#endregion
|
|
|
|
// ── 메인 흐름 ──────────────────────────────────────────────
|
|
sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION]
|
|
> sb[SEQ_RESET_ENCODER_ACS]
|
|
> sb[SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ]);
|
|
|
|
#region 26.04.10 NDY - PLC WeldRequest 분기 흐름
|
|
// 실제 장비: 서브시퀀스 진입
|
|
sb.Flows(sb[SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ]
|
|
> sb[SEQ_WAIT_CHANGE_PAGE_OR_WELD_REQUEST]
|
|
> sb[SEQ_GET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ]
|
|
> sb[SEQ_CHECK_WELDDING_RETRY]);
|
|
// 시뮬레이션: 서브시퀀스 스킵 → 바로 CheckWeldingRetry
|
|
sb.Flows(sb[SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ]
|
|
> sb[SEQ_CHECK_WELDDING_RETRY]);
|
|
#endregion
|
|
|
|
// CheckWeldingRetry 이후 공통 흐름
|
|
sb.Flows(sb[SEQ_CHECK_WELDDING_RETRY]
|
|
> sb[SEQ_GENERATE_WELDING_OBJECT]
|
|
> sb[SEQ_MOVE_OBJECT_LIST1_ACS]
|
|
> sb[SEQ_SET_PARAMS_COMPONENT_TEACHINGRUN_SEQ]
|
|
> sb[SEQ_DO_ID_COMPONENT_TEACHINGRUN_SEQ]
|
|
> sb[SEQ_GET_RESULT_COMPONENT_TEACHINGRUN_SEQ]
|
|
> sb[SEQ_MOVE_OBJECT_LIST2_ACS]
|
|
> sb[SEQ_SET_PARAMS_COMPONENT_TEACHINGRUN_SEQ2]
|
|
> sb[SEQ_DO_ID_COMPONENT_TEACHINGRUN_SEQ2]
|
|
> sb[SEQ_GET_RESULT_COMPONENT_TEACHINGRUN_SEQ2]
|
|
> sb[SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ]);
|
|
|
|
// 재시도: Object 생성 스킵하고 List1 이동으로
|
|
sb.Flows(sb[SEQ_CHECK_WELDDING_RETRY] > sb[SEQ_MOVE_OBJECT_LIST1_ACS]);
|
|
|
|
#region 26.04.10 NDY - PLC WeldComplete 분기 흐름
|
|
// 실제 장비: 서브시퀀스 진입 → 결과 수신 → SaveResultData
|
|
sb.Flows(sb[SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ]
|
|
> sb[SEQ_DO_PLC_WELD_COMPLETE_INTERFACE_SEQ]
|
|
> sb[SEQ_GET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ]
|
|
> sb[SEQ_SAVE_RESULT_DATA]);
|
|
// 시뮬레이션: 서브시퀀스 스킵 → SkipNode → SaveResultData
|
|
sb.Flows(sb[SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ]
|
|
> sb[SEQ_SKIP_PLC_WELD_COMPLETE_ACS]
|
|
> sb[SEQ_SAVE_RESULT_DATA]);
|
|
#endregion
|
|
|
|
// 결과 저장 후 PLC 요청 대기로 루프
|
|
sb.Flows(sb[SEQ_SAVE_RESULT_DATA]
|
|
> sb[SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ]);
|
|
|
|
// CheckInitCondition 실패 시 종료
|
|
sb.Flows(sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_COMPLETE] > sb.Final);
|
|
|
|
// 26.04.10 1사이클만 체크하고 빠져나오는 시퀸스로 테스트용 추가 Builder()에 추가 --
|
|
sb.Flows(sb[SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ]
|
|
> sb[SEQ_COMPLETE]);
|
|
|
|
sb.OnForceError(OnForceError);
|
|
sb.OnStopped(OnStopped);
|
|
|
|
//sb.Draw("SeqAutoRunPrismaticScannerACS.pdf");
|
|
|
|
return sb;
|
|
}
|
|
|
|
private string CheckWeldingRetry(SequenceContext context, string id, ICollection<string> branches, SequenceActionResult param)
|
|
{
|
|
// 기존 SeqAutoRunPrismaticScanner와 동일 로직
|
|
if (_IsWeldingRetry) return SEQ_MOVE_OBJECT_LIST1_ACS;
|
|
else return SEQ_GENERATE_WELDING_OBJECT;
|
|
}
|
|
|
|
#region 시퀀스 함수
|
|
|
|
#region 1. 디바이스 및 변수 상태 확인
|
|
private SequenceActionResult Sequence_Enter(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
Log.I("SeqAutoRunPrismaticScannerACS Start");
|
|
|
|
_InitParam = param.Param as SeqAutoRunPrismaticScannerACS_InitParam;
|
|
_SystemParameter = context.GetSetting<SystemParameter>(CommonSettingIds.SystemParameter);
|
|
_SequenceSetting = context.GetSetting<SequenceSetting>(CommonSettingIds.Sequence);
|
|
|
|
InitDev(context);
|
|
if (!CheckDeviceReady(context))
|
|
{
|
|
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_AutoRun_DeviceNotReady);
|
|
Log.E("The devices are not active.");
|
|
return null;
|
|
}
|
|
|
|
if (InitSequenceVariable(context) == false)
|
|
{
|
|
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_AutoRun_InitVariableError);
|
|
return null;
|
|
}
|
|
|
|
_PageNo = _SequenceSetting.LastPageNo;
|
|
Log.I($"Previous Page No is {_PageNo}");
|
|
|
|
return SequenceAction.Ok();
|
|
}
|
|
|
|
private string CheckInitCondition(SequenceContext context, string id, ICollection<string> branches, SequenceActionResult param)
|
|
{
|
|
Status.SetWorkingPosition(eWorkingPosition.CHECKING_CONDITION);
|
|
|
|
if (!CheckCondition(context))
|
|
{
|
|
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_AutoRun_CheckInitCondition);
|
|
Log.E("Condition is not in state to start sequence.");
|
|
context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, false);
|
|
return SEQ_COMPLETE;
|
|
}
|
|
|
|
var startTime = DateTime.Now;
|
|
context.PutValue(ContextKey.KEY_SEQUENCE_START_TIME, startTime);
|
|
|
|
#region 26.04.10 NDY - 시뮬레이션 환경 우회
|
|
if (ApplicationExt.IsSimulation)
|
|
{
|
|
Log.I("Simulation Mode - Skip Laser/PLC CheckInitCondition");
|
|
return SEQ_RESET_ENCODER_ACS;
|
|
}
|
|
#endregion
|
|
|
|
// 레이저 레디 확인 (기존과 동일)
|
|
if (!_Laser.GetLaserReady())
|
|
{
|
|
_Laser.LaserOn();
|
|
Log.I("Start Laser ON in CheckInitCondition");
|
|
}
|
|
var res = context.RetryUntilSuccess(_Laser.GetLaserReady, timeoutSeconds * 1000, 10);
|
|
if (res == E2.TIMEOUT)
|
|
{
|
|
Log.E("Laser Ready Fail When Autorun");
|
|
return null;
|
|
}
|
|
|
|
InitializePlcBits();
|
|
_PLC.WriteBit(eBitOutput.PC_AUTO_STATUS, true);
|
|
_PLC.WriteWord(eWordOutput.PC_PAGE_NO, (short)_PageNo);
|
|
|
|
Log.I("Complete CheckInitCondition.");
|
|
return SEQ_RESET_ENCODER_ACS;
|
|
}
|
|
#endregion
|
|
|
|
#region 26.04.09 NDY - MOTF Step 1 : 엔코더 리셋
|
|
/// <summary>
|
|
/// [MOTF Step 1]
|
|
/// 자동운전 시작 시 Linear 원점 위치에서 InitFly2D() 호출
|
|
/// → 스캐너 내부 X 엔코더 값을 0으로 초기화
|
|
/// → 이후 SeqUploadWeldingList 내 WaitForEncoderX가
|
|
/// RM(25,0) / LM(75,0) 시작점에서 정확히 트리거되도록 보장
|
|
/// </summary>
|
|
private SequenceActionResult ResetEncoder(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
Log.I("MOTF Step1: InitFly2D - Encoder Reset at Linear Origin");
|
|
_Scanner.InitFly2D();
|
|
return SequenceAction.Ok();
|
|
}
|
|
#endregion
|
|
|
|
#region 2. PLC 페이지 변경 / 용접 요청 대기
|
|
|
|
#region 26.04.10 NDY - Action → Decision 변경 (PLC WeldRequest)
|
|
/// <summary>
|
|
/// 기존 Action에서 Decision으로 변경
|
|
/// - 시뮬레이션: SEQ_CHECK_WELDDING_RETRY 로 직접 분기 (서브시퀀스 완전 스킵)
|
|
/// - 실제 장비: SEQ_WAIT_CHANGE_PAGE_OR_WELD_REQUEST 서브시퀀스로 진입
|
|
/// </summary>
|
|
private string SetPlcPageChangeOrWeldRequest_SeqParams(SequenceContext context, string id, ICollection<string> branches, SequenceActionResult param)
|
|
{
|
|
if (ApplicationExt.IsSimulation)
|
|
{
|
|
#region 26.04.10 NDY - 1사이클 테스트
|
|
if (_IsOneCycleTest && !_FirstUpdateStep)
|
|
{
|
|
// 첫 사이클 이후 루프 진입 시 종료
|
|
Log.I("Simulation Mode - 1 Cycle Test Complete, Stop Loop");
|
|
return SEQ_COMPLETE;
|
|
}
|
|
#endregion
|
|
|
|
Log.I("Simulation Mode - Skip PLC WeldRequest, proceed to CheckWeldingRetry");
|
|
_PageNo = 1;
|
|
_FirstUpdateStep = false;
|
|
_TotalWeldingStep = 1;
|
|
return SEQ_CHECK_WELDDING_RETRY;
|
|
}
|
|
|
|
// 기존 SeqAutoRunPrismaticScanner와 동일
|
|
Log.I(string.Format("PLC Interface(PageChange, WeldReq) Start; (REQ,ACK); ({0},{1});({2},{3})",
|
|
(int)eBitInput.PLC_PAGE_CHANGE_REQ, (int)eBitOutput.PC_PAGE_CHANGE_COMPLETE,
|
|
(int)eBitInput.PLC_WELD_READY_REQ, (int)eBitOutput.PC_WELD_READY_ACK));
|
|
|
|
Status.SetWorkingPosition(eWorkingPosition.LOADING_CELL);
|
|
return SEQ_WAIT_CHANGE_PAGE_OR_WELD_REQUEST;
|
|
}
|
|
#endregion
|
|
|
|
private SequenceActionResult GetPlcPageChangeOrWeldRequest_SeqParams(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
#region 26.04.10 NDY - 시뮬레이션 환경 우회
|
|
if (ApplicationExt.IsSimulation)
|
|
{
|
|
Log.I("Simulation Mode - Skip PLC WeldRequest Result");
|
|
return SequenceAction.Ok();
|
|
}
|
|
#endregion
|
|
|
|
// 기존 SeqAutoRunPrismaticScanner와 동일
|
|
var resultOutParam = param.Param as SeqWaitWeldOrChangePage_OutParam;
|
|
_WeldReqTime = resultOutParam._WeldReqTime;
|
|
if (resultOutParam.Success == false)
|
|
{
|
|
Log.E("Failed Page Change & Weld Request Interface");
|
|
return null;
|
|
}
|
|
|
|
_PreviousTotalWeldingStep = _TotalWeldingStep;
|
|
_PageNo = resultOutParam.PLCPageNo;
|
|
_TotalWeldingStep = Convert.ToInt16(_PLC.ReadWord(eWordInput.PLC_STEP_NO).data);
|
|
|
|
_SequenceSetting.LastPageNo = _PageNo;
|
|
context.Settings.Save(CommonSettingIds.Sequence, _SequenceSetting, "SeqAutoRunPrismaticScannerACS");
|
|
|
|
Status.GetProcessingWeldingData().IsVisionNG = false;
|
|
_PLC.WriteBit(eBitOutput.PC_WELD_NG_JUDGE, false);
|
|
var UploadDatas = Status.GetProcessingWeldingData().UploadProcessDatas;
|
|
foreach (var data in UploadDatas)
|
|
data.Clear();
|
|
_PLC.UploadReportDatas(UploadDatas);
|
|
|
|
Log.I(string.Format("Read Words; Page No: {0}; Step No: {1}", _PageNo, _TotalWeldingStep));
|
|
|
|
if (_PageNo > _ScannerCanvasUserCtrl.Model.Layers.Count() && _PageNo > 0)
|
|
{
|
|
Log.E(string.Format("Page No isn't proper. ;Page No : {0}; Layers Count: {1}", _PageNo, _ScannerCanvasUserCtrl.Model.Layers.Count()));
|
|
return null;
|
|
}
|
|
|
|
Status.GetProcessingWeldingData().CellId = _PLC.CellId();
|
|
Status.GetProcessingWeldingData().LotId = _PLC.LotId();
|
|
|
|
if (_FirstUpdateStep)
|
|
{
|
|
_FirstUpdateStep = false;
|
|
return SequenceAction.Ok();
|
|
}
|
|
if (_PreviousTotalWeldingStep != _TotalWeldingStep)
|
|
{
|
|
Log.E(string.Format("SeqCheckWeldingComplete Error;Changed Step No; Prev: {0}; Current: {1}", _PreviousTotalWeldingStep, _TotalWeldingStep));
|
|
return null;
|
|
}
|
|
|
|
return SequenceAction.Ok();
|
|
}
|
|
#endregion
|
|
|
|
#region 3. 용접 Object 생성 및 List 분리
|
|
/// <summary>
|
|
/// [MOTF Step 2]
|
|
/// 레시피 레이어에서 Object를 꺼내 List1 / List2 로 분리
|
|
/// - _SelectedDrawObjectList : 기존과 동일하게 전체 Object 보관
|
|
/// - _SelectedDrawObjectList1 : RM 용접용 (Page1 끝~Page2 시작, Virtual Line 포함)
|
|
/// - _SelectedDrawObjectList2 : LM 용접용 (Page3 끝~Page4 시작, Virtual Line 포함)
|
|
/// TODO: 실제 분리 기준(ScannerParamID 또는 레이어 구성)은 이정인 선임 확인 후 확정
|
|
/// </summary>
|
|
private SequenceActionResult GenerateWeldingObject(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
// 기존 _SelectedDrawObjectList 유지 (전체 Object)
|
|
_SelectedDrawObjectList = _ScannerCanvasUserCtrl.Model.Layers[_PageNo - 1].Objects;
|
|
|
|
// TODO: 실제 분리 기준 확인 후 구현
|
|
// 현재는 절반으로 단순 분리 (임시)
|
|
int half = _SelectedDrawObjectList.Count / 2;
|
|
_SelectedDrawObjectList1 = _SelectedDrawObjectList.Take(half).ToList();
|
|
_SelectedDrawObjectList2 = _SelectedDrawObjectList.Skip(half).ToList();
|
|
|
|
Log.I($"GenerateWeldingObject: Total({_SelectedDrawObjectList.Count}), List1({_SelectedDrawObjectList1.Count}), List2({_SelectedDrawObjectList2.Count})");
|
|
return SequenceAction.Ok();
|
|
}
|
|
#endregion
|
|
|
|
#region 26.04.09 NDY - MOTF Step 3 : List1 오프셋 이동
|
|
/// <summary>
|
|
/// [MOTF Step 3]
|
|
/// 1번 Object 리스트를 스캐너 중심으로부터 원점위치까지 떨어진 양만큼 오프셋 이동
|
|
/// - 스캐너 중심 = (50, 0), Linear 원점 = (0, 0) → 오프셋 X = -50mm
|
|
/// - 이 오프셋은 SeqUploadWeldingList 내 CellMeasureCenterOffset으로 전달됨
|
|
/// - WaitForEncoderX가 RM 용접 시작점(25, 0)에서 트리거되도록 준비
|
|
/// TODO: ScannerConfig 또는 SystemParameter에서 스캐너 중심 좌표 가져오도록 수정
|
|
/// </summary>
|
|
private SequenceActionResult MoveObjectList1(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
_ObjectList1OffsetX = -_Scanner.Config.MarkingOntheFlyOffsetX;
|
|
_ObjectList1OffsetY = -_Scanner.Config.MarkingOntheFlyOffsetY;
|
|
|
|
Log.I($"MOTF Step3: MoveObjectList1 Offset({_ObjectList1OffsetX}, {_ObjectList1OffsetY})");
|
|
return SequenceAction.Ok();
|
|
}
|
|
#endregion
|
|
|
|
#region 4. List1 용접 (RM)
|
|
private SequenceActionResult SetComponentTeachingrun_SeqParams(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
// 기존 SeqAutoRunPrismaticScanner와 동일 구조
|
|
_PLC.WriteBit(eBitOutput.PC_WELD_BUSY, true);
|
|
Log.I("Weld Busy On ! Set ComponentTeachingrun (List1) Start.");
|
|
|
|
_WeldStartTime = DateTime.Now;
|
|
Status.GetProcessingWeldingData().WeldingStartTime = _WeldStartTime;
|
|
|
|
var currentModelInfo = EquipmentSpecificModelUtil.GetCurrentRecipeEquipmentSpecificModel();
|
|
if (currentModelInfo.equipmentSpecificModel == null)
|
|
{
|
|
MessageUtil.ShowWarningToast(string.Format("선택된 model 이 없습니다.", "Laser On"));
|
|
return null;
|
|
}
|
|
|
|
var intervalTimeBlowOn = 0;
|
|
var intervalTimeBlowOff = 0;
|
|
if (currentModelInfo.equipmentSpecificModel.UseBlow)
|
|
{
|
|
intervalTimeBlowOn = currentModelInfo.equipmentSpecificModel.IntervalTimeBlowOn;
|
|
intervalTimeBlowOff = currentModelInfo.equipmentSpecificModel.IntervalTimeBlowOff;
|
|
}
|
|
|
|
SeqTeachingRun_InitParam seqParam = new SeqTeachingRun_InitParam()
|
|
{
|
|
IsUseExternalObjects = true,
|
|
ExternalObjects = _SelectedDrawObjectList1, // List1 사용
|
|
UseStream = currentModelInfo.equipmentSpecificModel.UseStream,
|
|
UseBlow = currentModelInfo.equipmentSpecificModel.UseBlow,
|
|
IntervalTimeBlowOn = intervalTimeBlowOn,
|
|
IntervalTimeBlowOff = intervalTimeBlowOff,
|
|
NeedSetLaserTiming = true,
|
|
// MOTF 오프셋 전달 (기존 CellCenterOffset 파라미터 재활용)
|
|
CellCenterOffsetX = _ObjectList1OffsetX,
|
|
CellCenterOffsetY = _ObjectList1OffsetY,
|
|
};
|
|
|
|
Log.I("Set ComponentTeachingrun (List1) Complete!");
|
|
return SequenceAction.Ok(seqParam);
|
|
}
|
|
|
|
private SequenceActionResult GetComponentTeachingrun_SeqResult(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
var resultOutParam = param.Param as SeqTeachingRun_OutParam;
|
|
if (resultOutParam.Success == false)
|
|
{
|
|
Log.E("ComponentTeachingrun (List1) Failed.");
|
|
return null;
|
|
}
|
|
Log.I("ComponentTeachingrun (List1) Complete!");
|
|
return SequenceAction.Ok();
|
|
}
|
|
#endregion
|
|
|
|
#region 26.04.09 NDY - MOTF Step 6 : List2 오프셋 이동
|
|
/// <summary>
|
|
/// [MOTF Step 6]
|
|
/// 2번 Object 리스트를 스캐너 중심으로부터 원점위치까지 떨어진 양만큼 오프셋 이동
|
|
/// - LM 용접 시작점(75, 0)에서 WaitForEncoderX가 트리거되도록 준비
|
|
/// TODO: List2 기준 오프셋 수치 확인 후 수정
|
|
/// </summary>
|
|
private SequenceActionResult MoveObjectList2(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
_ObjectList2OffsetX = -_Scanner.Config.MarkingOntheFlyOffsetX;
|
|
_ObjectList2OffsetY = -_Scanner.Config.MarkingOntheFlyOffsetY;
|
|
|
|
Log.I($"MOTF Step6: MoveObjectList2 Offset({_ObjectList2OffsetX}, {_ObjectList2OffsetY})");
|
|
return SequenceAction.Ok();
|
|
}
|
|
#endregion
|
|
|
|
#region 26.04.09 NDY - List2 용접 (LM)
|
|
private SequenceActionResult SetComponentTeachingrun_SeqParams2(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
// List1과 동일 구조, ExternalObjects와 Offset만 List2 기준으로 변경
|
|
var currentModelInfo = EquipmentSpecificModelUtil.GetCurrentRecipeEquipmentSpecificModel();
|
|
if (currentModelInfo.equipmentSpecificModel == null)
|
|
{
|
|
MessageUtil.ShowWarningToast(string.Format("선택된 model 이 없습니다.", "Laser On"));
|
|
return null;
|
|
}
|
|
|
|
var intervalTimeBlowOn = 0;
|
|
var intervalTimeBlowOff = 0;
|
|
if (currentModelInfo.equipmentSpecificModel.UseBlow)
|
|
{
|
|
intervalTimeBlowOn = currentModelInfo.equipmentSpecificModel.IntervalTimeBlowOn;
|
|
intervalTimeBlowOff = currentModelInfo.equipmentSpecificModel.IntervalTimeBlowOff;
|
|
}
|
|
|
|
SeqTeachingRun_InitParam seqParam = new SeqTeachingRun_InitParam()
|
|
{
|
|
IsUseExternalObjects = true,
|
|
ExternalObjects = _SelectedDrawObjectList2, // List2 사용
|
|
UseStream = currentModelInfo.equipmentSpecificModel.UseStream,
|
|
UseBlow = currentModelInfo.equipmentSpecificModel.UseBlow,
|
|
IntervalTimeBlowOn = intervalTimeBlowOn,
|
|
IntervalTimeBlowOff = intervalTimeBlowOff,
|
|
NeedSetLaserTiming = true,
|
|
CellCenterOffsetX = _ObjectList2OffsetX,
|
|
CellCenterOffsetY = _ObjectList2OffsetY,
|
|
};
|
|
|
|
Log.I("Set ComponentTeachingrun (List2) Complete!");
|
|
return SequenceAction.Ok(seqParam);
|
|
}
|
|
|
|
private SequenceActionResult GetComponentTeachingrun_SeqResult2(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
var resultOutParam = param.Param as SeqTeachingRun_OutParam;
|
|
if (resultOutParam.Success == false)
|
|
{
|
|
Log.E("ComponentTeachingrun (List2) Failed.");
|
|
return null;
|
|
}
|
|
|
|
// List2 용접까지 완료되면 WELD_BUSY OFF
|
|
_PLC.WriteBit(eBitOutput.PC_WELD_BUSY, false);
|
|
Log.I("Weld Busy OFF!");
|
|
|
|
_WeldEndTime = DateTime.Now;
|
|
_WeldSpanTime = _WeldEndTime.Subtract(_WeldStartTime);
|
|
Log.I($"[TactTime] Page({_PageNo}) Total Welding Time : {_WeldSpanTime.TotalMilliseconds}msec");
|
|
|
|
return SequenceAction.Ok();
|
|
}
|
|
#endregion
|
|
|
|
#region 5. 용접 완료 PLC 인터페이스
|
|
|
|
#region 26.04.10 NDY - Action → Decision 변경 (PLC WeldComplete)
|
|
/// <summary>
|
|
/// 기존 Action에서 Decision으로 변경
|
|
/// - 시뮬레이션: SEQ_SKIP_PLC_WELD_COMPLETE_ACS 로 분기 (서브시퀀스 완전 스킵)
|
|
/// - 실제 장비: SEQ_DO_PLC_WELD_COMPLETE_INTERFACE_SEQ 서브시퀀스로 진입
|
|
/// </summary>
|
|
private string SetPlcWeldCompleteInterface_SeqParams(SequenceContext context, string id, ICollection<string> branches, SequenceActionResult param)
|
|
{
|
|
if (ApplicationExt.IsSimulation)
|
|
{
|
|
Log.I("Simulation Mode - Skip PLC WeldComplete Interface");
|
|
return SEQ_SKIP_PLC_WELD_COMPLETE_ACS;
|
|
}
|
|
|
|
// 기존 SeqAutoRunPrismaticScanner와 동일
|
|
Log.I("SeqCheckWeldingComplete Set Parameters");
|
|
return SEQ_DO_PLC_WELD_COMPLETE_INTERFACE_SEQ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 시뮬레이션 환경에서 PLC WeldComplete 서브시퀀스 스킵 시 호출
|
|
/// 실제 장비에서는 이 노드를 타지 않음
|
|
/// </summary>
|
|
private SequenceActionResult SkipPlcWeldComplete(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
Log.I("Simulation Mode - PLC WeldComplete Skipped, Update ProductCount");
|
|
_CurrentWeldingStep = 0;
|
|
_IsWeldingRetry = false;
|
|
Status.SetWorkingPosition(eWorkingPosition.UNLOADING_CELL);
|
|
UpdateProductCount(context);
|
|
UpdateProductInfoForView();
|
|
return SequenceAction.Ok();
|
|
}
|
|
#endregion
|
|
|
|
private SequenceActionResult GetPlcWeldCompleteInterface_SeqParams(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
#region 26.04.10 NDY - 시뮬레이션 환경 우회
|
|
if (ApplicationExt.IsSimulation)
|
|
{
|
|
Log.I("Simulation Mode - Skip PLC WeldComplete Result");
|
|
return SequenceAction.Ok();
|
|
}
|
|
#endregion
|
|
|
|
// 기존 SeqAutoRunPrismaticScanner와 동일
|
|
var resultOutParam = param.Param as SeqExecutePlcBitInterface_OutParam;
|
|
if (resultOutParam.Success == false)
|
|
{
|
|
Log.E("SeqCheckWeldingComplete Error;Success Failed.");
|
|
return null;
|
|
}
|
|
Log.I("SeqCheckWeldingComplete Success;Welding Complete!");
|
|
|
|
_WeldComTime = DateTime.Now;
|
|
Log.I($"***** 공정 시간 WeldPage No : {_PageNo} WeldRequest ~ WeldComplete : {_WeldComTime.Subtract(_WeldReqTime).TotalMilliseconds}ms");
|
|
_CurrentWeldingStep++;
|
|
|
|
if (_CurrentWeldingStep >= _TotalWeldingStep)
|
|
{
|
|
Log.I(string.Format("Need to Update the Cell Info;{0}/{1}", _CurrentWeldingStep, _TotalWeldingStep));
|
|
_IsWeldingRetry = false;
|
|
_CurrentWeldingStep = 0;
|
|
Status.SetWorkingPosition(eWorkingPosition.UNLOADING_CELL);
|
|
UpdateProductCount(context);
|
|
UpdateProductInfoForView();
|
|
}
|
|
else
|
|
{
|
|
Log.I(string.Format("Update Welding Step;{0}/{1}", _CurrentWeldingStep, _TotalWeldingStep));
|
|
_IsWeldingRetry = true;
|
|
}
|
|
|
|
return SequenceAction.Ok();
|
|
}
|
|
#endregion
|
|
|
|
#region 6. 결과 저장 및 종료
|
|
private SequenceActionResult SaveResultData(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
Status.SetWorkingPosition(eWorkingPosition.REPORTING_RESULT);
|
|
context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, true);
|
|
return SequenceAction.Ok();
|
|
}
|
|
|
|
private SequenceActionResult Complete(SequenceContext context, string id, SequenceActionResult param)
|
|
{
|
|
RestoreDeviceStatus();
|
|
|
|
context.TryGetValue(ContextKey.KEY_SEQUENCE_RESULT, out bool seqResult);
|
|
SeqAutoRunPrismaticScannerACS_OutParam result = new SeqAutoRunPrismaticScannerACS_OutParam() { Success = seqResult };
|
|
|
|
if (seqResult == false)
|
|
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_AutoRun_ResultFailed);
|
|
|
|
Status.SetWorkingPosition(eWorkingPosition.DONE);
|
|
Log.I($"SeqAutoRunPrismaticScannerACS Complete. Result: {seqResult}"); // ← 추가 확인용
|
|
return SequenceAction.Ok(result);
|
|
}
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region 구동 함수
|
|
|
|
private bool InitSequenceVariable(SequenceContext context)
|
|
{
|
|
var mainPage = CommonHelper.GetMainPage();
|
|
_ScannerCanvasUserCtrl = mainPage.ScannerCanvasCtrlTeaching;
|
|
|
|
// 기존과 동일
|
|
_PageNo = 1;
|
|
_CurrentWeldingStep = 0;
|
|
_PreviousTotalWeldingStep = 0;
|
|
_TotalWeldingStep = 0;
|
|
_OffsetXfromModelFinder = 0;
|
|
_OffsetYfromModelFinder = 0;
|
|
_IsWeldingRetry = false;
|
|
_FirstUpdateStep = true;
|
|
_RetryCount = 0;
|
|
|
|
#region 26.04.09 NDY - ACS 추가 변수 초기화
|
|
_SelectedDrawObjectList1 = new List<IDrawObject>();
|
|
_SelectedDrawObjectList2 = new List<IDrawObject>();
|
|
_ObjectList1OffsetX = 0;
|
|
_ObjectList1OffsetY = 0;
|
|
_ObjectList2OffsetX = 0;
|
|
_ObjectList2OffsetY = 0;
|
|
#endregion
|
|
|
|
return true;
|
|
}
|
|
|
|
private void InitDev(SequenceContext context)
|
|
{
|
|
// 기존과 동일
|
|
_Scanner = context.GetLaserScanner();
|
|
_Laser = context.GetLaser();
|
|
_PLC = context.GetPLC() as PLCControllerPrismaticScanner;
|
|
_Light = context.GetLight();
|
|
|
|
#region 26.04.09 NDY - ACS 장치 초기화
|
|
// ScannerACS: WeldingCommon/Devices/ACS/ScannerACS.cs 에 등록된 장치
|
|
// Stage X(AxisX2), Y1/Y2/Z 축 제어 및 엔코더 읽기 담당
|
|
try
|
|
{
|
|
_ScannerACS = context.Devices[CommonDeviceIds.ACS] as ScannerACS;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.E($"ScannerACS Device Init Failed: {ex.Message}");
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
private bool CheckDeviceReady(SequenceContext context)
|
|
{
|
|
#region 26.04.10 NDY - 시뮬레이션 환경 우회
|
|
// 시뮬레이션 환경에서는 장치 활성화 체크 스킵
|
|
if (ApplicationExt.IsSimulation)
|
|
{
|
|
Log.I("Simulation Mode - Skip Device Ready Check");
|
|
return true;
|
|
}
|
|
#endregion
|
|
|
|
Log.I("CheckDeviceReady Start");
|
|
|
|
// 기존과 동일
|
|
List<string> deviceListToCheck = new List<string>() {
|
|
CommonDeviceIds.Scanner,
|
|
CommonDeviceIds.Laser,
|
|
CommonDeviceIds.PLCDevice
|
|
};
|
|
|
|
bool isAllDeviceActivated = true;
|
|
foreach (string item in deviceListToCheck)
|
|
{
|
|
bool isActivated = context.Devices[item].IsActivated;
|
|
Log.I($"Device: {item}, IsActivated: {isActivated}");
|
|
if (!isActivated)
|
|
isAllDeviceActivated = false;
|
|
}
|
|
|
|
#region 26.04.09 NDY - ScannerACS 장치 활성화 확인
|
|
if (_ScannerACS == null || !_ScannerACS.IsActivated)
|
|
{
|
|
Log.E("ScannerACS is not activated.");
|
|
isAllDeviceActivated = false;
|
|
}
|
|
#endregion
|
|
|
|
Log.I($"CheckDeviceReady Result: {isAllDeviceActivated}");
|
|
return isAllDeviceActivated;
|
|
}
|
|
|
|
private bool CheckCondition(SequenceContext context)
|
|
{
|
|
if (CheckCondition_Interlock())
|
|
{
|
|
if (InitializeDeviceStatus(context))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool CheckCondition_Interlock()
|
|
{
|
|
// TODO: 필요한 인터락 조건 추가
|
|
return true;
|
|
}
|
|
|
|
private bool InitializeDeviceStatus(SequenceContext context)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
private void InitializePlcBits()
|
|
{
|
|
// 기존 SeqAutoRunPrismaticScanner와 동일
|
|
_PLC.WriteBit(eBitOutput.PC_WELD_READY_ACK, false);
|
|
_PLC.WriteBit(eBitOutput.PC_WELD_BUSY, false);
|
|
_PLC.WriteBit(eBitOutput.PC_WELD_COMPLETE, false);
|
|
_PLC.WriteBit(eBitOutput.PC_PAGE_CHANGE_COMPLETE, false);
|
|
_PLC.WriteBit(eBitOutput.PC_WELD_NG_JUDGE, false);
|
|
|
|
var UploadDatas = Status.GetProcessingWeldingData().UploadProcessDatas;
|
|
foreach (var data in UploadDatas)
|
|
data.Clear();
|
|
_PLC.UploadReportDatas(UploadDatas);
|
|
}
|
|
|
|
private bool RestoreDeviceStatus()
|
|
{
|
|
#region 26.04.10 NDY - 시뮬레이션 환경 우회
|
|
if (ApplicationExt.IsSimulation)
|
|
{
|
|
Log.I("Simulation Mode - Skip RestoreDeviceStatus");
|
|
Status.SetStatus(eEqpStatus.Manual);
|
|
return true;
|
|
}
|
|
#endregion
|
|
|
|
if (_Laser.GetLaserReady()) _Laser.LaserOff();
|
|
Status.SetStatus(eEqpStatus.Manual);
|
|
_PLC.WriteBit(eBitOutput.PC_AUTO_STATUS, false);
|
|
InitializePlcBits();
|
|
return true;
|
|
}
|
|
|
|
private void UpdateProductCount(SequenceContext context)
|
|
{
|
|
// 기존 SeqAutoRunPrismaticScanner와 동일
|
|
_SystemParameter = context.GetSetting<SystemParameter>(CommonSettingIds.SystemParameter);
|
|
|
|
_TodayProduct = _SystemParameter.TodayProductCount;
|
|
_TotalProduct = _SystemParameter.TotalProductCount;
|
|
if (_TodayProduct < uint.MaxValue) _TodayProduct++;
|
|
if (_TotalProduct < uint.MaxValue) _TotalProduct++;
|
|
_SystemParameter.TodayProductCount = _TodayProduct;
|
|
_SystemParameter.TotalProductCount = _TotalProduct;
|
|
_WeldEndTime = DateTime.Now;
|
|
_SystemParameter.LastProductTime = _WeldEndTime;
|
|
|
|
context.Settings.Save(CommonSettingIds.SystemParameter, _SystemParameter, "SeqAutoRunPrismaticScannerACS");
|
|
}
|
|
|
|
private void UpdateProductInfoForView()
|
|
{
|
|
// 기존 SeqAutoRunPrismaticScanner와 동일
|
|
Status.GetProcessingWeldingData().TotalProduct = _TotalProduct;
|
|
Status.GetProcessingWeldingData().TodayProduct = _TodayProduct;
|
|
Status.GetProcessingWeldingData().WeldingStartTime = _WeldStartTime;
|
|
Status.GetProcessingWeldingData().WeldingEndTime = _WeldEndTime;
|
|
Status.GetProcessingWeldingData().WeldingSpanTimeMilliseconds = _WeldSpanTime.TotalMilliseconds.ToString();
|
|
Status.GetProcessingWeldingData().LotId = _LotId;
|
|
Status.GetProcessingWeldingData().CellId = _CellId;
|
|
}
|
|
|
|
private static void UnpackRecipes(List<IDrawObject> recipes)
|
|
{
|
|
// 기존 SeqAutoRunPrismaticScanner와 동일
|
|
if (recipes == null) return;
|
|
|
|
for (int i = 0; i < recipes.Count; i++)
|
|
{
|
|
if (recipes[i] is CoreUnit.Control.UserControl.Canvas.Group)
|
|
{
|
|
CoreUnit.Control.UserControl.Canvas.Group subGroup = recipes[i] as CoreUnit.Control.UserControl.Canvas.Group;
|
|
List<IDrawObject> subrecipes = subGroup.SubObjects as List<IDrawObject>;
|
|
UnpackRecipes(subrecipes);
|
|
}
|
|
else
|
|
{
|
|
_UnpackDrawObject.Add(recipes[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnForceError(SequenceContext arg1, string arg2)
|
|
{
|
|
OnSeqActionStop();
|
|
Log.E("SeqAutoRunPrismaticScannerACS OnForceError");
|
|
}
|
|
|
|
private void OnStopped(SequenceContext obj)
|
|
{
|
|
OnSeqActionStop();
|
|
Log.E("SeqAutoRunPrismaticScannerACS OnStopped");
|
|
}
|
|
|
|
private void OnSeqActionStop()
|
|
{
|
|
#region 26.04.10 NDY - 시뮬레이션 환경 우회
|
|
if (ApplicationExt.IsSimulation)
|
|
{
|
|
Log.I("Simulation Mode - Skip OnSeqActionStop");
|
|
Status.SetStatus(eEqpStatus.Manual);
|
|
return;
|
|
}
|
|
#endregion
|
|
|
|
// 기존 SeqAutoRunPrismaticScanner와 동일
|
|
if (_Laser.GetLaserReady()) _Laser.LaserOff();
|
|
Status.SetStatus(eEqpStatus.Manual);
|
|
_PLC.WriteBit(eBitOutput.PC_AUTO_STATUS, false);
|
|
InitializePlcBits();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public class SeqAutoRunPrismaticScannerACS_InitParam : InitParam
|
|
{
|
|
// 기존 SeqAutoRunPrismaticScanner_InitParam 패턴 유지
|
|
// 추후 MOTF 관련 파라미터 추가 가능
|
|
}
|
|
|
|
public class SeqAutoRunPrismaticScannerACS_OutParam : OutParam
|
|
{
|
|
}
|
|
} |