Goby v2
iridium_shore_sbd.h
1 // Copyright 2009-2018 Toby Schneider (http://gobysoft.org/index.wt/people/toby)
2 // GobySoft, LLC (2013-)
3 // Massachusetts Institute of Technology (2007-2014)
4 // Community contributors (see AUTHORS file)
5 //
6 //
7 // This file is part of the Goby Underwater Autonomy Project Libraries
8 // ("The Goby Libraries").
9 //
10 // The Goby Libraries are free software: you can redistribute them and/or modify
11 // them under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 2.1 of the License, or
13 // (at your option) any later version.
14 //
15 // The Goby Libraries are distributed in the hope that they will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with Goby. If not, see <http://www.gnu.org/licenses/>.
22 
23 #ifndef IridiumShoreSBD20150227H
24 #define IridiumShoreSBD20150227H
25 
26 #include "goby/acomms/protobuf/iridium_sbd_directip.pb.h"
27 #include "goby/acomms/protobuf/rudics_shore.pb.h"
28 #include "goby/common/time.h"
29 #include "goby/util/binary.h"
30 #include <boost/asio.hpp>
31 #include <boost/bind.hpp>
32 #include <boost/enable_shared_from_this.hpp>
33 
34 namespace goby
35 {
36 namespace acomms
37 {
39 {
40  public:
41  SBDMessageReader(boost::asio::ip::tcp::socket& socket)
42  : socket_(socket), pos_(0), data_(1 << 16)
43  {
44  }
45 
46  virtual bool data_ready() const = 0;
47 
48  const goby::acomms::protobuf::DirectIPMOPreHeader& pre_header() const { return pre_header_; }
49  const goby::acomms::protobuf::DirectIPMOHeader& header() const { return header_; }
50  const goby::acomms::protobuf::DirectIPMOPayload& body() const { return body_; }
51  const goby::acomms::protobuf::DirectIPMTConfirmation& confirm() const { return confirm_; }
52 
53  enum
54  {
55  PRE_HEADER_SIZE = 3,
56  BITS_PER_BYTE = 8
57  };
58 
59  void pre_header_handler(const boost::system::error_code& error, std::size_t bytes_transferred)
60  {
61  if (error)
62  throw std::runtime_error("Error while reading: " + error.message());
63 
64  pre_header_.set_protocol_ver(read_byte());
65  pre_header_.set_overall_length(read_uint16());
66 
67  boost::asio::async_read(socket_, boost::asio::buffer(data_),
68  boost::asio::transfer_at_least(pre_header_.overall_length() +
69  PRE_HEADER_SIZE - bytes_transferred),
70  boost::bind(&SBDMessageReader::ie_handler, this, _1, _2));
71  }
72 
73  std::vector<char>& data() { return data_; }
74 
75  private:
76  void ie_handler(const boost::system::error_code& error, std::size_t bytes_transferred)
77  {
78  if (error)
79  throw std::runtime_error("Error while reading: " + error.message());
80 
81  char iei = read_byte();
82  unsigned length = read_uint16();
83 
84  switch (iei)
85  {
86  case 0x01: // header
87  {
88  header_.set_iei(iei);
89  header_.set_length(length);
90  header_.set_cdr_reference(read_uint32()); // 4 bytes
91 
92  header_.set_imei(read_imei()); // 15 bytes
93 
94  header_.set_session_status(read_byte()); // 1 byte
95  header_.set_momsn(read_uint16()); // 2 bytes
96  header_.set_mtmsn(read_uint16()); // 2 bytes
97  header_.set_time_of_session(read_uint32()); // 4 bytes
98  }
99  break;
100 
101  case 0x02: // payload
102  body_.set_iei(iei);
103  body_.set_length(length);
104  body_.set_payload(std::string(data_.begin() + pos_, data_.begin() + pos_ + length));
105  pos_ += length;
106  break;
107 
108  case 0x44: // confirmation
109  confirm_.set_iei(iei);
110  confirm_.set_length(length);
111  confirm_.set_client_id(read_uint32());
112  confirm_.set_imei(read_imei());
113  confirm_.set_auto_ref_id(read_uint32());
114  confirm_.set_status(read_int16());
115 
116  default: // skip this IE
117  pos_ += length;
118  break;
119  }
120 
121  if (pos_ < pre_header_.overall_length() + PRE_HEADER_SIZE)
122  ie_handler(error, bytes_transferred);
123  }
124 
125  char read_byte() { return data_.at(pos_++) & 0xff; }
126 
127  unsigned read_uint16()
128  {
129  unsigned u = (data_.at(pos_++) & 0xff) << BITS_PER_BYTE;
130  u |= data_.at(pos_++) & 0xff;
131  return u;
132  }
133 
134  int read_int16()
135  {
136  int i = (data_.at(pos_++) & 0xff) << BITS_PER_BYTE;
137  i |= data_.at(pos_++) & 0xff;
138 
139  // sign extend
140  int sign_bit_mask = 0x8000;
141  if (i & sign_bit_mask)
142  i |= (-1 - 0xFFFF); // extend bits to fill int
143 
144  return i;
145  }
146 
147  unsigned read_uint32()
148  {
149  unsigned u = 0;
150  for (int i = 0; i < 4; ++i) u |= (data_.at(pos_++) & 0xff) << ((3 - i) * BITS_PER_BYTE);
151  return u;
152  }
153 
154  std::string read_imei()
155  {
156  const int imei_size = 15;
157  std::string imei = std::string(data_.begin() + pos_, data_.begin() + pos_ + imei_size);
158  pos_ += imei_size;
159  return imei;
160  }
161 
162  private:
167 
168  boost::asio::ip::tcp::socket& socket_;
169 
170  std::vector<char>::size_type pos_;
171  std::vector<char> data_;
172 };
173 
175 {
176  public:
177  SBDMOMessageReader(boost::asio::ip::tcp::socket& socket) : SBDMessageReader(socket) {}
178 
179  bool data_ready() const
180  {
181  return pre_header().IsInitialized() && header().IsInitialized() && body().IsInitialized();
182  }
183 };
184 
186 {
187  public:
188  SBDMTConfirmationMessageReader(boost::asio::ip::tcp::socket& socket) : SBDMessageReader(socket)
189  {
190  }
191 
192  bool data_ready() const { return pre_header().IsInitialized() && confirm().IsInitialized(); }
193 };
194 
195 class SBDConnection : public boost::enable_shared_from_this<SBDConnection>
196 {
197  public:
198  static boost::shared_ptr<SBDConnection> create(boost::asio::io_service& io_service)
199  {
200  return boost::shared_ptr<SBDConnection>(new SBDConnection(io_service));
201  }
202 
203  boost::asio::ip::tcp::socket& socket() { return socket_; }
204 
205  void start()
206  {
207  remote_endpoint_str_ = boost::lexical_cast<std::string>(socket_.remote_endpoint());
208 
209  connect_time_ = goby::common::goby_time<double>();
210  boost::asio::async_read(
211  socket_, boost::asio::buffer(message_.data()),
212  boost::asio::transfer_at_least(SBDMessageReader::PRE_HEADER_SIZE),
213  boost::bind(&SBDMessageReader::pre_header_handler, &message_, _1, _2));
214  }
215 
216  ~SBDConnection() {}
217 
218  double connect_time() const { return connect_time_; }
219 
220  const SBDMOMessageReader& message() const { return message_; }
221  const std::string& remote_endpoint_str() { return remote_endpoint_str_; }
222 
223  private:
224  SBDConnection(boost::asio::io_service& io_service)
225  : socket_(io_service), connect_time_(-1), message_(socket_), remote_endpoint_str_("Unknown")
226  {
227  }
228 
229  boost::asio::ip::tcp::socket socket_;
230  double connect_time_;
231  SBDMOMessageReader message_;
232  std::string remote_endpoint_str_;
233 };
234 
236 {
237  public:
238  SBDServer(boost::asio::io_service& io_service, int port)
239  : acceptor_(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
240  {
241  start_accept();
242  }
243 
244  std::set<boost::shared_ptr<SBDConnection> >& connections() { return connections_; }
245 
246  private:
247  void start_accept()
248  {
249  boost::shared_ptr<SBDConnection> new_connection =
250  SBDConnection::create(acceptor_.get_io_service());
251 
252  connections_.insert(new_connection);
253 
254  acceptor_.async_accept(new_connection->socket(),
255  boost::bind(&SBDServer::handle_accept, this, new_connection,
256  boost::asio::placeholders::error));
257  }
258 
259  void handle_accept(boost::shared_ptr<SBDConnection> new_connection,
260  const boost::system::error_code& error)
261  {
262  if (!error)
263  {
264  using namespace goby::common::logger;
265  using goby::glog;
266 
267  glog.is(DEBUG1) && glog << "Received SBD connection from: "
268  << new_connection->socket().remote_endpoint() << std::endl;
269 
270  new_connection->start();
271  }
272 
273  start_accept();
274  }
275 
276  std::set<boost::shared_ptr<SBDConnection> > connections_;
277  boost::asio::ip::tcp::acceptor acceptor_;
278 };
279 
280 } // namespace acomms
281 } // namespace goby
282 
283 #endif
double goby_time< double >()
Returns current UTC time as seconds and fractional seconds since 1970-01-01 00:00:00.
Definition: time.h:130
common::FlexOstream glog
Access the Goby logger through this object.
The global namespace for the Goby project.