using System; using System.Collections.Concurrent; using System.IO.Ports; using System.Threading; using System.Windows.Forms; using LFP_Manager.DataStructure; using LFP_Manager.Function; namespace LFP_Manager.Threads { public delegate void UartDataUpdate(object sender, ref DeviceSystemData aSystemData); public delegate void UartDataRecv(byte[] data); public delegate void UartDataPrint(object sender, string msg); public delegate void UartPacketLogEvent(DateTime timestamp, bool isTx, byte[] packet, string description); class csUartThread { #region VARIABLES private readonly CommConfig Config; private DeviceSystemData SystemData; private Thread serialComm = null; private SerialPort sPort = null; private bool SerialPortThreadEnd = true; public int SystemId = 0; private bool UartPolling = false; private bool AutoUartTx = true; private int addr_ch = 0; // Thread-Safe TX Queue private readonly ConcurrentQueue txQueue = new ConcurrentQueue(); public event UartDataUpdate OnUpdate = null; public event UartDataRecv OnDataRecv = null; public event UartDataPrint OnPrint = null; public event UartPacketLogEvent OnPacketLog = null; #endregion #region CONSTRUCTORS public csUartThread(int sId, CommConfig aConfig, ref DeviceSystemData aSystemData) { SystemId = sId; Config = aConfig; SystemData = aSystemData; serialComm = new Thread(uartCommThread); } public void disposeThread() { if (sPort != null) { if (sPort.IsOpen) { sPort.Close(); } sPort = null; } SerialPortThreadEnd = true; if (serialComm != null) { if (serialComm.IsAlive) { serialComm.Abort(); } serialComm = null; } } public bool Start(ref CommConfig aConfig, int sId, bool aPolling) { bool result = false; SystemId = sId; UartPolling = aPolling; if (Open(aConfig.UartPort, 9600)) //Byul Init 9600 { SerialPortThreadEnd = false; serialComm.Start(); result = true; } return result; } public void Stop() { SerialPortThreadEnd = true; Close(); } #endregion #region COMMPORT CONTROLS private void sDataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { byte[] sRead = new byte[1024]; int rLen = 0; try { rLen = ((SerialPort)sender).Read(sRead, 0, 1024); for (int i = 0; i < rLen; i++) { PutBuff(sRead[i]); } } catch (Exception) { } } private void sErrorReceived(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e) { } 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) { if (sPort.IsOpen) { sPort.Close(); } sPort = null; } } #endregion #region PUBLIC TX FUNCTIONS (QUEUE BASED) public void SetPolling(bool flag, int sId, ref DeviceSystemData aSystemData) { SystemId = sId; UartPolling = flag; SystemData = aSystemData; SystemData.mNo = SystemId; } public void SetAutoTx(bool autoTx) { AutoUartTx = autoTx; } /// /// 공통 TX 큐 삽입 메서드 /// public void EnqueueTx(TUartTxItem item) { if (item != null) { txQueue.Enqueue(item); } } /// /// 원시 패킷 전송 (FW 업데이트 등) /// public void SendRawPacket(byte[] rawData, bool expectReply = true) { if (rawData == null || rawData.Length == 0) return; TUartTxItem item = new TUartTxItem(UartTxType.Raw, SystemId, 0, (ushort)rawData.Length, rawData, expectReply, Config.RecvWaitTime); EnqueueTx(item); } /// /// 코일 레지스터 쓰기 (TX 큐 삽입) /// public void SetWriteCoilReg(ushort WriteAddr, short WriteData) { byte[] data = csSerialCommFunction.MakeWriteCoilData((ushort)SystemId, WriteAddr, WriteData); TUartTxItem item = new TUartTxItem(UartTxType.WriteCoil, SystemId, WriteAddr, 1, data, true, Config.RecvWaitTime); EnqueueTx(item); } /// /// 레지스터 쓰기 (TX 큐 삽입) /// public void SetWriteReg(ushort WriteAddr, byte[] WriteData, bool ReplyFlag) { if (WriteData == null) return; UartTxType txType = UartTxType.WriteReg; int timeoutMs = Config.RecvWaitTime; bool expectReply = ReplyFlag; // FW 업데이트 패킷 감지 (Target ID: 0x7F 또는 FW 명령어) if (WriteData.Length >= 2 && WriteData[0] == 0x7F) { txType = UartTxType.FwUpdate; byte cmd = WriteData[1]; if (cmd == csSerialCommFunction.FW_FLASH_ERASE_CMD) // 0x43 Erase { expectReply = true; timeoutMs = Math.Max(Config.RecvWaitTime, 5000); // Flash Erase 대기시간 (섹터 지우기 최대 수 초 소요) } else if (cmd == csSerialCommFunction.FW_FLASH_WRITE_CMD) // 0x31 Write { expectReply = true; timeoutMs = Math.Max(Config.RecvWaitTime, 2000); // Flash Write 128B 응답 대기 } } TUartTxItem item = new TUartTxItem(txType, SystemId, WriteAddr, (ushort)WriteData.Length, WriteData, expectReply, timeoutMs); EnqueueTx(item); } /// /// 레지스터 읽기 (TX 큐 삽입) /// public void SetReadReg(ushort ReadAddr, ushort ReadLen, bool ReplyFlag) { byte[] data = csSerialCommFunction.MakeReadRegisterData((ushort)SystemId, csSerialCommFunction.READ_HOLDING_REG, ReadAddr, ReadLen); TUartTxItem item = new TUartTxItem(UartTxType.ReadReg, SystemId, ReadAddr, ReadLen, data, ReplyFlag, Config.RecvWaitTime); EnqueueTx(item); } /// /// 파라미터 레지스터 쓰기 (TX 큐 삽입) /// public void SetParam(ushort WriteAddr, short[] WriteData) { if (WriteData == null) return; byte[] data = csSerialCommFunction.MakeWriteRegisterData((ushort)SystemId, WriteAddr, WriteData); TUartTxItem item = new TUartTxItem(UartTxType.WriteParam, SystemId, WriteAddr, (ushort)WriteData.Length, data, true, Config.RecvWaitTime); EnqueueTx(item); } public void SendProcessFromApp(int sId, int mode, int index, int flag, ref DeviceParamData aParam, ref 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.PCB_HIGH_TEMPERATURE: // Discharge Over Current Parameter data = new short[1]; data[0] = (short)(aParam.PcbHighTempWarning + 50); SetParam((ushort)(mode + 0), data); data[0] = (short)(aParam.PcbHighTempTrip + 50); SetParam((ushort)(mode + 1), data); data[0] = (short)(aParam.PcbHighTempRelease + 50); SetParam((ushort)(mode + 2), 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; try { 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.PCB_HIGH_TEMPERATURE; data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Warn_PCB_OT + 50); SetParam((ushort)(i + 0), data); data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Protect_PCB_OT + 50); SetParam((ushort)(i + 1), data); data[0] = (short)(csConstData.UART_PARAM_DEFAULT.Release_PCB_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); } catch (Exception) { } 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; data[0] = (short)aCalib.AntiTheft.GyroScope; SetParam((ushort)(i + 1), data); break; case 19: data = new short[1]; i = csConstData.UART_CALIB_ADDR_LIST.ANTI_THEFT; data[0] = (short)aCalib.AntiTheft.GyroScope; SetParam((ushort)(i + 1), data); break; default: break; } } #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) { rBuffer[rBufStart++] = c; rBufStart %= BUFFER_SIZE; if (rBufStart == rBufEnd) { rBufEnd++; rBufEnd %= BUFFER_SIZE; } } private int GetBuff() { int result = 0; if (rBufStart != rBufEnd) { result = 0x0100 + rBuffer[rBufEnd++]; rBufEnd %= BUFFER_SIZE; } return result; } private void FlushBuff() { rBufStart = rBufEnd = 0; } #endregion #region POLLING GENERATOR /// /// 다음 주기 Polling 패킷 아이템 생성 /// private TUartTxItem CreateNextPollingItem() { ushort reqAddr = 0x0000; ushort reqLen = 50; ushort sCmd = csSerialCommFunction.READ_HOLDING_REG; switch (addr_ch) { case 0: addr_ch = 1; reqAddr = 0x0000; reqLen = 0x0031; // 49 sCmd = csSerialCommFunction.READ_HOLDING_REG; break; case 1: addr_ch = 4; reqAddr = 58; reqLen = 139 - 58; // 81 (Registers 58 ~ 138) sCmd = csSerialCommFunction.READ_HOLDING_REG; break; case 2: addr_ch = 3; reqAddr = 0x3078; reqLen = 8; sCmd = csSerialCommFunction.READ_COIL_STATUS; break; case 3: addr_ch = 4; reqAddr = 0x502E; reqLen = 1; sCmd = csSerialCommFunction.READ_COIL_STATUS; break; case 4: if (Config.ModuleQty > 1) { SystemId++; if (SystemId > Config.ModuleQty) { SystemId = 1; } } addr_ch = 0; sCmd = csSerialCommFunction.NO_CMD; break; default: addr_ch = 0; reqAddr = 0x0000; reqLen = 100; sCmd = csSerialCommFunction.READ_HOLDING_REG; break; } if (sCmd == csSerialCommFunction.NO_CMD) { return null; } byte[] sData = csSerialCommFunction.MakeReadRegisterData((ushort)SystemId, sCmd, reqAddr, reqLen); if (sData == null || sData.Length == 0) { return null; } return new TUartTxItem(UartTxType.Polling, SystemId, reqAddr, reqLen, sData, true, Config.RecvWaitTime); } #endregion #region COMM THREAD (QUEUE BASED PIPELINE) private readonly byte[] ReadBuf = new byte[BUFFER_SIZE]; private ushort rPosition = 0; private bool BuffStart = false; private void RecvPacketInit() { BuffStart = false; rPosition = 0; } private void uartCommThread() { int getData = 0; byte cData = 0; int TimeOutCount = 0; while (SerialPortThreadEnd == false) { if ((sPort == null) || (sPort.IsOpen == false)) { Thread.Sleep(50); continue; } // 1. 큐가 비어있고 Polling이 활성화되어 있을 때 다음 Polling 패킷을 TX 큐에 삽입 if (txQueue.IsEmpty && UartPolling && AutoUartTx) { TUartTxItem pollItem = CreateNextPollingItem(); if (pollItem != null) { txQueue.Enqueue(pollItem); } else { Thread.Sleep(10); continue; } } // 2. TX 큐에서 패킷 1개 꺼내기 if (!txQueue.TryDequeue(out TUartTxItem currentTx) || currentTx == null || currentTx.Data == null || currentTx.Data.Length == 0) { Thread.Sleep(20); continue; } // 3. SerialPort로 패킷 전송 try { FlushBuff(); sPort.Write(currentTx.Data, 0, currentTx.Data.Length); OnPacketLog?.Invoke(DateTime.Now, true, currentTx.Data, GetTxDescription(currentTx)); } catch (Exception) { Thread.Sleep(50); continue; } // 4. 응답 대기 및 수신 처리 if (currentTx.ExpectReply) { int rTimeOut = currentTx.TimeoutMs > 0 ? currentTx.TimeoutMs : Config.RecvWaitTime; RecvPacketInit(); bool packetHandled = false; while (rTimeOut > 0 && SerialPortThreadEnd == false) { if (((getData = GetBuff()) & 0x0100) != 0x0000) { rTimeOut = currentTx.TimeoutMs > 0 ? currentTx.TimeoutMs : Config.RecvWaitTime; cData = (byte)(getData & 0x00FF); if (rPosition >= BUFFER_SIZE) RecvPacketInit(); if (BuffStart == false) { if ((cData == (byte)currentTx.SystemId) || (cData == (byte)0x7F) || (cData == (byte)SystemId)) { rPosition = 0; ReadBuf[rPosition++] = cData; BuffStart = true; } } else { ReadBuf[rPosition++] = cData; int checkResult = csSerialCommFunction.ModbusPacketFromSlaveCheck(ReadBuf, rPosition); if (checkResult == 0) { // Need more data } else if (checkResult == 1) { // Packet OK TimeOutCount = 0; SystemData.CommFail = false; SystemData.ShelfCommFail = false; byte[] rxPacket = new byte[rPosition]; Array.Copy(ReadBuf, rxPacket, rPosition); OnPacketLog?.Invoke(DateTime.Now, false, rxPacket, GetRxDescription(rxPacket, currentTx)); if (currentTx.TxType == UartTxType.Polling) { csSerialCommFunction.SerialRxProcess(ReadBuf, currentTx.RegAddr, rPosition, ref SystemData); OnUpdate?.Invoke(this, ref SystemData); } else { if (OnDataRecv != null) { byte[] adata = new byte[rPosition + 1]; for (int j = 0; j < adata.Length; j++) { adata[j] = ReadBuf[j]; } OnDataRecv(adata); } } packetHandled = true; Thread.Sleep(5); break; } else if (checkResult == 2) { // Fw Update Packet OK TimeOutCount = 0; byte[] rxPacket = new byte[rPosition]; Array.Copy(ReadBuf, rxPacket, rPosition); OnPacketLog?.Invoke(DateTime.Now, false, rxPacket, "Fw Update Response OK"); if (OnDataRecv != null) { byte[] adata = new byte[rPosition + 1]; for (int j = 0; j < adata.Length; j++) { adata[j] = ReadBuf[j]; } OnDataRecv(adata); } packetHandled = true; Thread.Sleep(5); break; } else if (checkResult == -1) { // Packet Error byte[] rxPacket = new byte[rPosition]; Array.Copy(ReadBuf, rxPacket, rPosition); OnPacketLog?.Invoke(DateTime.Now, false, rxPacket, "Packet CRC/Format Error"); RecvPacketInit(); Thread.Sleep(20); packetHandled = true; break; } } } else { Thread.Sleep(1); rTimeOut--; } } // 타임아웃 처리 if (!packetHandled) { TimeOutCount++; if (TimeOutCount >= 5) { TimeOutCount = 5; if (SystemData.ShelfCommFail == false) { SystemData.ShelfCommFail = true; DataInit(); } } OnUpdate?.Invoke(this, ref SystemData); Thread.Sleep(50); } rPosition = 0; } else { // No reply expected Thread.Sleep(10); } } } #endregion #region DATA INIT private void DataInit() { SystemData.ShelfCommFail = true; SystemData.cellQty = 15; SystemData.tempQty = 4; for (int j = 0; j < csConstData.SystemInfo.MAX_MODULE_CELL_SIZE; j++) { SystemData.ValueData.CellVoltage[j] = 0; } for (int j = 0; j < csConstData.SystemInfo.MAX_MODULE_TEMP_SIZE; j++) { SystemData.ValueData.CellTemperature[j] = 0; } SystemData.ValueData.voltageOfPack = 0; SystemData.ValueData.current = 0; SystemData.ValueData.rSOC = 0; SystemData.AvgData.maxCellVoltage = 0; SystemData.AvgData.minCellVoltage = 0; SystemData.AvgData.avgCellVoltage = 0; SystemData.AvgData.maxTemp = 0; SystemData.AvgData.minTemp = 0; SystemData.AvgData.avgTemp = 0; SystemData.heatbeat = 0; SystemData.StatusData.warning = 0; SystemData.StatusData.protection = 0; SystemData.StatusData.errorCode = 0; for (int j = 0; j < SystemData.Information.BMS_SN.Length; j++) { SystemData.Information.BMS_SN[j] = 0; } for (int j = 0; j < SystemData.Information.Module_SN.Length; j++) { SystemData.Information.Module_SN[j] = 0; } SystemData.CalibriationData.Battery.Capacity = 0; SystemData.CalibriationData.Current.ChaAndDchSelect = 0; SystemData.CalibriationData.Current.ChgCalibration_K = 0; SystemData.CalibriationData.Current.DchCalibration_K = 0; } #endregion #region PACKET LOG HELPERS private string GetTxDescription(TUartTxItem item) { if (item == null) return "Unknown TX"; switch (item.TxType) { case UartTxType.Polling: return string.Format("Polling (ID:{0}, Addr:0x{1:X4}, Len:{2})", item.SystemId, item.RegAddr, item.RegLen); case UartTxType.WriteParam: return string.Format("Write Param (ID:{0}, Addr:0x{1:X4})", item.SystemId, item.RegAddr); case UartTxType.WriteCoil: return string.Format("Write Coil (ID:{0}, Addr:0x{1:X4})", item.SystemId, item.RegAddr); case UartTxType.WriteReg: return string.Format("Write Reg (ID:{0}, Addr:0x{1:X4}, Len:{2})", item.SystemId, item.RegAddr, item.RegLen); case UartTxType.ReadReg: return string.Format("Read Reg (ID:{0}, Addr:0x{1:X4}, Len:{2})", item.SystemId, item.RegAddr, item.RegLen); case UartTxType.FwUpdate: return string.Format("FW Update (ID:{0}, Len:{1})", item.SystemId, item.Data != null ? item.Data.Length : 0); default: return string.Format("RAW TX (ID:{0}, Len:{1})", item.SystemId, item.Data != null ? item.Data.Length : 0); } } private string GetRxDescription(byte[] rxPacket, TUartTxItem item) { if (rxPacket == null || rxPacket.Length < 3) return "Unknown RX"; byte devId = rxPacket[0]; byte funcCode = rxPacket[1]; if (funcCode == 0x03) { byte byteCount = rxPacket[2]; if (item != null && item.TxType == UartTxType.Polling) { return string.Format("Polling Response (ID:{0}, Bytes:{1})", devId, byteCount); } return string.Format("Read Holding Reg Response (ID:{0}, Bytes:{1})", devId, byteCount); } else if (funcCode == 0x05) { return string.Format("Write Coil Response (ID:{0})", devId); } else if (funcCode == 0x06 || funcCode == 0x10) { return string.Format("Write Reg Response (ID:{0})", devId); } else if (funcCode > 0x80) { byte errCode = rxPacket.Length > 2 ? rxPacket[2] : (byte)0; return string.Format("Modbus Error Response (ID:{0}, Func:0x{1:X2}, ErrCode:0x{2:X2})", devId, funcCode, errCode); } return string.Format("RX Data (ID:{0}, Func:0x{1:X2}, Len:{2})", devId, funcCode, rxPacket.Length); } #endregion } }