Skip to content

bruceunx/snap7-rs

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rust-snap7

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.

Migrating to 1.142.3

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.

Example

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.

Origin

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.

Changelog

1.1423 - 2026-07-17

Breaking changes

  • All functions in utils::getters and utils::setters now return Result<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 returns Result<i16, String> instead of i16.

Fixed

  • parse_time_string (utils::time): the S7 TIME format 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_string now correctly bounds-checks the 2-byte length header before reading it, instead of potentially panicking on very short buffers.
  • utils::getters::get_s5time now returns Err on malformed input instead of panicking.
  • Fixed a build failure on newer Rust toolchains (deny(deref_nullptr)) caused by bindgen's null-pointer-based offsetof layout tests in src/ffi.rs. No functional change to the generated bindings.

Internal

  • Deduplicated parse_time_string into a single shared utils::time module; setters::set_time and getters now 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 in utils::bytes.

1.142.2 and earlier

See prior releases on crates.io.

License

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+).

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages