Hi I have a simple communication on serial port well all is according to book and documentation so open port method looks like this:
public SerialPort Op
Read data in a loop after write operation until get a full response. But you need to use synchronous API and Task.Run()
as current version of the asynchronous API ignores SerialPort
timeout properties completely and CancellationToken
in Task based API almost completely.
Excerpt from the SerialPort.ReadTimeout Microsoft Docs that is relevant to SerialPort.BaseStream.ReadAsync()
because it uses default implementation Stream.ReadAsync()
:
This property does not affect the BeginRead method of the stream returned by the BaseStream property.
Example implementation using synchronous API and dynamic timeout properties update:
static byte[] SendMessage(byte[] message, TimeSpan timeout)
{
// Use stopwatch to update SerialPort.ReadTimeout and SerialPort.WriteTimeout
// as we go.
var stopwatch = Stopwatch.StartNew();
// Organize critical section for logical operations using some standard .NET tool.
lock (_syncRoot)
{
var originalWriteTimeout = _serialPort.WriteTimeout;
var originalReadTimeout = _serialPort.ReadTimeout;
try
{
// Start logical request.
_serialPort.WriteTimeout = (int)Math.Max((timeout - stopwatch.Elapsed).TotalMilliseconds, 0);
_serialPort.Write(message, 0, message.Length);
// Expected response length. Look for the constant value from
// the device communication protocol specification or extract
// from the response header (first response bytes) if there is
// any specified in the protocol.
int count = ...;
byte[] buffer = new byte[count];
int offset = 0;
// Loop until we recieve a full response.
while (count > 0)
{
_serialPort.ReadTimeout = (int)Math.Max((timeout - stopwatch.Elapsed).TotalMilliseconds, 0);
var readCount = _serialPort.Read(buffer, offset, count);
offset += readCount;
count -= readCount;
}
return buffer;
}
finally
{
// Restore SerialPort state.
_serialPort.ReadTimeout = originalReadTimeout;
_serialPort.WriteTimeout = originalWriteTimeout;
}
}
}
And example usage:
byte[] request = ...;
TimeSpan timeout = ...;
var sendTask = Task.Run(() => SendMessage(request, timeout));
try
{
await await Task.WhenAny(sendTask, Task.Delay(timeout));
}
catch (TaskCanceledException)
{
throw new TimeoutException();
}
byte[] response = await sendTask;
You can do similar thing with CancellationToken
instance and use CancellationToken.ThrowIfCancellationRequested()
between read and write operations but you have to make sure that proper timeouts are set on SerialPort
or otherwise Thread pool thread will hang forever possible holding a lock. As far as I know you can't utilize CancellationToken.Register()
because there is no SerialPort
method to call to cancel an operation.
For more information check: