- Replace method-based `IsOpen()` checks with property-based `IsOpen` usage in `ISerialDriver` and related classes. - Simplify `Open()` and add `Close()` methods to unify connection management. - Introduce `InitializeThreeChannelState()` helper in `GenesisSmartReaderTest` for multi-channel state initialization. - Adjust calculations in `CalculateTimeByChannels()` and `CalculateVolumeByChannels()` for clarity. - Add new tests for multi-channel processing and validation of updated telegram handling logic.
317 lines
9.4 KiB
C#
317 lines
9.4 KiB
C#
using System;
|
|
using System.IO.Ports;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils
|
|
{
|
|
public class SerialDriver : IDisposable, ISerialDriver
|
|
{
|
|
readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(SerialDriver));
|
|
|
|
public string ErrorMessage { get; private set; }
|
|
|
|
private SerialPort _serialPort;
|
|
|
|
private readonly string _portName;
|
|
private readonly int _baudRate;
|
|
private readonly int _dataBits;
|
|
private readonly Parity _parity;
|
|
private readonly StopBits _stopBits;
|
|
private readonly Handshake _handshake;
|
|
private readonly int _readTimeout;
|
|
private readonly int _writeTimeout;
|
|
private readonly int _openTimeoutMs;
|
|
private readonly string _newLine;
|
|
private readonly Encoding _encoding;
|
|
private readonly bool _dtrEnable;
|
|
private readonly bool _rtsEnable;
|
|
private readonly bool _discardInBufferOnOpen;
|
|
private readonly bool _discardOutBufferOnOpen;
|
|
|
|
public SerialDriver()
|
|
{
|
|
_serialPort = new SerialPort();
|
|
}
|
|
|
|
internal SerialDriver(
|
|
string portName,
|
|
int baudRate,
|
|
int dataBits,
|
|
Parity parity,
|
|
StopBits stopBits,
|
|
Handshake handshake,
|
|
int readTimeout,
|
|
int writeTimeout,
|
|
int openTimeoutMs,
|
|
string newLine,
|
|
Encoding encoding,
|
|
bool dtrEnable,
|
|
bool rtsEnable,
|
|
bool discardInBufferOnOpen,
|
|
bool discardOutBufferOnOpen)
|
|
{
|
|
_portName = portName;
|
|
_baudRate = baudRate;
|
|
_dataBits = dataBits;
|
|
_parity = parity;
|
|
_stopBits = stopBits;
|
|
_handshake = handshake;
|
|
_readTimeout = readTimeout;
|
|
_writeTimeout = writeTimeout;
|
|
_openTimeoutMs = openTimeoutMs;
|
|
_newLine = newLine;
|
|
_encoding = encoding;
|
|
_dtrEnable = dtrEnable;
|
|
_rtsEnable = rtsEnable;
|
|
_discardInBufferOnOpen = discardInBufferOnOpen;
|
|
_discardOutBufferOnOpen = discardOutBufferOnOpen;
|
|
}
|
|
|
|
public void Open()
|
|
{
|
|
OpenConnection(
|
|
_portName,
|
|
_baudRate,
|
|
_dataBits,
|
|
_parity,
|
|
_stopBits,
|
|
_handshake,
|
|
_readTimeout,
|
|
_writeTimeout,
|
|
_openTimeoutMs,
|
|
_newLine,
|
|
_encoding,
|
|
_dtrEnable,
|
|
_rtsEnable,
|
|
_discardInBufferOnOpen,
|
|
_discardOutBufferOnOpen
|
|
);
|
|
}
|
|
|
|
public bool OpenConnection(
|
|
string comPort,
|
|
int baudrate,
|
|
int dataBits,
|
|
Parity parity,
|
|
StopBits stopbits,
|
|
int readTimeout = 1000,
|
|
int writeTimeout = 1000)
|
|
{
|
|
return OpenConnection(
|
|
comPort,
|
|
baudrate,
|
|
dataBits,
|
|
parity,
|
|
stopbits,
|
|
Handshake.None,
|
|
readTimeout,
|
|
writeTimeout,
|
|
5000,
|
|
"\n",
|
|
Encoding.ASCII,
|
|
true,
|
|
true,
|
|
true,
|
|
true
|
|
);
|
|
}
|
|
|
|
public bool OpenConnection(
|
|
string comPort,
|
|
int baudrate,
|
|
int dataBits,
|
|
Parity parity,
|
|
StopBits stopbits,
|
|
Handshake handshake,
|
|
int readTimeout,
|
|
int writeTimeout,
|
|
int openTimeoutMs,
|
|
string newLine,
|
|
Encoding encoding,
|
|
bool dtrEnable,
|
|
bool rtsEnable,
|
|
bool discardInBufferOnOpen,
|
|
bool discardOutBufferOnOpen)
|
|
{
|
|
lock (this)
|
|
{
|
|
CloseConnection();
|
|
|
|
try
|
|
{
|
|
ErrorMessage = string.Empty;
|
|
|
|
_serialPort = new SerialPort(comPort, baudrate, parity, dataBits, stopbits)
|
|
{
|
|
Handshake = handshake,
|
|
NewLine = newLine,
|
|
Encoding = encoding,
|
|
ReadTimeout = readTimeout,
|
|
WriteTimeout = writeTimeout,
|
|
DtrEnable = dtrEnable,
|
|
RtsEnable = rtsEnable
|
|
};
|
|
|
|
var openTask = Task.Run(() => _serialPort.Open());
|
|
|
|
if (!openTask.Wait(openTimeoutMs))
|
|
{
|
|
_serialPort.Dispose();
|
|
_serialPort = null;
|
|
|
|
ErrorMessage = $"COM error: Opening serial port {comPort} timed out after {openTimeoutMs} ms.";
|
|
return false;
|
|
}
|
|
|
|
if (!_serialPort.IsOpen)
|
|
{
|
|
ErrorMessage = $"COM error: Can't open {comPort}.";
|
|
return false;
|
|
}
|
|
|
|
if (discardInBufferOnOpen)
|
|
_serialPort.DiscardInBuffer();
|
|
|
|
if (discardOutBufferOnOpen)
|
|
_serialPort.DiscardOutBuffer();
|
|
|
|
log.Debug("SerialDriver opened successfully for port: " + comPort);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ErrorMessage = $"COM error: Open failed {comPort}. {ex.Message}";
|
|
CloseConnection();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
CloseConnection();
|
|
}
|
|
|
|
public void CloseConnection()
|
|
{
|
|
if (_serialPort != null)
|
|
{
|
|
if (_serialPort.IsOpen)
|
|
_serialPort.Close();
|
|
|
|
_serialPort.Dispose();
|
|
_serialPort = null;
|
|
}
|
|
}
|
|
|
|
public bool IsOpen => _serialPort?.IsOpen == true;
|
|
|
|
public void DiscardInBuffer()
|
|
{
|
|
if (_serialPort != null && _serialPort.IsOpen)
|
|
_serialPort.DiscardInBuffer();
|
|
}
|
|
|
|
public void DiscardOutBuffer()
|
|
{
|
|
if (_serialPort != null && _serialPort.IsOpen)
|
|
_serialPort.DiscardOutBuffer();
|
|
}
|
|
|
|
public string ReadExisting()
|
|
{
|
|
if (!IsOpen)
|
|
throw new InvalidOperationException("Serial port not open");
|
|
|
|
return _serialPort.ReadExisting();
|
|
}
|
|
|
|
public Encoding Encoding => _serialPort?.Encoding ?? _encoding;
|
|
|
|
public int BytesToRead => (_serialPort != null && _serialPort.IsOpen) ? _serialPort.BytesToRead : 0;
|
|
|
|
public int BytesToWrite => (_serialPort != null && _serialPort.IsOpen) ? _serialPort.BytesToWrite : 0;
|
|
|
|
public string ReadLine()
|
|
{
|
|
if (!IsOpen)
|
|
throw new InvalidOperationException("Serial port not open");
|
|
|
|
return _serialPort.ReadLine();
|
|
}
|
|
|
|
public string ReadLine(int timeoutMs)
|
|
{
|
|
if (!IsOpen)
|
|
throw new InvalidOperationException("Serial port not open");
|
|
|
|
int originalTimeout = _serialPort.ReadTimeout;
|
|
|
|
try
|
|
{
|
|
_serialPort.ReadTimeout = timeoutMs;
|
|
return _serialPort.ReadLine();
|
|
}
|
|
finally
|
|
{
|
|
_serialPort.ReadTimeout = originalTimeout;
|
|
}
|
|
}
|
|
|
|
public bool SendMessage(byte[] sendDataBytes, int length, int readTimeout = 1000, int writeTimeout = 1000)
|
|
{
|
|
if (!IsOpen) return false;
|
|
if (sendDataBytes == null || sendDataBytes.Length == 0) return true;
|
|
|
|
try
|
|
{
|
|
_serialPort.WriteTimeout = writeTimeout;
|
|
_serialPort.ReadTimeout = readTimeout;
|
|
_serialPort.Write(sendDataBytes, 0, length);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ErrorMessage = $"COM error: Transmit failed {_serialPort.PortName}. {ex.Message}";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public byte[] SendAndWait(byte[] data, int timeoutMs)
|
|
{
|
|
if (!IsOpen)
|
|
throw new InvalidOperationException("Serial port not open");
|
|
|
|
try
|
|
{
|
|
_serialPort.Write(data, 0, data.Length);
|
|
string line = ReadLine(timeoutMs);
|
|
return Encoding.GetBytes(line);
|
|
}
|
|
catch (TimeoutException)
|
|
{
|
|
ErrorMessage = "COM error: response timeout";
|
|
return null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ErrorMessage = $"COM error: SendAndWait failed {_serialPort.PortName}. {ex.Message}";
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
CloseConnection();
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
if (_serialPort == null)
|
|
return "SerialDriver: <null> (opened status:false)";
|
|
|
|
return "SerialDriver: " + _serialPort.PortName + " (opened status:" + _serialPort.IsOpen + ")";
|
|
}
|
|
}
|
|
} |