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 |
§Known Limitations
The following operations are not supported due to PLC firmware limitations:
§UDT Array Element Member Writes
Cannot write directly to UDT array element members (e.g., gTestUDT_Array[0].Member1_DINT).
This is a PLC firmware limitation, not a library bug. The PLC returns CIP Error 0x2107
(Vendor Specific Error) when attempting to write to such paths.
§STRING Tags and STRING Members in UDTs
Cannot write directly to STRING tags or STRING members in UDTs.
This is a PLC firmware limitation (CIP Error 0x2107). Both simple STRING tags
(e.g., gTest_STRING) and STRING members within UDTs (e.g., gTestUDT.Member5_String)
cannot be written directly. STRING values must be written as part of the entire UDT
structure, not as individual tags or members.
What works:
- ✅ Reading UDT array element members:
gTestUDT_Array[0].Member1_DINT(read) - ✅ Writing entire UDT array elements:
gTestUDT_Array[0](write full UDT) - ✅ Writing UDT members (non-STRING):
gTestUDT.Member1_DINT(write DINT/REAL/BOOL/INT members) - ✅ Writing array elements:
gArray[5](write element of simple array) - ✅ Reading STRING tags:
gTest_STRING(read) - ✅ Reading STRING members in UDTs:
gTestUDT.Member5_String(read)
What doesn’t work:
- ❌ Writing UDT array element members:
gTestUDT_Array[0].Member1_DINT(write) - ❌ Writing program-scoped UDT array element members:
Program:TestProgram.gTestUDT_Array[0].Member1_DINT(write) - ❌ Writing simple STRING tags:
gTest_STRING(write) - PLC limitation - ❌ Writing program-scoped STRING tags:
Program:TestProgram.gTest_STRING(write) - PLC limitation - ❌ Writing STRING members in UDTs:
gTestUDT.Member5_String(write) - must write entire UDT - ❌ Writing program-scoped STRING members:
Program:TestProgram.gTestUDT.Member5_String(write) - must write entire UDT
Workaround: To modify a UDT array element member, read the entire UDT array element, modify the member in memory, then write the entire UDT array element back:
use rust_ethernet_ip::{PlcValue, UdtData};
// Read the entire UDT array element
let udt_value = client.read_tag("gTestUDT_Array[0]").await?;
if let PlcValue::Udt(mut udt_data) = udt_value {
let udt_def = client.get_udt_definition("gTestUDT_Array").await?;
// Convert UdtDefinition to UserDefinedType
let mut user_def = rust_ethernet_ip::udt::UserDefinedType::new(udt_def.name.clone());
for member in &udt_def.members {
user_def.add_member(member.clone());
}
let mut members = udt_data.parse(&user_def)?;
// Modify the member
members.insert("Member1_DINT".to_string(), PlcValue::Dint(100));
// Write the entire UDT array element back
let modified_udt = UdtData::from_hash_map(&members, &user_def, udt_data.symbol_id)?;
client.write_tag("gTestUDT_Array[0]", PlcValue::Udt(modified_udt)).await?;
}§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;
}
return Err(EtherNetIpError::Protocol("Max retries exceeded".to_string()));
}
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 (including hierarchical UDT members)
Sourcepub async fn discover_udt_members(
&mut self,
udt_name: &str,
) -> Result<Vec<(String, TagMetadata)>>
pub async fn discover_udt_members( &mut self, udt_name: &str, ) -> Result<Vec<(String, TagMetadata)>>
Discovers UDT members for a specific structure
Sourcepub async fn get_udt_definition_cached(
&self,
udt_name: &str,
) -> Option<UdtDefinition>
pub async fn get_udt_definition_cached( &self, udt_name: &str, ) -> Option<UdtDefinition>
Gets cached UDT definition
Sourcepub async fn list_udt_definitions(&self) -> Vec<String>
pub async fn list_udt_definitions(&self) -> Vec<String>
Lists all cached UDT definitions
Discovers all tags with full attributes This method queries the PLC for all available tags and their detailed attributes
Discovers program-scoped tags This method discovers tags within a specific program scope
Sourcepub async fn list_cached_tag_attributes(&self) -> Vec<String>
pub async fn list_cached_tag_attributes(&self) -> Vec<String>
Lists all cached tag attributes
Sourcepub async fn clear_caches(&mut self)
pub async fn clear_caches(&mut self)
Clears all caches (UDT definitions, templates, tag attributes)
Sourcepub async fn with_route_path(addr: &str, route: RoutePath) -> Result<Self>
pub async fn with_route_path(addr: &str, route: RoutePath) -> Result<Self>
Creates a new client with a specific route path
Sourcepub async fn connect_with_stream<S>(
stream: S,
route: Option<RoutePath>,
) -> Result<Self>where
S: EtherNetIpStream + 'static,
pub async fn connect_with_stream<S>(
stream: S,
route: Option<RoutePath>,
) -> Result<Self>where
S: EtherNetIpStream + 'static,
Connect to a PLC using a custom stream
This method allows you to provide your own stream implementation, enabling:
- Wrapping streams for metrics/observability (bytes in/out)
- Applying custom socket options (keepalive, timeouts, bind local address)
- Reusing pre-established tunnels/connections
- Using in-memory streams for deterministic testing
§Arguments
stream- Any stream that implementsAsyncRead + AsyncWrite + Unpin + Send
§Example
use rust_ethernet_ip::EipClient;
use std::net::SocketAddr;
use tokio::net::TcpStream;
// Create a custom stream with socket options
let addr: SocketAddr = "192.168.1.100:44818".parse()?;
let stream = TcpStream::connect(addr).await?;
stream.set_nodelay(true)?;
// Connect using the custom stream
let client = EipClient::connect_with_stream(stream, None).await?;Sourcepub fn set_route_path(&mut self, route: RoutePath)
pub fn set_route_path(&mut self, route: RoutePath)
Sets the route path for the client
Sourcepub fn get_route_path(&self) -> Option<&RoutePath>
pub fn get_route_path(&self) -> Option<&RoutePath>
Gets the current route path
Sourcepub fn clear_route_path(&mut self)
pub fn clear_route_path(&mut self)
Removes the route path (uses direct connection)
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.
v0.6.0: For UDT tags, this returns PlcValue::Udt(UdtData) with symbol_id
and raw bytes. Use UdtData::parse() with a UDT definition to access members.
§Arguments
tag_name- The name of the tag to read
§Returns
The tag’s value as a PlcValue enum. For UDTs, this is PlcValue::Udt(UdtData).
§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?;
// Read a UDT (v0.6.0: returns UdtData)
let udt_val = client.read_tag("MyUDT").await?;
if let PlcValue::Udt(udt_data) = udt_val {
let udt_def = client.get_udt_definition("MyUDT").await?;
// Convert UdtDefinition to UserDefinedType
let mut user_def = rust_ethernet_ip::udt::UserDefinedType::new(udt_def.name.clone());
for member in &udt_def.members {
user_def.add_member(member.clone());
}
let members = udt_data.parse(&user_def)?;
println!("UDT has {} members", members.len());
}
// 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 fn build_write_array_request_with_index(
&self,
base_array_name: &str,
start_index: u32,
element_count: u16,
data_type: u16,
data: &[u8],
) -> Result<Vec<u8>>
pub fn build_write_array_request_with_index( &self, base_array_name: &str, start_index: u32, element_count: u16, data_type: u16, data: &[u8], ) -> Result<Vec<u8>>
Builds a CIP Write Tag Service request for array elements with element addressing
This method uses proper CIP element addressing (0x28/0x29/0x2A segments) in the Request Path to write to specific array elements or ranges.
Reference: 1756-PM020, Pages 603-611, 855-867 (Writing to Array Element)
§Arguments
base_array_name- Base name of the array (e.g., “MyArray” for “MyArray[10]”)start_index- Starting element index (0-based)element_count- Number of elements to writedata_type- CIP data type code (e.g., 0x00C4 for DINT)data- Raw bytes of the data to write
§Example
Writing value 0x12345678 to element 10 of array “MyArray”:
let data = 0x12345678u32.to_le_bytes();
let request = client.build_write_array_request_with_index(
"MyArray", 10, 1, 0x00C4, &data
)?;Sourcepub async fn read_udt_chunked(&mut self, tag_name: &str) -> Result<PlcValue>
pub async fn read_udt_chunked(&mut self, tag_name: &str) -> Result<PlcValue>
Reads a UDT with advanced chunked reading to handle large structures
v0.6.0: Returns PlcValue::Udt(UdtData) with symbol_id and raw bytes.
Use UdtData::parse() with a UDT definition to access individual members.
This method uses multiple strategies to handle large UDTs that exceed the maximum packet size, including intelligent chunking and member discovery.
§Arguments
tag_name- The name of the UDT tag to read
§Returns
PlcValue::Udt(UdtData) containing the symbol_id and raw data bytes
§Example
let udt_value = client.read_udt_chunked("Part_Data").await?;
if let rust_ethernet_ip::PlcValue::Udt(udt_data) = udt_value {
println!("UDT symbol_id: {}, data size: {} bytes", udt_data.symbol_id, udt_data.data.len());
// Parse members if needed
let udt_def = client.get_udt_definition("Part_Data").await?;
// Convert UdtDefinition to UserDefinedType
let mut user_def = rust_ethernet_ip::udt::UserDefinedType::new(udt_def.name.clone());
for member in &udt_def.members {
user_def.add_member(member.clone());
}
let members = udt_data.parse(&user_def)?;
}Sourcepub async fn read_udt_member_by_offset(
&mut self,
udt_name: &str,
member_offset: usize,
member_size: usize,
data_type: u16,
) -> Result<PlcValue>
pub async fn read_udt_member_by_offset( &mut self, udt_name: &str, member_offset: usize, member_size: usize, data_type: u16, ) -> Result<PlcValue>
Reads a specific UDT member by offset
This method reads a specific member of a UDT by calculating its offset and reading only that portion of the UDT.
§Arguments
udt_name- The name of the UDT tagmember_offset- The byte offset of the member in the UDTmember_size- The size of the member in bytesdata_type- The data type of the member (0x00C1 for BOOL, 0x00CA for REAL, etc.)
§Example
let member_value = client.read_udt_member_by_offset("MyUDT", 0, 1, 0x00C1).await?;
println!("Member value: {:?}", member_value);Sourcepub async fn write_udt_member_by_offset(
&mut self,
udt_name: &str,
member_offset: usize,
member_size: usize,
data_type: u16,
value: PlcValue,
) -> Result<()>
pub async fn write_udt_member_by_offset( &mut self, udt_name: &str, member_offset: usize, member_size: usize, data_type: u16, value: PlcValue, ) -> Result<()>
Writes a specific UDT member by offset
This method writes a specific member of a UDT by calculating its offset and writing only that portion of the UDT.
§Arguments
udt_name- The name of the UDT tagmember_offset- The byte offset of the member in the UDTmember_size- The size of the member in bytesdata_type- The data type of the member (0x00C1 for BOOL, 0x00CA for REAL, etc.)value- The value to write
§Example
client.write_udt_member_by_offset("MyUDT", 0, 1, 0x00C1, PlcValue::Bool(true)).await?;Sourcepub async fn get_udt_definition(
&mut self,
udt_name: &str,
) -> Result<UdtDefinition>
pub async fn get_udt_definition( &mut self, udt_name: &str, ) -> Result<UdtDefinition>
Gets UDT definition from the PLC This method queries the PLC for the UDT structure and caches it for future use
Sourcepub async fn get_tag_attributes(
&mut self,
tag_name: &str,
) -> Result<TagAttributes>
pub async fn get_tag_attributes( &mut self, tag_name: &str, ) -> Result<TagAttributes>
Gets tag attributes from the PLC
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
v0.6.0: For UDT tags, pass PlcValue::Udt(UdtData). The symbol_id must be set
(typically obtained by reading the UDT first). If symbol_id is 0, the method will
attempt to read tag attributes to get the symbol_id automatically.
§Arguments
tag_name- The name of the tag to write tovalue- The value to write. For UDTs, usePlcValue::Udt(UdtData).
§Example
use rust_ethernet_ip::{PlcValue, UdtData};
// Write simple types
client.write_tag("Counter", PlcValue::Dint(42)).await?;
client.write_tag("Message", PlcValue::String("Hello PLC".to_string())).await?;
// Write UDT (v0.6.0: read first to get symbol_id, then modify and write)
let udt_value = client.read_tag("MyUDT").await?;
if let PlcValue::Udt(mut udt_data) = udt_value {
let udt_def = client.get_udt_definition("MyUDT").await?;
// Convert UdtDefinition to UserDefinedType
let mut user_def = rust_ethernet_ip::udt::UserDefinedType::new(udt_def.name.clone());
for member in &udt_def.members {
user_def.add_member(member.clone());
}
let mut members = udt_data.parse(&user_def)?;
members.insert("Member1".to_string(), PlcValue::Dint(100));
let modified_udt = UdtData::from_hash_map(&members, &user_def, udt_data.symbol_id)?;
client.write_tag("MyUDT", PlcValue::Udt(modified_udt)).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 fn build_element_id_segment(&self, index: u32) -> Vec<u8> ⓘ
pub fn build_element_id_segment(&self, index: u32) -> Vec<u8> ⓘ
Builds an Element ID segment for array element addressing
Reference: 1756-PM020, Pages 603-611, 870-890 (Element ID Segment Size Selection)
Element ID segments use different sizes based on index value:
- 0-255: 8-bit Element ID (0x28 + 1 byte value)
- 256-65535: 16-bit Element ID (0x29 0x00 + 2 bytes low, high)
- 65536+: 32-bit Element ID (0x2A 0x00 + 4 bytes lowest to highest)
Sourcepub fn build_base_tag_path(&self, tag_name: &str) -> Vec<u8> ⓘ
pub fn build_base_tag_path(&self, tag_name: &str) -> Vec<u8> ⓘ
Builds base tag path without array element addressing
Extracts the base tag name from array notation (e.g., “MyArray[5]” -> “MyArray”) Reference: 1756-PM020, Page 894-909 (ANSI Extended Symbol Segment Construction)
Sourcepub fn build_read_array_request(
&self,
base_array_name: &str,
start_index: u32,
element_count: u16,
) -> Vec<u8> ⓘ
pub fn build_read_array_request( &self, base_array_name: &str, start_index: u32, element_count: u16, ) -> Vec<u8> ⓘ
Builds a CIP Read Tag Service request for array elements with element addressing
This method uses proper CIP element addressing (0x28/0x29/0x2A segments) in the Request Path to read specific array elements or ranges.
Reference: 1756-PM020, Pages 603-611, 815-851 (Array Element Addressing Examples)
§Arguments
base_array_name- Base name of the array (e.g., “MyArray” for “MyArray[10]”)start_index- Starting element index (0-based)element_count- Number of elements to read
§Example
Reading elements 10-14 of array “MyArray” (5 elements):
let request = client.build_read_array_request("MyArray", 10, 5);This generates:
- Request Path: [0x91] “MyArray” [0x28] [0x0A] (element 10)
- Request Data: [0x05 0x00] (5 elements)
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