MoosTranslation - GobySoft/goby GitHub Wiki
Preliminary docs on pAcommsHandler / pTranslator translation functionality
Since it's going to take me a bit to get the final Goby2 doc finished, I'm going to give you a preliminary start here how to use the moos<-->protobuf functionality built into the Goby2 moos applications. pTranslator and pAcommsHandler share the same code, so this applies to both. pTranslator can be thought of as a very minimal pAcommsHandler that simply immediately publishes all created messages. Note that pAcommsHandler requires DCCL extended protobuf messages, whereas pTranslator is fine working with vanilla protobuf messages.
Motivation: I needed a reasonably clean way to handle parsing the arbitrary (untyped) MOOS world to Google Protocol Buffers messages (protobuf) and the reverse (serialization). This replaces the <trigger>, <publish> and <layout> ... <moos_var> tags from Goby1. If you want to see what the autogenerated translator entries from Goby1 XML files are, you can run "dccl_xml_to_dccl_proto some_file.xml" and the relevant translator entries are written to stdout (along with writing the generated .proto to the current directory).
Schematically:
MOOS_VARs
|
v
pAcommsHandler1
| (translator: "trigger" & "create")
v
DCCL Protobuf Message
| (DCCL encode, enqueue, send over water)
| (receive over water, dequeue, DCCL decode)
v
pAcommsHandler2
|
v
DCCL Protobuf Message
| (translator: "publish")
v
MOOS_VARs
MOOS_VARs
|
v
pTranslator
| (translator: "trigger" & "create")
v
Protobuf Message
| (immediate loopback internally)
v
pTranslator
| (translator: "publish")
v
MOOS_VARs
Steps to use pTranslator/pAcommsHandler:
-
It's probably helpful to be looking at the output of "pTranslator -e" (example_config) which gives all the configurable options.
-
The protobuf message must be loaded. Preferably (because of static / compile-time checking) use
load_shared_library, e.g.load_shared_library: "/home/toby/lamss/lib/liblamss_protobuf.so"where liblamss_protobuf.so has some proto messages compiled in C++. Alternatively useload_proto-file:load_proto_file: "/home/toby/lamss/src/lib/liblamss_protobuf/node_report.proto"This compiles the proto file at runtime, so any syntax errors in the .proto file are not caught until then.
-
Define a
translator_entryfor each protobuf "message" you want to use.Each translator_entry has three parts:
-
exactly 1
triggerdirective: defines when this message is to be created (either synchronously on a configurable period or asynchronously on every publish to a given variable)These two examples should pretty self-explanatory: :
trigger { type: TRIGGER_TIME period: 5 # seconds}
trigger { # (optional)
type: TRIGGER_PUBLISH moos_var: "NODE_REPORT_LOCAL_PB"}
-
1 or more
createdirective(s): defines how to assemble the protobuf message. You can use multiple creates, which are all merged to create the final (single) protobuf message. For bothcreateandpublishyou currently have 5 parse/serialize techniques:-
TECHNIQUE_PROTOBUF_TEXT_FORMAT: exactly the same as if you used the Google TextFormat class: https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.text_format.
-
TECHNIQUE_PROTOBUF_NATIVE_ENCODED: exactly the same as if you used the default binary Google encoding (binary), represented as a byte string. This tends to break the MOOS tools that assume strings are ASCII / UTF-8. I'm not even sure MOOS can handle transporting this correctly.
-
TECHNIQUE_COMMA_SEPARATED_KEY_EQUALS_VALUE_PAIRS: all fields represented as key1=value1,key2=value2,... Messages with submessages flattened and the keys assembled by concatenation separated with "_". This is similar to the existing "NODE_REPORT" variable.
-
TECHNIQUE_FORMAT: sort of like printf / scanf, except instead of typed directives (e.g. %d), I use numeric directives that correspond the protobuf message field id (e.g. foobar=%2%). Submessages can be referenced using ":" (e.g. %5:1%, where field 5 is a Message), repeated fields can be referenced using "." (e.g. %7.1%, where field 7 is repeated). Note the ending % on each directive, which is different than printf.
-
TECHNIQUE_PREFIXED_PROTOBUF_TEXT_FORMAT (my recommendation for MOOS). Same as TECHNIQUE_PROTOBUF_TEXT_FORMAT but prefixed with "@PB[TypeName] ", so that you can put multiple Protobuf Types in a single MOOS Variable (if you really need to). It's also quite human readable and allows for programs to read / write generic Protobuf messages.
This technique is useful enough, I made two shortcut functions for use in your C++ code (#include "goby/moos/moos_protobuf_helpers.h"): :
// writes `msg` to `out` using TECHNIQUE_PREFIXED_PROTOBUF_TEXT_FORMAT void serialize_for_moos(std::string* out, const google::protobuf::Message& msg) // writes `in` to `msg` using TECHNIQUE_PREFIXED_PROTOBUF_TEXT_FORMAT void parse_for_moos(const std::string& in, google::protobuf::Message* msg)
-
-
1 or more
publishdirective(s): defines how to disassemble the protobuf messages. Same ascreatebut the other way around. Note that on publish, the final value is attempted to be cast to double. If this succeeds a MOOS double type is published, else a MOOS string type is published.
-
A translator_entry needs to be defined for each protobuf message you want to use. Multiple translator_entries can be created for a given Protobuf Message if they are all TRIGGER_PUBLISH.
All this verbiage is useless without an example. See test.moos which uses node_report.proto (change library paths and then "pAntler test.moos").
- pTranslator_MOOS2PB converts a bunch of NAV* MOOS junk into a single TECHNIQUE_PREFIXED_PROTOBUF_TEXT_FORMAT encoded message and writes it to NODE_REPORT_LOCAL_PB. - pTranslator_PB2MOOS does the opposite, taking the nice NODE_REPORT_LOCAL_PB and writing VEHNAME_NAV_X and VEHNAME_NAV_Y (shows use of TECHNIQUE_FORMAT and how it works in
moos_vartoo).