diff --git a/Toolkit.Foundation/ActionableInitializationScoped.cs b/Toolkit.Foundation/ActionableInitializationScoped.cs index 27e27d0..23b529b 100644 --- a/Toolkit.Foundation/ActionableInitializationScoped.cs +++ b/Toolkit.Foundation/ActionableInitializationScoped.cs @@ -4,4 +4,4 @@ public class ActionableInitializationScoped(IServiceProvider provider, Action delegateAction) : IInitializationScoped { public void Initialize() => delegateAction.Invoke(provider); -} \ No newline at end of file +} diff --git a/Toolkit.Foundation/ISerialConfiguration.cs b/Toolkit.Foundation/ISerialConfiguration.cs new file mode 100644 index 0000000..02b1271 --- /dev/null +++ b/Toolkit.Foundation/ISerialConfiguration.cs @@ -0,0 +1,8 @@ +namespace Toolkit.Foundation; + +public interface ISerialConfiguration +{ + public string PortName { get; set; } + + public int BaudRate { get; set; } +} diff --git a/Toolkit.Foundation/ISerialConnection.cs b/Toolkit.Foundation/ISerialConnection.cs new file mode 100644 index 0000000..827e433 --- /dev/null +++ b/Toolkit.Foundation/ISerialConnection.cs @@ -0,0 +1,10 @@ +namespace Toolkit.Foundation; + +public interface ISerialConnection +{ + bool IsOpen { get; } + + void Close(); + + bool Open(); +} diff --git a/Toolkit.Foundation/ISerialConnectionStreamer.cs b/Toolkit.Foundation/ISerialConnectionStreamer.cs new file mode 100644 index 0000000..209483d --- /dev/null +++ b/Toolkit.Foundation/ISerialConnectionStreamer.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public interface ISerialConnectionStreamer +{ + Stream Stream { get; } +} diff --git a/Toolkit.Foundation/ISerialContext.cs b/Toolkit.Foundation/ISerialContext.cs new file mode 100644 index 0000000..2f21b7a --- /dev/null +++ b/Toolkit.Foundation/ISerialContext.cs @@ -0,0 +1,7 @@ +namespace Toolkit.Foundation; + +public interface ISerialContext : + ISerialContext where TSerialReader : SerialReader +{ + +} \ No newline at end of file diff --git a/Toolkit.Foundation/ISerialContext1.cs b/Toolkit.Foundation/ISerialContext1.cs new file mode 100644 index 0000000..8271f2f --- /dev/null +++ b/Toolkit.Foundation/ISerialContext1.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public interface ISerialContext +{ + void Open(); +} diff --git a/Toolkit.Foundation/ISerialFactory.cs b/Toolkit.Foundation/ISerialFactory.cs new file mode 100644 index 0000000..dea9357 --- /dev/null +++ b/Toolkit.Foundation/ISerialFactory.cs @@ -0,0 +1,7 @@ +namespace Toolkit.Foundation; + +public interface ISerialFactory +{ + ISerialContext Create(ISerialConfiguration configuration) + where TSerialReader : SerialReader; +} diff --git a/Toolkit.Foundation/ISerialResponse.cs b/Toolkit.Foundation/ISerialResponse.cs new file mode 100644 index 0000000..039e1e1 --- /dev/null +++ b/Toolkit.Foundation/ISerialResponse.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public interface ISerialResponse +{ + ISerialContext Context { get; } +} \ No newline at end of file diff --git a/Toolkit.Foundation/ISerialStreamer.cs b/Toolkit.Foundation/ISerialStreamer.cs new file mode 100644 index 0000000..995175e --- /dev/null +++ b/Toolkit.Foundation/ISerialStreamer.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public interface ISerialStreamer +{ + Stream Create(); +} diff --git a/Toolkit.Foundation/ISerialWriter.cs b/Toolkit.Foundation/ISerialWriter.cs new file mode 100644 index 0000000..55a7719 --- /dev/null +++ b/Toolkit.Foundation/ISerialWriter.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public interface ISerialWriter +{ + void Write(byte[] buffer, int offset, int count); +} diff --git a/Toolkit.Foundation/SerialConfiguration.cs b/Toolkit.Foundation/SerialConfiguration.cs new file mode 100644 index 0000000..c58dcea --- /dev/null +++ b/Toolkit.Foundation/SerialConfiguration.cs @@ -0,0 +1,12 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Toolkit.Foundation; + +public class SerialConfiguration : + ISerialConfiguration +{ + [NotNull] + public string? PortName { get; set; } + + public int BaudRate { get; set; } +} diff --git a/Toolkit.Foundation/SerialConnection.cs b/Toolkit.Foundation/SerialConnection.cs new file mode 100644 index 0000000..d4e22e7 --- /dev/null +++ b/Toolkit.Foundation/SerialConnection.cs @@ -0,0 +1,48 @@ +using System.IO.Ports; + +namespace Toolkit.Foundation; + +public class SerialConnection(SerialPort serialPort) : + ISerialConnection +{ + 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; + } +} diff --git a/Toolkit.Foundation/SerialContext.cs b/Toolkit.Foundation/SerialContext.cs new file mode 100644 index 0000000..822aafd --- /dev/null +++ b/Toolkit.Foundation/SerialContext.cs @@ -0,0 +1,25 @@ +using CommunityToolkit.Mvvm.Messaging; + +namespace Toolkit.Foundation; + +public class SerialContext(IMessenger messenger, + ISerialConnection connection, + ISerialStreamer serialStreamer) : + ISerialContext where TSerialReader : SerialReader +{ + public async void Open() + { + if (connection.Open()) + { + Stream stream = serialStreamer.Create(); + + if ((TSerialReader?)Activator.CreateInstance(typeof(TSerialReader), [stream]) is TSerialReader reader) + { + await foreach (TContent content in reader.ReadAsync()) + { + messenger.Send(SerialResponse.Create(this, content)); + } + } + } + } +} \ No newline at end of file diff --git a/Toolkit.Foundation/SerialFactory.cs b/Toolkit.Foundation/SerialFactory.cs new file mode 100644 index 0000000..81e0c57 --- /dev/null +++ b/Toolkit.Foundation/SerialFactory.cs @@ -0,0 +1,31 @@ +using System.IO.Ports; + +namespace Toolkit.Foundation; + +public class SerialFactory(IServiceFactory factory) : + ISerialFactory +{ + private readonly Dictionary cache = []; + + public ISerialContext Create(ISerialConfiguration configuration) + where TSerialReader : SerialReader + { + if (cache.TryGetValue(configuration, out ISerialContext? context)) + { + return (ISerialContext)context; + } + + SerialPort serialPort = new(configuration.PortName, configuration.BaudRate) + { + DtrEnable = true + }; + + SerialConnection connection = new(serialPort); + SerialStreamer streamer = new(serialPort); + + context = factory.Create>(connection, streamer); + cache.Add(configuration, context); + + return (ISerialContext)context; + } +} diff --git a/Toolkit.Foundation/SerialLineReader.cs b/Toolkit.Foundation/SerialLineReader.cs new file mode 100644 index 0000000..ee7f697 --- /dev/null +++ b/Toolkit.Foundation/SerialLineReader.cs @@ -0,0 +1,46 @@ +using System.Buffers; +using System.IO.Pipelines; +using System.Text; + +namespace Toolkit.Foundation; + +public class SerialLineReader(Stream stream) : + SerialReader(stream) +{ + private readonly PipeReader reader = PipeReader.Create(stream); + + public override async IAsyncEnumerable ReadAsync() + { + while (true) + { + ReadResult result = await reader.ReadAsync(); + 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; + } + } + + } + + private bool TryReadLine(ref ReadOnlySequence buffer, out ReadOnlySequence 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; + } +} \ No newline at end of file diff --git a/Toolkit.Foundation/SerialReader.cs b/Toolkit.Foundation/SerialReader.cs new file mode 100644 index 0000000..4021b2a --- /dev/null +++ b/Toolkit.Foundation/SerialReader.cs @@ -0,0 +1,8 @@ +namespace Toolkit.Foundation; + +public abstract class SerialReader(Stream stream) +{ + public Stream Stream { get; } = stream; + + public abstract IAsyncEnumerable ReadAsync(); +} diff --git a/Toolkit.Foundation/SerialResponse.cs b/Toolkit.Foundation/SerialResponse.cs new file mode 100644 index 0000000..b00996b --- /dev/null +++ b/Toolkit.Foundation/SerialResponse.cs @@ -0,0 +1,24 @@ +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 new file mode 100644 index 0000000..608f7c5 --- /dev/null +++ b/Toolkit.Foundation/SerialStreamer.cs @@ -0,0 +1,12 @@ +using System.IO.Ports; + +namespace Toolkit.Foundation; + +public class SerialStreamer(SerialPort serialPort) : + ISerialStreamer +{ + public Stream Create() + { + return serialPort.BaseStream; + } +} diff --git a/Toolkit.Foundation/Toolkit.Foundation.csproj b/Toolkit.Foundation/Toolkit.Foundation.csproj index 8c0a477..8d657e7 100644 --- a/Toolkit.Foundation/Toolkit.Foundation.csproj +++ b/Toolkit.Foundation/Toolkit.Foundation.csproj @@ -9,6 +9,7 @@ +