### Example: Compiling Public Regulated Data Types Source: https://github.com/samcrow/canadensis/blob/master/canadensis_codegen_rust/README.md This example demonstrates how to compile the Cyphal public regulated data types repository into a Rust library file. Ensure you have cloned the repository first. ```bash canadensis_codegen_rust compile -o lib.rs public_regulated_data_types ``` -------------------------------- ### Monitor Cyphal Node with Yakut Source: https://github.com/samcrow/canadensis/blob/master/examples/s32k146_node/README.md Sets environment variables for Cyphal communication and starts monitoring the node using yakut. Ensure the CAN interface and MTU are configured correctly for your setup. ```shell export CYPHAL_PATH=~/.cyphal export UAVCAN__CAN__IFACE=socketcan:can0 export UAVCAN__CAN__MTU=8 export UAVCAN__NODE__ID=127 yakut monitor ``` -------------------------------- ### Configure UDP Transport for Canadensis CoreNode Source: https://context7.com/samcrow/canadensis/llms.txt Set up a UDP transport for Canadensis by replacing CAN-specific components with their UDP counterparts. This example demonstrates initializing CoreNode with UDP socket, transmitter, and receiver, and interacting with external tools like yakut. ```rust use canadensis::node::{BasicNode, CoreNode}; use canadensis::requester::TransferIdFixedMap; use canadensis_linux::SystemClock; use canadensis_udp::{ UdpNodeId, UdpReceiver, UdpSessionData, UdpTransferId, UdpTransmitter, UdpTransport, DEFAULT_PORT, }; use canadensis_udp::driver::StdUdpSocket; use canadensis_core::session::SessionDynamicMap; use core::net::Ipv4Addr; use std::convert::TryInto; let node_id: UdpNodeId = 42u16.try_into().unwrap(); const TRANSFER_IDS: usize = 1; const PUBLISHERS: usize = 8; const REQUESTERS: usize = 8; const MTU: usize = 1200; let socket = StdUdpSocket::bind(Ipv4Addr::UNSPECIFIED, DEFAULT_PORT).unwrap(); let transmitter = UdpTransmitter::::new(DEFAULT_PORT); let receiver = UdpReceiver::new(Some(node_id), Ipv4Addr::LOCALHOST); let core_node: CoreNode< SystemClock, UdpTransmitter, UdpReceiver, StdUdpSocket, MTU>, TransferIdFixedMap, StdUdpSocket, PUBLISHERS, REQUESTERS, > = CoreNode::new(SystemClock::new(), node_id, transmitter, receiver, socket); let mut node = BasicNode::new(core_node, node_info).unwrap(); // Interact with yakut: // UAVCAN__UDP__IFACE=127.0.0.1 UAVCAN__NODE__ID=127 yakut monitor ``` -------------------------------- ### Add ARM Cortex-M4F Target Source: https://github.com/samcrow/canadensis/blob/master/examples/s32k146_node/README.md Installs the necessary target for compiling Rust code for the ARM Cortex-M4F microcontroller. ```shell rustup target add thumbv7em-none-eabihf ``` -------------------------------- ### Node::start_sending_requests / Node::send_request Source: https://context7.com/samcrow/canadensis/llms.txt Enables sending service requests and receiving responses. Call `start_sending_requests` to get a `ServiceToken`, then use `send_request` for each request. Responses are handled via `TransferHandler::handle_response`. ```APIDOC ## `Node::start_sending_requests` / `Node::send_request` — Service client To send service requests and receive responses, call `start_sending_requests` to obtain a `ServiceToken`, then call `send_request` for each request. Responses are dispatched via `TransferHandler::handle_response`. ```rust use canadensis::Node; use canadensis_core::{Priority}; use canadensis_core::time::milliseconds; use canadensis_data_types::uavcan::register::list_1_0::{self, ListRequest, ListResponse}; use canadensis::encoding::Deserialize; // Register as a client for uavcan.register.List let list_token = node .start_sending_requests::( list_1_0::SERVICE, milliseconds(1000), // response timeout 256, // max response payload bytes Priority::Low, ) .unwrap(); // Send a request to node with ID 10 let _transfer_id = node .send_request(&list_token, &ListRequest { index: 0 }, target_node_id) .unwrap(); node.flush().unwrap(); // In TransferHandler::handle_response, deserialize the response: // if let Ok(resp) = ListResponse::deserialize_from_bytes(&transfer.payload) { // println!("Register name: {:?}", resp.name.name); // } ``` ``` -------------------------------- ### Send Service Requests with Node::send_request Source: https://context7.com/samcrow/canadensis/llms.txt Enables sending service requests and receiving responses. Call `start_sending_requests` to get a `ServiceToken`, then use `send_request` for each request. Responses are handled in `TransferHandler::handle_response`. Ensure the target node ID is correct. ```rust use canadensis::Node; use canadensis_core::{Priority}; use canadensis_core::time::milliseconds; use canadensis_data_types::uavcan::register::list_1_0::{self, ListRequest, ListResponse}; use canadensis::encoding::Deserialize; // Register as a client for uavcan.register.List let list_token = node .start_sending_requests::( list_1_0::SERVICE, milliseconds(1000), // response timeout 256, // max response payload bytes Priority::Low, ) .unwrap(); // Send a request to node with ID 10 let _transfer_id = node .send_request(&list_token, &ListRequest { index: 0 }, target_node_id) .unwrap(); node.flush().unwrap(); // In TransferHandler::handle_response, deserialize the response: // if let Ok(resp) = ListResponse::deserialize_from_bytes(&transfer.payload) { // println!("Register name: {:?}", resp.name.name); // } ``` -------------------------------- ### Publish HighColor Message to Control LED Source: https://github.com/samcrow/canadensis/blob/master/examples/s32k146_node/README.md Publishes a HighColor message to subject 5999 to set the on-board RGB LED color. This example sets the LED to magenta. ```shell export CYPHAL_PATH=~/.cyphal export UAVCAN__CAN__IFACE=socketcan:can0 export UAVCAN__CAN__MTU=8 export UAVCAN__NODE__ID=126 yakut publish 5999:reg.udral.physics.optics.HighColor.0.1 '{red: 31, green: 0, blue: 31}' ``` -------------------------------- ### Generate Rust Types from DSDL with canadensis_codegen_rust CLI Source: https://context7.com/samcrow/canadensis/llms.txt The canadensis_codegen_rust CLI tool compiles Cyphal DSDL files into a single Rust .rs file. It supports installing via cargo, compiling public regulated data types, showing dependencies, and compiling custom packages with external crate references. ```bash # Install carpenter install canadensis_codegen_rust # Compile the public regulated data types git clone https://github.com/OpenCyphal/public_regulated_data_types canadensis_codegen_rust compile --rustfmt \ -o src/generated.rs \ public_regulated_data_types # Show required Cargo.toml dependencies canadensis_codegen_rust print-dependencies # Compile a custom package that references the pre-built canadensis_data_types crate # (avoids recompiling the public regulated data types twice) canadensis_codegen_rust compile \ public_regulated_data_types \ my_custom_types \ --external-package uavcan,canadensis_data_types::uavcan \ --external-package reg,canadensis_data_types::reg \ --rustfmt \ -o src/my_types.rs # Mark a DSDL type as a Rust enum (add to the .dsdl file before the first field): # #[canadensis(enum)] # uint2 value # uint2 IDLE = 0 # uint2 RUNNING = 1 # @sealed ``` -------------------------------- ### Run Project with Cargo (probe-rs) Source: https://github.com/samcrow/canadensis/blob/master/examples/s32k146_node/README.md Compiles and flashes the project to the microcontroller using Cargo and probe-rs. ```shell cargo run --release ``` -------------------------------- ### Initialize BasicNode with GetInfoResponse Source: https://context7.com/samcrow/canadensis/llms.txt Initializes a BasicNode with node identity information. Ensure the core_node is built with sufficient publisher and requester slots. This node automatically handles GetInfo requests and periodically publishes port lists. ```rust use canadensis::node::{BasicNode, CoreNode}; use canadensis_data_types::uavcan::node::get_info_1_0::GetInfoResponse; use canadensis_data_types::uavcan::node::version_1_0::Version; // ... build core_node with PUBLISHERS >= 2, REQUESTERS >= 0 let node_info = GetInfoResponse { protocol_version: Version { major: 1, minor: 0 }, hardware_version: Version { major: 0, minor: 0 }, software_version: Version { major: 0, minor: 1 }, software_vcs_revision_id: 0, unique_id: rand::random(), name: heapless::Vec::from_slice(b"org.example.my_node").unwrap(), software_image_crc: heapless::Vec::new(), certificate_of_authenticity: Default::default(), }; let mut node = BasicNode::new(core_node, node_info).unwrap(); // In the main loop: loop { node.receive(&mut my_handler).unwrap_or_default(); // Call once per second: node.run_per_second_tasks().unwrap(); node.flush().unwrap(); } ``` -------------------------------- ### BasicNode::new Source: https://context7.com/samcrow/canadensis/llms.txt Initializes a BasicNode, which extends MinimalNode by adding support for uavcan.node.GetInfo service responses and periodic uavcan.node.port.List messages. It requires a GetInfoResponse describing the node's identity. ```APIDOC ## `BasicNode::new` — Full application-layer node `BasicNode` extends `MinimalNode` with `uavcan.node.GetInfo` service responses and periodic `uavcan.node.port.List` messages (every 10 s). It takes a `GetInfoResponse` that describes the node's identity and is returned to any requester. Uses two publisher slots and one server slot in the underlying node. ```rust use canadensis::node::{BasicNode, CoreNode}; use canadensis_data_types::uavcan::node::get_info_1_0::GetInfoResponse; use canadensis_data_types::uavcan::node::version_1_0::Version; // ... build core_node with PUBLISHERS >= 2, REQUESTERS >= 0 let node_info = GetInfoResponse { protocol_version: Version { major: 1, minor: 0 }, hardware_version: Version { major: 0, minor: 0 }, software_version: Version { major: 0, minor: 1 }, software_vcs_revision_id: 0, unique_id: rand::random(), name: heapless::Vec::from_slice(b"org.example.my_node").unwrap(), software_image_crc: heapless::Vec::new(), certificate_of_authenticity: Default::default(), }; let mut node = BasicNode::new(core_node, node_info).unwrap(); // In the main loop: loop { node.receive(&mut my_handler).unwrap_or_default(); // Call once per second: node.run_per_second_tasks().unwrap(); node.flush().unwrap(); } ``` ``` -------------------------------- ### MinimalNode::new Source: https://context7.com/samcrow/canadensis/llms.txt Wraps any existing Node implementation to automatically publish uavcan.node.Heartbeat.1.0 every second when run_per_second_tasks() is called. It requires one publisher slot and represents the minimum compliant Cyphal node. ```APIDOC ## `MinimalNode::new` — Heartbeat-sending node wrapper `MinimalNode` wraps any `Node` and automatically publishes `uavcan.node.Heartbeat.1.0` every second when `run_per_second_tasks()` is called. It consumes one publisher slot. This is the minimum compliant Cyphal node. ```rust use canadensis::node::{CoreNode, MinimalNode}; use canadensis::Node; // ... (build core_node as above, with PUBLISHERS >= 1) let mut node = MinimalNode::new(core_node).unwrap(); let start_time = std::time::Instant::now(); let mut prev_seconds = 0u64; loop { let seconds = std::time::Instant::now() .duration_since(start_time) .as_secs(); if seconds != prev_seconds { prev_seconds = seconds; // Increments uptime counter and publishes Heartbeat on uavcan.node.Heartbeat.1.0 node.run_per_second_tasks().unwrap(); node.node_mut().flush().unwrap(); } // node.set_mode(Mode { value: Mode::OPERATIONAL }); // node.set_health(Health { value: Health::NOMINAL }); } ``` ``` -------------------------------- ### Node::start_publishing / Node::publish Source: https://context7.com/samcrow/canadensis/llms.txt Allows any Node implementor to publish typed messages. First, register the subject ID and priority using `start_publishing`, then send messages with `publish`. The message type must implement `canadensis_encoding::Message + Serialize`. ```APIDOC ## `Node::start_publishing` / `Node::publish` — Topic publishing Any `Node` implementor can publish typed messages. First call `start_publishing` to register the subject ID and priority, then call `publish` each time a message should be sent. The message type must implement `canadensis_encoding::Message + Serialize`. ```rust use canadensis::Node; use canadensis_core::{Priority, SubjectId}; use canadensis_core::time::milliseconds; // Assuming a custom DSDL type MyMessage that implements Message + Serialize // and MY_SUBJECT is a SubjectId constant. node.start_publishing(MY_SUBJECT, milliseconds(500), Priority::Nominal.into()) .expect("Failed to register publisher"); let msg = MyMessage { value: 42 }; match node.publish(MY_SUBJECT, &msg) { Ok(()) => { /* queued */ } Err(canadensis::nb::Error::WouldBlock) => { /* retry next cycle */ } Err(canadensis::nb::Error::Other(e)) => panic!("publish error: {:?}", e), } node.flush().unwrap(); // Stop publishing when no longer needed: node.stop_publishing(MY_SUBJECT); ``` ``` -------------------------------- ### Compile Project with Cargo Source: https://github.com/samcrow/canadensis/blob/master/examples/s32k146_node/README.md Builds the project in release mode using Cargo, the Rust package manager. ```shell cargo build --release ``` -------------------------------- ### Print canadensis_codegen_rust Dependencies Source: https://github.com/samcrow/canadensis/blob/master/canadensis_codegen_rust/README.md Run this command to display the external library dependencies required by the generated Rust code. These should be added to your Cargo.toml file. ```bash canadensis_codegen_rust print-dependencies ``` -------------------------------- ### Create CoreNode for Cyphal Node Source: https://context7.com/samcrow/canadensis/llms.txt Initializes a CoreNode with specified clock, transport, and queue configurations. Ensure PUBLISHERS and REQUESTERS are powers of two. The CoreNode implements the main Node trait for publish/subscribe/request/respond operations. ```rust use canadensis::node::{CoreNode}; use canadensis::requester::TransferIdFixedMap; use canadensis_can::{CanNodeId, CanReceiver, CanTransmitter, CanTransport, Mtu}; use canadensis_can::queue::{ArrayQueue, SingleQueueDriver}; use canadensis_linux::{LinuxCan, SystemClock}; use socketcan::{CanSocket, Socket}; use std::convert::TryFrom; use std::time::Duration; let can = CanSocket::open("vcan0").unwrap(); can.set_read_timeout(Duration::from_millis(100)).unwrap(); can.set_write_timeout(Duration::from_millis(100)).unwrap(); let can = LinuxCan::new(can); let node_id = CanNodeId::try_from(42u8).unwrap(); let transmitter = CanTransmitter::new(Mtu::Can8); let receiver = CanReceiver::new(node_id); // Queue capacity in bytes, P = 8 publishers, R = 8 requesters const QUEUE_CAPACITY: usize = 1210; const TRANSFER_IDS: usize = 8; const PUBLISHERS: usize = 8; const REQUESTERS: usize = 8; type Queue = SingleQueueDriver, LinuxCan>; let queue: Queue = SingleQueueDriver::new(ArrayQueue::new(), can); let core_node: CoreNode< SystemClock, CanTransmitter, CanReceiver, TransferIdFixedMap, Queue, PUBLISHERS, REQUESTERS, > = CoreNode::new(SystemClock::new(), node_id, transmitter, receiver, queue); // core_node implements the Node trait and is ready to use directly or wrap in MinimalNode/BasicNode ``` -------------------------------- ### Automate Cyphal Node ID Allocation with PnpClientService Source: https://context7.com/samcrow/canadensis/llms.txt Use PnpClientService to broadcast allocation requests and automatically set the node ID upon receiving a response. This requires a unique 16-byte ID and a loop to periodically send requests and process responses. ```rust use canadensis::service::pnp::PnpClientService; use canadensis_data_types::uavcan::pnp::node_id_allocation_data_1_0::NodeIdAllocationData; let unique_id: [u8; 16] = rand::random(); let mut pnp = PnpClientService::<_, NodeIdAllocationData>::new(&mut node, unique_id).unwrap(); // Periodically send allocation requests until a node ID is assigned loop { pnp.send_request(&mut node).ok(); node.receive(&mut pnp.handler()).ok(); node.flush().ok(); if node.node_id().is_some() { println!("Allocated node ID: {:?}", node.node_id()); break; } std::thread::sleep(std::time::Duration::from_millis(500)); } ``` -------------------------------- ### MinimalNode for Heartbeat Publishing Source: https://context7.com/samcrow/canadensis/llms.txt Wraps a CoreNode to automatically publish uavcan.node.Heartbeat.1.0 every second when run_per_second_tasks() is called. Requires at least one publisher slot. This provides the minimum compliant Cyphal node functionality. ```rust use canadensis::node::{CoreNode, MinimalNode}; use canadensis::Node; // ... (build core_node as above, with PUBLISHERS >= 1) let mut node = MinimalNode::new(core_node).unwrap(); let start_time = std::time::Instant::now(); let mut prev_seconds = 0u64; loop { let seconds = std::time::Instant::now() .duration_since(start_time) .as_secs(); if seconds != prev_seconds { prev_seconds = seconds; // Increments uptime counter and publishes Heartbeat on uavcan.node.Heartbeat.1.0 node.run_per_second_tasks().unwrap(); node.node_mut().flush().unwrap(); } // node.set_mode(Mode { value: Mode::OPERATIONAL }); // node.set_health(Health { value: Health::NOMINAL }); } ``` -------------------------------- ### CoreNode::new Source: https://context7.com/samcrow/canadensis/llms.txt Creates a fully configured Cyphal node with specified clock, transmitter, receiver, and queue drivers. It implements the core Node trait for all publish/subscribe/request/respond operations. ```APIDOC ## `CoreNode::new` — Create a fully configured Cyphal node `CoreNode` is the low-level heart of the library. It owns a clock, a transport transmitter, a transport receiver, and a driver, and it implements the `Node` trait that provides all publish/subscribe/request/respond operations. The `const` generics `P` and `R` set the maximum number of concurrent publishers and requesters at compile time (must be powers of two). ```rust use canadensis::node::{CoreNode}; use canadensis::requester::TransferIdFixedMap; use canadensis_can::{CanNodeId, CanReceiver, CanTransmitter, CanTransport, Mtu}; use canadensis_can::queue::{ArrayQueue, SingleQueueDriver}; use canadensis_linux::{LinuxCan, SystemClock}; use socketcan::{CanSocket, Socket}; use std::convert::TryFrom; use std::time::Duration; let can = CanSocket::open("vcan0").unwrap(); can.set_read_timeout(Duration::from_millis(100)).unwrap(); can.set_write_timeout(Duration::from_millis(100)).unwrap(); let can = LinuxCan::new(can); let node_id = CanNodeId::try_from(42u8).unwrap(); let transmitter = CanTransmitter::new(Mtu::Can8); let receiver = CanReceiver::new(node_id); // Queue capacity in bytes, P = 8 publishers, R = 8 requesters const QUEUE_CAPACITY: usize = 1210; const TRANSFER_IDS: usize = 8; const PUBLISHERS: usize = 8; const REQUESTERS: usize = 8; type Queue = SingleQueueDriver, LinuxCan>; let queue: Queue = SingleQueueDriver::new(ArrayQueue::new(), can); let core_node: CoreNode< SystemClock, CanTransmitter, CanReceiver, TransferIdFixedMap, Queue, PUBLISHERS, REQUESTERS, > = CoreNode::new(SystemClock::new(), node_id, transmitter, receiver, queue); // core_node implements the Node trait and is ready to use directly or wrap in MinimalNode/BasicNode ``` ``` -------------------------------- ### Compile DSDL Package with canadensis_codegen_rust Source: https://github.com/samcrow/canadensis/blob/master/canadensis_codegen_rust/README.md Use this command to compile Cyphal DSDL files into Rust code. Specify an output file and one or more input directories containing DSDL files. The `--rustfmt` option can be added to format the generated code. ```bash canadensis_codegen_rust compile -o output-file input-directory.. ``` -------------------------------- ### Define and Handle Node Registers Source: https://context7.com/samcrow/canadensis/llms.txt Derive `RegisterBlock` on a struct of `SimpleRegister` fields to define registers. Create a `RegisterHandler` and call `subscribe_requests` once. Remote nodes can then list and read/write registers. ```rust use canadensis::register::basic::{SimpleRegister, RegisterString}; use canadensis::register::{RegisterBlock, RegisterHandler}; #[derive(RegisterBlock)] struct Registers { node_id: SimpleRegister, description: SimpleRegister, } let block = Registers { node_id: SimpleRegister::with_value("uavcan.node.id", /*mutable*/ true, /*persistent*/ false, 65535u16), description: SimpleRegister::new("uavcan.node.description", true, false), }; let mut registers = RegisterHandler::new(block); // Subscribe to register services (call once before the main loop) RegisterHandler::::subscribe_requests(&mut node).unwrap(); // Chain with your other handlers in the receive loop let mut handler = registers.chain(my_other_handler); loop { node.receive(&mut handler).ok(); node.run_per_second_tasks().ok(); node.flush().ok(); // Access register values directly: // handler.first().block().node_id.read() } // Yakut interaction example: // yakut call uavcan.register.List.1.0 "{ index: 0 }" // yakut call uavcan.register.Access.1.0 \ // "{ name: { name: \"uavcan.node.id\" }, value: { natural16: { value: [99] } } }" ``` -------------------------------- ### Compile with External Packages Source: https://github.com/samcrow/canadensis/blob/master/canadensis_codegen_rust/README.md Use the `--external-package` flag to refer to pre-generated types in other packages, avoiding duplicate compilation. This is useful when your project already depends on a package containing common DSDL types. ```bash canadensis_codegen_rust compile public_regulated_data_types depends_on_prdt --external-package uavcan,canadensis_data_types::uavcan --external-package reg,canadensis_data_types::reg -o lib.rs ``` -------------------------------- ### Implement TransferHandler for Incoming Messages Source: https://context7.com/samcrow/canadensis/llms.txt Implement `TransferHandler` to process messages, service requests, and responses. Return `true` to consume the transfer or `false` to pass it on. Use `.chain()` to compose multiple handlers. ```rust use canadensis::{Node, ResponseToken, TransferHandler}; use canadensis::core::transfer::{MessageTransfer, ServiceTransfer}; use canadensis::core::transport::Transport; use canadensis_can::CanTransport; struct MyHandler; impl TransferHandler for MyHandler { fn handle_message>( &mut self, _node: &mut N, transfer: &MessageTransfer, T>, ) -> bool { println!("Message on subject {:?}", transfer.header.subject); false // let other handlers also see this message } fn handle_request>( &mut self, node: &mut N, token: ResponseToken, transfer: &ServiceTransfer, T>, ) -> bool { // Echo the payload back as a response use canadensis_core::time::milliseconds; // let _ = node.send_response(token, milliseconds(1000), &MyResponse { .. }); true } fn handle_response>( &mut self, _node: &mut N, transfer: &ServiceTransfer, T>, ) -> bool { println!("Response from service {:?}", transfer.header.service); true } } // Chain two handlers: handler1 is tried first, then handler2 let mut chained = MyHandler.chain(another_handler); node.receive(&mut chained).unwrap(); ``` -------------------------------- ### Publish Typed Messages with Node::publish Source: https://context7.com/samcrow/canadensis/llms.txt Publishes typed messages to a registered subject ID. First, call `start_publishing` to register the subject and priority. Then, call `publish` to send messages. The message type must implement `Message + Serialize`. Use `stop_publishing` to unregister. ```rust use canadensis::Node; use canadensis_core::{Priority, SubjectId}; use canadensis_core::time::milliseconds; // Assuming a custom DSDL type MyMessage that implements Message + Serialize // and MY_SUBJECT is a SubjectId constant. node.start_publishing(MY_SUBJECT, milliseconds(500), Priority::Nominal.into()) .expect("Failed to register publisher"); let msg = MyMessage { value: 42 }; match node.publish(MY_SUBJECT, &msg) { Ok(()) => { /* queued */ } // Message queued for sending Err(canadensis::nb::Error::WouldBlock) => { /* retry next cycle */ } // Publisher is busy, retry later Err(canadensis::nb::Error::Other(e)) => panic!("publish error: {:?}", e), // Other error } node.flush().unwrap(); // Stop publishing when no longer needed: node.stop_publishing(MY_SUBJECT); ``` -------------------------------- ### Compile DSDL to Rust Code Source: https://github.com/samcrow/canadensis/blob/master/canadensis_data_types/README.md Use the canadensis codegen tool to compile DSDL files into Rust source code. The --rustfmt flag ensures the generated code is formatted according to Rust standards. Specify the output file with -o and the input DSDL directory. ```shell canadensis_codegen_rust compile --rustfmt -o src/generated.rs ../canadensis_dsdl_frontend/tests/public_regulated_data_types ``` -------------------------------- ### Inline DSDL Code Generation with types_from_dsdl! Macro Source: https://context7.com/samcrow/canadensis/llms.txt The types_from_dsdl! macro from canadensis_macro generates Rust types directly at compile time from DSDL definitions. It supports loading types from a directory, defining types inline, and marking packages as external to reuse existing crates. ```rust // In Cargo.toml: // [dependencies] // canadensis_macro = "0.6" // canadensis_encoding = "0.6" // canadensis_core = "0.6" // heapless = "0.9" // half = "2" use canadensis_macro::types_from_dsdl; // Load all types from a directory of DSDL files types_from_dsdl! { package("$CARGO_MANIFEST_DIR/dsdl/my_types") generate } // Or define types inline with their full DSDL key and content: types_from_dsdl! { "my_ns.SensorData.1.0" = " float32 temperature float32 pressure @sealed " generate } // Mark a package as external to reuse canadensis_data_types: types_from_dsdl! { make_external(uavcan, canadensis_data_types::uavcan) package("$CARGO_MANIFEST_DIR/dsdl/my_types") generate } ``` -------------------------------- ### DSDL Enum to Rust Enum Generation Source: https://github.com/samcrow/canadensis/blob/master/canadensis_codegen_rust/README.md Demonstrates the conversion of a DSDL enum definition to a Rust enum. The generated Rust code includes documentation attributes and derives. ```text #[canadensis(enum)] uint1 value uint1 BUTTERCREAM = 0 uint1 PASTRY_CREAM = 1 @sealed ``` ```rust /// `canadensis.Uint1Exhaustive.1.0` /// /// Fixed size 1 bytes /// #[cfg_attr(not(doctest), doc = "[canadensis(enum)]")] pub enum Uint1Exhaustive { Buttercream, PastryCream, } ``` -------------------------------- ### Publish Single-Frame Messages Anonymously Source: https://context7.com/samcrow/canadensis/llms.txt `AnonymousPublisher` allows sending single-frame messages before a node ID is assigned. It holds its own transfer ID counter and does not require a `Node`. Ensure the message fits in a single CAN frame. ```rust use canadensis::anonymous::AnonymousPublisher; use canadensis_core::{Priority, SubjectId}; use canadensis_core::time::milliseconds; // MyMessage must implement Message + Serialize and fit in one CAN frame let subject = SubjectId::try_from(7509u16).unwrap(); // e.g., PnP v1 allocation subject let mut publisher = AnonymousPublisher::>::new( subject, Priority::Nominal.into(), milliseconds(500), ); match publisher.send(&msg, &mut clock, &mut transmitter, &mut driver) { Ok(()) => { /* sent */ } Err(canadensis::nb::Error::Other( canadensis::anonymous::AnonymousPublishError::Length, )) => { panic!("message too large for single frame"); } Err(e) => { panic!("{:?}", e); } } ``` -------------------------------- ### Generated Rust Code for ContainsHealth Source: https://github.com/samcrow/canadensis/blob/master/canadensis_codegen_rust/README.md This Rust code is generated by canadensis_codegen_rust when compiling a DSDL type that depends on external packages. It defines the `ContainsHealth` struct and implements serialization/deserialization traits, referencing types from `canadensis_data_types`. ```rust #[cfg(not(target_endian = "little"))] compile_error!("Zero-copy serialization requires a little-endian target"); #[allow(unused_variables, unused_braces, unused_parens)] pub mod canadensis { pub mod test { pub mod contains_health_0_1 { /// `canadensis.test.ContainsHealth.0.1` /// /// Fixed size 2 bytes pub struct ContainsHealth { /// `uavcan.node.Health.1.0` /// /// Always aligned /// Size 8 bits pub health0: ::canadensis_data_types::uavcan::node::health_1_0::Health, /// `uavcan.node.Health.1.0` /// /// Always aligned /// Size 8 bits pub health1: ::canadensis_data_types::uavcan::node::health_1_0::Health, } impl ::canadensis_encoding::DataType for ContainsHealth { const EXTENT_BYTES: Option = None; } impl ::canadensis_encoding::Message for ContainsHealth {} impl ContainsHealth {} impl ::canadensis_encoding::Serialize for ContainsHealth { fn size_bits(&self) -> usize { 16 } fn serialize(&self, cursor: &mut ::canadensis_encoding::WriteCursor<'_>) { cursor.write_composite(&self.health0); cursor.write_composite(&self.health1); } } impl ::canadensis_encoding::Deserialize for ContainsHealth { fn deserialize( cursor: &mut ::canadensis_encoding::ReadCursor<'_>, ) -> ::core::result::Result where Self: Sized, { Ok(ContainsHealth { health0: { cursor.read_composite()? }, health1: { cursor.read_composite()? }, }) } } } } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.