Goby3  3.1.5a
2024.05.23
iridium_shore_rudics.h
Go to the documentation of this file.
1 // Copyright 2015-2023:
2 // GobySoft, LLC (2013-)
3 // Community contributors (see AUTHORS file)
4 // File authors:
5 // Toby Schneider <toby@gobysoft.org>
6 //
7 //
8 // This file is part of the Goby Underwater Autonomy Project Libraries
9 // ("The Goby Libraries").
10 //
11 // The Goby Libraries are free software: you can redistribute them and/or modify
12 // them under the terms of the GNU Lesser General Public License as published by
13 // the Free Software Foundation, either version 2.1 of the License, or
14 // (at your option) any later version.
15 //
16 // The Goby Libraries are distributed in the hope that they will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU Lesser General Public License for more details.
20 //
21 // You should have received a copy of the GNU Lesser General Public License
22 // along with Goby. If not, see <http://www.gnu.org/licenses/>.
23 
24 #ifndef GOBY_ACOMMS_MODEMDRIVER_IRIDIUM_SHORE_RUDICS_H
25 #define GOBY_ACOMMS_MODEMDRIVER_IRIDIUM_SHORE_RUDICS_H
26 
27 #include "goby/util/asio_compat.h"
28 #include <boost/asio.hpp>
29 
30 #include <boost/bind/bind.hpp>
31 #include <boost/signals2.hpp>
32 
33 #include "goby/time.h"
34 #include "goby/util/binary.h"
35 #include "goby/util/debug_logger.h"
36 
37 namespace goby
38 {
39 namespace acomms
40 {
41 class RUDICSConnection : public std::enable_shared_from_this<RUDICSConnection>
42 {
43  public:
44  static std::shared_ptr<RUDICSConnection> create(
46  boost::asio::io_service& executor)
47 #else
48  const boost::asio::ip::tcp::socket::executor_type& executor)
49 #endif
50  {
51  return std::shared_ptr<RUDICSConnection>(new RUDICSConnection(executor));
52  }
53 
54  boost::asio::ip::tcp::socket& socket() { return socket_; }
55 
56  void start()
57  {
58  remote_endpoint_str_ = boost::lexical_cast<std::string>(socket_.remote_endpoint());
59  read_start();
60  }
61 
62  void close()
63  {
64  socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
65  socket_.close();
66  }
67 
68  void read_start()
69  {
70  boost::asio::async_read_until(socket_, buffer_, '\r',
71  boost::bind(&RUDICSConnection::handle_read, this, boost::placeholders::_1, boost::placeholders::_2));
72  }
73 
74  void write_start(const std::string& data)
75  {
76  boost::asio::async_write(socket_, boost::asio::buffer(data),
77  boost::bind(&RUDICSConnection::handle_write, this, boost::placeholders::_1, boost::placeholders::_2));
78  }
79 
81  {
82  using goby::glog;
84  glog.is(DEBUG1) && glog << "Disconnecting from: " << remote_endpoint_str_ << std::endl;
85  }
86 
88  {
89  using goby::glog;
91  const int max_packet_failures = 3;
92  if (++packet_failures_ >= max_packet_failures)
93  {
94  glog.is(DEBUG1) && glog << "More than " << max_packet_failures << " bad RUDICS packets."
95  << std::endl;
96  close();
97  }
98  }
99 
100  boost::signals2::signal<void(const std::string& line,
101  std::shared_ptr<RUDICSConnection> connection)>
103  boost::signals2::signal<void(std::shared_ptr<RUDICSConnection> connection)> disconnect_signal;
104 
105  const std::string& remote_endpoint_str() { return remote_endpoint_str_; }
106 
107  private:
110  boost::asio::io_service& executor)
111 #else
112  const boost::asio::ip::tcp::socket::executor_type& executor)
113 #endif
114  : socket_(executor), remote_endpoint_str_("Unknown"), packet_failures_(0)
115  {
116  }
117 
118  void handle_write(const boost::system::error_code& error, size_t bytes_transferred)
119  {
120  if (error)
121  {
122  using goby::glog;
124  glog.is(WARN) && glog << "Error writing to TCP connection: " << error << std::endl;
125  disconnect_signal(shared_from_this());
126  }
127  }
128 
129  void handle_read(const boost::system::error_code& error, size_t bytes_transferred)
130  {
131  using goby::glog;
134  if (!error)
135  {
136  std::istream istrm(&buffer_);
137  std::string line;
138  std::getline(istrm, line, '\r');
139  line_signal(line + "\r", shared_from_this());
140  read_start();
141  }
142  else
143  {
144  if (error == boost::asio::error::eof)
145  {
146  glog.is(DEBUG1) && glog << "Connection reached EOF" << std::endl;
147  }
148  else if (error == boost::asio::error::operation_aborted)
149  {
150  glog.is(DEBUG1) && glog << "Read operation aborted (socket closed)" << std::endl;
151  }
152  else
153  {
154  glog.is(WARN) && glog << "Error reading from TCP connection: " << error
155  << std::endl;
156  }
157 
158  disconnect_signal(shared_from_this());
159  }
160  }
161 
162  private:
163  boost::asio::ip::tcp::socket socket_;
164  boost::asio::streambuf buffer_;
165  std::string remote_endpoint_str_;
166  int packet_failures_;
167 };
168 
170 {
171  public:
173  : acceptor_(io_context, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
174  {
175  start_accept();
176  }
177 
178  std::set<std::shared_ptr<RUDICSConnection> >& connections() { return connections_; }
179 
180  boost::signals2::signal<void(std::shared_ptr<RUDICSConnection> connection)> connect_signal;
181 
182  void disconnect(std::shared_ptr<RUDICSConnection> connection) { connection->close(); }
183 
184  private:
185  void start_accept()
186  {
187  std::shared_ptr<RUDICSConnection> new_connection =
188 #ifdef USE_BOOST_IO_SERVICE
189  RUDICSConnection::create(acceptor_.get_io_service());
190 #else
191  RUDICSConnection::create(acceptor_.get_executor());
192 #endif
193  acceptor_.async_accept(new_connection->socket(),
194  boost::bind(&RUDICSServer::handle_accept, this, new_connection,
195  boost::asio::placeholders::error));
196  }
197 
198  void handle_accept(std::shared_ptr<RUDICSConnection> new_connection,
199  const boost::system::error_code& error)
200  {
201  if (!error)
202  {
203  using namespace goby::util::logger;
204  using goby::glog;
205 
206  connections_.insert(new_connection);
207 
208  new_connection->disconnect_signal.connect(
209  boost::bind(&RUDICSServer::handle_disconnect, this, boost::placeholders::_1));
210  connect_signal(new_connection);
211  new_connection->start();
212  glog.is(DEBUG1) && glog << "Received connection from: "
213  << new_connection->remote_endpoint_str() << std::endl;
214  }
215 
216  start_accept();
217  }
218 
219  void handle_disconnect(std::shared_ptr<RUDICSConnection> connection)
220  {
221  using goby::glog;
223 
224  connections_.erase(connection);
225 
226  glog.is(DEBUG1) &&
227  glog << "Server removing connection: " << connection->remote_endpoint_str()
228  << ". Remaining connection count: " << connections_.size() << std::endl;
229  }
230 
231  std::set<std::shared_ptr<RUDICSConnection> > connections_;
232  boost::asio::ip::tcp::acceptor acceptor_;
233 };
234 
235 } // namespace acomms
236 } // namespace goby
237 
238 #endif
goby::util::logger
Definition: flex_ncurses.h:42
goby::acomms::RUDICSConnection::line_signal
boost::signals2::signal< void(const std::string &line, std::shared_ptr< RUDICSConnection > connection)> line_signal
Definition: iridium_shore_rudics.h:102
goby::acomms::RUDICSServer::disconnect
void disconnect(std::shared_ptr< RUDICSConnection > connection)
Definition: iridium_shore_rudics.h:182
goby::util::FlexOstream::is
bool is(goby::util::logger::Verbosity verbosity)
goby
The global namespace for the Goby project.
Definition: acomms_constants.h:33
goby::acomms::RUDICSConnection::socket
boost::asio::ip::tcp::socket & socket()
Definition: iridium_shore_rudics.h:54
goby::acomms::RUDICSConnection::~RUDICSConnection
~RUDICSConnection()
Definition: iridium_shore_rudics.h:80
goby::util::logger::WARN
@ WARN
Definition: flex_ostreambuf.h:74
boost
Definition: udp_driver.h:41
goby::acomms::RUDICSServer::RUDICSServer
RUDICSServer(boost::asio::io_context &io_context, int port)
Definition: iridium_shore_rudics.h:172
detail::void
j template void())
Definition: json.hpp:4822
io_service
boost::asio::io_context
io_service io_context
Definition: asio_compat.h:44
goby::acomms::RUDICSServer::connect_signal
boost::signals2::signal< void(std::shared_ptr< RUDICSConnection > connection)> connect_signal
Definition: iridium_shore_rudics.h:180
goby::acomms::RUDICSServer
Definition: iridium_shore_rudics.h:169
goby::acomms::RUDICSConnection::disconnect_signal
boost::signals2::signal< void(std::shared_ptr< RUDICSConnection > connection)> disconnect_signal
Definition: iridium_shore_rudics.h:103
time.h
goby::acomms::RUDICSServer::connections
std::set< std::shared_ptr< RUDICSConnection > > & connections()
Definition: iridium_shore_rudics.h:178
asio_compat.h
goby::acomms::RUDICSConnection::create
static std::shared_ptr< RUDICSConnection > create(const boost::asio::ip::tcp::socket::executor_type &executor)
Definition: iridium_shore_rudics.h:44
goby::acomms::RUDICSConnection::start
void start()
Definition: iridium_shore_rudics.h:56
goby::acomms::bind
void bind(ModemDriverBase &driver, QueueManager &queue_manager)
binds the driver link-layer callbacks to the QueueManager
Definition: bind.h:45
binary.h
debug_logger.h
goby::acomms::RUDICSConnection::write_start
void write_start(const std::string &data)
Definition: iridium_shore_rudics.h:74
USE_BOOST_IO_SERVICE
#define USE_BOOST_IO_SERVICE
Definition: asio_compat.h:33
goby::acomms::RUDICSConnection::remote_endpoint_str
const std::string & remote_endpoint_str()
Definition: iridium_shore_rudics.h:105
goby::acomms::RUDICSConnection::read_start
void read_start()
Definition: iridium_shore_rudics.h:68
goby::util::logger::DEBUG1
@ DEBUG1
Definition: flex_ostreambuf.h:77
goby::glog
util::FlexOstream glog
Access the Goby logger through this object.
goby::acomms::RUDICSConnection::close
void close()
Definition: iridium_shore_rudics.h:62
detail::cbor_tag_handler_t::error
@ error
throw a parse_error exception in case of a tag
goby::acomms::RUDICSConnection::add_packet_failure
void add_packet_failure()
Definition: iridium_shore_rudics.h:87
goby::acomms::RUDICSConnection
Definition: iridium_shore_rudics.h:41