309 lines
9.7 KiB
C#
309 lines
9.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO.Ports;
|
|
using System.Threading;
|
|
using FluentNHibernate.Conventions;
|
|
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.hexLogger;
|
|
|
|
namespace TBF.Rig.TestMethods.iPerlCommunication.communication.Utils
|
|
{
|
|
public class SerialDriver : IDisposable, ISerialDriver
|
|
{
|
|
readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(SerialDriver));
|
|
public string ErrorMessage { get; private set; }
|
|
private List<byte> SerialPortReadBuffer = new List<byte>();
|
|
|
|
private SerialPort _serialPort;
|
|
private readonly List<byte> _binMessages = new List<byte>();
|
|
private bool _isReading;
|
|
|
|
// Stored configuration (used by Builder)
|
|
private readonly string _portName;
|
|
private readonly int _baudRate;
|
|
private readonly int _dataBits;
|
|
private readonly Parity _parity;
|
|
private readonly StopBits _stopBits;
|
|
private readonly int _readTimeout;
|
|
private readonly int _writeTimeout;
|
|
|
|
private readonly ManualResetEvent _responseReceived = new ManualResetEvent(false);
|
|
|
|
#region Constructors
|
|
|
|
// Default constructor (legacy support)
|
|
public SerialDriver()
|
|
{
|
|
_serialPort = new SerialPort();
|
|
}
|
|
|
|
// Builder constructor
|
|
internal SerialDriver(
|
|
string portName,
|
|
int baudRate,
|
|
int dataBits,
|
|
Parity parity,
|
|
StopBits stopBits,
|
|
int readTimeout,
|
|
int writeTimeout)
|
|
{
|
|
_portName = portName;
|
|
_baudRate = baudRate;
|
|
_dataBits = dataBits;
|
|
_parity = parity;
|
|
_stopBits = stopBits;
|
|
_readTimeout = readTimeout;
|
|
_writeTimeout = writeTimeout;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Open / Close
|
|
|
|
// Builder-based open
|
|
public bool Open()
|
|
{
|
|
return OpenConnection(
|
|
_portName,
|
|
_baudRate,
|
|
_dataBits,
|
|
_parity,
|
|
_stopBits,
|
|
_readTimeout,
|
|
_writeTimeout
|
|
);
|
|
}
|
|
|
|
// Legacy API (unchanged)
|
|
public bool OpenConnection(
|
|
string comPort,
|
|
int baudrate,
|
|
int dataBits,
|
|
Parity parity,
|
|
StopBits stopbits,
|
|
int readTimeout = 1000,
|
|
int writeTimeout = 1000)
|
|
{
|
|
lock (this)
|
|
{
|
|
CloseConnection();
|
|
|
|
try
|
|
{
|
|
ErrorMessage = string.Empty;
|
|
|
|
_serialPort = new SerialPort(comPort, baudrate, parity, dataBits, stopbits)
|
|
{
|
|
ReadTimeout = readTimeout,
|
|
WriteTimeout = writeTimeout
|
|
};
|
|
|
|
_serialPort.DataReceived += DataReceivedHandler;
|
|
_serialPort.Open();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ErrorMessage = $"COM error: Open failed {comPort}. {ex.Message}";
|
|
return false;
|
|
}
|
|
|
|
if (!_serialPort.IsOpen)
|
|
{
|
|
ErrorMessage = $"COM error: Can't open {comPort}.";
|
|
return false;
|
|
}
|
|
|
|
log.Debug("SerialDriver opened successfully for port: " + comPort);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void CloseConnection()
|
|
{
|
|
if (_serialPort != null)
|
|
{
|
|
_serialPort.DataReceived -= DataReceivedHandler;
|
|
if (_serialPort.IsOpen)
|
|
_serialPort.Close();
|
|
|
|
_serialPort.Dispose();
|
|
_serialPort = null;
|
|
}
|
|
}
|
|
|
|
public bool IsOpen() => _serialPort?.IsOpen == true;
|
|
|
|
#endregion
|
|
|
|
#region Send / Receive
|
|
|
|
public bool SendMessage(byte[] sendDataBytes, int length, int readTimeout = 1000, int writeTimeout = 1000)
|
|
{
|
|
if (!IsOpen()) return false;
|
|
if (sendDataBytes.Length == 0) return true;
|
|
|
|
try
|
|
{
|
|
PrepareReading();
|
|
|
|
_serialPort.WriteTimeout = writeTimeout;
|
|
_serialPort.ReadTimeout = readTimeout;
|
|
_serialPort.Write(sendDataBytes, 0, length);
|
|
|
|
_isReading = true;
|
|
|
|
var stopwatch = Stopwatch.StartNew();
|
|
while (_isReading)
|
|
{
|
|
if (stopwatch.ElapsedMilliseconds > readTimeout)
|
|
{
|
|
ErrorMessage = "COM error: Receive timeout";
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ErrorMessage = $"COM error: Transmit failed {_serialPort.PortName}. {ex.Message}";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void PrepareReading()
|
|
{
|
|
_serialPort.DiscardInBuffer();
|
|
_binMessages.Clear();
|
|
_responseReceived.Reset();
|
|
SerialPortReadBuffer.Clear();
|
|
_isReading = true;
|
|
}
|
|
|
|
public byte[] GetRawData()
|
|
{
|
|
return _binMessages.ToArray();
|
|
}
|
|
|
|
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
|
|
{
|
|
lock (this)
|
|
{
|
|
if (_serialPort == null || !_serialPort.IsOpen) return;
|
|
|
|
try
|
|
{
|
|
//Thread.Sleep(5);
|
|
|
|
if (!SerialPortReadBuffer.IsEmpty())
|
|
{
|
|
SerialPortReadBuffer.Clear();
|
|
}
|
|
|
|
int iWordCounter = 0;
|
|
bool isStart = false;
|
|
bool isQuestion = false;
|
|
int iLength = 0;
|
|
while (true)//_serialPort.BytesToRead > 0
|
|
{
|
|
byte readByte = (byte)_serialPort.ReadByte();
|
|
|
|
//I have START
|
|
if (readByte == C4.IperlHatProtocol.IperlHatProtocolConstants.Start)
|
|
{
|
|
iWordCounter++;
|
|
isStart = true;
|
|
}
|
|
// I have QUESTION
|
|
if (readByte == C4.IperlHatProtocol.IperlHatProtocolConstants.Question)
|
|
{
|
|
iWordCounter++;
|
|
isQuestion = true;
|
|
}
|
|
//I count length from start
|
|
if (iWordCounter > 0)
|
|
iWordCounter++;
|
|
|
|
if (iWordCounter > 0)
|
|
{
|
|
//Store byte to data
|
|
SerialPortReadBuffer.Add(readByte);
|
|
}
|
|
// we have length
|
|
if (iLength == 0 && isStart && SerialPortReadBuffer.Count > 2 )
|
|
{
|
|
iLength = (int)SerialPortReadBuffer[2];
|
|
}
|
|
|
|
//If we have enough bytes
|
|
if (isStart && iLength > 0
|
|
&& (SerialPortReadBuffer.Count >= iLength ||
|
|
readByte == C4.IperlHatProtocol.IperlHatProtocolConstants.End
|
|
)
|
|
)
|
|
{
|
|
break;
|
|
}
|
|
//if we read END
|
|
if (isQuestion && readByte == C4.IperlHatProtocol.IperlHatProtocolConstants.End)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (SerialPortReadBuffer.Count > 0)
|
|
{
|
|
_binMessages.AddRange(SerialPortReadBuffer.ToArray());
|
|
_responseReceived.Set();
|
|
}
|
|
}
|
|
catch (TimeoutException te)
|
|
{
|
|
// Ignore shutdown race conditions
|
|
}
|
|
finally
|
|
{
|
|
_isReading = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public byte[] SendAndWait(byte[] data, int timeoutMs)
|
|
{
|
|
if (!IsOpen())
|
|
throw new InvalidOperationException("Serial port not open");
|
|
|
|
log.Debug("SendAndWait() - TX: " + HexFormatter.ToHex(data));
|
|
PrepareReading();
|
|
_serialPort.Write(data, 0, data.Length);
|
|
|
|
if (!_responseReceived.WaitOne(timeoutMs))
|
|
{
|
|
log.Error("SendAndWait() - Response timeout! Details: " +
|
|
" SerialPortReadBuffer: " + HexFormatter.ToHex(SerialPortReadBuffer.ToArray()) +
|
|
" _binMessages" + HexFormatter.ToHex(_binMessages.ToArray()) +
|
|
"_responseReceived: " + _responseReceived.WaitOne(0)
|
|
);
|
|
|
|
ErrorMessage = "COM error: response timeout";
|
|
return null;
|
|
}
|
|
|
|
return GetRawData();
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
public void Dispose()
|
|
{
|
|
CloseConnection();
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return "SerialDriver: " + _serialPort.PortName + " (opened status:" + _serialPort.IsOpen +")";
|
|
}
|
|
}
|
|
}
|