24#ifndef GOBY_MIDDLEWARE_TRANSPORT_INTERVEHICLE_H
25#define GOBY_MIDDLEWARE_TRANSPORT_INTERVEHICLE_H
35#include <google/protobuf/io/zero_copy_stream_impl.h>
73template <
typename Derived,
typename InnerTransporter>
77 public Poller<InterVehicleTransporterBase<Derived, InnerTransporter>>
102 << request.ShortDebugString() << std::endl;
104 switch (request.request())
108 if (omit_publish_metadata_.erase(request.key().type()))
109 last_publish_metadata_time_.erase(request.key().type());
112 omit_publish_metadata_.insert(request.key().type());
122 template <
typename Data>
static constexpr int scheme()
124 static_assert(goby::middleware::scheme<typename detail::primitive_type<Data>::type>() ==
126 "Can only use DCCL messages with InterVehicleTransporters");
136 "goby::middleware::Group must have non-zero numeric "
137 "value to publish on the InterVehicle layer");
147 template <typename Data, int scheme = goby::middleware::scheme<Data>()>
152 "Can only use DCCL messages with InterVehicleTransporters");
154 Data data_with_group = data;
155 publisher.set_group(data_with_group,
group);
157 static_cast<Derived*
>(
this)->
template _publish<Data>(data_with_group,
group, publisher);
159 this->
inner().template publish_dynamic<Data, MarshallingScheme::DCCL>(data_with_group,
161 this->
inner().template publish_dynamic<Data, MarshallingScheme::PROTOBUF>(data_with_group,
172 template <typename Data, int scheme = goby::middleware::scheme<Data>()>
177 "Can only use DCCL messages with InterVehicleTransporters");
181 std::shared_ptr<Data> data_with_group(data->New());
182 data_with_group->CopyFrom(*data);
184 publisher.set_group(*data_with_group,
group);
186 static_cast<Derived*
>(
this)->
template _publish<Data>(*data_with_group,
group,
190 this->
inner().template publish_dynamic<Data, MarshallingScheme::DCCL>(data_with_group,
192 this->
inner().template publish_dynamic<Data, MarshallingScheme::PROTOBUF>(
193 data_with_group,
group, publisher);
204 template <typename Data, int scheme = goby::middleware::scheme<Data>()>
208 publish_dynamic<Data, scheme>(std::shared_ptr<const Data>(data),
group, publisher);
218 template <typename Data, int scheme = goby::middleware::scheme<Data>()>
223 "Can only use DCCL messages with InterVehicleTransporters");
224 auto pointer_ref_lambda = [=](std::shared_ptr<const Data> d) { f(*d); };
225 static_cast<Derived*
>(
this)->
template _subscribe<Data>(
236 template <typename Data, int scheme = goby::middleware::scheme<Data>()>
242 "Can only use DCCL messages with InterVehicleTransporters");
243 static_cast<Derived*
>(
this)->
template _subscribe<Data>(f,
group, subscriber,
253 template <typename Data, int scheme = goby::middleware::scheme<Data>()>
258 "Can only use DCCL messages with InterVehicleTransporters");
259 static_cast<Derived*
>(
this)->
template _subscribe<Data>(
260 std::function<
void(std::shared_ptr<const Data>)>(),
group, subscriber,
265 template <
typename Data>
266 std::shared_ptr<goby::middleware::protobuf::SerializerTransporterMessage>
271 std::stringstream ss;
272 ss <<
"Error: Publisher must have set_group_func in order to publish to a "
273 "non-broadcast Group ("
275 <<
"). The set_group_func modifies the contents of the outgoing message to store "
276 "the group information.";
284 auto ack_handler = std::make_shared<
288 auto expire_handler =
294 data, ack_handler, expire_handler);
297 const auto& type = data->key().type();
298 if (!omit_publish_metadata_.count(type))
301 auto interval = goby::time::convert_duration<goby::time::SteadyClock::duration>(
303 auto last_it = last_publish_metadata_time_.find(type);
304 if (last_it == last_publish_metadata_time_.end() || now >= last_it->second + interval)
306 _set_protobuf_metadata<Data>(data->mutable_key()->mutable_metadata(), d);
307 last_publish_metadata_time_[type] = now;
312 goby::glog <<
"Set up publishing for: " << data->ShortDebugString() << std::endl;
317 template <
typename Data>
318 std::shared_ptr<intervehicle::protobuf::Subscription>
330 std::stringstream ss;
331 ss <<
"Error: Subscriber must have group_func in order to subscribe to "
332 "non-broadcast Group ("
334 <<
"). The group_func returns the appropriate Group based on the contents "
335 "of the incoming message.";
342 std::stringstream ss;
343 ss <<
"Error: Broadcast subscriptions cannot have ack_required: true";
347 auto subscription = std::make_shared<
349 func,
group, subscriber);
363 std::stringstream ss;
364 ss <<
"Cannot unsubscribe to DCCL id: " << dccl_id
365 <<
" and group: " << std::string(
group) <<
" as no subscription was found.";
372 auto dccl_subscription =
373 this->
template _serialize_subscription<Data>(
group, subscriber, action);
381 auto subscribe_time = dccl_subscription->time_with_units();
382 subscription_publication->mutable_key()->set_serialize_time_with_units(subscribe_time);
388 auto expire_handler =
394 << subscription_publication->ShortDebugString()
397 this->pending_ack_.insert(std::make_pair(*subscription_publication,
398 std::make_tuple(ack_handler, expire_handler)));
400 return dccl_subscription;
403 template <
int tuple_index,
typename AckorExpirePair>
406 auto original = ack_or_expire_pair.serializer();
407 const auto& ack_or_expire_msg = ack_or_expire_pair.data();
409 original.key().type() ==
415 auto bytes_begin = original.data().begin(), bytes_end = original.data().end();
416 decltype(bytes_begin) actual_end;
420 auto subscription = Helper::parse(bytes_begin, bytes_end, actual_end);
421 subscription->mutable_header()->set_src(0);
423 std::vector<char> bytes(Helper::serialize(*subscription));
424 std::string* sbytes =
new std::string(bytes.begin(), bytes.end());
425 original.set_allocated_data(sbytes);
428 auto it = pending_ack_.find(original);
429 if (it != pending_ack_.end())
432 <<
" for: " << original.ShortDebugString() <<
", "
433 << ack_or_expire_msg.ShortDebugString()
436 std::get<tuple_index>(it->second)
437 ->post(original.data().begin(), original.data().end(), ack_or_expire_msg);
442 << (is_subscription ?
"subscription: " :
"data: ")
443 << original.ShortDebugString() << std::endl;
450 << packets.ShortDebugString() << std::endl;
452 for (
const auto& packet : packets.
frame())
455 p.second->post(packet.data().begin(), packet.data().end(), packets.
header());
459 template <
typename Data>
460 std::shared_ptr<intervehicle::protobuf::Subscription>
465 auto dccl_subscription = std::make_shared<intervehicle::protobuf::Subscription>();
466 dccl_subscription->mutable_header()->set_src(0);
469 dccl_subscription->mutable_header()->add_dest(
id);
472 dccl_subscription->set_dccl_id(dccl_id);
473 dccl_subscription->set_group(
group.numeric());
474 dccl_subscription->set_time_with_units(
475 goby::time::SystemClock::now<goby::time::MicroTime>());
480 _set_protobuf_metadata<Data>(dccl_subscription->mutable_metadata());
481 *dccl_subscription->mutable_intervehicle() = subscriber.
cfg().
intervehicle();
482 return dccl_subscription;
486 int dccl_id, std::shared_ptr<goby::middleware::protobuf::SerializerTransporterMessage> data,
492 << data->ShortDebugString() << std::endl;
494 this->pending_ack_.insert(
495 std::make_pair(*data, std::make_tuple(ack_handler, expire_handler)));
508 int _poll(std::unique_ptr<std::unique_lock<std::mutex>>& lock)
510 _expire_pending_ack();
512 return static_cast<Derived*
>(
this)->_poll(lock);
518 _insert_file_desc_with_dependencies(Data::descriptor()->file(), meta);
521 template <
typename Data>
522 void _set_protobuf_metadata(protobuf::SerializerProtobufMetadata* meta,
const Data& d)
524 meta->set_protobuf_name(
526 _insert_file_desc_with_dependencies(d.GetDescriptor()->file(), meta);
530 void _insert_file_desc_with_dependencies(
const google::protobuf::FileDescriptor* file_desc,
531 protobuf::SerializerProtobufMetadata* meta)
533 std::set<std::string> inserted;
534 _insert_file_desc_with_dependencies(file_desc, meta, inserted);
537 void _insert_file_desc_with_dependencies(
const google::protobuf::FileDescriptor* file_desc,
538 protobuf::SerializerProtobufMetadata* meta,
539 std::set<std::string>& inserted)
541 if (!inserted.insert(std::string(file_desc->name())).second)
544 for (
int i = 0, n = file_desc->dependency_count(); i < n; ++i)
545 _insert_file_desc_with_dependencies(file_desc->dependency(i), meta, inserted);
547 google::protobuf::FileDescriptorProto* file_desc_proto = meta->add_file_descriptor();
548 file_desc->CopyTo(file_desc_proto);
552 void _expire_pending_ack()
554 auto now = goby::time::SystemClock::now<goby::time::MicroTime>();
555 for (
auto it = pending_ack_.begin(), end = pending_ack_.end(); it != end;)
558 ->FindFieldByName(
"ttl")
560 .GetExtension(dccl::field)
564 decltype(now) serialize_time(it->first.key().serialize_time_with_units());
565 decltype(now) expire_time(serialize_time + max_ttl);
568 const decltype(now) interprocess_wait(1.0 * boost::units::si::seconds);
572 if (now > expire_time + interprocess_wait)
575 << it->first.ShortDebugString() << std::endl;
576 it = pending_ack_.erase(it);
590 protobuf::SerializerTransporterMessage,
591 std::tuple<std::shared_ptr<SerializationHandlerBase<intervehicle::protobuf::AckData>>,
592 std::shared_ptr<SerializationHandlerBase<intervehicle::protobuf::ExpireData>>>>
596 std::set<std::string> omit_publish_metadata_;
597 std::map<std::string, goby::time::SteadyClock::time_point> last_publish_metadata_time_;
604template <
typename InnerTransporter>
626 this->
inner().template subscribe<intervehicle::groups::modem_ack_in, ack_pair_type>(
627 [
this](
const ack_pair_type& ack_pair)
628 { this->
template _handle_ack_or_expire<0>(ack_pair); });
631 this->
inner().template subscribe<intervehicle::groups::modem_expire_in, expire_pair_type>(
632 [
this](
const expire_pair_type& expire_pair)
633 { this->
template _handle_ack_or_expire<1>(expire_pair); });
641 template <
typename Data>
644 this->
inner().template publish<intervehicle::groups::modem_data_out>(
648 template <
typename Data>
649 void _subscribe(std::function<
void(std::shared_ptr<const Data> d)> func,
const Group&
group,
660 catch (
const InvalidUnsubscription& e)
666 int _poll(std::unique_ptr<std::unique_lock<std::mutex>>& lock) {
return 0; }
672template <
typename InnerTransporter>
678 using implementation_tag =
typename InnerTransporter::implementation_tag;
680 implementation_tag>::modem_id_type;
703 for (
auto& modem_driver_data : modem_drivers_)
705 modem_driver_data->driver_thread_alive =
false;
706 if (modem_driver_data->underlying_thread)
707 modem_driver_data->underlying_thread->join();
714 template <
typename Data>
717 this->
innermost().template publish<intervehicle::groups::modem_data_out>(
721 template <
typename Data>
722 void _subscribe(std::function<
void(std::shared_ptr<const Data> d)> func,
const Group&
group,
729 this->
innermost().template publish<intervehicle::groups::modem_subscription_forward_tx>(
732 catch (
const InvalidUnsubscription& e)
738 int _poll(std::unique_ptr<std::unique_lock<std::mutex>>& lock)
742 while (!received_.empty())
745 received_.pop_front();
758 using intervehicle::protobuf::Subscription;
759 auto subscribe_lambda = [=](std::shared_ptr<const Subscription> d)
763 intervehicle::protobuf::Subscription,
766 auto subscription = std::make_shared<
767 IntervehicleSerializationSubscription<Subscription, MarshallingScheme::DCCL>>(
770 auto dccl_id = SerializerParserHelper<Subscription, MarshallingScheme::DCCL>::id();
772 std::make_pair(subscription->subscribed_group(), subscription));
775 this->
innermost().template subscribe<intervehicle::groups::modem_data_in>(
776 [
this](
const intervehicle::protobuf::DCCLForwardedData&
msg)
777 { received_.push_back(
msg); });
782 using ack_pair_type = intervehicle::protobuf::AckMessagePair;
783 this->
innermost().template subscribe<intervehicle::groups::modem_ack_in, ack_pair_type>(
784 [
this](
const ack_pair_type& ack_pair)
785 { this->
template _handle_ack_or_expire<0>(ack_pair); });
787 using expire_pair_type = intervehicle::protobuf::ExpireMessagePair;
789 .template subscribe<intervehicle::groups::modem_expire_in, expire_pair_type>(
790 [
this](
const expire_pair_type& expire_pair)
791 { this->
template _handle_ack_or_expire<1>(expire_pair); });
793 this->
innermost().template subscribe<intervehicle::groups::modem_driver_ready, bool>(
794 [
this](
const bool& ready)
802 _set_up_persistent_subscriptions();
804 for (
int i = 0, n = cfg_.
link_size(); i < n; ++i)
809 link->mutable_mac()->set_modem_id(link->modem_id());
811 modem_drivers_.emplace_back(
new ModemDriverData);
812 ModemDriverData& data = *modem_drivers_.back();
814 data.underlying_thread.reset(
new std::thread(
819 data.modem_driver_thread.reset(
820 new intervehicle::ModemDriverThread<implementation_tag>(*link));
821 data.modem_driver_thread->run(data.driver_thread_alive);
823 catch (std::exception& e)
826 goby::glog <<
"Modem driver thread had uncaught exception: " <<
e.what()
834 std::this_thread::sleep_for(std::chrono::milliseconds(250));
837 while (drivers_ready_ < modem_drivers_.size())
841 std::this_thread::sleep_for(std::chrono::seconds(1));
848 goby::glog <<
"Begin loading subscriptions from persistent storage..." << std::endl;
849 for (
const auto& sub : former_sub_collection_.subscription())
852 intervehicle::protobuf::Subscription,
853 MarshallingScheme::PROTOBUF>(sub);
857 void _set_up_persistent_subscriptions()
864 std::stringstream file_name;
866 if (dir.back() !=
'/')
870 persist_sub_file_name_ = file_name.str();
872 std::ifstream persist_sub_ifs(persist_sub_file_name_.c_str());
875 if (persist_sub_ifs.is_open())
877 google::protobuf::TextFormat::Parser parser;
878 google::protobuf::io::IstreamInputStream iis(&persist_sub_ifs);
879 parser.Parse(&iis, &former_sub_collection_);
884 goby::glog <<
"Could not open persistent subscriptions file: "
885 << persist_sub_file_name_
886 <<
". Assuming no persistent subscriptions exist" << std::endl;
889 catch (
const std::exception& e)
892 goby::glog <<
"Error reading persistent subscriptions file: " <<
e.what()
897 std::ofstream persist_sub_ofs(persist_sub_file_name_.c_str());
898 if (!persist_sub_ofs.is_open())
901 goby::glog <<
"Could not open persistent subscriptions file for writing: "
902 << persist_sub_file_name_ << std::endl;
904 remove(persist_sub_file_name_.c_str());
906 this->
innermost().template subscribe<intervehicle::groups::subscription_report>(
907 [
this](
const intervehicle::protobuf::SubscriptionReport& report)
910 <<
report.ShortDebugString() << std::endl;
912 std::ofstream persist_sub_ofs(persist_sub_file_name_.c_str());
913 intervehicle::protobuf::SubscriptionPersistCollection collection;
914 collection.set_time_with_units(
915 goby::time::SystemClock::now<goby::time::MicroTime>());
916 for (
auto report_p : sub_reports_)
918 for (
const auto& sub : report_p.second.subscription())
919 *collection.add_subscription() = sub;
921 google::protobuf::TextFormat::Printer printer;
922 google::protobuf::io::OstreamOutputStream oos(&persist_sub_ofs);
924 goby::glog <<
"Collection: " << collection.ShortDebugString() << std::endl;
925 printer.Print(collection, &oos);
930 intervehicle::protobuf::PortalConfig cfg_;
932 struct ModemDriverData
934 std::unique_ptr<std::thread> underlying_thread;
935 std::unique_ptr<intervehicle::ModemDriverThread<implementation_tag>> modem_driver_thread;
936 std::atomic<bool> driver_thread_alive{
true};
938 std::vector<std::unique_ptr<ModemDriverData>> modem_drivers_;
939 unsigned drivers_ready_{0};
941 std::deque<intervehicle::protobuf::DCCLForwardedData> received_;
943 intervehicle::protobuf::SubscriptionPersistCollection former_sub_collection_;
944 std::string persist_sub_file_name_;
945 std::map<modem_id_type, intervehicle::protobuf::SubscriptionReport> sub_reports_;
simple exception class for goby applications
void set_modem_id(int32_t value)
static const ::PROTOBUF_NAMESPACE_ID::Descriptor * descriptor()
boost::units::unit< ttl_dimension, boost::units::si::system > ttl_unit
bool ack_required() const
Class for grouping publications in the Goby middleware. Analogous to "topics" in ROS,...
static constexpr std::uint32_t broadcast_group
Special group number representing the broadcast group (used when no grouping is required for a given ...
static constexpr std::uint32_t invalid_numeric_group
Special group number representing an invalid numeric group (unsuitable for intervehicle and outer lay...
InnerTransporter & inner()
Implements the forwarder concept for the intervehicle layer.
virtual ~InterVehicleForwarder()=default
InterVehicleForwarder(InnerTransporter &inner)
Construct a forwarder for the intervehicle layer.
typename InnerTransporter::implementation_tag implementation_tag
Implements a portal for the intervehicle layer based on Goby Acomms.
InterVehiclePortal(InnerTransporter &inner, const intervehicle::protobuf::PortalConfig &cfg)
Instantiate a portal with the given configuration and a reference to an external inner transporter.
InterVehiclePortal(const intervehicle::protobuf::PortalConfig &cfg)
Instantiate a portal with the given configuration (with the portal owning the inner transporter)
Base class for implementing transporters (both portal and forwarder) for the intervehicle layer.
std::shared_ptr< intervehicle::protobuf::Subscription > _serialize_subscription(const Group &group, const Subscriber< Data > &subscriber, SubscriptionAction action)
static constexpr int scheme()
returns the marshalling scheme id for a given data type on this layer. Only MarshallingScheme::DCCL i...
void publish_dynamic(std::shared_ptr< Data > data, const Group &group=Group(), const Publisher< Data > &publisher=Publisher< Data >())
Publish a message using a run-time defined DynamicGroup (shared pointer to mutable data variant)....
void subscribe_dynamic(std::function< void(std::shared_ptr< const Data >)> f, const Group &group=Group(), const Subscriber< Data > &subscriber=Subscriber< Data >())
Subscribe to a specific run-time defined group and data type (shared pointer variant)....
void subscribe_dynamic(std::function< void(const Data &)> f, const Group &group=Group(), const Subscriber< Data > &subscriber=Subscriber< Data >())
Subscribe to a specific run-time defined group and data type (const reference variant)....
virtual ~InterVehicleTransporterBase()=default
void _handle_ack_or_expire(const AckorExpirePair &ack_or_expire_pair)
void publish_dynamic(std::shared_ptr< const Data > data, const Group &group=Group(), const Publisher< Data > &publisher=Publisher< Data >())
Publish a message using a run-time defined DynamicGroup (shared pointer to const data variant)....
InterVehicleTransporterBase(InnerTransporter &inner)
std::shared_ptr< intervehicle::protobuf::Subscription > _set_up_subscribe(std::function< void(std::shared_ptr< const Data > d)> func, const Group &group, const Subscriber< Data > &subscriber, SubscriptionAction action)
void _receive(const intervehicle::protobuf::DCCLForwardedData &packets)
void _insert_pending_ack(int dccl_id, std::shared_ptr< goby::middleware::protobuf::SerializerTransporterMessage > data, std::shared_ptr< SerializationHandlerBase< intervehicle::protobuf::AckData > > ack_handler, std::shared_ptr< SerializationHandlerBase< intervehicle::protobuf::ExpireData > > expire_handler)
void publish_dynamic(const Data &data, const Group &group=Group(), const Publisher< Data > &publisher=Publisher< Data >())
Publish a message using a run-time defined DynamicGroup (const reference variant)....
void unsubscribe_dynamic(const Group &group=Group(), const Subscriber< Data > &subscriber=Subscriber< Data >())
Unsubscribe from a specific run-time defined group and data type. Where possible, prefer the static v...
InterVehicleTransporterBase()
void check_validity()
Check validity of the Group for interthread use (at compile time)
std::unordered_map< int, std::unordered_map< std::string, std::shared_ptr< const SerializationHandlerBase< intervehicle::protobuf::Header > > > > subscriptions_
std::shared_ptr< goby::middleware::protobuf::SerializerTransporterMessage > _set_up_publish(const Data &d, const Group &group, const Publisher< Data > &publisher)
Represents a subscription to a serialized data type (intervehicle layer).
InvalidPublication(const std::string &e)
InvalidSubscription(const std::string &e)
InvalidUnsubscription(const std::string &e)
int poll(const std::chrono::time_point< Clock, Duration > &timeout=std::chrono::time_point< Clock, Duration >::max())
poll for data. Blocks until a data event occurs or a timeout when a particular time has been reached
Utility class for allowing the various Goby middleware transporters to poll the underlying transport ...
Represents a callback for a published data type (e.g. acked_func or expired_func)
Class that holds additional metadata and callback functions related to a publication (and is optional...
const goby::middleware::protobuf::TransporterConfig & cfg() const
Returns the metadata configuration.
acked_func_type acked_func() const
Returns the acked data callback (or an empty function if none is set)
bool has_set_group_func() const
expired_func_type expired_func() const
Returns the expired data callback (or an empty function if none is set)
Base class for handling posting callbacks for serialized data types (interprocess and outer)
Defines the common interface for publishing and subscribing data using static (constexpr) groups on G...
void subscribe(std::function< void(const Data &)> f, const Subscriber< Data > &subscriber=Subscriber< Data >())
Subscribe to a specific group and data type (const reference variant)
void publish(const Data &data, const Publisher< Data > &publisher=Publisher< Data >())
Publish a message (const reference variant)
Class that holds additional metadata and callback functions related to a subscription (and is optiona...
subscribed_func_type subscribed_func() const
bool has_group_func() const
const goby::middleware::protobuf::TransporterConfig & cfg() const
subscribe_expired_func_type subscribe_expired_func() const
Provides the modem driver thread used by InterVehiclePortal, templated on ImplementationTag so it use...
const ::goby::middleware::intervehicle::protobuf::Header & header() const
const ::goby::middleware::intervehicle::protobuf::DCCLPacket & frame(int index) const
::goby::acomms::protobuf::DriverConfig * mutable_driver()
const std::string & name() const
const std::string & dir() const
const ::goby::middleware::intervehicle::protobuf::PortalConfig_PersistSubscriptions & persist_subscriptions() const
bool has_persist_subscriptions() const
::goby::middleware::intervehicle::protobuf::PortalConfig_LinkConfig * mutable_link(int index)
int subscription_size() const
static constexpr Action SUBSCRIBE
static constexpr Action UNSUBSCRIBE
static const ::PROTOBUF_NAMESPACE_ID::Descriptor * descriptor()
Quantity metadata_interval_with_units() const
int32_t publisher_id(int index) const
const ::goby::acomms::protobuf::DynamicBufferConfig & buffer() const
const ::goby::middleware::intervehicle::protobuf::TransporterConfig & intervehicle() const
#define GOBY_INTERVEHICLE_API_VERSION
goby::util::logger::GroupSetter group(std::string n)
extern ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier< ::goby::acomms::protobuf::ModemReport, ::PROTOBUF_NAMESPACE_ID::internal::MessageTypeTraits< ::goby::acomms::iridium::protobuf::Report >, 11, false > report
constexpr Group subscription_forward
constexpr Group modem_subscription_forward_tx
constexpr Group metadata_request
constexpr Group modem_data_in
constexpr Group modem_subscription_forward_rx
std::shared_ptr< goby::middleware::protobuf::SerializerTransporterMessage > serialize_publication(const Data &d, const Group &group, const Publisher< Data > &publisher)
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.
Class for parsing and serializing a given marshalling scheme. Must be specialized for a particular sc...
static std::string type_name()
The marshalling scheme specific string name for this type.
static time_point now() noexcept
Returns the current steady time unless SimulatorSettings::using_sim_time == true in which case a simu...