I have an application that communicates with a microcontroller via serial port and I need to check the controller's status periodically in a Background Worker, and also allow the user to interact with the controller asynchronously (via user interface), by sending commands and receiving responses (also in a Background Worker).
This is my Serial Communication class:
using System;
using System.IO.Ports;
using System.Timers;
namespace GUI_WPF
{
static class SerialCommunication
{
static SerialPort serialPort = new SerialPort();
static int readWriteTimeout = 2000; // [ms]
static string endOfString = "" + char.MinValue;
static object _commandLock = new object();
public static void SerialPortInit(string portName)
{
if (serialPort.IsOpen == true)
{
serialPort.Close();
}
serialPort.PortName = portName;
serialPort.BaudRate = 115200;
serialPort.Parity = Parity.None;
serialPort.DataBits = 8;
serialPort.StopBits = StopBits.One;
serialPort.Handshake = Handshake.None;
// Set the read/write timeouts [ms]
serialPort.ReadTimeout = readWriteTimeout;
serialPort.WriteTimeout = readWriteTimeout;
serialPort.Open();
}
public static string SerialRead()
{
try
{
serialPort.DiscardInBuffer();
return serialPort.ReadTo(endOfString);
}
catch (Exception ex)
{
throw new Exception("SerialCommunication.SerialRead(): " + ex.Message);
}
}
public static string SerialRead(int numberOfReads)
{
try
{ // The controller sends terminator char after each value transmitted, so using this method we can read multiple values
serialPort.DiscardInBuffer();
string response = "";
for (int i = 0; i < numberOfReads; i++)
{
response += serialPort.ReadTo(endOfString) + " ";
}
return response;
}
catch (Exception ex)
{
throw new Exception("SerialCommunication.SerialRead(): " + ex.Message);
}
}
public static void SerialWrite(string text)
{
try
{
serialPort.DiscardOutBuffer();
serialPort.Write(text);
}
catch (Exception ex)
{
throw new Exception("SerialCommunication.SerialWrite(): " + ex.Message);
}
}
public static string SerialWriteAndRead(string text)
{
lock (_commandLock) // If command is called from a thread while is executed on another thread, the lock forces the calling thread to wait
{
SerialWrite(text);
return SerialRead();
}
}
public static string SerialWriteAndRead(string text, int numberOfReads)
{
lock (_commandLock) // If command is called from a thread while is executed on another thread, the lock forces the calling thread to wait
{ // The controller sends terminator char after each value transmitted, so using this method we can read multiple values
SerialWrite(text);
return SerialRead(numberOfReads);
}
}
public static string SerialRead(int numberOfReads, int timeoutMillisecods)
{
serialPort.ReadTimeout = timeoutMillisecods;
string response = SerialRead(numberOfReads);
serialPort.ReadTimeout = readWriteTimeout;
return response;
}
public static string[] GetSerialPorts()
{
return SerialPort.GetPortNames();
}
public static void ClosePort()
{
try
{
if (serialPort.IsOpen == true)
{
serialPort.Close();
}
}
catch { }
}
}
}
In a scenario, the monitor interrogates the controller every 2 seconds. Sometimes (most of the times) when the user issues from the GUI a command which waits for a response, the following exception appears: The I/O operation has been aborted because of either a thread exit or an app request.
Why does the "lock()" method not work? How can it be done?