I've been working with the SerialPort
class for a while, trying to figure out the best way to work with it, and especially adding support for Async-Await in C#. I also started before 4.5, so I didn't have access to the BaseStream
member (so no BaseStream.ReadAsync()
). I wanted to get some opinions on it before I started relying on it too heavily. Performance considerations and stability are high priority, but also figuring out if I'm reinventing the wheel.
The code is designed to wrap around a SerialPort
to provide Async-Await operations. It does this by intercepting calls to Write and the SerialPort.DataRecieved
events and relaying the data to a pair of Reactive Subjects that client code can subscribe to. Note this code requires the nuget package Rx-Main.
using System.IO.Ports;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
namespace Core.IO {
public sealed class ComPort : IDisposable, INotifyPropertyChanged{
private Subject<byte[]> _txDataSubject;
private Subject<byte[]> _rxDataSubject;
private SerialPort _port;
public String PortName {
get {
return _port.PortName;
}
set {
_port.PortName = value;
RaisePropertyChanged("PortName");
RaisePropertyChanged("IsOpen");
}
}
public Boolean IsOpen {
get {
return _port.IsOpen;
}
}
public IObservable<byte[]> DataTx {
get {
return _txDataSubject.AsObservable();
}
}
public IObservable<byte[]> DataRx {
get {
return _rxDataSubject.AsObservable();
}
}
public ComPort() {
_port = new SerialPort();
_txDataSubject = new Subject<byte[]>();
_rxDataSubject = new Subject<byte[]>();
_port.DataReceived+=(sender,args)=>{
#if DEBUG
Console.WriteLine("({0}) RX on thread {1}",PortName, System.Threading.Thread.CurrentThread.ManagedThreadId);
#endif
_rxDataSubject.OnNext(_port.Encoding.GetBytes(_port.ReadExisting()));
};
}
public void Open() {
_port.Open();
RaisePropertyChanged("IsOpen");
}
public void Close() {
_port.Close();
RaisePropertyChanged("IsOpen");
}
private void RaisePropertyChanged(string propertyName) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public Boolean IsDisposed {
get;
private set;
}
public void Write(string data) {
_txDataSubject.OnNext(_port.Encoding.GetBytes(data));
_port.Write(data);
}
private void Dispose(bool isDisposing) {
if (IsDisposed) return;
if (isDisposing) {
_port.Dispose();
_txDataSubject.Dispose();
}
IsDisposed = true;
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
~ComPort() {
Dispose(false);
}
}
}