Skip to content

Commit

Permalink
finally fixed issue #1 (almost rewrote all code :))
Browse files Browse the repository at this point in the history
  • Loading branch information
genemars committed Oct 29, 2015
1 parent 4ec238d commit 9973feb
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 124 deletions.
230 changes: 108 additions & 122 deletions SerialPortLib/SerialPort.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ You should have received a copy of the GNU General Public License
using System.Diagnostics;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;

using NLog;

Expand All @@ -46,14 +45,14 @@ public class SerialPortInput

// Read/Write error state variable
private bool gotReadWriteError = true;
private bool isConnected = false;

// Serial port reader task
private Task readerTask;
private CancellationTokenSource readerTokenSource;
private Thread reader;
// Serial port connection watcher
private Task connectionWatcher;
private CancellationTokenSource watcherTokenSource;
private Thread connectionWatcher;

private object accessLock = new object();
private bool disconnectRequested = false;

#endregion

Expand Down Expand Up @@ -86,36 +85,37 @@ public class SerialPortInput
/// </summary>
public bool Connect()
{
Disconnect();
bool returnValue = Open();
gotReadWriteError = !returnValue;
watcherTokenSource = new CancellationTokenSource();
connectionWatcher = Task.Factory.StartNew(() => ConnectionWatcherTask(watcherTokenSource.Token), watcherTokenSource.Token);
return returnValue;
if (disconnectRequested)
return false;
lock (accessLock)
{
Disconnect();
Open();
connectionWatcher = new Thread(new ThreadStart(ConnectionWatcherTask));
connectionWatcher.Start();
}
return IsConnected;
}

/// <summary>
/// Disconnect the serial port.
/// </summary>
public void Disconnect()
{
if (connectionWatcher != null)
if (disconnectRequested)
return;
disconnectRequested = true;
Close();
lock (accessLock)
{
watcherTokenSource.Cancel();
try
{
connectionWatcher.Wait(5000);
}
catch (AggregateException e)
if (connectionWatcher != null)
{
logger.Error(e);
if (!connectionWatcher.Join(5000))
connectionWatcher.Abort();
connectionWatcher = null;
}
if (connectionWatcher != null)
connectionWatcher.Dispose();
connectionWatcher = null;
watcherTokenSource = null;
disconnectRequested = false;
}
Close();
}

/// <summary>
Expand All @@ -124,7 +124,7 @@ public void Disconnect()
/// <value><c>true</c> if connected; otherwise, <c>false</c>.</value>
public bool IsConnected
{
get { return isConnected && !gotReadWriteError; }
get { return serialPort != null && !gotReadWriteError && !disconnectRequested; }
}

/// <summary>
Expand All @@ -134,9 +134,11 @@ public bool IsConnected
/// <param name="baudrate">Baudrate.</param>
public void SetPort(string portname, int baudrate = 115200)
{
if (portName != portname && serialPort != null)
if (portName != portname)
{
Close();
// set to erro so that the connection watcher will reconnect
// using the new port
gotReadWriteError = true;
}
portName = portname;
baudRate = baudrate;
Expand All @@ -150,15 +152,18 @@ public void SetPort(string portname, int baudrate = 115200)
public bool SendMessage(byte[] message)
{
bool success = false;
try
{
serialPort.Write(message, 0, message.Length);
success = true;
logger.Debug(BitConverter.ToString(message));
}
catch (Exception e)
if (IsConnected)
{
logger.Error(e);
try
{
serialPort.Write(message, 0, message.Length);
success = true;
logger.Debug(BitConverter.ToString(message));
}
catch (Exception e)
{
logger.Error(e);
}
}
return success;
}
Expand All @@ -171,80 +176,70 @@ public bool SendMessage(byte[] message)

private bool Open()
{
Close();
bool success = false;
try
lock (accessLock)
{
bool tryOpen = (serialPort == null);
if (Environment.OSVersion.Platform.ToString().StartsWith("Win") == false)
{
tryOpen = (tryOpen && System.IO.File.Exists(portName));
}
if (tryOpen)
Close();
try
{
serialPort = new SerialPort();
serialPort.PortName = portName;
serialPort.BaudRate = baudRate;
serialPort.ErrorReceived += HanldeErrorReceived;
// We are not using serialPort.DataReceived event for receiving data since this is not working under Linux/Mono.
// We use the readerTask instead (see below).
if (serialPort.IsOpen == false)
bool tryOpen = true;
if (Environment.OSVersion.Platform.ToString().StartsWith("Win") == false)
{
tryOpen = (tryOpen && System.IO.File.Exists(portName));
}
if (tryOpen)
{
serialPort = new SerialPort();
serialPort.ErrorReceived += HanldeErrorReceived;
serialPort.PortName = portName;
serialPort.BaudRate = baudRate;
// We are not using serialPort.DataReceived event for receiving data since this is not working under Linux/Mono.
// We use the readerTask instead (see below).
serialPort.Open();
success = true;
}
}
}
catch (Exception e)
{
logger.Error(e);
}
isConnected = success;
if (isConnected)
{
// Start the Reader task
readerTokenSource = new CancellationTokenSource();
readerTask = Task.Factory.StartNew(() => ReaderTask(readerTokenSource.Token), readerTokenSource.Token);
OnConnectionStatusChanged(new ConnectionStatusChangedEventArgs(isConnected));
catch (Exception e)
{
logger.Error(e);
Close();
}
if (serialPort != null && serialPort.IsOpen)
{
gotReadWriteError = false;
// Start the Reader task
reader = new Thread(ReaderTask);
reader.Start();
OnConnectionStatusChanged(new ConnectionStatusChangedEventArgs(true));
}
}
return success;
}

private void Close()
{
if (serialPort != null)
lock (accessLock)
{
try
// Stop the Reader task
if (reader != null)
{
serialPort.Close();
serialPort.ErrorReceived -= HanldeErrorReceived;
if (!reader.Join(5000))
reader.Abort();
reader = null;
}
catch (Exception e)
{
logger.Error(e);
}
serialPort = null;
if (isConnected)
OnConnectionStatusChanged(new ConnectionStatusChangedEventArgs(false));
}
// Stop the Reader task
if (readerTask != null)
{
readerTokenSource.Cancel();
try
{
readerTask.Wait(5000);
}
catch (AggregateException e)
if (serialPort != null)
{
logger.Error(e);
serialPort.ErrorReceived -= HanldeErrorReceived;
if (serialPort.IsOpen)
{
serialPort.Close();
OnConnectionStatusChanged(new ConnectionStatusChangedEventArgs(false));
}
serialPort.Dispose();
serialPort = null;
}
if (readerTask != null)
readerTask.Dispose();
readerTask = null;
readerTokenSource = null;
gotReadWriteError = true;
}
isConnected = false;
}

private void HanldeErrorReceived(object sender, SerialErrorReceivedEventArgs e)
Expand All @@ -256,56 +251,46 @@ private void HanldeErrorReceived(object sender, SerialErrorReceivedEventArgs e)

#region Background Tasks

private void ReaderTask(CancellationToken readerToken)
private void ReaderTask()
{
while (!readerToken.IsCancellationRequested)
while (IsConnected)
{
int msglen = 0;
//
if (serialPort != null)
try
{
try
msglen = serialPort.BytesToRead;
if (msglen > 0)
{
msglen = serialPort.BytesToRead;
if (msglen > 0)
{
byte[] message = new byte[msglen];
//
int readbytes = 0;
while (serialPort.Read(message, readbytes, msglen - readbytes) <= 0)
; // noop
if (MessageReceived != null)
{
OnMessageReceived(new MessageReceivedEventArgs(message));
// Prevent event listeners from blocking the receiver task
//new Thread(() => OnMessageReceived(new MessageReceivedEventArgs(message))).Start();
//ThreadPool.QueueUserWorkItem(new WaitCallback(OnMessageReceived), new MessageReceivedEventArgs(message));
}
}
else
byte[] message = new byte[msglen];
//
int readbytes = 0;
while (serialPort.Read(message, readbytes, msglen - readbytes) <= 0)
; // noop
if (MessageReceived != null)
{
Thread.Sleep(50);
OnMessageReceived(new MessageReceivedEventArgs(message));
}
}
catch (Exception e)
else
{
gotReadWriteError = true;
logger.Error(e);
Thread.Sleep(1000);
Thread.Sleep(100);
}
}
else
catch (Exception e)
{
logger.Error(e);
gotReadWriteError = true;
Thread.Sleep(1000);
}
}
}

private void ConnectionWatcherTask(CancellationToken watcherToken)
private void ConnectionWatcherTask()
{
// This task takes care of automatically reconnecting the interface
// when the connection is drop or if an I/O error occurs
while (!watcherToken.IsCancellationRequested)
while (!disconnectRequested)
{
if (gotReadWriteError)
{
Expand All @@ -314,11 +299,11 @@ private void ConnectionWatcherTask(CancellationToken watcherToken)
Close();
// wait 1 sec before reconnecting
Thread.Sleep(1000);
if (!watcherToken.IsCancellationRequested)
if (!disconnectRequested)
{
try
{
gotReadWriteError = !Open();
Open();
}
catch (Exception e)
{
Expand All @@ -331,7 +316,8 @@ private void ConnectionWatcherTask(CancellationToken watcherToken)
logger.Error(e);
}
}
Thread.Sleep(1000);
if (!disconnectRequested)
Thread.Sleep(1000);
}
}

Expand Down
4 changes: 2 additions & 2 deletions SerialPortLib/SerialPortLib.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>SerialPortLib</id>
<version>1.0.8</version>
<version>1.0.10</version>
<title>SerialPortLib</title>
<authors>Generoso Martello</authors>
<owners>G-Labs</owners>
Expand All @@ -17,7 +17,7 @@ Features
- Automatically restabilish connection on error/disconnect
- It overcomes the lack of *DataReceived* event in Mono</description>
<summary>Serial Port libray for .Net / Mono</summary>
<releaseNotes>Fixed issue #1; Updated dependencies.</releaseNotes>
<releaseNotes>Fixed threading issues.</releaseNotes>
<copyright>G-Labs</copyright>
<tags>serial port</tags>
<dependencies>
Expand Down

0 comments on commit 9973feb

Please sign in to comment.