Refactor serials
This commit is contained in:
@@ -5,7 +5,6 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.FileProviders.Physical;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Toolkit.Foundation;
|
||||
@@ -197,25 +196,39 @@ public static class IHostBuilderExtension
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IHostBuilder ConfigureMicroControllers(this IHostBuilder hostBuilder, Action<IMicroControllerBuilder> builderDelegate)
|
||||
public static IHostBuilder AddSerial<TConfiguration, TReader, TRead, TEvent>(this IHostBuilder hostBuilder)
|
||||
where TConfiguration : ISerialConfiguration
|
||||
where TReader : SerialReader<TRead>
|
||||
where TEvent : SerialEventArgs<TRead>, new()
|
||||
{
|
||||
hostBuilder.ConfigureServices((hostBuilderContext, serviceCollection) =>
|
||||
{
|
||||
MicroControllerBuilder? builder = new();
|
||||
|
||||
builderDelegate.Invoke(builder);
|
||||
serviceCollection.TryAddSingleton<ISerialFactory, SerialFactory>();
|
||||
serviceCollection.TryAddSingleton<IMicroControllerContextFactory, MicroControllerContextFactory>();
|
||||
|
||||
foreach (IMicroControllerBuilderConfiguration configuration in builder.Configurations)
|
||||
{
|
||||
serviceCollection.AddSingleton(provider => configuration.Factory.Invoke(provider) ?? throw new NullReferenceException());
|
||||
}
|
||||
serviceCollection.TryAddSingleton<ISerialContextFactory, SerialContextFactory>();
|
||||
serviceCollection.AddSingleton<ISerialContext<TReader, TRead, TEvent>>(provider => provider.GetRequiredService<ISerialContextFactory>().Create<TConfiguration, TReader, TRead, TEvent>()
|
||||
?? throw new NullReferenceException());
|
||||
});
|
||||
|
||||
return hostBuilder;
|
||||
}
|
||||
|
||||
//public static IHostBuilder ConfigureSerials(this IHostBuilder hostBuilder, Action<ISerialBuilder> builderDelegate)
|
||||
//{
|
||||
// hostBuilder.ConfigureServices((hostBuilderContext, serviceCollection) =>
|
||||
// {
|
||||
// SerialBuilder? builder = new();
|
||||
|
||||
// builderDelegate.Invoke(builder);
|
||||
// serviceCollection.TryAddSingleton<ISerialContextFactory, SerialContextFactory>();
|
||||
|
||||
// foreach (ISerialBuilderConfiguration configuration in builder.Configurations)
|
||||
// {
|
||||
// serviceCollection.AddSingleton(provider => configuration.Factory.Invoke(provider) ?? throw new NullReferenceException());
|
||||
// }
|
||||
// });
|
||||
|
||||
// return hostBuilder;
|
||||
//}
|
||||
|
||||
public static IHostBuilder UseContentRoot(this IHostBuilder hostBuilder,
|
||||
string contentRoot,
|
||||
bool createDirectory)
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public interface IMicroControllerContextFactory
|
||||
{
|
||||
IMicroControllerContext<TRead, TEvent>? Create<TConfiguration, TReader, TRead, TEvent>(IReadOnlyCollection<IMicroControllerModuleDescriptor> modules)
|
||||
where TConfiguration : ISerialConfiguration
|
||||
where TReader : SerialReader<TRead>
|
||||
where TEvent : ISerialEventArgs<TRead>;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public interface IMicroControllerBuilder
|
||||
{
|
||||
IReadOnlyCollection<IMicroControllerBuilderConfiguration> Configurations { get; }
|
||||
|
||||
IMicroControllerBuilderConfiguration<TConfiguration, TReader, TRead, TEvent> Add<TConfiguration, TReader, TRead, TEvent>()
|
||||
where TConfiguration : ISerialConfiguration
|
||||
where TReader : SerialReader<TRead>
|
||||
where TEvent : ISerialEventArgs<TRead>;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public interface IMicroControllerBuilderConfiguration
|
||||
{
|
||||
IReadOnlyCollection<IMicroControllerModuleDescriptor> Modules { get; }
|
||||
|
||||
Func<IServiceProvider, IMicroControllerContext?> Factory { get; }
|
||||
}
|
||||
|
||||
public interface IMicroControllerBuilderConfiguration<TConfiguration, TReader, TRead, IEvent> :
|
||||
IMicroControllerBuilderConfiguration
|
||||
where TConfiguration : ISerialConfiguration
|
||||
where TReader : SerialReader<TRead>
|
||||
where IEvent : ISerialEventArgs<TRead>
|
||||
{
|
||||
|
||||
IMicroControllerBuilderConfiguration<TConfiguration, TReader, TRead, IEvent> AddModule<TModule>()
|
||||
where TModule : IMicroControllerModule, new();
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public interface IMicroControllerContext<TRead, TEvent> :
|
||||
IMicroControllerContext
|
||||
where TEvent : ISerialEventArgs<TRead>;
|
||||
|
||||
public interface IMicroControllerContext;
|
||||
@@ -1,3 +0,0 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public interface IMicroControllerModule;
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public interface IMicroControllerModuleDescriptor<TModule> :
|
||||
IMicroControllerModuleDescriptor
|
||||
where TModule : IMicroControllerModule
|
||||
{
|
||||
Func<TModule>? Factory { get; }
|
||||
}
|
||||
|
||||
public interface IMicroControllerModuleDescriptor
|
||||
{
|
||||
Type Type { get; }
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public interface ISerialContext<TReader, TRead> :
|
||||
public interface ISerialContext<TReader, TRead, TEvent> :
|
||||
ISerialContext
|
||||
where TReader : SerialReader<TRead>;
|
||||
where TReader : SerialReader<TRead>
|
||||
where TEvent : SerialEventArgs<TRead>, new();
|
||||
|
||||
|
||||
public interface ISerialContext
|
||||
{
|
||||
void Open();
|
||||
bool Open();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public interface ISerialContextFactory
|
||||
{
|
||||
ISerialContext<TReader, TRead, TEvent>? Create<TConfiguration, TReader, TRead, TEvent>()
|
||||
where TConfiguration : ISerialConfiguration
|
||||
where TReader : SerialReader<TRead>
|
||||
where TEvent : SerialEventArgs<TRead>, new();
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public interface ISerialEventArgs<TRead>;
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public interface ISerialFactory
|
||||
{
|
||||
ISerialContext<TReader, TRead>? Create<TConfiguration, TReader, TRead>()
|
||||
where TConfiguration : ISerialConfiguration
|
||||
where TReader : SerialReader<TRead>;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public interface ISerialResponse
|
||||
{
|
||||
ISerialContext Context { get; }
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public class MicroControllerContextFactory(IServiceProvider provider,
|
||||
IServiceFactory factory,
|
||||
ISerialFactory serialFactory) :
|
||||
IMicroControllerContextFactory
|
||||
{
|
||||
private readonly Dictionary<ISerialConfiguration, IMicroControllerContext> cache = [];
|
||||
|
||||
public IMicroControllerContext<TRead, THandler>? Create<TConfiguration, TReader, TRead, THandler>(IReadOnlyCollection<IMicroControllerModuleDescriptor> modules)
|
||||
where TConfiguration : ISerialConfiguration
|
||||
where TReader : SerialReader<TRead>
|
||||
where THandler : ISerialEventArgs<TRead>
|
||||
{
|
||||
if (provider.GetRequiredService<TConfiguration>() is TConfiguration configuration)
|
||||
{
|
||||
if (cache.TryGetValue(configuration, out IMicroControllerContext? context))
|
||||
{
|
||||
return (IMicroControllerContext<TRead, THandler>)context;
|
||||
}
|
||||
|
||||
if (serialFactory.Create<TConfiguration, TReader, TRead>() is ISerialContext<TReader, TRead> serialContext)
|
||||
{
|
||||
context = factory.Create<MicroControllerContext<TRead, THandler>>(modules, serialContext);
|
||||
cache.Add(configuration, context);
|
||||
|
||||
return (IMicroControllerContext<TRead, THandler>)context;
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public class MicroControllerBuilder :
|
||||
IMicroControllerBuilder
|
||||
{
|
||||
private readonly List<IMicroControllerBuilderConfiguration> configurations = [];
|
||||
|
||||
public IReadOnlyCollection<IMicroControllerBuilderConfiguration> Configurations =>
|
||||
new ReadOnlyCollection<IMicroControllerBuilderConfiguration>(configurations);
|
||||
|
||||
public IMicroControllerBuilderConfiguration<TConfiguration, TReader, TRead, TEvent> Add<TConfiguration, TReader, TRead, TEvent>()
|
||||
where TConfiguration : ISerialConfiguration
|
||||
where TReader : SerialReader<TRead>
|
||||
where TEvent : ISerialEventArgs<TRead>
|
||||
{
|
||||
MicroControllerBuilderConfiguration<TConfiguration, TReader, TRead, TEvent>? builderConfiguration = new();
|
||||
configurations.Add(builderConfiguration);
|
||||
|
||||
return builderConfiguration;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public class MicroControllerBuilderConfiguration<TConfiguration, TReader, TRead, TEvent> :
|
||||
IMicroControllerBuilderConfiguration<TConfiguration, TReader, TRead, TEvent>
|
||||
where TConfiguration : ISerialConfiguration
|
||||
where TReader : SerialReader<TRead>
|
||||
where TEvent : ISerialEventArgs<TRead>
|
||||
{
|
||||
private readonly List<IMicroControllerModuleDescriptor> modules = [];
|
||||
|
||||
public Func<IServiceProvider, IMicroControllerContext?> Factory => (IServiceProvider provider) => provider.GetService<IMicroControllerContextFactory>()!
|
||||
.Create<TConfiguration, TReader, TRead, TEvent>(Modules);
|
||||
|
||||
public IReadOnlyCollection<IMicroControllerModuleDescriptor> Modules => new ReadOnlyCollection<IMicroControllerModuleDescriptor>(modules);
|
||||
|
||||
public IMicroControllerBuilderConfiguration<TConfiguration, TReader, TRead, TEvent> AddModule<TModule>()
|
||||
where TModule : IMicroControllerModule, new()
|
||||
{
|
||||
modules.Add(new MicroControllerModuleDescriptor<TModule>());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public class MicroControllerConfiguration :
|
||||
ISerialConfiguration
|
||||
{
|
||||
[NotNull]
|
||||
public string? PortName { get; set; }
|
||||
|
||||
public int BaudRate { get; set; }
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public class MicroControllerContext<TRead, TEvent>(IReadOnlyCollection<IMicroControllerModuleDescriptor> modules,
|
||||
IMessenger messenger,
|
||||
ISerialContext serialContext) :
|
||||
IMicroControllerContext<TRead, TEvent>
|
||||
where TEvent : ISerialEventArgs<TRead>
|
||||
{
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
//eventAggregator.Subscribe<SerialResponse<TRead>>(OnEvent, null, args => args.Context.Equals(serialContext));
|
||||
serialContext.Open();
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async void OnEvent(SerialResponse<TRead> args)
|
||||
{
|
||||
//IMicrocontrollerModule? module = await messenger.SendAsync<IMicrocontrollerModule>(new TReadDeserializer { Read = args.Content }, modules);
|
||||
//messenger.Send((dynamic?)module);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public record MicroControllerModuleDescriptor<TModule> :
|
||||
IMicroControllerModuleDescriptor<TModule> where TModule : IMicroControllerModule, new()
|
||||
{
|
||||
public Type Type => typeof(TModule);
|
||||
|
||||
public Func<TModule>? Factory => new(() => new TModule());
|
||||
}
|
||||
@@ -2,24 +2,42 @@
|
||||
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public class SerialContext<TSerialReader, TContent>(IMessenger messenger,
|
||||
public class SerialContext<TReader, TValue, TEvent>(IMessenger messenger,
|
||||
ISerialConnection connection,
|
||||
ISerialStreamer serialStreamer) :
|
||||
ISerialContext<TSerialReader, TContent> where TSerialReader : SerialReader<TContent>
|
||||
ISerialContext<TReader, TValue, TEvent>
|
||||
where TReader : SerialReader<TValue>
|
||||
where TEvent : SerialEventArgs<TValue>, new()
|
||||
{
|
||||
public async void Open()
|
||||
{
|
||||
if (connection.Open())
|
||||
{
|
||||
Stream stream = serialStreamer.Create();
|
||||
public bool IsOpen { get; private set; }
|
||||
|
||||
if ((TSerialReader?)Activator.CreateInstance(typeof(TSerialReader), [stream]) is TSerialReader reader)
|
||||
public bool Open()
|
||||
{
|
||||
if (!connection.Open())
|
||||
return false;
|
||||
|
||||
IsOpen = true;
|
||||
|
||||
_ = ReadAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task ReadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await using Stream stream = serialStreamer.Create();
|
||||
if (Activator.CreateInstance(typeof(TReader), [stream]) is TReader reader)
|
||||
{
|
||||
await foreach (TContent content in reader.ReadAsync())
|
||||
await foreach (TValue value in reader.ReadAsync())
|
||||
{
|
||||
messenger.Send(SerialResponse.Create(this, content));
|
||||
messenger.Send(new SerialEventArgs<TValue> { Value = value });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
IsOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,35 +3,35 @@ using System.IO.Ports;
|
||||
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public class SerialFactory(IServiceProvider provider,
|
||||
public class SerialContextFactory(IServiceProvider provider,
|
||||
IServiceFactory factory) :
|
||||
ISerialFactory
|
||||
ISerialContextFactory
|
||||
{
|
||||
private readonly Dictionary<ISerialConfiguration, ISerialContext> cache = [];
|
||||
|
||||
public ISerialContext<TReader, TRead>? Create<TConfiguration, TReader, TRead>()
|
||||
public ISerialContext<TReader, TValue, TEvent>? Create<TConfiguration, TReader, TValue, TEvent>()
|
||||
where TConfiguration : ISerialConfiguration
|
||||
where TReader : SerialReader<TRead>
|
||||
where TReader : SerialReader<TValue>
|
||||
where TEvent : SerialEventArgs<TValue>, new()
|
||||
{
|
||||
if (provider.GetRequiredService<TConfiguration>() is TConfiguration configuration)
|
||||
{
|
||||
if (cache.TryGetValue(configuration, out ISerialContext? context))
|
||||
{
|
||||
return (ISerialContext<TReader, TRead>)context;
|
||||
return (ISerialContext<TReader, TValue, TEvent>)context;
|
||||
}
|
||||
|
||||
SerialPort serialPort = new(configuration.PortName, configuration.BaudRate)
|
||||
{
|
||||
DtrEnable = true
|
||||
};
|
||||
SerialPort serialPort = new(configuration.PortName, configuration.BaudRate);
|
||||
serialPort.ReadTimeout = 500; // Prevents blocking if no data is available
|
||||
serialPort.WriteTimeout = 500;
|
||||
|
||||
SerialConnection connection = new(serialPort);
|
||||
SerialStreamer streamer = new(serialPort);
|
||||
|
||||
context = factory.Create<SerialContext<TReader, TRead>>(connection, streamer);
|
||||
context = factory.Create<SerialContext<TReader, TValue, TEvent>>(connection, streamer);
|
||||
cache.Add(configuration, context);
|
||||
|
||||
return (ISerialContext<TReader, TRead>)context;
|
||||
return (ISerialContext<TReader, TValue, TEvent>)context;
|
||||
}
|
||||
|
||||
return default;
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public record SerialEventArgs<TValue>
|
||||
{
|
||||
public TValue? Value { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public record SerialEventArgs(byte Type, short Value);
|
||||
@@ -0,0 +1,61 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -13,24 +13,31 @@ public class SerialLineReader(Stream stream) :
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ReadResult result = await reader.ReadAsync();
|
||||
ReadOnlySequence<byte> buffer = result.Buffer;
|
||||
ReadResult result;
|
||||
try
|
||||
{
|
||||
result = await reader.ReadAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.IsCompleted)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryReadLine(ref ReadOnlySequence<byte> buffer, out ReadOnlySequence<byte> line)
|
||||
private bool TryReadLine(ref ReadOnlySequence<byte> buffer,
|
||||
out ReadOnlySequence<byte> line)
|
||||
{
|
||||
SequencePosition? position = buffer.PositionOf((byte)'\n');
|
||||
if (position == null)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public abstract class SerialReader<TRead>(Stream stream)
|
||||
public abstract class SerialReader<TValue>(Stream stream)
|
||||
{
|
||||
public Stream Stream { get; } = stream;
|
||||
|
||||
public abstract IAsyncEnumerable<TRead> ReadAsync();
|
||||
public abstract IAsyncEnumerable<TValue> ReadAsync();
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
namespace Toolkit.Foundation;
|
||||
|
||||
public record SerialResponse<TContent> :
|
||||
ISerialResponse
|
||||
{
|
||||
public SerialResponse(ISerialContext context,
|
||||
TContent content)
|
||||
{
|
||||
Context = context;
|
||||
Content = content;
|
||||
}
|
||||
|
||||
public ISerialContext Context { get; }
|
||||
|
||||
public TContent Content { get; }
|
||||
}
|
||||
|
||||
public record SerialResponse
|
||||
{
|
||||
public static SerialResponse<TContent> Create<TContent>(ISerialContext context, TContent content)
|
||||
{
|
||||
return new SerialResponse<TContent>(context, content);
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,5 @@ namespace Toolkit.Foundation;
|
||||
public class SerialStreamer(SerialPort serialPort) :
|
||||
ISerialStreamer
|
||||
{
|
||||
public Stream Create()
|
||||
{
|
||||
return serialPort.BaseStream;
|
||||
}
|
||||
public Stream Create() => serialPort.BaseStream;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user