25 #include "iridium_driver.h" 27 #include "goby/acomms/acomms_helpers.h" 28 #include "goby/acomms/modemdriver/driver_exception.h" 29 #include "goby/acomms/modemdriver/rudics_packet.h" 30 #include "goby/common/logger.h" 31 #include "goby/util/binary.h" 36 using goby::acomms::operator<<;
38 boost::shared_ptr<dccl::Codec> goby::acomms::iridium_header_dccl_;
40 goby::acomms::IridiumDriver::IridiumDriver()
41 : fsm_(driver_cfg_), last_triple_plus_time_(0), serial_fd_(-1), next_frame_(0)
48 goby::acomms::IridiumDriver::~IridiumDriver() {}
54 glog.is(DEBUG1) &&
glog << group(glog_out_group())
55 <<
"Goby Iridium RUDICS/SBD driver starting up." << std::endl;
57 driver_cfg_.set_line_delimiter(
"\r");
59 if (driver_cfg_.HasExtension(IridiumDriverConfig::debug_client_port))
62 "localhost", driver_cfg_.GetExtension(IridiumDriverConfig::debug_client_port),
"\r"));
63 debug_client_->start();
66 if (!driver_cfg_.HasExtension(IridiumDriverConfig::use_dtr) &&
67 driver_cfg_.connection_type() == protobuf::DriverConfig::CONNECTION_SERIAL)
69 driver_cfg_.SetExtension(IridiumDriverConfig::use_dtr,
true);
73 if (driver_cfg_.GetExtension(IridiumDriverConfig::use_dtr))
74 driver_cfg_.AddExtension(IridiumDriverConfig::config,
"&D2");
76 rudics_mac_msg_.set_src(driver_cfg_.modem_id());
77 rudics_mac_msg_.set_type(goby::acomms::protobuf::ModemTransmission::DATA);
78 rudics_mac_msg_.set_rate(RATE_RUDICS);
83 void goby::acomms::IridiumDriver::modem_init()
91 while (fsm_.state_cast<
const fsm::Ready*>() == 0)
95 if (driver_cfg_.GetExtension(IridiumDriverConfig::use_dtr) &&
modem().active() && !dtr_set)
99 glog.is(DEBUG1) &&
glog << group(glog_out_group()) <<
"DTR is: " << query_dtr()
104 const int pause_ms = 10;
105 usleep(pause_ms * 1000);
108 const int start_timeout = driver_cfg_.GetExtension(IridiumDriverConfig::start_timeout);
109 if (i / (1000 / pause_ms) > start_timeout)
111 protobuf::ModemDriverStatus::STARTUP_FAILED));
115 void goby::acomms::IridiumDriver::set_dtr(
bool state)
118 if (ioctl(serial_fd_, TIOCMGET, &status) == -1)
120 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn
121 <<
"IOCTL failed: " << strerror(errno) << std::endl;
124 glog.is(DEBUG1) &&
glog << group(glog_out_group()) <<
"Setting DTR to " 125 << (state ?
"high" :
"low") << std::endl;
129 status &= ~TIOCM_DTR;
130 if (ioctl(serial_fd_, TIOCMSET, &status) == -1)
132 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn
133 <<
"IOCTL failed: " << strerror(errno) << std::endl;
137 bool goby::acomms::IridiumDriver::query_dtr()
140 if (ioctl(serial_fd_, TIOCMGET, &status) == -1)
142 glog.is(DEBUG1) &&
glog << group(glog_out_group()) << warn
143 <<
"IOCTL failed: " << strerror(errno) << std::endl;
145 return (status & TIOCM_DTR);
148 void goby::acomms::IridiumDriver::hangup()
150 if (driver_cfg_.GetExtension(IridiumDriverConfig::use_dtr))
174 if (driver_cfg_.GetExtension(IridiumDriverConfig::use_dtr))
183 process_transmission(orig_msg,
true);
190 const static unsigned frame_max = IridiumHeader::descriptor()
191 ->FindFieldByName(
"frame_start")
193 .GetExtension(dccl::field)
196 if (!msg.has_frame_start())
197 msg.set_frame_start(next_frame_ % frame_max);
200 if (!msg.has_max_frame_bytes() ||
201 msg.max_frame_bytes() > driver_cfg_.GetExtension(IridiumDriverConfig::max_frame_size))
202 msg.set_max_frame_bytes(driver_cfg_.GetExtension(IridiumDriverConfig::max_frame_size));
206 next_frame_ += msg.frame_size();
208 if (!(msg.frame_size() == 0 || msg.frame(0).empty()))
210 if (dial && msg.rate() == RATE_RUDICS)
214 else if (msg.rate() == RATE_SBD)
216 if (msg.GetExtension(IridiumDriverConfig::if_no_data_do_mailbox_check))
225 double now = goby_time<double>();
232 const double send_wait = on_call->last_bytes_sent() /
233 (driver_cfg_.GetExtension(IridiumDriverConfig::target_bit_rate) /
234 static_cast<double>(BITS_IN_BYTE));
236 if (fsm_.data_out().empty() && (now > (on_call->last_tx_time() + send_wait)))
238 if (!on_call->bye_sent())
240 process_transmission(rudics_mac_msg_,
false);
244 if (!on_call->bye_sent() &&
245 now > (on_call->last_tx_time() +
246 driver_cfg_.GetExtension(IridiumDriverConfig::handshake_hangup_seconds)))
248 glog.is(DEBUG2) &&
glog <<
"Sending bye" << std::endl;
252 if ((on_call->bye_received() && on_call->bye_sent()) ||
253 (now > (on_call->last_rx_tx_time() +
254 driver_cfg_.GetExtension(IridiumDriverConfig::hangup_seconds_after_empty))))
266 data_event.line = in;
269 glog << group(glog_in_group())
270 << (boost::algorithm::all(in, boost::is_print() || boost::is_any_of(
"\r\n"))
271 ? boost::trim_copy(in)
272 : goby::util::hex_encode(in))
275 if (debug_client_ && fsm_.state_cast<
const fsm::OnCall*>() != 0)
277 debug_client_->write(in);
280 fsm_.process_event(data_event);
283 while (!fsm_.received().empty())
285 receive(fsm_.received().front());
286 fsm_.received().pop_front();
292 while (debug_client_->readline(&line))
294 fsm_.serial_tx_buffer().push_back(line);
305 glog.is(DEBUG2) &&
glog << group(glog_in_group()) << msg << std::endl;
307 if (msg.type() == protobuf::ModemTransmission::DATA && msg.ack_requested() &&
308 msg.dest() == driver_cfg_.modem_id())
312 ack.set_type(goby::acomms::protobuf::ModemTransmission::ACK);
313 ack.set_src(msg.dest());
314 ack.set_dest(msg.src());
315 ack.set_rate(msg.rate());
316 for (
int i = msg.frame_start(), n = msg.frame_size() + msg.frame_start(); i < n; ++i)
317 ack.add_acked_frame(i);
326 glog.is(DEBUG2) &&
glog << group(glog_out_group()) << msg << std::endl;
328 if (msg.rate() == RATE_RUDICS)
329 fsm_.buffer_data_out(msg);
330 else if (msg.rate() == RATE_SBD)
332 bool on_call = (fsm_.state_cast<
const fsm::OnCall*>() != 0);
334 fsm_.buffer_data_out(msg);
337 std::string iridium_packet;
338 serialize_iridium_modem_message(&iridium_packet, msg);
340 std::string rudics_packet;
341 serialize_rudics_packet(iridium_packet, &rudics_packet);
347 void goby::acomms::IridiumDriver::try_serial_tx()
351 while (!fsm_.serial_tx_buffer().empty())
353 double now = goby_time<double>();
354 if (last_triple_plus_time_ + TRIPLE_PLUS_WAIT > now)
357 const std::string& line = fsm_.serial_tx_buffer().front();
360 glog << group(glog_out_group())
361 << (boost::algorithm::all(line, boost::is_print() || boost::is_any_of(
"\r\n"))
362 ? boost::trim_copy(line)
363 : goby::util::hex_encode(line))
370 last_triple_plus_time_ = now;
372 fsm_.serial_tx_buffer().pop_front();
376 void goby::acomms::IridiumDriver::display_state_cfg(std::ostream* os)
378 char orthogonalRegion =
'a';
380 for (fsm::IridiumDriverFSM::state_iterator pLeafState = fsm_.state_begin();
381 pLeafState != fsm_.state_end(); ++pLeafState)
383 *os <<
"Orthogonal region " << orthogonalRegion <<
": ";
385 const fsm::IridiumDriverFSM::state_base_type* pState = &*pLeafState;
389 if (pState != &*pLeafState)
394 std::string name =
typeid(*pState).name();
395 std::string prefix =
"N4goby6acomms3fsm";
396 std::string suffix =
"E";
398 if (name.find(prefix) != std::string::npos && name.find(suffix) != std::string::npos)
400 name.substr(prefix.size() + 1, name.size() - prefix.size() - suffix.size() - 1);
404 pState = pState->outer_state_ptr();
provides a basic client for line by line text based communications over a 8N1 tty (such as an RS-232 ...
void modem_write(const std::string &out)
write a line to the serial port.
virtual void do_work()=0
Allows the modem driver to do its work.
void modem_start(const protobuf::DriverConfig &cfg)
start the physical connection to the modem (serial port, TCP, etc.). must be called before ModemDrive...
void shutdown()
Shuts down the modem driver.
provides a basic TCP client for line by line text based communications to a remote TCP server ...
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 startup(const protobuf::DriverConfig &cfg)
Starts the modem driver. Must be called before poll().
void do_work()
Allows the modem driver to do its work.
util::LineBasedInterface & modem()
use for direct access to the modem
common::FlexOstream glog
Access the Goby logger through this object.
bool modem_read(std::string *in)
read a line from the serial port, including end-of-line character(s)
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...
void handle_initiate_transmission(const protobuf::ModemTransmission &m)
Virtual initiate_transmission method. Typically connected to MACManager::signal_initiate_transmission...
void modem_close()
closes the serial port. Use modem_start to reopen the port.
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 ...