mavtables  0.2.1
MAVLink router and firewall.
UDPInterface.cpp
Go to the documentation of this file.
1 // MAVLink router and firewall.
2 // Copyright (C) 2018 Michael R. Shannon <mrshannon.aerospace@gmail.com>
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 
17 
18 #include <chrono>
19 #include <memory>
20 #include <stdexcept>
21 
22 #include "Connection.hpp"
23 #include "ConnectionFactory.hpp"
24 #include "ConnectionPool.hpp"
25 #include "Interface.hpp"
26 #include "IPAddress.hpp"
27 #include "UDPInterface.hpp"
28 #include "UDPSocket.hpp"
29 #include "utility.hpp"
30 
31 
32 using namespace std::chrono_literals;
33 
34 
35 /** Update connections.
36  *
37  * Adds a MAVLink address to the connection corresponding to the given IP
38  * address. If this connection does not exist, it will be constructed using
39  * the \ref UDPInterface's connection factory.
40  *
41  * \param mav_address The MAVLink address of the received packet.
42  * \param ip_address The IP address the packet was received on.
43  */
44 void UDPInterface::update_connections_(
45  const MAVAddress &mav_address, const IPAddress &ip_address)
46 {
47  auto it = connections_.find(ip_address);
48 
49  if (it == connections_.end())
50  {
51  it = connections_.insert(
52  {ip_address, connection_factory_->get(str(ip_address))}).first;
53  connection_pool_->add(it->second);
54  }
55 
56  it->second->add_address(mav_address);
57 }
58 
59 
60 /** Construct a UDP interface using a given socket.
61  *
62  * \param socket The UDP socket to communicate over.
63  * \param connection_pool The connection pool to use for sending packets and to
64  * register new connections with.
65  * \param connection_factory The connection factory to use for constructing
66  * new connections when an outside connection is made.
67  * \throws std::invalid_argument if the serial \p port device pointer is null.
68  * \throws std::invalid_argument if the \p connection_pool pointer is null.
69  * \throws std::invalid_argument if the \p connection_factory pointer is null.
70  */
72  std::unique_ptr<UDPSocket> socket,
73  std::shared_ptr<ConnectionPool> connection_pool,
74  std::unique_ptr<ConnectionFactory<>> connection_factory)
75  : socket_(std::move(socket)),
76  connection_pool_(std::move(connection_pool)),
77  connection_factory_(std::move(connection_factory)),
78  last_ip_address_(IPAddress(0))
79 {
80  if (socket_ == nullptr)
81  {
82  throw std::invalid_argument("Given socket pointer is null.");
83  }
84 
85  if (connection_pool_ == nullptr)
86  {
87  throw std::invalid_argument("Given connection pool pointer is null.");
88  }
89 
90  if (connection_factory_ == nullptr)
91  {
92  throw std::invalid_argument(
93  "Given connection factory pointer is null.");
94  }
95 }
96 
97 
98 /** \copydoc Interface::send_packet(const std::chrono::nanoseconds &)
99  *
100  * Sends up to one packet from each connection, belonging to the interface,
101  * over the UDP socket.
102  */
103 void UDPInterface::send_packet(const std::chrono::nanoseconds &timeout)
104 {
105  bool not_first = false;
106 
107  // Wait for a packet on any of the interface's connections.
108  if (connection_factory_->wait_for_packet(timeout))
109  {
110  for (auto &conn : connections_)
111  {
112  auto packet = conn.second->next_packet();
113 
114  // If connection has a packet send it.
115  if (packet != nullptr)
116  {
117  socket_->send(packet->data(), conn.first);
118 
119  if (not_first)
120  {
121  // Decrement semaphore once for each extra packet.
122  connection_factory_->wait_for_packet(0s);
123  }
124 
125  not_first = true;
126  }
127  }
128  }
129 }
130 
131 
132 /** \copydoc Interface::receive_packet(const std::chrono::nanoseconds &)
133  *
134  * Receives up to one UDP packet worth of data and parses it into MAVLink
135  * packets before passing these packets onto the connection pool. Will wait up
136  * to \p timeout for a UDP packet to be received.
137  */
138 void UDPInterface::receive_packet(const std::chrono::nanoseconds &timeout)
139 {
140  auto [buffer, ip_address] = socket_->receive(timeout);
141 
142  if (!buffer.empty())
143  {
144  // Clear the parser if the IP address is different from the last UDP
145  // packet received (we want complete MAVLink packets).
146  if (ip_address != last_ip_address_)
147  {
148  parser_.clear();
149  last_ip_address_ = ip_address;
150  }
151 
152  // Parse the bytes.
153  for (auto byte : buffer)
154  {
155  auto packet = parser_.parse_byte(byte);
156 
157  if (packet != nullptr)
158  {
159  update_connections_(packet->source(), ip_address);
160  // It is a post condition of update_connections_ that there is a
161  // connection for ip_address.
162  packet->connection(connections_[ip_address]);
163  connection_pool_->send(std::move(packet));
164  }
165  }
166  }
167 }
168 
169 
170 /** \copydoc Interface::print_(std::ostream &os)const
171  *
172  * Example:
173  * ```
174  * udp {
175  * port 14500;
176  * address 127.0.0.1;
177  * }
178  * ```
179  */
180 std::ostream &UDPInterface::print_(std::ostream &os) const
181 {
182  os << *socket_;
183  return os;
184 }
std::string str(const T &object)
Definition: utility.hpp:128
void receive_packet(const std::chrono::nanoseconds &timeout) final
STL namespace.
std::ostream & print_(std::ostream &os) const final
UDPInterface(std::unique_ptr< UDPSocket > socket, std::shared_ptr< ConnectionPool > connection_pool, std::unique_ptr< ConnectionFactory<>> connection_factory)
void send_packet(const std::chrono::nanoseconds &timeout) final
std::unique_ptr< Packet > parse_byte(uint8_t byte)