# Day 2: Communications (Whiteboard) ## Understanding Nested Publish/Subscribe Three-layer: - interthread: within a process - interprocess: within a vehicle (ZeroMQ) - intervehicle: within a squadron or cluster of vehicles ### Interprocess - A single publisher - A single subscriber ```mermaid graph TB publisher-->subscriber ``` In Goby3 interprocess: ```mermaid graph TB publisher-->gobyd-->subscriber ``` ```mermaid graph TB publisher1-->subscriber1a & subscriber1b publisher2-->subscriber2 ``` less frequently: ```mermaid graph TB publisher1a & publisher1b --> subscriber1a & subscriber1b ``` All of these topologies are supported in Goby. ## Hands-on with one publisher / one subscriber in Goby3 ### Interprocess Copied the single thread pattern into two new binaries: goby3_course_interprocess_publisher/subscriber #### Qualifying a publication in Goby To publish in Goby: - *layer*: interprocess - *group*: similar to LCM *channel*, ROS *topic* or MOOS *variable* - marshalling *scheme*: (Protobuf) - data *type*. (Protobuf == Message ==> C++ Class : public google::protobuf::Message) We still to decide on only two things now: - *group* - *type* (or Message) GPSPosition -> `groups::gps1` and `groups::gps2`. GPSDiagnostics -> `groups::gps1` and `groups::gps2`. Publications on a particular layer are automatically published to all inner layers. #### Anatomy of a goby::middleware::Group Group - thought of as a "union" of a string/integer (uint8_t). - high bandwidth layers (interthread / interprocess): string + integer is used: "goby3_course::usv_nav;1" - low bandwidth (intervehicle): integer is used. Some examples: ```cpp using goby::middleware::Group; // valid for interprocess and interthread constexpr Group foo1{"foo"}; // valid for all three of the layers: "foo;2", 2 constexpr Group foo2{"foo", 2}; // valid for all three layers: "3", 3 constexpr Group bar{3}; // bar == bar2 on intervehicle but not on interprocess/interthread constexpr Group bar2{"bar", 3}; // 0 == "broadcast_group" ~= "no group" constexpr Group bar_groupless{"bar", Group::broadcast_group}; ``` Let's say we want send the vehicle's health. Created a group `health_status`. ### Create a Protobuf message Create a HealthStatus message. ### Add messages to Publisher code Send our health status at 1 Hz. ### Add subscriber Lambdas expressions. ```mermaid graph TB goby3_course_interprocess1_publisher -->|HealthStatus| goby3_course_interprocess1_subscriber ``` ### Interthread Convert the interprocess pair of applications into a single standalone process (application) with two threads. MultiThreadApplication - thread type is goby::middleware::Thread -> goby::middleware::SimpleThread (3-layer). -`loop()` method - `interthread()`, `interprocess()`, and `intervehicle()` - configuration may differ from the parent application ```mermaid graph TB subgraph goby3_course_interthread1 Publisher -->|HealthStatus| Subscriber end ``` ### Cross layer: Interthread & Interprocess ```mermaid graph TB subgraph goby3_course_interthread1 Publisher --> Subscriber end Publisher-->goby3_course_interprocess1_subscriber ``` ### Intervehicle "Slow links": <= 1 Mbps, often much much less: acoustic modems, satcomms, etc. ```mermaid graph TB subgraph intervehicle / Goby Acomms buffer[Dynamic Buffer] dccl[DCCL] amac1[AMAC, link 1] driver1[Modem Driver, link1] amac2[AMAC, link N] driver2[Modem Driver, link N] dccl-->buffer buffer-->driver1 driver1-->buffer amac1-->driver1 buffer-->driver2 driver2-->buffer amac2-->driver2 end ``` ### DCCL libdccl.org DCCL1 == XML definition DCCL2+ == Protobuf definition (DCCL3 is current version). Protobuf (vanilla): ```protobuf syntax="proto2"; message NavigationReport { required double x = 1; required double y = 2; required double z = 3; enum VehicleClass { AUV = 1; USV = 2; SHIP = 3; } optional VehicleClass veh_class = 4; optional bool battery_ok = 5; } ``` DCCL: ```protobuf syntax="proto2"; import "dccl/option_extensions.proto"; message NavigationReport { option (dccl.msg) = { codec_version: 3 id: 124 max_bytes: 32 } required double x = 1 [(dccl.field) = { min: -10000 max: 10000 precision: 1 }]; required double y = 2 [(dccl.field) = { min: -10000 max: 10000 precision: 1 }]; required double z = 3 [(dccl.field) = { min: -5000 max: 0 precision: 0 }]; enum VehicleClass { AUV = 1; USV = 2; SHIP = 3; } optional VehicleClass veh_class = 4; optional bool battery_ok = 5; } ``` #### Setting up gobyd's InterVehiclePortal Created some configuration directories. Pick a driver: UDPMulticastDriver: UDP multicast #### Subscription forwarding (Note text aligns with arrows beneath) ```mermaid sequenceDiagram publisher->>publisher: data1 publisher->>publisher: data2 subscriber->>publisher: subscription message publisher->>subscriber: data3 publisher->>subscriber: data4 ``` ```mermaid graph TB subgraph veh1 goby3_course_intervehicle1_publisher end subgraph veh2 goby3_course_intervehicle1_subscriber end goby3_course_intervehicle1_publisher-->goby3_course_intervehicle1_subscriber ``` #### Dynamic Buffer Goby3 buffer is a priority queue: - base priority value - time sensitive priority (ttl) Assume we have three messages type (1,2,3): - 1: base value 1 and ttl: 1000 seconds ("vehicle status") - 2: base value 2 and ttl: 2000 seconds ("sensor data") - 3: base value 1 and ttl: 3000 seconds ("debug engineering data" or "health status IF good or nominal") ![](https://i.imgur.com/BNouiJi.png) ```protobuf message DynamicBufferConfig { optional bool ack_required = 2 [default = false]; // lowest value takes precedence optional double blackout_time = 3 [ default = 0, (dccl.field) = {min: 0 max: 3600 precision: 0 units {base_dimensions: "T"}} ]; // larger value takes precedence optional uint32 max_queue = 4 [default = 1000, (dccl.field) = {min: 1 max: 1000}]; // true takes precedence over false optional bool newest_first = 5 [default = true]; // use average of values optional double ttl = 6 [ default = 1800, (dccl.field) = {min: 1 max: 86400 precision: 0 units {base_dimensions: "T"}} ]; // use average of values optional double value_base = 7 [default = 100, (dccl.field) = {min: 1 max: 1000 precision: 0}]; } ``` #### Modem Drivers There are 8 drivers in the open source project (Goby3): - WHOI Micro-Modem (v1, v2) - Iridium 9523 and similar (RUDICS/SBD) - Iridium RUDICS and DirectIP shore-side connections - Benthos ATM900 Modems - delResearch Popoto Modem - UDP Point-to-point - UDP Multicast (using now) - ABC Driver (demo for writing new drivers) https://github.com/GobySoft/goby3/pulls (If contributing your own driver) Please don't worry if it's not "finished". ## Returning to the Trail example N AUVs trailing 1 USV communicates to a topside (ship, shore station). ```mermaid graph TD auv0-->|"auv_nav;2"|usv auv1-->|"auv_nav;2"|usv auvN-->|"auv_nav;2"|usv usv-->|"usv_nav;1"|auv0 usv-->|"usv_nav;1"|auv1 usv-->|"usv_nav;1"|auvN usv==>|"usv_nav;1"|topside usv==>|"auv_nav;2"|topside ``` Groups: `auv_nav;2`, `usv_nav;1`. Message type: `Navigation Report` 1. goby3_course_auv_manager 1. goby3_course_usv_manager 1. goby3_course_topside_manager ```mermaid graph TD subgraph auvN auvm[goby3_course_auv_manager] end subgraph usv usvm[goby3_course_usv_manager] end subgraph topside topsidem[goby3_course_topside_manager] end auvm-->|"auv_nav;2"|usvm-->|"usv_nav;1"|topsidem usvm-->|"auv_nav;2"|topsidem usvm-->|"usv_nav;1"|auvm ``` --- ``` ```