diff --git a/MultiTerm.Core/ViewModel/TerminalViewModel.cs b/MultiTerm.Core/ViewModel/TerminalViewModel.cs
index f3a5c69..5b266a8 100644
--- a/MultiTerm.Core/ViewModel/TerminalViewModel.cs
+++ b/MultiTerm.Core/ViewModel/TerminalViewModel.cs
@@ -54,9 +54,10 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
{
this.Title = $"{this.CommunicationProtocol?.InstanceIdentifier}";
}
- }
+ }
#endregion
+ #region Observable Properties
///
/// Holds communication data that was received via the communication protocol.
///
@@ -87,6 +88,15 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
[ObservableProperty]
private IProtocolSettingsViewModel? protocolSettings;
+ ///
+ /// of as ObservableProperty.
+ /// Required since does not implement .
+ ///
+ [ObservableProperty]
+ private ProtocolConnectionState communicationProtocolConnectionState;
+
+ #endregion
+
public TerminalViewModel(IAppSettingsProvider appSettings, IMessenger messenger, IContext context)
{
@@ -108,7 +118,7 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
// unsubscribe events
this.CommunicationProtocol.ReceivedDataEvent -= CommunicationProtocol_ReceivedDataEvent;
this.CommunicationProtocol.SentDataEvent -= CommunicationProtocol_SentDataEvent;
- this.CommunicationProtocol.DisconnectedEvent -= CommunicationProtocol_DisconnectedEvent;
+ this.CommunicationProtocol.ConnectionStateChangedEvent -= CommunicationProtocol_ConnectionStateChangedEvent;
// remove reference to object
this.CommunicationProtocol = null;
}
@@ -126,7 +136,7 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
{
this.CommunicationProtocol.ReceivedDataEvent += CommunicationProtocol_ReceivedDataEvent;
this.CommunicationProtocol.SentDataEvent += CommunicationProtocol_SentDataEvent;
- this.CommunicationProtocol.DisconnectedEvent += CommunicationProtocol_DisconnectedEvent;
+ this.CommunicationProtocol.ConnectionStateChangedEvent += CommunicationProtocol_ConnectionStateChangedEvent;
}
// initializes default newline separators, updates them directly inside ReceivedData and SentData objects
@@ -153,11 +163,14 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
this.SentData.HandleNewData(e.SentData);
}
- private void CommunicationProtocol_DisconnectedEvent(object? sender, DisconnectedEventArgs e)
+ private void CommunicationProtocol_ConnectionStateChangedEvent(object? sender, ProtocolConnectionState e)
{
- // on unintentional disconnect the view model may still display connected => force to disconnected state
+ // update internal state
+ this.CommunicationProtocolConnectionState = e;
+
+ // If ViewModel still displays connected after unintentional disconnect => force to disconnected state
// allows user to change settings
- if(e.Unintentional == true && this.ProtocolSettings!.DisplaysConnected == true)
+ if (e == ProtocolConnectionState.UnintentionallyDisconnected && this.ProtocolSettings!.DisplaysConnected == true)
{
this.ProtocolSettings.ForceConnectedState(false);
}
@@ -177,9 +190,9 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie
if(this.CommunicationProtocol == null) { throw new NullReferenceException($"'{nameof(SendToCommunicationProtocol)}()' was called but {nameof(CommunicationProtocol)} is null."); }
// inform user and quit if communication protocol is not connected
- if (this.CommunicationProtocol.IsConnected == false)
+ if (this.CommunicationProtocol.State != ProtocolConnectionState.Connected)
{
- this.messenger.Send(new ProtocolNotConnectedMessage("Cannot send message"));
+ this.messenger.Send(new ProtocolNotConnectedMessage("Cannot send message, Protocol is not connected."));
return false;
}
diff --git a/MultiTerm.Protocols/CommunicationProtocol.cs b/MultiTerm.Protocols/CommunicationProtocol.cs
index f95a327..cf95bb6 100644
--- a/MultiTerm.Protocols/CommunicationProtocol.cs
+++ b/MultiTerm.Protocols/CommunicationProtocol.cs
@@ -19,12 +19,21 @@ public abstract class CommunicationProtocol : ICommunicationProtocol
public event EventHandler? ReceivedDataEvent;
public event EventHandler? SentDataEvent;
- public event EventHandler? DisconnectedEvent;
+ public event EventHandler? ConnectionStateChangedEvent;
public abstract ProtocolType ProtocolType { get; }
public abstract string InstanceIdentifier { get; protected set; }
- public bool IsConnected { get; private set; } = false;
+ private ProtocolConnectionState state = ProtocolConnectionState.NotConnected;
+ public ProtocolConnectionState State
+ {
+ get { return state; }
+ set
+ {
+ state = value;
+ this.ConnectionStateChangedEvent?.Invoke(this, state);
+ }
+ }
public CommunicationProtocol(ILogger logger, IMessenger messenger)
{
@@ -52,14 +61,11 @@ public abstract class CommunicationProtocol : ICommunicationProtocol
// perform all steps in a task, to prevent deadlock when this method is called from a thread that is joined.
Task.Run(() =>
{
- // update state
- this.IsConnected = false;
-
this.CancelThreads();
this.logger.LogError($"'{nameof(OnUnintentionallyDisconnected)}()' called.", nameof(CommunicationProtocol));
- // raise event indicating an unintentional disconnect
- this.DisconnectedEvent?.Invoke(this, new DisconnectedEventArgs(true));
+ // update state
+ this.State = ProtocolConnectionState.UnintentionallyDisconnected;
});
}
@@ -75,9 +81,9 @@ public abstract class CommunicationProtocol : ICommunicationProtocol
bool success = true;
// guard is not connected => log warning. user of this function shall only use SendBytes if IsConnected is true
- if (this.IsConnected == false)
+ if (this.State != ProtocolConnectionState.Connected)
{
- this.logger.LogWarn($"'{nameof(SendBytes)}()' was reached with {nameof(IsConnected)} being false", nameof(CommunicationProtocol));
+ this.logger.LogWarn($"'{nameof(SendBytes)}()' was reached with wrong {nameof(ProtocolConnectionState)} of {this.State}", nameof(CommunicationProtocol));
return false; // return and do not send
}
@@ -111,9 +117,9 @@ public abstract class CommunicationProtocol : ICommunicationProtocol
public bool Connect(IProtocolSettings settings)
{
// check if not already connected
- if (this.IsConnected == true)
+ if (this.State == ProtocolConnectionState.Connected)
{
- this.logger.LogWarn($"'{nameof(Connect)}()' was reached even if {nameof(IsConnected)} is already true.", nameof(CommunicationProtocol));
+ this.logger.LogWarn($"'{nameof(Connect)}()' was reached with wrong {nameof(ProtocolConnectionState)} of {this.State}", nameof(CommunicationProtocol));
return true;
}
@@ -138,7 +144,7 @@ public abstract class CommunicationProtocol : ICommunicationProtocol
this.readingThread = new Thread(() => this.InternalRead(this.cancellationTokenSource.Token));
this.readingThread.Start();
// update state
- this.IsConnected = true;
+ this.State = ProtocolConnectionState.Connected;
return true;
}
else
@@ -156,10 +162,8 @@ public abstract class CommunicationProtocol : ICommunicationProtocol
{
this.CancelThreads();
this.InternalDisconnect();
- this.IsConnected = false;
- // raise event indicating an intentional disconnect
- this.DisconnectedEvent?.Invoke(this, new DisconnectedEventArgs(false));
+ this.State = ProtocolConnectionState.NotConnected;
}
///
diff --git a/MultiTerm.Protocols/ICommunicationProtocol.cs b/MultiTerm.Protocols/ICommunicationProtocol.cs
index 58aa487..8753739 100644
--- a/MultiTerm.Protocols/ICommunicationProtocol.cs
+++ b/MultiTerm.Protocols/ICommunicationProtocol.cs
@@ -28,15 +28,16 @@ public interface ICommunicationProtocol
event EventHandler? SentDataEvent;
///
- /// Thrown when the device disconnected.
+ /// Reports the state of connection to the communication protocol.
+ /// This property shall be used to check if the protocol is ready to receive data via .
///
- event EventHandler? DisconnectedEvent;
+ ProtocolConnectionState State { get; }
///
- /// Indicates wether the communication protocol is connected.
- /// True if connected and ready to receive data via , false if not.
+ /// Raised when the changed.
+ /// The handed is the new state.
///
- bool IsConnected { get; }
+ event EventHandler? ConnectionStateChangedEvent;
///
/// Connect to the device.
diff --git a/MultiTerm.Protocols/Serial/SerialProtocol.cs b/MultiTerm.Protocols/Serial/SerialProtocol.cs
index fed8d56..48674bb 100644
--- a/MultiTerm.Protocols/Serial/SerialProtocol.cs
+++ b/MultiTerm.Protocols/Serial/SerialProtocol.cs
@@ -75,8 +75,21 @@ public class SerialProtocol : CommunicationProtocol
{
while(ct.IsCancellationRequested == false)
{
- // reads character based on configured encoding (here ASCII)
- int readByte = this.serialPort.ReadByte();
+ int readByte = -1;
+
+ // try reading
+ try
+ {
+ // reads character based on configured encoding (here ASCII)
+ readByte = this.serialPort.ReadByte();
+ }
+ catch (IOException ex) // thrown when a device disconnected
+ {
+ this.logger.LogException(ex, $"Exception while reading data in {nameof(InternalRead)}", nameof(SerialProtocol));
+ this.OnUnintentionallyDisconnected(); // report disconnect
+ break; // break loop
+ }
+
if (readByte != -1) // -1 = end of stream
{
// report new data with event
diff --git a/MultiTerm.Protocols/Types/ProtocolConnectionState.cs b/MultiTerm.Protocols/Types/ProtocolConnectionState.cs
new file mode 100644
index 0000000..e8c83a1
--- /dev/null
+++ b/MultiTerm.Protocols/Types/ProtocolConnectionState.cs
@@ -0,0 +1,24 @@
+using System.ComponentModel;
+
+namespace MultiTerm.Protocols.Types;
+
+public enum ProtocolConnectionState
+{
+ ///
+ /// Indicates that the protocol is currently not connected or has not yet been connected.
+ ///
+ [Description("Not Connected")]
+ NotConnected,
+
+ ///
+ /// Indicates that the protocol is currently connected and ready to transmit/receive data.
+ ///
+ [Description("Connected")]
+ Connected,
+
+ ///
+ /// Indicates that the protocol disconnected unintentionally (e.g. device connection was interrupted).
+ ///
+ [Description("Unintentionally Disconnected")]
+ UnintentionallyDisconnected
+}
diff --git a/MultiTerm.Protocols/UsbHid/UsbHidProtocol.cs b/MultiTerm.Protocols/UsbHid/UsbHidProtocol.cs
index 6a64d60..8c576dc 100644
--- a/MultiTerm.Protocols/UsbHid/UsbHidProtocol.cs
+++ b/MultiTerm.Protocols/UsbHid/UsbHidProtocol.cs
@@ -125,10 +125,10 @@ public class UsbHidProtocol : CommunicationProtocol
// remove from start of original list either; taken amount of bytes or remaining count of bytes in list
bytesToSend.RemoveRange(0, Math.Min(numBytesToTake, bytesToSend.Count));
- Debug.WriteLine($"{nameof(InternalSendBytes)}() of {nameof(UsbHidProtocol)} took {nextBytes.Count()} bytes and has {bytesToSend.Count} left on queue.");
+ Debug.WriteLine($"{nameof(InternalSendBytes)}() of {nameof(UsbHidProtocol)} took {nextBytes.Count} bytes and has {bytesToSend.Count} left on queue.");
// check number of bytes to send
- int numBytesToSend = nextBytes.Count();
+ int numBytesToSend = nextBytes.Count;
if (numBytesToSend > MaxBytesPerReport || numBytesToSend > byte.MaxValue || numBytesToSend <= 0)
{
throw new Exception($"'{nameof(InternalSendBytes)}()': Invalid of bytes to send ({nameof(numBytesToSend)})");
diff --git a/MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.cs b/MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.cs
index 32b17be..d83c39c 100644
--- a/MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.cs
+++ b/MultiTerm.Wpf.CustomControl/MultiFormatDataView/MultiFormatDataView.cs
@@ -241,7 +241,6 @@ public class MultiFormatDataView : Control
{
var newSelection = new ObservableCollection();
int selectionStartIndex = this.tbCharOnlyView!.SelectionStart;
- // TEMP OLD
// extract text from the beginning to the start of the selected text
var textFromBeginningToStartOfSelection = this.tbCharOnlyView!.Text.Substring(0, selectionStartIndex);
// count amount of manually introduced newline sequences in this text section (these to not exist in the data source!)
diff --git a/MultiTerm.Wpf/ValueConverters/ProtocolConnectionStateToBrushConverter.cs b/MultiTerm.Wpf/ValueConverters/ProtocolConnectionStateToBrushConverter.cs
new file mode 100644
index 0000000..1b11e58
--- /dev/null
+++ b/MultiTerm.Wpf/ValueConverters/ProtocolConnectionStateToBrushConverter.cs
@@ -0,0 +1,34 @@
+using MultiTerm.Protocols.Types;
+using System;
+using System.Globalization;
+using System.Windows.Data;
+using System.Windows.Media;
+
+namespace MultiTerm.Wpf.ValueConverters;
+
+///
+/// Converts a to a acoordingly colored .
+///
+[ValueConversion(typeof(ProtocolConnectionState), typeof(Brush))]
+public class ProtocolConnectionStateToBrushConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is not ProtocolConnectionState protocolConnectionState)
+ { throw new ArgumentException($"Wrong object provided, can only convert from type {nameof(ProtocolConnectionState)}"); }
+
+ return protocolConnectionState switch
+ {
+ ProtocolConnectionState.NotConnected => Brushes.LightGray,
+ ProtocolConnectionState.Connected => Brushes.LightGreen,
+ ProtocolConnectionState.UnintentionallyDisconnected => Brushes.OrangeRed,
+ _ => 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 b93566f..ca10d39 100644
--- a/MultiTerm.Wpf/View/ShellView.xaml
+++ b/MultiTerm.Wpf/View/ShellView.xaml
@@ -20,6 +20,7 @@
+
-
+
+ ToolTipService.InitialShowDelay="200"/>