### Basic PLC Program Example in Rust Source: https://github.com/eva-ics/rplc/blob/main/README.md This example demonstrates a simple PLC program written in Rust using the rPLC library. It defines a PLC program named 'tempmon' that controls a fan based on temperature readings. The program requires the 'rplc' crate and uses macros like `plc_program`, `plc_context_mut`, `init_plc`, `tempmon_spawn`, and `run_plc`. ```rust use rplc::prelude::*; mod plc; #[plc_program(loop = "200ms")] fn tempmon() { let mut ctx = plc_context_mut!(); if ctx.temperature > 30.0 { ctx.fan = true; } else if ctx.temperature < 25.0 { ctx.fan = false; } } fn main() { init_plc!(); tempmon_spawn(); run_plc!(); } ``` -------------------------------- ### Start PLC Main Loop with run_plc! in Rust Source: https://context7.com/eva-ics/rplc/llms.txt Starts the main PLC execution loop, registers signal handlers for graceful shutdown (SIGTERM/SIGINT), launches I/O synchronization, and waits for termination. It also creates a Unix socket API and PID file in the configured directory. This macro should be called after initializing the PLC and spawning programs. ```rust use rplc::prelude::*; mod plc; #[plc_program(loop = "200ms")] fn control_loop() { let mut ctx = plc_context_mut!(); // Control logic here } fn main() { init_plc!(); control_loop_spawn(); // Blocks until SIGTERM/SIGINT received // Handles graceful shutdown automatically run_plc!(); } ``` -------------------------------- ### Initialize PLC Runtime with init_plc! in Rust Source: https://context7.com/eva-ics/rplc/llms.txt Initializes the rPLC runtime using configuration from the generated `plc` module. This function sets up logging, signal handlers, and internal state, and must be called before any programs are spawned or the PLC is run. It typically includes the `run_plc!()` call to start the main loop. ```rust use rplc::prelude::*; mod plc; fn main() { // Initialize PLC with name, description, and version from Cargo.toml init_plc!(); // Now you can spawn programs and run the PLC run_plc!(); } ``` -------------------------------- ### Configure OPC-UA Client for PLC I/O Source: https://context7.com/eva-ics/rplc/llms.txt This configuration sets up an OPC-UA client to read temperature data and write fan status. It specifies the OPC-UA server URL, authentication details, and maps server nodes to PLC context fields. The sync parameter controls the data update frequency, and cache limits write operations. ```yaml # plc.yml version: 1 context: fields: temperature: LREAL[2] fan1: BOOL fan2: BOOL io: - id: opc_server kind: opcua config: url: opc.tcp://localhost:4855 pki_dir: /var/plc/pki # Optional PKI directory trust_server_certs: true # Trust any server cert create_keys: true # Auto-create client keys timeout: 5.0 auth: # Authentication options user: operator password: secret123 # Or X.509: # cert_file: own/cert.der # key_file: private/private.pem input: - nodes: - id: "ns=2;s=Temperature" map: temperature[0] - id: "ns=2;g=dcff8e02-4706-49ea-979c-fc1ec6cff8ef" map: temperature[1] sync: 1s output: - nodes: - id: "ns=2;s=Fan1" map: fan1 - id: "ns=2;s=Fan2" map: fan2 sync: 1s cache: 10s # Only write on change within cache window ``` -------------------------------- ### Cargo.toml Dependencies for PLC Applications Source: https://context7.com/eva-ics/rplc/llms.txt This Cargo.toml file specifies the necessary dependencies for a Rust PLC application using the rplc crate. It includes features for Modbus and OPC-UA integration, as well as optional support for context persistence using rmp-serde. ```toml # Cargo.toml [package] name = "my-plc" version = "0.1.0" edition = "2021" [dependencies] rplc = { version = "0.3", features = ["modbus", "opcua"] } log = "0.4" # Optional: for context persistence rmp-serde = "1.1" [build-dependencies] rplc = { version = "0.3", features = ["modbus", "opcua"] } ``` -------------------------------- ### Build PLC Application with Code Generation (Rust) Source: https://context7.com/eva-ics/rplc/llms.txt The build.rs script in a Rust project is used to trigger code generation from plc.yml using the rplc::builder. This can be done with simple generation or by inserting template variables for dynamic configuration within the plc.yml file. ```rust // build.rs fn main() { // Simple generation rplc::builder::generate("plc.yml").unwrap(); } // Or with template variables fn main() { let mut builder = rplc::builder::Builder::new("plc.yml"); // Insert variables for Tera templates in plc.yml builder.insert("modbus_server_port", &5502); builder.insert("opc_url", &"opc.tcp://192.168.1.50:4840"); builder.generate().unwrap(); } ``` -------------------------------- ### Complete Temperature Control System (Rust PLC) Source: https://context7.com/eva-ics/rplc/llms.txt This Rust code implements a temperature control system for a PLC. It defines two main programs: `temperature_control` for managing fan speed based on temperature and `logger` for periodic logging. It also includes a `shutdown` handler and demonstrates context loading/saving. ```rust // src/main.rs use rplc::prelude::*; use std::fs; use std::time::Duration; mod plc; #[plc_program(loop = "200ms")] fn temperature_control() { let mut ctx = plc_context_mut!(); // Hysteresis control for fan if ctx.temperature > 30.0 { ctx.fan = true; ctx.fan_speed = 100; } else if ctx.temperature > 28.0 && ctx.fan { ctx.fan_speed = 75; } else if ctx.temperature < 25.0 { ctx.fan = false; ctx.fan_speed = 0; } // Update Modbus registers for SCADA ctx.modbus.set_holding(0, (ctx.temperature * 100.0) as u16).unwrap(); ctx.modbus.set_coil(0, ctx.fan).unwrap(); } #[plc_program(loop = "1s")] fn logger() { let ctx = plc_context!(); info!( "Temp: {:.1}C, Fan: {}, Speed: {}%", ctx.temperature, ctx.fan, ctx.fan_speed ); } fn shutdown() { warn!("Emergency shutdown - turning off all outputs"); let mut ctx = plc_context_mut!(); ctx.fan = false; ctx.fan_speed = 0; } fn main() { init_plc!(); // Register shutdown handler rplc::tasks::on_shutdown(shutdown); // Load saved context if exists if let Ok(data) = fs::read("plc.dat") { info!("Restoring saved context"); *plc_context_mut!() = rmp_serde::from_slice(&data).unwrap(); } // Spawn programs temperature_control_spawn(); logger_spawn(); // Enable statistics logging rplc::tasks::spawn_stats_log(Duration::from_secs(10)); // Run until SIGTERM run_plc!(); // Save context on exit info!("Saving context"); fs::write("plc.dat", rmp_serde::to_vec_named(&*plc_context!()).unwrap()).unwrap(); } ``` -------------------------------- ### Configure OPC-UA Communication - YAML Source: https://context7.com/eva-ics/rplc/llms.txt Sets up OPC-UA client connections for interacting with OPC-UA servers. This configuration in `plc.yml` allows for reading and writing node values, including support for authentication mechanisms. ```yaml # plc.yml version: 1 ``` -------------------------------- ### Define PLC Program Loops with #[plc_program] Attribute in Rust Source: https://context7.com/eva-ics/rplc/llms.txt This attribute macro transforms a Rust function into a spawnable PLC program with automatic loop timing. It accepts a `loop` parameter for the execution interval (e.g., "200ms", "1s") and an optional `shift` parameter for staggered startup. Each function decorated with this macro generates a corresponding `_spawn()` function. ```rust use rplc::prelude::*; mod plc; // Runs every 200ms #[plc_program(loop = "200ms")] fn fast_control() { let mut ctx = plc_context_mut!(); ctx.counter += 1; } // Runs every 1 second with 100ms startup shift #[plc_program(loop = "1s", shift = "100ms")] fn slow_monitor() { let ctx = plc_context!(); info!("Counter value: {}", ctx.counter); } fn main() { init_plc!(); // Each program generates a _spawn() function fast_control_spawn(); slow_monitor_spawn(); run_plc!(); } ``` -------------------------------- ### Spawn Custom Input Thread with tasks::spawn_input_loop in Rust Source: https://context7.com/eva-ics/rplc/llms.txt Spawns a custom input thread for reading data from external sources not covered by built-in I/O. This function allows specifying a thread name, loop interval, startup shift, and a closure for the reading logic. It automatically coordinates with the PLC lifecycle and updates PLC context variables. ```rust use rplc::prelude::*; use std::time::Duration; mod plc; fn spawn_sensor_reader() { rplc::tasks::spawn_input_loop( "sensor1", // Thread name (max 14 chars) Duration::from_millis(500), // Loop interval Duration::default(), // Startup shift move || { // Read from custom sensor let reading = read_sensor(); plc_context_mut!().sensor_value = reading; }, ); } fn read_sensor() -> f32 { // Custom sensor reading logic 25.5 } fn main() { init_plc!(); spawn_sensor_reader(); run_plc!(); } ``` -------------------------------- ### PLC YAML with Template Variables Source: https://context7.com/eva-ics/rplc/llms.txt This YAML snippet demonstrates how to use template variables within the plc.yml configuration. These variables, such as `modbus_server_port` and `opc_url`, can be dynamically injected during the build process using the rplc::builder. ```yaml # plc.yml with templates server: - kind: modbus config: proto: tcp listen: 127.0.0.1:{{ modbus_server_port }} unit: 0x01 ``` -------------------------------- ### Spawn Statistics Logging Thread - RPLC Source: https://context7.com/eva-ics/rplc/llms.txt Spawns a background thread dedicated to logging thread timing statistics. This includes metrics like iteration counts and jitter, providing insights into the performance of other PLC tasks. The logging interval is configurable. ```rust use rplc::prelude::*; use std::time::Duration; mod plc; #[plc_program(loop = "100ms")] fn critical_control() { let mut ctx = plc_context_mut!(); // Time-critical control logic } fn main() { init_plc!(); critical_control_spawn(); // Log stats every 5 seconds rplc::tasks::spawn_stats_log(Duration::from_secs(5)); // Output: thread Pcritical_control iters 50, jitter min: 12, max: 156, last: 45, avg: 38 run_plc!(); } ``` -------------------------------- ### Configure Modbus Server for PLC Context Exposure Source: https://context7.com/eva-ics/rplc/llms.txt This configuration enables the PLC to act as a Modbus slave, exposing its context registers. It defines the memory map for coils, discrete inputs, input registers, and holding registers. The server can be configured for TCP or RTU protocols, specifying listen addresses, unit IDs, and timeouts. ```yaml # plc.yml version: 1 context: modbus: c: 100 # 100 coils d: 100 # 100 discrete inputs i: 100 # 100 input registers h: 100 # 100 holding registers fields: # Regular fields - accessed via context temperature: REAL fan: BOOL server: - kind: modbus config: proto: tcp listen: 0.0.0.0:502 unit: 0x01 timeout: 60 maxconn: 5 # Multiple servers supported - kind: modbus config: proto: rtu listen: /dev/ttyUSB0:9600:8:N:1 unit: 0x02 timeout: 3600 ``` -------------------------------- ### Define PLC Context Variables - YAML Source: https://context7.com/eva-ics/rplc/llms.txt Defines PLC variables and their types within the `plc.yml` configuration file. Supports standard IEC 61131-3 types, arrays, nested structures, and automatic Modbus register mapping. This section is crucial for defining the PLC's data model. ```yaml # plc.yml version: 1 core: stop_timeout: 10 # Seconds to wait for graceful shutdown context: serialize: true # Enable MessagePack serialization modbus: # Built-in Modbus slave registers c: 1000 # Coils d: 1000 # Discrete inputs i: 1000 # Input registers h: 1000 # Holding registers fields: # Basic types (IEC 61131-3) temperature: REAL # f32 humidity: REAL # f32 pressure: LREAL # f64 fan: BOOL # bool counter: UDINT # u32 setpoint: INT # i16 # Arrays temps: REAL[4] # [f32; 4] flags: BOOL[8] # [bool; 8] # Nested structures motor: running: BOOL speed: UINT current: REAL # Array of structures "sensor[3]": value: REAL status: UINT ``` -------------------------------- ### Spawn Custom Output Loop Thread - RPLC Source: https://context7.com/eva-ics/rplc/llms.txt Spawns a dedicated thread for a custom output loop, allowing data to be written to external actuators. It automatically handles synchronization on shutdown. This function takes a name, update interval, and a closure for the output logic. ```rust use rplc::prelude::*; use std::time::Duration; mod plc; fn spawn_relay_controller() { rplc::tasks::spawn_output_loop( "relays", Duration::from_secs(1), Duration::default(), move || { let ctx = plc_context!(); // Write to relays based on context set_relay(0, ctx.relay1); set_relay(1, ctx.relay2); info!("Relays updated"); }, ); } fn set_relay(id: u8, state: bool) { // Custom relay control logic } ``` -------------------------------- ### Configure Modbus I/O Communication - YAML Source: https://context7.com/eva-ics/rplc/llms.txt Configures Modbus TCP or RTU communication for reading PLC inputs and writing outputs. This section in `plc.yml` specifies the connection details, register mapping, and synchronization intervals for Modbus devices. ```yaml # plc.yml version: 1 context: fields: temperature: REAL humidity: REAL fan: BOOL pump: BOOL io: - id: plc_io kind: modbus config: proto: tcp # tcp or rtu path: 192.168.1.100:502 # IP:port for TCP # path: /dev/ttyUSB0:9600:8:N:1 # For RTU timeout: 1.0 # Seconds frame_delay: 0.1 # RTU inter-frame delay input: # Read holding registers 0-3 as temperature (REAL = 2 registers) - reg: h0-3 unit: 0x01 map: - offset: 0 target: temperature - offset: 2 target: humidity sync: 500ms # Read discrete inputs - reg: d100 unit: 0x01 number: 8 map: - target: sensor_status sync: 1s output: # Write coils - reg: c0-2 unit: 0x01 map: - offset: 0 source: fan - offset: 1 source: pump sync: 500ms ``` -------------------------------- ### Access PLC Variables with plc_context! and plc_context_mut! in Rust Source: https://context7.com/eva-ics/rplc/llms.txt Provides thread-safe access to the PLC context, which holds shared variables auto-generated from `plc.yml`. `plc_context!()` returns a read-only lock, while `plc_context_mut!()` returns a mutable lock for read-write access. These are essential for interacting with PLC state within program loops. ```rust use rplc::prelude::*; mod plc; #[plc_program(loop = "100ms")] fn monitor() { // Read-only access let ctx = plc_context!(); info!("Temperature: {}", ctx.temperature); } #[plc_program(loop = "500ms")] fn controller() { // Read-write access let mut ctx = plc_context_mut!(); if ctx.temperature > 30.0 { ctx.fan = true; } else if ctx.temperature < 25.0 { ctx.fan = false; } } fn main() { init_plc!(); monitor_spawn(); controller_spawn(); run_plc!(); } ``` -------------------------------- ### Register Shutdown Handler - RPLC Source: https://context7.com/eva-ics/rplc/llms.txt Registers a function to be executed during the PLC's graceful shutdown process. This is crucial for ensuring that outputs are set to a safe state before the system stops. The handler function is provided as a closure. ```rust use rplc::prelude::*; mod plc; fn shutdown_handler() { warn!("Shutting down - setting safe state"); let mut ctx = plc_context_mut!(); ctx.fan = false; ctx.heater = false; ctx.pump = false; warn!("Safe state set"); } #[plc_program(loop = "500ms")] fn control() { let mut ctx = plc_context_mut!(); // Normal control logic } fn main() { init_plc!(); rplc::tasks::on_shutdown(shutdown_handler); control_spawn(); run_plc!(); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.