using System; using System.Threading; using System.IO.Ports; using System.Windows.Forms; using LFP_Manager.DataStructure; using LFP_Manager.Function; using LFP_Manager.Utils; using DevExpress.XtraGauges.Core.Model; using System.Collections.Concurrent; using System.ComponentModel; namespace LFP_Manager.Threads { #region DELEGATE // 표준 이벤트 패턴 public sealed class DataRecvEventArgs : EventArgs { public byte[] Data { get; } public DataRecvEventArgs(byte[] data) => Data = data; } public sealed class PrintEventArgs : EventArgs { public int Id { get; } public string Message { get; } public PrintEventArgs(int id, string message) => (Id, Message) = (id, message); } #endregion class csUartThread { #region DELEGATE // 표준 이벤트 패턴 public sealed class ModuleDataEventArgs : EventArgs { public CsDeviceData.DeviceModuleData Module { get; } public ModuleDataEventArgs(CsDeviceData.DeviceModuleData module) => Module = module; } #endregion #region CONSTANTS private const int RX_PACKET_BUFFER_SIZE = 512; #endregion #region VARIABLES private readonly CommConfig Config; private CsDeviceData.DeviceModuleData ModuleData; private Thread _serialComm = null; private SerialPort sPort = null; private CancellationTokenSource _cts; private volatile bool _serialPortThreadEnd = true; private readonly ISynchronizeInvoke _ui; // 보통 Form 또는 Control public int ModuleId = 0; private bool UartPolling = false; private bool AutoUartTx = true; private int addr_ch = 0; private ushort RequestRegAddr = 0x0000; //Byul Init 0x0000 private ushort RequestRegLen = 50; private bool rFlag = false; private int rFlag2 = 0; private ushort ReadRegAddr = 0x0000; //Byul Init 0x0000 private ushort ReadRegLen = 0x0000; //Byul Init 0x0000 private ushort WriteRegAddr = 0x0000; //Byul Init 0x0000 private short WriteCoilRegData = 0; private byte[] WriteRegData; private readonly ConcurrentQueue _rxQ = new ConcurrentQueue(); private TUartTxBuff UartTxBuff; // 이벤트 public event EventHandler OnUpdate; public event EventHandler OnDataRecv; public event EventHandler OnPrint; #endregion #region CONSTRUCTORS public csUartThread(int mId, CommConfig aConfig, ISynchronizeInvoke uiInvoker, // ← 추가 CsDeviceData.DeviceModuleData aModuleData) { ModuleId = mId; Config = aConfig; ModuleData = aModuleData; _ui = uiInvoker; // Form(this) 또는 어떤 Control UartTxBuff = new TUartTxBuff(); } public bool Start(ref CommConfig aConfig, int sId, bool aPolling) { ModuleId = sId; UartPolling = aPolling; //if (!Open(aConfig.UartPort, aConfig.UartBaudrate)) if (!Open(aConfig.UartPort, 115200)) return false; // 이중 시작 방지 if (_serialComm != null && _serialComm.IsAlive) return true; _serialPortThreadEnd = false; _cts = new CancellationTokenSource(); _serialComm = new Thread(() => uartCommThread(_cts.Token)) { IsBackground = true, Name = "UartCommThread" }; _serialComm.Start(); return true; } private void Stop() { _serialPortThreadEnd = true; var cts = _cts; _cts = null; if (cts != null && !cts.IsCancellationRequested) cts.Cancel(); var t = _serialComm; _serialComm = null; if (t != null && t.IsAlive) { try { t.Join(500); } catch { /* timeout 시 로그만 */ } } Close(); // SerialPort 닫기 및 핸들러 해제 } public void Dispose() { Stop(); } #endregion #region COMMPORT CONTROLS private void sDataReceived(object sender, SerialDataReceivedEventArgs e) { try { var sp = (SerialPort)sender; int available = sp.BytesToRead; if (available <= 0) return; byte[] tmp = new byte[available]; int read = sp.Read(tmp, 0, tmp.Length); for (int i = 0; i < read; i++) _rxQ.Enqueue(tmp[i]); } catch (Exception) { /* 필요시 로그 */ } } private void sErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e) { // //csMakeDataFunction.SetData() } private void sPinChanged(object sender, System.IO.Ports.SerialPinChangedEventArgs e) { // } private bool Open(string cPort, int cBaudrate) { sPort = new SerialPort(); sPort.PortName = cPort; sPort.BaudRate = cBaudrate; sPort.DataReceived += sDataReceived; sPort.ErrorReceived += sErrorReceived; sPort.PinChanged += sPinChanged; try { sPort.Open(); } catch (Exception ex) { throw new Exception("Error Open - " + ex.Message); } return sPort.IsOpen; } private void Close() { if (sPort != null) { try { sPort.DataReceived -= sDataReceived; sPort.ErrorReceived -= sErrorReceived; sPort.PinChanged -= sPinChanged; if (sPort.IsOpen) sPort.Close(); } finally { sPort.Dispose(); sPort = null; } } } #endregion #region PUBLIC FUNCTION public void SetPolling(bool flag, int sId, ref CsDeviceData.DeviceModuleData aSystemData) { ModuleId = sId; UartPolling = flag; ModuleData = aSystemData; ModuleData.mNo = ModuleId; } public void SetAutoTx(bool autoTx) { AutoUartTx = autoTx; } public void SetWriteReg(ushort WriteAddr, byte[] WriteData, bool ReplyFlag, int type) { WriteRegAddr = WriteAddr; TUartTRxData uartTRxData = new TUartTRxData(); uartTRxData.type = type; uartTRxData.data = WriteData; uartTRxData.length = uartTRxData.data.Length; UartTxBuff?.PutBuff(uartTRxData); } public void SetParam(ushort WriteAddr, short[] WriteData) { short[] wData = new short[1]; wData[0] = WriteData[0]; byte[] sData = CsModbusFunction.MakeWriteRegisterData((ushort)ModuleId, WriteAddr, wData); if (sData != null) { TUartTRxData aData = new TUartTRxData(); aData.length = sData.Length; aData.data = sData; UartTxBuff.PutBuff(aData); } } public void SendProcessFromApp(int sId, int mode, int index, int flag , ref CsDeviceData.DeviceModuleData.DeviceParamData aParam, ref CsDeviceData.DeviceModuleData.DeviceCalibration aCalib) { short[] data = null; switch (mode) { case csConstData.UART_PARAM_LIST.CELL_UNDER_VOLTAGE: // Cell Under Volatge Parameter data = new short[1]; data[0] = aParam.CellUnderVoltageWarning; SetParam((ushort)(mode + 0), data); data[0] = aParam.CellUnderVoltageTrip; SetParam((ushort)(mode + 1), data); data[0] = aParam.CellUnderVoltageRelease; SetParam((ushort)(mode + 2), data); break; case csConstData.UART_PARAM_LIST.CELL_OVER_VOLTAGE: // Cell Over Volatge Parameter data = new short[1]; data[0] = aParam.CellOverVoltageWarning; SetParam((ushort)(mode + 0), data); data[0] = aParam.CellOverVoltageTrip; SetParam((ushort)(mode + 1), data); data[0] = aParam.CellOverVoltageRelease; SetParam((ushort)(mode + 2), data); break; case csConstData.UART_PARAM_LIST.PACK_UNDER_VOLTAGE: // Pack Under Volatge Parameter data = new short[1]; data[0] = (short)(aParam.SysUnderVoltageWarning * 10); SetParam((ushort)(mode + 0), data); data[0] = (short)(aParam.SysUnderVoltageTrip * 10); SetParam((ushort)(mode + 1), data); data[0] = (short)(aParam.SysUnderVoltageRelease * 10); SetParam((ushort)(mode + 2), data); break; case csConstData.UART_PARAM_LIST.PACK_OVER_VOLTAGE: // Pack Over Volatge Parameter data = new short[1]; data[0] = (short)(aParam.SysOverVoltageWarning * 10); SetParam((ushort)(mode + 0), data); data[0] = (short)(aParam.SysOverVoltageTrip * 10); SetParam((ushort)(mode + 1), data); data[0] = (short)(aParam.SysOverVoltageRelease * 10); SetParam((ushort)(mode + 2), data); break; case csConstData.UART_PARAM_LIST.CHG_LOW_TEMPERATURE: // Charge Low Temperature Parameter data = new short[1]; data[0] = (short)(aParam.ChaLowTempWarning + 50); SetParam((ushort)(mode + 0), data); data[0] = (short)(aParam.ChaLowTempTrip + 50); SetParam((ushort)(mode + 1), data); data[0] = (short)(aParam.ChaLowTempRelease + 50); SetParam((ushort)(mode + 2), data); break; case csConstData.UART_PARAM_LIST.CHG_HIGH_TEMPERATURE: // Pack High Temperature Parameter data = new short[1]; data[0] = (short)(aParam.ChaHighTempWarning + 50); SetParam((ushort)(mode + 0), data); data[0] = (short)(aParam.ChaHighTempTrip + 50); SetParam((ushort)(mode + 1), data); data[0] = (short)(aParam.ChaHighTempRelease + 50); SetParam((ushort)(mode + 2), data); break; case csConstData.UART_PARAM_LIST.DCH_LOW_TEMPERATURE: // Charge Low Temperature Parameter data = new short[1]; data[0] = (short)(aParam.DchLowTempWarning + 50); SetParam((ushort)(mode + 0), data); data[0] = (short)(aParam.DchLowTempTrip + 50); SetParam((ushort)(mode + 1), data); data[0] = (short)(aParam.DchLowTempRelease + 50); SetParam((ushort)(mode + 2), data); break; case csConstData.UART_PARAM_LIST.DCH_HIGH_TEMPERATURE: // Pack High Temperature Parameter data = new short[1]; data[0] = (short)(aParam.DchHighTempWarning + 50); SetParam((ushort)(mode + 0), data); data[0] = (short)(aParam.DchHighTempTrip + 50); SetParam((ushort)(mode + 1), data); data[0] = (short)(aParam.DchHighTempRelease + 50); SetParam((ushort)(mode + 2), data); break; case csConstData.UART_PARAM_LIST.CHG_OVER_CURRENT: // Charge Over Current Parameter data = new short[1]; data[0] = (short)(aParam.ChaOverCurrentReleaseTime * 1); SetParam((ushort)(mode + 0), data); data[0] = (short)(aParam.ChaOverCurrentTrip1 * 10); SetParam((ushort)(mode + 2), data); data[0] = (short)(aParam.ChaOverCurrentTrip2 * 10); SetParam((ushort)(mode + 5), data); break; case csConstData.UART_PARAM_LIST.DCH_OVER_CURRENT: // Discharge Over Current Parameter data = new short[1]; data[0] = (short)(aParam.DchOverCurrentReleaseTime * 1); SetParam((ushort)(mode + 0), data); data[0] = (short)(aParam.DchOverCurrentTrip1 * 10); SetParam((ushort)(mode + 2), data); data[0] = (short)(aParam.DchOverCurrentTrip2 * 10); SetParam((ushort)(mode + 5), data); break; case csConstData.UART_PARAM_LIST.LOW_CAPACITY: data = new short[1]; data[0] = aParam.LowSocWarning; SetParam((ushort)(mode + 0), data); break; case csConstData.UART_PARAM_LIST.DEFAULT_PARAM: // Default Parameter int i = 0; Forms.fmxWait WaitForm = new Forms.fmxWait(); WaitForm.StartPosition = FormStartPosition.CenterScreen; try { //WaitForm.Show(); data = new short[1]; i = csConstData.UART_PARAM_LIST.CELL_UNDER_VOLTAGE; data[0] = csConstData.UART_PARAM_DEFAULT.Warn_Cell_UV; SetParam((ushort)(i + 0), data); data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Cell_UV; SetParam((ushort)(i + 1), data); data[0] = csConstData.UART_PARAM_DEFAULT.Release_Cell_UV; SetParam((ushort)(i + 2), data); i = csConstData.UART_PARAM_LIST.PACK_UNDER_VOLTAGE; data[0] = csConstData.UART_PARAM_DEFAULT.Warn_Pack_UV; SetParam((ushort)(i + 0), data); data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Pack_UV; SetParam((ushort)(i + 1), data); data[0] = csConstData.UART_PARAM_DEFAULT.Release_Pack_UV; SetParam((ushort)(i + 2), data); i = csConstData.UART_PARAM_LIST.CELL_OVER_VOLTAGE; data[0] = csConstData.UART_PARAM_DEFAULT.Warn_Cell_OV; SetParam((ushort)(i + 0), data); data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Cell_OV; SetParam((ushort)(i + 1), data); data[0] = csConstData.UART_PARAM_DEFAULT.Release_Cell_OV; SetParam((ushort)(i + 2), data); i = csConstData.UART_PARAM_LIST.PACK_OVER_VOLTAGE; data[0] = csConstData.UART_PARAM_DEFAULT.Warn_Pack_OV; SetParam((ushort)(i + 0), data); data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Pack_OV; SetParam((ushort)(i + 1), data); data[0] = csConstData.UART_PARAM_DEFAULT.Release_Pack_OV; SetParam((ushort)(i + 2), data); i = csConstData.UART_PARAM_LIST.CHG_OVER_CURRENT; data[0] = csConstData.UART_PARAM_DEFAULT.Times_Charge_OC; SetParam((ushort)(i - 2), data); data[0] = csConstData.UART_PARAM_DEFAULT.ReleaseTime_Charge_OC; SetParam((ushort)(i + 0), data); data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Charge_OC1; SetParam((ushort)(i + 2), data); data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Charge_OC2; SetParam((ushort)(i + 5), data); data[0] = csConstData.UART_PARAM_DEFAULT.Delay_Charge_OC1; SetParam((ushort)(i + 8), data); data[0] = csConstData.UART_PARAM_DEFAULT.Delay_Charge_OC2; SetParam((ushort)(i + 9), data); i = csConstData.UART_PARAM_LIST.DCH_OVER_CURRENT; data[0] = csConstData.UART_PARAM_DEFAULT.Times_Discharge_OC; SetParam((ushort)(i - 2), data); data[0] = csConstData.UART_PARAM_DEFAULT.ReleaseTime_Discharge_OC; SetParam((ushort)(i + 0), data); data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Discharge_OC1; SetParam((ushort)(i + 2), data); data[0] = csConstData.UART_PARAM_DEFAULT.Protect_Discharge_OC2; SetParam((ushort)(i + 5), data); data[0] = csConstData.UART_PARAM_DEFAULT.Delay_Discharge_OC1; SetParam((ushort)(i + 8), data); data[0] = csConstData.UART_PARAM_DEFAULT.Delay_Discharge_OC2; SetParam((ushort)(i + 9), data); i = csConstData.UART_PARAM_LIST.CHG_LOW_TEMPERATURE; data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Warn_Charge_UT + 50); SetParam((ushort)(i + 0), data); data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Protect_Charge_UT + 50); SetParam((ushort)(i + 1), data); data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Release_Charge_UT + 50); SetParam((ushort)(i + 2), data); i = csConstData.UART_PARAM_LIST.CHG_HIGH_TEMPERATURE; data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Warn_Charge_OT + 50); SetParam((ushort)(i + 0), data); data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Protect_Charge_OT + 50); SetParam((ushort)(i + 1), data); data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Release_Charge_OT + 50); SetParam((ushort)(i + 2), data); i = csConstData.UART_PARAM_LIST.DCH_LOW_TEMPERATURE; data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Warn_Discharge_UT + 50); SetParam((ushort)(i + 0), data); data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Protect_Discharge_UT + 50); SetParam((ushort)(i + 1), data); data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Release_Discharge_UT + 50); SetParam((ushort)(i + 2), data); i = csConstData.UART_PARAM_LIST.DCH_HIGH_TEMPERATURE; data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Warn_Discharge_OT + 50); SetParam((ushort)(i + 0), data); data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Protect_Discharge_OT + 50); SetParam((ushort)(i + 1), data); data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Release_Discharge_OT + 50); SetParam((ushort)(i + 2), data); i = csConstData.UART_PARAM_LIST.LOW_CAPACITY; data[0] = csConstData.UART_PARAM_DEFAULT.Warn_Low_Capacity + 0; SetParam((ushort)(i + 0), data); i = csConstData.UART_PARAM_LIST.ENV_LOW_TEMPERATURE; data[0] = 0; SetParam((ushort)(i + 0), data); data[0] = 0; SetParam((ushort)(i + 1), data); data[0] = 0; SetParam((ushort)(i + 2), data); i = csConstData.UART_PARAM_LIST.ENV_HIGH_TEMPERATURE; data[0] = 0; SetParam((ushort)(i + 0), data); data[0] = 0; SetParam((ushort)(i + 1), data); data[0] = 0; SetParam((ushort)(i + 2), data); } catch (Exception) { } finally { //WaitForm?.Close(); } break; case 15: // Capcity change data = new short[1]; i = csConstData.UART_PARAM_LIST.DESIGN_CAPACITY; data[0] = (short)aCalib.CapCalib.DesignCapacity; SetParam((ushort)(i + 0), data); i = csConstData.UART_PARAM_LIST.SOC_VALUE; data[0] = (short)aCalib.CapCalib.SocValue; SetParam((ushort)(i + 0), data); break; case 16: // Charge Mode data = new short[1]; i = csConstData.UART_CALIB_ADDR_LIST.CHAGE_MODE; data[0] = (short)aCalib.ChaMode.Mode; SetParam((ushort)(i + 0), data); data[0] = (short)aCalib.ChaMode.Value; SetParam((ushort)(i + 1), data); break; case 17: // BMS DateTime Set data = new short[2]; i = csConstData.UART_CALIB_ADDR_LIST.BMS_DATETIME; data[0] = (short)aCalib.BmsDateTime.sValue[0]; data[1] = (short)aCalib.BmsDateTime.sValue[1]; SetParam((ushort)(i + 0), data); break; case 18: data = new short[1]; i = csConstData.UART_CALIB_ADDR_LIST.ANTI_THEFT_GYRO; data[0] = (short)aCalib.AntiTheft.GyroScope; SetParam((ushort)(i + 0), data); break; case 19: // Clear Protect data = new short[1]; i = csConstData.UART_CALIB_ADDR_LIST.ANTI_THEFT_GYRO; data[0] = (short)0; SetParam((ushort)(i + 0), data); data[0] = (short)aCalib.AntiTheft.GyroScope; SetParam((ushort)(i + 0), data); break; case 20: data = new short[1]; i = csConstData.UART_CALIB_ADDR_LIST.ANTI_THEFT_COMM; data[0] = (short)aCalib.AntiTheft.Comm; SetParam((ushort)(i + 0), data); break; default: break; } } #endregion #region RX BUFFERING private int GetBuff() { return _rxQ.TryDequeue(out var b) ? (0x0100 | b) : 0; } private void FlushBuff() { while (_rxQ.TryDequeue(out _)) { } } #endregion #region TX BUFFERING private byte[] MakeWriteRegData(ushort DevID, ushort WriteAddr, ushort WriteLen, ref ushort[] WriteData) { byte[] wData = null; if (WriteLen > 0) { } return wData; } private string MakeCheckSum(byte[] sData, int len) { string result = ""; int checksum = 0; for (int i = 0; i < len; i++) { checksum += sData[i + 1]; } checksum = ~checksum + 1; result = String.Format("{0:X2}{1:X2}", (byte)(checksum >> 8), (byte)checksum); return result; } private byte[] MakeTxDataUart() { byte[] sData = null; rFlag2 = 0; if ((UartTxBuff != null) && (UartTxBuff.CheckBuff())) { TUartTRxData sBuff = UartTxBuff.GetBuff(); if (sBuff != null) { sData = sBuff.data; rFlag = true; rFlag2 = sBuff.type; } } else { if (UartPolling && AutoUartTx) { ushort sCmd; switch (addr_ch) { case 0: addr_ch = 1; RequestRegAddr = 0x0000; //Byul Init 0x0000 RequestRegLen = 0x0040; sCmd = csSerialCommFunction.READ_HOLDING_REG; rFlag = true; break; case 1: addr_ch = 2; RequestRegAddr = 0x0040; RequestRegLen = 0x0040; sCmd = csSerialCommFunction.READ_HOLDING_REG; rFlag = true; break; case 2: addr_ch = 3; RequestRegAddr = 0x0080; RequestRegLen = 0x0020 + 7; sCmd = csSerialCommFunction.READ_HOLDING_REG; rFlag = true; break; case 3: if (Config.ModuleQty > 1) { ModuleId++; if (ModuleId > Config.ModuleQty) { ModuleId = 1; } } addr_ch = 0; sCmd = csSerialCommFunction.NO_CMD; rFlag = false; break; default: addr_ch = 0; RequestRegAddr = 0x0000; //Byul Init 0x0000 RequestRegLen = 100; sCmd = csSerialCommFunction.READ_HOLDING_REG; break; } if (sCmd == csSerialCommFunction.NO_CMD) { sData = null; } else { sData = csSerialCommFunction.MakeReadRegisterData((ushort)ModuleId, sCmd, RequestRegAddr, RequestRegLen); } } } return sData; } #endregion #region COMM THREAD private readonly byte[] ReadBuf = new byte[RX_PACKET_BUFFER_SIZE]; ushort rPosition = 0; bool BuffStart = false; private void RecvPacketInit() { BuffStart = false; rPosition = 0; } private void uartCommThread(CancellationToken token) { int RecvTimeout = Config.RecvWaitTime; int TimeOutCount = 0; StartSend: while (!token.IsCancellationRequested && !_serialPortThreadEnd) { if (sPort == null || !sPort.IsOpen) { Thread.Sleep(50); continue; } FlushBuff(); byte[] txData = MakeTxDataUart(); if (txData != null) { try { sPort.Write(txData, 0, txData.Length); RaiseOnPrint(txData[0], csLog.trx_data_print(txData, txData.Length, 0)); } catch { /* 로그 */ RaiseOnPrint(ModuleId, "UART-232 Thread - TX Buff Error !"); } } if (rFlag == true) { var deadline = DateTime.UtcNow.AddMilliseconds(RecvTimeout); RecvPacketInit(); while (DateTime.UtcNow < deadline) { int gd = GetBuff(); if ((gd & 0x0100) == 0) { Thread.Sleep(1); continue; } byte cData = (byte)(gd & 0xFF); if (!BuffStart) { if (cData == (byte)ModuleId || cData == 0x7F) { rPosition = 0; ReadBuf[rPosition++] = cData; BuffStart = true; } } else { if (rPosition >= ReadBuf.Length) { RecvPacketInit(); continue; } ReadBuf[rPosition++] = cData; int chk = CsSerialCommFunctionDelta.ModbusPacketFromSlaveCheck(ReadBuf, rPosition); if (chk == 0) continue; RaiseOnPrint(ReadBuf[0], csLog.trx_data_print(ReadBuf, rPosition, 1)); if (chk == 1) { int mID = ReadBuf[0]; if (mID > 0) mID--; ModuleData.CommFail = false; ModuleData.ShelfCommFail = false; var rc = csSerialCommFunction.SerialRxProcess( ReadBuf, RequestRegAddr, (ushort)rPosition, ref ModuleData); var snapshot1 = ModuleData; // 필요 시 DeepCopy RaiseOnUpdate(snapshot1); Thread.Sleep(5); goto StartSend; } else if (chk == 2) { rFlag = false; RaiseOnPrint(ReadBuf[0], csLog.trx_data_print(ReadBuf, rPosition, 1)); goto StartSend; } else // -1 { RecvPacketInit(); Thread.Sleep(50); goto StartSend; } } } // timeout if (rPosition > 0) { RaiseOnPrint(ReadBuf[0], csLog.trx_data_print(ReadBuf, rPosition, 1)); } else { WriteRegAddr = 0; TimeOutCount++; if (TimeOutCount >= 5) { TimeOutCount = 5; //SystemData[0].CommFail = true; if (ModuleData.ShelfCommFail == false) { ModuleData.Reset(); ModuleData.ShelfCommFail = true; } } Thread.Sleep(100); } var snapshot2 = ModuleData; // 필요 시 DeepCopy RaiseOnUpdate(snapshot2); } else { Thread.Sleep(100); } /* if (rFlag == true) */ rPosition = 0; Thread.Sleep(50); } } #endregion #region SAFETY RAISE HELPER private void RaiseOnUpdate(CsDeviceData.DeviceModuleData snapshot) { var handler = OnUpdate; if (handler == null) return; var args = new ModuleDataEventArgs(snapshot); if (_ui != null && _ui.InvokeRequired) { try { _ui.BeginInvoke(new Action(() => handler(this, args)), null); } catch { /* 폼 종료 중 등 예외 무시(로그만) */ } } else { handler(this, args); } } private void RaiseOnDataRecv(byte[] data) { var handler = OnDataRecv; if (handler == null) return; var args = new DataRecvEventArgs(data); if (_ui != null && _ui.InvokeRequired) { try { _ui.BeginInvoke(new Action(() => handler(this, args)), null); } catch { } } else { handler(this, args); } } private void RaiseOnPrint(int id, string msg) { var handler = OnPrint; if (handler == null) return; var args = new PrintEventArgs(id, msg); if (_ui != null && _ui.InvokeRequired) { try { _ui.BeginInvoke(new Action(() => handler(this, args)), null); } catch { } } else { handler(this, args); } } #endregion } }