Skip to content

Commit

Permalink
Merge #3235 - Specify network interface in thunderscope
Browse files Browse the repository at this point in the history
  • Loading branch information
williamckha committed Jul 16, 2024
1 parent a8b1261 commit 30e5b7d
Show file tree
Hide file tree
Showing 32 changed files with 859 additions and 151 deletions.
20 changes: 13 additions & 7 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,11 @@ Now that you're setup, if you can run it on the command line, you can run it in

- If we want to run it with real robots:
- Open your terminal, `cd` into `Software/src` and run `ifconfig`.
- Pick the network interface you would like to use:
1. If you are running things locally, you can pick any interface that is not `lo`
2. If you would like to communicate with robots on the network, make sure to select the interface that is connected to the same network as the robots.
- Pick the network interface you would like to use. If you would like to communicate with robots on the network, make sure to select the interface that is connected to the same network as the robots.
- For example, on a sample machine, the output may look like this:

```
enp0s5: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
wlp3s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
...
[omitted]
...
Expand All @@ -241,22 +239,30 @@ Now that you're setup, if you can run it on the command line, you can run it in
...
```

- An appropriate interface we could choose is `enp0s5`
- An appropriate interface we could choose is `wlp3s0`
- Hint: If you are using a wired connection, the interface will likely start with `e-`. If you are using a WiFi connection, the interface will likely start with `w-`.
- If we are running the AI as "blue": `./tbots.py run thunderscope_main --interface=[interface_here] --run_blue`
- If we are running the AI as "yellow": `./tbots.py run thunderscope_main --interface=[interface_here] --run_yellow`
- `[interface_here]` corresponds to the `ifconfig` interfaces seen in the previous step
- For instance, a call to run the AI as blue on wifi could be: `./tbots.py run thunderscope_main --interface=enp0s5 --run_blue`
- For instance, a call to run the AI as blue on WiFi could be: `./tbots.py run thunderscope_main --interface=wlp3s0 --run_blue`. This will start Thunderscope and set up communication with robots over the wifi interface. It will also listen for referee and vision messages on the same interface.
- **Note: You do not need to include the `--interface=[interface_here]` argument!** You can run Thunderscope without it and use the dynamic configuration widget to set the interfaces for communication to send and receive robot, vision and referee messages.
- If you choose to include `--interface=[interface_here]` argument, Thunderscope will listen for and send robot messages on this port as well as receive vision and referee messages.
- Using the dynamic configuration widget is recommended at Robocup. To reduce latencies, it is recommended to connect the robot router to the AI computer via ethernet and use a separate ethernet connection to receive vision and referee messages. In this configuration, Thunderscope will need to bind to two different interfaces, each likely starting with a "e-".
- If you have specified `--run_blue` or `--run_yellow`, navigate to the "Parameters" widget. In "ai_config" > "ai_control_config" > "network_config", you can set the appropriate interface using the dropdowns for robot, vision and referee message communication.
- This command will set up robot communication and the Unix full system binary context manager. The Unix full system context manager hooks up our AI, Backend and SensorFusion
2. Run AI along with Robot Diagnostics:
- The Mechanical and Electrical sub-teams use Robot Diagnostics to test specific parts of the Robot.
- If we want to run with one AI and Diagnostics
- `./tbots.py run thunderscope_main [--run_blue | --run_yellow] --run_diagnostics` will start Thunderscope
- `./tbots.py run thunderscope_main [--run_blue | --run_yellow] --run_diagnostics --interface=[interface_here]` will start Thunderscope
- `[--run_blue | --run_yellow]` indicate which FullSystem to run
- `--run_diagnostics` indicates if diagnostics should be loaded as well
- Initially, the robots are all connected to the AI and only receive input from it
- To change the input source for the robot, use the drop-down menu of that robot to change it between None, AI, and Manual
- None means the robots are receiving no commands
- More info about Manual control below
- `--interface=[interface_here]` corresponds to the `ifconfig` interfaces seen in the previous step
- For instance, a call to run the AI as blue on WiFi could be: `./tbots.py run thunderscope_main --interface=wlp3s0 --run_blue --run_diagnostics`
- The `--interface` flag is optional. If you do not include it, you can set the interface in the dynamic configuration widget. See above for how to set the interface in the dynamic configuration widget.
3. Run only Diagnostics
- To run just Diagnostics
- `./tbots.py run thunderscope --run_diagnostics --interface <network_interface>`
Expand Down
15 changes: 15 additions & 0 deletions src/proto/parameters.proto
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ message AiControlConfig

// Override the existing play with the Play enum provided
required PlayName override_ai_play = 2 [default = UseAiSelection];

// Interfaces for various network listeners
required NetworkConfig network_config = 3;
}

message AiParameterConfig
Expand Down Expand Up @@ -719,6 +722,18 @@ message PossessionTrackerConfig
];
}

message NetworkConfig
{
// The robot communication interface
required string robot_communication_interface = 1 [default = "lo"];

// The referee interface
required string referee_interface = 2 [default = "lo"];

// The vision interface
required string vision_interface = 3 [default = "lo"];
}

message CreaseDefenderConfig
{
// The additional buffer length for each side of the goal
Expand Down
19 changes: 15 additions & 4 deletions src/software/embedded/services/network/network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,27 @@

NetworkService::NetworkService(const std::string& ip_address,
unsigned short primitive_listener_port,
unsigned short robot_status_sender_port, bool multicast)
unsigned short robot_status_sender_port,
const std::string& interface, bool multicast)
: primitive_tracker(ProtoTracker("primitive set"))
{
std::optional<std::string> error;
sender = std::make_unique<ThreadedProtoUdpSender<TbotsProto::RobotStatus>>(
ip_address, robot_status_sender_port, multicast);
ip_address, robot_status_sender_port, interface, multicast, error);
if (error)
{
LOG(FATAL) << *error;
}

udp_listener_primitive_set =
std::make_unique<ThreadedProtoUdpListener<TbotsProto::PrimitiveSet>>(
ip_address, primitive_listener_port,
boost::bind(&NetworkService::primitiveSetCallback, this, _1), multicast);
ip_address, primitive_listener_port, interface,
boost::bind(&NetworkService::primitiveSetCallback, this, _1), multicast,
error);
if (error)
{
LOG(FATAL) << *error;
}

radio_listener_primitive_set =
std::make_unique<ThreadedProtoRadioListener<TbotsProto::PrimitiveSet>>(
Expand Down
4 changes: 3 additions & 1 deletion src/software/embedded/services/network/network.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ class NetworkService
* @param ip_address The IP Address the service should connect to
* @param primitive_listener_port The port to listen for primitive protos
* @param robot_status_sender_port The port to send robot status
* @param interface the interface to listen and send on
* @param multicast If true, then the provided IP address is a multicast address and
* we should join the group
*/
NetworkService(const std::string& ip_address, unsigned short primitive_listener_port,
unsigned short robot_status_sender_port, bool multicast);
unsigned short robot_status_sender_port, const std::string& interface,
bool multicast);

/**
* When the network service is polled, it sends the robot_status and returns
Expand Down
10 changes: 5 additions & 5 deletions src/software/embedded/thunderloop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ extern "C"
crash_msg.set_exit_signal(g3::signalToStr(signal_num));
*(crash_msg.mutable_status()) = *robot_status;

std::optional<std::string> error;
auto sender = std::make_unique<ThreadedProtoUdpSender<TbotsProto::RobotCrash>>(
std::string(ROBOT_MULTICAST_CHANNELS.at(channel_id)) + "%" +
network_interface,
ROBOT_CRASH_PORT, true);
std::string(ROBOT_MULTICAST_CHANNELS.at(channel_id)), ROBOT_CRASH_PORT,
network_interface, true, error);
sender->sendProto(crash_msg);
std::cerr << "Broadcasting robot crash msg";

Expand Down Expand Up @@ -108,8 +108,8 @@ Thunderloop::Thunderloop(const RobotConstants_t& robot_constants, bool enable_lo
<< "THUNDERLOOP: Network Logger initialized! Next initializing Network Service";

network_service_ = std::make_unique<NetworkService>(
std::string(ROBOT_MULTICAST_CHANNELS.at(channel_id_)) + "%" + network_interface_,
PRIMITIVE_PORT, ROBOT_STATUS_PORT, true);
std::string(ROBOT_MULTICAST_CHANNELS.at(channel_id_)), PRIMITIVE_PORT,
ROBOT_STATUS_PORT, network_interface, true);
LOG(INFO)
<< "THUNDERLOOP: Network Service initialized! Next initializing Power Service";

Expand Down
10 changes: 8 additions & 2 deletions src/software/logger/network_sink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,15 @@ NetworkSink::NetworkSink(unsigned int channel, const std::string& interface, int
bool enable_log_merging)
: robot_id(robot_id), log_merger(LogMerger(enable_log_merging))
{
std::optional<std::string> error;
log_output.reset(new ThreadedProtoUdpSender<TbotsProto::RobotLog>(
std::string(ROBOT_MULTICAST_CHANNELS.at(channel)) + "%" + interface,
ROBOT_LOGS_PORT, true));
std::string(ROBOT_MULTICAST_CHANNELS.at(channel)), ROBOT_LOGS_PORT, interface,
true, error));
if (error)
{
std::cerr << error.value() << std::endl;
std::terminate();
}
}

void NetworkSink::sendToNetwork(g3::LogMessageMover log_entry)
Expand Down
11 changes: 8 additions & 3 deletions src/software/logger/plotjuggler_sink.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@

#include "software/logger/plotjuggler_sink.h"

#include <google/protobuf/util/json_util.h>

#include "shared/constants.h"

PlotJugglerSink::PlotJugglerSink()
: udp_sender(PLOTJUGGLER_GUI_DEFAULT_HOST, PLOTJUGGLER_GUI_DEFAULT_PORT, false)
PlotJugglerSink::PlotJugglerSink(const std::string& interface)
: udp_sender(PLOTJUGGLER_GUI_DEFAULT_HOST, PLOTJUGGLER_GUI_DEFAULT_PORT, interface,
false, error)
{
if (error.has_value())
{
std::cerr << "Error setting up UDP sender for PlotJugglerSink: " << error.value();
std::terminate();
}
}

void PlotJugglerSink::sendToPlotJuggler(g3::LogMessageMover log_entry)
Expand Down
7 changes: 6 additions & 1 deletion src/software/logger/plotjuggler_sink.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ class PlotJugglerSink
public:
/**
* Creates a PlotJugglerSink that sends udp packets to the PlotJuggler server
*
* @param interface The interface to send Plotjuggler UDP packets on
*/
PlotJugglerSink();
PlotJugglerSink(const std::string& interface = "lo");

~PlotJugglerSink() = default;

Expand All @@ -30,6 +32,9 @@ class PlotJugglerSink
void sendToPlotJuggler(g3::LogMessageMover log_entry);

private:
// Any error that occurs during the creation of the UDP sender will be stored here
std::optional<std::string> error;

ThreadedUdpSender udp_sender;
};

Expand Down
9 changes: 7 additions & 2 deletions src/software/network_log_listener_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,14 @@ int main(int argc, char **argv)
logFromNetworking(log);
};

std::optional<std::string> error;
auto log_input = std::make_unique<ThreadedProtoUdpListener<TbotsProto::RobotLog>>(
std::string(ROBOT_MULTICAST_CHANNELS.at(args.channel)) + "%" + args.interface,
ROBOT_LOGS_PORT, robot_log_callback, true);
std::string(ROBOT_MULTICAST_CHANNELS.at(args.channel)), ROBOT_LOGS_PORT,
args.interface, robot_log_callback, true, error);
if (error)
{
LOG(FATAL) << *error;
}


LOG(INFO) << "Network logger listening on channel "
Expand Down
52 changes: 52 additions & 0 deletions src/software/networking/udp/BUILD
Original file line number Diff line number Diff line change
@@ -1,12 +1,34 @@
package(default_visibility = ["//visibility:public"])

cc_library(
name = "network_utils",
srcs = [
"network_utils.cpp",
],
hdrs = [
"network_utils.h",
],
)

cc_test(
name = "network_utils_test",
srcs = [
"network_utils_test.cpp",
],
deps = [
":network_utils",
"//shared/test_util:tbots_gtest_main",
],
)

cc_library(
name = "proto_udp_listener",
hdrs = [
"proto_udp_listener.hpp",
],
visibility = ["//visibility:private"],
deps = [
":network_utils",
"//software/logger",
"//software/util/typename",
],
Expand All @@ -22,6 +44,7 @@ cc_library(
],
visibility = ["//visibility:private"],
deps = [
":network_utils",
"@boost//:asio",
],
)
Expand All @@ -37,6 +60,17 @@ cc_library(
],
)

cc_test(
name = "threaded_proto_udp_listener_test",
srcs = [
"threaded_proto_udp_listener_test.cpp",
],
deps = [
":threaded_proto_udp_listener",
"//shared/test_util:tbots_gtest_main",
],
)

cc_library(
name = "threaded_proto_udp_sender",
hdrs = [
Expand All @@ -47,6 +81,17 @@ cc_library(
],
)

cc_test(
name = "threaded_proto_udp_sender_test",
srcs = [
"threaded_proto_udp_sender_test.cpp",
],
deps = [
":threaded_proto_udp_sender",
"//shared/test_util:tbots_gtest_main",
],
)

cc_library(
name = "threaded_udp_sender",
srcs = [
Expand All @@ -59,3 +104,10 @@ cc_library(
":udp_sender",
],
)

cc_library(
name = "udp_network_factory",
hdrs = [
"udp_network_factory.hpp",
],
)
44 changes: 44 additions & 0 deletions src/software/networking/udp/network_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "software/networking/udp/network_utils.h"

#include <arpa/inet.h>

bool getLocalIp(const std::string& interface, std::string& ip_address, bool ipv4)
{
struct ifaddrs* ifAddrStruct = nullptr;
struct ifaddrs* ifa = nullptr;

getifaddrs(&ifAddrStruct);

for (ifa = ifAddrStruct; ifa != nullptr; ifa = ifa->ifa_next)
{
if (ifa->ifa_name == interface)
{
if (ipv4 && ifa->ifa_addr->sa_family == AF_INET)
{
char addressBuffer[INET_ADDRSTRLEN];
struct sockaddr_in* sa = (struct sockaddr_in*)ifa->ifa_addr;
inet_ntop(AF_INET, &sa->sin_addr, addressBuffer, INET_ADDRSTRLEN);
freeifaddrs(ifAddrStruct);
ip_address = addressBuffer;
return true;
}
else if (!ipv4 && ifa->ifa_addr->sa_family == AF_INET6)
{
char addressBuffer[INET6_ADDRSTRLEN];
struct sockaddr_in6* sa = (struct sockaddr_in6*)ifa->ifa_addr;
inet_ntop(AF_INET6, &sa->sin6_addr, addressBuffer, INET6_ADDRSTRLEN);
freeifaddrs(ifAddrStruct);
ip_address = addressBuffer;
return true;
}
}
}

return false;
}

bool isIpv6(const std::string& ip_address)
{
struct sockaddr_in6 sa;
return inet_pton(AF_INET6, ip_address.c_str(), &(sa.sin6_addr)) != 0;
}
30 changes: 30 additions & 0 deletions src/software/networking/udp/network_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once

#include <ifaddrs.h>

#include <string>

/**
* Given an interface, get the IP address associated with that interface
*
* The modified ip_address is valid only if the function returns true
*
* @param interface The interface to get the IP address from
* @param ip_address A reference to the std::string that will store the IP address if
* found
* @param ipv4 If true, get the IPv4 address, otherwise get the IPv6 address
*
* @return true if the IP address was found, false otherwise
*/
bool getLocalIp(const std::string& interface, std::string& ip_address, bool ipv4 = true);

/**
* Check if the given string follows the IPv6 address format
*
* Addresses that are actually an "embedded IPv4 address" are still considered as an IPv6
* address since it follows the IPv6 address format
*
* @param ip_address The string to check
* @return true if the string is a valid IPv6 address, false otherwise
*/
bool isIpv6(const std::string& ip_address);
Loading

0 comments on commit 30e5b7d

Please sign in to comment.