### Connect IEC 60870-5 104 TCP Client Source: https://github.com/roboplc/roboplc-io-iec60870-5/blob/main/README.md Establishes a connection to an IEC 60870-5 104 TCP server. It creates a client instance, retrieves a telegram receiver, and optionally starts a pinger for keep-alive or auto-reconnection. ```rust use roboplc::comm::Timeouts; use roboplc_io_iec60870_5::iec104::{Client, PingKind}; use std::time::Duration; // Create a new IEC 60870-5 104 client let (client, reader) = Client::new("192.168.1.100:2404", Timeouts::default(), 1024).unwrap(); // Get the telegram receiver let telegram_rx = reader.get_telegram_receiver(); // The reader must be run in a separate thread std::thread::spawn(move || reader.run()); // Create an optional pinger worker, which can send test/ack frames or // automatically re-connect the socket let pinger = client.pinger(PingKind::Test, Duration::from_secs(1)); std::thread::spawn(move || pinger.run()); ``` -------------------------------- ### Send Telegram Without Response in Rust Source: https://context7.com/roboplc/roboplc-io-iec60870-5/llms.txt Sends an IEC 60870-5 104 telegram frame to the remote server without awaiting a response. This method is suitable for fire-and-forget operations like sending test frames, acknowledgments, or commands where no confirmation is required. Examples include sending a test frame and a start data transfer frame. ```rust use iec60870_5::telegram104::Telegram104; use roboplc_io_iec60870_5::iec104::Client; // Send a test frame (U-frame) to keep connection alive let test_frame = Telegram104::new_test(); client.send(test_frame)?; // Send a start data transfer frame let start_dt = Telegram104::new_start_dt(); client.send(start_dt)?; ``` -------------------------------- ### Create New IEC 60870-5 104 Client in Rust Source: https://context7.com/roboplc/roboplc-io-iec60870-5/llms.txt Initializes a new IEC 60870-5 104 TCP client connection to a remote server. It returns a client instance for sending commands and a reader for processing incoming telegrams, which must be run in a separate thread. Connection timeouts and reader queue size are configurable. ```rust use roboplc::comm::Timeouts; use roboplc_io_iec60870_5::iec104::{Client, PingKind}; use std::time::Duration; fn main() -> roboplc::Result<()> { // Create a new IEC 60870-5 104 client with: // - Remote address and port (default IEC 104 port is 2404) // - Connection timeouts configuration // - Reader queue size for incoming telegram buffer let (client, reader) = Client::new( "192.168.1.100:2404", Timeouts::default(), 1024 )?; // Get the telegram receiver channel for processing incoming data let telegram_rx = reader.get_telegram_receiver(); // The reader MUST be run in a separate thread - it handles all incoming telegrams std::thread::spawn(move || reader.run()); // Create an optional pinger worker for connection keep-alive // PingKind::Test sends U-frame test packets // PingKind::Ack sends S-frame acknowledgments // PingKind::Connect only reconnects if connection dropped let pinger = client.pinger(PingKind::Test, Duration::from_secs(1)); std::thread::spawn(move || pinger.run()); Ok(()) } ``` -------------------------------- ### Cargo.toml Configuration - Feature Flags Source: https://context7.com/roboplc/roboplc-io-iec60870-5/llms.txt Configuration options for the Cargo.toml file, specifically focusing on feature flags related to locking policies. ```APIDOC ## Cargo.toml Configuration ### Description Configure the crate's locking policy to match your RoboPLC application. The locking policy must be consistent across all RoboPLC crates in your project. ### Method N/A (Configuration) ### Endpoint N/A (Configuration) ### Parameters None ### Request Example ```toml [dependencies] roboplc-io-iec60870-5 = "1.1.0" # Choose ONE locking feature (must match your roboplc configuration): # Default: Real-time safe locking (recommended for production) # roboplc-io-iec60870-5 = { version = "1.1.0", features = ["locking-rt-safe"] } # Standard locking (for non-real-time applications) # roboplc-io-iec60870-5 = { version = "1.1.0", features = ["locking-default"] } # Real-time locking with priority inheritance # roboplc-io-iec60870-5 = { version = "1.1.0", features = ["locking-rt"] } ``` ### Response N/A (Configuration) ### Response Example N/A (Configuration) ``` -------------------------------- ### Reader::get_restart_event_receiver - Monitor Connection Restarts Source: https://context7.com/roboplc/roboplc-io-iec60870-5/llms.txt Provides a channel receiver to monitor connection restart events, allowing applications to reinitialize state or restore subscriptions. ```APIDOC ## GET /api/reader/restart-events ### Description Returns a channel receiver for connection restart events. Use this to detect when the reader loop has restarted due to network issues or server restarts, allowing your application to restore subscriptions or reinitialize state. ### Method GET ### Endpoint `/api/reader/restart-events` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```rust use roboplc_io_iec60870_5::iec104::RestartEvent; let restart_rx = reader.get_restart_event_receiver(); // Monitor for restart events in a separate handler std::thread::spawn(move || { while let Ok(_event) = restart_rx.recv() { println!("Connection restarted - reinitializing subscriptions"); // Restore any notifications or handles here // Re-subscribe to data points // Reset local state as needed } }); ``` ### Response #### Success Response (200) - **receiver** (Receiver) - A channel receiver that will receive `RestartEvent` notifications when the connection restarts. #### Response Example ```json { "message": "Connection restart event receiver obtained." } ``` ``` -------------------------------- ### Client::command - Send Command and Wait for Response Source: https://context7.com/roboplc/roboplc-io-iec60870-5/llms.txt Sends a command telegram and waits for a response or timeout. This is the primary method for control operations requiring confirmation. ```APIDOC ## POST /api/command ### Description Sends a command telegram and blocks until a response is received or timeout occurs. This is the primary method for control operations where confirmation from the remote device is required. ### Method POST ### Endpoint `/api/command` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **telegram** (Telegram104) - Required - The command telegram to send. ### Request Example ```rust use iec60870_5::telegram104::{Telegram104, Telegram104_I}; use iec60870_5::types::{datatype::{DataType, SelectExecute, C_RC_TA_1, QU, RCO, RCS}, COT}; use roboplc::prelude::Timestamp; // Create a regulating step command telegram (C_RC_TA_1) let mut telegram_builder: Telegram104_I = Telegram104_I::new( DataType::C_RC_TA_1, // Regulating step command with time tag COT::Act, // Cause of transmission: Activation 47 // Common address of ASDU ); // Append an Information Object Unit with the command data telegram_builder.append_iou( 12, // Information object address C_RC_TA_1 { rco: RCO { rcs: RCS::Increment, // Regulating step: increment se: SelectExecute::Execute, // Execute (not select) qu: QU::Persistent, // Qualifier: persistent }, time: Timestamp::now().try_into().unwrap(), }, ); // Convert to Telegram104 and send command let request: Telegram104 = telegram_builder.into(); // let reply = client.command(request)?; // println!("Command reply: {:?}", reply); ``` ### Response #### Success Response (200) - **reply** (Telegram104) - The response telegram from the remote device. #### Response Example ```json { "reply": "" } ``` ``` -------------------------------- ### Send IEC 60870-5 Command and Wait for Response (Rust) Source: https://context7.com/roboplc/roboplc-io-iec60870-5/llms.txt Sends a command telegram (e.g., C_RC_TA_1) and blocks until a response is received or a timeout occurs. This is the primary method for control operations requiring confirmation. It utilizes types from the `iec60870_5` crate for constructing the telegram. ```rust use iec60870_5::{ telegram104::{Telegram104, Telegram104_I}, types::{datatype::{DataType, SelectExecute, C_RC_TA_1, QU, RCO, RCS}, COT}, }; use roboplc::prelude::*; // Create a regulating step command telegram (C_RC_TA_1) let mut telegram: Telegram104_I = Telegram104_I::new( DataType::C_RC_TA_1, // Regulating step command with time tag COT::Act, // Cause of transmission: Activation 47 // Common address of ASDU ); // Append an Information Object Unit with the command data telegram.append_iou( 12, // Information object address C_RC_TA_1 { rco: RCO { rcs: RCS::Increment, // Regulating step: increment se: SelectExecute::Execute, // Execute (not select) qu: QU::Persistent, // Qualifier: persistent }, time: Timestamp::now().try_into().unwrap(), }, ); // Convert to Telegram104 and send command let request: Telegram104 = telegram.into(); let reply = client.command(request)?; println!("Command reply: {:?}", reply); ``` -------------------------------- ### Monitor Connection Restarts with Receiver (Rust) Source: https://context7.com/roboplc/roboplc-io-iec60870-5/llms.txt Returns a channel receiver for connection restart events. This allows applications to detect when the reader loop has restarted (e.g., due to network issues) and reinitialize subscriptions or state. ```rust use roboplc_io_iec60870_5::iec104::RestartEvent; let restart_rx = reader.get_restart_event_receiver(); // Monitor for restart events in a separate handler std::thread::spawn(move || { while let Ok(_event) = restart_rx.recv() { println!("Connection restarted - reinitializing subscriptions"); // Restore any notifications or handles here // Re-subscribe to data points // Reset local state as needed } }); ``` -------------------------------- ### Configure roboplc-io-iec60870-5 Feature Flags (TOML) Source: https://context7.com/roboplc/roboplc-io-iec60870-5/llms.txt Configures the crate's locking policy in Cargo.toml to match the RoboPLC application's requirements. It is crucial that the chosen locking feature is consistent across all RoboPLC crates in the project to prevent deadlocks and ensure real-time performance. ```toml [dependencies] roboplc-io-iec60870-5 = "1.1.0" # Choose ONE locking feature (must match your roboplc configuration): # Default: Real-time safe locking (recommended for production) # roboplc-io-iec60870-5 = { version = "1.1.0", features = ["locking-rt-safe"] } # Standard locking (for non-real-time applications) # roboplc-io-iec60870-5 = { version = "1.1.0", features = ["locking-default"] } # Real-time locking with priority inheritance # roboplc-io-iec60870-5 = { version = "1.1.0", features = ["locking-rt"] } ``` -------------------------------- ### Send IEC 60870-5 104 Telegrams Source: https://github.com/roboplc/roboplc-io-iec60870-5/blob/main/README.md Sends IEC 60870-5 104 telegrams using the client. It shows how to send a command telegram (C_RC_TA_1) and wait for a reply, demonstrating the use of `client.command()` for acknowledged transmissions. ```rust use iec60870_5::{ telegram104::{Telegram104, Telegram104_I}, types::{datatype::{DataType, SelectExecute, C_RC_TA_1, QU, RCO, RCS}, COT}, }; use roboplc::prelude::*; let mut telegram: Telegram104_I = Telegram104_I::new(DataType::C_RC_TA_1, COT::Act, 47); telegram.append_iou( 12, C_RC_TA_1 { rco: RCO { rcs: RCS::Increment, se: SelectExecute::Execute, qu: QU::Persistent, }, time: Timestamp::now().try_into().unwrap(), }, ); let request: Telegram104 = telegram.into(); let reply = client.command(request).unwrap(); println!("COMMAND REPLY: {:?}", reply); ``` -------------------------------- ### Create IEC 60870-5 Keep-Alive Pinger Worker (Rust) Source: https://context7.com/roboplc/roboplc-io-iec60870-5/llms.txt Creates a pinger worker that periodically sends keep-alive frames or manages automatic reconnection. The pinger runs in its own thread to ensure the connection remains active. Supports different ping kinds: Test (U-frames), Ack (S-frames), and Connect (reconnect only). ```rust use roboplc_io_iec60870_5::iec104::{Client, PingKind}; use std::time::Duration; // PingKind::Test - Sends test U-frames (recommended for most use cases) let test_pinger = client.pinger(PingKind::Test, Duration::from_secs(5)); std::thread::spawn(move || test_pinger.run()); // PingKind::Ack - Sends acknowledgment S-frames let ack_pinger = client.pinger(PingKind::Ack, Duration::from_secs(10)); std::thread::spawn(move || ack_pinger.run()); // PingKind::Connect - Only reconnects if socket is dropped (minimal traffic) let connect_pinger = client.pinger(PingKind::Connect, Duration::from_secs(30)); std::thread::spawn(move || connect_pinger.run()); ``` -------------------------------- ### Receive Incoming IEC 60870-5 104 Telegrams in Rust Source: https://context7.com/roboplc/roboplc-io-iec60870-5/llms.txt Retrieves a channel receiver to process incoming IEC 60870-5 104 telegrams. The reader thread routes telegrams to this channel, distinguishing between command responses and push notifications. This snippet demonstrates how to handle I-frame telegrams and extract specific data types. ```rust use iec60870_5::{ telegram104::{Telegram104, Telegram104_I}, types::{datatype::{DataType, M_EP_TA_1}, COT}, }; use roboplc::prelude::*; use std::time::Duration; // Assuming client and reader are already created let telegram_rx = reader.get_telegram_receiver(); // Process incoming telegrams in a loop while let Ok(telegram) = telegram_rx.recv() { println!("Received telegram: {:?}", telegram); // Handle I-frame telegrams (data telegrams) if let Telegram104::I(i) = telegram { // Check for specific data type and cause of transmission if i.data_type() == DataType::M_EP_TA_1 && i.cot() == COT::Cyclic { // Process Information Object Units (IOUs) for iou in i.iou() { let v: M_EP_TA_1 = iou.value().into(); let dt = Timestamp::try_from(v.time) .unwrap() .try_into_datetime_local() .unwrap(); dbg!(v.sep.es, Duration::from(v.elapsed), dt); } } } } ``` -------------------------------- ### Handle Incoming IEC 60870-5 104 Telegrams Source: https://github.com/roboplc/roboplc-io-iec60870-5/blob/main/README.md Receives and processes incoming IEC 60870-5 104 telegrams from a connected client. It demonstrates how to deserialize telegrams and extract specific data types like M_EP_TA_1, including timestamp and elapsed duration. ```rust use iec60870_5::{ telegram104::{Telegram104, Telegram104_I}, types::{datatype::{DataType, M_EP_TA_1}, COT}, }; use roboplc::prelude::*; use std::time::Duration; while let Ok(telegram) = telegram_rx.recv() { println!("{:?}", telegram); if let Telegram104::I(i) = telegram { if i.data_type() == DataType::M_EP_TA_1 && i.cot() == COT::Cyclic { for iou in i.iou() { let v: M_EP_TA_1 = iou.value().into(); let dt = Timestamp::try_from(v.time) .unwrap() .try_into_datetime_local() .unwrap(); dbg!(v.sep.es, Duration::from(v.elapsed), dt); } } } } ``` -------------------------------- ### Client::pinger - Create Keep-Alive Pinger Worker Source: https://context7.com/roboplc/roboplc-io-iec60870-5/llms.txt Creates a pinger worker that periodically sends keep-alive frames or manages automatic reconnection to maintain an active connection. ```APIDOC ## POST /api/pinger ### Description Creates a pinger worker that periodically sends keep-alive frames or manages automatic reconnection. The pinger runs in its own thread and ensures the connection remains active with the remote server. ### Method POST ### Endpoint `/api/pinger` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **kind** (PingKind) - Required - The type of keep-alive frame to send (e.g., Test, Ack, Connect). - **interval** (Duration) - Required - The interval at which to send keep-alive frames. ### Request Example ```rust use roboplc_io_iec60870_5::iec104::{Client, PingKind}; use std::time::Duration; // PingKind::Test - Sends test U-frames (recommended for most use cases) let test_pinger = client.pinger(PingKind::Test, Duration::from_secs(5)); std::thread::spawn(move || test_pinger.run()); // PingKind::Ack - Sends acknowledgment S-frames let ack_pinger = client.pinger(PingKind::Ack, Duration::from_secs(10)); std::thread::spawn(move || ack_pinger.run()); // PingKind::Connect - Only reconnects if socket is dropped (minimal traffic) let connect_pinger = client.pinger(PingKind::Connect, Duration::from_secs(30)); std::thread::spawn(move || connect_pinger.run()); ``` ### Response #### Success Response (200) - **message** (string) - Confirmation that the pinger worker has been created and started. #### Response Example ```json { "message": "Pinger worker started successfully." } ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.