EoE (Ethernet over EtherCAT)
EoE 协议通过 EtherCAT 总线实现以太网通信,支持配置 IP/MAC/DNS 地址和以太网帧收发,符合 ETG.1000.6 和 ETG.1020 标准。
通过 slave.eoe() 获取 EoEInstance 实例。
属性
读写属性实时操作:getter 从设备读取当前值,setter 校验格式后立即写入设备。
| 方法 | 类型 | 访问 | 说明 |
|---|---|---|---|
| can_send_frame() | bool | 只读 | 是否支持发送以太网帧 |
| can_receive_frame() | bool | 只读 | 是否支持接收以太网帧 |
| can_set_ip() | bool | 只读 | 是否支持设置 IP 参数 |
| can_get_ip() | bool | 只读 | 是否支持获取 IP 参数 |
| ip() / set_ip() | Option<Ipv4Addr> | 读写 | IP 地址(仅 IPv4) |
| subnet() / set_subnet() | Option<Ipv4Addr> | 读写 | 子网掩码 |
| gateway() / set_gateway() | Option<Ipv4Addr> | 读写 | 默认网关 |
| mac() / set_mac() | Option<String> | 读写 | MAC 地址(格式 "AA:BB:CC:DD:EE:FF") |
| dns() / set_dns() | Option<Ipv4Addr> | 读写 | DNS 服务器地址 |
| dns_name() / set_dns_name() | Option<String> | 读写 | DNS 名称 |
示例:
use std::net::Ipv4Addr;
let eoe = slave.eoe().ok_or("不支持 EoE")?;
// 读取
println!("IP: {:?}", eoe.ip());
println!("MAC: {:?}", eoe.mac());
// 写入
eoe.set_ip(Some(Ipv4Addr::new(192, 168, 1, 100)));
eoe.set_subnet(Some(Ipv4Addr::new(255, 255, 255, 0)));
eoe.set_gateway(Some(Ipv4Addr::new(192, 168, 1, 1)));
eoe.set_mac(Some("AA:BB:CC:DD:EE:FF".to_string()));
eoe.set_dns(Some(Ipv4Addr::new(8, 8, 8, 8)));
eoe.set_dns_name(Some("dns.google".to_string()));
地址过滤器 (ETG.1020)
get_address_filters()
pub fn get_address_filters(&self, timeout_ms: i32) -> Result<Vec<String>, DarraError>
获取 MAC 地址过滤器列表。
set_address_filters()
pub fn set_address_filters(&self, filters: &[&str], timeout_ms: i32) -> Result<(), DarraError>
设置地址过滤器列表。
add_address_filter() / remove_address_filter() / clear_address_filters()
pub fn add_address_filter(&self, mac: &str, timeout_ms: i32) -> Result<(), DarraError>
pub fn remove_address_filter(&self, mac: &str, timeout_ms: i32) -> Result<(), DarraError>
pub fn clear_address_filters(&self, timeout_ms: i32) -> Result<(), DarraError>
示例:
eoe.add_address_filter("AA:BB:CC:DD:EE:FF", 5000)?;
let filters = eoe.get_address_filters(5000)?;
for f in &filters {
println!("过滤器: {}", f);
}
eoe.clear_address_filters(5000)?;
以太网帧收发
send_frame()
pub fn send_frame(&self, frame: &[u8], timeout_ms: i32) -> Result<()>
发送以太网帧(端口号在创建 EoEInstance 时指定)。
参数:
frame(&[u8]) — 以太网帧数据timeout_ms(i32) — 超时时间(毫秒)
返回值:
Result<()>— 成功或错误
receive_frame()
pub fn receive_frame(&self, timeout_ms: i32) -> Result<Vec<u8>>
接收以太网帧。
参数:
timeout_ms(i32) — 超时时间(毫秒)
返回值:
Result<Vec<u8>>— 接收到的帧数据
示例:
// 发送帧
let frame = vec![0xFF; 64]; // 示例帧数据
eoe.send_frame(&frame, 5000)?;
// 接收帧
let data = eoe.receive_frame(5000)?;
println!("收到帧: {} 字节", data.len());
Ping 测试
pub fn ping(&self, target_ip: u32, timeout_ms: i32) -> EoEPingResult
通过 EoE 发送 ICMP Ping 并等待响应。该方法不返回 Result,失败信息包含在 EoEPingResult 中。
参数:
target_ip(u32) — 目标 IP 地址(网络字节序,如 192.168.1.1 = 0xC0A80101)timeout_ms(i32) — 超时时间(毫秒)ttl(u8) — 生存时间
pub struct EoEPingResult {
pub success: bool, // 是否成功
pub round_trip_time_ms: f64, // 往返时间(毫秒)
pub target_address: String, // 目标地址
pub ttl: u8, // 生存时间
pub error_message: String, // 错误信息
}
示例:
let result = eoe.ping(Ipv4Addr::new(192, 168, 1, 1), 5000, 64)?;
if result.success {
println!("Ping 成功: {:.1}ms", result.round_trip_time_ms);
} else {
println!("Ping 失败: {}", result.error_message);
}
ARP 缓存管理
set_arp_entry()
pub fn set_arp_entry(&self, ip: Ipv4Addr, mac: &[u8; 6])
设置 ARP 缓存条目,将指定 IP 地址与 MAC 地址绑定。
参数:
ip(Ipv4Addr) — IP 地址mac(&[u8; 6]) — MAC 地址(6 字节)
clear_arp_cache()
pub fn clear_arp_cache(&self)
清除从站的 ARP 缓存。
示例:
// 设置静态 ARP 条目
eoe.set_arp_entry(
Ipv4Addr::new(192, 168, 1, 1),
&[0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]
);
// 清除 ARP 缓存
eoe.clear_arp_cache();
完整示例
网络配置与 Ping
use std::net::Ipv4Addr;
let eoe = slave.eoe().ok_or("不支持 EoE")?;
println!("当前 IP: {:?}", eoe.ip());
println!("当前 MAC: {:?}", eoe.mac());
eoe.set_ip(Some(Ipv4Addr::new(10, 0, 0, 100)));
eoe.set_subnet(Some(Ipv4Addr::new(255, 255, 255, 0)));
eoe.set_gateway(Some(Ipv4Addr::new(10, 0, 0, 1)));
eoe.set_dns(Some(Ipv4Addr::new(10, 0, 0, 1)));
let ping = eoe.ping(Ipv4Addr::new(10, 0, 0, 1), 5000, 64)?;
if ping.success {
println!("Ping 成功: {:.1}ms", ping.round_trip_time_ms);
}
以太网帧收发
let eoe = slave.eoe().ok_or("不支持 EoE")?;
if eoe.can_send_frame() {
let frame = vec![0xFF; 64];
eoe.send_frame(&frame, 5000)?;
}
if eoe.can_receive_frame() {
if let Some(data) = eoe.receive_frame(5000)? {
println!("收到帧: {} 字节", data.len());
}
}
异步接收 Hook (frame_received)
对应 C# EoEInstance.FrameReceived 事件。SDK 通过全局回调表 + native trampoline 把 DLL 推送的帧异步转发到用户闭包, 多帧可并发到达, 同主站只保留最近一次注册的回调。
set_receive_hook
pub fn set_receive_hook<F>(&self, callback: F) -> Result<()>
where
F: Fn(u16, u16, &[u8]) + Send + Sync + 'static
注册接收回调, 闭包参数 (master_index, slave_index, &frame_bytes)。同一 master_index 多次调用会覆盖之前的闭包 (与 C# 单回调语义一致)。DLL 未导出 EOESetReceiveHook 时返回 DarraError::Other 而非 panic。
clear_receive_hook
pub fn clear_receive_hook(&self) -> Result<()>
注销本主站的接收回调, 同时通知 DLL EOEClearReceiveHook。
示例:
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
let frame_count = Arc::new(AtomicU64::new(0));
let counter = Arc::clone(&frame_count);
eoe.set_receive_hook(move |master, slave, frame| {
counter.fetch_add(1, Ordering::Relaxed);
println!("[Master {} Slave {}] 收到 {} 字节", master, slave, frame.len());
})?;
// ... 业务逻辑 ...
eoe.clear_receive_hook()?;
println!("总共收到 {} 帧", frame_count.load(Ordering::Relaxed));
回调在 DLL 工作线程执行, 闭包内引用的数据需通过 Arc<Mutex<_>> / AtomicXxx 等同步原语共享; 不要捕获栈上引用。
地址过滤器
| 方法 | 说明 |
|---|---|
get_address_filters(timeout_ms) | 读取当前从站配置的 MAC 过滤器列表 (最多 16 条) |
set_address_filters_batch(&filters, timeout_ms) | 一次性替换全部过滤器 |
clear_arp_cache() | 清空本地 ARP 缓存 (重新创建空缓存) |
// 替换为只允许两个上位机
eoe.set_address_filters_batch(&[
[0x00, 0x1B, 0x21, 0xAA, 0xBB, 0xCC],
[0x00, 0x1B, 0x21, 0xDD, 0xEE, 0xFF],
], 1000)?;
let filters = eoe.get_address_filters(1000)?;
println!("当前过滤器数: {}", filters.len());
带时间戳帧发送
send_frame_with_timestamp
pub fn send_frame_with_timestamp(&self, frame: &[u8], timestamp: u64, timeout_ms: i32) -> Result<()>
ETG.1000.6 §5.7 TimeAppended: 在帧末尾追加 4 字节 (Unsigned32) 时间戳, 取 timestamp 低 32 位 (DC 时间纳秒)。
let dc_ns = master.diagnostics_info().dc_time();
eoe.send_frame_with_timestamp(&frame, dc_ns, 1000)?;
DLL 未导出 EOESetArpEntry, Rust SDK 不提供 set_arp_entry 方法。本机 ARP 学习与解析通过 ArpCache 完成, 不写入从站。