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>
68inline unsigned sbd_csum(
const std::string& data)
70 unsigned int csum = 0;
71 for (
char it : data) csum += it & 0xFF;
212 bool service_available{
false};
217 : serial_tx_buffer_(SERIAL_BUFFER_CAPACITY),
218 received_(RECEIVED_BUFFER_CAPACITY),
220 data_out_(DATA_BUFFER_CAPACITY)
223 glog_ir_group_ =
"iridiumdriver::" + goby::util::as<std::string>(count_);
232 boost::circular_buffer<goby::acomms::protobuf::ModemTransmission>&
received()
238 boost::circular_buffer<goby::acomms::protobuf::ModemTransmission>&
data_out()
251 const CIEVData&
ciev_data()
const {
return ciev_data_; }
258 SERIAL_BUFFER_CAPACITY = 10
262 RECEIVED_BUFFER_CAPACITY = 10
265 boost::circular_buffer<std::string> serial_tx_buffer_;
266 boost::circular_buffer<goby::acomms::protobuf::ModemTransmission> received_;
271 DATA_BUFFER_CAPACITY = 5
273 boost::circular_buffer<goby::acomms::protobuf::ModemTransmission> data_out_;
275 std::string glog_ir_group_;
283 boost::mpl::list<Command, NotOnCall>>
285 typedef boost::mpl::list<boost::statechart::transition<EvReset, Active>>
reactions;
290 boost::mpl::list<Configure, SBD>>,
302 boost::statechart::in_state_reaction<EvRxSerial, Command, &Command::in_state_react>,
303 boost::statechart::in_state_reaction<EvTxSerial, Command, &Command::in_state_react>,
304 boost::statechart::transition<EvOnline, Online>,
305 boost::statechart::in_state_reaction<EvAck, Command, &Command::in_state_react>>;
319 boost::circular_buffer<std::pair<ATSentenceMeta, std::string>>&
at_out() {
return at_out_; }
328 AT_BUFFER_CAPACITY = 100
330 boost::circular_buffer<std::pair<ATSentenceMeta, std::string>> at_out_;
333 COMMAND_TIMEOUT_SECONDS = 2,
334 DIAL_TIMEOUT_SECONDS = 60,
335 SBDIX_TIMEOUT_SECONDS = DIAL_TIMEOUT_SECONDS,
336 TRIPLE_PLUS_TIMEOUT_SECONDS = 6,
337 HANGUP_TIMEOUT_SECONDS = 10,
338 ANSWER_TIMEOUT_SECONDS = 30
343 RETRIES_BEFORE_RESET = 3
345 std::string sbd_rx_buffer_;
350 using reactions = boost::mpl::list<boost::statechart::transition<EvAtEmpty, Ready>>;
354 context<Command>().push_at_command(
"");
355 const auto& iridium_driver_config = context<IridiumDriverFSM>().iridium_driver_cfg();
356 for (
int i = 0, n = iridium_driver_config.config_size(); i < n; ++i)
358 context<Command>().push_at_command(iridium_driver_config.config(i));
373 if (state_downcast<const NotOnCall*>() != 0)
375 return transit<Dial>();
380 glog <<
group(
"iridiumdriver") <<
"Not dialing since we are already on a call."
386 using reactions = boost::mpl::list<boost::statechart::transition<EvRing, Answer>,
387 boost::statechart::custom_reaction<EvDial>>;
397 context<Command>().push_at_command(
"+++");
398 context<Command>().push_at_command(
"H");
402 using reactions = boost::mpl::list<boost::statechart::transition<EvAtEmpty, Ready>>;
414 glog <<
group(
"iridiumdriver") <<
"Disconnected; checking error details: " << std::endl;
415 context<Command>().push_at_command(
"+CEER");
419 using reactions = boost::mpl::list<boost::statechart::transition<EvAtEmpty, Ready>>;
426 using reactions = boost::mpl::list<boost::statechart::custom_reaction<EvNoCarrier>>;
443 using reactions = boost::mpl::list<boost::statechart::transition<EvNoCarrier, Ready>>;
447 context<Command>().push_at_command(
"A");
462 boost::statechart::transition<EvHangup, HangingUp>,
463 boost::statechart::transition<EvDisconnect, PostDisconnected>,
464 boost::statechart::in_state_reaction<EvRxSerial, Online, &Online::in_state_react>,
465 boost::statechart::in_state_reaction<EvTxSerial, Online, &Online::in_state_react>>;
471 using reactions = boost::mpl::list<boost::statechart::transition<EvConnect, OnCall>>;
485 context<IridiumDriverFSM>().serial_tx_buffer().push_front(
"goby\r");
504 boost::statechart::transition<EvNoCarrier, NotOnCall>,
505 boost::statechart::in_state_reaction<EvRxOnCallSerial, OnCall, &OnCall::in_state_react>,
506 boost::statechart::in_state_reaction<EvTxOnCallSerial, OnCall, &OnCall::in_state_react>,
507 boost::statechart::in_state_reaction<EvSendBye, OnCall, &OnCall::in_state_react>>;
520 in_response_to_ring_alert_ = e.in_response_to_ring_alert_;
523 const std::string&
data()
const {
return data_; }
535 const int bits_in_byte = 8;
536 data_ =
data + std::string(1, (csum & 0xFF00) >> bits_in_byte) +
537 std::string(1, (csum & 0xFF));
543 bool in_response_to_ring_alert_;
563 boost::statechart::transition<EvSBDBeginData, SBDClearBuffers, SBD, &SBD::set_data>>;
573 boost::mpl::list<boost::statechart::transition<EvSBDSendBufferCleared, SBDWrite>>;
577 context<Command>().clear_sbd_rx_buffer();
578 context<Command>().push_at_command(
"+SBDD2");
588 if (context<SBD>().data().empty())
591 <<
"Mailbox Check." << std::endl;
599 const int csum_bytes = 2;
600 context<Command>().push_at_command(
601 "+SBDWB=" + goby::util::as<std::string>(context<SBD>().data().size() - csum_bytes));
608 context<IridiumDriverFSM>().serial_tx_buffer().push_back(context<SBD>().data());
616 boost::statechart::in_state_reaction<EvSBDWriteReady, SBDWrite, &SBDWrite::in_state_react>,
619 boost::statechart::transition<EvSBDWriteComplete, SBDTransmit>>;
627 using reactions = boost::mpl::list<boost::statechart::custom_reaction<EvSBDTransmitComplete>>;
630 if (context<SBD>().in_response_to_ring_alert())
631 context<Command>().push_at_command(
"+SBDIXA");
633 context<Command>().push_at_command(
"+SBDIX");
642 case 0:
return "MO message, if any, transferred successfully";
644 return "MO message, if any, transferred successfully, but the MT message in the "
645 "queue was too big to be transferred";
647 return "MO message, if any, transferred successfully, but the requested Location "
648 "Update was not accepted";
651 return "Reserved, but indicate MO session success if used";
674 default:
return "Reserved, but indicate MO session failure if used";
675 case 10:
return "GSS reported that the call did not complete in the allowed time";
676 case 11:
return "MO message queue at the GSS is full";
677 case 12:
return "MO message has too many segments";
678 case 13:
return "GSS reported that the session did not complete";
679 case 14:
return "Invalid segment size";
680 case 15:
return "Access is denied";
681 case 16:
return "Modem has been locked and may not make SBD calls";
682 case 17:
return "Gateway not responding (local session timeout)";
683 case 18:
return "Connection lost (RF drop)";
684 case 19:
return "Link failure (A protocol error caused termination of the call)";
685 case 32:
return "No network service, unable to initiate call";
686 case 35:
return "Iridium 9523 is busy, unable to initiate call";
693 std::vector<std::string> sbdi_fields;
694 boost::algorithm::split(sbdi_fields, e.sbdi_, boost::is_any_of(
":,"));
697 sbdi_fields.begin(), sbdi_fields.end(),
698 boost::bind(&boost::trim<std::string>, boost::placeholders::_1, std::locale()));
700 if (sbdi_fields.size() != 7)
703 <<
"Invalid +SBDI response: " << e.sbdi_
705 return transit<SBDReady>();
720 MT_STATUS_NO_MESSAGE = 0,
721 MT_STATUS_RECEIVED_MESSAGE = 1,
728 MO_STATUS_SUCCESS_MAX = 4,
729 MO_STATUS_FAILURE_MIN = 5
732 int mo_status = goby::util::as<int>(sbdi_fields[MO_STATUS]);
733 if (mo_status <= MO_STATUS_SUCCESS_MAX)
746 return transit<SBDReady>();
749 int mt_status = goby::util::as<int>(sbdi_fields[MT_STATUS]);
750 if (mt_status == MT_STATUS_RECEIVED_MESSAGE)
751 return transit<SBDReceive>();
753 return transit<SBDReady>();
761 boost::mpl::list<boost::statechart::transition<EvSBDReceiveComplete, SBDReady>>;
764 context<Command>().push_at_command(
"+SBDRB");
void post_event(const event_base_ptr_type &pEvent)
int total_bytes_sent() const
_proto_TypeTraits::Singular::ConstType GetExtension(const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier< DriverConfig, _proto_TypeTraits, _field_type, _is_packed > &id) 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)
The global namespace for the Goby project.
extern ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier< ::PROTOBUF_NAMESPACE_ID::MessageOptions, ::PROTOBUF_NAMESPACE_ID::internal::MessageTypeTraits< ::goby::GobyMessageOptions >, 11, false > msg
util::FlexOstream glog
Access the Goby logger through this object.
boost::mpl::list< boost::statechart::transition< EvReset, Active > > reactions
~Answer() override=default
boost::mpl::list< boost::statechart::transition< EvNoCarrier, Ready > > reactions
boost::circular_buffer< std::pair< ATSentenceMeta, std::string > > & at_out()
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::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
const goby::acomms::iridium::protobuf::Config & iridium_driver_cfg() const
const goby::acomms::protobuf::DriverConfig & driver_cfg() const
boost::circular_buffer< goby::acomms::protobuf::ModemTransmission > & received()
void buffer_data_out(const goby::acomms::protobuf::ModemTransmission &msg)
boost::circular_buffer< std::string > & serial_tx_buffer()
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 std::string & glog_ir_group() const
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::in_state_reaction< EvSBDCheckWriteTimeout, SBDWrite, &SBDWrite::in_state_react >, boost::statechart::transition< EvSBDWriteComplete, SBDTransmit > > reactions
void in_state_react(const EvSBDCheckWriteTimeout &)
~SBDWrite() override=default
void in_state_react(const EvSBDWriteReady &)
const std::string & data() const
void set_data(const EvSBDBeginData &e)
bool in_response_to_ring_alert() const
StateNotify(std::string name)
std::chrono::time_point< SteadyClock > time_point
static time_point now() noexcept
Returns the current steady time unless SimulatorSettings::using_sim_time == true in which case a simu...