pub struct EipClient { /* private fields */ }
Expand description
High-performance EtherNet/IP client for PLC communication
This struct provides the core functionality for communicating with Allen-Bradley PLCs using the EtherNet/IP protocol. It handles connection management, session registration, and tag operations.
§Thread Safety
The EipClient
is NOT thread-safe. For multi-threaded applications:
use std::sync::Arc;
use tokio::sync::Mutex;
use rust_ethernet_ip::EipClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Create a thread-safe wrapper
let client = Arc::new(Mutex::new(EipClient::connect("192.168.1.100:44818").await?));
// Use in multiple threads
let client_clone = client.clone();
tokio::spawn(async move {
let mut client = client_clone.lock().await;
let _ = client.read_tag("Tag1").await?;
Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
});
Ok(())
}
§Performance Characteristics
Operation | Latency | Throughput | Memory |
---|---|---|---|
Connect | 100-500ms | N/A | ~8KB |
Read Tag | 1-5ms | 1,500+ ops/sec | ~2KB |
Write Tag | 2-10ms | 600+ ops/sec | ~2KB |
Batch Read | 5-20ms | 2,000+ ops/sec | ~4KB |
§Error Handling
All operations return Result<T, EtherNetIpError>
. Common errors include:
use rust_ethernet_ip::{EipClient, EtherNetIpError};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut client = EipClient::connect("192.168.1.100:44818").await?;
match client.read_tag("Tag1").await {
Ok(value) => println!("Tag value: {:?}", value),
Err(EtherNetIpError::Protocol(_)) => println!("Tag does not exist"),
Err(EtherNetIpError::Connection(_)) => println!("Lost connection to PLC"),
Err(EtherNetIpError::Timeout(_)) => println!("Operation timed out"),
Err(e) => println!("Other error: {}", e),
}
Ok(())
}
§Examples
Basic usage:
use rust_ethernet_ip::{EipClient, PlcValue};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut client = EipClient::connect("192.168.1.100:44818").await?;
// Read a boolean tag
let motor_running = client.read_tag("MotorRunning").await?;
// Write an integer tag
client.write_tag("SetPoint", PlcValue::Dint(1500)).await?;
// Read multiple tags in sequence
let tag1 = client.read_tag("Tag1").await?;
let tag2 = client.read_tag("Tag2").await?;
let tag3 = client.read_tag("Tag3").await?;
Ok(())
}
Advanced usage with error recovery:
use rust_ethernet_ip::{EipClient, PlcValue, EtherNetIpError};
use tokio::time::Duration;
async fn read_with_retry(client: &mut EipClient, tag: &str, retries: u32) -> Result<PlcValue, EtherNetIpError> {
for attempt in 0..retries {
match client.read_tag(tag).await {
Ok(value) => return Ok(value),
Err(EtherNetIpError::Connection(_)) => {
if attempt < retries - 1 {
tokio::time::sleep(Duration::from_secs(1)).await;
continue;
}
}
Err(e) => return Err(e),
}
}
Err(EtherNetIpError::Protocol("Max retries exceeded".to_string()))
}
Implementations§
Source§impl EipClient
impl EipClient
pub async fn new(addr: &str) -> Result<Self>
Sourcepub fn set_max_packet_size(&mut self, size: u32)
pub fn set_max_packet_size(&mut self, size: u32)
Sets the maximum packet size for communication
Discovers all tags in the PLC
Sourcepub async fn get_tag_metadata(&self, tag_name: &str) -> Option<TagMetadata>
pub async fn get_tag_metadata(&self, tag_name: &str) -> Option<TagMetadata>
Gets metadata for a tag
Sourcepub async fn read_tag(&mut self, tag_name: &str) -> Result<PlcValue>
pub async fn read_tag(&mut self, tag_name: &str) -> Result<PlcValue>
Reads a tag value from the PLC
This function performs a CIP read request for the specified tag. The tag’s data type is automatically determined from the PLC’s response.
§Arguments
tag_name
- The name of the tag to read
§Returns
The tag’s value as a PlcValue
enum
§Examples
use rust_ethernet_ip::{EipClient, PlcValue};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut client = EipClient::connect("192.168.1.100:44818").await?;
// Read different data types
let bool_val = client.read_tag("MotorRunning").await?;
let int_val = client.read_tag("Counter").await?;
let real_val = client.read_tag("Temperature").await?;
// Handle the result
match bool_val {
PlcValue::Bool(true) => println!("Motor is running"),
PlcValue::Bool(false) => println!("Motor is stopped"),
_ => println!("Unexpected data type"),
}
Ok(())
}
§Performance
- Latency: 1-5ms typical
- Throughput: 1,500+ ops/sec
- Network: 1 request/response cycle
§Error Handling
Common errors:
Protocol
: Tag doesn’t exist or invalid formatConnection
: Lost connection to PLCTimeout
: Operation timed out
Sourcepub async fn write_tag(&mut self, tag_name: &str, value: PlcValue) -> Result<()>
pub async fn write_tag(&mut self, tag_name: &str, value: PlcValue) -> Result<()>
Writes a value to a PLC tag
This method automatically determines the best communication method based on the data type:
- STRING values use unconnected explicit messaging with proper AB STRING format
- Other data types use standard unconnected messaging
§Arguments
tag_name
- The name of the tag to write tovalue
- The value to write
§Example
use rust_ethernet_ip::PlcValue;
client.write_tag("Counter", PlcValue::Dint(42)).await?;
client.write_tag("Message", PlcValue::String("Hello PLC".to_string())).await?;
Sourcepub async fn check_health(&self) -> bool
pub async fn check_health(&self) -> bool
Checks the health of the connection
Sourcepub async fn check_health_detailed(&mut self) -> Result<bool>
pub async fn check_health_detailed(&mut self) -> Result<bool>
Performs a more thorough health check by actually communicating with the PLC
Sourcepub async fn send_cip_request(&self, cip_request: &[u8]) -> Result<Vec<u8>>
pub async fn send_cip_request(&self, cip_request: &[u8]) -> Result<Vec<u8>>
Sends a CIP request wrapped in EtherNet/IP SendRRData command
Sourcepub async fn unregister_session(&mut self) -> Result<()>
pub async fn unregister_session(&mut self) -> Result<()>
Unregisters the EtherNet/IP session with the PLC
Sourcepub async fn execute_batch(
&mut self,
operations: &[BatchOperation],
) -> Result<Vec<BatchResult>>
pub async fn execute_batch( &mut self, operations: &[BatchOperation], ) -> Result<Vec<BatchResult>>
Executes a batch of read and write operations
This is the main entry point for batch operations. It takes a slice of
BatchOperation
items and executes them efficiently by grouping them
into optimal CIP packets based on the current BatchConfig
.
§Arguments
operations
- A slice of operations to execute
§Returns
A vector of BatchResult
items, one for each input operation.
Results are returned in the same order as the input operations.
§Performance
- Throughput: 5,000-15,000+ operations/second (vs 1,500 individual)
- Latency: 5-20ms per batch (vs 1-3ms per individual operation)
- Network efficiency: 1-5 packets vs N packets for N operations
§Examples
use rust_ethernet_ip::{EipClient, BatchOperation, PlcValue};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut client = EipClient::connect("192.168.1.100:44818").await?;
let operations = vec![
BatchOperation::Read { tag_name: "Motor1_Speed".to_string() },
BatchOperation::Read { tag_name: "Motor2_Speed".to_string() },
BatchOperation::Write {
tag_name: "SetPoint".to_string(),
value: PlcValue::Dint(1500)
},
];
let results = client.execute_batch(&operations).await?;
for result in results {
match result.result {
Ok(Some(value)) => println!("Read value: {:?}", value),
Ok(None) => println!("Write successful"),
Err(e) => println!("Operation failed: {}", e),
}
}
Ok(())
}
Reads multiple tags in a single batch operation
This is a convenience method for read-only batch operations. It’s optimized for reading many tags at once.
§Arguments
tag_names
- A slice of tag names to read
§Returns
A vector of tuples containing (tag_name, result) pairs
§Examples
use rust_ethernet_ip::EipClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut client = EipClient::connect("192.168.1.100:44818").await?;
let tags = ["Motor1_Speed", "Motor2_Speed", "Temperature", "Pressure"];
let results = client.read_tags_batch(&tags).await?;
for (tag_name, result) in results {
match result {
Ok(value) => println!("{}: {:?}", tag_name, value),
Err(e) => println!("{}: Error - {}", tag_name, e),
}
}
Ok(())
}
Writes multiple tag values in a single batch operation
This is a convenience method for write-only batch operations. It’s optimized for writing many values at once.
§Arguments
tag_values
- A slice of (tag_name, value) tuples to write
§Returns
A vector of tuples containing (tag_name, result) pairs
§Examples
use rust_ethernet_ip::{EipClient, PlcValue};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut client = EipClient::connect("192.168.1.100:44818").await?;
let writes = vec![
("SetPoint1", PlcValue::Bool(true)),
("SetPoint2", PlcValue::Dint(2000)),
("EnableFlag", PlcValue::Bool(true)),
];
let results = client.write_tags_batch(&writes).await?;
for (tag_name, result) in results {
match result {
Ok(_) => println!("{}: Write successful", tag_name),
Err(e) => println!("{}: Write failed - {}", tag_name, e),
}
}
Ok(())
}
Sourcepub fn configure_batch_operations(&mut self, config: BatchConfig)
pub fn configure_batch_operations(&mut self, config: BatchConfig)
Configures batch operation settings
This method allows fine-tuning of batch operation behavior, including performance optimizations and error handling.
§Arguments
config
- The new batch configuration to use
§Examples
use rust_ethernet_ip::{EipClient, BatchConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut client = EipClient::connect("192.168.1.100:44818").await?;
let config = BatchConfig {
max_operations_per_packet: 50,
max_packet_size: 1500,
packet_timeout_ms: 5000,
continue_on_error: false,
optimize_packet_packing: true,
};
client.configure_batch_operations(config);
Ok(())
}
Sourcepub fn get_batch_config(&self) -> &BatchConfig
pub fn get_batch_config(&self) -> &BatchConfig
Gets current batch operation configuration
Sourcepub async fn write_ab_string_components(
&mut self,
tag_name: &str,
value: &str,
) -> Result<()>
pub async fn write_ab_string_components( &mut self, tag_name: &str, value: &str, ) -> Result<()>
Writes a string value using Allen-Bradley UDT component access This writes to TestString.LEN and TestString.DATA separately
Sourcepub async fn write_ab_string_udt(
&mut self,
tag_name: &str,
value: &str,
) -> Result<()>
pub async fn write_ab_string_udt( &mut self, tag_name: &str, value: &str, ) -> Result<()>
Writes a string using a single UDT write with proper AB STRING format
Sourcepub async fn write_string_connected(
&mut self,
tag_name: &str,
value: &str,
) -> Result<()>
pub async fn write_string_connected( &mut self, tag_name: &str, value: &str, ) -> Result<()>
Writes a string using connected explicit messaging
Sourcepub async fn write_string_unconnected(
&mut self,
tag_name: &str,
value: &str,
) -> Result<()>
pub async fn write_string_unconnected( &mut self, tag_name: &str, value: &str, ) -> Result<()>
Writes a string using unconnected explicit messaging with proper AB STRING format
This method uses standard unconnected messaging instead of connected messaging and implements the proper Allen-Bradley STRING structure as described in the provided information about Len, MaxLen, and Data[82] format.
Sourcepub async fn write_string(&mut self, tag_name: &str, value: &str) -> Result<()>
pub async fn write_string(&mut self, tag_name: &str, value: &str) -> Result<()>
Write a string value to a PLC tag using unconnected messaging
§Arguments
tag_name
- The name of the tag to write tovalue
- The string value to write (max 82 characters)
§Returns
Ok(())
if the write was successfulErr(EtherNetIpError)
if the write failed
§Errors
StringTooLong
- If the string is longer than 82 charactersInvalidString
- If the string contains invalid charactersTagNotFound
- If the tag doesn’t existWriteError
- If the write operation fails
Sourcepub async fn subscribe_to_tag(
&self,
tag_path: &str,
options: SubscriptionOptions,
) -> Result<()>
pub async fn subscribe_to_tag( &self, tag_path: &str, options: SubscriptionOptions, ) -> Result<()>
Subscribes to a tag for real-time updates