diff --git a/AuroraDevices/AuroraCellTracker/CellTrackerConfig.cs b/AuroraDevices/AuroraCellTracker/CellTrackerConfig.cs index 847eea3..7ef9289 100644 --- a/AuroraDevices/AuroraCellTracker/CellTrackerConfig.cs +++ b/AuroraDevices/AuroraCellTracker/CellTrackerConfig.cs @@ -28,6 +28,8 @@ namespace AuroraCellTracker public int LotIdStartNo { get; set; } public int LotIdLength { get; set; } public int CellIdReceiveMethod { get; set; } - + [DirectoryPath] + public string reportDataLocalPath { get; set; } = "D:\\ILSOSConfig\\_device\\_cellTracker\\"; + public string EquipmentName { get; set; } = "CCW"; } } diff --git a/AuroraDevices/AuroraCellTracker/DaekhonCoporation/CellTrackerDaekhon.cs b/AuroraDevices/AuroraCellTracker/DaekhonCoporation/CellTrackerDaekhon.cs index 7d7189a..7eaf516 100644 --- a/AuroraDevices/AuroraCellTracker/DaekhonCoporation/CellTrackerDaekhon.cs +++ b/AuroraDevices/AuroraCellTracker/DaekhonCoporation/CellTrackerDaekhon.cs @@ -21,6 +21,8 @@ namespace AuroraCellTracker.DaekhonCoporation Logger _Logger = new Logger("CellTrackerDaekhon"); private const string REG_PATH = @"Software\H52A\H52A_ADPT_CELL"; + public event Action OnCellReceived; + public CellInfoStr CellInfoStrings { get; set; } = new CellInfoStr(); public ConcurrentQueue CellInfoQueue { get; set; } = new ConcurrentQueue(); @@ -318,6 +320,7 @@ namespace AuroraCellTracker.DaekhonCoporation CalcCellInfo(str, out lotId, out cellId); AddCellInfo(lotId, cellId); + OnCellReceived?.Invoke(lotId, cellId); } catch (Exception e) { _Logger.E(string.Format($"{e.Message}")); } @@ -339,6 +342,7 @@ namespace AuroraCellTracker.DaekhonCoporation //CalcCellInfo(str, out lotId, out cellId); AddCellInfo(lotId, cellId); + OnCellReceived?.Invoke(lotId, cellId); } catch (Exception e) { _Logger.E(string.Format($"{e.Message}")); } diff --git a/AuroraDevices/AuroraCellTracker/ICellTracker.cs b/AuroraDevices/AuroraCellTracker/ICellTracker.cs index 2ff9988..b0a0da0 100644 --- a/AuroraDevices/AuroraCellTracker/ICellTracker.cs +++ b/AuroraDevices/AuroraCellTracker/ICellTracker.cs @@ -10,6 +10,12 @@ namespace AuroraCellTracker { public interface ICellTracker { + /// + /// DllSocket_ReceiveDataBuffer에서 lotId/cellId 파싱 완료 시 발화. + /// ProcessManager 등 외부에서 구독하여 직접 수신 가능. + /// + event Action OnCellReceived; + void Initialize(eMode mode, string dllDirectory); void Close(); void CheckDllStatus(); diff --git a/Machines/ILSOS_CCW_H52A/Datas/CellInfoScheduler.cs b/Machines/ILSOS_CCW_H52A/Datas/CellInfoScheduler.cs index 9ac1abf..e052224 100644 --- a/Machines/ILSOS_CCW_H52A/Datas/CellInfoScheduler.cs +++ b/Machines/ILSOS_CCW_H52A/Datas/CellInfoScheduler.cs @@ -1,7 +1,9 @@ +using Aurora.Api; +using Aurora.Util.HislogPlus; +using ILSOS_CCW_H52A.Data; +using ILSOS_CCW_H52A.Sequences; using System; using System.Collections.Generic; -using ILSOS_CCW_H52A.Sequences; -using ILSOS_CCW_H52A.Data; namespace ILSOS_CCW_H52A.Data { @@ -15,7 +17,7 @@ namespace ILSOS_CCW_H52A.Data } /// - /// Dictionary<eJigNo, JigInfo> 상속으로 물리 지그 번호 직접 접근과 + /// Dictionary 상속으로 물리 지그 번호 직접 접근과 /// 파이프라인 단계 접근을 동시에 지원하는 셀 스케줄러. /// /// - 물리 지그 접근: scheduler[eJigNo.Jig5] @@ -31,6 +33,7 @@ namespace ILSOS_CCW_H52A.Data /// public class CellInfoScheduler : Dictionary { + private readonly Logger _log = new Logger("CellInfoScheduler"); public JigInfo Prefetch2 { get; private set; } // 전전 셀: 셀 정보 수신, 업로드 전 public JigInfo Prefetch1 { get; private set; } // 전 셀: 업로드 완료, 실행 중 / 완료 체크 대상 @@ -38,23 +41,27 @@ namespace ILSOS_CCW_H52A.Data public JigInfo ReportTarget { get; set; } // 재시작 보존용 독립 슬롯 — Reset()에서 건드리지 않음 - private readonly JigInfo _pendingSlot = new JigInfo(); + // 재시작 시, List를 최초 리스트 2번을 생성하는 로직 때문에.. 필요함 + private readonly JigInfo _pendingSlot = new JigInfo(); // 용접 직후 (용접 완, 보고 미완) + private readonly JigInfo _pendingPrefetch1 = new JigInfo(); // 정지 직전 Prefetch1 (업로드 완료, 용접 미완) + private readonly JigInfo _pendingPrefetch2 = new JigInfo(); // 정지 직전 Prefetch2 (업로드 완료, 용접 미완) // 재시작 전 미완료 보고 슬롯 (유효 시 non-null) public JigInfo PendingComplete => HasPendingComplete ? _pendingSlot : null; // 재시작 보존 보고가 있는지 - public bool HasPendingComplete - => _pendingSlot.IsCanExist && !_pendingSlot.ReportComplete; + public bool HasPendingComplete => _pendingSlot.IsCanExist && !_pendingSlot.ReportComplete; // 현재 활성 보고 슬롯 — 재시작 pending 우선 - public JigInfo ActiveReportSlot - => HasPendingComplete ? _pendingSlot : ReportTarget; + public JigInfo ActiveReportSlot => HasPendingComplete ? _pendingSlot : ReportTarget; - // 보고가 필요한 상태인지 (정상 + 재시작 모두 포함) - public bool HasReport - => HasPendingComplete - || (ReportTarget != null && !ReportTarget.ReportComplete); + // 정지 전 미용접 슬롯이 보존돼 있는지 (재시작 후 복원 가능 여부) + public bool HasPendingPrefetch => + !string.IsNullOrEmpty(_pendingPrefetch1.CellId) + || !string.IsNullOrEmpty(_pendingPrefetch2.CellId); + + // 보고가 필요한 상태인지 (정상 + 재시작 모두 포함) >> 용접 완, 보고 미완 + public bool HasReport => HasPendingComplete || (ReportTarget != null && !ReportTarget.ReportComplete); public CellInfoScheduler() { @@ -62,17 +69,63 @@ namespace ILSOS_CCW_H52A.Data Add(no, new JigInfo()); } - // 1-based jigNo (_WeldingJigNo 등) → 0-based eJigNo 변환 편의 인덱서 + // 1-based jigNo (_WeldingJigNo 등) → 0-based eJigNo 변환 의의 인덱서 public JigInfo this[int jigNo] { get => base[(eJigNo)(jigNo - 1)]; } - // Reset() 전에 호출 — 미완료 보고가 있으면 보류 슬롯(_pendingSlot)에 복사하여 보존. + // Reset() 전에 호출 — 미완료 보고 및 미용접 슬롯을 보존. public void SavePendingComplete() { - if (ReportTarget != null && ReportTarget.IsCanExist - && ReportTarget.WeldComplete && !ReportTarget.ReportComplete) + // 용접 완료됐으나 보고 전 정지된 경우 (_pendingSlot) + if (ReportTarget != null + && ReportTarget.IsCanExist + && ReportTarget.WeldComplete + && !ReportTarget.ReportComplete) _pendingSlot.CopyFrom(ReportTarget); + + // 업로드 완료됐으나 로터리 정지로 용접 자체가 안 된 경우 (_pendingPrefetch1/2) + // (연속셀이 들어왔을때, 중간에 멈춘 경우) + if (Prefetch1 != null + && Prefetch1.IsCanExist + && !string.IsNullOrEmpty(Prefetch1.CellId) + && !Prefetch1.WeldComplete) + _pendingPrefetch1.CopyFrom(Prefetch1); + else + _pendingPrefetch1.Clear(); + + if (Prefetch2 != null + && Prefetch2.IsCanExist + && !string.IsNullOrEmpty(Prefetch2.CellId) + && !Prefetch2.WeldComplete) + _pendingPrefetch2.CopyFrom(Prefetch2); + else + _pendingPrefetch2.Clear(); + } + + /// + /// 재시작 시 해당 JigNo의 보존된 셀 정보 복원 시도. + /// 성공 시 해당 슬롯을 즉시 클리어하여 한 번만 사용되도록 보장. + /// canExist=false 지그가 들어오면 슬롯을 소비(폐기)하여 이후 오염 방지. + /// + /// 복원 성공 시 true (ReceivedCellInfoQueue TryDequeue 불필요) + public bool TryRestoreCell(int jigNo, out string lotId, out string cellId) + { + if (_pendingPrefetch1.JigNo == jigNo && !string.IsNullOrEmpty(_pendingPrefetch1.CellId)) { + lotId = _pendingPrefetch1.LotId; + cellId = _pendingPrefetch1.CellId; + _pendingPrefetch1.Clear(); + return true; + } + if (_pendingPrefetch2.JigNo == jigNo && !string.IsNullOrEmpty(_pendingPrefetch2.CellId)) { + lotId = _pendingPrefetch2.LotId; + cellId = _pendingPrefetch2.CellId; + _pendingPrefetch2.Clear(); + return true; + } + lotId = null; + cellId = null; + return false; } // 보고 완료 처리 — ReportTarget 마킹 및 _pendingSlot 클리어. @@ -90,6 +143,13 @@ namespace ILSOS_CCW_H52A.Data /// /// 새 Prefetch2로 지정할 물리 지그 (eJigNo) public void Shift(eJigNo nextJig) + { + Prefetch1 = Prefetch2; + Prefetch2 = base[nextJig]; + Prefetch2.Clear(); + } + // AutoRun에서 사용되는 자료형으로 추가 (편의 인덱서 사용) + public void Shift(int nextJig) { Prefetch1 = Prefetch2; Prefetch2 = this[nextJig]; @@ -99,7 +159,7 @@ namespace ILSOS_CCW_H52A.Data /// /// 전체 초기화 (AutoRun 시작 시 호출). /// SavePendingComplete() 호출 후 이 메서드를 호출할 것. - /// _pendingSlot은 재시작 보고 보존을 위해 건드리지 않음. + /// _pendingSlot / _pendingPrefetch1 / _pendingPrefetch2 는 재시작 복원을 위해 건드리지 않음. /// public void Reset() { @@ -109,7 +169,7 @@ namespace ILSOS_CCW_H52A.Data Prefetch2 = null; Prefetch1 = null; ReportTarget = null; - // _pendingSlot: SavePendingComplete()가 복사해 둔 데이터를 유지 + // _pendingSlot, _pendingPrefetch1/2: SavePendingComplete()가 복사해 둔 데이터를 유지 } /// diff --git a/Machines/ILSOS_CCW_H52A/Datas/JigInfo.cs b/Machines/ILSOS_CCW_H52A/Datas/JigInfo.cs index e910175..0fdacb3 100644 --- a/Machines/ILSOS_CCW_H52A/Datas/JigInfo.cs +++ b/Machines/ILSOS_CCW_H52A/Datas/JigInfo.cs @@ -23,13 +23,12 @@ namespace ILSOS_CCW_H52A.Data public DateTime WeldEndTime; public TimeSpan WeldingSpanTime; // 스캐너 리스트 실행 시간 (GetTime() 결과) - // --- CellTracker 수신 정보 (GetReceiveCellInfo_SeqResult에서 채워짐) --- + // CellTracker 수신 정보 public string LotId; public string CellId; // --- 플래그 --- public bool CheckSendCellinfo; - public bool CheckSendPLCLaserPower; public bool ReportComplete; // --- 리포팅/뷰 용 상세 정보 --- @@ -48,22 +47,21 @@ namespace ILSOS_CCW_H52A.Data /// 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; + 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; + LotId = src.LotId; + CellId = src.CellId; + CheckSendCellinfo = src.CheckSendCellinfo; + ReportComplete = src.ReportComplete; UpdateCellInfo(src.InnerCellInfo); } @@ -81,40 +79,17 @@ namespace ILSOS_CCW_H52A.Data IsCanExist = false; IsWelding = false; WeldComplete = false; - WeldStartTime = DateTime.MinValue; - WeldEndTime = DateTime.MinValue; + 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.Clear(); } /// diff --git a/Machines/ILSOS_CCW_H52A/Sequences/ProcessObjects/ProcessManager.cs b/Machines/ILSOS_CCW_H52A/Sequences/ProcessObjects/ProcessManager.cs index 7b477c2..aa4664c 100644 --- a/Machines/ILSOS_CCW_H52A/Sequences/ProcessObjects/ProcessManager.cs +++ b/Machines/ILSOS_CCW_H52A/Sequences/ProcessObjects/ProcessManager.cs @@ -1,10 +1,12 @@ using Aurora; using Aurora.Api; using Aurora.Util.HislogPlus; +using AuroraCellTracker; using AuroraLaserScanner; using AuroraPLC; using ILSOS_CCW_H52A.Data; using ILSOS_CCW_H52A.Device; +using OpenCvSharp; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -38,9 +40,15 @@ namespace ILSOS_CCW_H52A.Sequences.ProcessObjects private IAuroraEnvironment _env; private PLCControllerCCWH52A _plc; private ScannerCCWH52A _scanner; + private CellTracker _cellTracker; public CellInfoScheduler cellScheduler { get; set; } + /// CellTracker 콜백에서 직접 수신한 (LotId, CellId) 큐. + /// DllSocket_ReceiveDataBuffer 발화 시 즉시 Enqueue — 시퀀스는 TryDequeue로 소비. + public ConcurrentQueue<(string LotId, string CellId)> ReceivedCellInfoQueue { get; } + = new ConcurrentQueue<(string, string)>(); + public const double compensateEncoderReset = 0.015; // 엔코더 리셋 보상 각도 설정(70도/sec 기준 약 0.015도) public const int RotaryJigCount = 16; public const int RotaryMaxAngle = 360; @@ -53,8 +61,21 @@ namespace ILSOS_CCW_H52A.Sequences.ProcessObjects _env = env; _plc = _env.DeviceManager[DeviceIds.PLCDevice] as PLCControllerCCWH52A; _scanner = _env.DeviceManager[DeviceIds.Scanner] as ScannerCCWH52A; + _cellTracker = _env.DeviceManager[CommonDeviceIds.CellTracker] as CellTracker; + + //CellTracker의 OnCellReceived 이벤트를 구독하여 콜백 수신 시 CellQueue에 직접 적재 + if (_cellTracker._CellTracker != null) + _cellTracker._CellTracker.OnCellReceived += OnCellReceived; + cellScheduler = new CellInfoScheduler(); } + + private void OnCellReceived(string lotId, string cellId) + { + ReceivedCellInfoQueue.Enqueue((lotId, cellId)); + _log.I($"Cell Tracker CallBack Received >> LotId({lotId}) / CellId({cellId})"); + } + public IEnumerable CheckRotaryState() { List rotaryStates = new List(); diff --git a/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunCCWH52A.cs b/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunCCWH52A.cs index 4664ab5..579f407 100644 --- a/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunCCWH52A.cs +++ b/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunCCWH52A.cs @@ -19,6 +19,8 @@ using ILSOS_CCW_H52A.Device; using ILSOS_CCW_H52A.Models; using ILSOS_CCW_H52A.RMS; using ILSOS_CCW_H52A.Sequences.ProcessObjects; +using Microsoft.Win32; +using OpenCvSharp; using System; using System.Collections.Generic; using System.IO; @@ -26,6 +28,7 @@ using System.Linq; using System.Reflection; using System.Threading; using System.Windows; +using System.Windows.Media.Animation; using WeldingCommon; using WeldingCommon.Alarm; using WeldingCommon.Devices; @@ -57,9 +60,9 @@ namespace ILSOS_CCW_H52A.Sequences public const string SEQ_CHECK_SCANNER_LIST_COMPLETE = "[SeqAutoRunCCWH52A] Check Scanner List Complete"; const string SEQ_READY_SCANNER_LIST = "[SeqAutoRunCCWH52A] Ready ScannerList"; - const string SEQ_SET_PARAMS_RECEIVE_CELL_INFO_SEQ = "[SeqAutoRunCCWH52A] Set Sequence Param (ReceiveCellInfo Seq)"; - 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_RECEIVE_CELL_INFO_SEQ = "[SeqAutoRunCCWH52A] Set Sequence Param (ReceiveCellInfo Seq)"; + //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)"; @@ -68,10 +71,11 @@ namespace ILSOS_CCW_H52A.Sequences 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"; + const string SEQ_REPORT_CELL_RESULT = "[SeqAutoRunCCWH52A] Report Cell Result"; - const string SEQ_SET_PARAMS_REPORT_CELL_RESULT_SEQ = "[SeqAutoRunCCWH52A] Set Sequence Param (ReportCellResult Seq)"; - const string SEQ_DO_ID_REPORT_CELL_RESULT_SEQ = "[SeqAutoRunCCWH52A] Process (ReportCellResult Seq)"; - const string SEQ_GET_RESULT_REPORT_CELL_RESULT_SEQ = "[SeqAutoRunCCWH52A] Get Sequence Result (ReportCellResult Seq)"; + //const string SEQ_SET_PARAMS_REPORT_CELL_RESULT_SEQ = "[SeqAutoRunCCWH52A] Set Sequence Param (ReportCellResult Seq)"; + //const string SEQ_DO_ID_REPORT_CELL_RESULT_SEQ = "[SeqAutoRunCCWH52A] Process (ReportCellResult Seq)"; + //const string SEQ_GET_RESULT_REPORT_CELL_RESULT_SEQ = "[SeqAutoRunCCWH52A] Get Sequence Result (ReportCellResult Seq)"; //const string SEQ_REPORT_CELLINFO_TO_PLC = "[SeqAutoRunCCWH52A] ReportCellInfoToPLC"; const string SEQ_CHECK_CYCLE_STOP = "[SeqAutoRunCCWH52A] Check Cycle Stop"; @@ -168,9 +172,10 @@ namespace ILSOS_CCW_H52A.Sequences 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 정보를 가져온다 +#warning 아래 서브시퀀스가 CellTracker에서 받은 데이터를 WeldingData에 넣기만함, ProcessManager에서 Cell정보를 직접 Callback으로 받도록 수정함 + //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_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ, SetUploadWeldingList_SeqParams); sb.AddSubsequence(SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ, SequenceManager.GetSeqBuilder_SeqUploadWeldingList()); @@ -178,13 +183,15 @@ namespace ILSOS_CCW_H52A.Sequences sb.AddAction(SEQ_EXECUTE_WELDING, ExecuteWelding); sb.AddAction(SEQ_REQUEST_UNLOAD_CELL, RequestUnloadCell); + sb.AddDecision(SEQ_CHECK_CAN_WELDED, CheckCanWelded); + sb.AddAction(SEQ_REPORT_CELL_RESULT, ReportCellResult); - sb.AddAction(SEQ_SET_PARAMS_REPORT_CELL_RESULT_SEQ, SetReportCellResult_SeqParams); - sb.AddSubsequence(SEQ_DO_ID_REPORT_CELL_RESULT_SEQ, SequenceManager.GetSeqBuilder_SeqReportCellResult()); - sb.AddAction(SEQ_GET_RESULT_REPORT_CELL_RESULT_SEQ, GetReportCellResult_SeqResult); +#warning ReportCellResult함수로 아래 시퀀스 대체 + //sb.AddAction(SEQ_SET_PARAMS_REPORT_CELL_RESULT_SEQ, SetReportCellResult_SeqParams); + //sb.AddSubsequence(SEQ_DO_ID_REPORT_CELL_RESULT_SEQ, SequenceManager.GetSeqBuilder_SeqReportCellResult()); + //sb.AddAction(SEQ_GET_RESULT_REPORT_CELL_RESULT_SEQ, GetReportCellResult_SeqResult); sb.AddDecision(SEQ_CHECK_CYCLE_STOP, CheckCycleStop); - sb.AddDecision(SEQ_CHECK_CAN_WELDED, CheckCanWelded); sb.AddAction(SEQ_SAVE_RESULT_DATA, SaveResultData); // 해당 공정의 정보를 기록 함. sb.AddAction(SEQ_WRITE_COMPLETION_PLC_BIT, WriteCompletionPLCBit); // 정상적으로 종료 되면, complete bit write @@ -193,12 +200,11 @@ namespace ILSOS_CCW_H52A.Sequences sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_FORK_ENCODER_MONITOR] > sb[SEQ_CHECK_LOAD_COMPLETE] > 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] + > sb[SEQ_EXECUTE_WELDING] > sb[SEQ_REQUEST_UNLOAD_CELL] > sb[SEQ_CHECK_CAN_WELDED] > sb[SEQ_REPORT_CELL_RESULT] + //> sb[SEQ_SET_PARAMS_REPORT_CELL_RESULT_SEQ] > sb[SEQ_DO_ID_REPORT_CELL_RESULT_SEQ] > sb[SEQ_GET_RESULT_REPORT_CELL_RESULT_SEQ] > sb[SEQ_SAVE_RESULT_DATA] > sb[SEQ_CHECK_CYCLE_STOP] - > sb[SEQ_WRITE_COMPLETION_PLC_BIT] > sb[SEQ_JOIN_ENCODER_MONITOR] > sb[SEQ_COMPLETE]); + > sb[SEQ_WRITE_COMPLETION_PLC_BIT] > sb[SEQ_JOIN_ENCODER_MONITOR] > sb[SEQ_COMPLETE] > sb.Final); sb.Flows(sb[SEQ_READY_SCANNER_LIST] > sb[SEQ_EXECUTE_WELDING]); @@ -206,7 +212,7 @@ namespace ILSOS_CCW_H52A.Sequences sb.Flows(sb[SEQ_CHECK_CYCLE_STOP] > sb[SEQ_CHECK_LOAD_COMPLETE]); sb.Flows(sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_INIT_CONDITION_ERROR] > sb.Final); - sb.Flows(sb[SEQ_FORK_ENCODER_MONITOR] > sb[SEQ_ENCODER_MONITOR] > sb[SEQ_JOIN_ENCODER_MONITOR] > sb[SEQ_COMPLETE]); + sb.Flows(sb[SEQ_FORK_ENCODER_MONITOR] > sb[SEQ_ENCODER_MONITOR] > sb[SEQ_JOIN_ENCODER_MONITOR] > sb[SEQ_COMPLETE] > sb.Final); #region //sb.Flows(sb[SEQ_FORK_ENCODER_MONITOR] > sb[SEQ_ENCODER_MONITOR]); @@ -443,6 +449,8 @@ namespace ILSOS_CCW_H52A.Sequences // SavePendingComplete()로 _pendingSlot에 복사한 뒤 Reset()을 호출한다. _CellScheduler.SavePendingComplete(); + if (_CellScheduler.HasPendingPrefetch) + Log.I("[PendingRestore] 미용접 셀 보존됨 — 재시작 후 JigNo 매칭 시 복원 예정"); _CellScheduler.Reset(); // ───────────────────────────────────────────────────────────────────────────── @@ -690,11 +698,11 @@ namespace ILSOS_CCW_H52A.Sequences else canExist = GetRotaryCanExist(_WeldingJigNo); - Log.I("Can Exist : " + canExist.ToString() + ", JigNo " + _WeldingJigNo + " ,CountNo " + _WeldingCount + ", StartAngle(Y) : " + _WeldingStartAngle); + //Log.I("Can Exist : " + canExist.ToString() + ", JigNo " + _WeldingJigNo + " ,CountNo " + _WeldingCount + ", StartAngle(Y) : " + _WeldingStartAngle); // canExist 여부와 무관하게 매 지그마다 파이프라인 전진 // canExist=false 지그도 Shift()해야 Prefetch1 타이밍이 올바르게 유지됨. - _CellScheduler.Shift((eJigNo)(_WeldingJigNo - 1)); + _CellScheduler.Shift(_WeldingJigNo); _CellScheduler.Prefetch2.JigNo = _WeldingJigNo; _CellScheduler.Prefetch2.ListNo = _WeldingScannerListNo; @@ -703,6 +711,36 @@ namespace ILSOS_CCW_H52A.Sequences _CellScheduler.Prefetch2.WeldingCount = _WeldingCount; _CellScheduler.Prefetch2.IsCanExist = canExist; +#warning OC2 ACW 기준 >> Cell Tracker는 어느 시점에 데이터는 주는지 모르겠지만, '전전전 셀' 쯤에서 이미 건네받음! 다른 프로젝트에서 확인 필요함 + // 복원 우선: 정지 전 업로드됐으나 로터리 감속·정지로 용접 미완된 셀 정보 복원. + // canExist=false 지그도 TryRestoreCell을 호출하여 슬롯 소비(폐기) — 이후 오염 방지. + if (_CellScheduler.TryRestoreCell(_WeldingJigNo, out var rLot, out var rCell)) { + if (canExist) { + _CellScheduler.Prefetch2.LotId = rLot; + _CellScheduler.Prefetch2.CellId = rCell; + Status.GetProcessingWeldingData().WorkLotId = rLot; + Status.GetProcessingWeldingData().WorkCellId = rCell; + Log.I($"[PendingRestore] Jig{_WeldingJigNo} 셀 복원 >> CellId({rCell})"); + } + else { + Log.W($"[PendingRestore] Jig{_WeldingJigNo} canExist=false, 복원 데이터 폐기 >> CellId({rCell})"); + } + } + else if (ProcessManager.Instance.ReceivedCellInfoQueue.TryDequeue(out var cell)) { + _CellScheduler.Prefetch2.LotId = cell.LotId; + _CellScheduler.Prefetch2.CellId = cell.CellId; + Status.GetProcessingWeldingData().WorkLotId = cell.LotId; + Status.GetProcessingWeldingData().WorkCellId = cell.CellId; + } + + Log.I($"상태 시프트(Rotate) >> 용접 전전셀({_CellScheduler.Prefetch2.CellId}) [CanExist({_CellScheduler.Prefetch2.IsCanExist})] [JigNo({_CellScheduler.Prefetch2.JigNo})] " + + $"[ListNo({_CellScheduler.Prefetch2.ListNo})] [WeldingCount({_CellScheduler.Prefetch2.WeldingCount})]" + + $"[StartAngle(Y)({_CellScheduler.Prefetch2.StartAngle})]"); + + Log.I($"이번 용접대상 >> 용접 전셀({_CellScheduler.Prefetch1.CellId}) [CanExist({_CellScheduler.Prefetch1.IsCanExist})] [JigNo({_CellScheduler.Prefetch1.JigNo})] " + + $"[ListNo({_CellScheduler.Prefetch1.ListNo})] [WeldingCount({_CellScheduler.Prefetch1.WeldingCount})]" + + $"[StartAngle(Y)({_CellScheduler.Prefetch1.StartAngle})]"); + if (canExist) { #region 용접할 Objects 선택 if (_WeldingJigNo - 1 < _ScannerCanvasUserCtrl.Model.Layers.Length) { @@ -718,7 +756,7 @@ namespace ILSOS_CCW_H52A.Sequences _RecipeNo = _RecipeManager.ActivatedRecipe().Id; SetWeldingData(); - return SEQ_SET_PARAMS_RECEIVE_CELL_INFO_SEQ; + return SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ; } else { return SEQ_EXECUTE_WELDING; @@ -847,22 +885,22 @@ namespace ILSOS_CCW_H52A.Sequences return SequenceAction.Ok(); } - private SequenceActionResult SetReceiveCellInfo_SeqParams(SequenceContext context, string id, SequenceActionResult param) - { - SeqReceiveCellInfo_InitParam seqParams = new SeqReceiveCellInfo_InitParam() { - }; + //private SequenceActionResult SetReceiveCellInfo_SeqParams(SequenceContext context, string id, SequenceActionResult param) + //{ + // SeqReceiveCellInfo_InitParam seqParams = new SeqReceiveCellInfo_InitParam() { + // }; - return SequenceAction.Ok(seqParams); - } + // return SequenceAction.Ok(seqParams); + //} - private SequenceActionResult GetReceiveCellInfo_SeqResult(SequenceContext context, string id, SequenceActionResult param) - { - var resultOutParam = param.Param as SeqReceiveCellInfo_OutParam; - if (resultOutParam.Success == false) { - return null; - } - return SequenceAction.Ok(); - } + //private SequenceActionResult GetReceiveCellInfo_SeqResult(SequenceContext context, string id, SequenceActionResult param) + //{ + // var resultOutParam = param.Param as SeqReceiveCellInfo_OutParam; + // if (resultOutParam.Success == false) { + // return null; + // } + // return SequenceAction.Ok(); + //} private SequenceActionResult ExecuteWelding(SequenceContext context, string id, SequenceActionResult param) { @@ -905,7 +943,8 @@ namespace ILSOS_CCW_H52A.Sequences double encoderX, currentAngle; _Scanner.GetEncoder(out encoderX, out currentAngle, true); 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; + bool WeldingInPosition = encoderX > _Scanner.Config.MarkingRangeMinRotate + 11.25 - send_time_offset - 0.1 + && encoderX < _Scanner.Config.MarkingRangeMinRotate + 11.25 - send_time_offset + 0.1; // ── JigScheduler: 포크 내에서 참조 캡처 (Shift와의 경합 방지) ────── // Prefetch1: 2회 전 업로드된 지그 = 현재 물리적으로 실행 중인 지그. @@ -945,7 +984,7 @@ namespace ILSOS_CCW_H52A.Sequences 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})"); + Log.I($"Fork Laser On({LaserIO}) 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) { @@ -998,6 +1037,58 @@ namespace ILSOS_CCW_H52A.Sequences Status.GetProcessingWeldingData().WeldingJigNo = report.JigNo.ToString(); } } + + private SequenceActionResult ReportCellResult(SequenceContext context, string id, SequenceActionResult param) + { + Status.SetWorkingPosition(eWorkingPosition.REPORTING_RESULT); + + string strDate = DateTime.Now.Date.ToString("yyyyMMdd"); + string strTime = DateTime.Now.ToString("HHmmss"); + + var wd = Status.GetProcessingWeldingData(); + string reportDataLocalPath = _CellTracker._CellTrackerConfig.reportDataLocalPath; + string equipName = _CellTracker._CellTrackerConfig.EquipmentName; + + string directory = string.Format(reportDataLocalPath + "{0}", strDate); + if (!Directory.Exists(directory)) + Directory.CreateDirectory(directory); + + string strPath = string.Format(reportDataLocalPath + "{0}\\{1}_{2}_{3}.csv", + strDate, strDate, "H52A_" + equipName, strTime.Substring(0, 2)); + + // ECS 전송용 strData 빌드 + string strData = string.Format( + "Date={0},Time={1},LotID={2},CellID={3}," + + equipName + "DATA001={4}," + + equipName + "DATA002={5}," + + equipName + "DATA003={6}," + + equipName + "DATA004={7}," + + equipName + "DATA005={8}," + + equipName + "DATA006={9}," + + equipName + "DATA007={10}," + + equipName + "DATA008={11}," + + equipName + "DATA009={12}", + strDate, strTime, wd.LotId, wd.CellId, + wd.RecipeNo, wd.LaserPowerCore, wd.MarkSpeed, wd.FocalLength, + wd.WeldingJigNo, wd.WeldingSpanTimeMilliseconds, wd.LaserPowerByLBAD, + wd.TodayProduct, wd.TotalProduct); + _CellTracker.SendEcsData(wd.CellId, strPath, strData); + + // 로컬 CSV 저장 + string strItem = "//Date, Time, LotID, CellID, RecipeNo, LaserPower, MarkSpeed, FocalLength, HeadNo, WeldingTime, LaserPowerByLBAD, TodayCount, TotalCount"; + string strCsvData = string.Format("{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}", + strDate, strTime, wd.LotId, wd.CellId, + wd.RecipeNo, wd.LaserPowerCore, wd.MarkSpeed, wd.FocalLength, + wd.WeldingJigNo, wd.WeldingSpanTimeMilliseconds, wd.LaserPowerByLBAD, + wd.TodayProduct, wd.TotalProduct); + WeldingCommon.Utils.ExcelManagementUnit.AppendStringInCSV(strPath, strItem, strCsvData); + + // 보고 완료: ReportTarget.ReportComplete=true 설정 + pendingSlot 클리어 + _CellScheduler.CompleteReport(); + + return SequenceAction.Ok(); + } + private SequenceActionResult SetReportCellResult_SeqParams(SequenceContext context, string id, SequenceActionResult param) { Status.SetWorkingPosition(eWorkingPosition.REPORTING_RESULT); @@ -1049,7 +1140,7 @@ namespace ILSOS_CCW_H52A.Sequences 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; + return SEQ_REPORT_CELL_RESULT; } // ───────────────────────────────────────────────────────────────────────────── @@ -1057,7 +1148,7 @@ namespace ILSOS_CCW_H52A.Sequences // - ReportTarget이 설정돼 있고 아직 보고 완료되지 않은 경우 // - 첫번째, 두번째 반복은 SetReportTarget이 호출되지 않으므로 자연스럽게 통과 if (_CellScheduler.HasReport) - return SEQ_SET_PARAMS_REPORT_CELL_RESULT_SEQ; + return SEQ_REPORT_CELL_RESULT; return SEQ_CHECK_CYCLE_STOP; } @@ -1073,7 +1164,6 @@ namespace ILSOS_CCW_H52A.Sequences } private SequenceActionResult SaveResultData(SequenceContext context, string id, SequenceActionResult param) { - context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, true); return SequenceAction.Ok(); } @@ -1201,6 +1291,12 @@ namespace ILSOS_CCW_H52A.Sequences } } + // 정지 시점 파이프라인 상태 로그 (재시작 후 복원 여부 사전 확인용) + if (_CellScheduler.Prefetch1 != null && !string.IsNullOrEmpty(_CellScheduler.Prefetch1.CellId)) + Log.I($"Stoped Seq >> Prefetch1[Jig{_CellScheduler.Prefetch1.JigNo}] CellId={_CellScheduler.Prefetch1.CellId}, WeldComplete={_CellScheduler.Prefetch1.WeldComplete}"); + if (_CellScheduler.Prefetch2 != null && !string.IsNullOrEmpty(_CellScheduler.Prefetch2.CellId)) + Log.I($"Stoped Seq >> Prefetch2[Jig{_CellScheduler.Prefetch2.JigNo}] CellId={_CellScheduler.Prefetch2.CellId}, WeldComplete={_CellScheduler.Prefetch2.WeldComplete}"); + //PLC All BIT OFF for (int i = 0; i < _RotaryJigCount; i++) _PLC.WriteBit(eBitOutput.PC_WELD_COMPLETE_1 + i, false); diff --git a/WeldingComponents/WeldingCommon/Data/WeldingData.cs b/WeldingComponents/WeldingCommon/Data/WeldingData.cs index 81576d4..28f4c49 100644 --- a/WeldingComponents/WeldingCommon/Data/WeldingData.cs +++ b/WeldingComponents/WeldingCommon/Data/WeldingData.cs @@ -103,11 +103,34 @@ namespace WeldingCommon.Data public WeldingData() { + Clear(); } public void Clear() { - + this.CellId = string.Empty; + this.LotId = string.Empty; + this.AutoRunStartTime = DateTime.MinValue; + this.WeldingStartTime = DateTime.MinValue; + this.WeldingEndTime = DateTime.MinValue; + this.WeldingSpanTimeMilliseconds = string.Empty; + this.TodayProduct = 0; + this.TotalProduct = 0; + this.RecipeNo = 0; + this.WeldStartTime = string.Empty; + this.WeldTime = string.Empty; + this.WorkCellId = string.Empty; + this.WorkLotId = string.Empty; + this.LBAD_Judge = string.Empty; + this.LDD_Judge = string.Empty; + this.LaserPowerCore = string.Empty; + this.LaserPowerRing = string.Empty; + this.MarkSpeed = string.Empty; + this.FocalLength = string.Empty; + this.WeldingJigNo = string.Empty; + this.LaserPowerByLBAD = string.Empty; + this.CheckCanExist = false; + this.CheckScannerListEnd = false; } public object Clone()