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()
238 const goby::acomms::protobuf::DriverConfig&
driver_cfg()
const {
return driver_cfg_; }
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_;
262 const goby::acomms::protobuf::DriverConfig& driver_cfg_;
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));
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>>;
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>>;
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");
void post_event(const event_base_ptr_type &pEvent)
int total_bytes_sent() const
bool is(goby::util::logger::Verbosity verbosity)
goby::util::logger::GroupSetter group(std::string n)
extern ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier< ::goby::acomms::protobuf::DriverConfig, ::PROTOBUF_NAMESPACE_ID::internal::MessageTypeTraits< ::goby::acomms::iridium::protobuf::Config >, 11, false > config
unsigned sbd_csum(const std::string &data)
void bind(ModemDriverBase &driver, QueueManager &queue_manager)
binds the driver link-layer callbacks to the QueueManager
The global namespace for the Goby project.
util::FlexOstream glog
Access the Goby logger through this object.
extern ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier< ::google::protobuf::MessageOptions, ::PROTOBUF_NAMESPACE_ID::internal::MessageTypeTraits< ::goby::GobyMessageOptions >, 11, false > msg
void split(const char *b, const char *e, char d, std::function< void(const char *, const char *)> fn)
boost::mpl::list< boost::statechart::transition< EvReset, Active > > reactions
~Answer() override=default
boost::mpl::list< boost::statechart::transition< EvNoCarrier, Ready > > reactions
void in_state_react(const EvRxSerial &)
void clear_sbd_rx_buffer()
void in_state_react(const EvTxSerial &)
void in_state_react(const EvAck &)
~Command() override=default
void push_at_command(const std::string &cmd)
boost::circular_buffer< std::pair< ATSentenceMeta, std::string > > & at_out()
boost::mpl::list< boost::statechart::in_state_reaction< EvRxSerial, Command, &Command::in_state_react >, boost::statechart::in_state_reaction< EvTxSerial, Command, &Command::in_state_react >, boost::statechart::transition< EvOnline, Online >, boost::statechart::in_state_reaction< EvAck, Command, &Command::in_state_react > > reactions
void handle_sbd_rx(const std::string &in)
boost::mpl::list< boost::statechart::custom_reaction< EvNoCarrier > > reactions
boost::statechart::result react(const EvNoCarrier &)
EvAck(std::string response)
EvSBDBeginData(std::string data="", bool in_response_to_ring_alert=false)
bool in_response_to_ring_alert_
EvSBDTransmitComplete(std::string sbdi)
HangingUp(my_context ctx)
boost::mpl::list< boost::statechart::transition< EvAtEmpty, Ready > > reactions
~HangingUp() override=default
boost::circular_buffer< goby::acomms::protobuf::ModemTransmission > & received()
const goby::acomms::iridium::protobuf::Config & iridium_driver_cfg() const
void buffer_data_out(const goby::acomms::protobuf::ModemTransmission &msg)
const std::string & glog_ir_group() const
boost::circular_buffer< std::string > & serial_tx_buffer()
const goby::acomms::protobuf::DriverConfig & driver_cfg() const
boost::circular_buffer< goby::acomms::protobuf::ModemTransmission > & data_out()
void parse_ciev(const std::string &ciev)
IridiumDriverFSM(const goby::acomms::protobuf::DriverConfig &driver_cfg)
const CIEVData & ciev_data() const
~NotOnCall() override=default
boost::mpl::list< boost::statechart::transition< EvConnect, OnCall > > reactions
boost::mpl::list< boost::statechart::transition< EvNoCarrier, NotOnCall >, boost::statechart::in_state_reaction< EvRxOnCallSerial, OnCall, &OnCall::in_state_react >, boost::statechart::in_state_reaction< EvTxOnCallSerial, OnCall, &OnCall::in_state_react >, boost::statechart::in_state_reaction< EvSendBye, OnCall, &OnCall::in_state_react > > reactions
void in_state_react(const EvSendBye &)
void in_state_react(const EvRxOnCallSerial &)
void in_state_react(const EvTxOnCallSerial &)
void in_state_react(const EvTxSerial &)
~Online() override=default
boost::mpl::list< boost::statechart::transition< EvHangup, HangingUp >, boost::statechart::transition< EvDisconnect, PostDisconnected >, boost::statechart::in_state_reaction< EvRxSerial, Online, &Online::in_state_react >, boost::statechart::in_state_reaction< EvTxSerial, Online, &Online::in_state_react > > reactions
void in_state_react(const EvRxSerial &)
boost::mpl::list< boost::statechart::transition< EvAtEmpty, Ready > > reactions
~PostDisconnected() override=default
PostDisconnected(my_context ctx)
boost::statechart::result react(const EvDial &)
~Ready() override=default
boost::mpl::list< boost::statechart::transition< EvRing, Answer >, boost::statechart::custom_reaction< EvDial > > reactions
boost::mpl::list< boost::statechart::transition< EvSBDSendBufferCleared, SBDWrite > > reactions
~SBDClearBuffers() override=default
SBDClearBuffers(my_context ctx)
boost::mpl::list< boost::statechart::transition< EvSBDBeginData, SBDClearBuffers, SBD, &SBD::set_data > > reactions
~SBDReady() override=default
boost::mpl::list< boost::statechart::transition< EvSBDReceiveComplete, SBDReady > > reactions
SBDReceive(my_context ctx)
~SBDReceive() override=default
std::string mo_status_as_string(int code)
SBDTransmit(my_context ctx)
boost::statechart::result react(const EvSBDTransmitComplete &e)
boost::mpl::list< boost::statechart::custom_reaction< EvSBDTransmitComplete > > reactions
boost::mpl::list< boost::statechart::in_state_reaction< EvSBDWriteReady, SBDWrite, &SBDWrite::in_state_react >, boost::statechart::transition< EvSBDWriteComplete, SBDTransmit > > reactions
~SBDWrite() override=default
void in_state_react(const EvSBDWriteReady &)
void set_data(const EvSBDBeginData &e)
const std::string & data() const
bool in_response_to_ring_alert() const
StateNotify(std::string name)