diff --git a/Toolkit.Foundation/IHostBuilderExtension.cs b/Toolkit.Foundation/IHostBuilderExtension.cs index 80ea01a..bf56456 100644 --- a/Toolkit.Foundation/IHostBuilderExtension.cs +++ b/Toolkit.Foundation/IHostBuilderExtension.cs @@ -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 builderDelegate) + public static IHostBuilder AddSerial(this IHostBuilder hostBuilder) + where TConfiguration : ISerialConfiguration + where TReader : SerialReader + where TEvent : SerialEventArgs, new() { hostBuilder.ConfigureServices((hostBuilderContext, serviceCollection) => { - MicroControllerBuilder? builder = new(); - - builderDelegate.Invoke(builder); - serviceCollection.TryAddSingleton(); - serviceCollection.TryAddSingleton(); - - foreach (IMicroControllerBuilderConfiguration configuration in builder.Configurations) - { - serviceCollection.AddSingleton(provider => configuration.Factory.Invoke(provider) ?? throw new NullReferenceException()); - } + serviceCollection.TryAddSingleton(); + serviceCollection.AddSingleton>(provider => provider.GetRequiredService().Create() + ?? throw new NullReferenceException()); }); return hostBuilder; } + //public static IHostBuilder ConfigureSerials(this IHostBuilder hostBuilder, Action builderDelegate) + //{ + // hostBuilder.ConfigureServices((hostBuilderContext, serviceCollection) => + // { + // SerialBuilder? builder = new(); + + // builderDelegate.Invoke(builder); + // serviceCollection.TryAddSingleton(); + + // 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) diff --git a/Toolkit.Foundation/IMicroControllerContextFactory.cs b/Toolkit.Foundation/IMicroControllerContextFactory.cs deleted file mode 100644 index 31ad49f..0000000 --- a/Toolkit.Foundation/IMicroControllerContextFactory.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Toolkit.Foundation; - -public interface IMicroControllerContextFactory -{ - IMicroControllerContext? Create(IReadOnlyCollection modules) - where TConfiguration : ISerialConfiguration - where TReader : SerialReader - where TEvent : ISerialEventArgs; -} diff --git a/Toolkit.Foundation/IMicrocontrollerBuilder.cs b/Toolkit.Foundation/IMicrocontrollerBuilder.cs deleted file mode 100644 index 75bc655..0000000 --- a/Toolkit.Foundation/IMicrocontrollerBuilder.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Toolkit.Foundation; - -public interface IMicroControllerBuilder -{ - IReadOnlyCollection Configurations { get; } - - IMicroControllerBuilderConfiguration Add() - where TConfiguration : ISerialConfiguration - where TReader : SerialReader - where TEvent : ISerialEventArgs; -} diff --git a/Toolkit.Foundation/IMicrocontrollerBuilderConfiguration.cs b/Toolkit.Foundation/IMicrocontrollerBuilderConfiguration.cs deleted file mode 100644 index e87e488..0000000 --- a/Toolkit.Foundation/IMicrocontrollerBuilderConfiguration.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Toolkit.Foundation; - -public interface IMicroControllerBuilderConfiguration -{ - IReadOnlyCollection Modules { get; } - - Func Factory { get; } -} - -public interface IMicroControllerBuilderConfiguration : - IMicroControllerBuilderConfiguration - where TConfiguration : ISerialConfiguration - where TReader : SerialReader - where IEvent : ISerialEventArgs -{ - - IMicroControllerBuilderConfiguration AddModule() - where TModule : IMicroControllerModule, new(); -} diff --git a/Toolkit.Foundation/IMicrocontrollerContext.cs b/Toolkit.Foundation/IMicrocontrollerContext.cs deleted file mode 100644 index 0990f1e..0000000 --- a/Toolkit.Foundation/IMicrocontrollerContext.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Toolkit.Foundation; - -public interface IMicroControllerContext : - IMicroControllerContext - where TEvent : ISerialEventArgs; - -public interface IMicroControllerContext; \ No newline at end of file diff --git a/Toolkit.Foundation/IMicrocontrollerModule.cs b/Toolkit.Foundation/IMicrocontrollerModule.cs deleted file mode 100644 index d04fad2..0000000 --- a/Toolkit.Foundation/IMicrocontrollerModule.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Toolkit.Foundation; - -public interface IMicroControllerModule; \ No newline at end of file diff --git a/Toolkit.Foundation/IMicrocontrollerModuleDescriptor.cs b/Toolkit.Foundation/IMicrocontrollerModuleDescriptor.cs deleted file mode 100644 index a63155d..0000000 --- a/Toolkit.Foundation/IMicrocontrollerModuleDescriptor.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Toolkit.Foundation; - -public interface IMicroControllerModuleDescriptor : - IMicroControllerModuleDescriptor - where TModule : IMicroControllerModule -{ - Func? Factory { get; } -} - -public interface IMicroControllerModuleDescriptor -{ - Type Type { get; } -} diff --git a/Toolkit.Foundation/ISerialContext.cs b/Toolkit.Foundation/ISerialContext.cs index 46d01d8..ff352b2 100644 --- a/Toolkit.Foundation/ISerialContext.cs +++ b/Toolkit.Foundation/ISerialContext.cs @@ -1,10 +1,12 @@ namespace Toolkit.Foundation; -public interface ISerialContext : +public interface ISerialContext : ISerialContext - where TReader : SerialReader; + where TReader : SerialReader + where TEvent : SerialEventArgs, new(); + public interface ISerialContext { - void Open(); + bool Open(); } diff --git a/Toolkit.Foundation/ISerialContextFactory.cs b/Toolkit.Foundation/ISerialContextFactory.cs new file mode 100644 index 0000000..bce407d --- /dev/null +++ b/Toolkit.Foundation/ISerialContextFactory.cs @@ -0,0 +1,9 @@ +namespace Toolkit.Foundation; + +public interface ISerialContextFactory +{ + ISerialContext? Create() + where TConfiguration : ISerialConfiguration + where TReader : SerialReader + where TEvent : SerialEventArgs, new(); +} diff --git a/Toolkit.Foundation/ISerialEventArgs.cs b/Toolkit.Foundation/ISerialEventArgs.cs deleted file mode 100644 index 0512557..0000000 --- a/Toolkit.Foundation/ISerialEventArgs.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Toolkit.Foundation; - -public interface ISerialEventArgs; \ No newline at end of file diff --git a/Toolkit.Foundation/ISerialFactory.cs b/Toolkit.Foundation/ISerialFactory.cs deleted file mode 100644 index 50a3bd3..0000000 --- a/Toolkit.Foundation/ISerialFactory.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Toolkit.Foundation; - -public interface ISerialFactory -{ - ISerialContext? Create() - where TConfiguration : ISerialConfiguration - where TReader : SerialReader; -} diff --git a/Toolkit.Foundation/ISerialResponse.cs b/Toolkit.Foundation/ISerialResponse.cs deleted file mode 100644 index 039e1e1..0000000 --- a/Toolkit.Foundation/ISerialResponse.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Toolkit.Foundation; - -public interface ISerialResponse -{ - ISerialContext Context { get; } -} \ No newline at end of file diff --git a/Toolkit.Foundation/MicroControllerContextFactory.cs b/Toolkit.Foundation/MicroControllerContextFactory.cs deleted file mode 100644 index a15c3b5..0000000 --- a/Toolkit.Foundation/MicroControllerContextFactory.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace Toolkit.Foundation; - -public class MicroControllerContextFactory(IServiceProvider provider, - IServiceFactory factory, - ISerialFactory serialFactory) : - IMicroControllerContextFactory -{ - private readonly Dictionary cache = []; - - public IMicroControllerContext? Create(IReadOnlyCollection modules) - where TConfiguration : ISerialConfiguration - where TReader : SerialReader - where THandler : ISerialEventArgs - { - if (provider.GetRequiredService() is TConfiguration configuration) - { - if (cache.TryGetValue(configuration, out IMicroControllerContext? context)) - { - return (IMicroControllerContext)context; - } - - if (serialFactory.Create() is ISerialContext serialContext) - { - context = factory.Create>(modules, serialContext); - cache.Add(configuration, context); - - return (IMicroControllerContext)context; - } - } - - return default; - } -} diff --git a/Toolkit.Foundation/MicrocontrollerBuilder.cs b/Toolkit.Foundation/MicrocontrollerBuilder.cs deleted file mode 100644 index 3e461fa..0000000 --- a/Toolkit.Foundation/MicrocontrollerBuilder.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.ObjectModel; - -namespace Toolkit.Foundation; - -public class MicroControllerBuilder : - IMicroControllerBuilder -{ - private readonly List configurations = []; - - public IReadOnlyCollection Configurations => - new ReadOnlyCollection(configurations); - - public IMicroControllerBuilderConfiguration Add() - where TConfiguration : ISerialConfiguration - where TReader : SerialReader - where TEvent : ISerialEventArgs - { - MicroControllerBuilderConfiguration? builderConfiguration = new(); - configurations.Add(builderConfiguration); - - return builderConfiguration; - } -} \ No newline at end of file diff --git a/Toolkit.Foundation/MicrocontrollerBuilderConfiguration.cs b/Toolkit.Foundation/MicrocontrollerBuilderConfiguration.cs deleted file mode 100644 index a5d24c7..0000000 --- a/Toolkit.Foundation/MicrocontrollerBuilderConfiguration.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using System.Collections.ObjectModel; - -namespace Toolkit.Foundation; - -public class MicroControllerBuilderConfiguration : - IMicroControllerBuilderConfiguration - where TConfiguration : ISerialConfiguration - where TReader : SerialReader - where TEvent : ISerialEventArgs -{ - private readonly List modules = []; - - public Func Factory => (IServiceProvider provider) => provider.GetService()! - .Create(Modules); - - public IReadOnlyCollection Modules => new ReadOnlyCollection(modules); - - public IMicroControllerBuilderConfiguration AddModule() - where TModule : IMicroControllerModule, new() - { - modules.Add(new MicroControllerModuleDescriptor()); - return this; - } -} \ No newline at end of file diff --git a/Toolkit.Foundation/MicrocontrollerConfiguration.cs b/Toolkit.Foundation/MicrocontrollerConfiguration.cs deleted file mode 100644 index d4413b9..0000000 --- a/Toolkit.Foundation/MicrocontrollerConfiguration.cs +++ /dev/null @@ -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; } -} \ No newline at end of file diff --git a/Toolkit.Foundation/MicrocontrollerContext.cs b/Toolkit.Foundation/MicrocontrollerContext.cs deleted file mode 100644 index f7818a5..0000000 --- a/Toolkit.Foundation/MicrocontrollerContext.cs +++ /dev/null @@ -1,24 +0,0 @@ -using CommunityToolkit.Mvvm.Messaging; - -namespace Toolkit.Foundation; - -public class MicroControllerContext(IReadOnlyCollection modules, - IMessenger messenger, - ISerialContext serialContext) : - IMicroControllerContext - where TEvent : ISerialEventArgs -{ - public async Task InitializeAsync() - { - //eventAggregator.Subscribe>(OnEvent, null, args => args.Context.Equals(serialContext)); - serialContext.Open(); - - await Task.CompletedTask; - } - - private async void OnEvent(SerialResponse args) - { - //IMicrocontrollerModule? module = await messenger.SendAsync(new TReadDeserializer { Read = args.Content }, modules); - //messenger.Send((dynamic?)module); - } -} \ No newline at end of file diff --git a/Toolkit.Foundation/MicrocontrollerModuleDescriptor.cs b/Toolkit.Foundation/MicrocontrollerModuleDescriptor.cs deleted file mode 100644 index c154e8b..0000000 --- a/Toolkit.Foundation/MicrocontrollerModuleDescriptor.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Toolkit.Foundation; - -public record MicroControllerModuleDescriptor : - IMicroControllerModuleDescriptor where TModule : IMicroControllerModule, new() -{ - public Type Type => typeof(TModule); - - public Func? Factory => new(() => new TModule()); -} diff --git a/Toolkit.Foundation/SerialContext.cs b/Toolkit.Foundation/SerialContext.cs index 94735aa..ce67c41 100644 --- a/Toolkit.Foundation/SerialContext.cs +++ b/Toolkit.Foundation/SerialContext.cs @@ -2,24 +2,42 @@ namespace Toolkit.Foundation; -public class SerialContext(IMessenger messenger, +public class SerialContext(IMessenger messenger, ISerialConnection connection, ISerialStreamer serialStreamer) : - ISerialContext where TSerialReader : SerialReader + ISerialContext + where TReader : SerialReader + where TEvent : SerialEventArgs, 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 { Value = value }); } } } + catch + { + IsOpen = false; + } } } diff --git a/Toolkit.Foundation/SerialFactory.cs b/Toolkit.Foundation/SerialContextFactory.cs similarity index 52% rename from Toolkit.Foundation/SerialFactory.cs rename to Toolkit.Foundation/SerialContextFactory.cs index aad2a74..bf6c966 100644 --- a/Toolkit.Foundation/SerialFactory.cs +++ b/Toolkit.Foundation/SerialContextFactory.cs @@ -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 cache = []; - public ISerialContext? Create() + public ISerialContext? Create() where TConfiguration : ISerialConfiguration - where TReader : SerialReader + where TReader : SerialReader + where TEvent : SerialEventArgs, new() { if (provider.GetRequiredService() is TConfiguration configuration) { if (cache.TryGetValue(configuration, out ISerialContext? context)) { - return (ISerialContext)context; + return (ISerialContext)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>(connection, streamer); + context = factory.Create>(connection, streamer); cache.Add(configuration, context); - return (ISerialContext)context; + return (ISerialContext)context; } return default; diff --git a/Toolkit.Foundation/SerialEventArgs.cs b/Toolkit.Foundation/SerialEventArgs.cs new file mode 100644 index 0000000..0add57a --- /dev/null +++ b/Toolkit.Foundation/SerialEventArgs.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public record SerialEventArgs +{ + public TValue? Value { get; init; } +} \ No newline at end of file diff --git a/Toolkit.Foundation/SerialEventArgs1.cs b/Toolkit.Foundation/SerialEventArgs1.cs new file mode 100644 index 0000000..2dd65af --- /dev/null +++ b/Toolkit.Foundation/SerialEventArgs1.cs @@ -0,0 +1,3 @@ +namespace Toolkit.Foundation; + +public record SerialEventArgs(byte Type, short Value); diff --git a/Toolkit.Foundation/SerialEventReader.cs b/Toolkit.Foundation/SerialEventReader.cs new file mode 100644 index 0000000..55a609f --- /dev/null +++ b/Toolkit.Foundation/SerialEventReader.cs @@ -0,0 +1,61 @@ +using System.Buffers; +using System.IO.Pipelines; + +namespace Toolkit.Foundation; + +public class SerialEventReader(Stream stream) : + SerialReader(stream) +{ + private readonly PipeReader reader = PipeReader.Create(stream); + + public override async IAsyncEnumerable ReadAsync() + { + while (true) + { + ReadResult result; + try + { + result = await reader.ReadAsync(); + } + catch (Exception) + { + continue; + } + + ReadOnlySequence 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 buffer, + out SerialEventArgs serialEvent) + { + SequenceReader 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; + } +} diff --git a/Toolkit.Foundation/SerialLineReader.cs b/Toolkit.Foundation/SerialLineReader.cs index ee7f697..f88d4f4 100644 --- a/Toolkit.Foundation/SerialLineReader.cs +++ b/Toolkit.Foundation/SerialLineReader.cs @@ -4,7 +4,7 @@ using System.Text; namespace Toolkit.Foundation; -public class SerialLineReader(Stream stream) : +public class SerialLineReader(Stream stream) : SerialReader(stream) { private readonly PipeReader reader = PipeReader.Create(stream); @@ -13,24 +13,31 @@ public class SerialLineReader(Stream stream) : { while (true) { - ReadResult result = await reader.ReadAsync(); - ReadOnlySequence buffer = result.Buffer; + ReadResult result; + try + { + result = await reader.ReadAsync(); + } + catch + { + continue; + } + ReadOnlySequence buffer = result.Buffer; while (TryReadLine(ref buffer, out ReadOnlySequence 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 buffer, out ReadOnlySequence line) + private bool TryReadLine(ref ReadOnlySequence buffer, + out ReadOnlySequence line) { SequencePosition? position = buffer.PositionOf((byte)'\n'); if (position == null) @@ -43,4 +50,4 @@ public class SerialLineReader(Stream stream) : buffer = buffer.Slice(buffer.GetPosition(1, position.Value)); return true; } -} \ No newline at end of file +} diff --git a/Toolkit.Foundation/SerialReader.cs b/Toolkit.Foundation/SerialReader.cs index 2ad3e4e..8491e2f 100644 --- a/Toolkit.Foundation/SerialReader.cs +++ b/Toolkit.Foundation/SerialReader.cs @@ -1,8 +1,8 @@ namespace Toolkit.Foundation; -public abstract class SerialReader(Stream stream) +public abstract class SerialReader(Stream stream) { public Stream Stream { get; } = stream; - public abstract IAsyncEnumerable ReadAsync(); + public abstract IAsyncEnumerable ReadAsync(); } diff --git a/Toolkit.Foundation/SerialResponse.cs b/Toolkit.Foundation/SerialResponse.cs deleted file mode 100644 index b00996b..0000000 --- a/Toolkit.Foundation/SerialResponse.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Toolkit.Foundation; - -public record SerialResponse : - 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 Create(ISerialContext context, TContent content) - { - return new SerialResponse(context, content); - } -} diff --git a/Toolkit.Foundation/SerialStreamer.cs b/Toolkit.Foundation/SerialStreamer.cs index 608f7c5..40e183c 100644 --- a/Toolkit.Foundation/SerialStreamer.cs +++ b/Toolkit.Foundation/SerialStreamer.cs @@ -5,8 +5,5 @@ namespace Toolkit.Foundation; public class SerialStreamer(SerialPort serialPort) : ISerialStreamer { - public Stream Create() - { - return serialPort.BaseStream; - } + public Stream Create() => serialPort.BaseStream; }