23 #include <boost/algorithm/string/replace.hpp> 24 #include <boost/assign.hpp> 25 #include <boost/format.hpp> 26 #include <boost/math/special_functions/fpclassify.hpp> 28 #include "goby/common/logger.h" 29 #include "goby/util/as.h" 31 #include "dccl/binary.h" 52 tcp_(bf_config_.huxley_tcp_address(), bf_config_.huxley_tcp_port(),
"\r\n",
53 bf_config_.reconnect_interval()),
54 frontseat_providing_data_(false), last_frontseat_data_time_(0),
55 frontseat_state_(
gpb::FRONTSEAT_NOT_CONNECTED), next_connect_attempt_time_(0),
56 last_write_time_(0), waiting_for_huxley_(false), nmea_demerits_(0),
57 nmea_present_fail_count_(0), last_heartbeat_time_(0)
60 glog.is(VERBOSE) &&
glog <<
"Trying to connect to Huxley server @ " 61 << bf_config_.huxley_tcp_address() <<
":" 62 << bf_config_.huxley_tcp_port() << std::endl;
66 void BluefinFrontSeat::loop()
73 frontseat_state_ = gpb::FRONTSEAT_NOT_CONNECTED;
77 if (frontseat_state_ == gpb::FRONTSEAT_NOT_CONNECTED)
79 glog.is(VERBOSE) &&
glog <<
"Connected to Huxley." << std::endl;
80 frontseat_state_ = gpb::FRONTSEAT_IDLE;
84 check_send_heartbeat();
89 if (now > last_frontseat_data_time_ + bf_config_.allow_missing_nav_interval())
90 frontseat_providing_data_ =
false;
95 if (command.has_cancel_request_id())
97 for (std::map<goby::moos::protobuf::BluefinExtraCommands::BluefinCommand,
99 it = outstanding_requests_.begin(),
100 end = outstanding_requests_.end();
103 if (it->second.request_id() == command.cancel_request_id())
105 glog.is(DEBUG1) &&
glog <<
"Cancelled request: " << it->second.ShortDebugString()
107 outstanding_requests_.erase(it);
112 <<
"Failed to cancel request: " << command.cancel_request_id()
113 <<
", could not find such a request." << std::endl;
117 gpb::BluefinExtraCommands::BluefinCommand type = gpb::BluefinExtraCommands::UNKNOWN_COMMAND;
120 if (command.HasExtension(gpb::bluefin_command))
123 command.GetExtension(gpb::bluefin_command);
124 type = bluefin_command.command();
127 case gpb::BluefinExtraCommands::UNKNOWN_COMMAND:
128 case gpb::BluefinExtraCommands::DESIRED_COURSE:
break;
130 case gpb::BluefinExtraCommands::TRIM_ADJUST:
133 glog <<
"Bluefin Extra Command: Trim adjust requested by backseat." 137 append_to_write_queue(nmea);
141 case gpb::BluefinExtraCommands::BUOYANCY_ADJUST:
144 glog <<
"Bluefin Extra Command: Buoyancy adjustment requested by backseat." 148 append_to_write_queue(nmea);
152 case gpb::BluefinExtraCommands::SILENT_MODE:
154 glog.is(DEBUG1) &&
glog <<
"Bluefin Extra Command: Silent mode change requested by " 156 << gpb::BluefinExtraCommands::SilentMode_Name(
157 bluefin_command.silent_mode())
161 nmea.push_back(static_cast<int>(bluefin_command.silent_mode()));
162 append_to_write_queue(nmea);
166 case gpb::BluefinExtraCommands::CANCEL_CURRENT_BEHAVIOR:
169 glog <<
"Bluefin Extra Command: Cancel current behavior requested by backseat." 174 append_to_write_queue(nmea);
178 case gpb::BluefinExtraCommands::ABORT_MISSION:
181 glog <<
"Bluefin Extra Command: Abort mission requested by backseat; reason: " 182 << gpb::BluefinExtraCommands::AbortReason_Name(
183 bluefin_command.abort_reason())
187 nmea.push_back(
"backseat abort");
188 nmea.push_back(static_cast<int>(bluefin_command.abort_reason()));
189 append_to_write_queue(nmea);
193 case gpb::BluefinExtraCommands::MISSION_START_CONFIRM:
196 glog <<
"Bluefin Extra Command: Mission start confirmation by backseat " 201 append_to_write_queue(nmea);
205 case gpb::BluefinExtraCommands::MISSION_END_CONFIRM:
208 glog <<
"Bluefin Extra Command: Mission end confirmation by backseat " 213 append_to_write_queue(nmea);
219 if (command.has_desired_course())
221 if (type != gpb::BluefinExtraCommands::UNKNOWN_COMMAND &&
222 type != gpb::BluefinExtraCommands::DESIRED_COURSE)
224 glog.is(WARN) &&
glog <<
"Ignoring desired course information in this message, as an " 225 "extra command was set. Only one command allowed per message." 228 else if (static_cast<int>(command.desired_course().depth()) == 0 &&
229 static_cast<int>(command.desired_course().speed()) == 0)
231 type = gpb::BluefinExtraCommands::DESIRED_COURSE;
240 append_to_write_queue(nmea);
244 type = gpb::BluefinExtraCommands::DESIRED_COURSE;
250 if (desired_course.speed() < 0.01)
261 nmea.push_back(desired_course.heading());
262 nmea.push_back(desired_course.depth());
264 nmea.push_back(desired_course.speed());
269 append_to_write_queue(nmea);
273 if (!bf_config_.disable_ack() && command.response_requested())
275 if (outstanding_requests_.count(type))
277 glog.is(WARN) &&
glog <<
"Request already outstanding for type: " 278 << gpb::BluefinExtraCommands::BluefinCommand_Name(type)
279 <<
", overwriting old request." << std::endl;
282 outstanding_requests_[type] = command;
291 if (data.has_ctd_sample())
297 if (data.ctd_sample().has_conductivity())
298 nmea.push_back(goby::util::as<std::string>(data.ctd_sample().conductivity(), 10));
303 if (!(boost::math::isnan)(data.ctd_sample().temperature()))
304 nmea.push_back(goby::util::as<std::string>(data.ctd_sample().temperature(), 10));
309 if (!(boost::math::isnan)(data.ctd_sample().pressure()))
310 nmea.push_back(goby::util::as<std::string>(data.ctd_sample().pressure() / 1.0e3, 10));
314 nmea.push_back(unix_time2nmea_time(data.ctd_sample().time()));
316 append_to_write_queue(nmea);
319 if (data.has_dccl_message())
323 nmea.push_back(boost::trim_copy(dccl::b64_encode(data.dccl_message())));
324 append_to_write_queue(nmea);
327 if (data.HasExtension(gpb::bluefin_data))
331 if (bf_extra.has_micro_modem_raw_in() && bf_config_.send_tmr_messages())
335 const int TRANSPORT_ACOUSTIC_MODEM = 3;
336 nmea.push_back(TRANSPORT_ACOUSTIC_MODEM);
338 std::string modem_nmea = bf_extra.micro_modem_raw_in().raw();
339 boost::replace_all(modem_nmea,
",",
":");
340 boost::replace_all(modem_nmea,
"*",
"/");
341 boost::replace_all(modem_nmea,
"\r",
" ");
342 nmea.push_back(modem_nmea);
343 append_to_write_queue(nmea);
346 for (
int i = 0, n = bf_extra.payload_status_size(); i < n; ++i)
347 payload_status_.insert(std::make_pair(bf_extra.payload_status(i).expire_time(),
348 bf_extra.payload_status(i)));
357 append_to_write_queue(nmea);
361 glog.is(DEBUG1) &&
glog << warn <<
"Refusing to send this invalid message: " << data.raw()
362 <<
", " << e.what() << std::endl;
366 void BluefinFrontSeat::check_send_heartbeat()
369 if (now > last_heartbeat_time_ + bf_config_.heartbeat_interval())
374 const int FAILED = 0, ALL_OK = 1;
375 bool ok =
state() != gpb::INTERFACE_HELM_ERROR &&
state() != gpb::INTERFACE_FS_ERROR;
378 if (payload_status_.size())
380 std::map<int, std::string> seen_ids;
382 payload_status_.erase(
383 payload_status_.begin(),
384 payload_status_.upper_bound(goby::common::goby_time<goby::uint64>()));
389 it = payload_status_.begin(),
390 end = payload_status_.end();
394 if (!seen_ids.count(it->second.id()))
396 ok = ok && it->second.all_ok();
397 seen_ids[it->second.id()] = it->second.msg();
401 for (std::map<int, std::string>::const_iterator it = seen_ids.begin(),
402 end = seen_ids.end();
406 status += it->second;
413 nmea.push_back(ok ? ALL_OK : FAILED);
414 nmea.push_back(status);
415 append_to_write_queue(nmea);
417 last_heartbeat_time_ = now + bf_config_.heartbeat_interval();
421 void BluefinFrontSeat::try_receive()
424 while (tcp_.readline(&in))
431 process_receive(nmea);
433 catch (std::exception& e)
435 glog.is(DEBUG1) &&
glog << warn <<
"Failed to handle message: " << e.what()
441 void BluefinFrontSeat::initialize_huxley()
444 waiting_for_huxley_ =
false;
448 using boost::assign::operator+=;
449 std::vector<SentenceIDs> log_requests;
450 if (!bf_config_.disable_ack())
452 log_requests += NVG, MIS, MSC, NVR, SVS, RVL, SHT, TOP, MBS, MBE, CTD, DVL, BOY, TRM;
454 if (bf_config_.accepting_commands_hook() == BluefinFrontSeatConfig::BFCTL_TRIGGER)
459 nmea.push_back(
"ON");
460 for (std::vector<SentenceIDs>::const_iterator it = log_requests.begin(),
461 end = log_requests.end();
464 nmea[1] = sentence_id_map_.right.at(*it);
465 append_to_write_queue(nmea);
468 for (
int i = 0, n = bf_config_.extra_bplog_size(); i < n; ++i)
470 nmea[1] = boost::to_upper_copy(bf_config_.extra_bplog(i));
471 append_to_write_queue(nmea);
475 void BluefinFrontSeat::append_to_write_queue(
const NMEASentence& nmea)
477 out_.push_back(nmea);
481 void BluefinFrontSeat::try_send()
488 bool resend = waiting_for_huxley_ &&
489 (last_write_time_ <= (goby_time<double>() - bf_config_.nmea_resend_interval()));
491 if (!waiting_for_huxley_)
497 glog.is(DEBUG1) &&
glog <<
"resending last command; no NMEA ack in " 498 << bf_config_.nmea_resend_interval() <<
" second(s). " << std::endl;
504 ++nmea_present_fail_count_;
505 if (nmea_present_fail_count_ >= bf_config_.nmea_resend_attempts())
512 glog.is(DEBUG1) &&
glog <<
"Huxley did not respond to our command even after " 513 << bf_config_.nmea_resend_attempts()
514 <<
" retries. continuing onwards anyway..." << std::endl;
515 remove_from_write_queue();
518 if (nmea_demerits_ > bf_config_.allowed_nmea_demerits())
521 glog <<
"Huxley server is connected but appears to not be responding." 524 frontseat_state_ = gpb::FRONTSEAT_NOT_CONNECTED;
531 void BluefinFrontSeat::remove_from_write_queue()
533 waiting_for_huxley_ =
false;
539 glog.is(DEBUG1) &&
glog <<
"Expected to pop outgoing NMEA message but out_ deque is empty" 543 nmea_present_fail_count_ = 0;
549 raw_msg.set_raw(nmea.message());
550 raw_msg.set_description(description_map_[nmea.front()]);
552 signal_raw_to_frontseat(raw_msg);
554 tcp_.write(nmea.message_cr_nl());
556 if (bf_config_.disable_ack())
558 remove_from_write_queue();
562 waiting_for_huxley_ =
true;
567 void BluefinFrontSeat::process_receive(
const NMEASentence& nmea)
570 raw_msg.set_raw(nmea.message());
571 raw_msg.set_description(description_map_[nmea.front()]);
573 signal_raw_from_frontseat(raw_msg);
578 switch (sentence_id_map_.left.at(nmea.sentence_id()))
580 case ACK: bfack(nmea);
break;
582 case NVG: bfnvg(nmea);
break;
583 case NVR: bfnvr(nmea);
break;
584 case RVL: bfrvl(nmea);
break;
586 case DVL: bfdvl(nmea);
break;
587 case CTD: bfctd(nmea);
break;
588 case SVS: bfsvs(nmea);
break;
590 case MSC: bfmsc(nmea);
break;
591 case SHT: bfsht(nmea);
break;
593 case MBS: bfmbs(nmea);
break;
594 case MIS: bfmis(nmea);
break;
595 case MBE: bfmbe(nmea);
break;
597 case CTL: bfctl(nmea);
break;
599 case BOY: bfboy(nmea);
break;
600 case TRM: bftrm(nmea);
break;
602 case TOP: bftop(nmea);
break;
607 std::string BluefinFrontSeat::unix_time2nmea_time(
double time)
613 boost::format f(
"%02d%02d%02d.%03d");
614 f % ptime.time_of_day().hours() % ptime.time_of_day().minutes() %
615 ptime.time_of_day().seconds() %
616 (ptime.time_of_day().fractional_seconds() * 1000 /
617 boost::posix_time::time_duration::ticks_per_second());
622 void BluefinFrontSeat::load_nmea_mappings()
624 boost::assign::insert(sentence_id_map_)(
"MSC", MSC)(
"SHT", SHT)(
"BDL", BDL)(
"SDL", SDL)(
625 "TOP", TOP)(
"DVT", DVT)(
"VER", VER)(
"NVG", NVG)(
"SVS", SVS)(
"RCM", RCM)(
"RDP", RDP)(
626 "RVL", RVL)(
"RBS", RBS)(
"MBS", MBS)(
"MBE", MBE)(
"MIS", MIS)(
"ERC", ERC)(
"DVL", DVL)(
627 "DV2", DV2)(
"IMU", IMU)(
"CTD", CTD)(
"RNV", RNV)(
"PIT", PIT)(
"CNV", CNV)(
"PLN", PLN)(
628 "ACK", ACK)(
"TRM", TRM)(
"LOG", LOG)(
"STS", STS)(
"DVR", DVR)(
"CPS", CPS)(
"CPR", CPR)(
629 "TRK", TRK)(
"RTC", RTC)(
"RGP", RGP)(
"RCN", RCN)(
"RCA", RCA)(
"RCB", RCB)(
"RMB", RMB)(
630 "EMB", EMB)(
"TMR", TMR)(
"ABT", ABT)(
"KIL", KIL)(
"MSG", MSG)(
"RMP", RMP)(
"SEM", SEM)(
631 "NPU", NPU)(
"CPD", CPD)(
"SIL", SIL)(
"BOY", BOY)(
"SUS", SUS)(
"CON", CON)(
"RES", RES)(
632 "SPD", SPD)(
"SAN", SAN)(
"GHP", GHP)(
"GBP", GBP)(
"RNS", RNS)(
"RBO", RBO)(
"CMA", CMA)(
633 "NVR", NVR)(
"TEL", TEL)(
"CTL", CTL)(
"DCL", DCL);
635 boost::assign::insert(talker_id_map_)(
"BF", BF)(
"BP", BP);
637 boost::assign::insert(description_map_)(
"$BFMSC",
"Payload Mission Command")(
638 "$BFSHT",
"Payload Shutdown")(
"$BFBDL",
"Begin Data Logging")
640 (
"$BFSDL",
"Stop Data Logging")(
"$BFTOP",
"Topside Message (Not Implemented) ")(
641 "$BFDVT",
"Begin/End DVL External Triggering")(
"$BFVER",
"Vehicle Interface Version")(
642 "$BFNVG",
"Navigation Update")(
"$BFNVR",
"Velocity and Rate Update")(
643 "$BFTEL",
"Telemetry Status (Not Implemented)")(
"$BFSVS",
"Sound Velocity")(
644 "$BFRCM",
"Raw Compass Data")(
"$BFRDP",
"Raw Depth Sensor Data")(
"$BFRVL",
645 "Raw Vehicle Speed")(
646 "$BFRBS",
"Battery Voltage")(
"$BFMBS",
"Begin New Behavior")(
"$BFMBE",
"End Behavior")(
647 "$BFMIS",
"Mission Status")(
"$BFERC",
"Elevator and Rudder Data")(
648 "$BFDVL",
"Raw DVL Data")(
"$BFDV2",
"Raw DVL Data, Extended")(
"$BFIMU",
"Raw IMU Data")(
649 "$BFCTD",
"Raw CTD Sensor Data")(
"$BFRNV",
"Relative Navigation Position")(
650 "$BFPIT",
"Pitch Servo Positions")(
"$BFCNV",
"Cartesian Relative Navigation Position")(
651 "$BFPLN",
"Mission Plan Element")(
"$BFACK",
"Message Acknowledgement")(
652 "$BFTRM",
"Trim Status")(
"$BPSMC",
"Confirm Mission Start")(
653 "$BFBOY",
"Buoyancy Status")(
"$BPLOG",
"Logging Control")(
654 "$BPSTS",
"Payload Status Message")(
"$BPTOP",
"Request to Send Data Topside")(
655 "$BPDVR",
"Request to Change DVL Triggering Method")(
"$BPTRK",
656 "Request Additional Trackline")(
657 "$BPRTC",
"Request Additional Trackcircle")(
"$BPRGP",
"Request Additional GPS Hits")(
658 "$BPRCN",
"Cancel Requested Behavior")(
"$BPRCE",
"Cancel Current Mission Element")(
659 "$BPRCA",
"Cancel All Requested Behaviors")(
"$BPRCB",
"Cancel Current Behavior")(
660 "$BPRMB",
"Modify Current Behavior")(
"$BPEMB",
"End Behavior Modify")(
661 "$BPTMR",
"Topside Message Relay (Not Available on Most Vehicles)")(
662 "$BPCTD",
"Raw CTD Sensor Data")(
"$BPABT",
"Abort Mission")(
"$BPKIL",
"Kill Mission")(
663 "$BPMSG",
"Log Message")(
"$BPRMP",
"Request Mission Plan")(
664 "$BPSEM",
"Start Empty Mission (Not Implemented)")(
"$BPNPU",
665 "Navigation Position Update")(
666 "$BPSIL",
"Silent Mode")(
"$BPTRM",
"Request Trim Adjustment Behavior")(
667 "$BPBOY",
"Request Buoyancy Adjustment Behavior")(
"$BPVER",
668 "Payload Interface Version")(
669 "$BPSUS",
"Suspend Mission")(
"$BPCON",
"Continue")(
"$BPRES",
"Resume Mission")(
670 "$BPSPD",
"Hull Relative Speed Limit")(
"$BPSAN",
"Set Sonar Angle")(
671 "$BPGHP",
"Go To Hull Position")(
"$BPGBP",
"Go to Bottom Position")(
672 "$BPRNS",
"Reset Relative Navigation")(
"$BPRBO",
"Hull Relative Bearing Offset")(
673 "$BFCMA",
"Communications Medium Access")(
"$BFCPS",
"Communications Packet Sent")(
674 "$BFCPR",
"Communications Packet Received Data")(
"$BPCPD",
675 "Communications Packet Data")(
676 "$BFCTL",
"Backseat Control")(
"$BPDCL",
"Forward DCCL message to Huxley from Payload");
Contains functions for adding color to Terminal window streams.
std::string goby_time_as_string(const boost::posix_time::ptime &t=goby_time())
Simple string representation of goby_time()
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.
common::FlexOstream glog
Access the Goby logger through this object.
google::protobuf::uint64 uint64
an unsigned 64 bit integer
boost::posix_time::ptime unix_double2ptime(double given_time)
convert to boost date_time ptime from the number of seconds (including fractional) since 1/1/1970 0:0...