diff --git a/Machines/ILSOS_CCW_H52A/Datas/CellInfoScheduler.cs b/Machines/ILSOS_CCW_H52A/Datas/CellInfoScheduler.cs
new file mode 100644
index 0000000..9ac1abf
--- /dev/null
+++ b/Machines/ILSOS_CCW_H52A/Datas/CellInfoScheduler.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+using ILSOS_CCW_H52A.Sequences;
+using ILSOS_CCW_H52A.Data;
+
+namespace ILSOS_CCW_H52A.Data
+{
+ // 물리 지그 번호 (0-based enum, 1-based 변환: (eJigNo)(jigNo - 1))
+ public enum eJigNo
+ {
+ Jig1, Jig2, Jig3, Jig4,
+ Jig5, Jig6, Jig7, Jig8,
+ Jig9, Jig10, Jig11, Jig12,
+ Jig13, Jig14, Jig15, Jig16,
+ }
+
+ ///
+ /// Dictionary<eJigNo, JigInfo> 상속으로 물리 지그 번호 직접 접근과
+ /// 파이프라인 단계 접근을 동시에 지원하는 셀 스케줄러.
+ ///
+ /// - 물리 지그 접근: scheduler[eJigNo.Jig5]
+ /// - 파이프라인 접근: scheduler.Prefetch1, scheduler.Prefetch2
+ ///
+ /// 3단계 파이프라인: Prefetch2 → Prefetch1 → ReportTarget
+ /// Prefetch2 : 셀 정보 수신 완료, 스캐너 리스트 업로드 전
+ /// Prefetch1 : 업로드 완료, 실행 중 — EncoderMonitor 감시 + 완료 체크 대상
+ /// ReportTarget: 완료, 보고 대기
+ ///
+ /// Shift()는 Prefetch2→Prefetch1만 전진. GC 할당 없음.
+ /// Prefetch1 ← Prefetch2 ← this[nextJig]
+ ///
+ public class CellInfoScheduler : Dictionary
+ {
+ public JigInfo Prefetch2 { get; private set; } // 전전 셀: 셀 정보 수신, 업로드 전
+ public JigInfo Prefetch1 { get; private set; } // 전 셀: 업로드 완료, 실행 중 / 완료 체크 대상
+
+ /// 현재 보고 대상 JigInfo (CheckScannerListComplete에서 지정, 보고 완료 후 null)
+ public JigInfo ReportTarget { get; set; }
+
+ // 재시작 보존용 독립 슬롯 — Reset()에서 건드리지 않음
+ private readonly JigInfo _pendingSlot = new JigInfo();
+
+ // 재시작 전 미완료 보고 슬롯 (유효 시 non-null)
+ public JigInfo PendingComplete => HasPendingComplete ? _pendingSlot : null;
+
+ // 재시작 보존 보고가 있는지
+ public bool HasPendingComplete
+ => _pendingSlot.IsCanExist && !_pendingSlot.ReportComplete;
+
+ // 현재 활성 보고 슬롯 — 재시작 pending 우선
+ public JigInfo ActiveReportSlot
+ => HasPendingComplete ? _pendingSlot : ReportTarget;
+
+ // 보고가 필요한 상태인지 (정상 + 재시작 모두 포함)
+ public bool HasReport
+ => HasPendingComplete
+ || (ReportTarget != null && !ReportTarget.ReportComplete);
+
+ public CellInfoScheduler()
+ {
+ foreach (eJigNo no in Enum.GetValues(typeof(eJigNo)))
+ Add(no, new JigInfo());
+ }
+
+ // 1-based jigNo (_WeldingJigNo 등) → 0-based eJigNo 변환 편의 인덱서
+ public JigInfo this[int jigNo] {
+ get => base[(eJigNo)(jigNo - 1)];
+ }
+
+ // Reset() 전에 호출 — 미완료 보고가 있으면 보류 슬롯(_pendingSlot)에 복사하여 보존.
+ public void SavePendingComplete()
+ {
+ if (ReportTarget != null && ReportTarget.IsCanExist
+ && ReportTarget.WeldComplete && !ReportTarget.ReportComplete)
+ _pendingSlot.CopyFrom(ReportTarget);
+ }
+
+ // 보고 완료 처리 — ReportTarget 마킹 및 _pendingSlot 클리어.
+ // GetReportCellResult_SeqResult 성공 시 호출.
+ public void CompleteReport()
+ {
+ ReportTarget?.SetReportComplete(true);
+ ReportTarget = null;
+ _pendingSlot.Clear();
+ }
+
+ ///
+ /// Prefetch2→Prefetch1 한 칸 전진. GC 할당 없음.
+ /// 새 Prefetch2 슬롯은 Clear 후 반환 — 호출자가 셀 정보를 채워야 한다.
+ ///
+ /// 새 Prefetch2로 지정할 물리 지그 (eJigNo)
+ public void Shift(eJigNo nextJig)
+ {
+ Prefetch1 = Prefetch2;
+ Prefetch2 = this[nextJig];
+ Prefetch2.Clear();
+ }
+
+ ///
+ /// 전체 초기화 (AutoRun 시작 시 호출).
+ /// SavePendingComplete() 호출 후 이 메서드를 호출할 것.
+ /// _pendingSlot은 재시작 보고 보존을 위해 건드리지 않음.
+ ///
+ public void Reset()
+ {
+ foreach (var info in Values)
+ info.Clear();
+
+ Prefetch2 = null;
+ Prefetch1 = null;
+ ReportTarget = null;
+ // _pendingSlot: SavePendingComplete()가 복사해 둔 데이터를 유지
+ }
+
+ ///
+ /// 스케줄러 자체는 Clear 할 수 없도록 함
+ ///
+ public new void Clear()
+ {
+ Reset();
+ }
+ }
+}
diff --git a/Machines/ILSOS_CCW_H52A/Datas/JigInfo.cs b/Machines/ILSOS_CCW_H52A/Datas/JigInfo.cs
new file mode 100644
index 0000000..e910175
--- /dev/null
+++ b/Machines/ILSOS_CCW_H52A/Datas/JigInfo.cs
@@ -0,0 +1,150 @@
+using AuroraLaserScanner;
+using System;
+using WeldingCommon.Data;
+
+namespace ILSOS_CCW_H52A.Data
+{
+ public class JigInfo
+ {
+ // --- 지그 식별 ---
+ public int JigNo; // 물리 지그 번호 (1-based, -1: 미설정)
+
+ // --- 스캐너 리스트 파라미터 (ReadyScannerList에서 채워짐) ---
+ public eListNo ListNo;
+ public DateTime WriteTime;
+ public double StartAngle;
+ public int WeldingCount;
+
+ // --- 캔 유무 / 용접 상태 ---
+ public bool IsCanExist;
+ public bool IsWelding;
+ public bool WeldComplete;
+ public DateTime WeldStartTime;
+ public DateTime WeldEndTime;
+ public TimeSpan WeldingSpanTime; // 스캐너 리스트 실행 시간 (GetTime() 결과)
+
+ // --- CellTracker 수신 정보 (GetReceiveCellInfo_SeqResult에서 채워짐) ---
+ public string LotId;
+ public string CellId;
+
+ // --- 플래그 ---
+ public bool CheckSendCellinfo;
+ public bool CheckSendPLCLaserPower;
+ public bool ReportComplete;
+
+ // --- 리포팅/뷰 용 상세 정보 ---
+ public WeldingData InnerCellInfo;
+
+ public JigInfo()
+ {
+ InnerCellInfo = new WeldingData();
+ Clear();
+ }
+
+ public void SetReportComplete(bool comp) => ReportComplete = comp;
+
+ ///
+ /// 다른 JigInfo의 모든 필드를 이 슬롯에 복사 (재시작 보존용 pendingSlot에 사용)
+ ///
+ public void CopyFrom(JigInfo src)
+ {
+ JigNo = src.JigNo;
+ ListNo = src.ListNo;
+ WriteTime = src.WriteTime;
+ StartAngle = src.StartAngle;
+ WeldingCount = src.WeldingCount;
+ IsCanExist = src.IsCanExist;
+ IsWelding = src.IsWelding;
+ WeldComplete = src.WeldComplete;
+ WeldStartTime = src.WeldStartTime;
+ WeldEndTime = src.WeldEndTime;
+ WeldingSpanTime = src.WeldingSpanTime;
+ LotId = src.LotId;
+ CellId = src.CellId;
+ CheckSendCellinfo = src.CheckSendCellinfo;
+ CheckSendPLCLaserPower = src.CheckSendPLCLaserPower;
+ ReportComplete = src.ReportComplete;
+ UpdateCellInfo(src.InnerCellInfo);
+ }
+
+ ///
+ /// 슬롯 전체 초기화 (Shift 시 새 Prefetch2 슬롯에 자동 호출됨)
+ ///
+ public void Clear()
+ {
+ JigNo = -1;
+ ListNo = 0;
+ WriteTime = DateTime.MinValue;
+ StartAngle = 0.0;
+ WeldingCount = -1;
+
+ IsCanExist = false;
+ IsWelding = false;
+ WeldComplete = false;
+ WeldStartTime = DateTime.MinValue;
+ WeldEndTime = DateTime.MinValue;
+ WeldingSpanTime = TimeSpan.Zero;
+
+ LotId = string.Empty;
+ CellId = string.Empty;
+
+ CheckSendCellinfo = false;
+ CheckSendPLCLaserPower = false;
+ ReportComplete = false;
+
+ InnerCellInfo.CellId = string.Empty;
+ InnerCellInfo.LotId = string.Empty;
+ InnerCellInfo.AutoRunStartTime = DateTime.MinValue;
+ InnerCellInfo.WeldingStartTime = DateTime.MinValue;
+ InnerCellInfo.WeldingEndTime = DateTime.MinValue;
+ InnerCellInfo.WeldingSpanTimeMilliseconds = string.Empty;
+ InnerCellInfo.TodayProduct = 0;
+ InnerCellInfo.TotalProduct = 0;
+ InnerCellInfo.RecipeNo = 0;
+ InnerCellInfo.WeldStartTime = string.Empty;
+ InnerCellInfo.WeldTime = string.Empty;
+ InnerCellInfo.WorkCellId = string.Empty;
+ InnerCellInfo.WorkLotId = string.Empty;
+ InnerCellInfo.LBAD_Judge = string.Empty;
+ InnerCellInfo.LDD_Judge = string.Empty;
+ InnerCellInfo.LaserPowerCore = string.Empty;
+ InnerCellInfo.LaserPowerRing = string.Empty;
+ InnerCellInfo.MarkSpeed = string.Empty;
+ InnerCellInfo.FocalLength = string.Empty;
+ InnerCellInfo.WeldingJigNo = string.Empty;
+ InnerCellInfo.LaserPowerByLBAD = string.Empty;
+ InnerCellInfo.CheckCanExist = false;
+ InnerCellInfo.CheckScannerListEnd = false;
+ }
+
+ ///
+ /// InnerCellInfo를 WeldingData 로부터 필드 단위로 복사 (GC 할당 없음)
+ ///
+ public void UpdateCellInfo(WeldingData data)
+ {
+ InnerCellInfo.CellId = data.CellId;
+ InnerCellInfo.LotId = data.LotId;
+ InnerCellInfo.AutoRunStartTime = data.AutoRunStartTime;
+ InnerCellInfo.WeldingStartTime = data.WeldingStartTime;
+ InnerCellInfo.WeldingEndTime = data.WeldingEndTime;
+ InnerCellInfo.WeldingSpanTimeMilliseconds = data.WeldingSpanTimeMilliseconds;
+ InnerCellInfo.TodayProduct = data.TodayProduct;
+ InnerCellInfo.TotalProduct = data.TotalProduct;
+ InnerCellInfo.RecipeNo = data.RecipeNo;
+ InnerCellInfo.WeldStartTime = data.WeldStartTime;
+ InnerCellInfo.WeldTime = data.WeldTime;
+ InnerCellInfo.WorkCellId = data.WorkCellId;
+ InnerCellInfo.WorkLotId = data.WorkLotId;
+ InnerCellInfo.LBAD_Judge = data.LBAD_Judge;
+ InnerCellInfo.LDD_Judge = data.LDD_Judge;
+ InnerCellInfo.LaserPowerCore = data.LaserPowerCore;
+ InnerCellInfo.LaserPowerRing = data.LaserPowerRing;
+ InnerCellInfo.MarkSpeed = data.MarkSpeed;
+ InnerCellInfo.FocalLength = data.FocalLength;
+ InnerCellInfo.WeldingJigNo = data.WeldingJigNo;
+ InnerCellInfo.LaserPowerByLBAD = data.LaserPowerByLBAD;
+ InnerCellInfo.CheckCanExist = data.CheckCanExist;
+ InnerCellInfo.CheckScannerListEnd = data.CheckScannerListEnd;
+ }
+ }
+}
diff --git a/Machines/ILSOS_CCW_H52A/EquipDef.cs b/Machines/ILSOS_CCW_H52A/EquipDef.cs
index 36c185d..b7bc594 100644
--- a/Machines/ILSOS_CCW_H52A/EquipDef.cs
+++ b/Machines/ILSOS_CCW_H52A/EquipDef.cs
@@ -1,7 +1,15 @@
-using WeldingCommon;
+using Aurora;
+using Aurora.Api;
+using ILSOS_CCW_H52A.Sequences.ProcessObjects;
+using WeldingCommon;
namespace ILSOS_CCW_H52A
{
+ public static class AuroraEnvironmentExt
+ {
+ public static ProcessManager GetProcessManager(this IAuroraEnvironment _) => ProcessManager.Instance;
+ public static ProcessManager GetProcessManager(this SequenceContext _) => ProcessManager.Instance;
+ }
internal class EquipDef
{
}
diff --git a/Machines/ILSOS_CCW_H52A/EquipmentSetup.cs b/Machines/ILSOS_CCW_H52A/EquipmentSetup.cs
index e2cdc06..4ed0b99 100644
--- a/Machines/ILSOS_CCW_H52A/EquipmentSetup.cs
+++ b/Machines/ILSOS_CCW_H52A/EquipmentSetup.cs
@@ -76,6 +76,8 @@ namespace ILSOS_CCW_H52A
base.OnStartUp();
AuroraEnv.SequenceEntry[SequenceIds.AlarmMonitoring].Start();
+
+ AuroraEnv.GetProcessManager().Init(AuroraEnv);
}
// For Selection LaserDevice
diff --git a/Machines/ILSOS_CCW_H52A/ILSOS_CCW_H52A.csproj b/Machines/ILSOS_CCW_H52A/ILSOS_CCW_H52A.csproj
index 1b10f5d..084871a 100644
--- a/Machines/ILSOS_CCW_H52A/ILSOS_CCW_H52A.csproj
+++ b/Machines/ILSOS_CCW_H52A/ILSOS_CCW_H52A.csproj
@@ -92,6 +92,8 @@
+
+
diff --git a/Machines/ILSOS_CCW_H52A/Sequences/ProcessObjects/ProcessManager.cs b/Machines/ILSOS_CCW_H52A/Sequences/ProcessObjects/ProcessManager.cs
index 7af96f0..7b477c2 100644
--- a/Machines/ILSOS_CCW_H52A/Sequences/ProcessObjects/ProcessManager.cs
+++ b/Machines/ILSOS_CCW_H52A/Sequences/ProcessObjects/ProcessManager.cs
@@ -3,12 +3,14 @@ using Aurora.Api;
using Aurora.Util.HislogPlus;
using AuroraLaserScanner;
using AuroraPLC;
+using ILSOS_CCW_H52A.Data;
using ILSOS_CCW_H52A.Device;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
+using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
@@ -29,12 +31,15 @@ namespace ILSOS_CCW_H52A.Sequences.ProcessObjects
return _Instance;
}
}
+ private bool usedAnalyzerLog = false;
+ private readonly Logger _log = new Logger("ProcessManager");
+
+
private IAuroraEnvironment _env;
private PLCControllerCCWH52A _plc;
private ScannerCCWH52A _scanner;
- private bool usedAnalyzerLog = false;
- private readonly Logger _log = new Logger("ProcessManager");
+ public CellInfoScheduler cellScheduler { get; set; }
public const double compensateEncoderReset = 0.015; // 엔코더 리셋 보상 각도 설정(70도/sec 기준 약 0.015도)
public const int RotaryJigCount = 16;
@@ -48,6 +53,7 @@ namespace ILSOS_CCW_H52A.Sequences.ProcessObjects
_env = env;
_plc = _env.DeviceManager[DeviceIds.PLCDevice] as PLCControllerCCWH52A;
_scanner = _env.DeviceManager[DeviceIds.Scanner] as ScannerCCWH52A;
+ cellScheduler = new CellInfoScheduler();
}
public IEnumerable CheckRotaryState()
{
diff --git a/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunCCWH52A.cs b/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunCCWH52A.cs
index 9f6bbba..4664ab5 100644
--- a/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunCCWH52A.cs
+++ b/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunCCWH52A.cs
@@ -14,9 +14,11 @@ using CoreUnit.Control.UserControl.Canvas;
using CoreUnit.Scanner.UserControl.ScannerCanvas;
using DB;
using Hermes;
+using ILSOS_CCW_H52A.Data;
using ILSOS_CCW_H52A.Device;
using ILSOS_CCW_H52A.Models;
using ILSOS_CCW_H52A.RMS;
+using ILSOS_CCW_H52A.Sequences.ProcessObjects;
using System;
using System.Collections.Generic;
using System.IO;
@@ -52,10 +54,6 @@ namespace ILSOS_CCW_H52A.Sequences
// TODO: [장비구현필요] 실제 공정 추가
const string SEQ_CHECK_LOAD_COMPLETE = "[SeqAutoRunCCWH52A] Check Load Complete";
- const string SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ = "[SeqAutoRunCCWH52A] Set Sequence Param (UploadWeldingList Seq)";
- const string SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ = "[SeqAutoRunCCWH52A] Process (UploadWeldingList Seq)";
- const string SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ = "[SeqAutoRunCCWH52A] Get Sequence Result (UploadWeldingList Seq)";
-
public const string SEQ_CHECK_SCANNER_LIST_COMPLETE = "[SeqAutoRunCCWH52A] Check Scanner List Complete";
const string SEQ_READY_SCANNER_LIST = "[SeqAutoRunCCWH52A] Ready ScannerList";
@@ -63,6 +61,10 @@ namespace ILSOS_CCW_H52A.Sequences
const string SEQ_DO_ID_RECEIVE_CELL_INFO_SEQ = "[SeqAutoRunCCWH52A] Process (ReceiveCellInfo Seq)";
const string SEQ_GET_PARAMS_RECEIVE_CELL_INFO_SEQ = "[SeqAutoRunCCWH52A] Get Sequence Result (ReceiveCellInfo Seq)";
+ const string SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ = "[SeqAutoRunCCWH52A] Set Sequence Param (UploadWeldingList Seq)";
+ const string SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ = "[SeqAutoRunCCWH52A] Process (UploadWeldingList Seq)";
+ const string SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ = "[SeqAutoRunCCWH52A] Get Sequence Result (UploadWeldingList Seq)";
+
const string SEQ_EXECUTE_WELDING = "[SeqAutoRunCCWH52A] Execute Welding";
const string SEQ_REQUEST_UNLOAD_CELL = "[SeqAutoRunCCWH52A] Request Unload Cell";
//const string SEQ_WAIT_LBAD_JUDGE = "[SeqAutoRunCCWH52A] Wait LBAD Judge";
@@ -101,8 +103,6 @@ namespace ILSOS_CCW_H52A.Sequences
private SystemParameter _SystemParameter;
private ScannerCanvasUserCtrl _ScannerCanvasUserCtrl; //스캐너 동작관련 객체 선언
- private string _LotId = "N/A";
- private string _CellId = "N/A";
private int _ModelNo;
private IRecipeManager _RecipeManager;
@@ -117,25 +117,13 @@ namespace ILSOS_CCW_H52A.Sequences
private uint _TotalProduct;
private double _LaserPowerByLBAD;
- private int _CompletedJigNo;
- private static bool isWeled;
private bool _ModulationMode;
private DateTime _AutoRunStartTime;
- private DateTime _WeldingStartTime;
- private TimeSpan _WeldingSpanTime;
- private DateTime _WeldEndTime;
- private TimeSpan _InterfaceSpanTime;
- private DateTime _InterfaceStartTime;
+ // 연속 지그 간 용접 간격 계산 전용 (보고 데이터 아님 — 인터벌 로그용)
+ private DateTime _prevWeldEndTime;
- private double _CellMeasureCenterOffsetX;
- private double _CellMeasureCenterOffsetY;
-
- // MOTF
- private TimeSpan _WeldInterval;
-
- private int _LastScannerParamNo = -1; //마지막 작업한 스캔 변수값들이 현재 작업하려는 스캔 변수값과 같은지 판단함(같으면 설정작업을 skip함)
private int _EncoderOverflowCount = 0;
private double _RotaryEncoderOffset = 0;
@@ -143,10 +131,11 @@ namespace ILSOS_CCW_H52A.Sequences
private int _WeldingCount = 0;
private int _RotaryJigCount = 16;
+ // 전전 셀/전 셀/대기 셀/완료 셀 파이프라인 스케줄러 (16개 JigInfo 풀, GC-free)
+ private CellInfoScheduler _CellScheduler;
+
private double _WeldingStartAngle;
- private bool isTheFirstListWrite;
- private bool isTheSecondListWrite;
private bool isTheFirstListExecute;
private int _RecipeNo;
@@ -176,13 +165,13 @@ namespace ILSOS_CCW_H52A.Sequences
sb.AddAction(SEQ_CHECK_LOAD_COMPLETE, CheckLoadComplete);
+ sb.AddAction(SEQ_CHECK_SCANNER_LIST_COMPLETE, CheckScannerListComplete);
+ sb.AddDecision(SEQ_READY_SCANNER_LIST, ReadyScannerList);
+
sb.AddAction(SEQ_SET_PARAMS_RECEIVE_CELL_INFO_SEQ, SetReceiveCellInfo_SeqParams); // Cell 정보를 받아와 입력한다.
sb.AddSubsequence(SEQ_DO_ID_RECEIVE_CELL_INFO_SEQ, SequenceManager.GetSeqBuilder_SeqReceiveCellInfo()); // cell 정보를 받아오는 Sub Seq 실행
sb.AddAction(SEQ_GET_PARAMS_RECEIVE_CELL_INFO_SEQ, GetReceiveCellInfo_SeqResult); // Cell 정보를 가져온다
- sb.AddAction(SEQ_CHECK_SCANNER_LIST_COMPLETE, CheckScannerListComplete);
- sb.AddDecision(SEQ_READY_SCANNER_LIST, ReadyScannerList);
-
sb.AddAction(SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ, SetUploadWeldingList_SeqParams);
sb.AddSubsequence(SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ, SequenceManager.GetSeqBuilder_SeqUploadWeldingList());
sb.AddAction(SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ, GetUploadWeldingList_SeqResult);
@@ -203,8 +192,8 @@ namespace ILSOS_CCW_H52A.Sequences
sb.AddAction(SEQ_COMPLETE, Complete);
sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_FORK_ENCODER_MONITOR] > sb[SEQ_CHECK_LOAD_COMPLETE]
- > sb[SEQ_SET_PARAMS_RECEIVE_CELL_INFO_SEQ] > sb[SEQ_DO_ID_RECEIVE_CELL_INFO_SEQ] > sb[SEQ_GET_PARAMS_RECEIVE_CELL_INFO_SEQ]
> sb[SEQ_CHECK_SCANNER_LIST_COMPLETE] > sb[SEQ_READY_SCANNER_LIST]
+ > sb[SEQ_SET_PARAMS_RECEIVE_CELL_INFO_SEQ] > sb[SEQ_DO_ID_RECEIVE_CELL_INFO_SEQ] > sb[SEQ_GET_PARAMS_RECEIVE_CELL_INFO_SEQ]
> 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_EXECUTE_WELDING] > sb[SEQ_REQUEST_UNLOAD_CELL] > sb[SEQ_CHECK_CAN_WELDED]
> sb[SEQ_SET_PARAMS_REPORT_CELL_RESULT_SEQ] > sb[SEQ_DO_ID_REPORT_CELL_RESULT_SEQ] > sb[SEQ_GET_RESULT_REPORT_CELL_RESULT_SEQ]
@@ -299,9 +288,6 @@ namespace ILSOS_CCW_H52A.Sequences
_WeldingScannerListNo = eListNo.LIST1;
- _CellMeasureCenterOffsetX = 0.0;
- _CellMeasureCenterOffsetY = 0.0;
-
var currentModelName = context.Recipes.ActivatedModelName();
var modelItem = context.GetModel(currentModelName, CommonModelIds.EquipmentSpecificModel);
_ModelNo = modelItem.Model_No;
@@ -322,6 +308,8 @@ namespace ILSOS_CCW_H52A.Sequences
_CellTracker = context.GetCellTracker();
_PLC = context.Devices[DeviceIds.PLCDevice] as PLCControllerCCWH52A;
_IO = context.GetIOUnit();
+ _CellScheduler = ProcessManager.Instance.cellScheduler;
+
}
private bool CheckDeviceReady(SequenceContext context)
@@ -403,15 +391,13 @@ namespace ILSOS_CCW_H52A.Sequences
_ModulationMode = false;
}
- //// PLC Initialize AutoRun Bit 최소화 작업 진행
- if (_PLC.ReadBit((int)eBitInput.PLC_AUTO_STATUS).data == false) // AutoRun Bit가 OFF인 경우
- {
- if (_PLC.ReadBit((int)eBitInput.PLC_AUTO_REQ).data == false) // AutoRun Request 상태 체크
- {
+ ///// AutoRun Bit가 OFF인 경우
+ if (_PLC.ReadBit((int)eBitInput.PLC_AUTO_STATUS).data == false) {
+ // AutoRun Request 상태 체크
+ if (_PLC.ReadBit((int)eBitInput.PLC_AUTO_REQ).data == false) {
Log.E(string.Format("ERROR Initialize PLC, PLC Alarm ON")); // AutoRun Request와 AutoRun Bit 모두 OFF인 경우, PLC 측에서 Alarm 발생함.
return (int)InitErrorList.PLC_ALARM;
- }
- else {
+ } else {
Log.E(string.Format("ERROR Initialize PLC, Bit Off PLC_AUTO_STATUS")); // AutoRun Request가 ON인데 AutoRun Bit 모두 OFF인 경우, PLC에서 AutoRun Bit를 켜지 않음.
return (int)InitErrorList.PLC_AUTORUN_BIT_OFF;
}
@@ -433,10 +419,6 @@ namespace ILSOS_CCW_H52A.Sequences
string[] strArr = Dao.SystemRecipe.WorkRecipeFile.Split('\\');
RecipeFileName = strArr[strArr.Length - 1];
- _LastScannerParamNo = -1;
-
- // 트럼프 TruDisk를 쓰기 위해서는 SetFirstPulseKiller를 사용해야 함.
-
// Rotary는 시작 트리거 미사용
_Scanner.SetControlMode(0);
UtilUnit.logUnit.SetLog(LogUnit.eLogType.DEBUG, MethodBase.GetCurrentMethod().Name, "[AutoRun] 외부 시작 트리거 DISABLE");
@@ -455,13 +437,15 @@ namespace ILSOS_CCW_H52A.Sequences
Log.I(string.Format($"ScannerUnit.InitRotaryOnTheFlyWeldingCount : m_abwRotaryEncoderOffset({_RotaryEncoderOffset}), weldingCount({_WeldingCount}), weldingJigNo({_WeldingJigNo}), rotaryJigCount({_RotaryJigCount})"));
- // 용접 완료 대기 스캐너 List 리셋
- ResetInputScannerList();
- WaitingScannerList.Reset();
- CompleteScannerList.Reset();
+ // JigInfo 풀 스케줄러 초기화 (전전/전/대기/완료 셀 파이프라인)
+ // ── 재시작 전 미완료 보고 보존 ───────────────────────────────────────────────
+ // ReportTarget이 있는 상태로 정지가 들어오면 Reset() 후 슬롯이 Clear()되어 보고 불가.
+ // SavePendingComplete()로 _pendingSlot에 복사한 뒤 Reset()을 호출한다.
+
+ _CellScheduler.SavePendingComplete();
+ _CellScheduler.Reset();
+ // ─────────────────────────────────────────────────────────────────────────────
- isTheFirstListWrite = true; // 작업 시작 시 처음 2개의 List는 입력하고 시작한다
- isTheSecondListWrite = true;
isTheFirstListExecute = true; // 제일 첫 List는 ExecuteList 필요. 이후는 AutoChange
_PLC.WriteBit(eBitOutput.PC_AUTO_STATUS, true);
@@ -559,100 +543,80 @@ namespace ILSOS_CCW_H52A.Sequences
private SequenceActionResult CheckScannerListComplete(SequenceContext context, string id, SequenceActionResult param)
{
- if (isTheFirstListWrite) // 작업 시작 시, 첫번째 리스트는 바로 쓰고, 바로 시작한다(ExecuteList). 다만 List 명령에 의해 Encoder가 들어올 때까지 대기.
- {
- isTheFirstListWrite = false;
- Log.I("Write the 1st scanner list");
-
+ // Prefetch1 = 2회 전 업로드된 지그 = 현재 물리적으로 레이저 통과 중 = 완료 체크 대상.
+ // Shift()는 이 메서드 이후 ReadyScannerList에서 실행되므로, 이 시점의 Prefetch1이
+ // 원본 코드의 InputScannerList[(listNo-1)]과 동일 대상이다.
+ // 예열 구간: iter 1~2(Pf1=null) → 통과. iter 3~: Pf1=Jig1 → 실제 처리.
+ var jigComplete = _CellScheduler.Prefetch1;
+ if (jigComplete == null) {
+ Log.I($"[Warmup] ScannerList {_WeldingCount}: pipeline not ready, skip complete check");
+ return SequenceAction.Ok();
+ }
+ if (!CheckCurrentEncoderAngle(jigComplete.StartAngle)) {
+ // IsCanExist=false(공 지그)는 리스트 실행은 하지만 용접이 없으므로
+ // 엔코더 범위를 벗어나도 에러가 아님
+ if (jigComplete.IsCanExist) {
+ Log.I("ERR_SCANNER_LIST_PROCESSING_TIMEOVER");
+ return SequenceAction.Error();
+ }
+ return SequenceAction.Ok();
}
else {
- if (isTheSecondListWrite) // 두번째 리스트는 바로 쓰지만, 첫번째 리스트 실행이 완료되면 자동으로 이어서 실행된다(AutoChange)
- {
- isTheSecondListWrite = false;
- Log.I("Write the 2nd scanner list");
+ if (jigComplete.IsCanExist) {
+ #region 용접 시간, 엔코더 측정/기록
+ double elaspedTime = _Scanner.GetTime();
- }
- else {
- isWeled = false;
- // 첫번째나 두번째가 아니라면, 실제 리스트 실행이 끝난 것이기 때문에 완료 처리가 필요
- CompleteScannerList = InputScannerList[(int)_WeldingScannerListNo - 1];
+ if (jigComplete.WeldComplete) {
+ jigComplete.WeldEndTime = DateTime.Now;
+ jigComplete.WeldingSpanTime = TimeSpan.FromSeconds(elaspedTime);
+ TimeSpan weldInterval = jigComplete.WeldEndTime - _prevWeldEndTime;
+ _prevWeldEndTime = jigComplete.WeldEndTime;
- if (!CheckCurrentEncoderAngle(CompleteScannerList.StartAngle)) {
- // 에러 상황(현재 Rotary Encoder 값이 정상적인 스캐너 리스트 각도 범위를 벗어남)
- Log.I("ERR_SCANNER_LIST_PROCESSING_TIMEOVER");
+ // 보고 대상 슬롯 지정 (스케줄러가 관리)
+ _CellScheduler.ReportTarget = jigComplete;
- return SequenceAction.Error();
+ Log.I(string.Format("JigNo : {0}, Weld start : {1}, Weld End : {2}, WeldTime : {3}, interval : {4:F3}",
+ jigComplete.JigNo,
+ jigComplete.WeldStartTime.ToString("HH:mm:ss.fff"),
+ jigComplete.WeldEndTime.ToString("HH:mm:ss.fff"),
+ jigComplete.WeldingSpanTime.TotalMilliseconds,
+ weldInterval.TotalSeconds));
- // WON : 2칸 전에 용접을 진행하지 않아 현재 Angle이 CompleteScannerList.StartAngle + 45 범위를 벗어난 경우라면 에러 상황이 아니므로 에러로 처리 할 필요가 없다
- // 위에 CheckCurrentEncoderAngle if문 조건 대신 아래 내용으로 테스트 해본 뒤에 적용 필요
- //if (!CheckCurrentEncoderAngle(CompleteScannerList.StartAngle)) {
- // // 에러 상황(현재 Rotary Encoder 값이 정상적인 스캐너 리스트 각도 범위를 벗어남)
- // if (CompleteScannerList.CanExist) {
- // Log.I("ERR_SCANNER_LIST_PROCESSING_TIMEOVER");
- // return SequenceAction.Error();
- // }
- // return SequenceAction.Ok();
- }
- else {
- if (CompleteScannerList.CanExist) {
- isWeled = true;
- #region 용접 시간, 엔코더 측정/기록
- //_WeldInterval = DateTime.Now - _WeldEndTime;
- //_WeldEndTime = DateTime.Now;
- //double elapsedTime = _Scanner.GetTime();
- //_WeldingSpanTime = TimeSpan.FromSeconds(elapsedTime); // Encoder 대기 넘어간 후의, 실제 스캐너 리스트 실행 시간
- //_WeldingStartTime = _WeldEndTime.Subtract(_WeldingSpanTime);
-
- double elaspedTime = _Scanner.GetTime();
-
- if (CompleteScannerList.WeldComplete) {
- _WeldInterval = DateTime.Now - _WeldEndTime;
- _WeldEndTime = DateTime.Now;
- _WeldingStartTime = CompleteScannerList.WeldStartTime;
- _WeldingSpanTime = TimeSpan.FromSeconds(elaspedTime);
- _LotId = CompleteScannerList.LotId;
- _CellId = CompleteScannerList.CellId;
-
- Log.I(string.Format("JigNo : {0}, Weld start : {1}, Weld End : {2}, WeldTime : {3}, interval : {4:F3}",
- CompleteScannerList.JigNo, _WeldingStartTime.ToString("HH:mm:ss.fff"), _WeldEndTime.ToString("HH:mm:ss.fff"), _WeldingSpanTime.TotalMilliseconds, _WeldInterval.TotalSeconds));
-
- if (_PLC.ReadBit((int)eBitInput.PLC_USE_WELD_JIG_1 + CompleteScannerList.JigNo - 1).data) {
- _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE_1 + CompleteScannerList.JigNo - 1, true);
- Log.I(string.Format($"PC_WELD_COMPLETE_{CompleteScannerList.JigNo} -> true)"));
- }
- UpdateProductCount(context);
- UpdateProductInfoForView();
- SaveCellAPD();
- }
-
- //if (_SystemParameter.BypassModeLBAD == false) {
- // if (_StreamLightLBAD.SendWeldingStop() == false) {
- // Log.I(string.Format($"[LBAD Interface]SendWeldingStop null"));
- // //return null;
- // }
- //}
-
- UtilUnit.logUnit.SetLog(LogUnit.eLogType.PRODUCT, MethodBase.GetCurrentMethod().Name, "UPDATE_WELDING_TIME;" + _WeldingSpanTime.TotalMilliseconds);
- UtilUnit.logUnit.SetLog(LogUnit.eLogType.PRODUCT, MethodBase.GetCurrentMethod().Name, "UPDATE_PRODUCT_COUNT;" + _WeldEndTime.ToString("yyyy-MM-dd HH:mm:ss"));
-
- double encoder0_0, encoder1_0, encoder0_1, encoder1_1;
- _Scanner.ReadEncoder(out encoder0_0, out encoder1_0, out encoder0_1, out encoder1_1, true);
-
- double startEncoderCount = encoder0_0;
- double endEncoderCount = encoder0_1;
- double angularVelocity = (endEncoderCount - startEncoderCount) / elaspedTime; // 단위는 [degree/sec]
-
- Log.I(string.Format("Encoder start : {0:F4}, end : {1:F4}, vel : {2:F2}", startEncoderCount, endEncoderCount, angularVelocity));
- #endregion
- #region 스캐너 리스트 Record 파일 기록
- if (_Scanner.Config.UseRecordSignal) {
- String RecordFileName = _RecipeManager.ActivatedRecipe().Name + "_Jig#" + CompleteScannerList.JigNo.ToString();
- string DataFilePath = ProjectUtil.GetDataLogSetting().GetFilePath_ScannerRecordLog(RecordFileName, true);
- _Scanner.SaveRecord(DataFilePath);
- }
- #endregion
+ if (_PLC.ReadBit((int)eBitInput.PLC_USE_WELD_JIG_1 + jigComplete.JigNo - 1).data) {
+ _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE_1 + jigComplete.JigNo - 1, true);
+ Log.I(string.Format($"PC_WELD_COMPLETE_{jigComplete.JigNo} -> true)"));
}
+ UpdateProductCount(context);
+ UpdateProductInfoForView();
+ SaveCellAPD();
}
+
+ //if (_SystemParameter.BypassModeLBAD == false) {
+ // if (_StreamLightLBAD.SendWeldingStop() == false) {
+ // Log.I(string.Format($"[LBAD Interface]SendWeldingStop null"));
+ // //return null;
+ // }
+ //}
+
+ UtilUnit.logUnit.SetLog(LogUnit.eLogType.PRODUCT, MethodBase.GetCurrentMethod().Name, "UPDATE_WELDING_TIME;" + jigComplete.WeldingSpanTime.TotalMilliseconds);
+ UtilUnit.logUnit.SetLog(LogUnit.eLogType.PRODUCT, MethodBase.GetCurrentMethod().Name, "UPDATE_PRODUCT_COUNT;" + jigComplete.WeldEndTime.ToString("yyyy-MM-dd HH:mm:ss"));
+
+ double encoder0_0, encoder1_0, encoder0_1, encoder1_1;
+ _Scanner.ReadEncoder(out encoder0_0, out encoder1_0, out encoder0_1, out encoder1_1, true);
+
+ double startEncoderCount = encoder0_0;
+ double endEncoderCount = encoder0_1;
+ double angularVelocity = (endEncoderCount - startEncoderCount) / elaspedTime; // 단위는 [degree/sec]
+
+ Log.I(string.Format("Encoder start : {0:F4}, end : {1:F4}, vel : {2:F2}", startEncoderCount, endEncoderCount, angularVelocity));
+ #endregion
+ #region 스캐너 리스트 Record 파일 기록
+ if (_Scanner.Config.UseRecordSignal) {
+ String RecordFileName = _RecipeManager.ActivatedRecipe().Name + "_Jig#" + jigComplete.JigNo.ToString();
+ string DataFilePath = ProjectUtil.GetDataLogSetting().GetFilePath_ScannerRecordLog(RecordFileName, true);
+ _Scanner.SaveRecord(DataFilePath);
+ }
+ #endregion
}
}
return SequenceAction.Ok();
@@ -668,14 +632,15 @@ namespace ILSOS_CCW_H52A.Sequences
// Encoder 반전되는 순간이며, 이 때만 인터락 무시
}
else if (startAngle <= currentAngle) {
- if (currentAngle < startAngle + 45)// 다음 리스트 구간이 끝나기 전까지는 완료가 되어야 한다
- {
+ // 다음 리스트 구간이 끝나기 전까지는 완료가 되어야 한다
+ if (currentAngle < startAngle + 45) {
Log.I(string.Format("Current encoder check OK, startAngle : {0}, currentAngle : {1})", startAngle, currentAngle));
return true;
- }
- else Log.W(string.Format("Current encoder check NG(Too late), startAngle : {0}, currentAngle : {1})", startAngle, currentAngle));
+ } else
+ Log.W(string.Format("Current encoder check NG(Too late), startAngle : {0}, currentAngle : {1})", startAngle, currentAngle));
}
- else Log.W(string.Format("Current encoder check NG(Too early), startAngle : {0}, currentAngle : {1})", startAngle, currentAngle));
+ else
+ Log.W(string.Format("Current encoder check NG(Too early), startAngle : {0}, currentAngle : {1})", startAngle, currentAngle));
return false;
}
@@ -727,6 +692,17 @@ namespace ILSOS_CCW_H52A.Sequences
Log.I("Can Exist : " + canExist.ToString() + ", JigNo " + _WeldingJigNo + " ,CountNo " + _WeldingCount + ", StartAngle(Y) : " + _WeldingStartAngle);
+ // canExist 여부와 무관하게 매 지그마다 파이프라인 전진
+ // canExist=false 지그도 Shift()해야 Prefetch1 타이밍이 올바르게 유지됨.
+ _CellScheduler.Shift((eJigNo)(_WeldingJigNo - 1));
+
+ _CellScheduler.Prefetch2.JigNo = _WeldingJigNo;
+ _CellScheduler.Prefetch2.ListNo = _WeldingScannerListNo;
+ _CellScheduler.Prefetch2.WriteTime = DateTime.Now;
+ _CellScheduler.Prefetch2.StartAngle = _WeldingStartAngle;
+ _CellScheduler.Prefetch2.WeldingCount = _WeldingCount;
+ _CellScheduler.Prefetch2.IsCanExist = canExist;
+
if (canExist) {
#region 용접할 Objects 선택
if (_WeldingJigNo - 1 < _ScannerCanvasUserCtrl.Model.Layers.Length) {
@@ -738,16 +714,11 @@ namespace ILSOS_CCW_H52A.Sequences
Log.I(string.Format("Page 불러오기 : [0])"));
}
#endregion
- // 입력한 Scanner List를 완료대기열에 추가
- // Won : GetProcessingWeldingData().WorkLotId 는 CellTrakcer로 부터 받은 상태의 LotId
- // GetProcessingWeldingData().LotId 는 AutorunView에 표기할 용접이 완료 된 LotID
- string LotId = Status.GetProcessingWeldingData().WorkLotId; // CellTracker로 부터 WorkLotID, WorkCellID 받는 구문 추가 후 해당 데이터로 입력 필요
- string CellId = Status.GetProcessingWeldingData().WorkCellId;
- SetInputScannerList(_WeldingScannerListNo, DateTime.Now, _WeldingCount, _WeldingStartAngle, _WeldingJigNo, _RotaryJigCount, canExist, LotId, CellId);
+
_RecipeNo = _RecipeManager.ActivatedRecipe().Id;
SetWeldingData();
- return SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ;
+ return SEQ_SET_PARAMS_RECEIVE_CELL_INFO_SEQ;
}
else {
return SEQ_EXECUTE_WELDING;
@@ -814,22 +785,22 @@ namespace ILSOS_CCW_H52A.Sequences
public bool GetRotaryCanExist(int jigNo)
{
#region 지그 16개 요청기록 로그 남기기, 빠져나간 캔에 대한 Complete Bit 초기화
- string result = "";
+ //string result = "";
- for (int i = 1; i <= _RotaryJigCount; i++) {
- if (_PLC.ReadBit((int)eBitInput.PLC_USE_WELD_JIG_1 + i).data)
- result += "1, ";
- else {
- result += "0, ";
+ //for (int i = 1; i <= _RotaryJigCount; i++) {
+ // if (_PLC.ReadBit((int)eBitInput.PLC_USE_WELD_JIG_1 + i).data)
+ // result += "1, ";
+ // else {
+ // result += "0, ";
- string outId = string.Format("PC_WELD_COMPLETE_{0}", i);
+ // string outId = string.Format("PC_WELD_COMPLETE_{0}", i);
- if (_PLC.ReadBit((int)eBitInput.PLC_USE_WELD_JIG_1 + i).data) {
- _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE_1 + i, false);
- Log.I(string.Format($"PC_WELD_COMPLETE_{1 + i} -> FALSE)"));
- }
- }
- }
+ // if (_PLC.ReadBit((int)eBitInput.PLC_USE_WELD_JIG_1 + i).data) {
+ // _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE_1 + i, false);
+ // Log.I(string.Format($"PC_WELD_COMPLETE_{1 + i} -> FALSE)"));
+ // }
+ // }
+ //}
//UtilUnit.logUnit.SetLog(LogUnit.eLogType.DEBUG, MethodBase.GetCurrentMethod().Name, string.Format("PLC -> PC 용접 Cmd : ({0})", result));
#endregion
@@ -846,8 +817,8 @@ namespace ILSOS_CCW_H52A.Sequences
SeqUploadWeldingList_InitParam seqParams = new SeqUploadWeldingList_InitParam() {
NeedSetLaserTiming = _ModulationMode,
IncludeListStartEnd = false,
- CellMeasureCenterOffsetX = _CellMeasureCenterOffsetX,
- CellMeasureCenterOffsetY = _CellMeasureCenterOffsetY,
+ CellMeasureCenterOffsetX = 0,
+ CellMeasureCenterOffsetY = 0,
SelectedDrawObjectList = _SelectedDrawObjectList,
WeldingScannerListNo = _WeldingScannerListNo,
Rotation_Degree = 11.25,
@@ -864,7 +835,15 @@ namespace ILSOS_CCW_H52A.Sequences
return SequenceAction.ForceStopByAlarm(AlarmCode.ALARM_SEQUENCE_UploadWeldingList_ResultFailed);
//return null;
}
+ // 입력한 Scanner List를 완료대기열에 추가
+ // Won : GetProcessingWeldingData().WorkLotId 는 CellTrakcer로 부터 받은 상태의 LotId
+ // GetProcessingWeldingData().LotId 는 AutorunView에 표기할 용접이 완료 된 LotID
+ string LotId = Status.GetProcessingWeldingData().WorkLotId; // CellTracker로 부터 WorkLotID, WorkCellID 받는 구문 추가 후 해당 데이터로 입력 필요
+ string CellId = Status.GetProcessingWeldingData().WorkCellId;
+ _CellScheduler.Prefetch2.LotId = LotId;
+ _CellScheduler.Prefetch2.CellId = CellId;
+ Log.FuncI("CellTracker Load");
return SequenceAction.Ok();
}
@@ -919,7 +898,6 @@ namespace ILSOS_CCW_H52A.Sequences
}
private SequenceActionResult EncoderMonitor(SequenceContext context, string id, SequenceActionResult param)
{
- int EncoderLogCount = 0;
double send_time_offset = 0.1;
string LotID = "", CellID = "";
@@ -929,40 +907,46 @@ namespace ILSOS_CCW_H52A.Sequences
double CurrentPosPLC = GetRotaryCurrentPosition();
bool WeldingInPosition = encoderX > _Scanner.Config.MarkingRangeMinRotate + 11.25 - send_time_offset - 0.1 && encoderX < _Scanner.Config.MarkingRangeMinRotate + 11.25 - send_time_offset + 0.1;
- if (WeldingInPosition) {
- if (InputScannerList[(int)_WeldingScannerListNo - 1].CanExist) {
- Log.I($"Fork Before Min Rotate Encoder X : ({encoderX}), Encoder Y : ({currentAngle}), PLC Rotary : ({CurrentPosPLC}), List {_WeldingScannerListNo}, JigNo : ({InputScannerList[(int)_WeldingScannerListNo - 1].JigNo})");
- if (!_SystemParameter.BypassModeLBAD) {
- if (InputScannerList[(int)_WeldingScannerListNo - 1].CheckSendCellinfo == false) {
- LotID = InputScannerList[(int)_WeldingScannerListNo - 1].LotId;
- CellID = InputScannerList[(int)_WeldingScannerListNo - 1].CellId;
- if (currentAngle > InputScannerList[(int)_WeldingScannerListNo - 1].StartAngle) {
- InputScannerList[(int)_WeldingScannerListNo - 1].CheckSendCellinfo = _StreamLightLBAD.SendCellInfo(LotID, CellID, _RecipeNo, _ModelNo, InputScannerList[(int)_WeldingScannerListNo - 1].JigNo);
+ // ── JigScheduler: 포크 내에서 참조 캡처 (Shift와의 경합 방지) ──────
+ // Prefetch1: 2회 전 업로드된 지그 = 현재 물리적으로 실행 중인 지그.
+ // 원본 코드의 InputScannerList[(listNo-1)]과 동일 대상.
+ // Shift()는 메인 시퀀스에서만 호출되므로 루프 시작 시 캡처해도 안전.
+ var executeTarget = _CellScheduler.Prefetch1;
- if (InputScannerList[(int)_WeldingScannerListNo - 1].CheckSendCellinfo == false)
- Log.I($"[LBAD Interface]SendCellInfo null, LotID [{LotID}], CellID [{CellID}], JigNo [{InputScannerList[(int)_WeldingScannerListNo - 1].JigNo}]");
+ if (WeldingInPosition) {
+ if (executeTarget != null && executeTarget.IsCanExist) {
+ Log.I($"Fork Before Min Rotate Encoder X : ({encoderX}), Encoder Y : ({currentAngle}), PLC Rotary : ({CurrentPosPLC}), List {executeTarget.ListNo}, JigNo : ({executeTarget.JigNo})");
+ if (!_SystemParameter.BypassModeLBAD) {
+ if (!executeTarget.CheckSendCellinfo) {
+ LotID = executeTarget.LotId;
+ CellID = executeTarget.CellId;
+ if (currentAngle > executeTarget.StartAngle) {
+ executeTarget.CheckSendCellinfo = _StreamLightLBAD.SendCellInfo(LotID, CellID, _RecipeNo, _ModelNo, executeTarget.JigNo);
+
+ if (!executeTarget.CheckSendCellinfo)
+ Log.I($"[LBAD Interface]SendCellInfo null, LotID [{LotID}], CellID [{CellID}], JigNo [{executeTarget.JigNo}]");
else
- Log.I($"[StreamLight]SendCellInfo LotID [{LotID}], CellID [{CellID}], JigNo [{InputScannerList[(int)_WeldingScannerListNo - 1].JigNo}]");
+ Log.I($"[StreamLight]SendCellInfo LotID [{LotID}], CellID [{CellID}], JigNo [{executeTarget.JigNo}]");
}
}
}
}
}
- if (_IO._iOUnit.ReadDiBit(Convert.ToInt32(eADIAddress.LaserOn))) {
- if (InputScannerList[(int)_WeldingScannerListNo - 1].WeldStartTime == DateTime.MinValue && InputScannerList[(int)_WeldingScannerListNo - 1].CanExist) {
- InputScannerList[(int)_WeldingScannerListNo - 1].WeldStartTime = DateTime.Now;
- InputScannerList[(int)_WeldingScannerListNo - 1].WeldComplete = true;
- EncoderLogCount = 0;
- Log.I($"Laser On, Welding Complete. JigNo : ({InputScannerList[(int)_WeldingScannerListNo - 1].JigNo})");
+ var LaserIO = _IO._iOUnit.ReadDiBit(Convert.ToInt32(eADIAddress.LaserOn));
+ var IsLaserOn = ChangeChecker.IsChangedBoolMonitoring("LaserOn", LaserIO, true);
+ if (IsLaserOn) {
+ if (executeTarget != null && executeTarget.IsCanExist && executeTarget.WeldStartTime == DateTime.MinValue) {
+ executeTarget.WeldStartTime = DateTime.Now;
+ executeTarget.WeldComplete = true;
+ Log.I($"Laser On, Welding Complete. JigNo : ({executeTarget.JigNo})");
}
-
- if (EncoderLogCount == 0) {
- Log.I($"Fork Laser On Encoder X : ({encoderX}), Encoder Y : ({currentAngle}), PLC Rotary : ({CurrentPosPLC}), List {_WeldingScannerListNo}, JigNo : ({InputScannerList[(int)_WeldingScannerListNo - 1].JigNo})");
- }
- EncoderLogCount = (EncoderLogCount + 1) % 7;
}
+ var IsLaserToggle = ChangeChecker.IsChangedBool("LaserToggle", LaserIO);
+ if (IsLaserToggle)
+ Log.I($"Fork Laser On({IsLaserToggle}) Encoder X : ({encoderX}), Encoder Y : ({currentAngle}), PLC Rotary : ({CurrentPosPLC}), List {executeTarget?.ListNo}, JigNo : ({executeTarget?.JigNo})");
+
for (int i = 0; i < _RotaryJigCount; i++) {
if (_PLC.ReadBit((int)eBitInput.PLC_USE_WELD_JIG_1 + i).data == false) {
_PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE_1 + i, false);
@@ -999,22 +983,20 @@ namespace ILSOS_CCW_H52A.Sequences
{
Status.GetProcessingWeldingData().TotalProduct = _TotalProduct;
Status.GetProcessingWeldingData().TodayProduct = _TodayProduct;
-
Status.GetProcessingWeldingData().AutoRunStartTime = _AutoRunStartTime;
-
- Status.GetProcessingWeldingData().WeldingStartTime = _WeldingStartTime;
- Status.GetProcessingWeldingData().WeldingEndTime = _WeldEndTime;
- Status.GetProcessingWeldingData().WeldingSpanTimeMilliseconds = _WeldingSpanTime.TotalMilliseconds.ToString();
-
- // Status.GetProcessingWeldingData().LotId 는 Autorun View에 표기할 용접이 완료 된 LotId 이므로 WorkLotID 와의 구분 확인
- Status.GetProcessingWeldingData().LotId = _LotId;
- Status.GetProcessingWeldingData().CellId = _CellId;
-
Status.GetProcessingWeldingData().RecipeNo = _RecipeNo;
Status.GetProcessingWeldingData().LaserPowerByLBAD = _LaserPowerByLBAD.ToString();
- Status.GetProcessingWeldingData().WeldingJigNo = _CompletedJigNo.ToString();
-
+ // 보고 데이터는 스케줄러의 ActiveReportSlot(ReportTarget 또는 PendingComplete)에서 읽음
+ var report = _CellScheduler.ActiveReportSlot;
+ if (report != null) {
+ Status.GetProcessingWeldingData().LotId = report.LotId;
+ Status.GetProcessingWeldingData().CellId = report.CellId;
+ Status.GetProcessingWeldingData().WeldingStartTime = report.WeldStartTime;
+ Status.GetProcessingWeldingData().WeldingEndTime = report.WeldEndTime;
+ Status.GetProcessingWeldingData().WeldingSpanTimeMilliseconds = report.WeldingSpanTime.TotalMilliseconds.ToString();
+ Status.GetProcessingWeldingData().WeldingJigNo = report.JigNo.ToString();
+ }
}
private SequenceActionResult SetReportCellResult_SeqParams(SequenceContext context, string id, SequenceActionResult param)
{
@@ -1034,9 +1016,8 @@ namespace ILSOS_CCW_H52A.Sequences
return null;
}
- // 중복 보고 방지 플래그 세팅
- CompleteScannerList.SetReportComplete(true);
- isWeled = false;
+ // 보고 완료: ReportTarget.ReportComplete=true 설정 + pendingSlot 클리어
+ _CellScheduler.CompleteReport();
return SequenceAction.Ok();
}
@@ -1063,18 +1044,19 @@ namespace ILSOS_CCW_H52A.Sequences
#endregion
private string CheckCanWelded(SequenceContext context, string id, ICollection branches, SequenceActionResult param)
{
- // 중복 보고 방지:
- // - CanExist=false (skip 대상) 는 보고하지 않음(미사용)
- // - WeldComplete=false (레이저 ON 감지/완료 처리 전) 는 보고하지 않음(미사용)
- // - ReportComplete=true (이미 보고 완료) 는 다시 보고하지 않음
- // .시퀀스가 너무 빨리 내려가서 보고하는 케이스로 예상
+ // ── 재시작 전 미완료 보고 우선 처리 ─────────────────────────────────────────
+ // SavePendingComplete()로 보존된 슬롯이 있으면 정상 파이프라인(ReportTarget)보다 우선.
+ if (_CellScheduler.HasPendingComplete) {
+ var p = _CellScheduler.PendingComplete;
+ Log.I($"[PendingReport] 재시작 전 미완료 보고: LotId={p.LotId}, CellId={p.CellId}, JigNo={p.JigNo}");
+ return SEQ_SET_PARAMS_REPORT_CELL_RESULT_SEQ;
+ }
+ // ─────────────────────────────────────────────────────────────────────────────
- // 보고 누락:
- // - 첫번째, 두번째는 UploadWeldingList를 해야하기 때문에 일단 넘긴다.
- if (isWeled
- //CompleteScannerList.CanExist &&
- //CompleteScannerList.WeldComplete &&
- && !CompleteScannerList.ReportComplete)
+ // 정상 보고 경로:
+ // - ReportTarget이 설정돼 있고 아직 보고 완료되지 않은 경우
+ // - 첫번째, 두번째 반복은 SetReportTarget이 호출되지 않으므로 자연스럽게 통과
+ if (_CellScheduler.HasReport)
return SEQ_SET_PARAMS_REPORT_CELL_RESULT_SEQ;
return SEQ_CHECK_CYCLE_STOP;
diff --git a/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunRMOTF.cs b/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunRMOTF.cs
index 352aaaa..d8fa028 100644
--- a/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunRMOTF.cs
+++ b/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunRMOTF.cs
@@ -760,6 +760,7 @@ namespace ILSOS_CCW_H52A.Sequences
if (laserParam != null) {
// EDC 보내는 용도
Status.GetProcessingWeldingData().LaserPowerCore = laserParam.LaserPower1.ToString();
+ Status.GetProcessingWeldingData().LaserPowerRing = laserParam.LaserPower2.ToString();
Status.GetProcessingWeldingData().MarkSpeed = laserParam.MarkSpeed.ToString();
Status.GetProcessingWeldingData().FocalLength = (_PLC.ReadDWord((int)eWordInput.PLC_WELDING_FOCAL_LENGTH).data / 1000f).ToString();
Status.GetProcessingWeldingData().WeldingJigNo = _WeldingJigNo.ToString();