1
0
Files
moniteur-baie/SerialCom/BatteryController.cs
2023-06-21 19:44:29 +02:00

195 lines
4.5 KiB
C#

using System.IO.Ports;
using MoniteurBaie.DataModels;
using MoniteurBaie.Utils;
namespace MoniteurBaie.SerialCom;
public sealed class BatteryController : IBatteryController
{
private readonly ILogger _logger;
private readonly SerialPort _serialPort;
private readonly bool _closePort;
private readonly List<IObserver<BatteryControllerPacket>> _packetObservers = new();
private readonly BlockingListener<string> _commandListener = new();
public BatteryController(ILogger logger, Action<SerialPort> configure)
{
_logger = logger;
_serialPort = new SerialPort();
configure(_serialPort);
_closePort = true;
}
public BatteryController(ILogger logger, SerialPort serialPort, bool closePort = false)
{
_logger = logger;
_serialPort = serialPort;
_closePort = closePort;
}
public Task Open(CancellationToken cancellationToken)
{
_serialPort.Open();
var readThread = new Thread(DoRead)
{
IsBackground = true
};
readThread.Start();
var writeThread = new Thread(DoWrite)
{
IsBackground = true
};
writeThread.Start();
return Task.CompletedTask;
}
public IDisposable AddSerialObserver(IObserver<BatteryControllerPacket> observer)
{
_packetObservers.Add(observer);
return new Disposer(() => _packetObservers.Remove(observer));
}
public Task SendCommand(string command, CancellationToken cancellationToken)
{
_commandListener.Push(command);
return Task.CompletedTask;
}
private void DoRead()
{
try
{
while (_serialPort.IsOpen)
{
string line;
try
{
line = _serialPort.ReadLine();
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to read line from serial connection.");
continue;
}
if (string.IsNullOrEmpty(line))
{
continue;
}
var packet = new BatteryControllerPacket(line);
foreach (var observer in _packetObservers)
{
try
{
observer.OnNext(packet);
}
catch (Exception ex)
{
_logger.LogError(ex, "An exception occurred in a packet handler.");
}
}
}
}
catch (ObjectDisposedException) { }
catch (Exception ex)
{
_logger.LogError(ex, "An exception occurred in the serial read loop.");
throw;
}
}
private void DoWrite()
{
try
{
while (_serialPort.IsOpen)
{
var line = _commandListener.Next();
if (string.IsNullOrEmpty(line))
{
continue;
}
try
{
_serialPort.Write(line + "\n");
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to send command.");
}
}
}
catch (ObjectDisposedException) { }
catch (Exception ex)
{
_logger.LogError(ex, "An exception occurred in the serial write loop.");
throw;
}
}
#region IObservable
IDisposable IObservable<BatteryControllerPacket>.Subscribe(IObserver<BatteryControllerPacket> observer)
{
return AddSerialObserver(observer);
}
#endregion
#region IObserver
void IObserver<string>.OnCompleted()
{
}
void IObserver<string>.OnError(Exception error)
{
}
void IObserver<string>.OnNext(string value)
{
SendCommand(value, default);
}
#endregion
#region IDisposable
private bool disposedValue;
private void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
if (_closePort)
{
_serialPort.Close();
}
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
#endregion
}