Rust bindings for the snap7 C++ library, statically linked with no additional runtime dependencies.
This crate also includes a utils module — modeled after python-snap7 — for encoding and decoding PLC data (bools, ints, reals, dates, times, strings, etc.) to and from raw byte buffers.
utils::getters and utils::setters functions now return Result<T, String> instead of panicking on out-of-bounds or malformed input.
// before
let value = utils::getters::get_int(&buff, 0);
// after
let value = utils::getters::get_int(&buff, 0)?; // or .unwrap()See Changelog for full details.
use std::thread;
use std::time::Duration;
use rust_snap7::utils;
use rust_snap7::AreaCode;
use rust_snap7::MaskKind;
use rust_snap7::S7Client;
use rust_snap7::S7Server;
use rust_snap7::{InternalParam, InternalParamValue};
fn create_s7_server() -> Result<(), Box<dyn std::error::Error>> {
// Creating the S7 Server
let server = S7Server::create();
// Creating shared memory areas
let mut db_buff = [0u8; 1024];
// Adding shared blocks
server
.register_area(AreaCode::S7AreaDB, 1, &mut db_buff)
.expect("Failed to register area");
// Filtering reads and writes
server
.set_mask(MaskKind::Event, 0x00020000 | 0x00040000)
.expect("Failed to set mask");
// Setting event callbacks
server
.set_events_callback(Some(move |_, p_event, _| {
if let Ok(text) = S7Server::event_text(p_event) {
println!("Server Event: {:?}", text);
}
}))
.expect("Failed to set events callback");
// Start service
server.start().expect("Failed to start server");
// Business logic would go here.
// For this example, we'll just wait a bit.
std::thread::sleep(std::time::Duration::from_secs(10));
// Close service
server.stop().expect("Failed to stop server");
Ok(())
}
fn connect_to_plc() -> Result<(), String> {
let client = S7Client::create();
// Configure client parameters
client
.set_param(InternalParam::RemotePort, InternalParamValue::U16(102))
.expect("Failed to set remote port value");
// Attempt to connect to PLC
match client.connect_to("127.0.0.1", 0, 1) {
Ok(_) => {
let mut buff = [0u8; 2];
// Read from DB
match client.db_read(1, 20, 2, &mut buff) {
Ok(_) => match utils::getters::get_int(&buff, 0) {
Ok(data) => println!("Successfully read from DB1.W20: {}", data),
Err(e) => println!("Failed to decode DB1.W20: {}", e),
},
Err(e) => println!("Failed to read DB: {:?}", e),
}
// Disconnect client
client.disconnect().expect("Client disconnect failed");
}
Err(e) => println!("Connection to PLC failed: {:?}", e),
}
Ok(())
}
fn main() {
std::thread::spawn(|| {
if let Err(e) = create_s7_server() {
eprintln!("S7 Server Error: {}", e);
}
});
thread::sleep(Duration::from_millis(500));
connect_to_plc().expect("connect fn error");
}Warning
This library has not undergone any security review. Use at your own risk, especially when exposed to untrusted networks or PLC data.
This repository is based on the original snap7-rs, and was created to translate parts of the original into English and fix some compilation issues.
Breaking changes
- All functions in
utils::gettersandutils::settersnow returnResult<T, String>instead of panicking on out-of-bounds or malformed input. Callers must handle or.unwrap()/?the result.- Example:
utils::getters::get_int(&buff, 0)now returnsResult<i16, String>instead ofi16.
- Example:
Fixed
parse_time_string(utils::time): the S7TIMEformat is[-]days:hours:minutes:seconds:millis[.fraction]. The previous regex didn't correctly parse this five-field format and silently mishandled negative durations (wrapping to a large unsigned value instead of a signed one).utils::getters::get_stringnow correctly bounds-checks the 2-byte length header before reading it, instead of potentially panicking on very short buffers.utils::getters::get_s5timenow returnsErron malformed input instead of panicking.- Fixed a build failure on newer Rust toolchains (
deny(deref_nullptr)) caused by bindgen's null-pointer-basedoffsetoflayout tests insrc/ffi.rs. No functional change to the generated bindings.
Internal
- Deduplicated
parse_time_stringinto a single sharedutils::timemodule;setters::set_timeandgettersnow both call the same implementation instead of maintaining separate, drifting copies. - Added consistent bounds checking (
check_bounds) across all fixed-width getters/setters. - Deduplicated fixed-width getter/setter boilerplate into shared
define_be_getter!/define_be_setter!macros inutils::bytes.
See prior releases on crates.io.
The source code and documentation for this project are licensed under the Mulan Permissive Software License, Version 2 (MulanPSL-2.0).
snap7 itself is licensed under the GNU Lesser General Public License v3+ (LGPL v3+).