초기 커밋.
This commit is contained in:
706
LFP_Manager/Threads/CsRs232Thread124050.cs
Normal file
706
LFP_Manager/Threads/CsRs232Thread124050.cs
Normal file
@@ -0,0 +1,706 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.IO.Ports;
|
||||
using System.Windows.Forms;
|
||||
using LFP_Manager.DataStructure;
|
||||
using LFP_Manager.Function;
|
||||
using LFP_Manager.Utils;
|
||||
|
||||
namespace LFP_Manager.Threads
|
||||
{
|
||||
public sealed class CsRs232Thread124050 : IDisposable
|
||||
{
|
||||
#region ENUMS
|
||||
|
||||
private enum CommandType
|
||||
{
|
||||
NoCommand = 0,
|
||||
WriteParam = 1,
|
||||
WriteCoil = 2,
|
||||
WriteRegister = 3,
|
||||
ReadRegister = 4
|
||||
}
|
||||
|
||||
private enum AddressChannel
|
||||
{
|
||||
Channel0 = 0,
|
||||
Channel1 = 1,
|
||||
Channel2 = 2,
|
||||
Channel3 = 3
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CONSTANTS
|
||||
|
||||
private static class Constants
|
||||
{
|
||||
public const int BUFFER_SIZE = 512;
|
||||
public const int DEFAULT_BAUD_RATE = 115200;
|
||||
public const int MAX_TIMEOUT_COUNT = 5;
|
||||
public const int DEFAULT_DATA_BITS = 8;
|
||||
public const int POLLING_INTERVAL = 100;
|
||||
public const int ERROR_RETRY_DELAY = 1000;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region PRIVATE FIELDS
|
||||
|
||||
private readonly object _lockObject = new object();
|
||||
private readonly CommConfig _config;
|
||||
private DeviceSystemData _systemData;
|
||||
private readonly CancellationTokenSource _cancellationTokenSource;
|
||||
private volatile bool _isRunning;
|
||||
private volatile bool _isDisposed;
|
||||
|
||||
private SerialPort _serialPort;
|
||||
private Thread _commThread;
|
||||
|
||||
private readonly byte[] _receiveBuffer;
|
||||
private readonly byte[] _readBuffer;
|
||||
private int _bufferStart;
|
||||
private int _bufferEnd;
|
||||
private ushort _readPosition;
|
||||
private bool _bufferStarted;
|
||||
|
||||
private readonly TUartTxBuff _uartTxBuff;
|
||||
|
||||
private int _systemId;
|
||||
private bool _uartPolling;
|
||||
private bool _autoUartTx;
|
||||
private AddressChannel _currentChannel;
|
||||
|
||||
private ushort _requestRegAddr;
|
||||
private ushort _requestRegLen;
|
||||
private bool _responseFlag;
|
||||
private int _responseType;
|
||||
private CommandType _currentCommand;
|
||||
private int _timeoutCount;
|
||||
|
||||
private ushort _writeRegAddr;
|
||||
private short _writeCoilRegData;
|
||||
private byte[] _writeRegData;
|
||||
|
||||
#endregion
|
||||
|
||||
#region EVENTS
|
||||
|
||||
public event UartDataUpdate OnUpdate;
|
||||
public event UartDataRecv OnDataRecv;
|
||||
public event UartDataPrint OnPrint;
|
||||
|
||||
#endregion
|
||||
|
||||
#region CONSTRUCTORS
|
||||
|
||||
public CsRs232Thread124050(int systemId, CommConfig config, ref DeviceSystemData systemData)
|
||||
{
|
||||
if (config == null)
|
||||
throw new ArgumentNullException(nameof(config));
|
||||
|
||||
_systemId = systemId;
|
||||
_config = config;
|
||||
_systemData = systemData;
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
_receiveBuffer = new byte[Constants.BUFFER_SIZE];
|
||||
_readBuffer = new byte[Constants.BUFFER_SIZE];
|
||||
_uartTxBuff = new TUartTxBuff();
|
||||
_autoUartTx = true;
|
||||
|
||||
InitializeSerialPort();
|
||||
}
|
||||
|
||||
private void InitializeSerialPort()
|
||||
{
|
||||
try
|
||||
{
|
||||
_serialPort = new SerialPort();
|
||||
_serialPort.BaudRate = Constants.DEFAULT_BAUD_RATE;
|
||||
_serialPort.DataBits = Constants.DEFAULT_DATA_BITS;
|
||||
_serialPort.StopBits = StopBits.One;
|
||||
_serialPort.Parity = Parity.None;
|
||||
_serialPort.ReadTimeout = 500;
|
||||
_serialPort.WriteTimeout = 500;
|
||||
|
||||
_serialPort.DataReceived += SerialPort_DataReceived;
|
||||
_serialPort.ErrorReceived += SerialPort_ErrorReceived;
|
||||
_serialPort.PinChanged += SerialPort_PinChanged;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (OnPrint != null)
|
||||
{
|
||||
OnPrint(this, string.Format("Failed to initialize serial port: {0}", ex.Message));
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region PUBLIC METHODS
|
||||
|
||||
public bool Start(ref CommConfig config, int systemId, bool polling)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_isRunning)
|
||||
return false;
|
||||
|
||||
_systemId = systemId;
|
||||
_uartPolling = polling;
|
||||
|
||||
try
|
||||
{
|
||||
if (OpenPort(config.UartPort))
|
||||
{
|
||||
_isRunning = true;
|
||||
_commThread = new Thread(CommThreadProcess);
|
||||
_commThread.IsBackground = true;
|
||||
_commThread.Name = "UART_Communication_Thread";
|
||||
_commThread.Priority = ThreadPriority.AboveNormal;
|
||||
_commThread.Start();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (OnPrint != null)
|
||||
{
|
||||
OnPrint(this, string.Format("Failed to start UART thread: {0}", ex.Message));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (_isDisposed)
|
||||
return;
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
_isRunning = false;
|
||||
if (_cancellationTokenSource != null)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
}
|
||||
ClosePort();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
return;
|
||||
|
||||
Stop();
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_cancellationTokenSource != null)
|
||||
{
|
||||
_cancellationTokenSource.Dispose();
|
||||
}
|
||||
if (_serialPort != null)
|
||||
{
|
||||
_serialPort.Dispose();
|
||||
}
|
||||
_isDisposed = true;
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public void SetPolling(bool flag, int systemId, ref DeviceSystemData systemData)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
_systemId = systemId;
|
||||
_uartPolling = flag;
|
||||
_systemData = systemData;
|
||||
_systemData.mNo = systemId;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAutoTx(bool autoTx)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
_autoUartTx = autoTx;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetWriteCoilReg(ushort writeAddr, short writeData)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
_currentCommand = CommandType.WriteCoil;
|
||||
_writeRegAddr = writeAddr;
|
||||
_writeCoilRegData = writeData;
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetReadReg(ushort readAddr, ushort readLen, bool replyFlag)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
_currentCommand = CommandType.ReadRegister;
|
||||
_requestRegAddr = readAddr;
|
||||
_requestRegLen = readLen;
|
||||
_responseFlag = replyFlag;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetWriteReg(ushort writeAddr, short[] writeData)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (writeData == null)
|
||||
throw new ArgumentNullException(nameof(writeData));
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
_currentCommand = CommandType.WriteParam;
|
||||
_writeRegAddr = writeAddr;
|
||||
|
||||
TUartTRxData uartTRxData = new TUartTRxData();
|
||||
uartTRxData.type = (int)CommandType.WriteParam;
|
||||
uartTRxData.data = csSerialCommFunction.MakeWriteRegisterData((ushort)_systemId, writeAddr, writeData);
|
||||
uartTRxData.length = uartTRxData.data.Length;
|
||||
|
||||
if (_uartTxBuff != null)
|
||||
{
|
||||
_uartTxBuff.PutBuff(uartTRxData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region PRIVATE METHODS
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
if (_isDisposed)
|
||||
throw new ObjectDisposedException(GetType().FullName);
|
||||
}
|
||||
|
||||
private bool OpenPort(string portName)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(portName))
|
||||
throw new ArgumentException("Port name cannot be null or empty", "portName");
|
||||
|
||||
if (_serialPort.IsOpen)
|
||||
_serialPort.Close();
|
||||
|
||||
_serialPort.PortName = portName;
|
||||
_serialPort.Open();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (OnPrint != null)
|
||||
{
|
||||
OnPrint(this, string.Format("Failed to open port {0}: {1}", portName, ex.Message));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClosePort()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_serialPort != null && _serialPort.IsOpen)
|
||||
_serialPort.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (OnPrint != null)
|
||||
{
|
||||
OnPrint(this, string.Format("Error closing port: {0}", ex.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_serialPort.IsOpen) return;
|
||||
|
||||
int bytesToRead = _serialPort.BytesToRead;
|
||||
if (bytesToRead == 0) return;
|
||||
|
||||
byte[] buffer = new byte[bytesToRead];
|
||||
int bytesRead = _serialPort.Read(buffer, 0, bytesToRead);
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
for (int i = 0; i < bytesRead; i++)
|
||||
{
|
||||
AddToBuffer(buffer[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (OnPrint != null)
|
||||
{
|
||||
OnPrint(this, string.Format("Error in data receive: {0}", ex.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SerialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
|
||||
{
|
||||
if (OnPrint != null)
|
||||
{
|
||||
OnPrint(this, string.Format("Serial port error: {0}", e.EventType));
|
||||
}
|
||||
}
|
||||
|
||||
private void SerialPort_PinChanged(object sender, SerialPinChangedEventArgs e)
|
||||
{
|
||||
if (OnPrint != null)
|
||||
{
|
||||
OnPrint(this, string.Format("Serial pin changed: {0}", e.EventType));
|
||||
}
|
||||
}
|
||||
|
||||
private void AddToBuffer(byte data)
|
||||
{
|
||||
_receiveBuffer[_bufferStart++] = data;
|
||||
_bufferStart %= Constants.BUFFER_SIZE;
|
||||
|
||||
if (_bufferStart == _bufferEnd)
|
||||
{
|
||||
_bufferEnd = (_bufferEnd + 1) % Constants.BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
private int GetFromBuffer()
|
||||
{
|
||||
if (_bufferStart == _bufferEnd)
|
||||
return 0;
|
||||
|
||||
int result = 0x0100 + _receiveBuffer[_bufferEnd++];
|
||||
_bufferEnd %= Constants.BUFFER_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
private void FlushBuffer()
|
||||
{
|
||||
_bufferStart = _bufferEnd = 0;
|
||||
}
|
||||
|
||||
private void ProcessReceivedData()
|
||||
{
|
||||
if (_readPosition >= Constants.BUFFER_SIZE)
|
||||
{
|
||||
ResetReceiveState();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_bufferStarted)
|
||||
{
|
||||
int data = GetFromBuffer();
|
||||
if ((data & 0xFF) == _systemId || (data & 0xFF) == 0x7F)
|
||||
{
|
||||
_readPosition = 0;
|
||||
_readBuffer[_readPosition++] = (byte)(data & 0xFF);
|
||||
_bufferStarted = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int receivedByte = GetFromBuffer();
|
||||
if ((receivedByte & 0x0100) == 0)
|
||||
return;
|
||||
|
||||
_readBuffer[_readPosition++] = (byte)(receivedByte & 0xFF);
|
||||
|
||||
ProcessModbusResponse();
|
||||
}
|
||||
|
||||
private void ProcessModbusResponse()
|
||||
{
|
||||
int result = csSerialCommFunction.ModbusPacketFromSlaveCheck(_readBuffer, _readPosition);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case 0: // Need more data
|
||||
return;
|
||||
|
||||
case 1: // Valid packet
|
||||
if (OnPrint != null)
|
||||
{
|
||||
OnPrint(this, csLog.trx_data_print(_readBuffer, _readPosition, 1));
|
||||
}
|
||||
_timeoutCount = 0;
|
||||
ProcessValidResponse();
|
||||
break;
|
||||
|
||||
case 2: // Firmware update packet
|
||||
if (OnPrint != null)
|
||||
{
|
||||
OnPrint(this, csLog.trx_data_print(_readBuffer, _readPosition, 1));
|
||||
}
|
||||
_timeoutCount = 0;
|
||||
ProcessFirmwareResponse();
|
||||
break;
|
||||
|
||||
case -1: // Error packet
|
||||
ResetReceiveState();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessValidResponse()
|
||||
{
|
||||
try
|
||||
{
|
||||
int moduleId = _readBuffer[0];
|
||||
if (moduleId > 0) moduleId--;
|
||||
|
||||
_systemData.CommFail = false;
|
||||
_systemData.ShelfCommFail = false;
|
||||
|
||||
if (_responseType == 0)
|
||||
{
|
||||
short[] resultCode = csSerialCommFunction124050.SerialRxProcess(_readBuffer, _requestRegAddr, _readPosition, ref _systemData);
|
||||
if (OnUpdate != null)
|
||||
{
|
||||
OnUpdate(this, ref _systemData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_responseType = 0;
|
||||
if (OnDataRecv != null)
|
||||
{
|
||||
byte[] responseData = new byte[_readPosition];
|
||||
Array.Copy(_readBuffer, responseData, _readPosition);
|
||||
OnDataRecv(responseData);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (OnPrint != null)
|
||||
{
|
||||
OnPrint(this, string.Format("Error processing valid response: {0}", ex.Message));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ResetReceiveState();
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessFirmwareResponse()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (OnDataRecv != null)
|
||||
{
|
||||
byte[] responseData = new byte[_readPosition];
|
||||
Array.Copy(_readBuffer, responseData, _readPosition);
|
||||
OnDataRecv(responseData);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (OnPrint != null)
|
||||
{
|
||||
OnPrint(this, string.Format("Error processing firmware response: {0}", ex.Message));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ResetReceiveState();
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetReceiveState()
|
||||
{
|
||||
_bufferStarted = false;
|
||||
_readPosition = 0;
|
||||
_responseFlag = false;
|
||||
}
|
||||
|
||||
private void CommThreadProcess()
|
||||
{
|
||||
while (_isRunning && !_cancellationTokenSource.Token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessCommunication();
|
||||
Thread.Sleep(Constants.POLLING_INTERVAL);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (OnPrint != null)
|
||||
{
|
||||
OnPrint(this, string.Format("Communication thread error: {0}", ex.Message));
|
||||
}
|
||||
Thread.Sleep(Constants.ERROR_RETRY_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessCommunication()
|
||||
{
|
||||
if (_serialPort == null || !_serialPort.IsOpen)
|
||||
return;
|
||||
|
||||
bool writeMode = false;
|
||||
byte[] txData = PrepareTransmitData(ref writeMode);
|
||||
|
||||
if (txData != null)
|
||||
{
|
||||
SendData(txData);
|
||||
if (_responseFlag)
|
||||
{
|
||||
ProcessReceivedData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] PrepareTransmitData(ref bool writeMode)
|
||||
{
|
||||
byte[] txData = null;
|
||||
_responseType = 0;
|
||||
|
||||
if (_uartTxBuff != null && _uartTxBuff.CheckBuff())
|
||||
{
|
||||
TUartTRxData txBuffer = _uartTxBuff.GetBuff();
|
||||
if (txBuffer != null)
|
||||
{
|
||||
txData = txBuffer.data;
|
||||
writeMode = true;
|
||||
_responseFlag = true;
|
||||
_responseType = txBuffer.type;
|
||||
}
|
||||
}
|
||||
else if (_currentCommand == CommandType.WriteCoil)
|
||||
{
|
||||
txData = csSerialCommFunction.MakeWriteCoilData((ushort)_systemId, _writeRegAddr, _writeCoilRegData);
|
||||
_currentCommand = CommandType.NoCommand;
|
||||
_responseFlag = true;
|
||||
}
|
||||
else if (_currentCommand == CommandType.ReadRegister)
|
||||
{
|
||||
txData = csSerialCommFunction.MakeReadRegisterData((ushort)_systemId, csSerialCommFunction.READ_HOLDING_REG, _requestRegAddr, _requestRegLen);
|
||||
_currentCommand = CommandType.NoCommand;
|
||||
_responseFlag = true;
|
||||
}
|
||||
else if (_uartPolling && _autoUartTx)
|
||||
{
|
||||
txData = PreparePollingData();
|
||||
}
|
||||
|
||||
return txData;
|
||||
}
|
||||
|
||||
private byte[] PreparePollingData()
|
||||
{
|
||||
ushort command;
|
||||
|
||||
switch (_currentChannel)
|
||||
{
|
||||
case AddressChannel.Channel0:
|
||||
_currentChannel = AddressChannel.Channel1;
|
||||
_requestRegAddr = 0x0000;
|
||||
_requestRegLen = 0x0040;
|
||||
command = csSerialCommFunction.READ_HOLDING_REG;
|
||||
_responseFlag = true;
|
||||
break;
|
||||
|
||||
case AddressChannel.Channel1:
|
||||
_currentChannel = AddressChannel.Channel2;
|
||||
_requestRegAddr = 0x0040;
|
||||
_requestRegLen = 0x0040;
|
||||
command = csSerialCommFunction.READ_HOLDING_REG;
|
||||
_responseFlag = true;
|
||||
break;
|
||||
|
||||
case AddressChannel.Channel2:
|
||||
_currentChannel = AddressChannel.Channel3;
|
||||
_requestRegAddr = 0x0080;
|
||||
_requestRegLen = 0x0040;
|
||||
command = csSerialCommFunction.READ_HOLDING_REG;
|
||||
_responseFlag = true;
|
||||
break;
|
||||
|
||||
case AddressChannel.Channel3:
|
||||
if (_config.ModuleQty > 1)
|
||||
{
|
||||
_systemId++;
|
||||
if (_systemId > _config.ModuleQty)
|
||||
{
|
||||
_systemId = 1;
|
||||
}
|
||||
}
|
||||
_currentChannel = AddressChannel.Channel0;
|
||||
command = csSerialCommFunction.NO_CMD;
|
||||
_responseFlag = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
_currentChannel = AddressChannel.Channel0;
|
||||
_requestRegAddr = 0x0000;
|
||||
_requestRegLen = 0x0040;
|
||||
command = csSerialCommFunction.READ_HOLDING_REG;
|
||||
break;
|
||||
}
|
||||
|
||||
return command == csSerialCommFunction.NO_CMD ? null :
|
||||
csSerialCommFunction.MakeReadRegisterData((ushort)_systemId, command, _requestRegAddr, _requestRegLen);
|
||||
}
|
||||
|
||||
private void SendData(byte[] data)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (data == null)
|
||||
throw new ArgumentNullException("data");
|
||||
|
||||
FlushBuffer();
|
||||
_serialPort.Write(data, 0, data.Length);
|
||||
if (OnPrint != null)
|
||||
{
|
||||
OnPrint(this, csLog.trx_data_print(data, data.Length, 0));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (OnPrint != null)
|
||||
{
|
||||
OnPrint(this, string.Format("Send data error: {0}", ex.Message));
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user