98 lines
3.1 KiB
C#
98 lines
3.1 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using System.Collections.Generic;
|
|
using System.IO.Ports;
|
|
|
|
namespace SharpSerial
|
|
{
|
|
// com0com BytesToRead is UNRELIABLE
|
|
// Solution based on BaseStream and influenced by
|
|
// https://www.sparxeng.com/blog/software/must-use-net-system-io-ports-serialport
|
|
// https://www.vgies.com/a-reliable-serial-port-in-c/
|
|
public class SerialDevice : ISerialStream, IDisposable
|
|
{
|
|
private readonly SerialPort serial;
|
|
private readonly List<byte> list;
|
|
private readonly Queue<byte> queue;
|
|
private readonly byte[] buffer;
|
|
|
|
public SerialDevice(object settings)
|
|
{
|
|
this.buffer = new byte[256];
|
|
this.list = new List<byte>(256);
|
|
this.queue = new Queue<byte>(256);
|
|
this.serial = new SerialPort();
|
|
|
|
//init serial port and launch async reader
|
|
SerialSettings.CopyProperties(settings, serial);
|
|
serial.Open();
|
|
//DiscardInBuffer not needed by FTDI and ignored by com0com
|
|
var stream = serial.BaseStream;
|
|
//unavailable after closed so pass it
|
|
stream.BeginRead(buffer, 0, buffer.Length, ReadCallback, stream);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Tools.Try(serial.Close);
|
|
Tools.Try(serial.Dispose);
|
|
}
|
|
|
|
public void Write(byte[] data)
|
|
{
|
|
var stream = serial.BaseStream;
|
|
stream.Write(data, 0, data.Length);
|
|
//always flush to allow sync by following read available
|
|
stream.Flush();
|
|
}
|
|
|
|
public byte[] Read(int size, int eop, int toms)
|
|
{
|
|
list.Clear();
|
|
var dl = DateTime.Now.AddMilliseconds(toms);
|
|
while (true)
|
|
{
|
|
int b = ReadByte();
|
|
if (b == -1)
|
|
{
|
|
//toms=0 should return immediately with available
|
|
if (DateTime.Now >= dl) break;
|
|
Thread.Sleep(1);
|
|
continue;
|
|
}
|
|
list.Add((byte)b);
|
|
if (eop >= 0 && b == eop) break;
|
|
if (size >= 0 && list.Count >= size) break;
|
|
dl = DateTime.Now.AddMilliseconds(toms);
|
|
}
|
|
return list.ToArray();
|
|
}
|
|
|
|
private int ReadByte()
|
|
{
|
|
lock (queue)
|
|
{
|
|
if (queue.Count == 0) return -1;
|
|
return queue.Dequeue();
|
|
}
|
|
}
|
|
|
|
private void ReadCallback(IAsyncResult ar)
|
|
{
|
|
Tools.Try(() =>
|
|
{
|
|
//try needed to avoid triggering the domain unhandled
|
|
//exception handler when used as standalone stream
|
|
var stream = ar.AsyncState as Stream;
|
|
int count = stream.EndRead(ar);
|
|
if (count > 0) //0 for closed stream
|
|
{
|
|
lock (queue) for (var i = 0; i < count; i++) queue.Enqueue(buffer[i]);
|
|
stream.BeginRead(buffer, 0, buffer.Length, ReadCallback, stream);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|