변경 내용: - CellInfoScheduler: ReportSlot 추가, 4단계 Shift() pending 슬롯을 _pendingFromReportSlot / _pendingFromComplete로 분리 TrySetLbadAvg(cellId, avg) 추가 — Complete→ReportSlot 순 폴백 탐색 - JigInfo: LbadAvg 필드 추가 (Clear/CopyFrom 포함) - StreamLightLBAD: MesReportReceived 이벤트 추가, 수신 시 발화 - ProcessManager: RegisterLbadStream() / OnLbadMesReportReceived() 추가 LBAD 콜백 처리를 시퀀스가 아닌 ProcessManager에서 담당 - SeqAutoRunCCWH52A: RegisterLbadStream 호출, ReportCellResult에서 report.LbadAvg 직접 사용
187 lines
7.2 KiB
C#
187 lines
7.2 KiB
C#
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<ReceivedCellInfo>
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// LBAD MES Report 수신 콜백을 등록.
|
|
/// InitDev에서 StreamLightLBAD 생성 후 호출.
|
|
/// </summary>
|
|
public void RegisterLbadStream(AuroraLBAD.Service.StreamLightLBAD lbad)
|
|
{
|
|
lbad.MesReportReceived += OnLbadMesReportReceived;
|
|
}
|
|
|
|
private void OnLbadMesReportReceived(string lotId, string cellId, int headNum, double avg)
|
|
{
|
|
if (cellScheduler.TrySetLbadAvg(cellId, avg))
|
|
_log.I($"[LBAD] AVG 저장 >> CellId({cellId}) HeadNum({headNum}) AVG({avg:F3})");
|
|
else
|
|
_log.W($"[LBAD] 매칭 슬롯 없음 >> CellId({cellId}) HeadNum({headNum}) AVG({avg:F3})");
|
|
}
|
|
|
|
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})]");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 재시작 시 큐 잔류 데이터 전량 폐기.
|
|
/// CellTracker는 투입 기준으로 선읽음하므로, 정지 시점에 큐에 남은 데이터는
|
|
/// 이미 처리됐거나 이전 회차의 셀 정보일 수 있어 모두 버린다.
|
|
/// _pendingPrefetch1/2 는 이미 TryDequeue 이후 저장된 별도 슬롯이므로 영향 없음.
|
|
/// </summary>
|
|
public void DrainCellInfoQueue()
|
|
{
|
|
int count = 0;
|
|
while (ReceivedCellInfoQueue.TryDequeue(out _)) count++;
|
|
if (count > 0)
|
|
_log.W($"[QueueDrain] 재시작 시 잔류 데이터 {count}건 폐기");
|
|
}
|
|
|
|
public IEnumerable<bool> CheckRotaryState()
|
|
{
|
|
List<bool> rotaryStates = new List<bool>();
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rotary의 현재 위치정보를 0~360로 전달함
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 중심 기준 가장 가까운 지그 번호(내림 연산)
|
|
/// <param name="HalfDegreePerJig">내림 카운팅을 중심 기준 값</param>
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public int getCurrentJigNo() => (int)((GetRotaryCurrentPosition(isView360:true) + HalfDegreePerJig) / DegreePerJig) + 1;
|
|
|
|
/// <summary>
|
|
/// 360도 이상으로 준다고하면 Welding Count를 가져올 수 있음 2word = 2147483.647 / 3600 = 596 >> 596*16 = 9_536까지 카운팅 가능함
|
|
/// <param name="HalfDegreePerJig">내림 카운팅을 중심 기준 값</param>
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public int getWeldingCount() => (int)((GetRotaryCurrentPosition(isView360:false) + HalfDegreePerJig) / DegreePerJig);
|
|
|
|
}
|
|
}
|