Add project files.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public static class IHostBuilderExtensions
|
||||
{
|
||||
public static IHostBuilder ConfigureSerial<TConfiguration, TSerialReader, TContent>(this IHostBuilder hostBuilder, Action<HostBuilderContext> configureDelegate) where TSerialReader : SerialReader<TContent> where TConfiguration : ISerialConfiguration, new()
|
||||
{
|
||||
hostBuilder.ConfigureServices((hostBuilderContext, serviceCollection) =>
|
||||
{
|
||||
configureDelegate.Invoke(hostBuilderContext);
|
||||
|
||||
serviceCollection.TryAddSingleton<ISerialFactory, SerialFactory>();
|
||||
serviceCollection.AddSingleton(provider => provider.GetService<ISerialFactory>()!.Create<TSerialReader, TContent>(provider.GetService<IOptionsMonitor<TConfiguration>>()!.CurrentValue));
|
||||
});
|
||||
|
||||
return hostBuilder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public interface ISerialConfiguration
|
||||
{
|
||||
public string PortName { get; set; }
|
||||
|
||||
public int BaudRate { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public interface ISerialConnection
|
||||
{
|
||||
bool IsOpen { get; }
|
||||
|
||||
void Close();
|
||||
|
||||
bool Open();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public interface ISerialConnectionStreamer
|
||||
{
|
||||
Stream Stream { get; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public interface ISerialContext
|
||||
{
|
||||
void Open();
|
||||
}
|
||||
|
||||
public interface ISerialContext<TSerialReader, TContent> : ISerialContext where TSerialReader : SerialReader<TContent>
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public interface ISerialFactory
|
||||
{
|
||||
ISerialContext<TSerialReader, TContent> Create<TSerialReader, TContent>(ISerialConfiguration configuration) where TSerialReader : SerialReader<TContent>;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public interface ISerialResponse
|
||||
{
|
||||
ISerialContext Context { get; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public interface ISerialStreamer
|
||||
{
|
||||
Stream Create();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public interface ISerialWriter
|
||||
{
|
||||
void Write(byte[] buffer, int offset, int count);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0-rc.2.22472.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0-rc.2.22472.3" />
|
||||
<PackageReference Include="System.IO.Pipelines" Version="7.0.0-rc.2.22472.3" />
|
||||
<PackageReference Include="System.IO.Ports" Version="7.0.0-rc.2.22472.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Core\Core.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public class SerialConfiguration : ISerialConfiguration
|
||||
{
|
||||
[NotNull]
|
||||
public string? PortName { get; set; }
|
||||
|
||||
public int BaudRate { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public class SerialConnection : ISerialConnection
|
||||
{
|
||||
private readonly SerialPort serialPort;
|
||||
|
||||
public SerialConnection(SerialPort serialPort)
|
||||
{
|
||||
this.serialPort = serialPort;
|
||||
}
|
||||
|
||||
public bool IsOpen { get; private set; }
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (IsOpen)
|
||||
{
|
||||
try
|
||||
{
|
||||
serialPort.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
IsOpen = serialPort.IsOpen;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Open()
|
||||
{
|
||||
if (!IsOpen)
|
||||
{
|
||||
try
|
||||
{
|
||||
serialPort.Open();
|
||||
|
||||
serialPort.DiscardInBuffer();
|
||||
serialPort.DiscardOutBuffer();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
IsOpen = serialPort.IsOpen;
|
||||
}
|
||||
|
||||
return IsOpen;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public class SerialConnectionStreamer : ISerialConnectionStreamer
|
||||
{
|
||||
public SerialConnectionStreamer(SerialPort serial)
|
||||
{
|
||||
Stream = serial.BaseStream;
|
||||
}
|
||||
|
||||
public Stream Stream { get; }
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using TheXamlGuy.Framework.Core;
|
||||
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public class SerialContext<TSerialReader, TContent> : ISerialContext<TSerialReader, TContent> where TSerialReader : SerialReader<TContent>
|
||||
{
|
||||
private readonly ISerialConnection connection;
|
||||
private readonly IEventAggregator eventAggregator;
|
||||
private readonly ISerialStreamer serialStreamer;
|
||||
|
||||
public SerialContext(IEventAggregator eventAggregator,
|
||||
ISerialConnection connection,
|
||||
ISerialStreamer serialStreamer)
|
||||
{
|
||||
this.eventAggregator = eventAggregator;
|
||||
this.connection = connection;
|
||||
this.serialStreamer = serialStreamer;
|
||||
}
|
||||
|
||||
public async void Open()
|
||||
{
|
||||
if (connection.Open())
|
||||
{
|
||||
Stream stream = serialStreamer.Create();
|
||||
|
||||
if ((TSerialReader?)Activator.CreateInstance(typeof(TSerialReader), new object[] { stream }) is TSerialReader reader)
|
||||
{
|
||||
await foreach (TContent content in reader.ReadAsync())
|
||||
{
|
||||
eventAggregator.Publish(SerialResponse.Create(this, content));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.IO.Ports;
|
||||
using TheXamlGuy.Framework.Core;
|
||||
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public class SerialFactory : ISerialFactory
|
||||
{
|
||||
private readonly IServiceFactory factory;
|
||||
private readonly Dictionary<ISerialConfiguration, ISerialContext> cache = new();
|
||||
|
||||
public SerialFactory(IServiceFactory factory)
|
||||
{
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public ISerialContext<TSerialReader, TContent> Create<TSerialReader, TContent>(ISerialConfiguration configuration) where TSerialReader : SerialReader<TContent>
|
||||
{
|
||||
if (cache.TryGetValue(configuration, out ISerialContext? context))
|
||||
{
|
||||
return (ISerialContext<TSerialReader, TContent>)context;
|
||||
}
|
||||
|
||||
SerialPort serialPort = new(configuration.PortName, configuration.BaudRate)
|
||||
{
|
||||
DtrEnable = true
|
||||
};
|
||||
|
||||
SerialConnection connection = new(serialPort);
|
||||
SerialStreamer streamer = new(serialPort);
|
||||
|
||||
context = factory.Create<SerialContext<TSerialReader, TContent>>(connection, streamer);
|
||||
cache.Add(configuration, context);
|
||||
|
||||
return (ISerialContext<TSerialReader, TContent>)context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Buffers;
|
||||
using System.IO.Pipelines;
|
||||
using System.Text;
|
||||
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public class SerialLineReader : SerialReader<string>
|
||||
{
|
||||
private readonly PipeReader reader;
|
||||
|
||||
public SerialLineReader(Stream stream) : base(stream)
|
||||
{
|
||||
reader = PipeReader.Create(stream);
|
||||
}
|
||||
|
||||
public override async IAsyncEnumerable<string> ReadAsync()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ReadResult result = await reader.ReadAsync();
|
||||
ReadOnlySequence<byte> buffer = result.Buffer;
|
||||
|
||||
while (TryReadLine(ref buffer, out ReadOnlySequence<byte> line))
|
||||
{
|
||||
yield return EncodingExtensions.GetString(Encoding.UTF8, line);
|
||||
}
|
||||
|
||||
reader.AdvanceTo(buffer.Start, buffer.End);
|
||||
if (result.IsCompleted)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private bool TryReadLine(ref ReadOnlySequence<byte> buffer, out ReadOnlySequence<byte> line)
|
||||
{
|
||||
SequencePosition? position = buffer.PositionOf((byte)'\n');
|
||||
if (position == null)
|
||||
{
|
||||
line = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
line = buffer.Slice(0, position.Value);
|
||||
buffer = buffer.Slice(buffer.GetPosition(1, position.Value));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public abstract class SerialReader<TContent>
|
||||
{
|
||||
public SerialReader(Stream stream)
|
||||
{
|
||||
Stream = stream;
|
||||
}
|
||||
|
||||
public Stream Stream { get; }
|
||||
|
||||
public abstract IAsyncEnumerable<TContent> ReadAsync();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Buffers;
|
||||
using TheXamlGuy.Framework.Core;
|
||||
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public abstract class SerialReaderHandler<TRead> : IMediatorAsyncHandler<TRead, ReadOnlySequence<byte>> where TRead : class
|
||||
{
|
||||
public abstract Task<TRead> Handle(ReadOnlySequence<byte> request, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public record SerialResponse
|
||||
{
|
||||
public static SerialResponse<TContent> Create<TContent>(ISerialContext context, TContent content)
|
||||
{
|
||||
return new SerialResponse<TContent>(context, content);
|
||||
}
|
||||
}
|
||||
|
||||
public record SerialResponse<TContent> : ISerialResponse
|
||||
{
|
||||
public SerialResponse(ISerialContext context, TContent content)
|
||||
{
|
||||
Context = context;
|
||||
Content = content;
|
||||
}
|
||||
|
||||
public ISerialContext Context { get; }
|
||||
|
||||
public TContent Content { get; }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace TheXamlGuy.Framework.Serial;
|
||||
|
||||
public class SerialStreamer : ISerialStreamer
|
||||
{
|
||||
private readonly SerialPort serialPort;
|
||||
|
||||
public SerialStreamer(SerialPort serialPort)
|
||||
{
|
||||
this.serialPort = serialPort;
|
||||
}
|
||||
|
||||
public Stream Create()
|
||||
{
|
||||
return serialPort.BaseStream;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user