Goby3 3.5.1
2026.06.04
Loading...
Searching...
No Matches
iridium_shore_rudics.h
Go to the documentation of this file.
1// Copyright 2015-2026:
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(
45 const boost::asio::ip::tcp::socket::executor_type& executor)
46 {
47 return std::shared_ptr<RUDICSConnection>(new RUDICSConnection(executor));
48 }
49
50 boost::asio::ip::tcp::socket& socket() { return socket_; }
51
52 void start()
53 {
54 remote_endpoint_str_ = boost::lexical_cast<std::string>(socket_.remote_endpoint());
55 read_start();
56 }
57
58 void close()
59 {
60 socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
61 socket_.close();
62 }
63
65 {
66 boost::asio::async_read_until(socket_, buffer_, '\r',
67 boost::bind(&RUDICSConnection::handle_read, this,
68 boost::placeholders::_1,
69 boost::placeholders::_2));
70 }
71
72 void write_start(const std::string& data)
73 {
74 boost::asio::async_write(socket_, boost::asio::buffer(data),
75 boost::bind(&RUDICSConnection::handle_write, this,
76 boost::placeholders::_1, boost::placeholders::_2));
77 }
78
80 {
81 using goby::glog;
83 glog.is(DEBUG1) && glog << "Disconnecting from: " << remote_endpoint_str_ << std::endl;
84 }
85
87 {
88 using goby::glog;
90 const int max_packet_failures = 3;
91 if (++packet_failures_ >= max_packet_failures)
92 {
93 glog.is(DEBUG1) && glog << "More than " << max_packet_failures << " bad RUDICS packets."
94 << std::endl;
95 close();
96 }
97 }
98
99 boost::signals2::signal<void(const std::string& line,
100 std::shared_ptr<RUDICSConnection> connection)>
102 boost::signals2::signal<void(std::shared_ptr<RUDICSConnection> connection)> disconnect_signal;
103
104 const std::string& remote_endpoint_str() { return remote_endpoint_str_; }
105
106 private:
108 const boost::asio::ip::tcp::socket::executor_type& executor)
109 : socket_(executor), remote_endpoint_str_("Unknown"), packet_failures_(0)
110 {
111 }
112
113 void handle_write(const boost::system::error_code& error, size_t bytes_transferred)
114 {
115 if (error)
116 {
117 using goby::glog;
119 glog.is(WARN) && glog << "Error writing to TCP connection: " << error << std::endl;
120 disconnect_signal(shared_from_this());
121 }
122 }
123
124 void handle_read(const boost::system::error_code& error, size_t bytes_transferred)
125 {
126 using goby::glog;
129 if (!error)
130 {
131 std::istream istrm(&buffer_);
132 std::string line;
133 std::getline(istrm, line, '\r');
134 line_signal(line + "\r", shared_from_this());
135 read_start();
136 }
137 else
138 {
139 if (error == boost::asio::error::eof)
140 {
141 glog.is(DEBUG1) && glog << "Connection reached EOF" << std::endl;
142 }
143 else if (error == boost::asio::error::operation_aborted)
144 {
145 glog.is(DEBUG1) && glog << "Read operation aborted (socket closed)" << std::endl;
146 }
147 else
148 {
149 glog.is(WARN) && glog << "Error reading from TCP connection: " << error
150 << std::endl;
151 }
152
153 disconnect_signal(shared_from_this());
154 }
155 }
156
157 private:
158 boost::asio::ip::tcp::socket socket_;
159 boost::asio::streambuf buffer_;
160 std::string remote_endpoint_str_;
161 int packet_failures_;
162};
163
165{
166 public:
167 RUDICSServer(boost::asio::io_context& io_context, int port, bool ipv6)
168 : acceptor_(io_context,
169 boost::asio::ip::tcp::endpoint(
170 ipv6 ? boost::asio::ip::tcp::v6() : boost::asio::ip::tcp::v4(), port))
171 {
172 start_accept();
173 }
174
175 std::set<std::shared_ptr<RUDICSConnection>>& connections() { return connections_; }
176
177 boost::signals2::signal<void(std::shared_ptr<RUDICSConnection> connection)> connect_signal;
178
179 void disconnect(std::shared_ptr<RUDICSConnection> connection) { connection->close(); }
180
181 private:
182 void start_accept()
183 {
184 std::shared_ptr<RUDICSConnection> new_connection =
185 RUDICSConnection::create(acceptor_.get_executor());
186 acceptor_.async_accept(new_connection->socket(),
187 boost::bind(&RUDICSServer::handle_accept, this, new_connection,
188 boost::asio::placeholders::error));
189 }
190
191 void handle_accept(std::shared_ptr<RUDICSConnection> new_connection,
192 const boost::system::error_code& error)
193 {
194 if (!error)
195 {
196 using namespace goby::util::logger;
197 using goby::glog;
198
199 connections_.insert(new_connection);
200
201 new_connection->disconnect_signal.connect(
202 boost::bind(&RUDICSServer::handle_disconnect, this, boost::placeholders::_1));
203 connect_signal(new_connection);
204 new_connection->start();
205 glog.is(DEBUG1) && glog << "Received connection from: "
206 << new_connection->remote_endpoint_str() << std::endl;
207 }
208
209 start_accept();
210 }
211
212 void handle_disconnect(std::shared_ptr<RUDICSConnection> connection)
213 {
214 using goby::glog;
216
217 connections_.erase(connection);
218
219 glog.is(DEBUG1) &&
220 glog << "Server removing connection: " << connection->remote_endpoint_str()
221 << ". Remaining connection count: " << connections_.size() << std::endl;
222 }
223
224 std::set<std::shared_ptr<RUDICSConnection>> connections_;
225 boost::asio::ip::tcp::acceptor acceptor_;
226};
227
228} // namespace acomms
229} // namespace goby
230
231#endif
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.