25 #ifndef GOBY_ACOMMS_MODEMDRIVER_IRIDIUM_DRIVER_FSM_H
26 #define GOBY_ACOMMS_MODEMDRIVER_IRIDIUM_DRIVER_FSM_H
35 #include <boost/algorithm/string/classification.hpp>
36 #include <boost/algorithm/string/split.hpp>
37 #include <boost/algorithm/string/trim.hpp>
38 #include <boost/bind/bind.hpp>
39 #include <boost/circular_buffer.hpp>
40 #include <boost/lexical_cast/bad_lexical_cast.hpp>
41 #include <boost/mpl/list.hpp>
42 #include <boost/smart_ptr/intrusive_ptr.hpp>
43 #include <boost/statechart/custom_reaction.hpp>
45 #include <boost/statechart/in_state_reaction.hpp>
46 #include <boost/statechart/result.hpp>
50 #include <boost/statechart/transition.hpp>
67 inline unsigned sbd_csum(
const std::string& data)
69 unsigned int csum = 0;
70 for (
char it : data) csum += it & 0xFF;
207 bool service_available{
false};
212 : serial_tx_buffer_(SERIAL_BUFFER_CAPACITY),
213 received_(RECEIVED_BUFFER_CAPACITY),
215 data_out_(DATA_BUFFER_CAPACITY)
218 glog_ir_group_ =
"iridiumdriver::" + goby::util::as<std::string>(count_);
227 boost::circular_buffer<goby::acomms::protobuf::ModemTransmission>&
received()
233 boost::circular_buffer<goby::acomms::protobuf::ModemTransmission>&
data_out()
246 const CIEVData&
ciev_data()
const {
return ciev_data_; }
253 SERIAL_BUFFER_CAPACITY = 10
257 RECEIVED_BUFFER_CAPACITY = 10
260 boost::circular_buffer<std::string> serial_tx_buffer_;
261 boost::circular_buffer<goby::acomms::protobuf::ModemTransmission> received_;
266 DATA_BUFFER_CAPACITY = 5
268 boost::circular_buffer<goby::acomms::protobuf::ModemTransmission> data_out_;
270 std::string glog_ir_group_;
278 boost::mpl::list<Command, NotOnCall>>
280 typedef boost::mpl::list<boost::statechart::transition<EvReset, Active>>
reactions;
285 boost::mpl::list<Configure, SBD>>,
297 boost::statechart::in_state_reaction<EvRxSerial, Command, &Command::in_state_react>,
298 boost::statechart::in_state_reaction<EvTxSerial, Command, &Command::in_state_react>,
299 boost::statechart::transition<EvOnline, Online>,
300 boost::statechart::in_state_reaction<EvAck, Command, &Command::in_state_react>>;
314 boost::circular_buffer<std::pair<ATSentenceMeta, std::string>>&
at_out() {
return at_out_; }
323 AT_BUFFER_CAPACITY = 100
325 boost::circular_buffer<std::pair<ATSentenceMeta, std::string>> at_out_;
328 COMMAND_TIMEOUT_SECONDS = 2,
329 DIAL_TIMEOUT_SECONDS = 60,
330 SBDIX_TIMEOUT_SECONDS = DIAL_TIMEOUT_SECONDS,
331 TRIPLE_PLUS_TIMEOUT_SECONDS = 6,
332 HANGUP_TIMEOUT_SECONDS = 10,
333 ANSWER_TIMEOUT_SECONDS = 30
338 RETRIES_BEFORE_RESET = 3
340 std::string sbd_rx_buffer_;
345 using reactions = boost::mpl::list<boost::statechart::transition<EvAtEmpty, Ready>>;
349 context<Command>().push_at_command(
"");
350 const auto& iridium_driver_config = context<IridiumDriverFSM>().iridium_driver_cfg();
351 for (
int i = 0, n = iridium_driver_config.config_size(); i < n; ++i)
353 context<Command>().push_at_command(iridium_driver_config.config(i));
364 ~Ready()
override =
default;
368 if (state_downcast<const NotOnCall*>() != 0)
370 return transit<Dial>();
375 glog <<
group(
"iridiumdriver") <<
"Not dialing since we are already on a call."
381 using reactions = boost::mpl::list<boost::statechart::transition<EvRing, Answer>,
382 boost::statechart::custom_reaction<EvDial>>;
392 context<Command>().push_at_command(
"+++");
393 context<Command>().push_at_command(
"H");
397 using reactions = boost::mpl::list<boost::statechart::transition<EvAtEmpty, Ready>>;
409 glog <<
group(
"iridiumdriver") <<
"Disconnected; checking error details: " << std::endl;
410 context<Command>().push_at_command(
"+CEER");
414 using reactions = boost::mpl::list<boost::statechart::transition<EvAtEmpty, Ready>>;
421 using reactions = boost::mpl::list<boost::statechart::custom_reaction<EvNoCarrier>>;
427 ~Dial()
override =
default;
438 using reactions = boost::mpl::list<boost::statechart::transition<EvNoCarrier, Ready>>;
442 context<Command>().push_at_command(
"A");
457 boost::statechart::transition<EvHangup, HangingUp>,
458 boost::statechart::transition<EvDisconnect, PostDisconnected>,
459 boost::statechart::in_state_reaction<EvRxSerial, Online, &Online::in_state_react>,
460 boost::statechart::in_state_reaction<EvTxSerial, Online, &Online::in_state_react>>;
466 using reactions = boost::mpl::list<boost::statechart::transition<EvConnect, OnCall>>;
480 context<IridiumDriverFSM>().serial_tx_buffer().push_front(
"goby\r");
499 boost::statechart::transition<EvNoCarrier, NotOnCall>,
500 boost::statechart::in_state_reaction<EvRxOnCallSerial, OnCall, &OnCall::in_state_react>,
501 boost::statechart::in_state_reaction<EvTxOnCallSerial, OnCall, &OnCall::in_state_react>,
502 boost::statechart::in_state_reaction<EvSendBye, OnCall, &OnCall::in_state_react>>;
510 ~SBD()
override =
default;
515 in_response_to_ring_alert_ =
e.in_response_to_ring_alert_;
518 const std::string&
data()
const {
return data_; }
530 const int bits_in_byte = 8;
531 data_ =
data + std::string(1, (csum & 0xFF00) >> bits_in_byte) +
532 std::string(1, (csum & 0xFF));
538 bool in_response_to_ring_alert_;
558 boost::statechart::transition<EvSBDBeginData, SBDClearBuffers, SBD, &SBD::set_data>>;
568 boost::mpl::list<boost::statechart::transition<EvSBDSendBufferCleared, SBDWrite>>;
572 context<Command>().clear_sbd_rx_buffer();
573 context<Command>().push_at_command(
"+SBDD2");
583 if (context<SBD>().data().empty())
586 <<
"Mailbox Check." << std::endl;
594 const int csum_bytes = 2;
595 context<Command>().push_at_command(
596 "+SBDWB=" + goby::util::as<std::string>(context<SBD>().data().size() - csum_bytes));
602 context<IridiumDriverFSM>().serial_tx_buffer().push_back(context<SBD>().data());
608 boost::statechart::in_state_reaction<EvSBDWriteReady, SBDWrite, &SBDWrite::in_state_react>,
609 boost::statechart::transition<EvSBDWriteComplete, SBDTransmit>>;
614 using reactions = boost::mpl::list<boost::statechart::custom_reaction<EvSBDTransmitComplete>>;
617 if (context<SBD>().in_response_to_ring_alert())
618 context<Command>().push_at_command(
"+SBDIXA");
620 context<Command>().push_at_command(
"+SBDIX");
629 case 0:
return "MO message, if any, transferred successfully";
631 return "MO message, if any, transferred successfully, but the MT message in the "
632 "queue was too big to be transferred";
634 return "MO message, if any, transferred successfully, but the requested Location "
635 "Update was not accepted";
638 return "Reserved, but indicate MO session success if used";
661 default:
return "Reserved, but indicate MO session failure if used";
662 case 10:
return "GSS reported that the call did not complete in the allowed time";
663 case 11:
return "MO message queue at the GSS is full";
664 case 12:
return "MO message has too many segments";
665 case 13:
return "GSS reported that the session did not complete";
666 case 14:
return "Invalid segment size";
667 case 15:
return "Access is denied";
668 case 16:
return "Modem has been locked and may not make SBD calls";
669 case 17:
return "Gateway not responding (local session timeout)";
670 case 18:
return "Connection lost (RF drop)";
671 case 19:
return "Link failure (A protocol error caused termination of the call)";
672 case 32:
return "No network service, unable to initiate call";
673 case 35:
return "Iridium 9523 is busy, unable to initiate call";
680 std::vector<std::string> sbdi_fields;
684 sbdi_fields.begin(), sbdi_fields.end(),
685 boost::bind(&boost::trim<std::string>, boost::placeholders::_1, std::locale()));
687 if (sbdi_fields.size() != 7)
690 <<
"Invalid +SBDI response: " <<
e.sbdi_
692 return transit<SBDReady>();
707 MT_STATUS_NO_MESSAGE = 0,
708 MT_STATUS_RECEIVED_MESSAGE = 1,
715 MO_STATUS_SUCCESS_MAX = 4,
716 MO_STATUS_FAILURE_MIN = 5
719 int mo_status = goby::util::as<int>(sbdi_fields[MO_STATUS]);
720 if (mo_status <= MO_STATUS_SUCCESS_MAX)
733 return transit<SBDReady>();
736 int mt_status = goby::util::as<int>(sbdi_fields[MT_STATUS]);
737 if (mt_status == MT_STATUS_RECEIVED_MESSAGE)
738 return transit<SBDReceive>();
740 return transit<SBDReady>();
748 boost::mpl::list<boost::statechart::transition<EvSBDReceiveComplete, SBDReady>>;
751 context<Command>().push_at_command(
"+SBDRB");