implemented IndependentRobotConfigurator to adopt robots via UDP,

updated UI to support IndependentRobotConfigurator commands
main
Jonas Arnold 4 years ago
parent d43c04d3ad
commit ad76d09438
  1. 2
      ADIS_Csharp/RobotClientWpf/MainWindow.xaml
  2. 13
      ADIS_Csharp/RobotClientWpf/MainWindow.xaml.cs
  3. 28
      ADIS_Csharp/RobotClientWpf/Views/ConfigView.xaml
  4. 58
      ADIS_Csharp/RobotClientWpf/Views/ConfigView.xaml.cs
  5. 2
      ADIS_Csharp/RobotClientWpf/Views/MainView.xaml
  6. 12
      ADIS_Csharp/RobotClientWpf/Views/MainView.xaml.cs
  7. 73
      ADIS_Csharp/RobotLib/IndependentRobotConfigurator.cs

@ -7,7 +7,7 @@
xmlns:views="clr-namespace:RobotClientWpf.Views" xmlns:views="clr-namespace:RobotClientWpf.Views"
xmlns:dj="clr-namespace:DJ;assembly=NLogViewer" xmlns:dj="clr-namespace:DJ;assembly=NLogViewer"
mc:Ignorable="d" FontSize="15" mc:Ignorable="d" FontSize="15"
Title="Challenge UI" Height="700" Width="1200" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" Closing="Window_Closing" Loaded="Window_Loaded"> Title="Challenge UI" Height="700" Width="1200" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" Closing="Window_Closing" Loaded="Window_Loaded" KeyDown="Window_KeyDown">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="40"/> <RowDefinition Height="40"/>

@ -20,7 +20,7 @@ namespace RobotClientWpf
private static readonly string mutexName = "00489402-435c-426e-9d11-9a2b839b39b6"; // random guid private static readonly string mutexName = "00489402-435c-426e-9d11-9a2b839b39b6"; // random guid
private static readonly NLog.Logger log = NLog.LogManager.GetCurrentClassLogger(); private static readonly NLog.Logger log = NLog.LogManager.GetCurrentClassLogger();
private ChallengeFactory? challenge; private ChallengeFactory challenge;
private bool manualDisconnect; private bool manualDisconnect;
public MainWindow() public MainWindow()
@ -43,9 +43,9 @@ namespace RobotClientWpf
// start window // start window
InitializeComponent(); InitializeComponent();
this.challenge = new ChallengeFactory(); this.challenge = new ChallengeFactory();
// inform other views about challenge factory // inform other views about challenge factory and this view
this.mainView.SetChallengeFactory(this.challenge); this.mainView.InitializeChildView(this.challenge, this);
this.configView.SetChallengeFactory(this.challenge); this.configView.InitializeChildView(this.challenge, this);
// subscribe to events // subscribe to events
this.challenge.PublisherSubscriber.ConnectionStateChanged += PublisherSubscriber_ConnectionStateChanged; this.challenge.PublisherSubscriber.ConnectionStateChanged += PublisherSubscriber_ConnectionStateChanged;
} }
@ -173,5 +173,10 @@ namespace RobotClientWpf
{ {
this.tbIp.Text = Settings.Default.MqttBrokerIp; this.tbIp.Text = Settings.Default.MqttBrokerIp;
} }
private void Window_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
this.mainView.HandleKeyDownEvent(sender, e);
}
} }
} }

@ -5,12 +5,13 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:RobotClientWpf.Views" xmlns:local="clr-namespace:RobotClientWpf.Views"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="430" d:DesignWidth="1200" FontSize="15">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="120"/>
<RowDefinition Height="120"/> <RowDefinition Height="120"/>
<RowDefinition Height="150"/> <RowDefinition Height="150"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
@ -50,13 +51,12 @@
<Grid Margin="5"> <Grid Margin="5">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/> <ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
@ -65,9 +65,29 @@
<Label Grid.Row="1" Grid.Column="0" Content="Calibration data:" /> <Label Grid.Row="1" Grid.Column="0" Content="Calibration data:" />
<TextBox Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" x:Name="tbCalibrationData" IsReadOnly="True" Width="500" Height="30" /> <TextBox Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" x:Name="tbCalibrationData" IsReadOnly="True" Width="500" Height="30" />
<Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" x:Name="btnGetCalibrationData" DockPanel.Dock="Right" Content="Refresh" Margin="10 0" Width="100" Height="30" Click="btnGetCalibrationData_Click"/> <Button Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" HorizontalAlignment="Right" x:Name="btnGetCalibrationData" Content="Refresh" Width="100" Height="30" Click="btnGetCalibrationData_Click"/>
</Grid> </Grid>
</DockPanel> </DockPanel>
</GroupBox> </GroupBox>
<!-- ROBOT CONFIGURATOR -->
<GroupBox Grid.Row="2" Grid.Column="0" Header="Robot configurator">
<DockPanel Margin="5">
<StackPanel Orientation="Vertical">
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal" VerticalAlignment="Top">
<Label DockPanel.Dock="Left" Content="Hostname:" Margin="5 0" Height="30" Width="100"/>
<TextBox DockPanel.Dock="Left" x:Name="tbRobotConfiguratorHostname" Width="200" Height="30" Margin="5" TextChanged="tbRobotConfiguratorHostname_TextChanged"/>
<Button DockPanel.Dock="Left" x:Name="btnClearRobotConfHostname" Content="Clear" Margin="5" Width="50" Height="30" Click="btnClearRobotConfHostname_Click" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label DockPanel.Dock="Left" Content="Options:" Margin="5 0" Height="30" Width="100"/>
<Button x:Name="btnSetBrokerIp" Content="Update Broker IP" Margin="5" Width="120" Height="30" HorizontalAlignment="Left" Click="btnSetBrokerIp_Click"/>
<Button x:Name="btnReboot" Content="Reboot ESP32" Margin="5" Width="120" Height="30" HorizontalAlignment="Left" Click="btnReboot_Click" />
<Button x:Name="btnSetModeStationary" Content="Set Mode Stationary" Margin="5" Width="150" Height="30" HorizontalAlignment="Left" Click="btnSetModeStationary_Click" />
<Button x:Name="btnSetModeMobile" Content="Set Mode Mobile" Margin="5" Width="120" Height="30" HorizontalAlignment="Left" Click="btnSetModeMobile_Click" />
</StackPanel>
</StackPanel>
</DockPanel>
</GroupBox>
</Grid> </Grid>
</UserControl> </UserControl>

@ -1,6 +1,7 @@
using RobotClientWpf.Utilities; using RobotClientWpf.Utilities;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
@ -13,15 +14,18 @@ namespace RobotClientWpf.Views
{ {
private static readonly NLog.Logger log = NLog.LogManager.GetCurrentClassLogger(); private static readonly NLog.Logger log = NLog.LogManager.GetCurrentClassLogger();
private ChallengeFactory? challenge; private ChallengeFactory? challenge;
private MainWindow? mainWindow;
public ConfigView() public ConfigView()
{ {
InitializeComponent(); InitializeComponent();
this.EnableRobotConfigurationOptions(false);
} }
public void SetChallengeFactory(ChallengeFactory challenge) public void InitializeChildView(ChallengeFactory challenge, MainWindow parent)
{ {
this.challenge = challenge; this.challenge = challenge;
this.mainWindow = parent;
this.challenge.RobotMobile.LineSensor.NewCalibrationDataArrived += LineSensor_NewCalibrationDataArrived; this.challenge.RobotMobile.LineSensor.NewCalibrationDataArrived += LineSensor_NewCalibrationDataArrived;
} }
@ -89,5 +93,57 @@ namespace RobotClientWpf.Views
{ {
this.challenge?.RobotStationary.SplitFlap.InitializeAllSplitflaps(); this.challenge?.RobotStationary.SplitFlap.InitializeAllSplitflaps();
} }
private void tbRobotConfiguratorHostname_TextChanged(object sender, TextChangedEventArgs e)
{
this.EnableRobotConfigurationOptions(String.IsNullOrEmpty(UIAccessHelpers.GetTextboxText(this.tbRobotConfiguratorHostname)) == false);
}
private void EnableRobotConfigurationOptions(bool enabled)
{
UIAccessHelpers.SetButtonState(btnSetBrokerIp, enabled);
UIAccessHelpers.SetButtonState(btnReboot, enabled);
UIAccessHelpers.SetButtonState(btnSetModeStationary, enabled);
UIAccessHelpers.SetButtonState(btnSetModeMobile, enabled);
}
private void btnClearRobotConfHostname_Click(object sender, RoutedEventArgs e)
{
UIAccessHelpers.SetTextboxText(tbRobotConfiguratorHostname, "");
}
private async void btnSetBrokerIp_Click(object sender, RoutedEventArgs e)
{
if (this.mainWindow == null) return;
UIAccessHelpers.SetButtonState(btnSetBrokerIp, false);
var brokerIp = UIAccessHelpers.GetTextboxText(this.mainWindow.tbIp);
var robotHostname = UIAccessHelpers.GetTextboxText(this.tbRobotConfiguratorHostname);
await Task.Run(() => RobotLib.IndependentRobotConfigurator.Adopt(robotHostname, brokerIp));
UIAccessHelpers.SetButtonState(btnSetBrokerIp, true);
}
private async void btnReboot_Click(object sender, RoutedEventArgs e)
{
UIAccessHelpers.SetButtonState(btnReboot, false);
var robotHostname = UIAccessHelpers.GetTextboxText(this.tbRobotConfiguratorHostname);
await Task.Run(() => RobotLib.IndependentRobotConfigurator.RebootEsp32(robotHostname));
UIAccessHelpers.SetButtonState(btnReboot, true);
}
private async void btnSetModeStationary_Click(object sender, RoutedEventArgs e)
{
UIAccessHelpers.SetButtonState(btnSetModeStationary, false);
var robotHostname = UIAccessHelpers.GetTextboxText(this.tbRobotConfiguratorHostname);
await Task.Run(() => RobotLib.IndependentRobotConfigurator.SetRobotMode(robotHostname, "s"));
UIAccessHelpers.SetButtonState(btnSetModeStationary, true);
}
private async void btnSetModeMobile_Click(object sender, RoutedEventArgs e)
{
UIAccessHelpers.SetButtonState(btnSetModeMobile, false);
var robotHostname = UIAccessHelpers.GetTextboxText(this.tbRobotConfiguratorHostname);
await Task.Run(() => RobotLib.IndependentRobotConfigurator.SetRobotMode(robotHostname, "m"));
UIAccessHelpers.SetButtonState(btnSetModeMobile, true);
}
} }
} }

@ -5,7 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:RobotClientWpf.Views" xmlns:local="clr-namespace:RobotClientWpf.Views"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" KeyDown="UserControl_KeyDown"> d:DesignHeight="430" d:DesignWidth="1200" FontSize="15" KeyDown="UserControl_KeyDown">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>

@ -1,5 +1,4 @@
using RobotClientWpf.Utilities; using RobotClientWpf.Utilities;
using System.DirectoryServices.ActiveDirectory;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
@ -12,6 +11,7 @@ namespace RobotClientWpf.Views
{ {
private static readonly NLog.Logger log = NLog.LogManager.GetCurrentClassLogger(); private static readonly NLog.Logger log = NLog.LogManager.GetCurrentClassLogger();
private ChallengeFactory? challenge; private ChallengeFactory? challenge;
private MainWindow? mainWindow;
private const int AMOUNT_SPEED_ADDED_PER_CLICK = 500; private const int AMOUNT_SPEED_ADDED_PER_CLICK = 500;
private const int TURN_ANGLE_PER_CLICK = 30; private const int TURN_ANGLE_PER_CLICK = 30;
@ -20,9 +20,10 @@ namespace RobotClientWpf.Views
InitializeComponent(); InitializeComponent();
} }
public void SetChallengeFactory(ChallengeFactory challenge) public void InitializeChildView(ChallengeFactory challenge, MainWindow parent)
{ {
this.challenge = challenge; this.challenge = challenge;
this.mainWindow = parent;
// subscribe to events // subscribe to events
this.challenge.RobotStationary.SplitFlap.SplitFlapDisplayChanged += this.SplitFlap_SplitFlapDisplayChanged; this.challenge.RobotStationary.SplitFlap.SplitFlapDisplayChanged += this.SplitFlap_SplitFlapDisplayChanged;
this.challenge.RobotMobile.Battery.BatteryChanged += Battery_BatteryChanged; this.challenge.RobotMobile.Battery.BatteryChanged += Battery_BatteryChanged;
@ -67,7 +68,7 @@ namespace RobotClientWpf.Views
this.challenge?.RobotMobile.Movement.Turn(-TURN_ANGLE_PER_CLICK); this.challenge?.RobotMobile.Movement.Turn(-TURN_ANGLE_PER_CLICK);
} }
private void UserControl_KeyDown(object sender, System.Windows.Input.KeyEventArgs e) public void HandleKeyDownEvent(object sender, KeyEventArgs e)
{ {
if (e.Key == Key.Up) if (e.Key == Key.Up)
{ {
@ -95,5 +96,10 @@ namespace RobotClientWpf.Views
e.Handled = true; e.Handled = true;
} }
} }
private void UserControl_KeyDown(object sender, KeyEventArgs e)
{
this.HandleKeyDownEvent(sender, e);
}
} }
} }

@ -0,0 +1,73 @@
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace RobotLib
{
public static class IndependentRobotConfigurator
{
private static readonly NLog.Logger log = NLog.LogManager.GetCurrentClassLogger();
private const int udpPortRobot = 1818;
/// <summary>
/// Sends a command via UDP to set the MQTT broker IP on a robot.
/// This serves to adopt a robot (get it to communicate via MQTT).
/// Consider rebooting ESP32 with <see cref="RebootEsp32(string)"/> to apply the newly set IP.
/// </summary>
/// <param name="hostnameRobot">hostname or IP address of the robot</param>
/// <param name="brokerIp">ip address of the MQTT broker</param>
public static void Adopt(string hostnameRobot, string brokerIp)
{
// test if IP valid
if (IPAddress.TryParse(brokerIp, out _))
{
SendMessageUdp(hostnameRobot, udpPortRobot, $"mqtt setIp {brokerIp}");
}
else
{
log.Error($"Invalid broker IP address: {brokerIp}");
}
}
/// <summary>
/// Sends a command via UDP to set the challenge mode of a robot.
/// </summary>
/// <param name="hostnameRobot">hostname or IP address of the robot</param>
/// <param name="brokerIp">ip address of the MQTT broker</param>
public static void SetRobotMode(string hostnameRobot, string mode)
{
// test if mode is valid
if (mode == "stationary" || mode == "s" || mode == "mobile" || mode == "m")
{
SendMessageUdp(hostnameRobot, udpPortRobot, $"challenge setMode {mode}");
}
else
{
log.Error($"Invalid robot mode: {mode}");
}
}
/// <summary>
/// Sends a command via UDP to reboot the ESP32 of a robot.
/// For example when a new mqtt broker IP was set, the robot needs to reboot in order to apply the IP and reconnect to a new broker.
/// </summary>
/// <param name="hostnameRobot">hostname or IP address of the robot</param>
public static void RebootEsp32(string hostnameRobot)
{
SendMessageUdp(hostnameRobot, udpPortRobot, $"challenge reboot");
}
private static void SendMessageUdp(string hostname, int port, string message)
{
var udpClient = new UdpClient();
udpClient.Connect(hostname, port);
byte[] data = Encoding.ASCII.GetBytes(message);
udpClient.Send(data, data.Length);
log.Trace($"Msg sent to {udpClient.Client.RemoteEndPoint}: {message}");
udpClient.Close();
}
}
}
Loading…
Cancel
Save