Goby3
3.1.5a
2024.05.23
|
Transporters come in two flavors: either a Portal or a Forwarder:
Every Portal or Forwarder will have a inner Transporter (the type is passed as a template parameter, and the instantiation as a constructor argument). If the Transporter is on the innermost layer (interthread for multi-threaded applications, or interprocess for single-threaded applications), this inner Transporter is goby::middleware::NullTransporter, the do-nothing Transporter.
For example, a typical fully realized multi-threaded application might use the following hierarchy:
Generally the gobyd
is the process running the InterVehiclePortal
, but this isn't a hard requirement:
The full interface of a Transporter is composed from several classes (all defined in goby/middleware/transport/interface.h
) using compile-time polymorphism:
The subclass must implement several publish_dynamic and subscribe_dynamic methods that actually do the publishing and subscribing.
The typical publication requires four pieces of information (for details, see goby::middleware::StaticTransporterInterface::publish):
Additionally, a goby::middleware::Publisher can optionally be passed as well to provide more control over the publication or monitor its result. This is typically only used on the outer layers (e.g. intervehicle), and the default (an empty Publisher object) is generally sufficient for interthread and interprocess publications.
Generally the static methods should be preferred (goby::middleware::StaticTransporterInterface::publish) over the dynamic method (e.g. goby::middleware::InterThreadTransporter::publish_dynamic) as this forces use of compile-time (static) goby::middleware::Group instantations. This allows for static generation of the publish/subscribe graph, and hopefully additional static validation in the future. However, the various publish_dynamic calls are valuable when groups are truly runtime defined (such as for arbitrarily scalable applications). The tradeoff here is compile-time checking versus runtime flexibility. C++ in general leans heavily on the former (for good reason when created large real-world systems) so the goal of the Goby middleware is to transfer that design philosophy into the middleware itself.
The subscription requires nearly the same information as publication (see goby::middleware::StaticTransporterInterface::subscribe):
Additionally, a goby::middleware::Subscriber can optionally be passed as well to provide more control over the subscription. Like the goby::middleware::Publisher, this is typically only used on the outer layers (e.g. intervehicle).
The interthread layer is typically the innermost layer for multithreaded Goby3 applications, so there is no distinction between Portals and Forwarders. The implementation provided by Goby is the goby::middleware::InterThreadTransporter, which uses std::shared_ptr and STL containers to provide the publish/subscribe transport.
If the std::shared_ptr variants of goby::middleware::StaticTransporterInterface::publish are used, no copy of the data is made, making the InterThreadTransporter quite efficient. In this case, the publisher must not modify the data after publication, or else thread-safety is violated.
Since shared pointers are used (and no serialization/parsing is done), any data type (primitive types, STL containers, user classes, user structs, etc.) can be used for this layer.
The interprocess layer provides process to process communications, typically for processes on the same host or connected via a fast, reliable link (e.g. several computers in the same payload housing of a vehicle connected via wired Ethernet).
Goby3 provides a Forwarder implementation (goby::middleware::InterProcessForwarder), and a reference implementation of the Portal using the ZeroMQ message passing library (for details see the documentation on the Goby ZeroMQ library.)
Any data type that can be serialized to a message of bytes is suitable for use on the interprocess layer. For more details, see the marshalling documentation page.
The intervehicle layer is the most complicated because it is designed to allow support publish/subscribe messaging over an unreliable slow link (such as an acoustic modem) by taking advantage of the goby-acomms functionality.
The Forwarder implementation is goby::middleware::InterVehicleForwarder, which uses its inner transporter (typically one of the interprocess Transporters) to forward data to and from the Portal.
The Portal implementation is goby::middleware::InterVehiclePortal, which allows one or more physical (vehicle-to-vehicle) links to be specified for use. The gobyd
process runs an InterVehiclePortal, so this is a useful place to look for use on this class.
The InterVehiclePortal is configured using a goby::middleware::intervehicle::protobuf::PortalConfig Protobuf message. The contents of this message is described in the User Manual under the gobyd
section.
Each link on the InterVehiclePortal has an 15-bit modem id (roughly similar to an IP address) and a 15-bit subnet mask. The logical AND of the modem id and the subnet mask provides the given subnet. Each link should be on its own logical subnet. The 0 address on a given subnet is used as broadcast.
For example, using a subnet mask of 0xFFF0 provides subnets with 16 addresses each:
An instantiation of the goby::middleware::Publisher class can be passed to calls to InterVehicleForwarder and InterVehiclePortal publish for more information and control.
The Publisher can contain the following:
ack_required
is automatically set true in the DynamicBuffer configuration.On the intervehicle layer, subscriptions are published DCCL messages themselves (goby::middleware::intervehicle::protobuf::Subscription). A goby::middleware::InterVehiclePortal does not actually send any data "on the wire" unless a subscriber exists for a given publication.
This requires that the subscriber explicitly define the modem id(s) for one or more prospective publishers in the publisher_id
field of the goby::middleware::intervehicle::protobuf::TransporterConfig message. Each of these publishers will be sent a Subscription message, and if they are publishing (or begin publishing) the matching information, then these data will actually be sent "on the wire" (e.g. acoustically).
Given this, the configuration for goby::middleware::Subscriber looks similar to that of the goby::middleware::Publisher:
The goby::middleware::Group is a class that allows distinguishing different conceptual groupings of a particular data type. The scheme, type, and group fully define any publication or subscription.
A Group can have either a string or 8-bit numeric (uint8_t) representation, or both.
For interthread and interprocess use, the Group is defined by its string representation. For the intervehicle layer, the Group's numeric representation is used (which can be transmitted much more efficiently). Since all publications also publish to their inner layers, a Group used on intervehicle layer will need both a string and a numeric representation.
For example:
Group numbers 0 and 255 are reserved for broadcast
and invalid group
, respectively. The broadcast
group is essentially a wildcard, and is useful when the scheme/*type* pair alone is sufficient to distinguish publications.