using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using System.Threading; using System.IO; using System.IO.Ports; using System.Windows.Forms; using LFP_Manager.DataStructure; using LFP_Manager.Function; using LFP_Manager.Utils; namespace LFP_Manager.Threads { public delegate void CanDataUpdate(object sender, ref DeviceSystemData aSystemData); public delegate void CanDataRecv(UInt32 hdr, byte[] data); public delegate void CanDataPrint(object sender, string msg); class csCanThread { #region DLL FUNCTION [DllImport("library\\controlcan.dll")] static extern UInt32 VCI_OpenDevice(UInt32 DeviceType, UInt32 DeviceInd, UInt32 Reserved); [DllImport("library\\controlcan.dll")] static extern UInt32 VCI_CloseDevice(UInt32 DeviceType, UInt32 DeviceInd); [DllImport("library\\controlcan.dll")] static extern UInt32 VCI_InitCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_INIT_CONFIG pInitConfig); [DllImport("library\\controlcan.dll")] static extern UInt32 VCI_ReadBoardInfo(UInt32 DeviceType, UInt32 DeviceInd, ref VCI_BOARD_INFO pInfo); [DllImport("library\\controlcan.dll")] static extern UInt32 VCI_ReadErrInfo(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_ERR_INFO pErrInfo); [DllImport("library\\controlcan.dll")] static extern UInt32 VCI_ReadCANStatus(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_CAN_STATUS pCANStatus); [DllImport("library\\controlcan.dll")] static extern UInt32 VCI_GetReference(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, UInt32 RefType, ref byte pData); [DllImport("library\\controlcan.dll")] //static extern UInt32 VCI_SetReference(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, UInt32 RefType, ref byte pData); unsafe static extern UInt32 VCI_SetReference(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, UInt32 RefType, byte* pData); [DllImport("library\\controlcan.dll")] static extern UInt32 VCI_GetReceiveNum(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd); [DllImport("library\\controlcan.dll")] static extern UInt32 VCI_ClearBuffer(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd); [DllImport("library\\controlcan.dll")] static extern UInt32 VCI_StartCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd); [DllImport("library\\controlcan.dll")] static extern UInt32 VCI_ResetCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd); [DllImport("library\\controlcan.dll")] static extern UInt32 VCI_Transmit(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_CAN_OBJ pSend, UInt32 Len); //[DllImport("library\\controlcan.dll")] //static extern UInt32 VCI_Receive(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_CAN_OBJ pReceive, UInt32 Len, Int32 WaitTime); [DllImport("library\\controlcan.dll", CharSet = CharSet.Ansi)] static extern UInt32 VCI_Receive(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, IntPtr pReceive, UInt32 Len, Int32 WaitTime); #endregion #region VARIABLES public delegate void invokeDelegate(); CommConfig Config; DeviceSystemData SystemData; TCanTxBuff CanTxBuf; int SystemId = 0; Thread canCommTx = null; Thread canCommRx = null; //string Msg = ""; static UInt32 can_devtype = 21; //USBCAN-2e-u //////////////////////////////////////// const UInt32 STATUS_OK = 1; bool can_bOpen = false; UInt32 can_devind = 0; UInt32 can_canind = 0; bool CanTxThreadEnd = true; bool CanRxThreadEnd = true; bool CanPolling = false; bool AutoCanTx = true; int dcp = 0; // Data Control Parameter public event CanDataUpdate OnUpdate = null; public event CanDataPrint OnPrint = null; public event CanDataRecv OnDataRecv = null; private object lockObject = new object(); #endregion #region CONSTRUCTORS public csCanThread(int sId, CommConfig aConfig, ref DeviceSystemData aSystemData) { SystemId = sId; Config = aConfig; SystemData = aSystemData; CanTxBuf = new TCanTxBuff(); canCommTx = new Thread(CanTxThread); canCommRx = new Thread(CanRxThread); } public void disposeThread() { CanTxThreadEnd = true; if (canCommTx != null) { if (canCommTx.IsAlive) { canCommTx.Abort(); } canCommTx = null; } CanRxThreadEnd = true; if (canCommRx != null) { if (canCommRx.IsAlive) { canCommRx.Abort(); } canCommRx = null; } if (can_bOpen == true) { _ = VCI_CloseDevice(can_devtype, can_devind); } } public unsafe bool Start(ref CommConfig aConfig, int sId, bool aPolling) { bool result = false; SystemId = sId; CanPolling = aPolling; Config = aConfig; can_devtype = (UInt32)csCanConstData.CanDeviceInfo.DeviceIds[Config.CanDevice]; can_devind = (UInt32)Config.CanIndex; can_canind = (UInt32)Config.CanNo; try { if (VCI_OpenDevice(can_devtype, can_devind, 0) == 0) throw new System.InvalidOperationException("Failed to open the device, please check if the device type and device index number are correct."); //USB-E-U Code UInt32 baud; VCI_INIT_CONFIG config = new VCI_INIT_CONFIG(); if (can_devtype == csCanConstData.CanDeviceInfo.VCI_USBCAN_2E_U) { baud = (UInt32)csCanConstData.BaudRate.BaudRates[aConfig.CanBaudrate]; config.Timing0 = 0x00; config.Timing1 = 0x14; if (VCI_SetReference(can_devtype, can_devind, can_canind, 0, (byte*)&baud) != STATUS_OK) { VCI_CloseDevice(can_devtype, can_devind); throw new System.InvalidOperationException("Set baud rate error, failed to open device!"); } } else { baud = (UInt32)csCanConstData.BaudRate.BaudRatesOther[aConfig.CanBaudrate]; config.Timing0 = (byte)(baud >> 8); config.Timing1 = (byte)(baud & 0xFF); } can_bOpen = true; //Config setting config.AccCode = 0x00000000; config.AccMask = 0xFFFFFFFF; config.Filter = 1; config.Mode = 0; VCI_InitCAN(can_devtype, can_devind, can_canind, ref config); ////Filter setting //Int32 filterMode = comboBox_e_u_Filter.SelectedIndex; //if (2 != filterMode)//Not disabled //{ // VCI_FILTER_RECORD filterRecord = new VCI_FILTER_RECORD(); // filterRecord.ExtFrame = (UInt32)filterMode; // filterRecord.Start = System.Convert.ToUInt32("0x" + textBox_e_u_startid.Text, 16); // filterRecord.End = System.Convert.ToUInt32("0x" + textBox_e_u_endid.Text, 16); // //Fill filter table // VCI_SetReference(m_devtype, m_devind, m_canind, 1, (byte*)&filterRecord); // //Make the filter table take effect // byte tm = 0; // if (VCI_SetReference(m_devtype, m_devind, m_canind, 2, &tm) != STATUS_OK) // { // MessageBox.Show("Set filter failed", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); // VCI_CloseDevice(m_devtype, m_devind); // return; // } //} //////////////////////////////////////////////////////////////////////////// ////buttonConnect.Text = m_bOpen == 1 ? "DISCONNECT" : "CONNECT"; ////timer_rec.Enabled = m_bOpen == 1 ? true : false; UInt32 res; res = VCI_StartCAN(can_devtype, can_devind, can_canind); CanTxThreadEnd = false; CanRxThreadEnd = false; canCommTx.Start(); canCommRx.Start(); result = true; } catch (Exception ex) { System.Exception Ex = new System.Exception(ex.Message, ex); throw Ex; } return result; } #endregion #region TX FUNCTION public void SetAutoTx(bool autoTx) { AutoCanTx = autoTx; } public void SetDcp(int aDcp) { dcp = aDcp; } //public void SendProcessFromApp(int sId, int mode, int index, int flag, ref DeviceParamData aParam, ref DeviceCalibration aCalib, int type) //{ // if (can_bOpen == true) // { // TCanTRxData[] CanTRxData = csCanCommFunction.SendProcessFromAppPacket(sId, mode, index, flag, ref aParam, ref aCalib); // if (CanTRxData != null) // { // for (int i = 0; i < CanTRxData.Length; i++) // { // CanTRxData[i].type = type; // CanTxBuf.PutBuff(CanTRxData[i]); // } // } // } //} public void SendDataFromApp(uint header, byte[] data, int type) { if (can_bOpen == true) { TCanTRxData CanTRxData = new TCanTRxData { exid = header }; for (int i = 0; i < data.Length; i++) { CanTRxData.data[i] = data[i]; } CanTxBuf.PutBuff(CanTRxData); } } unsafe public void SendDataFromAppA(UInt32 header, byte[] data) { if (can_bOpen == false) return; VCI_CAN_OBJ sendobj = new VCI_CAN_OBJ(); sendobj.SendType = csCanConstData.SendType.Normal; sendobj.ExternFlag = csCanConstData.FrameType.Extended; sendobj.RemoteFlag = csCanConstData.FrameFormat.Data_Frame; sendobj.ID = header; // 0x1815c9c8 int len = 8; sendobj.DataLen = System.Convert.ToByte(len); for (int i = 0; i < len; i++) sendobj.Data[i] = data[i]; //int nTimeOut = 3000; //if (can_devtype == csCanConstData.CanDeviceInfo.VCI_USBCAN_2E_U) //{ // VCI_SetReference(can_devtype, can_devind, can_canind, 3, (byte*)&nTimeOut); //} if (VCI_Transmit(can_devtype, can_devind, can_canind, ref sendobj, 1) == 0) { throw new Exception("Failed to send"); } OnPrint?.Invoke(this, csCanCommFunction.PacketToMsg(sendobj, 1)); } public void SetPolling(bool flag, int sId, ref DeviceSystemData aSystemData) { SystemId = sId; CanPolling = flag; SystemData = aSystemData; SystemData.mNo = SystemId; } unsafe public void SendData() { if (can_bOpen == false) return; VCI_CAN_OBJ sendobj = new VCI_CAN_OBJ(); sendobj.SendType = csCanConstData.SendType.Normal; sendobj.ExternFlag = csCanConstData.FrameType.Extended; sendobj.RemoteFlag = csCanConstData.FrameFormat.Data_Frame; sendobj.ID = 0x1815c9c8; // 0x1815c9c8 int len = 8; sendobj.DataLen = Convert.ToByte(len); sendobj.Data[0] = 0xff; sendobj.Data[1] = 0xff; sendobj.Data[2] = 0xff; sendobj.Data[3] = 0xff; sendobj.Data[4] = 0xff; sendobj.Data[5] = 0xff; sendobj.Data[6] = 0xff; sendobj.Data[7] = 0xff; //int nTimeOut = 3000; //if (can_devtype == csCanConstData.CanDeviceInfo.VCI_USBCAN_2E_U) //{ // VCI_SetReference(can_devtype, can_devind, can_canind, 3, (byte*)&nTimeOut); //} if (VCI_Transmit(can_devtype, can_devind, can_canind, ref sendobj, 1) == 0) { throw new Exception("Failed to send"); } } public void SendProcessFromApp(int sId, int mode, int flag, int dcp, ref DeviceParamData aParam, ref DeviceCalibration aCalib) { switch (mode) { case 5: // Cell Volatge Parameter SendData(sId, 5, flag, dcp, ref aParam, ref aCalib); SendData(sId, 23, flag, dcp, ref aParam, ref aCalib); break; case 7: SendData(sId, 7, flag, dcp, ref aParam, ref aCalib); SendData(sId, 23, flag, dcp, ref aParam, ref aCalib); break; case 6: SendData(sId, 6, flag, dcp, ref aParam, ref aCalib); SendData(sId, 24, flag, dcp, ref aParam, ref aCalib); break; case 22: SendData(sId, 22, flag, dcp, ref aParam, ref aCalib); SendData(sId, 24, flag, dcp, ref aParam, ref aCalib); break; case 14: SendData(sId, 14, flag, dcp, ref aParam, ref aCalib); break; case 99: // All param read SendData(sId, 5, flag, dcp, ref aParam, ref aCalib); SendData(sId, 6, flag, dcp, ref aParam, ref aCalib); SendData(sId, 7, flag, dcp, ref aParam, ref aCalib); SendData(sId, 14, flag, dcp, ref aParam, ref aCalib); SendData(sId, 22, flag, dcp, ref aParam, ref aCalib); SendData(sId, 23, flag, dcp, ref aParam, ref aCalib); SendData(sId, 24, flag, dcp, ref aParam, ref aCalib); SendData(sId, 2, flag, dcp, ref aParam, ref aCalib); break; case 9: // Cell Voltage Calibration SendData(sId, 9, flag, dcp, ref aParam, ref aCalib); break; case 1: // Cell Balancing Flag Set SendData(sId, 1, flag, dcp, ref aParam, ref aCalib); break; case 12: // Cell Voltage Calibration SendData(sId, 12, flag, dcp, ref aParam, ref aCalib); break; case 8: // Battery Parameter Setting SendData(sId, 8, flag, dcp, ref aParam, ref aCalib); break; case 17: // Cell Balancing Parameter - 240531 SendData(sId, 17, flag, dcp, ref aParam, ref aCalib); break; case 21: // System Information (Device Address) SendData(sId, 21, flag, dcp, ref aParam, ref aCalib); break; case 13: // Soc Calibration SendData(sId, 13, flag, dcp, ref aParam, ref aCalib); break; case 15: // Default Parameter SendData(sId, 15, flag, dcp, ref aParam, ref aCalib); break; case 16: // Default Parameter if (aParam.DefalutParamAll == 1) SendData(0xFF, 16, flag, dcp, ref aParam, ref aCalib); else SendData(sId, 16, flag, dcp, ref aParam, ref aCalib); break; case 2: // Cell Voltage Difference Parameter SendData(sId, 2, flag, dcp, ref aParam, ref aCalib); break; case 25: SendData(sId, 25, flag, dcp, ref aParam, ref aCalib); break; // Inventory Data Control case 900: // Manufacturer Cmd SendData(sId, 900, flag, dcp, ref aParam, ref aCalib); break; case 901: // Serial Number #1 Cmd SendData(sId, 901, flag, dcp, ref aParam, ref aCalib); break; case 902: // Serial Number #2 Cmd SendData(sId, 902, flag, dcp, ref aParam, ref aCalib); break; } } //unsafe void SendData(int sId, int mode, int flag, ref DeviceParamData aParam, ref DeviceCalibration aCalib) //{ // if (can_bOpen == 0) return; // VCI_CAN_OBJ sendobj = MakeTxData(sId, mode, flag, ref aParam, ref aCalib); // Monitor.Enter(lockObject); // try // { // OnPrint?.Invoke(this, csCanCommFunction.PacketToMsg(sendobj, 1)); // if (VCI_Transmit(can_devtype, can_devind, can_canind, ref sendobj, 1) == 0) // { // throw new Exception("Failed to send"); // } // } // catch (Exception) // { // //throw new Exception(String.Format("CAN Transmit Exception", ex.Message)); // } // finally // { // Monitor.Exit(lockObject); // } //} void SendData(int sid, int mode, int flag, int dcp, ref DeviceParamData aParam, ref DeviceCalibration aCalib) { UInt32 exID; byte[] sendData = new byte[8]; exID = MakeTxData(sid, mode, flag, dcp, ref aParam, ref aCalib, ref sendData); SendDataFromApp(exID, sendData, 0); //SendCAN(exID, sendData); } unsafe void SendCAN(UInt32 exID, byte[] data) { if (can_bOpen == false) return; VCI_CAN_OBJ sendobj = new VCI_CAN_OBJ(); sendobj.SendType = csCanConstData.SendType.Normal; sendobj.RemoteFlag = csCanConstData.FrameFormat.Data_Frame; sendobj.ExternFlag = csCanConstData.FrameType.Extended; sendobj.ID = exID; sendobj.DataLen = (byte)data.Length; for (int i = 0; i < data.Length; i++) sendobj.Data[i] = data[i]; //Monitor.Enter(lockObject); if (Monitor.TryEnter(lockObject, 2000)) { try { if (VCI_Transmit(can_devtype, can_devind, can_canind, ref sendobj, 1) == 0) { throw new Exception("Failed to send"); } OnPrint?.Invoke(this, csCanCommFunction.PacketToMsg(sendobj, 1)); //OnPrint?.Invoke(this, csCanCommFunction.PacketToMsg(sendobj, 1)); } catch (Exception) { //throw new Exception(String.Format("CAN Transmit Exception", ex.Message)); } finally { Monitor.Exit(lockObject); } } else { //OnPrint?.Invoke(this, "CAN SEND FAIL"); throw new Exception("CAN SEND FAIL"); } //try //{ // if (VCI_Transmit(can_devtype, can_devind, can_canind, ref sendobj, 1) == 0) // { // throw new Exception("Failed to send"); // } // OnPrint?.Invoke(this, csCanCommFunction.PacketToMsg(sendobj, 1)); //} //catch (Exception) //{ // //throw new Exception(String.Format("CAN Transmit Exception", ex.Message)); //} //finally //{ // Monitor.Exit(lockObject); //} } private UInt32 MakeID(int sId, int mode) { UInt32 result = 0; PACKET_HEADER hdr = new PACKET_HEADER(); hdr.Index = 6; // Fixed 6 hdr.R = 0; // Fixed 0 hdr.DP = 0; // Fixed 0 hdr.PF = (byte)mode; // Command if (sId == 0xFF) hdr.PS = (byte)(sId); // PDU SPECIFIC else hdr.PS = (byte)(sId + 200); // PDU SPECIFIC hdr.SA = 200; // SOURCE ADDRESS result = csCanCommFunction.CovertHtoP(hdr); return result; } private UInt32 MakeInvID(int sId, int mode) { UInt32 result = 0; PACKET_HEADER hdr = new PACKET_HEADER(); hdr.Index = 1; // Fixed 1 for Inventory Data hdr.R = 0; // Fixed 0 hdr.DP = 0; // Fixed 0 hdr.PF = (byte)((mode % 900) + 1); // Command if (sId == 0xFF) hdr.PS = (byte)(sId); // PDU SPECIFIC else hdr.PS = (byte)(sId + 200); // PDU SPECIFIC hdr.SA = 200; // SOURCE ADDRESS result = csCanCommFunction.CovertHtoP(hdr); return result; } unsafe private VCI_CAN_OBJ MakeTxData(int sId, int mode, int flag, int dcp, ref DeviceParamData aParam, ref DeviceCalibration aCalib) { VCI_CAN_OBJ sendobj = new VCI_CAN_OBJ(); sendobj.SendType = csCanConstData.SendType.Normal; sendobj.RemoteFlag = csCanConstData.FrameFormat.Data_Frame; sendobj.ExternFlag = csCanConstData.FrameType.Extended; if (mode < 900) { sendobj.ID = MakeID(sId, mode); } else { sendobj.ID = MakeInvID(sId, mode); } sendobj.DataLen = (byte)(8); byte[] aData = csCanCommFunction.MakeCanData(mode, flag, dcp, ref aParam, ref aCalib); for (int i = 0; i < 8; i++) { sendobj.Data[i] = aData[i]; } return sendobj; } private UInt32 MakeTxData(int sId, int mode, int flag, int dcp, ref DeviceParamData aParam, ref DeviceCalibration aCalib, ref byte[] wData) { UInt32 result = 0; if (mode < 900) { result = MakeID(sId, mode); } else { result = MakeInvID(sId, mode); } wData = csCanCommFunction.MakeCanData(mode, flag, dcp, ref aParam, ref aCalib); return result; } #endregion #region COMM TX THREAD int current_cmd = 0; int RxSystemId = 0; public int GetCommSystemId() { return RxSystemId; } unsafe private void CanTxThread() { while (CanTxThreadEnd == false) { TCanTRxData tData = CanTxBuf.GetBuff(); if (tData != null) { SendCanPacket(tData, tData.type); //Thread.Sleep(1); } else { if ((CanPolling) && (AutoCanTx)) { int flag = 0; TxProcess(ref flag); RxSystemId = SystemId; SendData(SystemId, current_cmd, flag, dcp, ref SystemData.ParamData, ref SystemData.CalibriationData); } Thread.Sleep(50); } Thread.Sleep(1); } } int TxFlag = 0; void TxProcess(ref int aflag) { switch (TxFlag) { case 0: current_cmd = 900; TxFlag = 1; break; case 1: current_cmd = 901; TxFlag = 2; break; case 2: current_cmd = 902; TxFlag = 3; break; default: switch (current_cmd) { case 900: current_cmd = 901; break; // INV Manufacture Date case 901: current_cmd = 902; break; // INV pcb sn MSB case 902: current_cmd = 9; break; // INV pcb sn LSB case 9: current_cmd = 20; break; // Module Address get case 20: current_cmd = 1; break; case 1: current_cmd = 19; break; // Status case 19: current_cmd = 3; break; case 3: current_cmd = 4; break; case 4: current_cmd = 11; break; case 10: current_cmd = 11; break; case 11: current_cmd = 15; break; case 15: current_cmd = 16; break; case 16: current_cmd = 900; break; default: current_cmd = 900; break; } break; } } private unsafe void SendCanPacket(TCanTRxData CanTxData, int type) { VCI_CAN_OBJ sendobj = new VCI_CAN_OBJ(); sendobj.SendType = csCanConstData.SendType.Normal; sendobj.RemoteFlag = csCanConstData.FrameFormat.Data_Frame; sendobj.ExternFlag = csCanConstData.FrameType.Extended; sendobj.ID = CanTxData.exid; sendobj.DataLen = (byte)(8); for (int i = 0; i < 8; i++) { sendobj.Data[i] = CanTxData.data[i]; } Monitor.Enter(lockObject); try { if (VCI_Transmit(can_devtype, can_devind, can_canind, ref sendobj, 1) == 0) { throw new Exception("Failed to send"); } //OnPrint?.Invoke(this, csCanConstData.PacketToMsg(CanTxData.exid, CanTxData.data, 1, type)); } catch (Exception) { } finally { Monitor.Exit(lockObject); } } #endregion #region COMM RX THREAD unsafe private void CanRxThread() { DateTime aTime; while (CanRxThreadEnd == false) { UInt32 res = new UInt32(); aTime = DateTime.Now; res = VCI_GetReceiveNum(can_devtype, can_devind, can_canind); if (res > 0) { // Received data IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(VCI_CAN_OBJ)) * (Int32)res); res = VCI_Receive(can_devtype, can_devind, can_canind, pt, res, 100); for (UInt32 i = 0; i < res; i++) { VCI_CAN_OBJ obj = (VCI_CAN_OBJ)Marshal.PtrToStructure((IntPtr)((UInt32)pt + i * Marshal.SizeOf(typeof(VCI_CAN_OBJ))), typeof(VCI_CAN_OBJ)); OnPrint?.Invoke(this, csCanCommFunction.PacketToMsg(obj, 0)); UInt32 exID = obj.ID; byte[] adata = new byte[obj.DataLen]; for (int j = 0; j < obj.DataLen; j++) adata[j] = obj.Data[j]; PACKET_HEADER pHeader = csCanCommFunction.CovertPtoH(exID); switch (pHeader.Index) { case 0x01: // Inventory Data Packet if ((pHeader.SA == (200 + SystemId))) { RxSystemId = pHeader.SA - 200; csCanCommFunction.CanInvRxProcess(RxSystemId, exID, adata, ref SystemData, aTime); OnUpdate?.Invoke(this, ref SystemData); } break; case 0x04: // Firmware Update Packet OnDataRecv?.Invoke(exID, adata); break; case 0x06: // Normal Packet if (pHeader.SA == (200 + SystemId)) { RxSystemId = pHeader.SA - 200; csCanCommFunction.CanRxProcess(ref Config, RxSystemId, exID, adata, ref SystemData, aTime); OnUpdate?.Invoke(this, ref SystemData); } break; } } Marshal.FreeHGlobal(pt); Thread.Sleep(1); } else if (res == 0) { TimeSpan dTime = DateTime.Now - SystemData.LastRxTime; if (SystemData.ShelfCommFail == false) { if (dTime.TotalSeconds > 3) { SystemData.ShelfCommFail = true; DataInit(); } } OnUpdate?.Invoke(this, ref SystemData); Thread.Sleep(100); } } } #endregion #region DATA INIT private void DataInit() { DataFunction.DataInit(ref SystemData, ref Config); } #endregion } }