I'm currently working on a project where I use a serial port with a UWP app. Sometimes I have the problem that I expect a certain amount of data but when I don't get that amount of data my serial port keeps reading until I cancel it manually.
When I initialize the serialDevice I set the serialDevice.ReadTimeout parameter but that doesn't do anything. I also tried to bind a cancellationToken to the ReadTimeout parameter but that didn't work either. If I remember correctly it cancelled my method before it even finished...
Is there some way to implement a working timeout?
I use this as my base:
public class ReadWriteAdapter
{
public SemaphoreSlim Semaphore { get; }
private static readonly object LockObject = new object();
private static ReadWriteAdapter _instance;
public static ReadWriteAdapter Current
{
get
{
if (_instance == null)
{
lock (LockObject)
{
if (_instance == null)
{
_instance = new ReadWriteAdapter();
}
}
}
return _instance;
}
}
private ReadWriteAdapter()
{
Semaphore = new SemaphoreSlim(1, 1);
}
private SerialDevice _serialPort;
public CancellationTokenSource ReadCancellationTokenSource;
public CancellationTokenSource WriteCancellationTokenSource;
private object _readCancelLock = new object();
private object _writeCancelLock = new object();
private DataWriter _dataWriter;
private DataReader _dataReader;
public bool IsDeviceInitialized()
{
return _serialPort != null;
}
public async Task<string> Init()
{
try
{
if (_serialPort != null) return "port already configured!";
var aqs = SerialDevice.GetDeviceSelector("COM3");
var devices = await DeviceInformation.FindAllAsync(aqs, null);
if (!devices.Any()) return "no devices found!";
await OpenPort(devices[0].Id);
WriteCancellationTokenSource = new CancellationTokenSource();
ReadCancellationTokenSource = new CancellationTokenSource();
return "found port!";
}
catch (Exception ex)
{
return ex.Message;
}
}
private async Task OpenPort(string deviceId)
{
try
{
_serialPort = await SerialDevice.FromIdAsync(deviceId);
if (_serialPort != null)
{
_serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
_serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
_serialPort.BaudRate = 19200;
_serialPort.Parity = SerialParity.None;
_serialPort.StopBits = SerialStopBitCount.One;
_serialPort.DataBits = 8;
_serialPort.Handshake = SerialHandshake.None;
_dataWriter = new DataWriter(_serialPort.OutputStream);
_dataReader = new DataReader(_serialPort.InputStream);
}
}
catch (Exception ex)
{
throw ex;
}
}
private async Task<byte[]> ReadAsync(CancellationToken cancellationToken, uint readBufferLength)
{
Task<uint> loadAsyncTask;
var returnArray = new byte[readBufferLength];
lock (_readCancelLock)
{
cancellationToken.ThrowIfCancellationRequested();
_dataReader.InputStreamOptions = InputStreamOptions.Partial;
loadAsyncTask = _dataReader.LoadAsync(readBufferLength).AsTask(cancellationToken);
}
var bytesRead = await loadAsyncTask;
if (bytesRead > 0)
{
_dataReader.ReadBytes(returnArray);
}
return returnArray;
}
public async Task<uint> WriteCommand(string PairedCommand)
{
var commandArray = StringToByteArray(PairedCommand);
uint taskResult = 0;
try
{
if (_serialPort != null)
{
if (_dataWriter == null)
{
_dataWriter = new DataWriter(_serialPort.OutputStream);
taskResult = await WriteAsync(WriteCancellationTokenSource.Token, commandArray);
}
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
_dataWriter.DetachStream();
_dataWriter.Dispose();
_dataWriter = null;
}
return taskResult;
}
public async Task<uint> WriteAsync(CancellationToken cancellationToken, byte[] data)
{
Task<uint> storeAsyncTask;
try
{
lock (_writeCancelLock)
{
cancellationToken.ThrowIfCancellationRequested();
_dataWriter.WriteBytes(data);
storeAsyncTask = _dataWriter.StoreAsync().AsTask(cancellationToken);
}
}
catch (Exception ex)
{
throw ex;
}
return await storeAsyncTask;
}
public async Task<byte[]> Listen(uint bufferLength)
{
var listen = new byte[bufferLength];
try
{
if (_serialPort != null)
{
if (_dataReader == null)
{
_dataReader = new DataReader(_serialPort.InputStream);
}
listen = await ReadAsync(ReadCancellationTokenSource.Token, bufferLength);
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (_dataReader != null)
{
_dataReader.DetachStream();
_dataReader.Dispose();
_dataReader = null;
}
}
return listen;
}
public byte[] StringToByteArray(string str)
{
var enc = new ASCIIEncoding();
return enc.GetBytes(str);
}
public void CancelReadTask()
{
lock (_readCancelLock)
{
if (ReadCancellationTokenSource == null) return;
if (ReadCancellationTokenSource.IsCancellationRequested) return;
ReadCancellationTokenSource.Cancel();
ReadCancellationTokenSource = new CancellationTokenSource();
}
}
private void CancelWriteTask()
{
lock (_writeCancelLock)
{
if (WriteCancellationTokenSource == null) return;
if (WriteCancellationTokenSource.IsCancellationRequested) return;
WriteCancellationTokenSource.Cancel();
WriteCancellationTokenSource = new CancellationTokenSource();
}
}
}
And I call it with a code similar to this (short version):
public class ReadData
{
private ReadBlock readBlock = new ReadBlock();
public async Task<byte[]> ReadParaBlock()
{
await ReadWriteAdapter.Current.Semaphore.WaitAsync();
await ReadWriteAdapter.Current.WriteCommand("getData");
byte[] recievedArray = await ReadWriteAdapter.Current.Listen(100);
ReadWriteAdapter.Current.Semaphore.Release();
return recievedArray;
}
}