23 #include <boost/algorithm/string.hpp> 24 #include <boost/algorithm/string/classification.hpp> 26 #include "goby/acomms/acomms_constants.h" 27 #include "goby/common/logger.h" 28 #include "goby/common/time.h" 29 #include "goby/util/binary.h" 31 #include "iridium_driver_fsm.h" 32 #include "rudics_packet.h" 38 int goby::acomms::fsm::IridiumDriverFSM::count_ = 0;
40 void goby::acomms::fsm::IridiumDriverFSM::buffer_data_out(
43 data_out_.push_back(msg);
46 void goby::acomms::fsm::Command::in_state_react(
const EvRxSerial& e)
48 std::string in = e.line;
51 if (!at_out().empty() && at_out().front().second ==
"+SBDRB")
60 if (!at_out().empty() && at_out().front().second !=
"E" &&
61 (in == std::string(
"AT" + at_out().front().second)))
63 glog.is(WARN) &&
glog << group(
"iridiumdriver") <<
"Echo turned on. Disabling" << std::endl;
65 at_out_.insert(at_out_.begin() + 1, std::make_pair(ATSentenceMeta(),
"E"));
69 static const std::string
connect =
"CONNECT";
70 static const std::string sbdi =
"+SBDI";
74 post_event(EvAck(in));
76 else if (in ==
"RING")
80 else if (in ==
"NO CARRIER")
82 post_event(EvAck(in));
83 post_event(EvNoCarrier());
85 else if (in.compare(0, connect.size(), connect) == 0)
87 post_event(EvAck(in));
88 post_event(EvConnect());
90 else if (in ==
"NO DIALTONE")
92 post_event(EvAck(in));
93 post_event(EvNoCarrier());
95 else if (in ==
"BUSY")
97 post_event(EvAck(in));
98 post_event(EvNoCarrier());
100 else if (in ==
"ERROR")
102 post_event(EvReset());
104 else if (in ==
"0" || in ==
"1" || in ==
"2" || in ==
"3")
106 post_event(EvAck(in));
108 else if (in ==
"READY")
110 post_event(EvAck(in));
112 else if (in.compare(0, sbdi.size(), sbdi) == 0)
114 post_event(EvSBDTransmitComplete(in));
116 else if (in ==
"SBDRING")
118 post_event(EvSBDBeginData(
"",
true));
122 void goby::acomms::fsm::Command::handle_sbd_rx(
const std::string& in)
126 SBD_FIELD_SIZE_BYTES = 2,
127 SBD_BITS_IN_BYTE = 8,
128 SBD_CHECKSUM_BYTES = 2
131 if (sbd_rx_buffer_.empty() && in.at(0) ==
'\n')
132 sbd_rx_buffer_ = in.substr(1);
134 sbd_rx_buffer_ += in;
137 if (sbd_rx_buffer_.size() < SBD_FIELD_SIZE_BYTES)
141 unsigned sbd_rx_size =
142 ((sbd_rx_buffer_[0] & 0xff) << SBD_BITS_IN_BYTE) | (sbd_rx_buffer_[1] & 0xff);
143 glog.is(DEBUG1) &&
glog << group(
"iridiumdriver") <<
"SBD RX Size: " << sbd_rx_size
146 if (sbd_rx_buffer_.size() < (SBD_FIELD_SIZE_BYTES + sbd_rx_size))
152 std::string sbd_rx_data = sbd_rx_buffer_.substr(SBD_FIELD_SIZE_BYTES, sbd_rx_size);
154 parse_rudics_packet(&bytes, sbd_rx_data);
155 protobuf::ModemTransmission msg;
156 parse_iridium_modem_message(bytes, &msg);
157 context<IridiumDriverFSM>().received().push_back(msg);
158 at_out().pop_front();
160 post_event(EvSBDReceiveComplete());
168 void goby::acomms::fsm::Command::in_state_react(
const EvTxSerial&)
172 if (!at_out_.empty())
174 double timeout = COMMAND_TIMEOUT_SECONDS;
175 switch (at_out_.front().second[0])
178 case 'D': timeout = DIAL_TIMEOUT_SECONDS;
break;
179 case 'A': timeout = ANSWER_TIMEOUT_SECONDS;
break;
180 case 'H': timeout = HANGUP_TIMEOUT_SECONDS;
break;
182 if (at_out_.front().second ==
"+++")
183 timeout = TRIPLE_PLUS_TIMEOUT_SECONDS;
187 static const std::string sbdi =
"+SBDI";
188 if (at_out_.front().second.compare(0, sbdi.size(), sbdi) == 0)
189 timeout = SBDIX_TIMEOUT_SECONDS;
191 if (at_out_.front().second ==
"+SBDRB")
192 clear_sbd_rx_buffer();
194 if ((at_out_.front().first.last_send_time_ + timeout) < now)
196 std::string at_command;
197 if (at_out_.front().second !=
"+++")
198 at_command =
"AT" + at_out_.front().second +
"\r";
200 at_command = at_out_.front().second;
202 if (++at_out_.front().first.tries_ > RETRIES_BEFORE_RESET)
204 glog.is(DEBUG1) &&
glog << group(
"iridiumdriver") << warn
205 <<
"No valid response after " << RETRIES_BEFORE_RESET
206 <<
" tries. Resetting state machine" << std::endl;
207 post_event(EvReset());
211 context<IridiumDriverFSM>().serial_tx_buffer().push_back(at_command);
212 at_out_.front().first.last_send_time_ = now;
218 void goby::acomms::fsm::Online::in_state_react(
const EvRxSerial& e)
225 void goby::acomms::fsm::Online::in_state_react(
const EvTxSerial&)
227 post_event(EvTxOnCallSerial());
230 void goby::acomms::fsm::Command::in_state_react(
const EvAck& e)
233 if (e.response_.size() > 0)
235 switch (e.response_[0])
238 if (!at_out().empty() && at_out().front().second ==
"+SBDD2")
240 post_event(EvSBDSendBufferCleared());
242 else if (at_out().empty())
244 post_event(EvSBDWriteComplete());
245 push_at_command(
"AT");
255 if (!at_out().empty())
257 const std::string& last_at = at_out().front().second;
258 if (last_at.size() > 0 && (e.response_ ==
"OK"))
263 post_event(EvNoCarrier());
268 case 'D': post_event(EvNoCarrier());
break;
274 if (e.response_ ==
"READY")
275 post_event(EvSBDWriteReady());
277 at_out().pop_front();
278 if (at_out().empty())
279 post_event(EvAtEmpty());
283 glog.is(DEBUG1) &&
glog << group(
"iridiumdriver") << warn <<
"Unexpected '" << e.response_
288 boost::statechart::result goby::acomms::fsm::Dial::react(
const EvNoCarrier& x)
290 const int redial_wait_seconds = 2;
291 glog.is(DEBUG1) &&
glog << group(
"iridiumdriver") <<
"Redialing in " << redial_wait_seconds
292 <<
" seconds ..." << std::endl;
294 sleep(redial_wait_seconds);
296 const int max_attempts =
297 context<IridiumDriverFSM>().driver_cfg().GetExtension(IridiumDriverConfig::dial_attempts);
298 if (dial_attempts_ < max_attempts)
301 return discard_event();
305 glog.is(DEBUG1) &&
glog << warn << group(
"iridiumdriver") <<
"Failed to connect after " 306 << max_attempts <<
" tries." << std::endl;
308 return transit<Ready>();
312 void goby::acomms::fsm::Dial::dial()
315 context<Command>().push_at_command(
"D" + context<IridiumDriverFSM>()
317 .GetExtension(IridiumDriverConfig::remote)
321 void goby::acomms::fsm::OnCall::in_state_react(
const EvRxOnCallSerial& e)
323 std::string in = e.line;
326 static const std::string no_carrier =
"NO CARRIER";
327 if (in.find(no_carrier) != std::string::npos)
329 post_event(EvNoCarrier());
331 else if (boost::trim_copy(in) ==
"goby")
333 glog.is(DEBUG1) &&
glog << group(
"iridiumdriver") <<
"Detected start of Goby RUDICS call" 336 else if (boost::trim_copy(in) ==
"bye")
338 glog.is(DEBUG1) &&
glog << group(
"iridiumdriver")
339 <<
"Detected remote completion of Goby RUDICS call" << std::endl;
340 set_bye_received(
true);
347 parse_rudics_packet(&bytes, in);
349 protobuf::ModemTransmission msg;
350 parse_iridium_modem_message(bytes, &msg);
351 context<IridiumDriverFSM>().received().push_back(msg);
354 catch (RudicsPacketException& e)
356 glog.is(DEBUG1) &&
glog << warn << group(
"iridiumdriver")
357 <<
"Could not decode packet: " << e.what() << std::endl;
362 void goby::acomms::fsm::OnCall::in_state_react(
const EvTxOnCallSerial&)
364 const static double target_byte_rate = (context<IridiumDriverFSM>().driver_cfg().GetExtension(
365 IridiumDriverConfig::target_bit_rate) /
366 static_cast<double>(goby::acomms::BITS_IN_BYTE));
368 const double send_wait = last_bytes_sent() / target_byte_rate;
371 boost::circular_buffer<protobuf::ModemTransmission>& data_out =
372 context<IridiumDriverFSM>().data_out();
373 if (!data_out.empty() && (now > last_tx_time() + send_wait))
377 serialize_iridium_modem_message(&bytes, data_out.front());
380 std::string rudics_packet;
381 serialize_rudics_packet(bytes, &rudics_packet);
383 context<IridiumDriverFSM>().serial_tx_buffer().push_back(rudics_packet);
384 data_out.pop_front();
386 set_last_bytes_sent(rudics_packet.size());
387 set_last_tx_time(now);
391 void goby::acomms::fsm::OnCall::in_state_react(
const EvSendBye&)
393 context<IridiumDriverFSM>().serial_tx_buffer().push_front(
"bye\r");
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.
void connect(Signal *signal, Slot slot)
connect a signal to a slot (e.g. function pointer)
common::FlexOstream glog
Access the Goby logger through this object.