diff --git a/AuroraDevices/AuroraLaserScanner/AuroraLaserScanner.csproj b/AuroraDevices/AuroraLaserScanner/AuroraLaserScanner.csproj index 13173c6..ac1e87d 100644 --- a/AuroraDevices/AuroraLaserScanner/AuroraLaserScanner.csproj +++ b/AuroraDevices/AuroraLaserScanner/AuroraLaserScanner.csproj @@ -143,6 +143,10 @@ {E7E68E54-43D2-4413-9510-43D25F13CD80} AuroraLaser + + {1A47B1F3-5BDE-41DF-8F4D-25298AD4F6DE} + AuroraPLC + \ No newline at end of file diff --git a/AuroraDevices/AuroraLaserScanner/Scanner.cs b/AuroraDevices/AuroraLaserScanner/Scanner.cs index 85a052e..0b8e0e2 100644 --- a/AuroraDevices/AuroraLaserScanner/Scanner.cs +++ b/AuroraDevices/AuroraLaserScanner/Scanner.cs @@ -22,7 +22,7 @@ namespace AuroraLaserScanner public ScannerConfig Config { get; set; } //public ScannerObj _scannerObj { get; set; } - private ScannerObj _scannerObj { get; set; } + protected ScannerObj _scannerObj { get; set; } public Scanner(string deviceId) { @@ -37,9 +37,9 @@ namespace AuroraLaserScanner Config = config as ScannerConfig; if (IsSimul) { _scannerObj = new ScannerSimul(); - return E1.OK; + //return E1.OK; } - if (Config.Type == eRTCType.RTC5) { + else if (Config.Type == eRTCType.RTC5) { _scannerObj = new ScannerRTC5(); } else if (Config.Type == eRTCType.RTC6) { _scannerObj = new ScannerRTC6(); diff --git a/AuroraDevices/AuroraLaserScanner/ScannerObj.cs b/AuroraDevices/AuroraLaserScanner/ScannerObj.cs index 412c149..da7e0cd 100644 --- a/AuroraDevices/AuroraLaserScanner/ScannerObj.cs +++ b/AuroraDevices/AuroraLaserScanner/ScannerObj.cs @@ -91,7 +91,7 @@ namespace AuroraLaserScanner RealImage = 0, VirtualImage = 1 } - public enum eListNo { LIST1 = 1, LIST2 = 2 }; + public enum eListNo { NONE = 0, LIST1 = 1, LIST2 = 2 }; public enum eAutoControlParameter { diff --git a/AuroraDevices/AuroraLaserScanner/ScannerSimul.cs b/AuroraDevices/AuroraLaserScanner/ScannerSimul.cs index 3c9e1c2..4ee48df 100644 --- a/AuroraDevices/AuroraLaserScanner/ScannerSimul.cs +++ b/AuroraDevices/AuroraLaserScanner/ScannerSimul.cs @@ -1,10 +1,14 @@ using Aurora.Api; using Hermes; +using System; +using System.Diagnostics; using System.Runtime.InteropServices; +using System.Threading; +using static AuroraPLC.PLCBase; namespace AuroraLaserScanner { - internal class ScannerSimul : ScannerObj + public class ScannerSimul : ScannerObj { static new readonly Logger Log = new Logger("Scanner"); @@ -14,7 +18,154 @@ namespace AuroraLaserScanner //set_trigger 관련 추가, get_waveform 에서 얻어올 포인터 추가, 최대 크기로 설정 (2^20 = 1,048,576) [MarshalAs(UnmanagedType.LPArray, SizeConst = 1048576)] int[] m_getWaveformPtr = new int[1048576]; private bool _ModulationMode; + private ScannerConfig _config; + // ----------------------------- + // MOTF Simulation state + // ----------------------------- + public Func PLCReadBit; + public Func PLCReadWord; + public Func PLCReadDWord; + public Func PLCGetAbsDeg; + + private const int PLC_CURRENT_POS_1_ADDR = 4; + + public Stopwatch sw = Stopwatch.StartNew(); + + // encoder가 반환할 값 + public volatile int _simEncXCount = 0; //랩핑 각도(0~360 + public volatile int _simEncYCount = 0; //누적 각도 + + public volatile bool LastWaitMissedRange = false; + + private const int RotaryJigCount = 16; + private const double JigPerMsec = 300; + private const double DegreePerRev = 360.0; + private const double OneCycleMsec = JigPerMsec * RotaryJigCount; + + // 누적각을 scanner 내부에서 유지 + public double _angleAbsDeg = 0.0; + private bool _listClosed = false; + private bool _list1Busy = false; + private bool _list2Busy = false; + private eListNo _currentExecutingList = eListNo.NONE; + private bool _autoChangeRequested = false; + + private long _listStartMs = 0; // ← 이게 executionStartTime 역할 + + private const int MinListTimeMs = 30; // 너무 빠른 완료 방지 + private const int MaxListTimeMs = 800; // 너무 느린 완료 방지 + private int SimulatedListTimeMs = 250; // 리스트 실행 시간 + + private const double RecommendWindowDeg = 45.0; + private const double RecommendUseRatio = 0.35; // 45도 중 실제 마킹이 차지하는 비율(가볍게) + + public void EquipmentSimulator() + { + PullEncoderFromPlcOnce(); + UpdateListExecution(); + } + + private int DegToCount(double deg) + { + double c = deg * _config.MarkingOnTheFlyEncoderResolutionRotate / DegreePerRev; + return (int)System.Math.Round(c); + } + + private int DegToCountAbs(double absDeg) + { + double c = absDeg * _config.MarkingOnTheFlyEncoderResolutionRotate / DegreePerRev; + long lc = (long)System.Math.Round(c); + return unchecked((int)lc); + } + + private void UpdateListExecution() + { + if (_currentExecutingList == eListNo.NONE) + return; + + long now = sw.ElapsedMilliseconds; + long elapsed = now - _listStartMs; + + int doneTime = GetEffectiveListTimeMs(); + + // 최소 현실 가드: elapsed가 음수인 경우 방지 + if (elapsed < 0) { + _listStartMs = now; + return; + } + + if (elapsed >= doneTime) { + // 현재 리스트 실행 완료 + if (_currentExecutingList == eListNo.LIST1) + _list1Busy = false; + else + _list2Busy = false; + + if (_autoChangeRequested) { + // 반대 리스트 자동 실행 + _currentExecutingList = + (_currentExecutingList == eListNo.LIST1) ? eListNo.LIST2 : eListNo.LIST1; + + if (_currentExecutingList == eListNo.LIST1) { + _list1Busy = true; + _list2Busy = false; + } else { + _list2Busy = true; + _list1Busy = false; + } + + _listStartMs = now; + _autoChangeRequested = false; + } else { + _currentExecutingList = eListNo.NONE; + } + } + } + private int GetEffectiveListTimeMs() + { + int t = SimulatedListTimeMs; + + // 4800msec / 360 * 45 * ratio + int recommendedListRunTime = (int)Math.Round((OneCycleMsec / DegreePerRev) * RecommendWindowDeg * RecommendUseRatio); + + // recommended가 너무 작거나 0이 되는 경우 방지 + if (recommendedListRunTime < MinListTimeMs) + recommendedListRunTime = MinListTimeMs; + + if (t < MinListTimeMs) + t = MinListTimeMs; + + if (t > MaxListTimeMs) + t = MaxListTimeMs; + + if (recommendedListRunTime > MaxListTimeMs) + recommendedListRunTime = MaxListTimeMs; + + return t; + } + public void TrySyncAbsAngleToPlcNow() + { + _angleAbsDeg = PLCGetAbsDeg(); + + // mod 동기화도 같이 + int modRaw = PLCReadDWord(PLC_CURRENT_POS_1_ADDR, ePLCDevice.W, false).data; + double modDeg = (modRaw / 100_000.0) % DegreePerRev; + if (modDeg < 0) + modDeg += DegreePerRev; + + LastWaitMissedRange = false; + } + public void ResetSim() + { + _angleAbsDeg = 0.0; + + _simEncXCount = 0; + _simEncYCount = 0; + + StopExecution(); // 리스트 상태 리셋 + LastWaitMissedRange = false; + } public override uint Open() { IsOpenUnit = true; @@ -33,6 +184,7 @@ namespace AuroraLaserScanner CommandPosZ = 0; _ModulationMode = config.Modulation; + _config = config; return E1.OK; } @@ -107,12 +259,17 @@ namespace AuroraLaserScanner } - public override void GetEncoder(out double encoderX, out double encoderY, bool rotateMode = false) { - // simulation 할때 값 반영 검토 필요 - encoderX = 0; - encoderY = 0; + int valueX = _simEncXCount; + int valueY = _simEncYCount; + + // 너가 쓰는 변환식 그대로 + encoderX = (valueX * DegreePerRev) / _config.MarkingOnTheFlyEncoderResolutionRotate; + encoderY = _angleAbsDeg; + if (encoderY > -0.5 && encoderY < 0) + encoderY = 0; // 시뮬 안정화 + return; } public override uint GetMarkingInfo() @@ -187,9 +344,62 @@ namespace AuroraLaserScanner public override void WaitForEncoderRotateRange(double minX, double maxX, double minY, double maxY) { - // simulation 할때 값 반영 검토 필요 - } + LastWaitMissedRange = false; + long t0 = sw.ElapsedMilliseconds; + + while (true) { + // 블로킹 대기 중에도 PLC에서 최신값을 직접 당겨온다 + PullEncoderFromPlcOnce(); + + // GetEncoder는 _simEncYCount를 deg로 바꿔줄 것 + GetEncoder(out _, out double curY, true); + + // 정상 진입 + if (curY >= minY && curY <= maxY) + return; + + // 최소 현실 가드: 구간을 지나쳤으면 TIMEOUT까지 끌지 말고 즉시 종료 + // (실장에서도 "놓치면" 의미 없고 다음 로직이 꼬임) + if (curY > maxY) { + LastWaitMissedRange = true; + Log.W($"[SIM] WaitForEncoderRotateRange MISSED. curY={curY:F3}, range=[{minY:F3}..{maxY:F3}]"); + return; + } + + // 최후 안전장치 + if (sw.ElapsedMilliseconds - t0 > 5000) { + LastWaitMissedRange = true; + Log.W($"[SIM] WaitForEncoderRotateRange TIMEOUT. curY={curY:F3}, range=[{minY:F3}..{maxY:F3}]"); + return; + } + + Thread.Sleep(1); + } + } + private void PullEncoderFromPlcOnce() + { + // 방어: delegate 미설정이면 아무 것도 못함 + if (PLCReadDWord == null || _config == null || _config.MarkingOnTheFlyEncoderResolutionRotate == 0) + return; + + // 1) 랩핑각 (0~360) + int modRaw = PLCReadDWord(PLC_CURRENT_POS_1_ADDR, ePLCDevice.W, true).data; + double angleModDeg = (modRaw / 100_000.0) % 360.0; + if (angleModDeg < 0) + angleModDeg += 360.0; + + // 2) 누적각 (계속 증가) + //int absRaw = PLCReadDWord(PLC_CURRENT_POS_ABS_ADDR, ePLCDevice.W, true).data; + //double angleAbsDeg = absRaw / 100_000.0; + if (PLCGetAbsDeg != null) { + _angleAbsDeg = PLCGetAbsDeg(); + } + + // 3) count로 변환 (n_get_encoder가 반환한다고 가정하는 값) + _simEncXCount = DegToCount(angleModDeg); + _simEncYCount = DegToCountAbs(_angleAbsDeg); + } public override void SetFlyX(double encoderResolution) { // simulation 할때 값 반영 검토 필요 @@ -283,16 +493,34 @@ namespace AuroraLaserScanner } public override uint LoadList(eListNo no) { - // simulation 할때 값 반영 검토 필요 - return 0; + bool busy = (no == eListNo.LIST1) ? _list1Busy : _list2Busy; + + if (busy) + return 0; // 실행 중 → Load 불가 + + return (uint)no; // Load 가능 } public override void SetEndList() { - // simulation 할때 값 반영 검토 필요 + _listClosed = true; } public override void ExecuteList(eListNo no) { - // simulation 할때 값 반영 검토 필요 + if (!_listClosed) + Log.W("ExecuteList called without SetEndList"); + _listClosed = false; // 실행 시작하면서 다시 열림 상태로 + + _currentExecutingList = no; + + if (no == eListNo.LIST1) { + _list1Busy = true; + _list2Busy = false; + } else { + _list2Busy = true; + _list1Busy = false; + } + + _listStartMs = sw.ElapsedMilliseconds; } public override void StartLoop() { @@ -304,16 +532,28 @@ namespace AuroraLaserScanner } public override void AutoChange() { - // simulation 할때 값 반영 검토 필요 + if (_currentExecutingList == eListNo.NONE) + return; + + _autoChangeRequested = true; } public override bool GetBusyStatus() { - // simulation 할때 값 반영 검토 필요 - return false; + return _list1Busy || _list2Busy || _currentExecutingList != eListNo.NONE; } public override void StopExecution() { - // simulation 할때 값 반영 검토 필요 + _list1Busy = false; + _list2Busy = false; + _currentExecutingList = eListNo.NONE; + _autoChangeRequested = false; + _listStartMs = 0; + _listClosed = false; + + _angleAbsDeg = 0.0; + + _simEncXCount = 0; + _simEncYCount = 0; } public override uint GetRTCVersion() { diff --git a/Machines/ILSOS_CCW_H52A/Device/PLC/PLCContoller.Config.cs b/Machines/ILSOS_CCW_H52A/Device/PLC/PLCContoller.Config.cs index 9044b2e..a964cb0 100644 --- a/Machines/ILSOS_CCW_H52A/Device/PLC/PLCContoller.Config.cs +++ b/Machines/ILSOS_CCW_H52A/Device/PLC/PLCContoller.Config.cs @@ -1,11 +1,15 @@ using Aurora; +using Aurora.Api; using AuroraPLC; +using Hermes; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using WeldingCommon.Devices; +using static System.Windows.Forms.VisualStyles.VisualStyleElement.Rebar; namespace ILSOS_CCW_H52A.Device { @@ -13,7 +17,183 @@ namespace ILSOS_CCW_H52A.Device { public PLCControllerCCWH52A(string deviceId, ePLCNetType plcNetType, PLCMMapFileType fileType, IAuroraEnvironment aurora) : base(deviceId, plcNetType, fileType, aurora) { + } + protected override int DoActivate(IDeviceConfig config) + { + var ret = base.DoActivate(config); + if (ret == E1.OK && IsSimul) { + //WriteBit((int)eBitInput.PLC_AUTO_STATUS, true, ePLCDevice.B, false); + //WriteBit((int)eBitInput.PLC_AUTO_REQ, true, ePLCDevice.B, false); + SetMonitoringService(EquipmentSimulator, 1); + } + return ret; + } + + protected override int DoDeactivate() + { + StopMonitoringService(); + + var res = base.DoDeactivate(); + + if (res != E1.OK) { + Log.E("Deactivate is failed"); + return res; + } + + return res; + } + + // ----------------------------- + // Simulation state + // ----------------------------- + private const int PLC_CURRENT_POS_1_ADDR = 4; // 기존 (mod 0~360) + + private readonly Random _rand = new Random(); + + private readonly Stopwatch _sw = Stopwatch.StartNew(); + private long _lastMs = -1; + + private const int RotaryJigCount = 16; + private const double JigPerMsec = 300; + private const double DegreePerRev = 360.0; + private const double OneCycleMsec = JigPerMsec * RotaryJigCount; + private const double DegreePerMsec = DegreePerRev / OneCycleMsec; + private const double DegreePerJig = DegreePerRev / RotaryJigCount; // 22.5 + private const double HalfDegreePerJig = DegreePerJig / 2.0; // 11.25 + + // PLC_CURRENT_POS_1 = degree * 100000 + private const double PlcAngleScale = 100_000.0; + + // 시퀀스가 "다음 지그"를 미리 읽으므로 안전하게 2칸 선행(권장) + private const int LeadSectors = 2; + + // Motor position + private readonly object _absLock = new object(); + public double GetSimAbsDeg() + { + long value = System.Threading.Interlocked.Read(ref _angleAbsDeg); + return value / 100000.0; + } + private long _angleAbsDeg = 0; // 누적 각도(계속 증가) + + private double _angleModDeg = 0.0; // 0~360 랩핑 각도 + + // PLC USE_WELD_JIG bits + private readonly bool[] _canExist = new bool[RotaryJigCount]; + private readonly bool[] _decided = new bool[RotaryJigCount]; // 이번 회전에서 해당 섹터가 결정됐는지 + private int _prevSector = -1; + + private static void FillBool(bool[] arr, bool value) + { + if (arr == null) + return; + for (int i = 0; i < arr.Length; i++) + arr[i] = value; + } + + private static int Mod(int x, int m) + { + int r = x % m; + return (r < 0) ? (r + m) : r; + } + + private int GetSectorFromAngle(double angleDeg) + { + // 시퀀스의 (angle + half)/per와 동일한 기준으로 섹터 결정 + return (int)((angleDeg + HalfDegreePerJig) / DegreePerJig) % RotaryJigCount; + } + + private void OnSectorChanged(int prev, int curr) + { + // 첫 진입 + if (prev < 0) { + // 처음 시작 시에는 전부 OFF 상태에서 시작 + FillBool(_canExist, false); + FillBool(_decided, false); + } else { + // 랩(15 -> 0) 감지: 새 바퀴 시작으로 보고 "지나간 영역 OFF"를 강하게 반영 + if (prev == RotaryJigCount - 1 && curr == 0) { + FillBool(_canExist, false); + FillBool(_decided, false); + } else { + // 지나간 섹터(prev)는 OFF 처리 + _canExist[prev] = false; + _decided[prev] = false; // 다음 회전에서 다시 결정되도록 + } + } + + // 시퀀스는 "다음 지그"를 읽으므로 curr+1부터 선행 결정 + for (int k = 1; k <= LeadSectors; k++) { + int s = Mod(curr + k, RotaryJigCount); + + if (!_decided[s]) { + // 7/8 확률 ON (0~7 중 0만 OFF로 만들려면 >0) + bool isCanExist = true;// (_rand.Next(0, 8) > 0); + _canExist[s] = isCanExist; + _decided[s] = true; + } + } + + // 현재 섹터(curr)도 켜고 싶다면 여기서 결정 가능 + } + + private void EquipmentSimulator() + { + if (!ReadBit((int)eBitOutput.PC_AUTO_STATUS, ePLCDevice.B, false).data) + return; + + long now = _sw.ElapsedMilliseconds; + if (_lastMs < 0) + _lastMs = now; + + long dtMs = now - _lastMs; + if (dtMs < 0) + dtMs = 0; + + if (dtMs > 100) + dtMs = 100; + + _lastMs = now; + + // abs 누적각 (시뮬 전용) + long deltaScaled = (long)Math.Round(DegreePerMsec * dtMs * 100000.0); + System.Threading.Interlocked.Add(ref _angleAbsDeg, deltaScaled); + + double absDeg = GetSimAbsDeg(); + _angleModDeg = absDeg % DegreePerRev; + if (_angleModDeg < 0) + _angleModDeg += DegreePerRev; + + // mod 각도 + int plcAngleMod = (int)Math.Round(_angleModDeg * PlcAngleScale); + WriteDWord(PLC_CURRENT_POS_1_ADDR, plcAngleMod, ePLCDevice.W, false); + + // 섹터 변경/비트 처리 로직은 기존 그대로 + int sectorNow = GetSectorFromAngle(_angleModDeg); + if (sectorNow != _prevSector) { + OnSectorChanged(_prevSector, sectorNow); + _prevSector = sectorNow; + } + + WriteBitBlock((int)eBitInput.PLC_USE_WELD_JIG_1, RotaryJigCount, _canExist, ePLCDevice.B, false); + } + + public void ResetSimulator() + { + _lastMs = -1; + System.Threading.Interlocked.Exchange(ref _angleAbsDeg, 0); + _angleModDeg = 0.0; + + FillBool(_canExist, false); + FillBool(_decided, false); + _prevSector = -1; + + // 각도 초기화 반영 + WriteDWord(PLC_CURRENT_POS_1_ADDR, 0, ePLCDevice.W, false); + + // 지그 비트 초기화 반영 + WriteBitBlock((int)eBitInput.PLC_USE_WELD_JIG_1, RotaryJigCount, _canExist, ePLCDevice.B, false); } } } diff --git a/Machines/ILSOS_CCW_H52A/Device/Scanner/ScannerCCWH52A.cs b/Machines/ILSOS_CCW_H52A/Device/Scanner/ScannerCCWH52A.cs new file mode 100644 index 0000000..acfeda4 --- /dev/null +++ b/Machines/ILSOS_CCW_H52A/Device/Scanner/ScannerCCWH52A.cs @@ -0,0 +1,66 @@ +using Aurora; +using Aurora.Api; +using AuroraLaserScanner; +using Hermes; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using WeldingCommon; +using static ILSOS_CCW_H52A.Device.PLCControllerCCWH52A; + +namespace ILSOS_CCW_H52A.Device +{ + public class ScannerCCWH52A : Scanner + { + // ----------------------------- + // Simulation state + // ----------------------------- + private PLCControllerCCWH52A _plc; + + public ScannerCCWH52A(string deviceId) : base(deviceId) + { + } + + protected override int DoActivate(IDeviceConfig config) + { + var ret = base.DoActivate(config); + if (ret == E1.OK && IsSimul) { + if (_plc == null) { + _plc = Application.Current.AuroraEnv().DeviceManager[CommonDeviceIds.PLCDevice] as PLCControllerCCWH52A; + return E2.ABORTED; + } + + // 값의 동기화가 필요해서 추가 함 + if (!_plc.IsConnected && !_plc.IsSimul) + return E2.ABORTED; + + var core = _scannerObj as ScannerSimul; + core.PLCReadBit = _plc.ReadBit; + core.PLCReadWord = _plc.ReadWord; + core.PLCReadDWord = _plc.ReadDWord; + core.PLCGetAbsDeg = _plc.GetSimAbsDeg; + //_plc.ResetSimulator(); + core.ResetSim(); + core.TrySyncAbsAngleToPlcNow(); + + SetMonitoringService((_scannerObj as ScannerSimul).EquipmentSimulator, 1); + } + return ret; + } + + protected override int DoDeactivate() + { + var ret = base.DoDeactivate(); + if (ret == E1.OK && IsSimul){ + var core = _scannerObj as ScannerSimul; + StopMonitoringService(); + } + + return ret; + } + } +} diff --git a/Machines/ILSOS_CCW_H52A/EquipmentSetup.cs b/Machines/ILSOS_CCW_H52A/EquipmentSetup.cs index 9f4c817..37694af 100644 --- a/Machines/ILSOS_CCW_H52A/EquipmentSetup.cs +++ b/Machines/ILSOS_CCW_H52A/EquipmentSetup.cs @@ -11,6 +11,7 @@ using AuroraCellTracker.Views; using AuroraIO; using AuroraIO.View; using AuroraLaser; +using AuroraLaserScanner; using AuroraLBAD; using AuroraLBAD.Services; using AuroraOCT; @@ -153,6 +154,8 @@ namespace ILSOS_CCW_H52A () => _plc, PLCMelsecNetConfig.GetDefaultConfig(), device => new UCPLCDevice() { DataContext = new UCPLCDeviceViewModel(AuroraEnv, _plc) }); + registry.Register(CommonDeviceIds.Scanner, () => new ScannerCCWH52A(CommonDeviceIds.Scanner), new ScannerConfig()); + var ioController = new IOController_CCW_H52A(AuroraEnv, CommonDeviceIds.IODevice); registry.Register(CommonDeviceIds.IODevice, () => ioController, new IOUnitConfig()); diff --git a/Machines/ILSOS_CCW_H52A/ILSOS_CCW_H52A.csproj b/Machines/ILSOS_CCW_H52A/ILSOS_CCW_H52A.csproj index f58c19c..7e803d2 100644 --- a/Machines/ILSOS_CCW_H52A/ILSOS_CCW_H52A.csproj +++ b/Machines/ILSOS_CCW_H52A/ILSOS_CCW_H52A.csproj @@ -98,6 +98,7 @@ + @@ -105,6 +106,7 @@ + diff --git a/Machines/ILSOS_CCW_H52A/Sequences/ProcessManager.cs b/Machines/ILSOS_CCW_H52A/Sequences/ProcessManager.cs new file mode 100644 index 0000000..57ba05c --- /dev/null +++ b/Machines/ILSOS_CCW_H52A/Sequences/ProcessManager.cs @@ -0,0 +1,115 @@ +using Aurora; +using Aurora.Api; +using Aurora.Util.HislogPlus; +using AuroraLaserScanner; +using AuroraPLC; +using ILSOS_CCW_H52A.Device; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static ILSOS_CCW_H52A.Device.PLCControllerCCWH52A; + +namespace ILSOS_CCW_H52A.Sequences +{ + public class ProcessManager + { + private bool usedAnalyzerLog = false; + private readonly Logger _log = new Logger("ProcessManager"); + + 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; } + + + private readonly IAuroraEnvironment _env; + private readonly PLCControllerCCWH52A _plc; + private readonly Scanner _scanner; + + public ProcessManager() + { + _plc = _env.DeviceManager[DeviceIds.PLCDevice] as PLCControllerCCWH52A; + _scanner = _env.DeviceManager[DeviceIds.Scanner] as Scanner; + } + + 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) + { + //var RotaryStates = CheckRotaryState(); + + 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); + + } +} diff --git a/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunCCWH52A.cs b/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunCCWH52A.cs index f8283c3..8aa9fa4 100644 --- a/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunCCWH52A.cs +++ b/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunCCWH52A.cs @@ -12,6 +12,7 @@ using CoreUnit.Control.UserControl.Canvas; using CoreUnit.Scanner.UserControl.ScannerCanvas; using DB; using Hermes; +using ILSOS_CCW_H52A.Device; using ILSOS_CCW_H52A.Models; using ILSOS_CCW_H52A.RMS; using System; @@ -49,14 +50,13 @@ namespace ILSOS_CCW_H52A.Sequences // TODO: [장비구현필요] 실제 공정 추가 const string SEQ_CHECK_LOAD_COMPLETE = "[SeqAutoRunCCWH52A] Check Load Complete"; - 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_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"; + 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)"; @@ -91,7 +91,7 @@ namespace ILSOS_CCW_H52A.Sequences private Scanner _Scanner; private LaserDevice _Laser; - private PLCController _PLC; + private PLCControllerCCWH52A _PLC; private IODevice _IO; private StreamLightLBAD _StreamLightLBAD; private static CellTracker _CellTracker; @@ -173,6 +173,11 @@ namespace ILSOS_CCW_H52A.Sequences sb.AddJoin(SEQ_JOIN_ENCODER_MONITOR); sb.AddAction(SEQ_CHECK_LOAD_COMPLETE, CheckLoadComplete); + + 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); @@ -180,10 +185,6 @@ namespace ILSOS_CCW_H52A.Sequences sb.AddSubsequence(SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ, SequenceManager.GetSeqBuilder_SeqUploadWeldingList()); sb.AddAction(SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ, GetUploadWeldingList_SeqResult); - 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_EXECUTE_WELDING, ExecuteWelding); sb.AddAction(SEQ_REQUEST_UNLOAD_CELL, RequestUnloadCell); @@ -216,7 +217,7 @@ namespace ILSOS_CCW_H52A.Sequences 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]); - #region + #region //sb.Flows(sb[SEQ_FORK_ENCODER_MONITOR] > sb[SEQ_ENCODER_MONITOR]); ////sb.Flows(sb[SEQ_FORK_ENCODER_MONITOR] > sb[SEQ_CHECK_LOAD_COMPLETE]); //sb.Flows(sb[SEQ_JOIN_ENCODER_MONITOR] > sb[SEQ_COMPLETE]); @@ -270,19 +271,13 @@ namespace ILSOS_CCW_H52A.Sequences _SystemParameter = context.GetSetting(CommonSettingIds.SystemParameter); InitDev(context); - if (!CheckDeviceReady(context)) - { //Check Device - //AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_AutoRun_DeviceNotReady); + if (!CheckDeviceReady(context)) { + //Check Device Log.E("The devices are not active."); return SequenceAction.ForceStopByAlarm(AlarmCode.ALARM_SEQUENCE_AutoRun_DeviceNotReady); - - //return null; } - if (InitSequenceVariable(context) == false) - { - //AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_AutoRun_InitVariableError); + if (InitSequenceVariable(context) == false) { return SequenceAction.ForceStopByAlarm(AlarmCode.ALARM_SEQUENCE_AutoRun_InitVariableError); - //return null; } Thread.Sleep(1); // 부하 방지 return SequenceAction.Ok(); @@ -317,14 +312,13 @@ namespace ILSOS_CCW_H52A.Sequences //Device Init _Scanner = context.GetLaserScanner(); _Laser = context.GetLaser(); - if (_InitParam.UseStream) - { + if (_InitParam.UseStream) { _StreamLightLBAD = new StreamLightLBAD(); _StreamLightLBAD._Client = context.GetStreamLightClient(); _StreamLightLBAD._Sub = context.GetStreamLightSub(); } _CellTracker = context.GetCellTracker(); - _PLC = context.GetPLC(); + _PLC = context.Devices[DeviceIds.PLCDevice] as PLCControllerCCWH52A; _IO = context.GetIOUnit(); } @@ -332,17 +326,14 @@ namespace ILSOS_CCW_H52A.Sequences { List deviceListToCheck = new List() { CommonDeviceIds.Scanner, CommonDeviceIds.Laser, CommonDeviceIds.PLCDevice, CommonDeviceIds.IODevice }; - if (_InitParam.UseStream) - { + if (_InitParam.UseStream) { deviceListToCheck.Add(CommonServiceIds.StreamLightLBADClient); deviceListToCheck.Add(CommonServiceIds.StreamLightLBADSub); } bool isAllDeviceActivated = true; - foreach (string item in deviceListToCheck) - { - if (!context.Devices[item].IsActivated) - { + foreach (string item in deviceListToCheck) { + if (!context.Devices[item].IsActivated) { isAllDeviceActivated = isAllDeviceActivated && false; } } @@ -352,29 +343,14 @@ namespace ILSOS_CCW_H52A.Sequences private bool CheckCondition(SequenceContext context) { - if (CheckCondition_Interlock()) - { - int InitializeCode = InitializeDeviceStatus(context); - if (InitializeCode == (int)InitErrorList.SUCCESS) - return true; - else - context.PutValue(ContextKey.KEY_SEQUENCE_ERROR_CODE, InitializeCode); - } + int InitializeCode = InitializeDeviceStatus(context); + if (InitializeCode == (int)InitErrorList.SUCCESS) + return true; + else + context.PutValue(ContextKey.KEY_SEQUENCE_ERROR_CODE, InitializeCode); return false; } - private bool CheckCondition_Interlock() - { - // PLC, Data, IO 등 Interlock checking - // Config checking - //if (_Scanner.Config.UseMarkingOnTheFly == false) - //{ - // Log.E("ERROR Config Setting : Scanner Marking on the fly (OFF)"); - // return false; - //} - return true; - } - private int InitializeDeviceStatus(SequenceContext context) { // PLC Interface AutoStatus Start @@ -384,32 +360,26 @@ namespace ILSOS_CCW_H52A.Sequences //InitAutoRunBit(); // LBAD Initialize // LBAD 는 server 이기 때문에, 만약 끊어졌으면 다시 접속이 필요함 - if (_SystemParameter.BypassModeLBAD == false) - { - if (!_StreamLightLBAD._Client.IsActivated) - { + if (_SystemParameter.BypassModeLBAD == false) { + if (!_StreamLightLBAD._Client.IsActivated) { int ret = _StreamLightLBAD._Client.Activate(); Thread.Sleep(500); - if (ret != E1.OK) - { + if (ret != E1.OK) { Log.E($"StreamLightClient activate failed"); return (int)InitErrorList.LBAD_CLIENT_ACTIVATE_FAILED; } } - if (_StreamLightLBAD._Sub.IsActivated == false) - { + if (_StreamLightLBAD._Sub.IsActivated == false) { int ret = _StreamLightLBAD._Sub.Activate(); Thread.Sleep(500); - if (ret != E1.OK) - { + if (ret != E1.OK) { Log.E($"StreamLightSub activate failed"); return (int)InitErrorList.LBAD_SUB_ACTIVATE_FAILED; } } _StreamLightLBAD.Initialize(); - if (_StreamLightLBAD.SendReadyStatus(eRequestData.Ready_On) == false) - { + if (_StreamLightLBAD.SendReadyStatus(eRequestData.Ready_On) == false) { Log.I(string.Format($"[LBAD Interface]CheckReadyStatus null")); //return false; } @@ -419,18 +389,15 @@ namespace ILSOS_CCW_H52A.Sequences _Laser.PilotLaserOff(); _Laser.LaserOn(); Thread.Sleep(500); - if (!_Laser.GetLaserReady()) - { + if (!_Laser.GetLaserReady()) { Log.E("ERROR Initialize Laser"); return (int)InitErrorList.LASER_INIT_ERROR; } // Scanner Initialize - if (_Scanner.GetModulationMode()) - { + if (_Scanner.GetModulationMode()) { _ModulationMode = true; } - else - { + else { _ModulationMode = false; } @@ -442,16 +409,14 @@ namespace ILSOS_CCW_H52A.Sequences 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; } } // Marking On The Flying Off - if (_Scanner.Config.UseMarkingOnTheFly == false) - { + if (_Scanner.Config.UseMarkingOnTheFly == false) { Log.E("ERROR Config Setting : Scanner Marking on the fly (OFF)"); return (int)InitErrorList.MOTF_OFF; } @@ -497,46 +462,36 @@ namespace ILSOS_CCW_H52A.Sequences isTheSecondListWrite = false; isTheFirstListExecute = true; // 제일 첫 List는 ExecuteList 필요. 이후는 AutoChange - InitAutoRunBit(); // GetRotaryCurrentPosition() 호출 이후 AutoStatus True 변경 + _PLC.WriteBit(eBitOutput.PC_AUTO_STATUS, true); + Log.I(string.Format($"[PLCInterface]SET PC_AUTO_STATUS True")); #endregion return (int)InitErrorList.SUCCESS; } - private void InitAutoRunBit() - { - _PLC.WriteBit((int)eBitOutput.PC_AUTO_STATUS, true); - Log.I(string.Format($"[PLCInterface]SET PC_AUTO_STATUS True")); - } private double GetRotaryCurrentPosition() { - try - { + try { //double finalValue = Convert.ToDouble(_plc.GetLongAIValue("PLC_CURRENT_POS_1") / 100000f); double finalValue = Convert.ToDouble(_PLC.ReadDWord((int)eWordInput.PLC_CURRENT_POS_1).data / 100000f); finalValue += _Scanner.Config.RotaryEncoderOffset; - if (360.0 <= finalValue) - { - while (360.0 <= finalValue) - { + if (360.0 <= finalValue) { + while (360.0 <= finalValue) { finalValue -= 360.0; } } - else if (finalValue < 0) - { - while (finalValue < 0) - { + else if (finalValue < 0) { + while (finalValue < 0) { finalValue += 360.0; } } return finalValue; } - catch - { + catch { return -9999.9999; } } @@ -560,8 +515,7 @@ namespace ILSOS_CCW_H52A.Sequences private string CheckInitCondition(SequenceContext context, string id, ICollection branches, SequenceActionResult param) { - if (!CheckCondition(context)) - { + if (!CheckCondition(context)) { //AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_AutoRun_CheckInitCondition); Log.E("Condition is not in state to start sequence."); context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, false); @@ -586,29 +540,18 @@ namespace ILSOS_CCW_H52A.Sequences { Status.SetWorkingPosition(eWorkingPosition.LOADING_CELL); - // MOTF WaitScannerLoadListEnable - // List가 실행 중이라면(ex : 엔코더 대기 상태 등), 반환값은 0, LoadList 할 수 없는 상태(List 실행 중이거나, 엔코더 대기 상태) - // LoadList가 0보다 크면 다음 시퀀스 진행해야 함 - _InterfaceStartTime = DateTime.Now; - while (!context.Stopped) - { - if (_Scanner.LoadList(_WeldingScannerListNo) > 0) - { - // List가 실행 상태가 아니라면(ex : 시작을 안 했거나, 완료되었으면) 반환값은 1 또는 2(리스트 번호) - Log.I(string.Format($"LoadList function success : List {_WeldingScannerListNo}")); - break; - } - - _InterfaceSpanTime = DateTime.Now.Subtract(_InterfaceStartTime); - if (_InterfaceSpanTime.TotalMilliseconds > (PLC_TIMEOUT * 1000)) - { - Log.E("TIMEOUT_Scanner_LoadList"); - _InterfaceStartTime = DateTime.Now; - } - Thread.Sleep(10); + // MOTF WaitScannerLoadListEnable + // List가 실행 중이라면(ex : 엔코더 대기 상태 등), 반환값은 0, LoadList 할 수 없는 상태(List 실행 중이거나, 엔코더 대기 상태) + // LoadList가 0보다 크면 다음 시퀀스 진행해야 함 + WaitContinue: + var ret = context.RetryUntilSuccess(() => _Scanner.LoadList(_WeldingScannerListNo) > 0, int.MaxValue, 10, true); + if (ret == E2.TIMEOUT) { + Log.E("TIMEOUT_Scanner_LoadList : 600시간 Run"); + goto WaitContinue; } - Thread.Sleep(1); // 부하 방지 + Log.I($"LoadList function success : List {_WeldingScannerListNo}"); + Thread.Sleep(1); // 부하 방지 return SequenceAction.Ok(); } @@ -620,22 +563,19 @@ namespace ILSOS_CCW_H52A.Sequences Log.I("Write the 1st scanner list"); } - else - { + else { if (isTheSecondListWrite) // 두번째 리스트는 바로 쓰지만, 첫번째 리스트 실행이 완료되면 자동으로 이어서 실행된다(AutoChange) { isTheSecondListWrite = false; Log.I("Write the 2nd scanner list"); } - else - { + else { isWeled = false; // 첫번째나 두번째가 아니라면, 실제 리스트 실행이 끝난 것이기 때문에 완료 처리가 필요 CompleteScannerList = InputScannerList[(int)_WeldingScannerListNo - 1]; - if (!CheckCurrentEncoderAngle(CompleteScannerList.StartAngle)) - { + if (!CheckCurrentEncoderAngle(CompleteScannerList.StartAngle)) { // 에러 상황(현재 Rotary Encoder 값이 정상적인 스캐너 리스트 각도 범위를 벗어남) Log.I("ERR_SCANNER_LIST_PROCESSING_TIMEOVER"); @@ -651,10 +591,8 @@ namespace ILSOS_CCW_H52A.Sequences // } // return SequenceAction.Ok(); } - else - { - if (CompleteScannerList.CanExist) - { + else { + if (CompleteScannerList.CanExist) { isWeled = true; #region 용접 시간, 엔코더 측정/기록 //_WeldInterval = DateTime.Now - _WeldEndTime; @@ -665,8 +603,7 @@ namespace ILSOS_CCW_H52A.Sequences double elaspedTime = _Scanner.GetTime(); - if (CompleteScannerList.WeldComplete) - { + if (CompleteScannerList.WeldComplete) { _WeldInterval = DateTime.Now - _WeldEndTime; _WeldEndTime = DateTime.Now; _WeldingStartTime = CompleteScannerList.WeldStartTime; @@ -677,8 +614,7 @@ namespace ILSOS_CCW_H52A.Sequences 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) - { + 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)")); } @@ -707,8 +643,7 @@ namespace ILSOS_CCW_H52A.Sequences Log.I(string.Format("Encoder start : {0:F4}, end : {1:F4}, vel : {2:F2}", startEncoderCount, endEncoderCount, angularVelocity)); #endregion #region 스캐너 리스트 Record 파일 기록 - if (_Scanner.Config.UseRecordSignal) - { + if (_Scanner.Config.UseRecordSignal) { String RecordFileName = _RecipeManager.ActivatedRecipe().Name + "_Jig#" + CompleteScannerList.JigNo.ToString(); string DataFilePath = ProjectUtil.GetDataLogSetting().GetFilePath_ScannerRecordLog(RecordFileName, true); _Scanner.SaveRecord(DataFilePath); @@ -727,20 +662,18 @@ namespace ILSOS_CCW_H52A.Sequences _Scanner.GetEncoder(out encoderX, out currentAngle, true); - if (0 < startAngle && currentAngle < 0) - { + if (0 < startAngle && currentAngle < 0) { // Encoder 반전되는 순간이며, 이 때만 인터락 무시 } - else if (startAngle <= currentAngle) - { + else if (startAngle <= currentAngle) { if (currentAngle < startAngle + 45)// 다음 리스트 구간이 끝나기 전까지는 완료가 되어야 한다 { Log.I(string.Format("Current encoder check OK, startAngle : {0}, currentAngle : {1})", startAngle, currentAngle)); return true; } - else Log.I(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.I(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; } @@ -783,8 +716,7 @@ namespace ILSOS_CCW_H52A.Sequences bool canExist; // 지그 용접 수행 여부 확인 - if (UsePlcShotThemAll) - { + if (UsePlcShotThemAll) { canExist = true; Log.I("Settings - PLC : Shot Them All Mode"); } @@ -793,40 +725,13 @@ namespace ILSOS_CCW_H52A.Sequences Log.I("Can Exist : " + canExist.ToString() + ", JigNo " + _WeldingJigNo + " ,CountNo " + _WeldingCount + ", StartAngle(Y) : " + _WeldingStartAngle); - // 입력한 Scanner List를 완료대기열에 추가 - //SetInputScannerList(_WeldingScannerListNo, DateTime.Now, _WeldingCount, _WeldingStartAngle, _WeldingJigNo, _RotaryJigCount, canExist); - - //if (canExist) // Can이 존재할 경우만 리스트 안에 내용이 들어가며, 그렇지 않은 경우는 빈 리스트가 된다 - //{ - // #region 용접할 Objects 선택 - // if (UsePlcPositionCompensation && _WeldingJigNo - 1 < _ScannerCanvasUserCtrl.Model.Layers.Length) { - // _SelectedDrawObjectList = _ScannerCanvasUserCtrl.Model.Layers[_WeldingJigNo - 1].Objects; - // Log.I(string.Format("Page 불러오기 : [{0}])", _WeldingJigNo - 1)); - // } else { - // _SelectedDrawObjectList = _ScannerCanvasUserCtrl.Model.Layers[0].Objects; - // Log.I(string.Format("Page 불러오기 : [0])")); - // } - // #endregion - - // // 여기서 어디로 가는지 설정 필요함 - // return SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ; - //} - - //// 기존 소스는 UploadWeldingList 건너 뛰고 SEQ_EXECUTE_WELDING 으로 바로 감 - //// SEQ_WAIT_WELDING_START 의 기능이 어떻게 되는 지 확인 후 - //// 건너뛰어도 되면 SEQ_EXECUTE_WELDING 으로 변경 해도 됨 - //return SEQ_WAIT_WELDING_START; - - if (canExist) - { + if (canExist) { #region 용접할 Objects 선택 - if (_WeldingJigNo - 1 < _ScannerCanvasUserCtrl.Model.Layers.Length) - { + if (_WeldingJigNo - 1 < _ScannerCanvasUserCtrl.Model.Layers.Length) { _SelectedDrawObjectList = _ScannerCanvasUserCtrl.Model.Layers[_WeldingJigNo - 1].Objects; Log.I(string.Format("Page 불러오기 : [{0}])", _WeldingJigNo - 1)); } - else - { + else { _SelectedDrawObjectList = _ScannerCanvasUserCtrl.Model.Layers[0].Objects; Log.I(string.Format("Page 불러오기 : [0])")); } @@ -840,12 +745,11 @@ namespace ILSOS_CCW_H52A.Sequences _RecipeNo = _RecipeManager.ActivatedRecipe().Id; SetWeldingData(); + return SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ; } - else - { + else { return SEQ_EXECUTE_WELDING; } - return SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ; #endregion } @@ -865,8 +769,7 @@ namespace ILSOS_CCW_H52A.Sequences ScannerParam laserParam = null; laserParam = GetSelectedDrawObjectData(_SelectedDrawObjectList[0]); - if (laserParam != null) - { + if (laserParam != null) { // EDC 보내는 용도 Status.GetProcessingWeldingData().LaserPowerCore = laserParam.LaserPower1.ToString(); Status.GetProcessingWeldingData().MarkSpeed = laserParam.MarkSpeed.ToString(); @@ -895,8 +798,7 @@ namespace ILSOS_CCW_H52A.Sequences // 1.2. 계산된 시작각도가 이미 지나버렸으면 알람 발생 필요 // 1.3. Overflow가 나는 숫자라면, 보상할 것. - if (sectionStartAngle > encoderAllowedMaxDegree) - { + if (sectionStartAngle > encoderAllowedMaxDegree) { sectionStartAngle -= encoderAllowedMaxDegree - encoderAllowedMinDegree; _EncoderOverflowCount++; } @@ -911,18 +813,15 @@ namespace ILSOS_CCW_H52A.Sequences #region 지그 16개 요청기록 로그 남기기, 빠져나간 캔에 대한 Complete Bit 초기화 string result = ""; - for (int i = 1; i <= _RotaryJigCount; i++) - { + for (int i = 1; i <= _RotaryJigCount; i++) { if (_PLC.ReadBit((int)eBitInput.PLC_USE_WELD_JIG_1 + i).data) result += "1, "; - else - { + else { result += "0, "; string outId = string.Format("PC_WELD_COMPLETE_{0}", i); - if (_PLC.ReadBit((int)eBitInput.PLC_USE_WELD_JIG_1 + i).data) - { + 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)")); } @@ -941,8 +840,7 @@ namespace ILSOS_CCW_H52A.Sequences private SequenceActionResult SetUploadWeldingList_SeqParams(SequenceContext context, string id, SequenceActionResult param) { //현재 시퀀스에서 LoadList를 해주기 때문에 Sub 시퀀스에 IncludeListStartEnd를 false로 넘겨줘야 함 - SeqUploadWeldingList_InitParam seqParams = new SeqUploadWeldingList_InitParam() - { + SeqUploadWeldingList_InitParam seqParams = new SeqUploadWeldingList_InitParam() { NeedSetLaserTiming = _ModulationMode, IncludeListStartEnd = false, CellMeasureCenterOffsetX = _CellMeasureCenterOffsetX, @@ -958,8 +856,7 @@ namespace ILSOS_CCW_H52A.Sequences private SequenceActionResult GetUploadWeldingList_SeqResult(SequenceContext context, string id, SequenceActionResult param) { var resultOutParam = param.Param as SeqUploadWeldingList_OutParam; - if (resultOutParam.Success == false) - { + if (resultOutParam.Success == false) { //AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_UploadWeldingList_ResultFailed); return SequenceAction.ForceStopByAlarm(AlarmCode.ALARM_SEQUENCE_UploadWeldingList_ResultFailed); //return null; @@ -970,8 +867,7 @@ namespace ILSOS_CCW_H52A.Sequences private SequenceActionResult SetReceiveCellInfo_SeqParams(SequenceContext context, string id, SequenceActionResult param) { - SeqReceiveCellInfo_InitParam seqParams = new SeqReceiveCellInfo_InitParam() - { + SeqReceiveCellInfo_InitParam seqParams = new SeqReceiveCellInfo_InitParam() { }; return SequenceAction.Ok(seqParams); @@ -980,8 +876,7 @@ namespace ILSOS_CCW_H52A.Sequences private SequenceActionResult GetReceiveCellInfo_SeqResult(SequenceContext context, string id, SequenceActionResult param) { var resultOutParam = param.Param as SeqReceiveCellInfo_OutParam; - if (resultOutParam.Success == false) - { + if (resultOutParam.Success == false) { return null; } return SequenceAction.Ok(); @@ -996,15 +891,13 @@ namespace ILSOS_CCW_H52A.Sequences _Scanner.SetEndList(); #region List 실행(처음엔 ExecuteList, 다음부터 AutoChangeList, 이후 대상 리스트 교체 (1 <-> 2)) - if (isTheFirstListExecute) - { + if (isTheFirstListExecute) { isTheFirstListExecute = false; _Scanner.ExecuteList(_WeldingScannerListNo); Log.I(string.Format($"START Welding : ExecuteList_{_WeldingScannerListNo}")); } - else - { + else { _Scanner.AutoChange(); Log.I(string.Format($"Next upload list : uploadList_{_WeldingScannerListNo}")); } @@ -1027,26 +920,20 @@ namespace ILSOS_CCW_H52A.Sequences double send_time_offset = 0.1; string LotID = "", CellID = ""; - while (!context.Stopped) - { + while (!context.Stopped) { 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; - if (WeldingInPosition) - { - if (InputScannerList[(int)_WeldingScannerListNo - 1].CanExist) - { + 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) - { + 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) - { + if (currentAngle > InputScannerList[(int)_WeldingScannerListNo - 1].StartAngle) { InputScannerList[(int)_WeldingScannerListNo - 1].CheckSendCellinfo = _StreamLightLBAD.SendCellInfo(LotID, CellID, _RecipeNo, _ModelNo, InputScannerList[(int)_WeldingScannerListNo - 1].JigNo); if (InputScannerList[(int)_WeldingScannerListNo - 1].CheckSendCellinfo == false) @@ -1059,32 +946,27 @@ namespace ILSOS_CCW_H52A.Sequences } } - if (_IO._iOUnit.ReadDiBit(Convert.ToInt32(eADIAddress.LaserOn))) - { - if (InputScannerList[(int)_WeldingScannerListNo - 1].WeldStartTime == DateTime.MinValue && InputScannerList[(int)_WeldingScannerListNo - 1].CanExist) - { + 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})"); } - if (EncoderLogCount == 0) - { + 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; } - for (int i = 0; i < _RotaryJigCount; i++) - { - if (_PLC.ReadBit((int)eBitInput.PLC_USE_WELD_JIG_1 + i).data == false) - { + 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); } } - _PLC.WriteDWord((int)eWordOutput.PC_CURRENT_POS, (int)((currentAngle + _RotaryEncoderOffset) % 360 * 100000f)); + _PLC.WriteDWord((int)eWordOutput.PC_CURRENT_POS, (int)((currentAngle + _RotaryEncoderOffset) % 360 * 100_000f)); Thread.Sleep(1); // 부하 방지 } return SequenceAction.Ok(); @@ -1092,8 +974,7 @@ namespace ILSOS_CCW_H52A.Sequences private void UpdateProductCount(SequenceContext context) { - if (_SystemParameter.LastResetTime < DateTime.Now.Date) - { + if (_SystemParameter.LastResetTime < DateTime.Now.Date) { _SystemParameter.LastResetTime = DateTime.Now.Date; Log.I($"Today Count Reset. {_SystemParameter.TodayProductCount} -> 0"); _SystemParameter.TodayProductCount = 0; @@ -1101,12 +982,10 @@ namespace ILSOS_CCW_H52A.Sequences _TodayProduct = _SystemParameter.TodayProductCount; _TotalProduct = _SystemParameter.TotalProductCount; - if (_TodayProduct < uint.MaxValue) - { + if (_TodayProduct < uint.MaxValue) { _TodayProduct++; } - if (_TotalProduct < uint.MaxValue) - { + if (_TotalProduct < uint.MaxValue) { _TotalProduct++; } _SystemParameter.TodayProductCount = _TodayProduct; @@ -1138,8 +1017,7 @@ namespace ILSOS_CCW_H52A.Sequences { Status.SetWorkingPosition(eWorkingPosition.REPORTING_RESULT); - SeqReportCellResult_InitParam seqParams = new SeqReportCellResult_InitParam() - { + SeqReportCellResult_InitParam seqParams = new SeqReportCellResult_InitParam() { EDC_Interface = new EDC(), }; @@ -1149,8 +1027,7 @@ namespace ILSOS_CCW_H52A.Sequences private SequenceActionResult GetReportCellResult_SeqResult(SequenceContext context, string id, SequenceActionResult param) { var resultOutParam = param.Param as SeqReportCellResult_OutParam; - if (resultOutParam.Success == false) - { + if (resultOutParam.Success == false) { return null; } @@ -1168,12 +1045,10 @@ namespace ILSOS_CCW_H52A.Sequences //Jin : cycleStop 명령을 용접 프로그램에서 할 경우가 현재는 없음.. // CheckCycleStop 노드와 Complete 노드 사이 다른 노드가 필요하다면 해당 노드를 CheckCycleStop 노드 위로 올리자 bool cycleStop = false; - if (cycleStop) - { + if (cycleStop) { return SEQ_SAVE_RESULT_DATA; } - else - { + else { return SEQ_CHECK_LOAD_COMPLETE; } } @@ -1189,8 +1064,7 @@ namespace ILSOS_CCW_H52A.Sequences private void SaveCellAPD() { - if (Status.GetProcessingWeldingData().APD.Count > 0) - { + if (Status.GetProcessingWeldingData().APD.Count > 0) { string directoryPath = "D:\\ILSOSConfig\\APD_History"; string fileName = DateTime.Now.ToString("HH") + "_CellAPD"; string fullPath = Path.Combine(directoryPath, Status.GetProcessingWeldingData().APD.First().Date.ToString("yyyy-MM-dd"), fileName); @@ -1215,8 +1089,7 @@ namespace ILSOS_CCW_H52A.Sequences private SequenceActionResult InitConditionError(SequenceContext context, string id, SequenceActionResult param) { context.TryGetValue(ContextKey.KEY_SEQUENCE_ERROR_CODE, out int InitializeCode); - switch (InitializeCode) - { + switch (InitializeCode) { case (int)InitErrorList.PLC_AUTORUN_BIT_OFF: Log.D("AutoRun Sequence InitConditionError : PLC_AUTORUN_OFF"); return SequenceAction.ForceStopByAlarm(AlarmCode.ALARM_SEQUENCE_AutoRun_CheckInitCondition_PLC_AUTORUN_OFF); @@ -1250,8 +1123,7 @@ namespace ILSOS_CCW_H52A.Sequences context.TryGetValue(ContextKey.KEY_SEQUENCE_RESULT, out bool seqResult); SeqAutoRunCCWH52A_OutParam result = new SeqAutoRunCCWH52A_OutParam() { Success = seqResult }; - if (seqResult == false) - { + if (seqResult == false) { //AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_AutoRun_ResultFailed); } Log.D("AutoRun Sequence Complete"); @@ -1263,8 +1135,7 @@ namespace ILSOS_CCW_H52A.Sequences private bool RestoreDeviceStatus() { //PLC All BIT OFF - for (int i = 0; i < _RotaryJigCount; i++) - { + for (int i = 0; i < _RotaryJigCount; i++) { _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE_1 + i, false); } _PLC.WriteBit((int)eBitOutput.PC_AUTO_STATUS, false); @@ -1273,10 +1144,8 @@ namespace ILSOS_CCW_H52A.Sequences //LBAD Ready Off //_LBAD.SetReadybyAuto(false); - if (_SystemParameter.BypassModeLBAD == false) - { - if (_StreamLightLBAD.SendReadyStatus(eRequestData.Ready_Off) == false) - { + if (_SystemParameter.BypassModeLBAD == false) { + if (_StreamLightLBAD.SendReadyStatus(eRequestData.Ready_Off) == false) { Log.I(string.Format($"[LBAD Interface]CheckReadyStatus null")); } } @@ -1327,19 +1196,14 @@ namespace ILSOS_CCW_H52A.Sequences //PLC All BIT OFF for (int i = 0; i < _RotaryJigCount; i++) - { - _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE_1 + i, false); - } - _PLC.WriteBit((int)eBitOutput.PC_AUTO_STATUS, false); - //_PLC.WriteBit((int)eBitOutput.PC_RECIPE_PARAM_UPLOAD_COMPLETE, false); - Log.I(string.Format("[PLCInterface]PC All Bit Off")); + _PLC.WriteBit(eBitOutput.PC_WELD_COMPLETE_1 + i, false); + _PLC.WriteBit(eBitOutput.PC_AUTO_STATUS, false); + Log.I("[PLCInterface]PC All Bit Off"); //LBAD Ready Off //_LBAD.SetReadybyAuto(false); - if (_SystemParameter.BypassModeLBAD == false) - { - if (_StreamLightLBAD.SendReadyStatus(eRequestData.Ready_Off) == false) - { + if (_SystemParameter.BypassModeLBAD == false) { + if (_StreamLightLBAD.SendReadyStatus(eRequestData.Ready_Off) == false) { Log.I(string.Format($"[LBAD Interface]CheckReadyStatus null")); } } diff --git a/WeldingComponents/WeldingCommon/CommonEquipmentSetup.cs b/WeldingComponents/WeldingCommon/CommonEquipmentSetup.cs index 9b574a5..0a307a0 100644 --- a/WeldingComponents/WeldingCommon/CommonEquipmentSetup.cs +++ b/WeldingComponents/WeldingCommon/CommonEquipmentSetup.cs @@ -236,7 +236,7 @@ namespace WeldingCommon //registry.Register(CommonDeviceIds.Laser, () => new Laser(CommonDeviceIds.Laser, isSimul), new LaserConfig(), // device => new UCLaserDevice() { DataContext = new UCLaserDeviceViewModel(AuroraEnv, CommonDeviceIds.Laser) }); - registry.Register(CommonDeviceIds.Scanner, () => new Scanner(CommonDeviceIds.Scanner), new ScannerConfig()); + //registry.Register(CommonDeviceIds.Scanner, () => new Scanner(CommonDeviceIds.Scanner), new ScannerConfig()); //registry.Register(CommonDeviceIds.SmcChiller, () => new SmcChiller(CommonDeviceIds.SmcChiller, 0, isSimul), new SmcChiller.Config(), // device => new UCSMCChillerDevice() { DataContext = new UCSMCChillerDeviceViewModel(AuroraEnv, CommonDeviceIds.SmcChiller) }); registry.Register(CommonDeviceIds.SmcChiller, () => new SmcChiller(CommonDeviceIds.SmcChiller), new SmcChillerConfig(), diff --git a/WeldingComponents/WeldingCommon/EquipDefCommon.cs b/WeldingComponents/WeldingCommon/EquipDefCommon.cs index 6d0fd20..d8eea03 100644 --- a/WeldingComponents/WeldingCommon/EquipDefCommon.cs +++ b/WeldingComponents/WeldingCommon/EquipDefCommon.cs @@ -108,11 +108,11 @@ public const string Laser = "Laser"; public const string LaserIO = "LaserIO"; - public const string Scanner = "Scanner"; public const string SmcChiller = "SmcChiller"; public const string PowerMeter = "PowerMeter"; public const string Light = "Light"; // Camera Light Controller public const string PLCDevice = "PLC"; + public const string Scanner = "Scanner"; public const string IODevice = "IO_Board"; public const string CellTracker = "CellTracker";