/ˈtrelis/
noun
- a frame or structure of latticework; lattice.
- a framework of this kind used as a support for growing vines or plants.
- a middleware framework for distributed applications using the actor model.
Trellis is primarily developed for use in robotics in an embedded Linux environment, however the project aims to be suited for general purpose.
Trellis is C++-only at the time of writing although additional language support is anticipated in the future.
Additionally, Trellis is only supported on Linux. At the moment there are no plans of supporting other platforms.
Trellis is based on the actor model, in which actors can only effect each other's state indirectly through the passing of messages.
See: https://en.wikipedia.org/wiki/Actor_model
Trellis applications are intended to be purely event-driven with callbacks firing in response to events. The two primary events being the passage of time (timers) and the reception of messages (subscribers)
Trellis uses Asio under the hood to run an event loop and perform asynchronous I/O.
- Dynamic service discovery allowing publishers, subscribers, RPC servers, and RPC clients to find and establish connections
- Inter-process pub/sub messaging patterns using Linux shared memory
- Protobuf-based messaging
- Inter-process remote procedure call (RPC) messaging patterns using protobuf RPC and local TCP sockets
- System introspection via a command-line tool called trellis-cli
- Data recording building blocks
- Configuration and parameter management framework
- Deterministic replay of messages
- An abstraction layer for common forms of I/O such as networking
- A framework for broadcasting reference frame transformations (useful for some robotics applications)
Trellis provides the following core primitives
- Node - for defining a node in the actor graph and proving APIs for constructing the other primitives
- Publisher - for sending messages on topics to interested subscribers
- Subscriber - for receiving messages on topics from relevant publishers
- Service Client - for initiating remote procedure calls
- Service Server - for exposing remotely callable procedures
- Timer - for queuing timed events
- MessageConsumer - for receiving messages from many publishers
- Inbox - another variant for receiving messages from many publishers
- Transforms - for caching reference frame transformations and broadcasting updates
Services are implemented using Protobuf's RPC syntax, which declares a method
name, input message type, and output message type. See examples
for more detail.
Each node runs a single event loop thread that dispatches received messages and timer events. Applications need not worry about explicit data synchronization by default as their callbacks while running concurrently will not run in parallel. This removes the burden on application developers to deal with thread-safety by default.
The RPC calls however occur on a separate background thread, and the calls are synchronous meaning a response message must be generated by the time the call ends. Some synchronization with the event loop may be necessary depending on the application.
Trellis can load some configuration parameters from YAML files. See trellis/examples/config.yml
.
Trellis is built on Google's Bazel build system.
Add to your WORKSPACE file:
TRELLIS_COMMIT = "XXXX"
http_archive(
name = "com_github_agtonomy_trellis",
strip_prefix = "trellis-" + TRELLIS_COMMIT,
url = "https://github.com/agtonomy/trellis/archive/" + TRELLIS_COMMIT + ".tar.gz",
# Make sure to add the correct sha256 corresponding to this commit.
# sha256 = "blah",
)
load("@com_github_agtonomy_trellis//third_party:repositories.bzl", "trellis_deps")
trellis_deps()
# Required transitive loader for protobuf dependencies.
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
protobuf_deps()
See examples
directory for some code examples for publishing, subscribing, calling
a service, and hosting a service.
You can use the provided Docker image, which has all the build/runtime dependencies
# Build the image (only needs to be done once)
./docker/build.sh
# Run the examples in the docker environment
./docker/shell.sh bazel run //trellis/examples/publisher
./docker/shell.sh bazel run //trellis/examples/subscriber
./docker/shell.sh bazel run //trellis/examples/service_server
./docker/shell.sh bazel run //trellis/examples/service_client
Alternatively, you can run ./docker/shell.sh
without any arguments to drop into a bash shell within the Docker environment.
You can simply run bazel natively, assuming all system dependencies are met. See the Dockerfile
to understand the system dependencies.
bazel run //trellis/examples/publisher
bazel run //trellis/examples/subscriber
bazel run //trellis/examples/service_server
bazel run //trellis/examples/service_client