23 #include <boost/bimap.hpp> 25 #include "goby/acomms/modemdriver/driver_exception.h" 26 #include "goby/acomms/modemdriver/rudics_packet.h" 27 #include "goby/common/logger.h" 28 #include "goby/common/time.h" 29 #include "goby/util/binary.h" 31 #include "iridium_shore_driver.h" 32 #include "iridium_shore_sbd.h" 44 goby::acomms::IridiumShoreDriver::IridiumShoreDriver() : next_frame_(0) { init_iridium_dccl(); }
46 goby::acomms::IridiumShoreDriver::~IridiumShoreDriver() {}
52 glog.is(DEBUG1) &&
glog << group(glog_out_group())
53 <<
"Goby Shore Iridium RUDICS/SBD driver starting up." << std::endl;
55 rudics_mac_msg_.set_src(driver_cfg_.modem_id());
56 rudics_mac_msg_.set_type(goby::acomms::protobuf::ModemTransmission::DATA);
57 rudics_mac_msg_.set_rate(RATE_RUDICS);
60 rudics_io_, driver_cfg_.GetExtension(IridiumShoreDriverConfig::rudics_server_port)));
62 sbd_io_, driver_cfg_.GetExtension(IridiumShoreDriverConfig::mo_sbd_server_port)));
64 rudics_server_->connect_signal.connect(
65 boost::bind(&IridiumShoreDriver::rudics_connect,
this, _1));
67 for (
int i = 0, n = driver_cfg_.ExtensionSize(IridiumShoreDriverConfig::modem_id_to_imei);
69 modem_id_to_imei_[driver_cfg_.GetExtension(IridiumShoreDriverConfig::modem_id_to_imei, i)
71 driver_cfg_.GetExtension(IridiumShoreDriverConfig::modem_id_to_imei, i).imei();
79 process_transmission(orig_msg);
86 if (!msg.has_frame_start())
87 msg.set_frame_start(next_frame_);
90 if (!msg.has_max_frame_bytes() ||
91 msg.max_frame_bytes() > driver_cfg_.GetExtension(IridiumDriverConfig::max_frame_size))
92 msg.set_max_frame_bytes(driver_cfg_.GetExtension(IridiumDriverConfig::max_frame_size));
96 next_frame_ += msg.frame_size();
98 if (!(msg.frame_size() == 0 || msg.frame(0).empty()))
108 double now = goby_time<double>();
110 for (std::map<ModemId, RemoteNode>::iterator it = remote_.begin(), end = remote_.end();
113 RemoteNode& remote = it->second;
114 boost::shared_ptr<OnCallBase> on_call_base = remote.on_call;
115 ModemId
id = it->first;
121 const double send_wait =
122 on_call_base->last_bytes_sent() /
123 (driver_cfg_.GetExtension(IridiumDriverConfig::target_bit_rate) /
124 static_cast<double>(BITS_IN_BYTE));
126 if (now > (on_call_base->last_tx_time() + send_wait))
128 if (!on_call_base->bye_sent())
130 rudics_mac_msg_.set_dest(it->first);
131 process_transmission(rudics_mac_msg_);
135 if (!on_call_base->bye_sent() &&
136 now > (on_call_base->last_tx_time() +
137 driver_cfg_.GetExtension(IridiumDriverConfig::handshake_hangup_seconds)))
139 glog.is(DEBUG1) &&
glog <<
"Sending bye" << std::endl;
140 rudics_send(
"bye\r",
id);
141 on_call_base->set_bye_sent(
true);
144 if ((on_call_base->bye_received() && on_call_base->bye_sent()) ||
145 (now > (on_call_base->last_rx_tx_time() +
146 driver_cfg_.GetExtension(IridiumDriverConfig::hangup_seconds_after_empty))))
148 glog.is(DEBUG1) &&
glog <<
"Hanging up by disconnecting" << std::endl;
149 typedef boost::bimap<
150 ModemId, boost::shared_ptr<RUDICSConnection> >::left_map::iterator LeftIt;
151 LeftIt client_it = clients_.left.find(
id);
152 if (client_it != clients_.left.end())
153 rudics_server_->disconnect(client_it->second);
155 glog.is(WARN) &&
glog <<
"Failed to find connection from ModemId " <<
id 157 remote_[id].on_call.reset();
168 glog.is(DEBUG2) &&
glog << group(glog_in_group()) << msg.DebugString() << std::endl;
170 if (msg.type() == protobuf::ModemTransmission::DATA && msg.ack_requested() &&
171 msg.dest() == driver_cfg_.modem_id())
175 ack.set_type(goby::acomms::protobuf::ModemTransmission::ACK);
176 ack.set_src(msg.dest());
177 ack.set_dest(msg.src());
178 ack.set_rate(msg.rate());
179 for (
int i = msg.frame_start(), n = msg.frame_size() + msg.frame_start(); i < n; ++i)
180 ack.add_acked_frame(i);
189 glog.is(DEBUG2) &&
glog << group(glog_out_group()) << msg.DebugString() << std::endl;
191 RemoteNode& remote = remote_[msg.dest()];
193 if (msg.rate() == RATE_RUDICS || remote.on_call)
196 serialize_iridium_modem_message(&bytes, msg);
199 std::string rudics_packet;
200 serialize_rudics_packet(bytes, &rudics_packet);
201 rudics_send(rudics_packet, msg.dest());
202 boost::shared_ptr<OnCallBase> on_call_base = remote.on_call;
203 on_call_base->set_last_tx_time(goby_time<double>());
204 on_call_base->set_last_bytes_sent(rudics_packet.size());
206 else if (msg.rate() == RATE_SBD)
209 serialize_iridium_modem_message(&bytes, msg);
211 std::string sbd_packet;
212 serialize_rudics_packet(bytes, &sbd_packet);
214 if (modem_id_to_imei_.count(msg.dest()))
215 send_sbd_mt(sbd_packet, modem_id_to_imei_[msg.dest()]);
217 glog.is(WARN) &&
glog <<
"No IMEI configured for destination address " << msg.dest()
218 <<
" so unabled to send SBD message." << std::endl;
222 void goby::acomms::IridiumShoreDriver::rudics_send(
const std::string& data,
223 goby::acomms::IridiumShoreDriver::ModemId
id)
225 typedef boost::bimap<ModemId, boost::shared_ptr<RUDICSConnection> >::left_map::iterator LeftIt;
226 LeftIt client_it = clients_.left.find(
id);
227 if (client_it != clients_.left.end())
229 glog.is(DEBUG1) &&
glog <<
"RUDICS sending bytes: " << goby::util::hex_encode(data)
231 client_it->second->write_start(data);
235 glog.is(WARN) &&
glog <<
"Failed to find connection from ModemId " <<
id << std::endl;
239 void goby::acomms::IridiumShoreDriver::rudics_connect(
240 boost::shared_ptr<RUDICSConnection> connection)
242 connection->line_signal.connect(boost::bind(&IridiumShoreDriver::rudics_line,
this, _1, _2));
243 connection->disconnect_signal.connect(
244 boost::bind(&IridiumShoreDriver::rudics_disconnect,
this, _1));
247 void goby::acomms::IridiumShoreDriver::rudics_disconnect(
248 boost::shared_ptr<RUDICSConnection> connection)
250 typedef boost::bimap<ModemId, boost::shared_ptr<RUDICSConnection> >::right_map::iterator
253 RightIt client_it = clients_.right.find(connection);
254 if (client_it != clients_.right.end())
256 ModemId
id = client_it->second;
257 remote_[id].on_call.reset();
258 clients_.right.erase(client_it);
259 glog.is(DEBUG1) &&
glog <<
"Disconnecting client for modem id: " <<
id <<
"; " 260 << clients_.size() <<
" clients remaining." << std::endl;
265 glog <<
"Disconnection received from connection we do not have in the clients_ map: " 266 << connection->remote_endpoint_str() << std::endl;
270 void goby::acomms::IridiumShoreDriver::rudics_line(
const std::string& data,
271 boost::shared_ptr<RUDICSConnection> connection)
273 glog.is(DEBUG1) &&
glog <<
"RUDICS received bytes: " << goby::util::hex_encode(data)
278 std::string decoded_line;
280 if (data ==
"goby\r" ||
283 glog.is(DEBUG1) &&
glog <<
"Detected start of Goby RUDICS connection from " 284 << connection->remote_endpoint_str() << std::endl;
286 else if (data ==
"bye\r")
288 typedef boost::bimap<ModemId, boost::shared_ptr<RUDICSConnection> >::right_map::iterator
291 RightIt client_it = clients_.right.find(connection);
292 if (client_it != clients_.right.end())
294 ModemId
id = client_it->second;
295 glog.is(DEBUG1) &&
glog <<
"Detected bye from " << connection->remote_endpoint_str()
296 <<
" ID: " <<
id << std::endl;
297 remote_[id].on_call->set_bye_received(
true);
302 glog <<
"Bye detected from connection we do not have in the clients_ map: " 303 << connection->remote_endpoint_str() << std::endl;
308 parse_rudics_packet(&decoded_line, data);
311 parse_iridium_modem_message(decoded_line, &modem_msg);
313 glog.is(DEBUG1) &&
glog <<
"Received RUDICS message from: " << modem_msg.src()
314 <<
" to: " << modem_msg.dest()
315 <<
" from endpoint: " << connection->remote_endpoint_str()
317 if (!clients_.left.count(modem_msg.src()))
319 clients_.left.insert(std::make_pair(modem_msg.src(), connection));
320 remote_[modem_msg.src()].on_call.reset(
new OnCallBase);
323 remote_[modem_msg.src()].on_call->set_last_rx_time(goby_time<double>());
330 glog.is(DEBUG1) &&
glog << warn <<
"Could not decode packet: " << e.what() << std::endl;
331 connection->add_packet_failure();
335 void goby::acomms::IridiumShoreDriver::receive_sbd_mo()
341 catch (std::exception& e)
343 glog.is(DEBUG1) &&
glog << warn <<
"Could not handle SBD receive: " << e.what()
347 std::set<boost::shared_ptr<SBDConnection> >::iterator it =
348 mo_sbd_server_->connections().begin(),
349 end = mo_sbd_server_->connections().end();
352 const int timeout = 5;
353 if ((*it)->message().data_ready())
357 glog.is(DEBUG1) &&
glog <<
"Rx SBD PreHeader: " 358 << (*it)->message().pre_header().DebugString() << std::endl;
359 glog.is(DEBUG1) &&
glog <<
"Rx SBD Header: " << (*it)->message().header().DebugString()
361 glog.is(DEBUG1) &&
glog <<
"Rx SBD Payload: " << (*it)->message().body().DebugString()
367 parse_rudics_packet(&bytes, (*it)->message().body().payload());
368 parse_iridium_modem_message(bytes, &modem_msg);
370 glog.is(DEBUG1) &&
glog <<
"Rx SBD ModemTransmission: " 371 << modem_msg.ShortDebugString() << std::endl;
377 glog.is(DEBUG1) &&
glog << warn <<
"Could not decode SBD packet: " << e.what()
380 mo_sbd_server_->connections().erase(it++);
382 else if ((*it)->connect_time() > 0 &&
385 glog.is(DEBUG1) &&
glog <<
"Removing SBD connection that has timed out:" 386 << (*it)->remote_endpoint_str() << std::endl;
387 mo_sbd_server_->connections().erase(it++);
396 void goby::acomms::IridiumShoreDriver::send_sbd_mt(
const std::string& bytes,
397 const std::string& imei)
401 using boost::asio::ip::tcp;
403 boost::asio::io_service io_service;
405 tcp::resolver resolver(io_service);
406 tcp::resolver::query query(
407 driver_cfg_.GetExtension(IridiumShoreDriverConfig::mt_sbd_server_address),
408 goby::util::as<std::string>(
409 driver_cfg_.GetExtension(IridiumShoreDriverConfig::mt_sbd_server_port)));
410 tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
411 tcp::resolver::iterator end;
413 tcp::socket socket(io_service);
414 boost::system::error_code error = boost::asio::error::host_not_found;
415 while (error && endpoint_iterator != end)
418 socket.connect(*endpoint_iterator++, error);
421 throw boost::system::system_error(error);
423 boost::asio::write(socket, boost::asio::buffer(create_sbd_mt_data_message(bytes, imei)));
426 boost::asio::async_read(
427 socket, boost::asio::buffer(message.data()),
428 boost::asio::transfer_at_least(SBDMessageReader::PRE_HEADER_SIZE),
429 boost::bind(&SBDMessageReader::pre_header_handler, &message, _1, _2));
432 const int timeout = 5;
437 if (message.data_ready())
439 glog.is(DEBUG1) &&
glog <<
"Tx SBD Confirmation: " << message.confirm().DebugString()
444 glog.is(WARN) &&
glog <<
"Timeout waiting for confirmation message from DirectIP server" 448 catch (std::exception& e)
450 glog.is(WARN) &&
glog <<
"Could not sent MT SBD message: " << e.what() << std::endl;
454 std::string goby::acomms::IridiumShoreDriver::create_sbd_mt_data_message(
const std::string& bytes,
455 const std::string& imei)
467 IEI_MT_HEADER = 0x41,
468 IEI_MT_PAYLOAD = 0x42
473 header.set_iei(IEI_MT_HEADER);
474 header.set_length(HEADER_SIZE);
475 header.set_client_id(i++);
476 header.set_imei(imei);
480 DISP_FLAG_FLUSH_MT_QUEUE = 0x01,
481 DISP_FLAG_SEND_RING_ALERT_NO_MTM = 0x02,
482 DISP_FLAG_UPDATE_SSD_LOCATION = 0x08,
483 DISP_FLAG_HIGH_PRIORITY_MESSAGE = 0x10,
484 DISP_FLAG_ASSIGN_MTMSN = 0x20
487 header.set_disposition_flags(DISP_FLAG_FLUSH_MT_QUEUE);
489 std::string header_bytes(IEI_SIZE + HEADER_SIZE,
'\0');
491 std::string::size_type pos = 0;
496 HEADER_CLIENT_ID = 3,
498 HEADER_DISPOSITION_FLAGS = 5
501 for (
int field = HEADER_IEI; field <= HEADER_DISPOSITION_FLAGS; ++field)
505 case HEADER_IEI: header_bytes[pos++] = header.iei() & 0xff;
break;
508 header_bytes[pos++] = (header.length() >> BITS_PER_BYTE) & 0xff;
509 header_bytes[pos++] = (header.length()) & 0xff;
512 case HEADER_CLIENT_ID:
513 header_bytes[pos++] = (header.client_id() >> 3 * BITS_PER_BYTE) & 0xff;
514 header_bytes[pos++] = (header.client_id() >> 2 * BITS_PER_BYTE) & 0xff;
515 header_bytes[pos++] = (header.client_id() >> BITS_PER_BYTE) & 0xff;
516 header_bytes[pos++] = (header.client_id()) & 0xff;
520 header_bytes.replace(pos, 15, header.imei());
524 case HEADER_DISPOSITION_FLAGS:
525 header_bytes[pos++] = (header.disposition_flags() >> BITS_PER_BYTE) & 0xff;
526 header_bytes[pos++] = (header.disposition_flags()) & 0xff;
532 payload.set_iei(IEI_MT_PAYLOAD);
533 payload.set_length(bytes.size());
534 payload.set_payload(bytes);
536 std::string payload_bytes(IEI_SIZE + bytes.size(),
'\0');
537 payload_bytes[0] = payload.iei();
538 payload_bytes[1] = (payload.length() >> BITS_PER_BYTE) & 0xff;
539 payload_bytes[2] = (payload.length()) & 0xff;
540 payload_bytes.replace(3, payload.payload().size(), payload.payload());
544 int overall_length = header_bytes.size() + payload_bytes.size();
545 std::string pre_header_bytes(PRE_HEADER_SIZE,
'\0');
546 pre_header_bytes[0] = 1;
547 pre_header_bytes[1] = (overall_length >> BITS_PER_BYTE) & 0xff;
548 pre_header_bytes[2] = (overall_length)&0xff;
550 glog.is(DEBUG1) &&
glog <<
"Tx SBD PreHeader: " << goby::util::hex_encode(pre_header_bytes)
552 glog.is(DEBUG1) &&
glog <<
"Tx SBD Header: " << header.DebugString() << std::endl;
553 glog.is(DEBUG1) &&
glog <<
"Tx SBD Payload: " << payload.DebugString() << std::endl;
555 return pre_header_bytes + header_bytes + payload_bytes;
void do_work()
Allows the modem driver to do its work.
void startup(const protobuf::DriverConfig &cfg)
Starts the modem driver. Must be called before poll().
void handle_initiate_transmission(const protobuf::ModemTransmission &m)
Virtual initiate_transmission method. Typically connected to MACManager::signal_initiate_transmission...
double goby_time< double >()
Returns current UTC time as seconds and fractional seconds since 1970-01-01 00:00:00.
ReturnType goby_time()
Returns current UTC time as a boost::posix_time::ptime.
boost::signals2::signal< void(const protobuf::ModemTransmission &message)> signal_receive
Called when a binary data transmission is received from the modem.
void shutdown()
Shuts down the modem driver.
common::FlexOstream glog
Access the Goby logger through this object.
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...
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 ...