Files
LFP_Manager_DBG/LFP_Manager/Threads/csUartThread.cs

834 lines
36 KiB
C#

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<TUartTxItem> txQueue = new ConcurrentQueue<TUartTxItem>();
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;
}
/// <summary>
/// 공통 TX 큐 삽입 메서드
/// </summary>
public void EnqueueTx(TUartTxItem item)
{
if (item != null)
{
txQueue.Enqueue(item);
}
}
/// <summary>
/// 원시 패킷 전송 (FW 업데이트 등)
/// </summary>
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);
}
/// <summary>
/// 코일 레지스터 쓰기 (TX 큐 삽입)
/// </summary>
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);
}
/// <summary>
/// 레지스터 쓰기 (TX 큐 삽입)
/// </summary>
public void SetWriteReg(ushort WriteAddr, byte[] WriteData, bool ReplyFlag)
{
if (WriteData == null) return;
TUartTxItem item = new TUartTxItem(UartTxType.WriteReg, SystemId, WriteAddr, (ushort)WriteData.Length, WriteData, ReplyFlag, Config.RecvWaitTime);
EnqueueTx(item);
}
/// <summary>
/// 레지스터 읽기 (TX 큐 삽입)
/// </summary>
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);
}
/// <summary>
/// 파라미터 레지스터 쓰기 (TX 큐 삽입)
/// </summary>
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
/// <summary>
/// 다음 주기 Polling 패킷 아이템 생성
/// </summary>
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
}
}