### PLC Example with rmodbus Server Context Source: https://github.com/alttch/rmodbus/blob/master/README.md Demonstrates a simple PLC-like application using the rmodbus server context. It shows how to read parameters, save/load context to a file, and write results. This example requires external files like 'servers/tcp.rs' and uses bincode for serialization. ```rust use std::error::Error; use std::fs::File; use std::io::{Read, Write}; use rmodbus::server::{storage::ModbusStorageFull, context::ModbusContext}; #[path = "../examples/servers/tcp.rs"] mod srv; // put 1 to holding register 1500 to save current context to /tmp/plc1.dat // if the file exists, context will be loaded at the next start fn looping() { println!("Loop started"); loop { // READ WORK MODES ETC let ctx = srv::CONTEXT.read().unwrap(); let _param1 = ctx.get_holding(1000).unwrap(); let _param2 = ctx.get_holdings_as_f32(1100).unwrap(); // ieee754 f32 let _param3 = ctx.get_holdings_as_u32(1200).unwrap(); // u32 let cmd = ctx.get_holding(1500).unwrap(); drop(ctx); if cmd != 0 { println!("got command code {}", cmd); let mut ctx = srv::CONTEXT.write().unwrap(); ctx.set_holding(1500, 0).unwrap(); match cmd { 1 => { println!("saving memory context"); let _ = save("/tmp/plc1.dat", &ctx).map_err(|_| { eprintln!("unable to save context!"); }); } _ => println!("command not implemented"), } } // ============================================== // DO SOME JOB // ............ // WRITE RESULTS let mut ctx = srv::CONTEXT.write().unwrap(); ctx.set_coil(0, true).unwrap(); ctx.set_holdings_bulk(10, &(vec![10, 20])).unwrap(); ctx.set_inputs_from_f32(20, 935.77).unwrap(); } } fn save(fname: &str, ctx: &ModbusStorageFull) -> Result<(), Box> { let config = bincode::config::standard(); let mut file = File::create(fname)?; file.write(&bincode::encode_to_vec(ctx, config)?)?; file.sync_all()?; Ok(()) } fn load(fname: &str, ctx: &mut ModbusStorageFull) -> Result<(), Box> { let config = bincode::config::standard(); let mut file = File::open(fname)?; let mut data: Vec = Vec::new(); file.read_to_end(&mut data)?; (*ctx, _) = bincode::decode_from_slice(&data, config)?; Ok(()) } fn main() { // read context let unit_id = 1; { let mut ctx = srv::CONTEXT.write().unwrap(); let _ = load(&"/tmp/plc1.dat", &mut ctx).map_err(|_| { eprintln!("warning: no saved context"); }); } use std::thread; thread::spawn(move || { srv::tcpserver(unit_id, "localhost:5502"); }); looping(); } ``` -------------------------------- ### Modbus TCP Client Example in Rust Source: https://github.com/alttch/rmodbus/blob/master/README.md This snippet demonstrates how to implement a Modbus TCP client using the rmodbus crate in Rust. It covers establishing a TCP connection, setting timeouts, generating and sending Modbus requests (setting coils and getting coils), reading and parsing responses, and handling potential errors. Dependencies include `std::io`, `std::net`, `std::time`, and rmodbus components like `ModbusRequest`, `guess_response_frame_len`, and `ModbusProto`. ```rust use std::io::{Read, Write}; use std::net::TcpStream; use std::time::Duration; use rmodbus::{client::ModbusRequest, guess_response_frame_len, ModbusProto}; let timeout = Duration::from_secs(1); // open TCP connection let mut stream = TcpStream::connect("localhost:5502").unwrap(); stream.set_read_timeout(Some(timeout)).unwrap(); stream.set_write_timeout(Some(timeout)).unwrap(); // create request object let mut mreq = ModbusRequest::new(1, ModbusProto::TcpUdp); mreq.tr_id = 2; // just for test, default tr_id is 1 // set 2 coils let mut request = Vec::new(); mreq.generate_set_coils_bulk(0, &[true, true], &mut request) .unwrap(); // write request to stream stream.write(&request).unwrap(); // read first 6 bytes of response frame let mut buf = [0u8; 6]; stream.read_exact(&mut buf).unwrap(); let mut response = Vec::new(); response.extend_from_slice(&buf); let len = guess_response_frame_len(&buf, ModbusProto::TcpUdp).unwrap(); // read rest of response frame if len > 6 { let mut rest = vec![0u8; (len - 6) as usize]; stream.read_exact(&mut rest).unwrap(); response.extend(rest); } // check if frame has no Modbus error inside mreq.parse_ok(&response).unwrap(); // get coil values back mreq.generate_get_coils(0, 2, &mut request).unwrap(); stream.write(&request).unwrap(); let mut buf = [0u8; 6]; stream.read_exact(&mut buf).unwrap(); let mut response = Vec::new(); response.extend_from_slice(&buf); let len = guess_response_frame_len(&buf, ModbusProto::TcpUdp).unwrap(); if len > 6 { let mut rest = vec![0u8; (len - 6) as usize]; stream.read_exact(&mut rest).unwrap(); response.extend(rest); } let mut data = Vec::new(); // check if frame has no Modbus error inside and parse response bools into data vec mreq.parse_bool(&response, &mut data).unwrap(); for i in 0..data.len() { println!("{} {}", i, data[i]); } ``` -------------------------------- ### Initialize rmodbus Small Storage Source: https://github.com/alttch/rmodbus/blob/master/README.md Example of initializing a small Modbus storage with 1000 registers for each type, suitable for systems with limited RAM. ```rust use rmodbus::server::{storage::ModbusStorageSmall, context::ModbusContext}; ``` -------------------------------- ### Working with Modbus Coils (Read/Write) Source: https://context7.com/alttch/rmodbus/llms.txt This example illustrates how to interact with Modbus coils, which represent boolean values. It shows setting individual coils, setting multiple coils in bulk, and reading a range of coils. The `ModbusRequest` structure and `ModbusProto::TcpUdp` are utilized. The `send_fn` closure is expected to handle the actual transmission and reception of Modbus frames. ```rust use rmodbus::client::ModbusRequest; use rmodbus::ModbusProto; fn coils_example(send_fn: impl Fn(&[u8]) -> Vec) { let mut mreq = ModbusRequest::new(1, ModbusProto::TcpUdp); // Set single coil let mut request = Vec::new(); mreq.generate_set_coil(10, true, &mut request).unwrap(); let response = send_fn(&request); mreq.parse_ok(&response).unwrap(); // Set multiple coils let coils = [true, false, true, true, false, false, true, false]; mreq.generate_set_coils_bulk(0, &coils, &mut request).unwrap(); let response = send_fn(&request); mreq.parse_ok(&response).unwrap(); // Read coils mreq.generate_get_coils(0, 13, &mut request).unwrap(); let response = send_fn(&request); let mut data = Vec::new(); mreq.parse_bool(&response, &mut data).unwrap(); for (i, value) in data.iter().enumerate() { println!("Coil {}: {}", i, value); } } ``` -------------------------------- ### Embedded Modbus Server Loop (Rust) Source: https://context7.com/alttch/rmodbus/llms.txt Demonstrates how to use rmodbus in a no_std environment for embedded systems. This example initializes a Modbus context with custom storage sizes and enters a loop to process incoming Modbus frames from a transport layer. ```rust #![no_std] use rmodbus::{ server::{storage::ModbusStorage, context::ModbusContext, ModbusFrame}, ModbusFrameBuf, ModbusProto, }; use fixedvec::{FixedVec, alloc_stack}; fn embedded_server_loop() { // Custom-sized storage for embedded: 64 coils, 32 discretes, 128 inputs, 128 holdings let mut ctx = ModbusStorage::<64, 32, 128, 128>::new(); loop { let framebuf: ModbusFrameBuf = [0; 256]; // Read frame into buffer from your transport (UART, etc.) let mut mem = alloc_stack!([u8; 256]); let mut response = FixedVec::new(&mut mem); let mut frame = ModbusFrame::new(1, &framebuf, ModbusProto::Rtu, &mut response); if frame.parse().is_ok() && frame.processing_required { let _result = if frame.readonly { frame.process_read(&ctx) } else { frame.process_write(&mut ctx) }; } if frame.response_required { let _ = frame.finalize_response(); // Send response.as_slice() to transport } } } ``` -------------------------------- ### Modbus Protocol and Transport Error Handling Source: https://context7.com/alttch/rmodbus/llms.txt This snippet demonstrates robust error handling for Modbus operations. It covers potential `ErrorKind`s such as `ModbusIllegalDataAddress`, `ModbusIllegalFunction`, and `FrameBroken`, as well as transport-level `std::io::Error`s. The example attempts to read a holding register and shows how to differentiate between various error conditions. It relies on `rmodbus::{ErrorKind, client::ModbusRequest, ModbusProto}` and a `send_fn` that can return `Result`. ```rust use rmodbus::{ErrorKind, client::ModbusRequest, ModbusProto}; fn error_handling_example(send_fn: impl Fn(&[u8]) -> Result, std::io::Error>) { let mut mreq = ModbusRequest::new(1, ModbusProto::TcpUdp); let mut request = Vec::new(); // Try to read a holding register mreq.generate_get_holdings(9999, 1, &mut request).unwrap(); match send_fn(&request) { Ok(response) => { match mreq.parse_ok(&response) { Ok(_) => { let mut values = Vec::new(); mreq.parse_u16(&response, &mut values).unwrap(); println!("Value: {}", values[0]); } Err(ErrorKind::ModbusIllegalDataAddress) => { println!("Register address out of range"); } Err(ErrorKind::ModbusIllegalFunction) => { println!("Function not supported"); } Err(ErrorKind::FrameBroken) => { println!("Malformed response frame"); } Err(e) => { println!("Other error: {:?}", e); } } } Err(e) => { println!("Transport error: {}", e); } } } ``` -------------------------------- ### Initialize rmodbus Custom-Sized Storage Source: https://github.com/alttch/rmodbus/blob/master/README.md Demonstrates how to define a Modbus context with custom sizes for coils, discretes, inputs, and holdings using generic constants. The order is coils, discretes, inputs, holdings. ```rust use rmodbus::server::{storage::ModbusStorage, context::ModbusContext}; let context = ModbusStorage::<128, 16, 0, 100>::new(); ``` -------------------------------- ### Define Custom-Sized Modbus Storage Contexts Source: https://context7.com/alttch/rmodbus/llms.txt Illustrates how to create `ModbusStorage` instances with specific register counts (coils, discretes, inputs, holdings) for memory optimization. It also shows the usage of `ModbusStorageSmall` for a pre-defined smaller storage size. ```rust use rmodbus::server::{storage::ModbusStorage, context::ModbusContext}; fn main() { // 128 coils, 16 discretes, 0 inputs, 100 holdings let mut ctx = ModbusStorage::<128, 16, 0, 100>::new(); ctx.set_coil(0, true).unwrap(); ctx.set_holding(0, 42).unwrap(); let coil = ctx.get_coil(0).unwrap(); let holding = ctx.get_holding(0).unwrap(); println!("Coil: {}, Holding: {}", coil, holding); // Small pre-defined storage (1000 registers each) use rmodbus::server::storage::ModbusStorageSmall; let mut small_ctx = ModbusStorageSmall::new(); small_ctx.set_holding(500, 100).unwrap(); } ``` -------------------------------- ### Modbus TCP Server using rmodbus in Rust Source: https://context7.com/alttch/rmodbus/llms.txt Demonstrates building a Modbus TCP server by integrating rmodbus frame processing with standard TCP networking. It handles incoming connections, reads Modbus frames, processes read/write requests using a shared context, and sends back responses. ```rust use std::io::{Read, Write}; use std::net::TcpListener; use std::thread; use std::sync::RwLock; use once_cell::sync::Lazy; use rmodbus::{ server::{storage::ModbusStorageFull, context::ModbusContext, ModbusFrame}, ModbusFrameBuf, ModbusProto, }; static CONTEXT: Lazy> = Lazy::new(<_>::default); fn main() { let unit_id = 1; let listener = TcpListener::bind("127.0.0.1:5502").unwrap(); for stream in listener.incoming() { thread::spawn(move || { let mut stream = stream.unwrap(); loop { let mut buf: ModbusFrameBuf = [0; 256]; let mut response = Vec::new(); if stream.read(&mut buf).unwrap_or(0) == 0 { return; } let mut frame = ModbusFrame::new(unit_id, &buf, ModbusProto::TcpUdp, &mut response); if frame.parse().is_err() { return; } if frame.processing_required { let result = if frame.readonly { frame.process_read(&*CONTEXT.read().unwrap()) } else { frame.process_write(&mut *CONTEXT.write().unwrap()) }; if result.is_err() { return; } } if frame.response_required { frame.finalize_response().unwrap(); stream.write(response.as_slice()).unwrap(); } } }); } } ``` -------------------------------- ### Save and Load Modbus Context State Source: https://context7.com/alttch/rmodbus/llms.txt This function pair allows for saving and loading the Modbus context state, essential for PLC-style applications requiring persistent memory. It uses `bincode` for serialization and deserialization of the `ModbusStorageFull` context to a file. Dependencies include `rmodbus::server::storage::ModbusStorageFull`, `std::fs::File`, `std::io::{Read, Write}`, and the `bincode` crate. ```rust use rmodbus::server::{storage::ModbusStorageFull, context::ModbusContext}; use std::fs::File; use std::io::{Read, Write}; fn save_context(fname: &str, ctx: &ModbusStorageFull) -> Result<(), Box> { let config = bincode::config::standard(); let mut file = File::create(fname)?; file.write_all(&bincode::encode_to_vec(ctx, config)?)?; file.sync_all()?; Ok(()) } fn load_context(fname: &str) -> Result> { let config = bincode::config::standard(); let mut file = File::open(fname)?; let mut data: Vec = Vec::new(); file.read_to_end(&mut data)?; let (ctx, _) = bincode::decode_from_slice(&data, config)?; Ok(ctx) } fn main() { let mut ctx = ModbusStorageFull::new(); ctx.set_holding(100, 42).unwrap(); ctx.set_coil(50, true).unwrap(); save_context("/tmp/modbus_state.dat", &ctx).unwrap(); let restored_ctx = load_context("/tmp/modbus_state.dat").unwrap(); assert_eq!(restored_ctx.get_holding(100).unwrap(), 42); assert_eq!(restored_ctx.get_coil(50).unwrap(), true); } ``` -------------------------------- ### Manage Server Modbus Context with Thread-Safe Storage Source: https://context7.com/alttch/rmodbus/llms.txt Demonstrates how to access and modify Modbus registers using a thread-safe context API with `ModbusStorageFull`. It shows writing to coils, holding registers, and inputs, as well as reading them back, utilizing read and write locks for concurrent access. ```rust use rmodbus::server::{storage::ModbusStorageFull, context::ModbusContext}; use std::sync::RwLock; fn main() { let ctx = RwLock::new(ModbusStorageFull::new()); // Write operations require write lock { let mut ctx = ctx.write().unwrap(); ctx.set_coil(0, true).unwrap(); ctx.set_holding(1000, 42).unwrap(); ctx.set_holdings_bulk(10, &[10, 20, 30]).unwrap(); ctx.set_inputs_from_f32(20, 935.77).unwrap(); ctx.set_holdings_from_u32(1200, 0xDEADBEEF).unwrap(); } // Read operations require read lock { let ctx = ctx.read().unwrap(); let coil = ctx.get_coil(0).unwrap(); let holding = ctx.get_holding(1000).unwrap(); let temp = ctx.get_inputs_as_f32(20).unwrap(); let value = ctx.get_holdings_as_u32(1200).unwrap(); let mut holdings = Vec::new(); ctx.get_holdings_bulk(10, 3, &mut holdings).unwrap(); println!("Coil 0: {}", coil); println!("Holding 1000: {}", holding); println!("Temperature: {}", temp); println!("Holdings 10-12: {:?}", holdings); } } ``` -------------------------------- ### Rust TCP Modbus Server Implementation using rmodbus Source: https://github.com/alttch/rmodbus/blob/master/README.md This Rust code demonstrates a simple blocking TCP Modbus server using the rmodbus framework. It listens for incoming connections, processes Modbus requests, and interacts with a shared Modbus context for data storage and manipulation. Dependencies include `rmodbus`, `once_cell`, `std::io`, `std::net`, and `std::thread`. It handles reading and writing to the Modbus context based on received frames. ```rust use std::io::{Read, Write}; use std::net::TcpListener; use std::thread; use std::sync::RwLock; use once_cell::sync::Lazy; use rmodbus::{ server::{storage::ModbusStorageFull, context::ModbusContext, ModbusFrame}, ModbusFrameBuf, ModbusProto, }; static CONTEXT: Lazy> = Lazy::new(<_>::default); pub fn tcpserver(unit: u8, listen: &str) { let listener = TcpListener::bind(listen).unwrap(); println!("listening started, ready to accept"); for stream in listener.incoming() { thread::spawn(move || { println!("client connected"); let mut stream = stream.unwrap(); loop { let mut buf: ModbusFrameBuf = [0; 256]; let mut response = Vec::new(); // for nostd use FixedVec with alloc [u8;256] if stream.read(&mut buf).unwrap_or(0) == 0 { return; } let mut frame = ModbusFrame::new(unit, &buf, ModbusProto::TcpUdp, &mut response); if frame.parse().is_err() { println!("server error"); return; } if frame.processing_required { let result = if frame.readonly { frame.process_read(&*CONTEXT.read().unwrap()) } else { frame.process_write(&mut *CONTEXT.write().unwrap()) }; if result.is_err() { println!("frame processing error"); return; } } if frame.response_required { frame.finalize_response().unwrap(); println!("{:x?}", response.as_slice()); if stream.write(response.as_slice()).is_err() { return; } } } }); } } ``` -------------------------------- ### Client: Write Modbus Holding Registers with Different Data Types Source: https://context7.com/alttch/rmodbus/llms.txt Shows how to generate Modbus requests for writing to holding registers. This includes writing a single register, multiple registers in bulk, a string, and a byte slice, demonstrating flexible data manipulation capabilities. ```rust use rmodbus::client::ModbusRequest; use rmodbus::ModbusProto; fn write_holdings_example(send_fn: impl Fn(&[u8]) -> Vec) { let mut mreq = ModbusRequest::new(1, ModbusProto::TcpUdp); // Write single holding register let mut request = Vec::new(); mreq.generate_set_holding(100, 0x1234, &mut request).unwrap(); let response = send_fn(&request); mreq.parse_ok(&response).unwrap(); // Write multiple holdings mreq.generate_set_holdings_bulk(200, &[100, 200, 300], &mut request).unwrap(); let response = send_fn(&request); mreq.parse_ok(&response).unwrap(); // Write string to holdings mreq.generate_set_holdings_string(300, "Hello", &mut request).unwrap(); let response = send_fn(&request); mreq.parse_ok(&response).unwrap(); // Write byte slice to holdings let data = vec![0x12, 0x34, 0x56, 0x78, 0x9A]; mreq.generate_set_holdings_bulk_from_slice(400, &data, &mut request).unwrap(); let response = send_fn(&request); mreq.parse_ok(&response).unwrap(); } ``` -------------------------------- ### rmodbus no_std Configuration Source: https://github.com/alttch/rmodbus/blob/master/README.md Instructions for configuring rmodbus to work in a no_std environment. This involves modifying the Cargo.toml file to disable default features. ```toml rmodbus = { version = "*", default-features = false } ``` -------------------------------- ### Client: Read Modbus Holding Registers with Various Data Types Source: https://context7.com/alttch/rmodbus/llms.txt Illustrates how a Modbus client can generate requests to read holding registers and subsequently parse the responses into different numeric formats, including `u16`, big-endian `u32`, and big-endian IEEE 754 floats. ```rust use rmodbus::client::ModbusRequest; use rmodbus::ModbusProto; fn read_holdings_example(send_fn: impl Fn(&[u8]) -> Vec) { let mut mreq = ModbusRequest::new(1, ModbusProto::TcpUdp); // Read 10 holdings starting at register 100 let mut request = Vec::new(); mreq.generate_get_holdings(100, 10, &mut request).unwrap(); let response = send_fn(&request); // Parse as u16 values let mut values = Vec::new(); mreq.parse_u16(&response, &mut values).unwrap(); println!("Holdings as u16: {:?}", values); // Parse as 32-bit values (big-endian) let mut values_u32 = Vec::new(); mreq.parse_u32_be(&response, &mut values_u32).unwrap(); println!("Holdings as u32: {:?}", values_u32); // Parse as IEEE 754 floats let mut values_f32 = Vec::new(); mreq.parse_f32_be(&response, &mut values_f32).unwrap(); println!("Holdings as f32: {:?}", values_f32); } ``` -------------------------------- ### Process Modbus RTU Frames with CRC Validation Source: https://context7.com/alttch/rmodbus/llms.txt This snippet demonstrates how to process Modbus RTU frames, including CRC validation, for serial communications. It utilizes the `rmodbus` crate to parse incoming frames, determine if processing is required (read or write), and generate appropriate responses. Dependencies include `rmodbus::server::{storage::ModbusStorageFull, ModbusFrame}` and `ModbusProto`. ```rust use rmodbus::{ server::{storage::ModbusStorageFull, ModbusFrame}, ModbusFrameBuf, ModbusProto, }; fn process_rtu_frame(frame_data: &[u8]) -> Vec { let mut ctx = ModbusStorageFull::new(); let mut response = Vec::new(); let unit_id = 1; let mut frame = ModbusFrame::new(unit_id, frame_data, ModbusProto::Rtu, &mut response); if frame.parse().is_ok() { if frame.processing_required { if frame.readonly { frame.process_read(&ctx).unwrap(); } else { frame.process_write(&mut ctx).unwrap(); } } if frame.response_required { frame.finalize_response().unwrap(); } } response } // Example RTU frame: read 2 holdings from register 0 let frame = vec![0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xC4, 0x0B]; let response = process_rtu_frame(&frame); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.