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; using System.Linq; using System.Reactive.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Windows; using WeldingCommon; using WeldingCommon.Data; using static ILSOS_CCW_H52A.Device.PLCControllerCCWH52A; namespace ILSOS_CCW_H52A.Sequences.ProcessObjects { public class ProcessManager { private static ProcessManager _Instance; public static ProcessManager Instance { get { if (_Instance is null) { _Instance = new ProcessManager(); } return _Instance; } } private bool usedAnalyzerLog = false; private readonly Logger _log = new Logger("ProcessManager"); 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; public double DegreePerJig { get => RotaryMaxAngle / RotaryJigCount; } public double HalfDegreePerJig { get => DegreePerJig / 2; } //public ConcurrentQueue public void Init(IAuroraEnvironment env) { _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) { lotId = lotId.Replace("\0", "").Trim(); cellId = cellId.Replace("\0", "").Trim(); ReceivedCellInfoQueue.Enqueue((lotId, cellId)); _log.I($"Cell Tracker CallBack Received >> [LotId({lotId})] [CellId({cellId})] [QueueCount({ReceivedCellInfoQueue.Count})]"); } /// /// 재시작 시 큐 잔류 데이터 전량 폐기. /// CellTracker는 투입 기준으로 선읽음하므로, 정지 시점에 큐에 남은 데이터는 /// 이미 처리됐거나 이전 회차의 셀 정보일 수 있어 모두 버린다. /// _pendingPrefetch1/2 는 이미 TryDequeue 이후 저장된 별도 슬롯이므로 영향 없음. /// public void DrainCellInfoQueue() { int count = 0; while (ReceivedCellInfoQueue.TryDequeue(out _)) count++; if (count > 0) _log.W($"[QueueDrain] 재시작 시 잔류 데이터 {count}건 폐기"); } public IEnumerable CheckRotaryState() { List rotaryStates = new List(); for (int i = 0; i < RotaryJigCount; i++) { var rotaryProcessState = _plc.ReadBit(eBitInput.PLC_USE_WELD_JIG_1 + i).data; rotaryStates.Add(rotaryProcessState); //if (rotaryProcessState == false) // _plc.ToggleBit(eBitInput.PLC_USE_WELD_JIG_1 + i, eBitOutput.PC_WELD_COMPLETE_1 + i); } if (usedAnalyzerLog) { StringBuilder sb = new StringBuilder(); rotaryStates.ForEach(x => sb.Append($"{x} ")); _log.D($"Check Rotary State : {sb.ToString()}"); } return rotaryStates; } public bool GetRotaryCanExist(int jigNo) { if (jigNo == 0) jigNo = 1; if (jigNo <= RotaryJigCount) return _plc.ReadBit(eBitInput.PLC_USE_WELD_JIG_1 + (jigNo - 1)).data; else return false; } /// /// Rotary의 현재 위치정보를 0~360로 전달함 /// public double GetRotaryCurrentPosition(bool isView360 = true) { try { double finalValue = Convert.ToDouble(_plc.ReadDWord(eWordInput.PLC_CURRENT_POS_1).data / 100_000f); finalValue += _scanner.Config.RotaryEncoderOffset; if (isView360) { if (RotaryMaxAngle <= finalValue) { while (RotaryMaxAngle <= finalValue) { finalValue -= RotaryMaxAngle; } } else if (finalValue < 0) { while (finalValue < 0) { finalValue += RotaryMaxAngle; } } } return finalValue; } catch { return -9999.9999; } } /// /// 중심 기준 가장 가까운 지그 번호(내림 연산) /// 내림 카운팅을 중심 기준 값 /// /// public int getCurrentJigNo() => (int)((GetRotaryCurrentPosition(isView360:true) + HalfDegreePerJig) / DegreePerJig) + 1; /// /// 360도 이상으로 준다고하면 Welding Count를 가져올 수 있음 2word = 2147483.647 / 3600 = 596 >> 596*16 = 9_536까지 카운팅 가능함 /// 내림 카운팅을 중심 기준 값 /// /// public int getWeldingCount() => (int)((GetRotaryCurrentPosition(isView360:false) + HalfDegreePerJig) / DegreePerJig); } }