600 lines
20 KiB
C#
600 lines
20 KiB
C#
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)
|
|
{
|
|
SystemData[ModuleID - 1].ShelfCommFail = true;
|
|
csUtils.DataInit(Config, ref SystemData[ModuleID - 1]);
|
|
}
|
|
}
|
|
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
|
|
}
|
|
}
|