Goby v2
udp_driver.cpp
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 #include "udp_driver.h"
24 
25 #include "goby/acomms/modemdriver/driver_exception.h"
26 #include "goby/acomms/modemdriver/mm_driver.h"
27 #include "goby/common/logger.h"
28 #include "goby/util/binary.h"
29 
30 using goby::glog;
31 using goby::util::hex_decode;
32 using goby::util::hex_encode;
33 using namespace goby::common::logger;
35 
36 const size_t UDP_MAX_PACKET_SIZE = 65507; // (16 bit length = 65535 - 8 byte UDP header -20 byte IP)
37 
38 goby::acomms::UDPDriver::UDPDriver(boost::asio::io_service* io_service)
39  : io_service_(io_service), socket_(*io_service), receive_buffer_(UDP_MAX_PACKET_SIZE),
40  next_frame_(0)
41 {
42 }
43 
44 goby::acomms::UDPDriver::~UDPDriver() {}
45 
47 {
48  driver_cfg_ = cfg;
49 
50  const UDPDriverConfig::EndPoint& local = driver_cfg_.GetExtension(UDPDriverConfig::local);
51  socket_.open(boost::asio::ip::udp::v4());
52  socket_.bind(boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), local.port()));
53 
54  const UDPDriverConfig::EndPoint& remote = driver_cfg_.GetExtension(UDPDriverConfig::remote);
55  boost::asio::ip::udp::resolver resolver(*io_service_);
56  boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), remote.ip(),
57  goby::util::as<std::string>(remote.port()));
58  boost::asio::ip::udp::resolver::iterator endpoint_iterator = resolver.resolve(query);
59  receiver_ = *endpoint_iterator;
60 
61  glog.is(DEBUG1) && glog << group(glog_out_group())
62  << "Receiver endpoint is: " << receiver_.address().to_string() << ":"
63  << receiver_.port() << std::endl;
64 
65  start_receive();
66  io_service_->reset();
67 }
68 
70 {
71  io_service_->stop();
72  socket_.close();
73 }
74 
76  const protobuf::ModemTransmission& orig_msg)
77 {
78  // buffer the message
79  protobuf::ModemTransmission msg = orig_msg;
81 
82  if (!msg.has_frame_start())
83  msg.set_frame_start(next_frame_);
84 
85  msg.set_max_frame_bytes(driver_cfg_.GetExtension(UDPDriverConfig::max_frame_size));
86  signal_data_request(&msg);
87 
88  glog.is(DEBUG1) && glog << group(glog_out_group())
89  << "After modification, initiating transmission with " << msg
90  << std::endl;
91 
92  next_frame_ += msg.frame_size();
93 
94  if (!(msg.frame_size() == 0 || msg.frame(0).empty()))
95  start_send(msg);
96 }
97 
98 void goby::acomms::UDPDriver::do_work() { io_service_->poll(); }
99 
100 void goby::acomms::UDPDriver::receive_message(const protobuf::ModemTransmission& msg)
101 {
102  if (msg.type() == protobuf::ModemTransmission::DATA && msg.ack_requested() &&
103  msg.dest() != BROADCAST_ID)
104  {
105  // make any acks
107  ack.set_type(goby::acomms::protobuf::ModemTransmission::ACK);
108  ack.set_src(msg.dest());
109  ack.set_dest(msg.src());
110  for (int i = msg.frame_start(), n = msg.frame_size() + msg.frame_start(); i < n; ++i)
111  ack.add_acked_frame(i);
112  start_send(ack);
113  }
114 
115  signal_receive(msg);
116 }
117 
118 void goby::acomms::UDPDriver::start_send(const google::protobuf::Message& msg)
119 {
120  // send the message
121  std::string bytes;
122  msg.SerializeToString(&bytes);
123 
124  glog.is(DEBUG1) && glog << group(glog_out_group())
125  << "Sending hex: " << goby::util::hex_encode(bytes) << std::endl;
126 
127  protobuf::ModemRaw raw_msg;
128  raw_msg.set_raw(bytes);
129  signal_raw_outgoing(raw_msg);
130 
131  socket_.async_send_to(boost::asio::buffer(bytes), receiver_,
132  boost::bind(&UDPDriver::send_complete, this, _1, _2));
133 }
134 
135 void goby::acomms::UDPDriver::send_complete(const boost::system::error_code& error,
136  std::size_t bytes_transferred)
137 {
138  if (error)
139  {
140  glog.is(DEBUG1) && glog << group(glog_out_group()) << warn
141  << "Send error: " << error.message() << std::endl;
142  return;
143  }
144 
145  glog.is(DEBUG1) && glog << group(glog_out_group()) << "Sent " << bytes_transferred << " bytes."
146  << std::endl;
147 }
148 
149 void goby::acomms::UDPDriver::start_receive()
150 {
151  socket_.async_receive_from(boost::asio::buffer(receive_buffer_), sender_,
152  boost::bind(&UDPDriver::receive_complete, this, _1, _2));
153 }
154 
155 void goby::acomms::UDPDriver::receive_complete(const boost::system::error_code& error,
156  std::size_t bytes_transferred)
157 {
158  if (error)
159  {
160  glog.is(DEBUG1) && glog << group(glog_in_group()) << warn
161  << "Receive error: " << error.message() << std::endl;
162  start_receive();
163  return;
164  }
165 
166  protobuf::ModemRaw raw_msg;
167  raw_msg.set_raw(std::string(&receive_buffer_[0], bytes_transferred));
168  signal_raw_incoming(raw_msg);
169 
170  glog.is(DEBUG1) && glog << group(glog_in_group()) << "Received " << bytes_transferred
171  << " bytes from " << sender_.address().to_string() << ":"
172  << sender_.port() << std::endl;
173 
175  msg.ParseFromArray(&receive_buffer_[0], bytes_transferred);
176  receive_message(msg);
177 
178  start_receive();
179 }
void handle_initiate_transmission(const protobuf::ModemTransmission &m)
Virtual initiate_transmission method. Typically connected to MACManager::signal_initiate_transmission...
Definition: udp_driver.cpp:75
boost::signals2::signal< void(const protobuf::ModemRaw &msg)> signal_raw_incoming
Called after any message is received from the modem by the driver. Used by the MACManager for auto-di...
Definition: driver_base.h:107
ReturnType goby_time()
Returns current UTC time as a boost::posix_time::ptime.
Definition: time.h:104
boost::signals2::signal< void(const protobuf::ModemTransmission &message)> signal_receive
Called when a binary data transmission is received from the modem.
Definition: driver_base.h:85
const int BROADCAST_ID
special modem id for the broadcast destination - no one is assigned this address. Analogous to 192...
void shutdown()
Shuts down the modem driver.
Definition: udp_driver.cpp:69
common::FlexOstream glog
Access the Goby logger through this object.
boost::signals2::signal< void(const protobuf::ModemRaw &msg)> signal_raw_outgoing
Called after any message is sent from the driver to the modem. Useful for higher level analysis and d...
Definition: driver_base.h:112
boost::signals2::signal< void(protobuf::ModemTransmission *msg)> signal_data_request
Called when the modem or modem driver needs data to send. The returned data should be stored in Modem...
Definition: driver_base.h:96
void do_work()
Allows the modem driver to do its work.
Definition: udp_driver.cpp:98
boost::signals2::signal< void(protobuf::ModemTransmission *msg_request)> signal_modify_transmission
Called before the modem driver begins processing a transmission. This allows a third party to modify ...
Definition: driver_base.h:102
void startup(const protobuf::DriverConfig &cfg)
Starts the modem driver. Must be called before poll().
Definition: udp_driver.cpp:46