diff --git a/ADIS_Csharp/RobotClientWpf/Assets/cog.png b/ADIS_Csharp/RobotClientWpf/Assets/cog.png
new file mode 100644
index 0000000..a39a424
Binary files /dev/null and b/ADIS_Csharp/RobotClientWpf/Assets/cog.png differ
diff --git a/ADIS_Csharp/RobotClientWpf/Assets/robot-industrial.png b/ADIS_Csharp/RobotClientWpf/Assets/robot-industrial.png
new file mode 100644
index 0000000..1babb14
Binary files /dev/null and b/ADIS_Csharp/RobotClientWpf/Assets/robot-industrial.png differ
diff --git a/ADIS_Csharp/RobotClientWpf/ChallengeFactory.cs b/ADIS_Csharp/RobotClientWpf/ChallengeFactory.cs
new file mode 100644
index 0000000..59f3366
--- /dev/null
+++ b/ADIS_Csharp/RobotClientWpf/ChallengeFactory.cs
@@ -0,0 +1,19 @@
+using RobotLib;
+using RobotLib.Communication;
+
+namespace RobotClientWpf
+{
+ internal class ChallengeFactory
+ {
+ public MqttPublisherSubscriber PublisherSubscriber { get; }
+ public Robot RobotStationary { get; }
+ public Robot RobotMobile { get; }
+
+ public ChallengeFactory()
+ {
+ this.PublisherSubscriber = MqttPublisherSubscriber.Instance;
+ //this.RobotStationary = new Robot(this.PublisherSubscriber);
+ //this.RobotMobile = new Robot(this.PublisherSubscriber);
+ }
+ }
+}
diff --git a/ADIS_Csharp/RobotClientWpf/IUiService.cs b/ADIS_Csharp/RobotClientWpf/IUiService.cs
new file mode 100644
index 0000000..fb46398
--- /dev/null
+++ b/ADIS_Csharp/RobotClientWpf/IUiService.cs
@@ -0,0 +1,15 @@
+namespace RobotClientWpf
+{
+ public interface IUiService
+ {
+ void DisplayBottomMessage(MessageSeverity severity, string message);
+ }
+ public enum MessageSeverity
+ {
+ Unknown = 0,
+ Success,
+ Warning,
+ Error,
+ Information
+ }
+}
diff --git a/ADIS_Csharp/RobotClientWpf/MainWindow.xaml b/ADIS_Csharp/RobotClientWpf/MainWindow.xaml
index 069ff63..e2a3025 100644
--- a/ADIS_Csharp/RobotClientWpf/MainWindow.xaml
+++ b/ADIS_Csharp/RobotClientWpf/MainWindow.xaml
@@ -4,11 +4,50 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RobotClientWpf"
- mc:Ignorable="d"
- Title="RobotClientWpf" Height="450" Width="800">
+ xmlns:views="clr-namespace:RobotClientWpf.Views"
+ mc:Ignorable="d" FontSize="15"
+ Title="Challenge UI" Height="700" Width="1200" WindowStartupLocation="CenterScreen" ResizeMode="NoResize">
-
-
+
+
+
+
+
+
+
+ 192.168.156.156
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ADIS_Csharp/RobotClientWpf/MainWindow.xaml.cs b/ADIS_Csharp/RobotClientWpf/MainWindow.xaml.cs
index 4bdc636..231c149 100644
--- a/ADIS_Csharp/RobotClientWpf/MainWindow.xaml.cs
+++ b/ADIS_Csharp/RobotClientWpf/MainWindow.xaml.cs
@@ -1,17 +1,9 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using System.Net;
using System.Threading.Tasks;
using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
-using System.Windows.Input;
using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Navigation;
-using System.Windows.Shapes;
+using RobotClientWpf.Utilities;
using RobotLib;
namespace RobotClientWpf
@@ -21,11 +13,24 @@ namespace RobotClientWpf
///
public partial class MainWindow : Window
{
+ private ChallengeFactory challenge;
+ private bool manualDisconnect;
+
public MainWindow()
{
InitializeComponent();
- Robot.Instance.Connect("host", 1818);
- Robot.Instance.Battery.BatteryChanged += BatteryChanged;
+ this.challenge = new ChallengeFactory();
+ this.challenge.PublisherSubscriber.ConnectionStateChanged += PublisherSubscriber_ConnectionStateChanged;
+ }
+
+ private void PublisherSubscriber_ConnectionStateChanged(object? sender, bool e)
+ {
+ if(e == false)
+ {
+ if (manualDisconnect == true) manualDisconnect = false; return;
+ this.DisplayBottomMessage(MessageSeverity.Error, "Connection to MQTT broker lost.");
+ this.SetIpFieldsState(true, true, "Connect");
+ }
}
private void BatteryChanged(object? sender, BatteryEventArgs e)
@@ -37,13 +42,90 @@ namespace RobotClientWpf
}
else
{
- labelBattery.Content = $"Battery: {e.Voltage} V";
+ //labelBattery.Content = $"Battery: {e.Voltage} V";
}
}
private void buttonBuzz_Click(object sender, RoutedEventArgs e)
{
- Robot.Instance.Buzzer.Beep(300, 500);
+ //Robot.Instance.Buzzer.Beep(300, 500);
+ }
+
+ private void btnConnectDisconnect_Click(object sender, RoutedEventArgs e)
+ {
+ Task.Run(delegate ()
+ {
+ this.ClearBottomMessage();
+ if (this.challenge.PublisherSubscriber.IsConnected) // is connected => disconnect
+ {
+ manualDisconnect = true; // prevent wrong message
+ this.challenge.PublisherSubscriber.Disconnect();
+ this.DisplayBottomMessage(MessageSeverity.Success, "Disconnected from MQTT broker.");
+ this.SetIpFieldsState(true, true, "Connect");
+ }
+ else // is not yet connected => connect
+ {
+ this.SetIpFieldsState(false, false, "Connecting...");
+ string ipAddress = UIAccessHelpers.GetTextboxText(tbIp);
+ if (IPAddress.TryParse(ipAddress, out _))
+ {
+ var connectionSuccess = this.challenge.PublisherSubscriber.Connect(ipAddress);
+ if (connectionSuccess)
+ {
+ this.DisplayBottomMessage(MessageSeverity.Success, "Successfully connected to MQTT broker.");
+ this.SetIpFieldsState(false, true, "Disconnect");
+ }
+ else
+ {
+ this.DisplayBottomMessage(MessageSeverity.Error, $"Failed to connect to MQTT broker on address {ipAddress}.");
+ this.SetIpFieldsState(true, true, "Connect");
+ }
+ }
+ else
+ {
+ this.DisplayBottomMessage(MessageSeverity.Error, $"Unable to parse MQTT Broker IP address {ipAddress}.");
+ this.SetIpFieldsState(true, true, "Connect");
+ }
+ }
+ });
+ }
+
+ private void SetIpFieldsState(bool ipTextbox, bool connectDisconnectButton, string buttonText)
+ {
+ UIAccessHelpers.SetButtonState(btnConnectDisconnect, connectDisconnectButton);
+ UIAccessHelpers.SetTextboxState(tbIp, ipTextbox);
+ UIAccessHelpers.SetButtonContent(btnConnectDisconnect, buttonText);
+ }
+
+ public void DisplayBottomMessage(MessageSeverity severity, string message)
+ {
+ Brush col;
+ switch (severity)
+ {
+ case MessageSeverity.Success:
+ col = Brushes.Green;
+ break;
+ case MessageSeverity.Warning:
+ col = Brushes.Orange;
+ break;
+ case MessageSeverity.Error:
+ col = Brushes.Red;
+ break;
+ case MessageSeverity.Information:
+ col = Brushes.Blue;
+ break;
+ case MessageSeverity.Unknown:
+ default:
+ col = Brushes.DarkGray;
+ break;
+ }
+
+ UIAccessHelpers.SetTextblockTextAndForegroundColor(tbBottomMessage, message, col);
+ }
+
+ private void ClearBottomMessage()
+ {
+ this.DisplayBottomMessage(MessageSeverity.Information, "");
}
}
}
diff --git a/ADIS_Csharp/RobotClientWpf/NLog.config b/ADIS_Csharp/RobotClientWpf/NLog.config
new file mode 100644
index 0000000..7da0939
--- /dev/null
+++ b/ADIS_Csharp/RobotClientWpf/NLog.config
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ADIS_Csharp/RobotClientWpf/RobotClientWpf.csproj b/ADIS_Csharp/RobotClientWpf/RobotClientWpf.csproj
index 08b8f5f..fed4496 100644
--- a/ADIS_Csharp/RobotClientWpf/RobotClientWpf.csproj
+++ b/ADIS_Csharp/RobotClientWpf/RobotClientWpf.csproj
@@ -7,8 +7,22 @@
true
+
+
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+
diff --git a/ADIS_Csharp/RobotClientWpf/Utilities/UIAccessHelpers.cs b/ADIS_Csharp/RobotClientWpf/Utilities/UIAccessHelpers.cs
new file mode 100644
index 0000000..b43aa37
--- /dev/null
+++ b/ADIS_Csharp/RobotClientWpf/Utilities/UIAccessHelpers.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Media;
+
+namespace RobotClientWpf.Utilities
+{
+ public static class UIAccessHelpers
+ {
+ public static void SetButtonState(ButtonBase button, bool enabled)
+ {
+ if (!Application.Current.Dispatcher.CheckAccess())
+ {
+ Application.Current.Dispatcher.BeginInvoke(new Action(() =>
+ {
+ SetButtonState(button, enabled);
+ }));
+ }
+ else
+ {
+ button.IsEnabled = enabled;
+ }
+ }
+ public static void SetButtonContent(ButtonBase button, string content)
+ {
+ if (!Application.Current.Dispatcher.CheckAccess())
+ {
+ Application.Current.Dispatcher.BeginInvoke(new Action(() =>
+ {
+ SetButtonContent(button, content);
+ }));
+ }
+ else
+ {
+ button.Content = content;
+ }
+ }
+
+ public static string GetTextboxText(TextBox textBox)
+ {
+ string text = String.Empty;
+ if (!Application.Current.Dispatcher.CheckAccess())
+ {
+ Application.Current.Dispatcher.BeginInvoke(new Action(() =>
+ {
+ text = GetTextboxText(textBox);
+ })).Wait();
+ }
+ else
+ {
+ text = textBox.Text;
+ }
+ return text;
+ }
+
+ public static void SetTextboxState(TextBoxBase textBox, bool enabled)
+ {
+ if (!Application.Current.Dispatcher.CheckAccess())
+ {
+ Application.Current.Dispatcher.BeginInvoke(new Action(() =>
+ {
+ SetTextboxState(textBox, enabled);
+ }));
+ }
+ else
+ {
+ textBox.IsEnabled = enabled;
+ }
+ }
+
+ public static void SetTextboxText(TextBox textBox, string text)
+ {
+ if (!Application.Current.Dispatcher.CheckAccess())
+ {
+ Application.Current.Dispatcher.BeginInvoke(new Action(() =>
+ {
+ SetTextboxText(textBox, text);
+ }));
+ }
+ else
+ {
+ textBox.Text = text;
+ }
+ }
+
+ public static void SetTextblockTextAndForegroundColor(TextBlock textBlock, string text, Brush foregroundColor)
+ {
+ if (!Application.Current.Dispatcher.CheckAccess())
+ {
+ Application.Current.Dispatcher.BeginInvoke(new Action(() =>
+ {
+ SetTextblockTextAndForegroundColor(textBlock, text, foregroundColor);
+ }));
+ }
+ else
+ {
+ textBlock.Text = text;
+ textBlock.Foreground = foregroundColor;
+ }
+ }
+ }
+}
diff --git a/ADIS_Csharp/RobotClientWpf/Views/ConfigView.xaml b/ADIS_Csharp/RobotClientWpf/Views/ConfigView.xaml
new file mode 100644
index 0000000..97a0623
--- /dev/null
+++ b/ADIS_Csharp/RobotClientWpf/Views/ConfigView.xaml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/ADIS_Csharp/RobotClientWpf/Views/ConfigView.xaml.cs b/ADIS_Csharp/RobotClientWpf/Views/ConfigView.xaml.cs
new file mode 100644
index 0000000..f7dfbd6
--- /dev/null
+++ b/ADIS_Csharp/RobotClientWpf/Views/ConfigView.xaml.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace RobotClientWpf.Views
+{
+ ///
+ /// Interaction logic for ConfigView.xaml
+ ///
+ public partial class ConfigView : UserControl
+ {
+ public ConfigView()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/ADIS_Csharp/RobotClientWpf/Views/MainView.xaml b/ADIS_Csharp/RobotClientWpf/Views/MainView.xaml
new file mode 100644
index 0000000..37b735b
--- /dev/null
+++ b/ADIS_Csharp/RobotClientWpf/Views/MainView.xaml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/ADIS_Csharp/RobotClientWpf/Views/MainView.xaml.cs b/ADIS_Csharp/RobotClientWpf/Views/MainView.xaml.cs
new file mode 100644
index 0000000..8e7df43
--- /dev/null
+++ b/ADIS_Csharp/RobotClientWpf/Views/MainView.xaml.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace RobotClientWpf.Views
+{
+ ///
+ /// Interaction logic for MainView.xaml
+ ///
+ public partial class MainView : UserControl
+ {
+ public MainView()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/ADIS_Csharp/RobotLib/Communication/MqttPublisherSubscriber.cs b/ADIS_Csharp/RobotLib/Communication/MqttPublisherSubscriber.cs
index 9c66fde..be31f5c 100644
--- a/ADIS_Csharp/RobotLib/Communication/MqttPublisherSubscriber.cs
+++ b/ADIS_Csharp/RobotLib/Communication/MqttPublisherSubscriber.cs
@@ -21,10 +21,28 @@ namespace RobotLib.Communication
///
public event EventHandler NewMessageArrived;
+ ///
+ /// Event to catch for the connection state to the broker.
+ ///
+ public event EventHandler ConnectionStateChanged;
+
///
/// Connection state to the broker.
///
- public bool IsConnected { get ; private set; }
+ public bool IsConnected
+ {
+ get { return _isConnected; }
+ private set
+ {
+ // generate event if the value has changed
+ if(_isConnected != value)
+ {
+ this.ConnectionStateChanged?.Invoke(this, value);
+ }
+ _isConnected = value;
+ }
+ }
+ private bool _isConnected;
private MqttPublisherSubscriber()
{ }
@@ -63,14 +81,22 @@ namespace RobotLib.Communication
client.ConnectionClosed += Client_ConnectionClosed;
// generate a clientID and connect to Broker
string clientId = Guid.NewGuid().ToString();
- if (String.IsNullOrEmpty(username) || String.IsNullOrEmpty(password))
+ try
{
- client.Connect(clientId);
+ if (String.IsNullOrEmpty(username) || String.IsNullOrEmpty(password))
+ {
+ client.Connect(clientId);
+ }
+ else
+ {
+ client.Connect(clientId, username, password);
+ }
}
- else
+ catch (Exception ex)
{
- client.Connect(clientId, username, password);
+ log.Error(ex.Message);
}
+
success = client.IsConnected;
log.Info($"Connecting done. Success = {success}");
}
@@ -83,7 +109,12 @@ namespace RobotLib.Communication
{
if (IsConnected == false) return;
- client.Disconnect();
+ try
+ {
+ client.Disconnect();
+ }
+ catch { }
+
log.Info($"Disconnect was called.");
IsConnected = false;
}