23 #include <boost/algorithm/string.hpp> 24 #include <boost/algorithm/string/classification.hpp> 25 #include <boost/units/base_units/metric/knot.hpp> 26 #include <boost/units/systems/si/prefixes.hpp> 28 #include "goby/common/logger.h" 29 #include "goby/common/time.h" 30 #include "goby/util/binary.h" 32 #include "benthos_atm900_driver.h" 33 #include "benthos_atm900_driver_fsm.h" 34 #include "rudics_packet.h" 40 int goby::acomms::benthos_fsm::BenthosATM900FSM::count_ = 0;
42 void goby::acomms::benthos_fsm::BenthosATM900FSM::buffer_data_out(
45 data_out_.push_back(msg);
48 void goby::acomms::benthos_fsm::Active::in_state_react(
const EvRxSerial& e)
50 std::string in = e.line;
53 static const std::string data =
"DATA";
54 static const std::string user =
"user:";
56 if (in.compare(0, data.size(), data) == 0)
59 post_event(EvReceive(in));
61 else if (in.compare(0, user.size(), user) == 0)
63 post_event(EvShellPrompt());
65 if (in.find(
"Lowpower") != std::string::npos)
66 post_event(EvAck(in));
70 post_event(EvAck(in));
72 static const std::string
connect =
"CONNECT";
73 if (in.compare(0, connect.size(), connect) == 0)
74 post_event(EvConnect());
78 goby::acomms::benthos_fsm::ReceiveData::ReceiveData(my_context ctx)
79 : my_base(ctx), StateNotify(
"ReceiveData"), reported_size_(0)
83 if (
const EvReceive* ev_rx = dynamic_cast<const EvReceive*>(triggering_event()))
85 std::string first = ev_rx->first_;
87 boost::erase_all(first,
" ");
95 if (first.size() < BYTES_START)
96 throw(std::runtime_error(
"String too short"));
98 std::string size_str = first.substr(SIZE_START, SIZE_END - SIZE_START);
99 boost::trim_left_if(size_str, boost::is_any_of(
"0"));
100 reported_size_ = boost::lexical_cast<
unsigned>(size_str);
101 encoded_bytes_ += goby::util::hex_decode(first.substr(BYTES_START));
105 throw(std::runtime_error(
"Invalid triggering_event, expected EvReceive"));
108 catch (std::exception& e)
111 goby::glog <<
"Invalid data received, ignoring: " << e.what() << std::endl;
112 post_event(EvReceiveComplete());
116 void goby::acomms::benthos_fsm::ReceiveData::in_state_react(
const EvRxSerial& e)
120 std::string in = e.line;
122 const std::string source =
"Source";
123 const std::string crc =
"CRC";
128 rx_msg_.GetExtension(benthos::protobuf::receive_stat);
130 if (rx_stats.crc() == benthos::protobuf::ReceiveStatistics::CRC_PASS)
131 parse_benthos_modem_message(encoded_bytes_, &rx_msg_);
133 context<BenthosATM900FSM>().received().push_back(rx_msg_);
135 post_event(EvReceiveComplete());
137 else if (encoded_bytes_.size() < reported_size_)
140 boost::erase_all(in,
" ");
141 encoded_bytes_ += goby::util::hex_decode(in);
143 else if (in.compare(0, source.size(), source) == 0)
146 std::vector<std::string> src_dest;
147 boost::split(src_dest, in, boost::is_any_of(
" "), boost::token_compress_on);
148 if (src_dest.size() != 2)
150 throw(std::runtime_error(
151 "Invalid source/dest string, expected \"Source:NNN Destination: MMM\""));
159 for (
int i = 0; i < 2; ++i)
161 std::string id_str = src_dest[i].substr(i == 0 ? SOURCE_ID_START : DEST_ID_START);
162 boost::trim_left_if(id_str, boost::is_any_of(
"0"));
168 int id = boost::lexical_cast<
unsigned>(id_str);
172 rx_msg_.set_dest(
id);
175 else if (in.compare(0, crc.size(), crc) == 0)
178 *rx_msg_.MutableExtension(benthos::protobuf::receive_stat);
181 std::vector<std::string> stats;
182 boost::split(stats, in, boost::is_any_of(
" "), boost::token_compress_on);
195 std::map<std::string, int> statmap;
196 statmap.insert(std::make_pair(
"CRC", (
int)STAT_CRC));
197 statmap.insert(std::make_pair(
"MPD", (
int)STAT_MPD));
198 statmap.insert(std::make_pair(
"SNR", (
int)STAT_SNR));
199 statmap.insert(std::make_pair(
"AGC", (
int)STAT_AGC));
200 statmap.insert(std::make_pair(
"SPD", (
int)STAT_SPD));
201 statmap.insert(std::make_pair(
"CCERR", (
int)STAT_CCERR));
203 for (
unsigned i = 0; i < stats.size(); ++i)
205 std::string::size_type col_pos = stats[i].find(
":");
206 if (col_pos == std::string::npos)
209 std::string key_str = stats[i].substr(0, col_pos);
211 std::map<std::string, int>::const_iterator it = statmap.find(key_str);
212 if (it == statmap.end())
215 StatField field =
static_cast<StatField
>(it->second);
216 std::string val_str = stats[i].substr(col_pos + 1);
217 boost::trim(val_str);
218 boost::trim_left_if(val_str, boost::is_any_of(
"+0"));
222 if (field != STAT_CRC)
223 val = boost::lexical_cast<
float>(val_str);
227 if (val_str ==
"Pass")
229 rx_stats.set_crc(benthos::protobuf::ReceiveStatistics::CRC_PASS);
233 rx_stats.set_crc(benthos::protobuf::ReceiveStatistics::CRC_FAIL);
237 rx_stats.set_multipath_delay_with_units(val * boost::units::si::milli *
238 boost::units::si::seconds);
240 case STAT_SNR: rx_stats.set_snr(val);
break;
241 case STAT_AGC: rx_stats.set_auto_gain_control(val);
break;
244 boost::units::metric::knot_base_unit::unit_type knots;
245 rx_stats.set_relative_speed_with_units(val * knots);
248 case STAT_CCERR: rx_stats.set_corrected_channel_error(val);
break;
253 catch (std::exception& e)
256 goby::glog <<
"Invalid data received, ignoring. Reason: " << e.what() << std::endl;
257 post_event(EvReceiveComplete());
261 void goby::acomms::benthos_fsm::Range::in_state_react(
const EvRxSerial& e)
265 std::string in = e.line;
268 static const std::string tx_time =
"TX time";
269 static const std::string range =
"Range";
271 if (!context<Command>().at_out().empty())
273 const std::string& last_cmd = context<Command>().at_out().front().second;
274 if (last_cmd.substr(0, 3) ==
"ATR" &&
275 boost::iequals(in.substr(0, tx_time.size()), tx_time))
277 context<Command>().at_out().pop_front();
281 if (in.compare(0, range.size(), range) == 0)
283 protobuf::ModemTransmission range_msg;
288 const std::string roundtrip =
"(Round-trip";
289 const std::string ms =
"ms)";
291 std::string::size_type range_pos = in.find(range);
292 std::string::size_type col_pos = in.find(
":");
293 std::string::size_type rt_start_pos = in.find(roundtrip);
294 std::string::size_type rt_end_pos = in.find(ms);
296 if (range_pos == std::string::npos || col_pos == std::string::npos ||
297 rt_start_pos == std::string::npos || rt_end_pos == std::string::npos)
298 throw(std::runtime_error(
"Invalid format for range string"));
301 std::string src_to_dest =
302 in.substr(range_pos + range.size(), col_pos - (range_pos + range.size()));
303 boost::trim(src_to_dest);
304 std::vector<std::string> src_dest;
305 boost::split(src_dest, src_to_dest, boost::is_any_of(
" to"), boost::token_compress_on);
306 if (src_dest.size() != 2)
308 throw(std::runtime_error(
"Invalid source/dest string, expected \"Range 1 to 2\""));
311 range_msg.set_src(boost::lexical_cast<unsigned>(src_dest[0]));
312 range_msg.set_dest(boost::lexical_cast<unsigned>(src_dest[1]));
314 range_msg.set_type(protobuf::ModemTransmission::DRIVER_SPECIFIC);
315 range_msg.SetExtension(benthos::protobuf::type,
316 benthos::protobuf::BENTHOS_TWO_WAY_PING);
318 std::string rt_ms = in.substr(rt_start_pos + roundtrip.size(),
319 rt_end_pos - (rt_start_pos + roundtrip.size()));
322 range_msg.MutableExtension(benthos::protobuf::ranging_reply)
323 ->set_one_way_travel_time_with_units(boost::lexical_cast<double>(rt_ms) / 2 *
324 boost::units::si::milli *
325 boost::units::si::seconds);
327 context<BenthosATM900FSM>().received().push_back(range_msg);
328 post_event(EvRangingComplete());
331 catch (std::exception& e)
334 goby::glog <<
"Invalid ranging data received, ignoring. Reason: " << e.what()
336 post_event(EvRangingComplete());
340 void goby::acomms::benthos_fsm::Command::in_state_react(
const EvTxSerial&)
342 double now = goby_time<double>();
344 static const std::string atd =
"ATD";
346 if (!at_out_.empty())
348 double timeout = COMMAND_TIMEOUT_SECONDS;
350 if ((at_out_.front().first.last_send_time_ + timeout) < now)
352 std::string at_command;
353 if (at_out_.front().second ==
"+++")
358 at_command = at_out_.front().second +
"\r";
362 at_command = at_out_.front().second +
"\r";
365 if (++at_out_.front().first.tries_ > RETRIES_BEFORE_RESET)
367 glog.is(DEBUG1) &&
glog << group(
"benthosatm900") << warn
368 <<
"No valid response after " << RETRIES_BEFORE_RESET
369 <<
" tries. Resetting state machine" << std::endl;
370 post_event(EvReset());
374 context<BenthosATM900FSM>().serial_tx_buffer().push_back(at_command);
375 at_out_.front().first.last_send_time_ = now;
381 void goby::acomms::benthos_fsm::TransmitData::in_state_react(
const EvTxSerial&)
383 boost::circular_buffer<protobuf::ModemTransmission>& data_out =
384 context<BenthosATM900FSM>().data_out();
385 if (!data_out.empty())
389 serialize_benthos_modem_message(&packet, data_out.front());
390 context<BenthosATM900FSM>().serial_tx_buffer().push_back(packet);
392 data_out.pop_front();
396 void goby::acomms::benthos_fsm::TransmitData::in_state_react(
const EvAck& e)
398 static const std::string forwarding_delay_up =
"Forwarding Delay Up";
399 if (e.response_.compare(0, forwarding_delay_up.size(), forwarding_delay_up) == 0)
401 post_event(EvTransmitBegun());
405 void goby::acomms::benthos_fsm::Command::in_state_react(
const EvAck& e)
408 if (!at_out().empty())
410 const std::string& last_cmd = at_out().front().second;
411 if (last_cmd.size() > 0 && (e.response_ ==
"OK"))
413 if (last_cmd ==
"ATH")
414 post_event(EvNoCarrier());
417 static const std::string connect =
"CONNECT";
418 static const std::string user =
"user";
420 if (e.response_ ==
"OK")
424 else if (e.response_.compare(0, connect.size(),
connect) == 0 &&
425 last_cmd.substr(0, 3) !=
"+++")
429 else if (e.response_ ==
"Command '+++' not found" &&
430 last_cmd.substr(0, 3) ==
435 else if (last_cmd.substr(0, 3) ==
"+++" &&
436 (e.response_.compare(0, user.size(), user) == 0))
441 else if (last_cmd.substr(0, 3) ==
"ATL" &&
442 e.response_.find(
"Lowpower") != std::string::npos)
444 post_event(EvLowPower());
447 else if (last_cmd.size() > 0)
449 std::string::size_type eq_pos = last_cmd.find(
'=');
450 static const std::string cfg_store =
"cfg store";
451 static const std::string date_cmd =
"date -t";
453 if (last_cmd[0] ==
'@' && eq_pos != std::string::npos &&
454 boost::iequals(last_cmd.substr(1, eq_pos - 1), e.response_.substr(0, eq_pos - 1)))
461 else if (e.response_ ==
"Configuration stored." &&
462 boost::iequals(last_cmd.substr(0, cfg_store.size()), cfg_store))
466 else if (boost::iequals(last_cmd.substr(0, date_cmd.size()), date_cmd) &&
467 e.response_.find(last_cmd.substr(date_cmd.size(), 8)) !=
476 glog.is(DEBUG2) &&
glog <<
"Popping: " << at_out().front().second << std::endl;
477 at_out().pop_front();
478 if (at_out().empty())
479 post_event(EvAtEmpty());
485 glog.is(DEBUG1) &&
glog << group(
"benthosatm900") <<
"Ignoring: '" << e.response_ <<
"'"
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.