Reputation: 725
I have an application that communicates with a microcontroller via serial port. I want 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.
The UI and Background Worker use the SerialCommunication static class:
static class SerialCommunication
{
static SerialPort serialPort;
static int readWriteTimeout = 1000; // [ms]
static int waitForTransmissionTimeout = 2; // [s]
static string rxData = "", endOfString = "" + char.MinValue;
static bool WaitingForSerialData = false; // Set in SerialWrite(), cleared in SerialRead()
[...]
public static string SerialRead()
{
try
{
rxData = serialPort.ReadTo(endOfString);
}
catch (TimeoutException)
{
WaitingForSerialData = false;
throw new Exception(Properties.Resources.serial_read_timeout);
}
WaitingForSerialData = false;
return rxData;
}
public static void SerialWrite(string text)
{
DateTime start = DateTime.Now;
while (WaitingForSerialData) // Avoids the situation in which a command executed on a thread receives the response for the command executed from a different thread
{
if (!WaitingForSerialData)
{
try
{
WaitingForSerialData = true; // All commands wait for confirmation/data, so it is normal to set this boolean value for every serial transmission
serialPort.Write(text);
}
catch (TimeoutException)
{
throw new Exception(Properties.Resources.serial_write_timeout);
}
}
else
{
System.Threading.Thread.Sleep(100);
if ((DateTime.Now - start).Seconds >= waitForTransmissionTimeout)
{
throw new Exception("Timeout");
}
}
}
}
}
The serial port is initialized at application startup. SerialWrite
and SerialRead
are called in the UI or Background Worker consecutively.
I want to avoid situations in which a command receives the response from another command executed in a different thread. Currently, I've implemented waiting in SerialWrite for the command to be received (for SerialRead to finish) before sending the command, but I'm afraid that this can block the UI (for up to waitForTransmissionTimeout
seconds), if SerialWrite is executed from there.
So what is the best way to synchronize the SerialPort operations?
Upvotes: 0
Views: 2444
Reputation: 101483
I don't know the whole situation, but it seems to me you can just use a simple lock to serialize your write followed by read, like this:
static class SerialCommunication
{
static SerialPort serialPort;
static string endOfString = "" + char.MinValue;
static readonly object _commandLock = new object();
public static string SendCommand(string text) {
lock (_commandLock) {
SerialWrite(text);
return SerialRead();
}
}
private static string SerialRead() {
try {
return serialPort.ReadTo(endOfString);
}
catch (TimeoutException) {
throw new Exception(Properties.Resources.serial_read_timeout);
}
}
private static void SerialWrite(string text) {
try {
serialPort.Write(text);
}
catch (TimeoutException) {
throw new Exception(Properties.Resources.serial_write_timeout);
}
}
}
Upvotes: 1