From 614b5f1a5b2acb0faa84759f1b37b9decf8c1f62 Mon Sep 17 00:00:00 2001 From: choieigun Date: Sat, 28 Feb 2026 16:52:42 +0900 Subject: [PATCH] =?UTF-8?q?CellInfoScheduler=20=ED=94=84=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EB=9E=A8=20=EC=B5=9C=EC=B4=88=20=EC=97=85=EB=A1=9C=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ILSOS_CCW_H52A/Datas/CellInfoScheduler.cs | 123 ++++++ Machines/ILSOS_CCW_H52A/Datas/JigInfo.cs | 150 +++++++ Machines/ILSOS_CCW_H52A/EquipDef.cs | 10 +- Machines/ILSOS_CCW_H52A/EquipmentSetup.cs | 2 + Machines/ILSOS_CCW_H52A/ILSOS_CCW_H52A.csproj | 2 + .../ProcessObjects/ProcessManager.cs | 10 +- .../Sequences/SeqAutoRunCCWH52A.cs | 400 +++++++++--------- .../Sequences/SeqAutoRunRMOTF.cs | 1 + 8 files changed, 486 insertions(+), 212 deletions(-) create mode 100644 Machines/ILSOS_CCW_H52A/Datas/CellInfoScheduler.cs create mode 100644 Machines/ILSOS_CCW_H52A/Datas/JigInfo.cs 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();