跳到主要内容

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));
闭包必须 Send + Sync + 'static

回调在 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)?;
ARP 表条目设置不支持

DLL 未导出 EOESetArpEntry, Rust SDK 不提供 set_arp_entry 方法。本机 ARP 学习与解析通过 ArpCache 完成, 不写入从站。