This commit is contained in:
Dan Clark
2025-02-10 12:50:45 +00:00
parent 18ceb512f6
commit f6e55b7a21
8 changed files with 150 additions and 69 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ public class SerialContext<TReader, TValue, TEvent>(IMessenger messenger,
IsOpen = true;
_ = ReadAsync();
_ = Task.Run(ReadAsync);
return true;
}
+7 -3
View File
@@ -21,9 +21,13 @@ public class SerialContextFactory(IServiceProvider provider,
return (ISerialContext<TReader, TValue, TEvent>)context;
}
SerialPort serialPort = new(configuration.PortName, configuration.BaudRate);
serialPort.ReadTimeout = 500; // Prevents blocking if no data is available
serialPort.WriteTimeout = 500;
SerialPort serialPort = new(configuration.PortName, configuration.BaudRate)
{
ReadTimeout = SerialPort.InfiniteTimeout,
WriteTimeout = SerialPort.InfiniteTimeout,
DtrEnable = false,
RtsEnable = false
};
SerialConnection connection = new(serialPort);
SerialStreamer streamer = new(serialPort);
-3
View File
@@ -1,3 +0,0 @@
namespace Toolkit.Foundation;
public record SerialEventArgs(byte Type, short Value);
-61
View File
@@ -1,61 +0,0 @@
using System.Buffers;
using System.IO.Pipelines;
namespace Toolkit.Foundation;
public class SerialEventReader(Stream stream) :
SerialReader<SerialEventArgs>(stream)
{
private readonly PipeReader reader = PipeReader.Create(stream);
public override async IAsyncEnumerable<SerialEventArgs> ReadAsync()
{
while (true)
{
ReadResult result;
try
{
result = await reader.ReadAsync();
}
catch (Exception)
{
continue;
}
ReadOnlySequence<byte> buffer = result.Buffer;
while (TryParseEvent(ref buffer, out SerialEventArgs serialEvent))
{
yield return serialEvent;
}
reader.AdvanceTo(buffer.Start, buffer.End);
if (result.IsCompleted)
break;
}
await reader.CompleteAsync();
}
private bool TryParseEvent(ref ReadOnlySequence<byte> buffer,
out SerialEventArgs serialEvent)
{
SequenceReader<byte> reader = new(buffer);
serialEvent = default!;
if (reader.Remaining < 3)
return false;
if (!reader.TryRead(out byte type))
return false;
if (!reader.TryReadLittleEndian(out short value))
return false;
serialEvent = new SerialEventArgs(type, value);
buffer = buffer.Slice(reader.Position);
return true;
}
}
+72
View File
@@ -0,0 +1,72 @@
using System.IO.Ports;
namespace Toolkit.Foundation;
public class SerialPortStream :
Stream
{
private readonly SerialPort serialPort;
public SerialPortStream(SerialPort serialPort)
{
this.serialPort = serialPort ?? throw new ArgumentNullException(nameof(serialPort));
if (!this.serialPort.IsOpen) this.serialPort.Open();
}
public override bool CanRead => serialPort.IsOpen && serialPort.BaseStream.CanRead;
public override bool CanSeek => false;
public override bool CanWrite => serialPort.IsOpen && serialPort.BaseStream.CanWrite;
public bool HasData => serialPort.BytesToRead > 0;
public override long Length => throw new NotSupportedException();
public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
public override void Flush() => serialPort.BaseStream.Flush();
public override int Read(byte[] buffer, int offset, int count) =>
HasData ? serialPort.BaseStream.Read(buffer, offset, count) : 0;
public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
await WaitForDataAsync(cancellationToken);
return await serialPort.BaseStream.ReadAsync(buffer, cancellationToken);
}
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
await WaitForDataAsync(cancellationToken);
return await serialPort.BaseStream.ReadAsync(buffer.AsMemory(offset, count), cancellationToken);
}
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
public override void SetLength(long value) => throw new NotSupportedException();
public override void Write(byte[] buffer, int offset, int count) =>
serialPort.BaseStream.Write(buffer, offset, count);
public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default) =>
await serialPort.BaseStream.WriteAsync(buffer, cancellationToken);
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) =>
await serialPort.BaseStream.WriteAsync(buffer.AsMemory(offset, count), cancellationToken);
protected override void Dispose(bool disposing)
{
if (disposing) serialPort?.Dispose();
base.Dispose(disposing);
}
private async Task WaitForDataAsync(CancellationToken cancellationToken)
{
while (!HasData)
{
await Task.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
}
+1 -1
View File
@@ -5,5 +5,5 @@ namespace Toolkit.Foundation;
public class SerialStreamer(SerialPort serialPort) :
ISerialStreamer
{
public Stream Create() => serialPort.BaseStream;
public Stream Create() => new SerialPortStream(serialPort);
}
@@ -0,0 +1,3 @@
namespace Toolkit.Foundation;
public record SerialStructEventArgs(byte Type, short Value);
+66
View File
@@ -0,0 +1,66 @@
using System.Buffers;
using System.IO.Pipelines;
namespace Toolkit.Foundation;
public class SerialStructReader(Stream stream) :
SerialReader<SerialStructEventArgs>(stream)
{
private readonly PipeReader reader = PipeReader.Create(stream);
public override async IAsyncEnumerable<SerialStructEventArgs> ReadAsync()
{
while (true)
{
ReadResult? result = default;
try
{
result = await reader.ReadAsync();
}
catch (IOException)
{
continue;
}
catch (OperationCanceledException)
{
yield break;
}
catch (Exception)
{
yield break;
}
if (result.HasValue)
{
ReadOnlySequence<byte> buffer = result.Value.Buffer;
while (TryParse(ref buffer, out SerialStructEventArgs serialEvent))
{
yield return serialEvent;
}
reader.AdvanceTo(buffer.Start, buffer.End);
}
}
}
private bool TryParse(ref ReadOnlySequence<byte> buffer, out SerialStructEventArgs serialEvent)
{
SequenceReader<byte> reader = new(buffer);
serialEvent = default!;
if (reader.Remaining < 3)
return false;
if (!reader.TryRead(out byte type))
return false;
if (!reader.TryReadLittleEndian(out short value))
return false;
serialEvent = new SerialStructEventArgs(type, value);
buffer = buffer.Slice(reader.Position);
return true;
}
}