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 @@
+
-
+
+
+
+
+
+
+
+
+