implemented CommunicationProtocolFactory, added to App,

implemented ProtocolSettings and ProtocolSettingsViewModel,
extended CommunicationProtocol by settings and ProtocolType
master
Jonas Arnold 3 years ago
parent 77e355a269
commit 0b36f17997
  1. 5
      MultiTerm.Core/Factories/ITerminalViewModelFactory.cs
  2. 10
      MultiTerm.Core/Factories/TerminalViewModelFactory.cs
  3. 2
      MultiTerm.Core/ViewModel/ITerminalViewModel.cs
  4. 2
      MultiTerm.Core/ViewModel/TerminalViewModel.cs
  5. 11
      MultiTerm.Protocols/CommunicationProtocol.cs
  6. 64
      MultiTerm.Protocols/Factories/CommunicationProtocolFactory.cs
  7. 8
      MultiTerm.Protocols/Factories/ICommunicationProtocolFactory.cs
  8. 34
      MultiTerm.Protocols/Helpers/ServiceExtensions.cs
  9. 17
      MultiTerm.Protocols/ICommunicationProtocol.cs
  10. 19
      MultiTerm.Protocols/IProtocolSettings.cs
  11. 21
      MultiTerm.Protocols/IProtocolSettingsViewModel.cs
  12. 40
      MultiTerm.Protocols/ProtocolConnectionSettingsViewModel.cs
  13. 61
      MultiTerm.Protocols/ProtocolSettingsViewModel.cs
  14. 7
      MultiTerm.Protocols/Serial/ISerialProtocolSettings.cs
  15. 34
      MultiTerm.Protocols/Serial/SerialProtocol.cs
  16. 23
      MultiTerm.Protocols/Serial/SerialProtocolSettingsViewModel.cs
  17. 2
      MultiTerm.Wpf/App.xaml.cs

@ -2,10 +2,9 @@
using MultiTerm.Core.ViewModel; using MultiTerm.Core.ViewModel;
using MultiTerm.Protocols.Types; 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);
} }
}

@ -1,5 +1,6 @@
using MultiTerm.Core.Types; using MultiTerm.Core.Types;
using MultiTerm.Core.ViewModel; using MultiTerm.Core.ViewModel;
using MultiTerm.Protocols.Factories;
using MultiTerm.Protocols.Types; using MultiTerm.Protocols.Types;
namespace MultiTerm.Core.Factories; namespace MultiTerm.Core.Factories;
@ -10,14 +11,16 @@ namespace MultiTerm.Core.Factories;
public class TerminalViewModelFactory : ITerminalViewModelFactory public class TerminalViewModelFactory : ITerminalViewModelFactory
{ {
private readonly Func<IEnumerable<ITerminalViewModel>> factory; private readonly Func<IEnumerable<ITerminalViewModel>> factory;
private readonly ICommunicationProtocolFactory communicationProtocolFactory;
/// <summary> /// <summary>
/// Constructor of <see cref="TerminalViewModelFactory"/>. /// Constructor of <see cref="TerminalViewModelFactory"/>.
/// </summary> /// </summary>
/// <param name="factory">function that gets all registered services with Interface <see cref="ITerminalViewModel"/></param> /// <param name="factory">function that gets all registered services with Interface <see cref="ITerminalViewModel"/></param>
public TerminalViewModelFactory(Func<IEnumerable<ITerminalViewModel>> factory) public TerminalViewModelFactory(Func<IEnumerable<ITerminalViewModel>> factory, ICommunicationProtocolFactory communicationProtocolFactory)
{ {
this.factory = factory; this.factory = factory;
this.communicationProtocolFactory = communicationProtocolFactory;
} }
/// <summary> /// <summary>
@ -46,6 +49,11 @@ public class TerminalViewModelFactory : ITerminalViewModelFactory
// immediately initialize protocol type // immediately initialize protocol type
output.ProtocolType = protocolType; 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 the filled up instance
return output; return output;
} }

@ -29,7 +29,7 @@ public interface ITerminalViewModel
/// <summary> /// <summary>
/// Settings ViewModel that displays protocol specific settings to user. /// Settings ViewModel that displays protocol specific settings to user.
/// </summary> /// </summary>
ProtocolSettingsViewModel? ProtocolSettings { get; set; } IProtocolSettingsViewModel? ProtocolSettings { get; set; }
/// <summary> /// <summary>
/// Request Closing of Terminal. /// Request Closing of Terminal.

@ -12,7 +12,7 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
public abstract TerminalViewType ViewType { get; } public abstract TerminalViewType ViewType { get; }
public ProtocolType ProtocolType { get; set; } public ProtocolType ProtocolType { get; set; }
public ICommunicationProtocol? CommunicationProtocol { get; set; } public ICommunicationProtocol? CommunicationProtocol { get; set; }
public ProtocolSettingsViewModel? ProtocolSettings { get; set; } public IProtocolSettingsViewModel? ProtocolSettings { get; set; }
public event EventHandler? ClosingEvent; public event EventHandler? ClosingEvent;

@ -1,27 +1,22 @@
using Common.Logging; using Common.Logging;
using MultiTerm.Protocols.Types;
namespace MultiTerm.Protocols; namespace MultiTerm.Protocols;
public abstract class CommunicationProtocol : ICommunicationProtocol public abstract class CommunicationProtocol : ICommunicationProtocol
{ {
public string NewlineSequenceOnSend { get; set; }
public string NewlineOnReceivedSequence { get; set; }
public ProtocolConnectionSettingsViewModel? ConnectionSettings { get; set; }
protected ILogger logger; protected ILogger logger;
private CancellationTokenSource cancellationTokenSource; private CancellationTokenSource cancellationTokenSource;
private Thread? readingThread; private Thread? readingThread;
public IProtocolSettings? Settings { get; set; }
public event EventHandler<ReceivedDataEventArgs>? ReceivedDataEvent; public event EventHandler<ReceivedDataEventArgs>? ReceivedDataEvent;
public event EventHandler<SentDataEventArgs>? SentDataEvent; public event EventHandler<SentDataEventArgs>? SentDataEvent;
public abstract ProtocolType ProtocolType { get; }
public CommunicationProtocol(ILogger logger) public CommunicationProtocol(ILogger logger)
{ {
// initialize with empty string, must be correctly initalized by factory
this.NewlineSequenceOnSend = string.Empty;
this.NewlineOnReceivedSequence = string.Empty;
// initialize other // initialize other
this.cancellationTokenSource = new CancellationTokenSource(); this.cancellationTokenSource = new CancellationTokenSource();
this.logger = logger; this.logger = logger;

@ -0,0 +1,64 @@
using MultiTerm.Protocols.Types;
namespace MultiTerm.Protocols.Factories;
/// <summary>
/// Can create <see cref="ICommunicationProtocol"/> instances during runtime.
/// </summary>
public class CommunicationProtocolFactory : ICommunicationProtocolFactory
{
private readonly Func<IEnumerable<ICommunicationProtocol>> protocolFactory;
private readonly Func<IEnumerable<IProtocolSettingsViewModel>> settingsViewModelFactory;
/// <summary>
/// Constructor of <see cref="CommunicationProtocolFactory"/>.
/// </summary>
/// <param name="protocolFactory">function that gets all registered services with Interface <see cref="ICommunicationProtocol"/></param>
/// <param name="settingsViewModelFactory">function that gets all registered services with Interface <see cref="IProtocolSettingsViewModel"/></param>
public CommunicationProtocolFactory(Func<IEnumerable<ICommunicationProtocol>> protocolFactory, Func<IEnumerable<IProtocolSettingsViewModel>> settingsViewModelFactory)
{
this.protocolFactory = protocolFactory;
this.settingsViewModelFactory = settingsViewModelFactory;
}
/// <summary>
/// Creates a new <see cref="ICommunicationProtocol"/> and its corresponding <see cref="IProtocolSettingsViewModel"/>.
/// </summary>
/// <param name="protocolType"><see cref="ProtocolType"/> of the new <see cref="ICommunicationProtocol"/></param>
/// <returns>tuple of communication protocol and protocol settings viewmodel</returns>
public Tuple<ICommunicationProtocol, IProtocolSettingsViewModel> 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<ICommunicationProtocol, IProtocolSettingsViewModel>(protocol, settingsViewModel);
}
}

@ -0,0 +1,8 @@
using MultiTerm.Protocols.Types;
namespace MultiTerm.Protocols.Factories;
public interface ICommunicationProtocolFactory
{
Tuple<ICommunicationProtocol, IProtocolSettingsViewModel> Create(ProtocolType protocolType);
}

@ -0,0 +1,34 @@
using Microsoft.Extensions.DependencyInjection;
using MultiTerm.Protocols.Factories;
using MultiTerm.Protocols.Serial;
namespace MultiTerm.Protocols.Helpers;
public static class ServiceExtensions
{
/// <summary>
/// Service Extension to add all implementations of <see cref="ICommunicationProtocol"/> to Dependency Injection.
/// Also adds <see cref="CommunicationProtocolFactory"/> to Dependency injection, which allows instanciation of <see cref="ICommunicationProtocol"/>
/// and <see cref="IProtocolSettingsViewModel"/> during runtime.
/// </summary>
/// <param name="services">existing services collection</param>
public static void AddCommunicationProtocolFactory(this IServiceCollection services)
{
// add all protocol implementations to the services collection
services.AddTransient<ICommunicationProtocol, SerialProtocol>();
// TODO extend
// add all settings view model implementations to the services collection
services.AddTransient<IProtocolSettingsViewModel, SerialProtocolSettingsViewModel>();
// 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<Func<IEnumerable<ICommunicationProtocol>>>(x => () => x.GetService<IEnumerable<ICommunicationProtocol>>()!);
// to get a list of all registered implementations of IProtocolSettingsViewModel
services.AddSingleton<Func<IEnumerable<IProtocolSettingsViewModel>>>(x => () => x.GetService<IEnumerable<IProtocolSettingsViewModel>>()!);
// add the factory itself to the services collection
services.AddSingleton<ICommunicationProtocolFactory, CommunicationProtocolFactory>();
}
}

@ -1,4 +1,6 @@
namespace MultiTerm.Protocols; using MultiTerm.Protocols.Types;
namespace MultiTerm.Protocols;
/// <summary> /// <summary>
/// Interface to interact with a Communication protocol. /// Interface to interact with a Communication protocol.
@ -6,19 +8,14 @@
public interface ICommunicationProtocol public interface ICommunicationProtocol
{ {
/// <summary> /// <summary>
/// Contains settings that are required to connect using this communication protocol. /// Indicates the protocol type of the implementing class.
/// </summary>
ProtocolConnectionSettingsViewModel? ConnectionSettings { get; }
/// <summary>
/// Newline sequence to add on the end when sending a message using <see cref="SendBytes(byte[])"/>.
/// </summary> /// </summary>
string NewlineSequenceOnSend { get; } ProtocolType ProtocolType { get; }
/// <summary> /// <summary>
/// When this sequence is detected while reading, a new line is introduced. /// Contains settings that are required to connect and use this communication protocol.
/// </summary> /// </summary>
string NewlineOnReceivedSequence { get; } IProtocolSettings? Settings { get; }
/// <summary> /// <summary>
/// New data received from connected device. /// New data received from connected device.

@ -0,0 +1,19 @@
namespace MultiTerm.Protocols;
public interface IProtocolSettings
{
/// <summary>
/// Newline sequence to add on the end when sending a message.
/// </summary>
string NewlineSequenceOnSend { get; }
/// <summary>
/// When this sequence is detected while reading, a new line is introduced.
/// </summary>
string NewlineOnReceivedSequence { get; }
/// <summary>
/// Checks if the entered settings are valid and a connection can be made.
/// </summary>
void AreValid();
}

@ -0,0 +1,21 @@
using MultiTerm.Protocols.Types;
namespace MultiTerm.Protocols;
public interface IProtocolSettingsViewModel
{
/// <summary>
/// Indicates the protocol type of the implementing SettingsViewModel.
/// </summary>
ProtocolType ProtocolType { get; }
/// <summary>
/// Event that is thrown when the user requested to connect to the device with the entered settings.
/// </summary>
event EventHandler? ConnectRequested;
/// <summary>
/// Event that is thrown when the user requested to disconnect from the device.
/// </summary>
event EventHandler? DisconnectRequested;
}

@ -1,40 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace MultiTerm.Protocols;
/// <summary>
/// Class that represents all connection settings for a specific <see cref="CommunicationProtocol"/>.
/// Can be bound to UI using MVVM pattern.
/// </summary>
public abstract partial class ProtocolConnectionSettingsViewModel : ObservableObject
{
/// <summary>
/// Event that is thrown when the user requested to connect to the device with the entered settings.
/// </summary>
public event EventHandler? ConnectRequested;
/// <summary>
/// Event that is thrown when the user requested to disconnect from the device.
/// </summary>
public event EventHandler? DisconnectRequested;
/// <summary>
/// Indicates wether the settings can currently be edited.
/// </summary>
public bool AreEditable;
[ObservableProperty]
private string connectDisconnectButtonText = "Connect";
/// <summary>
/// Binding to Connect/Disconnect button.
/// Button text is provided in <see cref="ConnectDisconnectButtonText"/>
/// </summary>
[RelayCommand]
private void ConnectDisconnect()
{
}
}

@ -0,0 +1,61 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MultiTerm.Protocols.Types;
namespace MultiTerm.Protocols;
/// <summary>
/// Class that represents all connection and usage settings for a specific <see cref="CommunicationProtocol"/>.
/// Can be bound to UI using MVVM pattern.
/// </summary>
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; }
/// <summary>
/// Indicates wether the settings can currently be edited.
/// </summary>
public bool AreEditable;
[ObservableProperty]
private string connectDisconnectButtonText = disconnectedStateButtonText;
/// <summary>
/// Binding to Connect/Disconnect button.
/// Button text is provided in <see cref="ConnectDisconnectButtonText"/>
/// </summary>
[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.");
}
});
}
}

@ -0,0 +1,7 @@
namespace MultiTerm.Protocols.Serial;
public interface ISerialProtocolSettings : IProtocolSettings
{
//TODO
int BaudRate { get; set; }
}

@ -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();
}
}

@ -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();
}
}

@ -7,6 +7,7 @@ using Common.Logging;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Common.AppSettings; using Common.AppSettings;
using MultiTerm.Protocols.Helpers;
namespace MultiTerm.Wpf; namespace MultiTerm.Wpf;
@ -30,6 +31,7 @@ public partial class App : Application
services.AddSingleton<ShellViewModel>(); services.AddSingleton<ShellViewModel>();
// application specific // application specific
services.AddCommunicationProtocolFactory();
services.AddTerminalViewModelFactory(); services.AddTerminalViewModelFactory();
}) })
.Build(); .Build();

Loading…
Cancel
Save