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 ...