tbf/TBF/Rig/RegisterReaders/GenesisRegReader/communication/Utils/SerialDriver.cs
Michal Buzik a5a68ec457 Refactor Genesis serial communication handling (ISerialDriver interface compatible with SerialPort):
- 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.
2026-03-24 10:04:30 +01:00

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 + ")";
}
}
}