Goby3 3.2.3
2025.05.13
Loading...
Searching...
No Matches
iridium_shore_rudics.h
Go to the documentation of this file.
1// Copyright 2015-2025:
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
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"
36
37namespace goby
38{
39namespace acomms
40{
41class 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
69 {
70 boost::asio::async_read_until(socket_, buffer_, '\r',
71 boost::bind(&RUDICSConnection::handle_read, this,
72 boost::placeholders::_1,
73 boost::placeholders::_2));
74 }
75
76 void write_start(const std::string& data)
77 {
78 boost::asio::async_write(socket_, boost::asio::buffer(data),
79 boost::bind(&RUDICSConnection::handle_write, this,
80 boost::placeholders::_1, boost::placeholders::_2));
81 }
82
84 {
85 using goby::glog;
87 glog.is(DEBUG1) && glog << "Disconnecting from: " << remote_endpoint_str_ << std::endl;
88 }
89
91 {
92 using goby::glog;
94 const int max_packet_failures = 3;
95 if (++packet_failures_ >= max_packet_failures)
96 {
97 glog.is(DEBUG1) && glog << "More than " << max_packet_failures << " bad RUDICS packets."
98 << std::endl;
99 close();
100 }
101 }
102
103 boost::signals2::signal<void(const std::string& line,
104 std::shared_ptr<RUDICSConnection> connection)>
106 boost::signals2::signal<void(std::shared_ptr<RUDICSConnection> connection)> disconnect_signal;
107
108 const std::string& remote_endpoint_str() { return remote_endpoint_str_; }
109
110 private:
113 boost::asio::io_service& executor)
114#else
115 const boost::asio::ip::tcp::socket::executor_type& executor)
116#endif
117 : socket_(executor), remote_endpoint_str_("Unknown"), packet_failures_(0)
118 {
119 }
120
121 void handle_write(const boost::system::error_code& error, size_t bytes_transferred)
122 {
123 if (error)
124 {
125 using goby::glog;
127 glog.is(WARN) && glog << "Error writing to TCP connection: " << error << std::endl;
128 disconnect_signal(shared_from_this());
129 }
130 }
131
132 void handle_read(const boost::system::error_code& error, size_t bytes_transferred)
133 {
134 using goby::glog;
137 if (!error)
138 {
139 std::istream istrm(&buffer_);
140 std::string line;
141 std::getline(istrm, line, '\r');
142 line_signal(line + "\r", shared_from_this());
143 read_start();
144 }
145 else
146 {
147 if (error == boost::asio::error::eof)
148 {
149 glog.is(DEBUG1) && glog << "Connection reached EOF" << std::endl;
150 }
151 else if (error == boost::asio::error::operation_aborted)
152 {
153 glog.is(DEBUG1) && glog << "Read operation aborted (socket closed)" << std::endl;
154 }
155 else
156 {
157 glog.is(WARN) && glog << "Error reading from TCP connection: " << error
158 << std::endl;
159 }
160
161 disconnect_signal(shared_from_this());
162 }
163 }
164
165 private:
166 boost::asio::ip::tcp::socket socket_;
167 boost::asio::streambuf buffer_;
168 std::string remote_endpoint_str_;
169 int packet_failures_;
170};
171
173{
174 public:
175 RUDICSServer(boost::asio::io_context& io_context, int port, bool ipv6)
176 : acceptor_(io_context,
177 boost::asio::ip::tcp::endpoint(
178 ipv6 ? boost::asio::ip::tcp::v6() : boost::asio::ip::tcp::v4(), port))
179 {
180 start_accept();
181 }
182
183 std::set<std::shared_ptr<RUDICSConnection>>& connections() { return connections_; }
184
185 boost::signals2::signal<void(std::shared_ptr<RUDICSConnection> connection)> connect_signal;
186
187 void disconnect(std::shared_ptr<RUDICSConnection> connection) { connection->close(); }
188
189 private:
190 void start_accept()
191 {
192 std::shared_ptr<RUDICSConnection> new_connection =
193#ifdef USE_BOOST_IO_SERVICE
194 RUDICSConnection::create(acceptor_.get_io_service());
195#else
196 RUDICSConnection::create(acceptor_.get_executor());
197#endif
198 acceptor_.async_accept(new_connection->socket(),
199 boost::bind(&RUDICSServer::handle_accept, this, new_connection,
200 boost::asio::placeholders::error));
201 }
202
203 void handle_accept(std::shared_ptr<RUDICSConnection> new_connection,
204 const boost::system::error_code& error)
205 {
206 if (!error)
207 {
208 using namespace goby::util::logger;
209 using goby::glog;
210
211 connections_.insert(new_connection);
212
213 new_connection->disconnect_signal.connect(
214 boost::bind(&RUDICSServer::handle_disconnect, this, boost::placeholders::_1));
215 connect_signal(new_connection);
216 new_connection->start();
217 glog.is(DEBUG1) && glog << "Received connection from: "
218 << new_connection->remote_endpoint_str() << std::endl;
219 }
220
221 start_accept();
222 }
223
224 void handle_disconnect(std::shared_ptr<RUDICSConnection> connection)
225 {
226 using goby::glog;
228
229 connections_.erase(connection);
230
231 glog.is(DEBUG1) &&
232 glog << "Server removing connection: " << connection->remote_endpoint_str()
233 << ". Remaining connection count: " << connections_.size() << std::endl;
234 }
235
236 std::set<std::shared_ptr<RUDICSConnection>> connections_;
237 boost::asio::ip::tcp::acceptor acceptor_;
238};
239
240} // namespace acomms
241} // namespace goby
242
243#endif
#define USE_BOOST_IO_SERVICE
Definition asio_compat.h:33
const std::string & remote_endpoint_str()
static std::shared_ptr< RUDICSConnection > create(const boost::asio::ip::tcp::socket::executor_type &executor)
boost::signals2::signal< void(const std::string &line, std::shared_ptr< RUDICSConnection > connection)> line_signal
boost::signals2::signal< void(std::shared_ptr< RUDICSConnection > connection)> disconnect_signal
void write_start(const std::string &data)
boost::asio::ip::tcp::socket & socket()
void disconnect(std::shared_ptr< RUDICSConnection > connection)
std::set< std::shared_ptr< RUDICSConnection > > & connections()
boost::signals2::signal< void(std::shared_ptr< RUDICSConnection > connection)> connect_signal
RUDICSServer(boost::asio::io_context &io_context, int port, bool ipv6)
bool is(goby::util::logger::Verbosity verbosity)
@ error
throw a parse_error exception in case of a tag
The global namespace for the Goby project.
util::FlexOstream glog
Access the Goby logger through this object.