.unit.yaml declaration, run the code generator, implement your handler, and run the Unit against the coordinator.
Complete this guide inside the Basis Docker environment or after sourcing
/opt/basis/bash/source_env.sh. See Environment Setup if you haven’t done that yet.What you’re building
You’ll create agreeter Unit with a single handler called OnPing. It subscribes to /ping (a TimeTest protobuf message), logs the received timestamp, and publishes a forwarded copy to /pong.
The TimeTest message is defined in the Basis example protobuf and has a single double time field — it’s a simple, self-contained type you can use immediately without writing any .proto files.
Project layout
Create a directory for your unit:Step-by-step
Write the unit YAML file
Create Breaking down the key fields:
greeter.unit.yaml in the ~/greeter directory:greeter.unit.yaml
threading_model: single— all handlers on this unit run mutually exclusive from each other. This is the only supported threading model today.cpp_includes— headers that the generated code needs to resolve the message types.basis_example.pb.hdefinesTimeTest.handlers.OnPing— the name of the handler function you will implement.sync.type: all— the handler fires once all required inputs have a message queued. For a single input this means “fire whenever a/pingmessage arrives”.inputs./ping— the topic name and its message type. Theprotobuf:prefix selects the protobuf serializer.::TimeTestis the fully-qualified C++ type name (the leading::puts it in the global namespace).outputs./pong— the topic and type of the value your handler must return.
Run code generation
The code generator reads your Arguments:After running, inspect what was generated:You should see:
.unit.yaml and produces a C++ base class, handler structs, and stub implementation files.greeter.unit.yaml— path to the unit definition filegenerated— output directory for generated base files.— source directory whereinclude/,src/, andtemplate/live
The generator only writes
include/greeter.h and src/greeter.cpp if they do not already exist. Subsequent runs update all generated files under generated/ but never overwrite your handler implementation.include/greeter.h— your Unit class declaration (edit this)src/greeter.cpp— your handler implementations (edit this)template/greeter.example.handtemplate/greeter.example.cpp— reference copies, regenerated each timegenerated/unit/greeter/unit_base.h,unit_base.cpp,create_unit.cpp— the generated base class (do not edit)
Review the generated header
Open The generated Topic names are converted to valid C++ identifiers:
include/greeter.h. It was generated once and is yours to edit:include/greeter.h
Base class handles all publisher and subscriber setup. Your job is to implement OnPing.The Input and Output structs are also generated, in generated/unit/greeter/handlers/OnPing.h:/ping becomes ping, /my_topic becomes my_topic.Implement the handler
Open A few things to note:
src/greeter.cpp. Replace the generated stub with a real implementation:src/greeter.cpp
input.pingis astd::shared_ptr<const TimeTest>. Access proto fields via->.- Return an
Outputstruct with each declared output field populated. Non-optional outputs that are leftnullptrwill produce an error log at runtime. - Zero-copy forwarding (returning the same shared pointer from input to output) is safe and efficient when using inproc transport.
Write a CMakeLists.txt
Create The
CMakeLists.txt in the ~/greeter directory:CMakeLists.txt
generate_unit CMake function (from cmake/Unit.cmake) wires up the code generator as a build step and creates two targets:| CMake target | Description |
|---|---|
unit_greeter | Shared library (greeter.unit.so) — loadable by the coordinator |
unit_greeter_bin | Standalone executable (greeter) — runs the unit directly |
DEPENDS lists any additional libraries your unit needs to link against. For protobuf messages you always need basis::plugins::serialization::protobuf. Add basis::plugins::serialization::rosmsg if you use ROS message types.Build the unit
Configure and build from the On success, the build outputs:
~/greeter directory:build/greeter.unit.so— the loadable unit shared librarybuild/greeter— the standalone unit executable
Start the coordinator and run the unit
Basis requires a coordinator process to be running before any Unit can connect. The coordinator manages topic routing and transport negotiation between Units.Open a first terminal and start the coordinator:Open a second terminal and run the unit:The standalone unit binary calls If the coordinator is not running,
WaitForCoordinatorConnection(), CreateTransportManager(), Initialize(), and then loops calling Update(). This is the same pattern used in basis_example.cpp:The generated
unit_greeter_bin target produces this main() automatically — you do not need to write it by hand when using generate_unit. The standalone binary is generated from create_unit.cpp.j2.WaitForCoordinatorConnection() retries every second and logs:What happens at runtime
When you callUpdate(), Basis:
- Drains any pending transport-level messages (connects new subscribers/publishers as reported by the coordinator).
- Pops queued subscriber callbacks from the
overall_queue. - For each queued callback, checks whether the synchronizer for the matching handler is satisfied.
- If satisfied, calls your handler function with the collected inputs.
- Publishes each non-null field from the returned
Outputstruct to the corresponding topic.
threading_model is single, all handlers are serialized through a single queue — no locks required in your handler code.
Complete example: the basis_example unit
For a richer reference, thebasis_example binary in the Basis repository demonstrates:
- A 1 Hz
RateSubscriberpublishingTimeTestmessages - A 10 Hz
RateSubscriberpublishingExampleStampedVectormessages - A topic subscriber (
OnTimeTest) that receives published messages and forwards them - Conditional compilation for ROS message types (
sensor_msgs::PointCloud2) - An approximate-timestamp synchronizer combining two topics
WaitForCoordinatorConnection → CreateTransportManager → Initialize → Update loop:
Next steps
Unit YAML schema
Full reference for all handler, sync, input, and output options
Code generation
Understand how generate_unit.py works and what files it produces
Synchronizers
Learn about
all, equal, approximate, and rate sync strategiesTransport
Inproc, TCP, and how to control which transports a topic uses