From 0b36f17997a2cc4d7cdfe32cfbb0173cf42dac5b Mon Sep 17 00:00:00 2001 From: Jonas Arnold Date: Wed, 12 Apr 2023 10:37:08 +0200 Subject: [PATCH] implemented CommunicationProtocolFactory, added to App, implemented ProtocolSettings and ProtocolSettingsViewModel, extended CommunicationProtocol by settings and ProtocolType --- .../Factories/ITerminalViewModelFactory.cs | 9 ++- .../Factories/TerminalViewModelFactory.cs | 10 ++- .../ViewModel/ITerminalViewModel.cs | 2 +- MultiTerm.Core/ViewModel/TerminalViewModel.cs | 2 +- MultiTerm.Protocols/CommunicationProtocol.cs | 11 +--- .../Factories/CommunicationProtocolFactory.cs | 64 +++++++++++++++++++ .../ICommunicationProtocolFactory.cs | 8 +++ .../Helpers/ServiceExtensions.cs | 34 ++++++++++ MultiTerm.Protocols/ICommunicationProtocol.cs | 17 ++--- MultiTerm.Protocols/IProtocolSettings.cs | 19 ++++++ .../IProtocolSettingsViewModel.cs | 21 ++++++ .../ProtocolConnectionSettingsViewModel.cs | 40 ------------ .../ProtocolSettingsViewModel.cs | 61 ++++++++++++++++++ .../Serial/ISerialProtocolSettings.cs | 7 ++ MultiTerm.Protocols/Serial/SerialProtocol.cs | 34 ++++++++++ .../Serial/SerialProtocolSettingsViewModel.cs | 23 +++++++ MultiTerm.Wpf/App.xaml.cs | 2 + 17 files changed, 298 insertions(+), 66 deletions(-) create mode 100644 MultiTerm.Protocols/Factories/CommunicationProtocolFactory.cs create mode 100644 MultiTerm.Protocols/Factories/ICommunicationProtocolFactory.cs create mode 100644 MultiTerm.Protocols/Helpers/ServiceExtensions.cs create mode 100644 MultiTerm.Protocols/IProtocolSettings.cs create mode 100644 MultiTerm.Protocols/IProtocolSettingsViewModel.cs delete mode 100644 MultiTerm.Protocols/ProtocolConnectionSettingsViewModel.cs create mode 100644 MultiTerm.Protocols/ProtocolSettingsViewModel.cs create mode 100644 MultiTerm.Protocols/Serial/ISerialProtocolSettings.cs create mode 100644 MultiTerm.Protocols/Serial/SerialProtocol.cs create mode 100644 MultiTerm.Protocols/Serial/SerialProtocolSettingsViewModel.cs diff --git a/MultiTerm.Core/Factories/ITerminalViewModelFactory.cs b/MultiTerm.Core/Factories/ITerminalViewModelFactory.cs index 2363afc..ae59138 100644 --- a/MultiTerm.Core/Factories/ITerminalViewModelFactory.cs +++ b/MultiTerm.Core/Factories/ITerminalViewModelFactory.cs @@ -2,10 +2,9 @@ using MultiTerm.Core.ViewModel; using MultiTerm.Protocols.Types; -namespace MultiTerm.Core.Factories +namespace MultiTerm.Core.Factories; + +public interface ITerminalViewModelFactory { - public interface ITerminalViewModelFactory - { - ITerminalViewModel Create(TerminalViewType viewType, ProtocolType protocolType); - } + ITerminalViewModel Create(TerminalViewType viewType, ProtocolType protocolType); } \ No newline at end of file diff --git a/MultiTerm.Core/Factories/TerminalViewModelFactory.cs b/MultiTerm.Core/Factories/TerminalViewModelFactory.cs index b9f6ef4..4d5fe18 100644 --- a/MultiTerm.Core/Factories/TerminalViewModelFactory.cs +++ b/MultiTerm.Core/Factories/TerminalViewModelFactory.cs @@ -1,5 +1,6 @@ using MultiTerm.Core.Types; using MultiTerm.Core.ViewModel; +using MultiTerm.Protocols.Factories; using MultiTerm.Protocols.Types; namespace MultiTerm.Core.Factories; @@ -10,14 +11,16 @@ namespace MultiTerm.Core.Factories; public class TerminalViewModelFactory : ITerminalViewModelFactory { private readonly Func> factory; + private readonly ICommunicationProtocolFactory communicationProtocolFactory; /// /// Constructor of . /// /// function that gets all registered services with Interface - public TerminalViewModelFactory(Func> factory) + public TerminalViewModelFactory(Func> factory, ICommunicationProtocolFactory communicationProtocolFactory) { this.factory = factory; + this.communicationProtocolFactory = communicationProtocolFactory; } /// @@ -46,6 +49,11 @@ public class TerminalViewModelFactory : ITerminalViewModelFactory // immediately initialize protocol type output.ProtocolType = protocolType; + // create communication protocol instances + var createdComProtocol = this.communicationProtocolFactory.Create(protocolType); + output.CommunicationProtocol = createdComProtocol.Item1; + output.ProtocolSettings = createdComProtocol.Item2; + // return the filled up instance return output; } diff --git a/MultiTerm.Core/ViewModel/ITerminalViewModel.cs b/MultiTerm.Core/ViewModel/ITerminalViewModel.cs index 55e3cb4..4f90d37 100644 --- a/MultiTerm.Core/ViewModel/ITerminalViewModel.cs +++ b/MultiTerm.Core/ViewModel/ITerminalViewModel.cs @@ -29,7 +29,7 @@ public interface ITerminalViewModel /// /// Settings ViewModel that displays protocol specific settings to user. /// - ProtocolSettingsViewModel? ProtocolSettings { get; set; } + IProtocolSettingsViewModel? ProtocolSettings { get; set; } /// /// Request Closing of Terminal. diff --git a/MultiTerm.Core/ViewModel/TerminalViewModel.cs b/MultiTerm.Core/ViewModel/TerminalViewModel.cs index a5b5bf3..7d52bf4 100644 --- a/MultiTerm.Core/ViewModel/TerminalViewModel.cs +++ b/MultiTerm.Core/ViewModel/TerminalViewModel.cs @@ -12,7 +12,7 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie public abstract TerminalViewType ViewType { get; } public ProtocolType ProtocolType { get; set; } public ICommunicationProtocol? CommunicationProtocol { get; set; } - public ProtocolSettingsViewModel? ProtocolSettings { get; set; } + public IProtocolSettingsViewModel? ProtocolSettings { get; set; } public event EventHandler? ClosingEvent; diff --git a/MultiTerm.Protocols/CommunicationProtocol.cs b/MultiTerm.Protocols/CommunicationProtocol.cs index 1e4c575..1e646da 100644 --- a/MultiTerm.Protocols/CommunicationProtocol.cs +++ b/MultiTerm.Protocols/CommunicationProtocol.cs @@ -1,27 +1,22 @@ using Common.Logging; +using MultiTerm.Protocols.Types; namespace MultiTerm.Protocols; public abstract class CommunicationProtocol : ICommunicationProtocol { - public string NewlineSequenceOnSend { get; set; } - public string NewlineOnReceivedSequence { get; set; } - public ProtocolConnectionSettingsViewModel? ConnectionSettings { get; set; } - protected ILogger logger; private CancellationTokenSource cancellationTokenSource; private Thread? readingThread; + public IProtocolSettings? Settings { get; set; } public event EventHandler? ReceivedDataEvent; public event EventHandler? SentDataEvent; + public abstract ProtocolType ProtocolType { get; } public CommunicationProtocol(ILogger logger) { - // initialize with empty string, must be correctly initalized by factory - this.NewlineSequenceOnSend = string.Empty; - this.NewlineOnReceivedSequence = string.Empty; - // initialize other this.cancellationTokenSource = new CancellationTokenSource(); this.logger = logger; diff --git a/MultiTerm.Protocols/Factories/CommunicationProtocolFactory.cs b/MultiTerm.Protocols/Factories/CommunicationProtocolFactory.cs new file mode 100644 index 0000000..c0ae114 --- /dev/null +++ b/MultiTerm.Protocols/Factories/CommunicationProtocolFactory.cs @@ -0,0 +1,64 @@ +using MultiTerm.Protocols.Types; + +namespace MultiTerm.Protocols.Factories; + +/// +/// Can create instances during runtime. +/// +public class CommunicationProtocolFactory : ICommunicationProtocolFactory +{ + private readonly Func> protocolFactory; + private readonly Func> settingsViewModelFactory; + + /// + /// Constructor of . + /// + /// function that gets all registered services with Interface + /// function that gets all registered services with Interface + public CommunicationProtocolFactory(Func> protocolFactory, Func> settingsViewModelFactory) + { + this.protocolFactory = protocolFactory; + this.settingsViewModelFactory = settingsViewModelFactory; + } + + /// + /// Creates a new and its corresponding . + /// + /// of the new + /// tuple of communication protocol and protocol settings viewmodel + public Tuple Create(ProtocolType protocolType) + { + ICommunicationProtocol protocol; + IProtocolSettingsViewModel settingsViewModel; + + // get all registered ICommunicationProtocol as IEnumerable + var registeredProtocols = protocolFactory(); + + // get all registered IProtocolSettingsViewModel as IEnumerable + var registeredSettingsViewModel = settingsViewModelFactory(); + + // search for correct type of protocol implementation by ProtocolType + try + { + protocol = registeredProtocols.Where(x => x.ProtocolType == protocolType).First(); + } + catch (Exception ex) + { + throw new NotImplementedException($"'{nameof(CommunicationProtocolFactory)}' cannot create CommunicationProtocol with {nameof(ProtocolType)} {protocolType}", ex); + } + + // search for correct type of settings View Model implementation by ProtocolType + try + { + settingsViewModel = registeredSettingsViewModel.Where(x => x.ProtocolType == protocolType).First(); + } + catch (Exception ex) + { + throw new NotImplementedException($"'{nameof(CommunicationProtocolFactory)}' cannot create ProtocolSettingsViewModel with {nameof(ProtocolType)} {protocolType}", ex); + } + + // return the filled up instances + return new Tuple(protocol, settingsViewModel); + } + +} diff --git a/MultiTerm.Protocols/Factories/ICommunicationProtocolFactory.cs b/MultiTerm.Protocols/Factories/ICommunicationProtocolFactory.cs new file mode 100644 index 0000000..eca0497 --- /dev/null +++ b/MultiTerm.Protocols/Factories/ICommunicationProtocolFactory.cs @@ -0,0 +1,8 @@ +using MultiTerm.Protocols.Types; + +namespace MultiTerm.Protocols.Factories; + +public interface ICommunicationProtocolFactory +{ + Tuple Create(ProtocolType protocolType); +} \ No newline at end of file diff --git a/MultiTerm.Protocols/Helpers/ServiceExtensions.cs b/MultiTerm.Protocols/Helpers/ServiceExtensions.cs new file mode 100644 index 0000000..59de2c0 --- /dev/null +++ b/MultiTerm.Protocols/Helpers/ServiceExtensions.cs @@ -0,0 +1,34 @@ +using Microsoft.Extensions.DependencyInjection; +using MultiTerm.Protocols.Factories; +using MultiTerm.Protocols.Serial; + +namespace MultiTerm.Protocols.Helpers; + +public static class ServiceExtensions +{ + /// + /// Service Extension to add all implementations of to Dependency Injection. + /// Also adds to Dependency injection, which allows instanciation of + /// and during runtime. + /// + /// existing services collection + public static void AddCommunicationProtocolFactory(this IServiceCollection services) + { + // add all protocol implementations to the services collection + services.AddTransient(); + // TODO extend + + // add all settings view model implementations to the services collection + services.AddTransient(); + // TODO extend + + // add a function to the services collection, which is used by the CommunicationProtocolFactory + // to get a list of all registered implementations of ICommunicationProtocol + services.AddSingleton>>(x => () => x.GetService>()!); + // to get a list of all registered implementations of IProtocolSettingsViewModel + services.AddSingleton>>(x => () => x.GetService>()!); + + // add the factory itself to the services collection + services.AddSingleton(); + } +} diff --git a/MultiTerm.Protocols/ICommunicationProtocol.cs b/MultiTerm.Protocols/ICommunicationProtocol.cs index 7952d0c..c21326c 100644 --- a/MultiTerm.Protocols/ICommunicationProtocol.cs +++ b/MultiTerm.Protocols/ICommunicationProtocol.cs @@ -1,4 +1,6 @@ -namespace MultiTerm.Protocols; +using MultiTerm.Protocols.Types; + +namespace MultiTerm.Protocols; /// /// Interface to interact with a Communication protocol. @@ -6,19 +8,14 @@ public interface ICommunicationProtocol { /// - /// Contains settings that are required to connect using this communication protocol. - /// - ProtocolConnectionSettingsViewModel? ConnectionSettings { get; } - - /// - /// Newline sequence to add on the end when sending a message using . + /// Indicates the protocol type of the implementing class. /// - string NewlineSequenceOnSend { get; } + ProtocolType ProtocolType { get; } /// - /// When this sequence is detected while reading, a new line is introduced. + /// Contains settings that are required to connect and use this communication protocol. /// - string NewlineOnReceivedSequence { get; } + IProtocolSettings? Settings { get; } /// /// New data received from connected device. diff --git a/MultiTerm.Protocols/IProtocolSettings.cs b/MultiTerm.Protocols/IProtocolSettings.cs new file mode 100644 index 0000000..4cfebec --- /dev/null +++ b/MultiTerm.Protocols/IProtocolSettings.cs @@ -0,0 +1,19 @@ +namespace MultiTerm.Protocols; + +public interface IProtocolSettings +{ + /// + /// Newline sequence to add on the end when sending a message. + /// + string NewlineSequenceOnSend { get; } + + /// + /// When this sequence is detected while reading, a new line is introduced. + /// + string NewlineOnReceivedSequence { get; } + + /// + /// Checks if the entered settings are valid and a connection can be made. + /// + void AreValid(); +} diff --git a/MultiTerm.Protocols/IProtocolSettingsViewModel.cs b/MultiTerm.Protocols/IProtocolSettingsViewModel.cs new file mode 100644 index 0000000..3272788 --- /dev/null +++ b/MultiTerm.Protocols/IProtocolSettingsViewModel.cs @@ -0,0 +1,21 @@ +using MultiTerm.Protocols.Types; + +namespace MultiTerm.Protocols; + +public interface IProtocolSettingsViewModel +{ + /// + /// Indicates the protocol type of the implementing SettingsViewModel. + /// + ProtocolType ProtocolType { get; } + + /// + /// Event that is thrown when the user requested to connect to the device with the entered settings. + /// + event EventHandler? ConnectRequested; + + /// + /// Event that is thrown when the user requested to disconnect from the device. + /// + event EventHandler? DisconnectRequested; +} \ No newline at end of file diff --git a/MultiTerm.Protocols/ProtocolConnectionSettingsViewModel.cs b/MultiTerm.Protocols/ProtocolConnectionSettingsViewModel.cs deleted file mode 100644 index 5796603..0000000 --- a/MultiTerm.Protocols/ProtocolConnectionSettingsViewModel.cs +++ /dev/null @@ -1,40 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.Input; - -namespace MultiTerm.Protocols; - -/// -/// Class that represents all connection settings for a specific . -/// Can be bound to UI using MVVM pattern. -/// -public abstract partial class ProtocolConnectionSettingsViewModel : ObservableObject -{ - /// - /// Event that is thrown when the user requested to connect to the device with the entered settings. - /// - public event EventHandler? ConnectRequested; - - /// - /// Event that is thrown when the user requested to disconnect from the device. - /// - public event EventHandler? DisconnectRequested; - - /// - /// Indicates wether the settings can currently be edited. - /// - public bool AreEditable; - - - [ObservableProperty] - private string connectDisconnectButtonText = "Connect"; - - /// - /// Binding to Connect/Disconnect button. - /// Button text is provided in - /// - [RelayCommand] - private void ConnectDisconnect() - { - - } -} diff --git a/MultiTerm.Protocols/ProtocolSettingsViewModel.cs b/MultiTerm.Protocols/ProtocolSettingsViewModel.cs new file mode 100644 index 0000000..efa9590 --- /dev/null +++ b/MultiTerm.Protocols/ProtocolSettingsViewModel.cs @@ -0,0 +1,61 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using MultiTerm.Protocols.Types; + +namespace MultiTerm.Protocols; + +/// +/// Class that represents all connection and usage settings for a specific . +/// Can be bound to UI using MVVM pattern. +/// +public abstract partial class ProtocolSettingsViewModel : ObservableObject, IProtocolSettingsViewModel +{ + private const string connectedStateButtonText = "Disconnect"; + private const string disconnectedStateButtonText = "Connect"; + + public event EventHandler? ConnectRequested; + public event EventHandler? DisconnectRequested; + + public abstract ProtocolType ProtocolType { get; } + + /// + /// Indicates wether the settings can currently be edited. + /// + public bool AreEditable; + + [ObservableProperty] + private string connectDisconnectButtonText = disconnectedStateButtonText; + + + /// + /// Binding to Connect/Disconnect button. + /// Button text is provided in + /// + [RelayCommand(AllowConcurrentExecutions = false)] + private async Task ConnectDisconnectAsync() + { + await Task.Factory.StartNew(() => + { + // if currently disconnected + if (this.ConnectDisconnectButtonText == disconnectedStateButtonText) + { + // CONNECT + this.AreEditable = false; + this.ConnectRequested?.Invoke(this, EventArgs.Empty); + this.ConnectDisconnectButtonText = connectedStateButtonText; + } + // if currently connected + else if (this.ConnectDisconnectButtonText == connectedStateButtonText) + { + // DISCONNECT + this.DisconnectRequested?.Invoke(this, EventArgs.Empty); + this.ConnectDisconnectButtonText = disconnectedStateButtonText; + this.AreEditable = true; + } + else + { + throw new Exception($"'{nameof(ConnectDisconnectAsync)}()' has not recognized button text, cannot identify state of connection."); + } + }); + } +} diff --git a/MultiTerm.Protocols/Serial/ISerialProtocolSettings.cs b/MultiTerm.Protocols/Serial/ISerialProtocolSettings.cs new file mode 100644 index 0000000..d99a8bf --- /dev/null +++ b/MultiTerm.Protocols/Serial/ISerialProtocolSettings.cs @@ -0,0 +1,7 @@ +namespace MultiTerm.Protocols.Serial; + +public interface ISerialProtocolSettings : IProtocolSettings +{ + //TODO + int BaudRate { get; set; } +} diff --git a/MultiTerm.Protocols/Serial/SerialProtocol.cs b/MultiTerm.Protocols/Serial/SerialProtocol.cs new file mode 100644 index 0000000..5203d28 --- /dev/null +++ b/MultiTerm.Protocols/Serial/SerialProtocol.cs @@ -0,0 +1,34 @@ +using Common.Logging; +using MultiTerm.Protocols.Types; + +namespace MultiTerm.Protocols.Serial; + +public class SerialProtocol : CommunicationProtocol +{ + public override ProtocolType ProtocolType => ProtocolType.Serial; + + public SerialProtocol(ILogger logger) : base(logger) + { + + } + + protected override bool InternalConnect() + { + throw new NotImplementedException(); + } + + protected override void InternalDisconnect() + { + throw new NotImplementedException(); + } + + protected override void InternalRead(CancellationToken ct) + { + throw new NotImplementedException(); + } + + protected override void InternalSendBytes(byte[] bytes) + { + throw new NotImplementedException(); + } +} diff --git a/MultiTerm.Protocols/Serial/SerialProtocolSettingsViewModel.cs b/MultiTerm.Protocols/Serial/SerialProtocolSettingsViewModel.cs new file mode 100644 index 0000000..2f33615 --- /dev/null +++ b/MultiTerm.Protocols/Serial/SerialProtocolSettingsViewModel.cs @@ -0,0 +1,23 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using MultiTerm.Protocols.Types; + +namespace MultiTerm.Protocols.Serial; + +public partial class SerialProtocolSettingsViewModel : ProtocolSettingsViewModel, ISerialProtocolSettings +{ + public override ProtocolType ProtocolType => ProtocolType.Serial; + + [ObservableProperty] + private int baudRate; + //public int BaudRate { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public string NewlineSequenceOnSend => throw new NotImplementedException(); + + public string NewlineOnReceivedSequence => throw new NotImplementedException(); + + + public void AreValid() + { + throw new NotImplementedException(); + } +} diff --git a/MultiTerm.Wpf/App.xaml.cs b/MultiTerm.Wpf/App.xaml.cs index 16a891c..f0d06db 100644 --- a/MultiTerm.Wpf/App.xaml.cs +++ b/MultiTerm.Wpf/App.xaml.cs @@ -7,6 +7,7 @@ using Common.Logging; using System; using System.Threading.Tasks; using Common.AppSettings; +using MultiTerm.Protocols.Helpers; namespace MultiTerm.Wpf; @@ -30,6 +31,7 @@ public partial class App : Application services.AddSingleton(); // application specific + services.AddCommunicationProtocolFactory(); services.AddTerminalViewModelFactory(); }) .Build();