From 585ce8bebd1e92a5c8428faed9698ff788036217 Mon Sep 17 00:00:00 2001 From: Jonas Arnold Date: Wed, 12 Apr 2023 16:21:26 +0200 Subject: [PATCH] implemented serial protocol, made changes to CommunicationProtocol, added ILibraryEquivalentConverter to convert library types to local types, added Messenger to App, implemented UserInterfaceMessages --- Common/Messaging/IUserInterfaceMessage.cs | 18 +++++ Common/Messaging/MessageImportance.cs | 28 +++++++ MultiTerm.Core/ViewModel/ShellViewModel.cs | 27 ++++++- MultiTerm.Core/ViewModel/TerminalViewModel.cs | 6 +- MultiTerm.Protocols/CommunicationProtocol.cs | 17 +++- MultiTerm.Protocols/ICommunicationProtocol.cs | 8 +- .../ILibraryEquivalentConverter.cs | 23 ++++++ MultiTerm.Protocols/IProtocolSettings.cs | 3 +- .../IProtocolSettingsViewModel.cs | 3 +- .../MultiTerm.Protocols.csproj | 1 + .../ProtocolSettingsViewModel.cs | 12 ++- MultiTerm.Protocols/Serial/Handshake.cs | 74 +++++++++++++++++ .../Serial/ISerialProtocolSettings.cs | 32 +++++++- MultiTerm.Protocols/Serial/Parity.cs | 56 +++++++++++++ MultiTerm.Protocols/Serial/SerialProtocol.cs | 80 ++++++++++++++++--- .../Serial/SerialProtocolSettingsViewModel.cs | 46 +++++++++-- MultiTerm.Protocols/Serial/StopBits.cs | 44 ++++++++++ .../Types/ProtocolSettingsInvalidMessage.cs | 22 +++++ MultiTerm.Wpf/App.xaml.cs | 2 + .../MessageImportanceToBrushConverter.cs | 33 ++++++++ MultiTerm.Wpf/View/ShellView.xaml | 11 ++- 21 files changed, 510 insertions(+), 36 deletions(-) create mode 100644 Common/Messaging/IUserInterfaceMessage.cs create mode 100644 Common/Messaging/MessageImportance.cs create mode 100644 MultiTerm.Protocols/ILibraryEquivalentConverter.cs create mode 100644 MultiTerm.Protocols/Serial/Handshake.cs create mode 100644 MultiTerm.Protocols/Serial/Parity.cs create mode 100644 MultiTerm.Protocols/Serial/StopBits.cs create mode 100644 MultiTerm.Protocols/Types/ProtocolSettingsInvalidMessage.cs create mode 100644 MultiTerm.Wpf/ValueConverters/MessageImportanceToBrushConverter.cs diff --git a/Common/Messaging/IUserInterfaceMessage.cs b/Common/Messaging/IUserInterfaceMessage.cs new file mode 100644 index 0000000..5845f67 --- /dev/null +++ b/Common/Messaging/IUserInterfaceMessage.cs @@ -0,0 +1,18 @@ +namespace Common.Messaging; + +/// +/// Interface represents a user interface message. +/// +public interface IUserInterfaceMessage +{ + /// + /// Converst message to string that is displayed to user. + /// + /// string to be displayed to user + string ToString(); + + /// + /// Defines how prominent the message shall be displayed to the user. + /// + MessageImportance Importance { get; } +} diff --git a/Common/Messaging/MessageImportance.cs b/Common/Messaging/MessageImportance.cs new file mode 100644 index 0000000..95fedca --- /dev/null +++ b/Common/Messaging/MessageImportance.cs @@ -0,0 +1,28 @@ +namespace Common.Messaging; + +public enum MessageImportance +{ + /// + /// Normal importance. + /// Message is displayed but user might not recognize it immediately. + /// + Normal, + + /// + /// Medium importance. + /// Message is displayed so user should recognizes it in a matter of seconds. + /// + Medium, + + /// + /// High importance. + /// Message is prominently displayed so user should recognize it immediately. + /// + High, + + /// + /// High importance. + /// User is interrupted with this very prominently displayed message and needs to confirm the message. + /// + HighAndRequiresConfirmation +} diff --git a/MultiTerm.Core/ViewModel/ShellViewModel.cs b/MultiTerm.Core/ViewModel/ShellViewModel.cs index 4bade26..bfa1a27 100644 --- a/MultiTerm.Core/ViewModel/ShellViewModel.cs +++ b/MultiTerm.Core/ViewModel/ShellViewModel.cs @@ -7,10 +7,12 @@ using MultiTerm.Core.Factories; using System.Collections.ObjectModel; using MultiTerm.Core.Types; using MultiTerm.Protocols.Types; +using CommunityToolkit.Mvvm.Messaging; +using Common.Messaging; namespace MultiTerm.Core.ViewModel; -public partial class ShellViewModel : ObservableObject +public partial class ShellViewModel : ObservableObject, IRecipient { private const string defaultReceiveNewlineSeparatorAppSettingsKey = "DefaultReceiveNewlineSeparator"; private const string defaultSendNewlineSeparatorAppSettingsKey = "DefaultSendNewlineSeparator"; @@ -36,19 +38,32 @@ public partial class ShellViewModel : ObservableObject private TerminalViewType selectedTerminalViewType = TerminalViewType.SendReceive; #endregion + #region Status Bar + [ObservableProperty] + private string statusBarMessage = ""; + + [ObservableProperty] + private MessageImportance statusBarMessageImportance = MessageImportance.Normal; + #endregion + private readonly ITerminalViewModelFactory terminalViewModelFactory; private readonly ILogger logger; private readonly IAppSettingsProvider appSettings; + private readonly IMessenger messenger; - public ShellViewModel(ITerminalViewModelFactory terminalViewModelFactory, ILogger logger, IAppSettingsProvider appSettings) + public ShellViewModel(ITerminalViewModelFactory terminalViewModelFactory, ILogger logger, IAppSettingsProvider appSettings, IMessenger messenger) { this.terminalViewModelFactory = terminalViewModelFactory; this.logger = logger; this.appSettings = appSettings; + this.messenger = messenger; // intialize values from app settings this.LoadFromAppSettings(); + // register to messages + this.messenger.Register(this); + // TEMP Init this.AppendTerminalWithSelectedViewType(ProtocolType.Serial); } @@ -110,4 +125,12 @@ public partial class ShellViewModel : ObservableObject this.appSettings.WriteSetting(defaultSendNewlineSeparatorAppSettingsKey, value.ToString()); } #endregion + + #region Messaging handling + public void Receive(IUserInterfaceMessage message) + { + this.StatusBarMessage = message.ToString(); + this.StatusBarMessageImportance = message.Importance; + } + #endregion } diff --git a/MultiTerm.Core/ViewModel/TerminalViewModel.cs b/MultiTerm.Core/ViewModel/TerminalViewModel.cs index 0d5bf4e..44bf8ad 100644 --- a/MultiTerm.Core/ViewModel/TerminalViewModel.cs +++ b/MultiTerm.Core/ViewModel/TerminalViewModel.cs @@ -26,7 +26,7 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie // register event handler for connection request from viewmodel if(value != null) { - protocolSettings!.ConnectRequested += OnViewModelRequestedConnect; + protocolSettings!.ConnectRequested += OnViewModelRequestedConnect; ; protocolSettings!.DisconnectRequested += OnViewModelRequestedDisconnect; } } @@ -49,9 +49,9 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie } } - private void OnViewModelRequestedConnect(object? sender, EventArgs e) + private void OnViewModelRequestedConnect(object? sender, IProtocolSettings e) { - this.CommunicationProtocol?.Connect(); + this.CommunicationProtocol?.Connect(e); } private void OnViewModelRequestedDisconnect(object? sender, EventArgs e) diff --git a/MultiTerm.Protocols/CommunicationProtocol.cs b/MultiTerm.Protocols/CommunicationProtocol.cs index 1e646da..b506f67 100644 --- a/MultiTerm.Protocols/CommunicationProtocol.cs +++ b/MultiTerm.Protocols/CommunicationProtocol.cs @@ -1,4 +1,5 @@ using Common.Logging; +using CommunityToolkit.Mvvm.Messaging; using MultiTerm.Protocols.Types; namespace MultiTerm.Protocols; @@ -9,7 +10,6 @@ public abstract class CommunicationProtocol : ICommunicationProtocol private CancellationTokenSource cancellationTokenSource; private Thread? readingThread; - public IProtocolSettings? Settings { get; set; } public event EventHandler? ReceivedDataEvent; public event EventHandler? SentDataEvent; @@ -48,8 +48,9 @@ public abstract class CommunicationProtocol : ICommunicationProtocol /// /// Allows to connect to the selected device using the implemented protocol. /// + /// protocol settings required to connect /// true on success - protected abstract bool InternalConnect(); + protected abstract bool InternalConnect(IProtocolSettings settings); /// /// Reads from the connected device in an endless loop. @@ -58,9 +59,17 @@ public abstract class CommunicationProtocol : ICommunicationProtocol /// cancellation token protected abstract void InternalRead(CancellationToken ct); - public void Connect() + public void Connect(IProtocolSettings settings) { - if (this.InternalConnect()) + // check if settings are valid, cancel if not + if (settings.AreValid() == false) + { + this.logger.LogError($"'{nameof(Connect)}()' failed since the provided protocol settings are invalid", nameof(CommunicationProtocol)); + return; + } + + // try connecting if serttings are valid + if (this.InternalConnect(settings)) { // renew token source this.cancellationTokenSource = new CancellationTokenSource(); diff --git a/MultiTerm.Protocols/ICommunicationProtocol.cs b/MultiTerm.Protocols/ICommunicationProtocol.cs index c21326c..72a3a90 100644 --- a/MultiTerm.Protocols/ICommunicationProtocol.cs +++ b/MultiTerm.Protocols/ICommunicationProtocol.cs @@ -12,11 +12,6 @@ public interface ICommunicationProtocol /// ProtocolType ProtocolType { get; } - /// - /// Contains settings that are required to connect and use this communication protocol. - /// - IProtocolSettings? Settings { get; } - /// /// New data received from connected device. /// @@ -30,7 +25,8 @@ public interface ICommunicationProtocol /// /// Connect to the device. /// - void Connect(); + /// settings required to connect and use the protocol + void Connect(IProtocolSettings settings); /// /// Disconnect from the device. Ends all internal activities. diff --git a/MultiTerm.Protocols/ILibraryEquivalentConverter.cs b/MultiTerm.Protocols/ILibraryEquivalentConverter.cs new file mode 100644 index 0000000..fd2b0c1 --- /dev/null +++ b/MultiTerm.Protocols/ILibraryEquivalentConverter.cs @@ -0,0 +1,23 @@ +namespace MultiTerm.Protocols; + +/// +/// Interface for a converter that converts a facade type into the equivalent of the related software library. +/// +/// type of the local class +/// type of the library class +internal interface ILibraryEquivalentConverter +{ + /// + /// Convert the object to the library type. + /// + /// object of local class + /// object of class within library + T_library ConvertToLibraryType(T_local obj); + + /// + /// Convert the object to the local type. + /// + /// object of class within library + /// object of local class + T_local ConvertToLocalType(T_library obj); +} diff --git a/MultiTerm.Protocols/IProtocolSettings.cs b/MultiTerm.Protocols/IProtocolSettings.cs index 4cfebec..d77569d 100644 --- a/MultiTerm.Protocols/IProtocolSettings.cs +++ b/MultiTerm.Protocols/IProtocolSettings.cs @@ -15,5 +15,6 @@ public interface IProtocolSettings /// /// Checks if the entered settings are valid and a connection can be made. /// - void AreValid(); + /// true if the settings are valid + bool AreValid(); } diff --git a/MultiTerm.Protocols/IProtocolSettingsViewModel.cs b/MultiTerm.Protocols/IProtocolSettingsViewModel.cs index 3272788..4cc1d02 100644 --- a/MultiTerm.Protocols/IProtocolSettingsViewModel.cs +++ b/MultiTerm.Protocols/IProtocolSettingsViewModel.cs @@ -11,8 +11,9 @@ public interface IProtocolSettingsViewModel /// /// Event that is thrown when the user requested to connect to the device with the entered settings. + /// Provides protocol settings to use for connection. /// - event EventHandler? ConnectRequested; + event EventHandler ConnectRequested; /// /// Event that is thrown when the user requested to disconnect from the device. diff --git a/MultiTerm.Protocols/MultiTerm.Protocols.csproj b/MultiTerm.Protocols/MultiTerm.Protocols.csproj index 3a461b3..cfcec65 100644 --- a/MultiTerm.Protocols/MultiTerm.Protocols.csproj +++ b/MultiTerm.Protocols/MultiTerm.Protocols.csproj @@ -8,6 +8,7 @@ + diff --git a/MultiTerm.Protocols/ProtocolSettingsViewModel.cs b/MultiTerm.Protocols/ProtocolSettingsViewModel.cs index 19f1dba..0474a16 100644 --- a/MultiTerm.Protocols/ProtocolSettingsViewModel.cs +++ b/MultiTerm.Protocols/ProtocolSettingsViewModel.cs @@ -1,5 +1,6 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; using MultiTerm.Protocols.Types; namespace MultiTerm.Protocols; @@ -7,13 +8,15 @@ namespace MultiTerm.Protocols; /// /// Class that represents all connection and usage settings for a specific . /// Can be bound to UI using MVVM pattern. +/// Implementing class must also implement ! Otherwise does not work. /// public abstract partial class ProtocolSettingsViewModel : ObservableObject, IProtocolSettingsViewModel { private const string connectedStateButtonText = "Disconnect"; private const string disconnectedStateButtonText = "Connect"; + protected readonly IMessenger messenger; - public event EventHandler? ConnectRequested; + public event EventHandler? ConnectRequested; public event EventHandler? DisconnectRequested; public abstract ProtocolType ProtocolType { get; } @@ -27,10 +30,15 @@ public abstract partial class ProtocolSettingsViewModel : ObservableObject, IPro [ObservableProperty] private string connectDisconnectButtonText = disconnectedStateButtonText; + public ProtocolSettingsViewModel(IMessenger messenger) + { + this.messenger = messenger; + } /// /// Binding to Connect/Disconnect button. /// Button text is provided in + /// Implementing class must also implement ! Otherwise does not work. /// [RelayCommand(AllowConcurrentExecutions = false)] private async Task ConnectDisconnectAsync() @@ -42,7 +50,7 @@ public abstract partial class ProtocolSettingsViewModel : ObservableObject, IPro { // CONNECT this.AreEditable = false; - this.ConnectRequested?.Invoke(this, EventArgs.Empty); + this.ConnectRequested?.Invoke(this, (IProtocolSettings)this); // implementing class must also implement IProtocolSettings! this.ConnectDisconnectButtonText = connectedStateButtonText; } // if currently connected diff --git a/MultiTerm.Protocols/Serial/Handshake.cs b/MultiTerm.Protocols/Serial/Handshake.cs new file mode 100644 index 0000000..5eeda9c --- /dev/null +++ b/MultiTerm.Protocols/Serial/Handshake.cs @@ -0,0 +1,74 @@ +using System.ComponentModel; + +using Library = RJCP.IO.Ports; + +namespace MultiTerm.Protocols.Serial; + +public enum Handshake +{ + // + // Summary: + // No handshaking. + [Description("None")] + None = 0x0, + // + // Summary: + // Software handshaking. + [Description("XON")] + XOn = 0x1, + // + // Summary: + // Hardware handshaking (RTS/CTS). + [Description("RTS")] + Rts = 0x2, + // + // Summary: + // Hardware handshaking (DTR/DSR) (uncommon). + [Description("DTR")] + Dtr = 0x4, + // + // Summary: + // RTS and Software handshaking. + [Description("RTS XON")] + RtsXOn = 0x3, + // + // Summary: + // DTR and Software handshaking (uncommon). + [Description("DTR XON")] + DtrXOn = 0x5, + // + // Summary: + // Hardware handshaking with RTS/CTS and DTR/DSR (uncommon). + [Description("DTR RTS")] + DtrRts = 0x6, + // + // Summary: + // Hardware handshaking with RTS/CTS and DTR/DSR and Software handshaking (uncommon). + [Description("DTR RTS XON")] + DtrRtsXOn = 0x7 +} + +internal class HandshakeLibraryEquivalentConverter : ILibraryEquivalentConverter +{ + public Library.Handshake ConvertToLibraryType(Handshake obj) + { + return obj switch + { + Handshake.None => Library.Handshake.None, + Handshake.XOn => Library.Handshake.XOn, + Handshake.Rts => Library.Handshake.Rts, + Handshake.Dtr => Library.Handshake.Dtr, + Handshake.RtsXOn => Library.Handshake.RtsXOn, + Handshake.DtrXOn => Library.Handshake.DtrXOn, + Handshake.DtrRts => Library.Handshake.DtrRts, + Handshake.DtrRtsXOn => Library.Handshake.DtrRtsXOn, + _ => throw new NotImplementedException(), + }; + } + + public Handshake ConvertToLocalType(Library.Handshake obj) + { + throw new NotImplementedException(); + } +} + diff --git a/MultiTerm.Protocols/Serial/ISerialProtocolSettings.cs b/MultiTerm.Protocols/Serial/ISerialProtocolSettings.cs index d99a8bf..300b34e 100644 --- a/MultiTerm.Protocols/Serial/ISerialProtocolSettings.cs +++ b/MultiTerm.Protocols/Serial/ISerialProtocolSettings.cs @@ -1,7 +1,35 @@ namespace MultiTerm.Protocols.Serial; public interface ISerialProtocolSettings : IProtocolSettings -{ - //TODO +{ + /// + /// Port for communication, e.g. COM3 or com50. + /// + string PortName { get; set; } + + /// + /// Serial Baud rate. + /// int BaudRate { get; set; } + + /// + /// The type of parity to use. + /// + Parity Parity { get; set; } + + /// + /// The standard length of data bits per byte. + /// + int DataBits { get; set; } + + /// + /// Number of stop bits to use. + /// + StopBits StopBits { get; set; } + + /// + /// Handshaking protocol for serial port transmission of data. + /// + Handshake Handshake { get; set; } + } diff --git a/MultiTerm.Protocols/Serial/Parity.cs b/MultiTerm.Protocols/Serial/Parity.cs new file mode 100644 index 0000000..ca475f0 --- /dev/null +++ b/MultiTerm.Protocols/Serial/Parity.cs @@ -0,0 +1,56 @@ +using System.ComponentModel; + +using Library = RJCP.IO.Ports; + +namespace MultiTerm.Protocols.Serial; + +public enum Parity +{ + // + // Summary: + // No parity. + [Description("None")] + None, + // + // Summary: + // Odd parity. + [Description("Odd")] + Odd, + // + // Summary: + // Even parity. + [Description("Even")] + Even, + // + // Summary: + // Mark parity. + [Description("Mark")] + Mark, + // + // Summary: + // Space parity. + [Description("Space")] + Space +} + +internal class ParityLibraryEquivalentConverter : ILibraryEquivalentConverter +{ + public Library.Parity ConvertToLibraryType(Parity obj) + { + return obj switch + { + Parity.None => Library.Parity.None, + Parity.Odd => Library.Parity.Odd, + Parity.Even => Library.Parity.Even, + Parity.Mark => Library.Parity.Mark, + Parity.Space => Library.Parity.Space, + _ => throw new NotImplementedException(), + }; + } + + public Parity ConvertToLocalType(Library.Parity obj) + { + throw new NotImplementedException(); + } +} + diff --git a/MultiTerm.Protocols/Serial/SerialProtocol.cs b/MultiTerm.Protocols/Serial/SerialProtocol.cs index 5203d28..d85515a 100644 --- a/MultiTerm.Protocols/Serial/SerialProtocol.cs +++ b/MultiTerm.Protocols/Serial/SerialProtocol.cs @@ -1,34 +1,96 @@ using Common.Logging; +using MultiTerm.Protocols.Model; using MultiTerm.Protocols.Types; +using RJCP.IO.Ports; +using System.Text; namespace MultiTerm.Protocols.Serial; public class SerialProtocol : CommunicationProtocol { public override ProtocolType ProtocolType => ProtocolType.Serial; + private ISerialProtocolSettings? serialSettings; + private SerialPortStream serialPort = new(); - public SerialProtocol(ILogger logger) : base(logger) - { - - } - protected override bool InternalConnect() + public SerialProtocol(ILogger logger) : base(logger) { } + + protected override bool InternalConnect(IProtocolSettings protocolSettings) { - throw new NotImplementedException(); + // check if settings are of correct type + if (protocolSettings is not ISerialProtocolSettings serialProtocolSettings) + { + this.serialSettings = null; + throw new ArgumentException($"Cannot connect due to wrong type of Protocol Settings. " + + $"Check parameter {nameof(protocolSettings)}' of '{nameof(InternalConnect)}()' in {nameof(SerialProtocol)}."); + } + + // store locally + this.serialSettings = serialProtocolSettings; + + // create new serial port + this.serialPort = new() + { + // apply user settings, where needed converters are used + PortName = this.serialSettings.PortName, + BaudRate = this.serialSettings.BaudRate, + DataBits = this.serialSettings.DataBits, + Parity = new ParityLibraryEquivalentConverter().ConvertToLibraryType(this.serialSettings.Parity), + StopBits = new StopBitsLibraryEquivalentConverter().ConvertToLibraryType(this.serialSettings.StopBits), + Handshake = new HandshakeLibraryEquivalentConverter().ConvertToLibraryType(this.serialSettings.Handshake), + + // define static settings + Encoding = Encoding.Unicode, + ReadTimeout = 500, + WriteTimeout = 500 + }; + + // try opening serial port + try + { + serialPort.Open(); + } + catch (Exception ex) + { + this.logger.LogException(ex, $"'{nameof(InternalConnect)}()'Opening serial port failed:", nameof(SerialProtocol)); + return false; + } + return true; } protected override void InternalDisconnect() { - throw new NotImplementedException(); + serialPort.Close(); + this.serialSettings = null; } protected override void InternalRead(CancellationToken ct) { - throw new NotImplementedException(); + while(ct.IsCancellationRequested == false) + { + // reads character based on configured encoding (here Unicode) + int readCharacter = serialPort.ReadChar(); + if (readCharacter != -1) // -1 = timeout + { + // create extended char type + var character = new ExtendedChar((char)readCharacter); + + // report new data with event + this.OnReceivedData(new ReceivedDataEventArgs(new ExtendedChar[] { character })); + } + } } protected override void InternalSendBytes(byte[] bytes) { - throw new NotImplementedException(); + foreach (byte b in bytes) + { + serialPort.WriteByte(b); + } + } + + public static IEnumerable GetPortNames() + { + return SerialPortStream.GetPortNames(); } } diff --git a/MultiTerm.Protocols/Serial/SerialProtocolSettingsViewModel.cs b/MultiTerm.Protocols/Serial/SerialProtocolSettingsViewModel.cs index 2f33615..d3014ae 100644 --- a/MultiTerm.Protocols/Serial/SerialProtocolSettingsViewModel.cs +++ b/MultiTerm.Protocols/Serial/SerialProtocolSettingsViewModel.cs @@ -1,4 +1,6 @@ -using CommunityToolkit.Mvvm.ComponentModel; +using Common.Messaging; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Messaging; using MultiTerm.Protocols.Types; namespace MultiTerm.Protocols.Serial; @@ -8,16 +10,50 @@ public partial class SerialProtocolSettingsViewModel : ProtocolSettingsViewModel public override ProtocolType ProtocolType => ProtocolType.Serial; [ObservableProperty] - private int baudRate; - //public int BaudRate { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + private int baudRate = 115200; + [ObservableProperty] + private string portName = ""; + + [ObservableProperty] + private Parity parity = Parity.None; + + [ObservableProperty] + private int dataBits = 8; + + [ObservableProperty] + private StopBits stopBits = StopBits.One; + + [ObservableProperty] + private Handshake handshake = Handshake.None; + + + // TODO public string NewlineSequenceOnSend => throw new NotImplementedException(); public string NewlineOnReceivedSequence => throw new NotImplementedException(); - public void AreValid() + public SerialProtocolSettingsViewModel(IMessenger messenger) : base(messenger) { } + + public bool AreValid() { - throw new NotImplementedException(); + if (String.IsNullOrEmpty(this.PortName)) + { + this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.PortName), "Null or empty")); + return false; + } + if (this.PortName.ToLower().StartsWith("com") == false) + { + this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.PortName), "Must start with COM")); + return false; + } + if (this.DataBits < 5 && this.DataBits > 8 && this.DataBits != 16) + { + this.messenger.Send(new ProtocolSettingsInvalidMessage(nameof(this.DataBits), "Must be 5...8 or 16")); + return false; + } + + return true; } } diff --git a/MultiTerm.Protocols/Serial/StopBits.cs b/MultiTerm.Protocols/Serial/StopBits.cs new file mode 100644 index 0000000..7e318d9 --- /dev/null +++ b/MultiTerm.Protocols/Serial/StopBits.cs @@ -0,0 +1,44 @@ +using System.ComponentModel; + +using Library = RJCP.IO.Ports; + +namespace MultiTerm.Protocols.Serial; + +public enum StopBits +{ + // + // Summary: + // One stop bit. + [Description("1")] + One, + // + // Summary: + // 1.5 stop bits. + [Description("1.5")] + One5, + // + // Summary: + // Two stop bits. + [Description("2")] + Two +} + +internal class StopBitsLibraryEquivalentConverter : ILibraryEquivalentConverter +{ + public Library.StopBits ConvertToLibraryType(StopBits obj) + { + return obj switch + { + StopBits.One => Library.StopBits.One, + StopBits.One5 => Library.StopBits.One5, + StopBits.Two => Library.StopBits.Two, + _ => throw new NotImplementedException(), + }; + } + + public StopBits ConvertToLocalType(Library.StopBits obj) + { + throw new NotImplementedException(); + } +} + diff --git a/MultiTerm.Protocols/Types/ProtocolSettingsInvalidMessage.cs b/MultiTerm.Protocols/Types/ProtocolSettingsInvalidMessage.cs new file mode 100644 index 0000000..82553c8 --- /dev/null +++ b/MultiTerm.Protocols/Types/ProtocolSettingsInvalidMessage.cs @@ -0,0 +1,22 @@ +using Common.Messaging; + +namespace MultiTerm.Protocols.Types; + +public sealed class ProtocolSettingsInvalidMessage : IUserInterfaceMessage +{ + public MessageImportance Importance => MessageImportance.Medium; + + public string SettingName { get; } + public string Message { get; } + + public ProtocolSettingsInvalidMessage(string settingName, string message) + { + this.SettingName = settingName; + this.Message = message; + } + + public override string ToString() + { + return $"Protocol setting '{this.SettingName}' is invalid: {this.Message}"; + } +} diff --git a/MultiTerm.Wpf/App.xaml.cs b/MultiTerm.Wpf/App.xaml.cs index f0d06db..e0fbf58 100644 --- a/MultiTerm.Wpf/App.xaml.cs +++ b/MultiTerm.Wpf/App.xaml.cs @@ -8,6 +8,7 @@ using System; using System.Threading.Tasks; using Common.AppSettings; using MultiTerm.Protocols.Helpers; +using CommunityToolkit.Mvvm.Messaging; namespace MultiTerm.Wpf; @@ -26,6 +27,7 @@ public partial class App : Application services.AddSingleton(); services.AddSingleton(new SerilogLogger("C:/log/multiterm-log-.txt", true)); services.AddSingleton(new XmlAppSettingsProvider("C:/log/multiterm-config.xml")); + services.AddSingleton(); // viewmodels services.AddSingleton(); diff --git a/MultiTerm.Wpf/ValueConverters/MessageImportanceToBrushConverter.cs b/MultiTerm.Wpf/ValueConverters/MessageImportanceToBrushConverter.cs new file mode 100644 index 0000000..389d1ea --- /dev/null +++ b/MultiTerm.Wpf/ValueConverters/MessageImportanceToBrushConverter.cs @@ -0,0 +1,33 @@ +using Common.Messaging; +using System; +using System.Globalization; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Media; + +namespace MultiTerm.Wpf.ValueConverters; + +[ValueConversion(typeof(MessageImportance), typeof(Brush))] + +internal class MessageImportanceToBrushConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if(value is not MessageImportance msgImportance) + { throw new ArgumentException($"Wrong object provided, can only convert from type {nameof(MessageImportance)}"); } + + return msgImportance switch + { + MessageImportance.Normal => Brushes.Black, + MessageImportance.Medium => Brushes.DarkSalmon, + MessageImportance.High => Brushes.Red, + MessageImportance.HighAndRequiresConfirmation => throw new NotImplementedException(), + _ => throw new NotImplementedException(), + }; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} diff --git a/MultiTerm.Wpf/View/ShellView.xaml b/MultiTerm.Wpf/View/ShellView.xaml index b1f02d6..f5875ca 100644 --- a/MultiTerm.Wpf/View/ShellView.xaml +++ b/MultiTerm.Wpf/View/ShellView.xaml @@ -17,6 +17,7 @@ + - + + + + + + + + +