Reputation: 15
I am working on a project using .NET 4.0. I have a device connected via a serial port and I send it commands and then wait for a response. Once the response is received, I will check the response to make sure it is valid, and then send the next command.
The main problem is that the commands I send have vastly different response times. A response from one command may take 30ms or 30sec. Because of that I implemented a TimerCallback to check every 500ms for a response from the Serial Port. I've posted the code below along with the output that I am getting.
// From different class
private string SendCommandAwaitResponse(string command)
{
// Set timeout to 30 seconds.
this.port.ReadTimeout = 30000;
// Clears existing
this.port.ReadExisting();
this.port.Write(command);
var autoEvent = new AutoResetEvent(false);
var responseTimer = new ResponseTimer(port, command);
var timer = new Timer(responseTimer.GetResponse, autoEvent, 0, 500);
autoEvent.WaitOne();
Trace.WriteLine(string.Format("AutoEvent Set: {0}", command));
timer.Dispose();
return responseTimer.Response;
}
public class ResponseTimer
{
private ISerialPort serialPort;
private string command;
private bool isAttemptingRead;
public string Response
{
get { return this.response; }
set
{
if (this.response == value)
return;
this.response = value;
}
}
private string response;
public ResponseTimer(ISerialPort port, string command)
{
Trace.WriteLine("Constructor Created");
this.isAttemptingRead = false;
this.serialPort = port;
this.command = command;
Response= string.Empty;
}
public void GetResponse(Object stateInfo)
{
AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
try
{
if (!isAttemptingRead)
{
Trace.WriteLine("Initiating ReadLine");
Response= serialPort.ReadLine();
isAttemptingRead = true;
}
Trace.WriteLine("Try: " + Response);
}
catch (TimeoutException ex)
{
Trace.WriteLine(String.Format("EX: TIMED OUT: Response {0}", Response));
autoEvent.Set();
}
Trace.WriteLine(String.Format("CURRENT Response {0}", Response));
if (Response == command)
{
Trace.WriteLine(string.Format("COMMAND: {0}", command));
Trace.WriteLine(string.Format("Clearing Buffer: {0}", Response));
Response= string.Empty;
isAttemptingRead = false;
}
else if (!string.IsNullOrEmpty(Response))
{
Trace.WriteLine(String.Format("Got Response - {0}", Response));
autoEvent.Set();
}
else if (string.IsNullOrEmpty(Response))
Trace.WriteLine("Empty or Null read");
}
}
The code works for the smaller response times (30ms), but when it gets to a command that has a response time of 30 seconds, I get the following output (mostly).
WriteLine(#XSFCD)
Then I usually just end the program. Am I missing something with the TimerCallback method?
Upvotes: 0
Views: 899
Reputation: 2976
From the MSDN pages:
will wait until it sees an end of line character on the serial port or until ReadTimeout has elapsed.
...
By default, the ReadLine method will block until a line is received. If this behavior is undesirable, set the ReadTimeout property to any non-zero value to force the ReadLine method to throw a TimeoutException if a line is not available on the port.
And for System.Threading.Timer:
Provides a mechanism for executing a method on a thread pool thread at specified intervals.
So Each timer callback may be on a separate thread and each readline() call pauses the current thread until it sees an end of line character. Your timer is firing every 500 milliseconds regardless of what else has occurred. So the first one calls readline() and is waiting there for a end of line character. It doesn't see one so it keeps waiting. 500ms later another thread does the exact same thing. 500ms later the same thing happens, etc. So you are stacking up all these readline() calls waiting for readline() to complete. Eventually you get tired of waiting and kill the program. Instead of readLine() you could do readExisting() to read the current buffer, but you'd have to find where the end of line character is yourself. Either that or you could get rid of the timer entirely and use the serialport events (such as DataReceived) to tell you when you've gotten some data.
Upvotes: 1