using Aurora; using Aurora.Api; using AuroraChiller; using AuroraIO; using AuroraLaser; using AuroraLaserScanner; using AuroraLBAD; using AuroraOCT; using CoreUnit.Scanner.UserControl.ScannerCanvas; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using WeldingCommon.Alarm; using WeldingCommon.Devices; using WeldingCommon.Settings; //using static AuroraIO.AJINEXTEK.AjinIO.IOModuleInfo; namespace WeldingCommon.Sequences { public class AlarmMonitoring { static readonly Logger Log = new Logger("AlarmMonitoring"); const string SEQ_START = "SEQ START"; // 딱 한번 Sequence 진입 시에만 실행될 Node const string SEQ_LoopFirst = "SEQ Loop First"; const string SEQ_WAIT_TIME = "SEQ Wait Time"; //const string SEQ_LoopLast = "SEQ Loop Last"; public const string CHECK_ALARM_PLC = "Check Alarm PLC"; public const string CHECK_ALARM_SCANNER = "Check Alarm Scanner"; public const string CHECK_ALARM_LASER = "Check Alarm Laser"; public const string CHECK_ALARM_LBAD = "Check Alarm LBAD"; public const string CHECK_ALARM_OCT = "Check Alarm OCT"; public const string CHECK_ALARM_CHILLER = "Check Alarm Chiller"; public const string CHECK_ALARM_LEAK_SENSOR = "Check Alarm LeakSensor"; public const string CHECK_ALARM_CHECK_DEVICE_WITH_IO = "Check Device_With_IO"; private static SequenceSetting _SequenceSetting; private static PLCController _Plc; private static Scanner _Scanner; private static LaserDevice _Laser; private static IODevice _IO; private static SmcChiller _Chiller; private static OCT _OCT; private static LBAD _LBAD; public delegate void ExternalDeviceActivatedCheckWithIO(SequenceContext context); public static ExternalDeviceActivatedCheckWithIO _ExternalDeviceActivatedCheckWithIO; public static void SetExternalDeviceActivatedCheckWithIO(ExternalDeviceActivatedCheckWithIO externalDeviceActivatedCheckWithIO) { _ExternalDeviceActivatedCheckWithIO = externalDeviceActivatedCheckWithIO; } public const int TIMEOUT_PLC_LIVE_CHECK = 2000; // msec public SequenceBuilder Builder() { SequenceBuilder sb = new SequenceBuilder(1000, true); sb.AddAction(SEQ_START, StartNode); sb.AddDecision(SEQ_LoopFirst, LoopFirstNode); sb.AddAction(SEQ_WAIT_TIME, WaitTime); sb.AddAction(CHECK_ALARM_PLC, CheckAlarmPLC); sb.AddAction(CHECK_ALARM_SCANNER, CheckAlarmScanner); sb.AddAction(CHECK_ALARM_LASER, CheckAlarmLaser); sb.AddAction(CHECK_ALARM_LBAD, CheckAlarmLBAD); sb.AddAction(CHECK_ALARM_OCT, CheckAlarmOCT); sb.AddAction(CHECK_ALARM_CHILLER, CheckAlarmChiller); sb.AddAction(CHECK_ALARM_LEAK_SENSOR, CheckAlarmLeakSensor); sb.AddAction(CHECK_ALARM_CHECK_DEVICE_WITH_IO, CheckDeviceWithIO); //sb.AddAction(SEQ_LoopLast, LoopLastNode); sb.Flows(sb.Init > sb[SEQ_START] > sb[SEQ_LoopFirst] > sb[SEQ_WAIT_TIME] > sb[CHECK_ALARM_PLC] > sb[CHECK_ALARM_SCANNER] > sb[CHECK_ALARM_LASER] > sb[CHECK_ALARM_LBAD] > sb[CHECK_ALARM_OCT] > sb[CHECK_ALARM_CHILLER] > sb[CHECK_ALARM_LEAK_SENSOR] > sb[CHECK_ALARM_CHECK_DEVICE_WITH_IO] //> sb[SEQ_LoopLast] > sb[SEQ_LoopFirst]); > sb[SEQ_LoopFirst]); sb.Flows(sb[SEQ_LoopFirst] > sb.Final); sb.OnStopped(OnStopped); sb.OnForceError(OnForceError); //sb.Draw("AlarmMonitor.pdf"); return sb; } private SequenceActionResult StartNode(SequenceContext context, string id, SequenceActionResult param) { _SequenceSetting = context.GetSetting(CommonSettingIds.Sequence); InitDev(context); return SequenceAction.Ok(); } private void InitDev(SequenceContext context) { if (_SequenceSetting.PlcActivated) { _Plc = context.GetPLC(); } if (_SequenceSetting.ScannerActivated) { _Scanner = context.GetLaserScanner(); } if (_SequenceSetting.LaserActivated) { _Laser = context.GetLaser(); } if (_SequenceSetting.LbadActivated) { _LBAD = context.GetLBAD(); } if (_SequenceSetting.OctActivated) { _OCT = context.GetOCT(); } if (_SequenceSetting.ChillerActivated) { _Chiller = context.GetChiller(); } // Scanner, Laser, LBAD, OCT, Chiller, LeakSensor 는 IO 로도 상태 check 할 수 있음. if (_SequenceSetting.ScannerActivatedWithIO || _SequenceSetting.LaserActivatedWithIO || _SequenceSetting.LbadActivatedWithIO || _SequenceSetting.OctActivatedWithIO || _SequenceSetting.ChillerActivatedWithIO || _SequenceSetting.LeakSensorActivatedWithIO) { _IO = context.GetIOUnit(); } } private string LoopFirstNode(SequenceContext context, string id, ICollection branches, SequenceActionResult param) { while (!context.Stopped) { _SequenceSetting = context.GetSetting(CommonSettingIds.Sequence); return SEQ_WAIT_TIME; } return SeqNodeIds.FINAL; } private SequenceActionResult WaitTime(SequenceContext context, string id, SequenceActionResult param) { Thread.Sleep(100); // Delay 가 얼마가 적당 한가 return SequenceAction.Ok(); } private SequenceActionResult CheckAlarmPLC(SequenceContext context, string id, SequenceActionResult param) { if (_Plc == null || _SequenceSetting.PlcActivated == false) { return SequenceAction.Ok(); } if (_Plc.IsSimul) { return SequenceAction.Ok(); } if (!_Plc.IsActivated) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_PLC_DEACTIVATED); } else { if (_SequenceSetting.PlcActivatedLive) { //PLC Live Check 시간은 확인 필요 Stopwatch sw = Stopwatch.StartNew(); while (!context.Stopped) { if (_Plc.ReadBit(_Plc.PLC_LIVE_STATUSAddr).data == true) { break; } if (sw.ElapsedMilliseconds > TIMEOUT_PLC_LIVE_CHECK) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_PLC_DISCONNECT); break; } Thread.Sleep(10); } } // EMO check 는 항상 해야 함. if (_Plc.ReadBit(_Plc.PLC_EMO_STATUSAddr).data == false) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_PLC_EMO); } } return SequenceAction.Ok(); } private SequenceActionResult CheckAlarmScanner(SequenceContext context, string id, SequenceActionResult param) { if (_Scanner == null || _SequenceSetting.ScannerActivated == false) { return SequenceAction.Ok(); } if (_Scanner.IsSimul) { return SequenceAction.Ok(); } if (!_Scanner.IsActivated) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_SCANNER_DEACTIVATED); } else { var lastErrorcode = _Scanner.GetLastError(); //Scanner의 activate 상태를 체크하면.. 수동상태에서 디바이스를 deactivaed시키면 알람 발생하므로 조건문 변경 필요 if (lastErrorcode != 0) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_SCANNER_ERROR); Log.E($"Scanner Error Code : {lastErrorcode}"); } // TODO: Scanner 는 Alarm 과 연결되어 있지 않음. 알람 추가되었을 시에 어떤 IO 를 check 하지?? } return SequenceAction.Ok(); } private SequenceActionResult CheckAlarmLaser(SequenceContext context, string id, SequenceActionResult param) { if (_Laser == null || _SequenceSetting.LaserActivated == false) { return SequenceAction.Ok(); } if (_Laser.IsSimul) { return SequenceAction.Ok(); } if (!_Laser.IsActivated) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_LASER_DEACTIVATED); } else { if (!_Laser.GetConnected()) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_IO_LASER_DISCONNECT); } // Won : IPG Ethernet Type 대비하여 Interface 함수로 확인 추가 if (_SequenceSetting.LaserActivated) { //Laser Disconnect Check 조건문 변경 필요 //if (!_IO._iOUnit.ReadDiBit(Convert.ToInt32(eADIAddress.LaserAssigned))) { //TODO: 아래 부분은 Laser 에 따로 추가 필요. Interface 에 추가 해야 할까?? 구현상 모든 Laser 에 있지는 않던데 // Won : GetLaserAssinged는 Pilot 설비에서만 사용함, 내용 추가 필요하면 구현 후 주석해제 //if (!_Laser.GetLaserAssigned()) { // AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_IO_LASER_DISCONNECT); //} if (_Laser.GetLaserError()) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_IO_LASER_ERROR); } if (_Laser.GetWarningStatus()) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_IO_LASER_WARNING); } // Won : 2170 EMO 배선 현재 없음 //if (_Laser.GetEStop()) { // AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_IO_LASER_EMO); //} } } return SequenceAction.Ok(); } private SequenceActionResult CheckAlarmLBAD(SequenceContext context, string id, SequenceActionResult param) { if (_LBAD == null || _SequenceSetting.LbadActivated == false) { return SequenceAction.Ok(); } if (_LBAD.IsSimul) { return SequenceAction.Ok(); } if (!_LBAD.IsActivated) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_LBAD_DEACTIVATED); } else { // TODO: ReadyByAuto 는 Auto 돌렸을 때, Sequence 안뜨도록 하는 로직 이라고 함. 이 부분 수정이 필요 할 듯. // 이게 Sequence 안에서 Try 하면서 계속 붙이는 부분과 관련이 있는 듯함. if (_LBAD.GetReadybyAuto()) { if (!_LBAD.GetConnected()) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_LBAD_DISCONNECT); } // TODO: 이건 바로 밖의 else 부분으로 빼야 할 지도, Device check 와 IO 로 check 는 다르므로 if (_SequenceSetting.LbadActivatedWithIO && CheckDeviceIO()) { //if (!_IO._iOUnit.ReadDiBit(Convert.ToInt32(eADIAddress.LBADReadyStatus))) { // AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_IO_LBAD_ERROR); //} } } } return SequenceAction.Ok(); } private SequenceActionResult CheckAlarmOCT(SequenceContext context, string id, SequenceActionResult param) { if (_OCT == null || _SequenceSetting.OctActivated == false) { return SequenceAction.Ok(); } if (_OCT.IsSimul) { return SequenceAction.Ok(); } if (!_OCT.IsActivated) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_OCT_DEACTIVATED); } else { if (!_OCT.GetConnected()) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_OCT_DISCONNECT); } if (_SequenceSetting.OctActivatedWithIO && CheckDeviceIO()) { //if (!_IO._iOUnit.ReadDiBit(Convert.ToInt32(eADIAddress.LDDError))) { // AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_IO_OCT_ERROR); //} } if (_OCT.OCT_QA_Error == true) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_OCT_QA_ERROR); _OCT.OCT_QA_Error = false; } } return SequenceAction.Ok(); } private SequenceActionResult CheckAlarmChiller(SequenceContext context, string id, SequenceActionResult param) { if (_Chiller == null || _SequenceSetting.ChillerActivated == false) { return SequenceAction.Ok(); } if (_Chiller.IsSimul) { return SequenceAction.Ok(); } if (!_Chiller.IsActivated) { //AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_CHILLER_DEACTIVATED); } else { if (!_Chiller.GetConnected()) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_CHILLER_DISCONNECT); } if (_Chiller.GetChillerRunningFlag()) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_CHILLER_ERROR); } if (_SequenceSetting.ChillerActivatedWithIO && CheckDeviceIO()) { // TODO: IO 로 Chiller check 하는 것이 있으면 추가 } } return SequenceAction.Ok(); } private SequenceActionResult CheckAlarmLeakSensor(SequenceContext context, string id, SequenceActionResult param) { if (_SequenceSetting.LeakSensorActivatedWithIO && CheckDeviceIO()) { //Leak Sensor IO Map 추가 필요 //if (_IO._iOUnit.ReadDiBit(Convert.ToInt32(eADIAddress.LeakSensorHead))) { // AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_IO_LEAK_SENSOR_DETECT_SCANNER); //} //if (_IO._iOUnit.ReadDiBit(Convert.ToInt32(eADIAddress.LeakSensorChiller))) { // AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_IO_LEAK_SENSOR_DETECT_CHILLER); //} } return SequenceAction.Ok(); } private SequenceActionResult CheckDeviceWithIO(SequenceContext context, string id, SequenceActionResult param) { if (CheckDeviceIO()) { if (_ExternalDeviceActivatedCheckWithIO != null) { _ExternalDeviceActivatedCheckWithIO(context); } } return SequenceAction.Ok(); } private bool CheckDeviceIO() { // WithIO 중 1개라도 True가 되어있지 않으면 _IO 객체 생성을 하지못하기때문에 Null 체크를 먼저 하자 if (_IO == null) { //_IO가 Simul이라서 Null이면 Alarm이 지속발생해서 우선 주석해둔다 //AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_IO_IS_NULL); return false; } // IO 가 Simul 이면, Read Bit 를 해주지 않기 위해 if (_IO.IsSimul) { return false; } if (_IO.IsActivated == false) { AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_IO_DEACTIVATED); return false; } // 우선 주석 처리 함. 잦은 호출이 발생할 수 있어서 //if (_IO.IsConnected() == false) { // AlarmManager.Alarm(AlarmCode.ALARM_DEVICE_IO_DISCONNECT); //} return true; } //private static SequenceActionResult LoopLastNode(SequenceContext context, string id, SequenceActionResult param) //{ // //Thread.Sleep(100); // return SequenceAction.Ok(); //} private void OnForceError(SequenceContext arg1, string arg2) { } private void OnStopped(SequenceContext obj) { } } }