using DevExpress.XtraGauges.Core.Model; using LFP_Manager.DataStructure; using LFP_Manager.Function; using LFP_Manager.Utils; using System; using System.IO.Ports; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace LFP_Manager.Threads { class CsRs485Thread124050 { #region VARIABLES private readonly CommConfig Config; private DeviceSystemData[] SystemData; private Task serialCommTask = null; private CancellationTokenSource _cts; private SerialPort sPort = null; private volatile bool SerialPortThreadEnd = true; public int ModuleID = 1; 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 volatile bool rFlag = false; private int wFlag = 0; private int rFlag2 = 0; private ushort ExtReqRegAddr = 0x0000; private ushort WriteRegAddr = 0x0000; //Byul Init 0x0000 private short WriteCoilRegData = 0; private byte[] WriteRegData; private short WriteParamRegData; private TUartTxBuff UartTxBuff; public event UartDataUpdateRS485 OnUpdate = null; public event UartDataRecvRS485 OnDataRecv = null; public event UartDataPrintRS485 OnPrint = null; #endregion #region CONSTRUCTORS public CsRs485Thread124050(int mID, CommConfig aConfig, ref DeviceSystemData[] aSystemData) { ModuleID = mID; Config = aConfig; SystemData = aSystemData; UartTxBuff = new TUartTxBuff(); _cts = new CancellationTokenSource(); } public void disposeThread() { try { Stop(); } catch { } Close(); _cts?.Dispose(); _cts = null; } public bool Start(ref CommConfig aConfig, int mID, bool aPolling) { bool result = false; ModuleID = mID; UartPolling = aPolling; // Port open with configured baudrate (fallback to 9600) int baud = (aConfig?.UartBaudrate > 0) ? aConfig.UartBaudrate : 9600; if (Open(aConfig.UartPort, baud)) { SerialPortThreadEnd = false; // recreate CTS for this run _cts?.Dispose(); _cts = new CancellationTokenSource(); serialCommTask = Task.Run(() => uartCommThread(_cts.Token), _cts.Token); result = true; } return result; } public void Stop() { try { SerialPortThreadEnd = true; if (_cts != null && !_cts.IsCancellationRequested) _cts.Cancel(); serialCommTask?.Wait(TimeSpan.FromSeconds(5)); } catch (AggregateException) { } catch (OperationCanceledException) { } catch { } finally { Close(); } } #endregion #region COMMPORT CONTROLS private readonly object _rxLock = new object(); private readonly object _portLock = new object(); private void sDataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { try { if (sPort == null || !sPort.IsOpen) return; byte[] sRead = new byte[1024]; int rLen = sPort.Read(sRead, 0, sRead.Length); lock (_rxLock) { for (int i = 0; i < rLen; i++) { PutBuff(sRead[i]); } } } catch (Exception ex) { // 무시하지 말고 로그 출력 OnPrint?.Invoke(this, $"DataRecv Error: {ex.Message}"); } } private void sErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e) { OnPrint?.Invoke(this, $"Serial Error: {e.EventType}"); } private void sPinChanged(object sender, System.IO.Ports.SerialPinChangedEventArgs e) { // } private bool Open(string cPort, int cBaudrate) { lock (_portLock) { try { if (sPort != null) { if (sPort.IsOpen) sPort.Close(); sPort.Dispose(); sPort = null; } sPort = new SerialPort { PortName = cPort, BaudRate = cBaudrate, DataBits = 8, Parity = Parity.None, StopBits = StopBits.One, ReadTimeout = 500, WriteTimeout = 500 }; sPort.DataReceived += sDataReceived; sPort.ErrorReceived += sErrorReceived; sPort.PinChanged += sPinChanged; sPort.Open(); return sPort.IsOpen; } catch (Exception ex) { OnPrint?.Invoke(this, $"Error Open - {ex.Message}"); if (sPort != null) { try { sPort.Dispose(); } catch { } sPort = null; } return false; } } } private void Close() { lock (_portLock) { try { if (sPort != null) { try { sPort.DataReceived -= sDataReceived; sPort.ErrorReceived -= sErrorReceived; sPort.PinChanged -= sPinChanged; } catch { } if (sPort.IsOpen) sPort.Close(); sPort.Dispose(); sPort = null; } } catch (Exception ex) { OnPrint?.Invoke(this, $"Port Close Fail: {ex.Message}"); } } } #endregion #region PUBLIC FUNCTION public void SetPolling(bool flag, int mID, ref DeviceSystemData[] aSystemData) { ModuleID = mID; UartPolling = flag; SystemData = aSystemData; if (SystemData != null && ModuleID - 1 >= 0 && ModuleID - 1 < SystemData.Length) SystemData[ModuleID - 1].mNo = ModuleID; } public void SetAutoTx(bool autoTx) { AutoUartTx = autoTx; } public void SetWriteReg(ushort WriteAddr, byte[] WriteData, bool ReplyFlag, int type) { WriteRegAddr = WriteAddr; var uartTRxData = new TUartTRxData { type = type, data = WriteData, length = WriteData?.Length ?? 0 }; UartTxBuff?.PutBuff(uartTRxData); } public void SetParam(ushort WriteAddr, short WriteData) { byte[] sData = CsRs485CommFunction124050.MakeWriteRegisterData((ushort)ModuleID, WriteAddr, WriteData); if (sData != null) { var aData = new TUartTRxData { length = sData.Length, data = sData }; UartTxBuff.PutBuff(aData); } } public void SetReadWriteParam(ushort WriteAddr, short WriteData) { byte[] sData = CsRs485CommFunction124050.MakeReadWriteRegisterData((ushort)ModuleID, WriteAddr, WriteData); if (sData != null) { var aData = new TUartTRxData { length = sData.Length, data = sData }; ExtReqRegAddr = WriteAddr; UartTxBuff.PutBuff(aData); } } #endregion #region RX BUFFERING private const int BUFFER_SIZE = 512; private readonly byte[] rBuffer = new byte[BUFFER_SIZE]; private int rBufStart = 0; private int rBufEnd = 0; private void PutBuff(byte c) { // called from DataReceived event -> lock to be safe lock (_rxLock) { rBuffer[rBufStart++] = c; rBufStart %= BUFFER_SIZE; if (rBufStart == rBufEnd) { rBufEnd++; rBufEnd %= BUFFER_SIZE; } } } private int GetBuff() { lock (_rxLock) { int result = -1; if (rBufStart != rBufEnd) { result = 0x0100 + rBuffer[rBufEnd++]; rBufEnd %= BUFFER_SIZE; } return result; } } private void FlushBuff() { lock (_rxLock) { rBufStart = rBufEnd = 0; } } #endregion #region TX BUFFERING private byte[] MakeTxDataDelta(bool wData) { byte[] sData = null; if ((UartTxBuff != null) && (UartTxBuff.CheckBuff())) { TUartTRxData sBuff = UartTxBuff.GetBuff(); if (sBuff != null) { sData = sBuff.data; wData = true; rFlag = true; rFlag2 = sBuff.type; RequestRegAddr = ExtReqRegAddr; } } else { if (AutoUartTx && UartPolling) { ushort sCmd; switch (addr_ch) { case 0: // Battery Status Data addr_ch = 1; RequestRegAddr = 0x0FFF; //Byul Init 0x0000 RequestRegLen = 0x4C; sCmd = CsSerialCommFunctionDelta.READ_INPUT_REG; // Command 0x04 rFlag = true; break; case 1: // Gyroscope Data addr_ch = 2; RequestRegAddr = 0x4000; RequestRegLen = 7; sCmd = CsSerialCommFunctionDelta.READ_INPUT_REG; // Command 0x17 rFlag = true; break; case 2: addr_ch = 3; RequestRegAddr = 0x3000; RequestRegLen = 3; sCmd = CsSerialCommFunctionDelta.READ_INPUT_REG; // Command 0x19 rFlag = true; break; case 3: addr_ch = 4; RequestRegAddr = 100; RequestRegLen = 28; sCmd = CsSerialCommFunctionDelta.READ_HOLDING_REG; // Command 0x03 rFlag = true; break; case 4: if (Config.ModuleQty > 1) { ModuleID++; if (ModuleID > Config.ModuleQty) { ModuleID = 1; } } addr_ch = 0; sCmd = CsSerialCommFunctionDelta.NO_CMD; rFlag = false; break; default: addr_ch = 0; RequestRegAddr = 0x0FFF; RequestRegLen = 27; sCmd = CsSerialCommFunctionDelta.READ_INPUT_REG; rFlag = true; break; } if (sCmd == CsSerialCommFunctionDelta.NO_CMD) { sData = null; } else if (sCmd == CsSerialCommFunctionDelta.READ_DEV_ID) { sData = CsSerialCommFunctionDelta.MakeReadDevIdRegReqData((ushort)ModuleID, sCmd, RequestRegAddr); } else { sData = CsSerialCommFunctionDelta.MakeReadRegisterData((ushort)ModuleID, sCmd, RequestRegAddr, RequestRegLen); } } } return sData; } #endregion #region COMM THREAD private readonly byte[] ReadBuf = new byte[BUFFER_SIZE]; ushort rPosition = 0; bool BuffStart = false; private void RecvPacketInit() { BuffStart = false; rPosition = 0; } private void uartCommThread(CancellationToken token) { try { int RecvTimeout = (Config != null) ? Config.RecvWaitTime : 1500; int getData = 0; byte cData = 0; int[] TimeOutCount = new int[csConstData.SystemInfo.MAX_MODULE_SIZE]; StartSend: while (!token.IsCancellationRequested && SerialPortThreadEnd == false) { if ((sPort == null) || (sPort.IsOpen == false)) { Thread.Sleep(100); continue; } FlushBuff(); byte[] txData = MakeTxDataDelta(false); if (sPort == null) return; if (txData != null) { try { sPort.Write(txData, 0, txData.Length); OnPrint?.Invoke(this, csLog.trx_data_print(txData, txData.Length, 0)); } catch (Exception ex) { OnPrint?.Invoke(this, $"Write error: {ex.Message}"); } } if (rFlag == true) { DateTime rDateTime = DateTime.Now; RecvPacketInit(); while (!token.IsCancellationRequested) { DateTime nDateTime = rDateTime.AddMilliseconds(RecvTimeout); if (nDateTime < DateTime.Now) break; getData = GetBuff(); if ((getData & 0x0100) != 0x0000) { rDateTime = DateTime.Now; cData = (byte)(getData & 0x00FF); if (rPosition >= BUFFER_SIZE) RecvPacketInit(); if (!BuffStart) { if ((cData == (byte)ModuleID) || (cData == (byte)0x7F)) { rPosition = 0; ReadBuf[rPosition++] = cData; BuffStart = true; } } else { ReadBuf[rPosition++] = cData; int chk = CsRs485CommFunction124050.ModbusPacketFromSlaveCheck(ReadBuf, rPosition); switch (chk) { case 0: // Need more data break; case 1: // Packet OK, no error OnPrint?.Invoke(this, csLog.trx_data_print(ReadBuf, rPosition, 1)); TimeOutCount[ModuleID - 1] = 0; int mID = ReadBuf[0]; if (mID > 0) mID--; SystemData[mID].CommFail = false; SystemData[mID].ShelfCommFail = false; short[] result_code = CsRs485CommFunction124050.SerialRxProcess(ReadBuf, RequestRegAddr, rPosition, ref SystemData[mID]); OnUpdate?.Invoke(this, ref SystemData); Thread.Sleep(5); goto StartSend; case 2: // Fw Update Packet OK OnPrint?.Invoke(this, csLog.trx_data_print(ReadBuf, rPosition, 1)); TimeOutCount[ModuleID - 1] = 0; rFlag = false; if (OnDataRecv != null) { var adata = new byte[rPosition]; Array.Copy(ReadBuf, 0, adata, 0, rPosition); OnDataRecv(adata); } goto StartSend; case -1: // Packet error RecvPacketInit(); Thread.Sleep(100); goto StartSend; default: break; } } Thread.Sleep(1); } else { Thread.Sleep(1); } } if (rPosition > 0) { OnPrint?.Invoke(this, csLog.trx_data_print(ReadBuf, rPosition, 1)); Thread.Sleep(1); } else { TimeOutCount[ModuleID - 1]++; if (TimeOutCount[ModuleID - 1] >= 10) { TimeOutCount[ModuleID - 1] = 10; if (SystemData[ModuleID - 1].ShelfCommFail == false) { csUtils.DataInit(Config, ref SystemData[ModuleID - 1]); SystemData[ModuleID - 1].ShelfCommFail = true; } } Thread.Sleep(100); } OnUpdate?.Invoke(this, ref SystemData); Thread.Sleep(1); } else { Thread.Sleep(10); } /* if (rFlag == true) */ rPosition = 0; Thread.Sleep(100); } } catch (OperationCanceledException) { } catch (Exception ex) { OnPrint?.Invoke(this, $"Comm thread error: {ex.Message}"); } finally { SerialPortThreadEnd = true; } } #endregion } }