### Create Host with Basic Context and Custom Handler in Rust Source: https://context7.com/roboplc/rpdo/llms.txt Illustrates the creation of an RPDO `Host` which binds a unique host ID to a shared context. This example also shows how to integrate a custom command handler for processing non-standard RPDO commands, returning a specific response. ```rust use rpdo::context::Basic; use rpdo::host::Host; use std::sync::Arc; // Create context and host let context = Basic::new(1000, 0, true); let host = Host::new(1, context.clone()); // Host with custom command handler struct MyHandler; impl rpdo::host::CustomCommandHandler for MyHandler { fn handle(&self, frame: &rpdo::comm::Frame, data: &[u8]) -> rpdo::Result>> { match frame.command { rpdo::comm::Command::Other(0x8000) => { println!("Custom command received: {:?}", data); Ok(Some(b"response".to_vec())) } _ => Err(rpdo::Error::InvalidCommand), } } } let host_with_handler = Host::new(1, context) .with_custom_command_handler(Arc::new(MyHandler)); ``` -------------------------------- ### Custom Command Handler Implementation in Rust Source: https://context7.com/roboplc/rpdo/llms.txt Shows how to implement the `CustomCommandHandler` trait in Rust to manage application-specific commands with codes starting from 0x8000. It includes defining custom commands, converting between `Command` and custom enums, and handling command logic. ```rust use rpdo::comm::{Command, Frame}; use rpdo::host::CustomCommandHandler; use rpdo::{Error, Result}; use std::sync::Arc; const COMMAND_POKE: u16 = 0x8000; const COMMAND_REVERSE: u16 = 0x8001; #[repr(u16)] enum CustomCommand { Poke = COMMAND_POKE, Reverse = COMMAND_REVERSE, } impl TryFrom for CustomCommand { type Error = Error; fn try_from(command: Command) -> Result { match command { Command::Other(COMMAND_POKE) => Ok(Self::Poke), Command::Other(COMMAND_REVERSE) => Ok(Self::Reverse), _ => Err(Error::InvalidCommand), } } } impl From for Command { fn from(cmd: CustomCommand) -> Self { Command::Other(cmd as u16) } } struct CommandHandler; impl CustomCommandHandler for CommandHandler { fn handle(&self, frame: &Frame, data: &[u8]) -> Result>> { let custom_command = CustomCommand::try_from(frame.command)?; match custom_command { CustomCommand::Poke => { let msg = std::str::from_utf8(data).map_err(Error::failed)?; println!("Poked with: {}", msg); Ok(None) // No reply } CustomCommand::Reverse => { let mut buf = data.to_vec(); buf.reverse(); Ok(Some(buf)) // Reply with reversed data } } } } // Usage with host let context = rpdo::context::Basic::new(100, 0, true); let host = rpdo::host::Host::new(1, context) .with_custom_command_handler(Arc::new(CommandHandler)); ``` -------------------------------- ### RPDO Context Operations with Locking Source: https://context7.com/roboplc/rpdo/llms.txt Illustrates the usage of RPDO's context operations, which remain identical regardless of the selected locking backend. The example shows thread-safe concurrent access to a context, demonstrating that the chosen locking strategy ensures predictable timing behavior without priority inversion issues in real-time applications. ```rust // The locking feature affects internal mutex implementation // API usage remains identical regardless of locking backend use rpdo::context::Basic; // Context operations are thread-safe with chosen locking strategy let context = Basic::new(100, 64, false); // Concurrent access is safe std::thread::scope(|s| { s.spawn(|| { context.set_bytes(0, 0, &[1, 2, 3, 4]).unwrap(); }); s.spawn(|| { let _ = context.get_bytes(0, 0, 4); }); }); ``` -------------------------------- ### Implement TCP Server with SimpleServerProcessor in Rust Source: https://context7.com/roboplc/rpdo/llms.txt Shows how to set up a TCP server using `SimpleServerProcessor` to handle incoming RPDO packets. This processor automatically manages standard and custom commands over a `Read + Write` stream, with configurable options for flushing and zero-copy. ```rust use rpdo::context::Basic; use rpdo::host::Host; use rpdo::io::SimpleServerProcessor; use std::net::TcpListener; use std::time::Duration; use std::thread; let context = Basic::new(1000, 0, true); let host = Host::new(1, context); let listener = TcpListener::bind("0.0.0.0:3003").unwrap(); for stream in listener.incoming() { let stream = stream.unwrap(); stream.set_nodelay(true).unwrap(); stream.set_read_timeout(Some(Duration::from_secs(5))).unwrap(); stream.set_write_timeout(Some(Duration::from_secs(5))).unwrap(); let mut processor = SimpleServerProcessor::new(host.clone(), stream) .with_always_flush(false) .with_zero_copy_after(32768); thread::spawn(move || { loop { if let Err(e) = processor.process_next() { eprintln!("Connection error: {:?}", e); break; } } }); } ``` -------------------------------- ### Create and Use Basic Shared Context in Rust Source: https://context7.com/roboplc/rpdo/llms.txt Demonstrates how to create a `Basic` shared context for register-based data storage. It shows how to initialize the context with a specified number of registers and size, and then perform read and write operations on both typed values and raw bytes. ```rust use rpdo::context::Basic; // Create a context with 1000 registers, initial size 0, flexible resizing enabled let context = Basic::new(1000, 0, true); // Write a u32 value to register 0 at offset 0 let counter: u32 = 42; context.set(0, 0, &counter).unwrap(); // Read back the value let read_back: u32 = context.get(0, 0, 4).unwrap(); assert_eq!(read_back, 42); // Write raw bytes directly context.set_bytes(1, 0, &[0x01, 0x02, 0x03, 0x04]).unwrap(); // Read raw bytes let bytes = context.get_bytes(1, 0, 4).unwrap(); assert_eq!(bytes, vec![0x01, 0x02, 0x03, 0x04]); ``` -------------------------------- ### Implement TCP Client with SimpleClient in Rust Source: https://context7.com/roboplc/rpdo/llms.txt Demonstrates how to use the `SimpleClient` to communicate with an RPDO server over TCP. It provides methods for pinging the server, reading and writing registers, and sending custom commands, including waiting for a response. ```rust use rpdo::io::SimpleClient; use rpdo::comm::Command; use std::net::TcpStream; use std::time::Duration; let stream = TcpStream::connect("127.0.0.1:3003").unwrap(); stream.set_nodelay(true).unwrap(); stream.set_read_timeout(Some(Duration::from_secs(5))).unwrap(); stream.set_write_timeout(Some(Duration::from_secs(5))).unwrap(); let mut client = SimpleClient::new(stream, 1) // target host ID = 1 .with_always_flush(false) .with_zero_copy_after(32768); // Ping the server client.ping().unwrap(); // Write a u32 value to register 0 let value: u32 = 12345; client.write_register(0, 0, &value.to_le_bytes()).unwrap(); // Read back from register 0 (4 bytes) let data = client.read_register(0, 0, 4).unwrap(); let read_value = u32::from_le_bytes(data.try_into().unwrap()); assert_eq!(read_value, 12345); // Send custom command (code >= 0x8000) let response = client.communicate( Command::Other(0x8000), b"custom data", true // wait for reply ).unwrap(); println!("Response: {:?}", response); ``` -------------------------------- ### UDP Communication with UdpStream in Rust Source: https://context7.com/roboplc/rpdo/llms.txt Demonstrates setting up server and client sides for UDP communication using RPDO's UdpStream. It covers creating streams, setting timeouts, processing packets, and performing basic read/write operations on registers. ```rust use rpdo::io::{UdpStream, SimpleClient, SimpleServerProcessor}; use rpdo::context::Basic; use rpdo::host::Host; use std::time::Duration; use std::thread; // Server side let context = Basic::new(1000, 0, true); let host = Host::new(1, context.clone()); thread::spawn(move || { let stream = UdpStream::create("0.0.0.0:3003") .unwrap() .with_timeouts(Duration::from_secs(10), Duration::from_secs(5)) .unwrap(); let mut processor = SimpleServerProcessor::new(host, stream); loop { if let Err(e) = processor.process_next() { eprintln!("Error: {:?}", e); break; } } }); thread::sleep(Duration::from_secs(1)); // Client side let mut stream = UdpStream::create("0.0.0.0:3004") .unwrap() .with_read_timeout(Duration::from_secs(5)) .unwrap() .with_write_timeout(Duration::from_secs(3)) .unwrap() .try_with_mtu(1500) .unwrap(); stream.set_peer("127.0.0.1:3003").unwrap(); let mut client = SimpleClient::new(stream, 1); client.ping().unwrap(); // Read/write registers over UDP client.write_register(0, 0, &42u32.to_le_bytes()).unwrap(); let data = client.read_register(0, 0, 4).unwrap(); ``` -------------------------------- ### RPDO Locking Features Configuration Source: https://context7.com/roboplc/rpdo/llms.txt Configures RPDO's locking strategies using Cargo features for different real-time requirements. This allows users to select appropriate synchronization primitives based on their application's needs, such as default (parking_lot), real-time spin-free, or priority-inheritance locking. ```toml # Cargo.toml - Default locking (parking_lot) [dependencies] rpdo = "0.2" # Real-time spin-free locking [dependencies] rpdo = { version = "0.2", default-features = false, features = ["locking-rt"] } # Priority-inheritance locking (Linux only, prevents priority inversion) [dependencies] rpdo = { version = "0.2", default-features = false, features = ["locking-rt-safe"] } ``` -------------------------------- ### RPDO Protocol Packet Structure in Rust Source: https://context7.com/roboplc/rpdo/llms.txt Details the binary protocol format used in RPDO communication, defining the `Packet`, `Frame`, and `RawDataHeader` structures. It illustrates packet creation, writing to a buffer, and reading from a buffer. ```rust use rpdo::comm::{Packet, Frame, Command, RawDataHeader, PacketHeader, VERSION}; use std::io::Cursor; // Packet header: "RD" magic + version (u8) + size (u32) = 7 bytes let header = PacketHeader::new(100); assert_eq!(PacketHeader::SIZE, 7); header.check_version().unwrap(); // Frame: source (u32) + target (u32) + id (u32) + in_reply_to (u32) + command (u16) = 19 bytes let frame = Frame { source: 0, // Client ID target: 1, // Host ID id: 42, // Frame ID in_reply_to: 0, // 0 if not a reply command: Command::ReadSharedContext, }; assert_eq!(Frame::SIZE, 19); // Create reply frame let reply = frame.to_reply(43, false); // id=43, error=false assert_eq!(reply.target, 0); // Reply to source assert_eq!(reply.in_reply_to, 42); // References original frame // RawDataHeader for read/write operations: register (u32) + offset (u32) + size (u32) = 12 bytes let data_header = RawDataHeader { register: 0, offset: 0, size: 4, }; assert_eq!(RawDataHeader::SIZE, 12); // Write packet to buffer let packet = Packet::new(frame, 12); // 12 bytes of data (RawDataHeader) let mut buffer = Vec::new(); packet.write_to(&mut Cursor::new(&mut buffer)).unwrap(); // Read packet from buffer let mut cursor = Cursor::new(&buffer); let read_packet = Packet::read_from(&mut cursor).unwrap(); assert_eq!(read_packet.data_len(), 12); ``` -------------------------------- ### RPDO Error Handling and Transmission Source: https://context7.com/roboplc/rpdo/llms.txt Demonstrates how to create, use, and transmit RPDO errors. Errors have numeric codes for protocol-level transmission and can be converted to bytes or parsed from received bytes. This functionality is crucial for robust communication in distributed systems. ```rust use rpdo::Error; use rpdo::error::{ERR_UNKNOWN_HOST, ERR_INVALID_REGISTER, ERR_INVALID_OFFSET}; // Create errors let err = Error::InvalidRegister; assert_eq!(err.code(), ERR_INVALID_REGISTER); let err = Error::failed("Custom error message"); assert_eq!(err.code(), 0x0000); // ERR_FAILED // Convert error to bytes for transmission let bytes: Vec = Error::InvalidOffset.into(); assert_eq!(bytes[0..2], ERR_INVALID_OFFSET.to_le_bytes()); // Parse error from received bytes let received: &[u8] = &[0x03, 0x00]; // ERR_INVALID_REGISTER let parsed_err = Error::from(received); match parsed_err { Error::InvalidRegister => println!("Register error"), _ => panic!("Unexpected error"), } // Error codes reference: // 0x0000 - ERR_FAILED (generic) // 0x0001 - ERR_UNKNOWN_HOST // 0x0002 - ERR_INVALID_COMMAND // 0x0003 - ERR_INVALID_REGISTER // 0x0004 - ERR_INVALID_OFFSET // 0x0005 - ERR_INVALID_REPLY // 0x0006 - ERR_OVERFLOW // 0x0007 - ERR_INVALID_VERSION // 0x0008 - ERR_IO // 0x0009 - ERR_INVALID_DATA // 0x0010 - ERR_PACKER ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.