- Fix FW update freezing at first 128B by setting ReplyFlag=true in RequestFlashWrite and handling FW Update timeouts in csUartThread - Update Operating Status: remove Anti-Theft from Status (0x0004 -> TRIP) and display TRIP (Anti-Theft) in Alarm when errorCode is 0x4000 - Sync Status/Alarm string mapping in csLog for CSV data logging - Bump assembly version to v1.1.1.2
856 lines
37 KiB
C#
856 lines
37 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;
|
|
|
|
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);
|
|
}
|
|
|
|
/// <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
|
|
}
|
|
}
|