I’ve been playing around with an implementation here: https://github.com/davidlenfesty/uavcan.rs.
It’s starting to get to a place where I’m mostly happy with the API. Some kinks to work out with broadcasting messages, but I’ll be spending some time over my christmas break on this as well.
I took something close to the libcanard approach, where the message serialization and hardware drivers are completely ignored, you’re required to implement that in your own project, with the major difference that I’m splitting out the transport-specific implementation, and the session management.
The current (and probable) solution for abstracting over generic transports is a Transport
trait that I can simply declare to handle all of the transport specific operations, like processing an incoming frame into the internal representation. This will allow the user to specify the transport a node operates on simply by declaring it at instantiation, e.g. let node = Node<uavcan::transports::Can>::new();
There’s some room as well for an implementation of placing messages from one transport onto another, but there are a lot of questions to answer before I can even start to think of an interface.
Session management is handled similarly, with the major difference being that the trait to implement will be public to any user, meaning they can implement their own system to fit their memory model. One of my annoyances with the current libcanard implementation is that it’s a bit heavy for a small node where all subscriptions and input buffers could be statically allocated, and it’s extra effort to set up and less performant on something with an OS than if we just used native OS types, so I plan to implement three managers in the library, one pure-static implementation, one similar to libcanard’s model, and one based entirely on std
data structures that is flexible and easy to use. This specifically lets it serve both the no_std
and std
users quite well.
I can see wrapper libraries that automatically handle transmission/reception of messages being quite easy to implement, however those would be transport (and system) dependent so they should live outside of the main crate.
Again all the serialization would be handled externally to the main crate, and generating the structures at least would be done by nunavut. I’m currently leaning towards handling the serialization business logic with Rust macros, but that’s a discussion for much later.
The current state is:
- I’m only working on the simplest possible
std
implementation for now - it’s the easiest to debug, and also fits my most immediate use case.
- Full receive path with generic transports and session managers implemented (“functional” but not verified to be protocol-compliant)
- I need to figure out the API for transmitting messages, specifically how that fits into generic transports and
no_std
implementations.
- at one point I had a “functional” (but not verified) implementation: https://github.com/davidlenfesty/uavcan.rs/commit/7a1385a17bf1acd9c2f542c3e44745251c459cce
- lots and lots of general code cleanup to do
Hope this is at least interesting I am open to comments and suggestions.