跳到主要内容

EoE (Ethernet over EtherCAT)

EoE 协议通过 EtherCAT 总线实现以太网通信,支持配置 IP/MAC/DNS 地址和以太网帧收发,符合 ETG.1000.6 和 ETG.1020 标准。

通过 slave.eoe() 获取 EoEInstance 实例 —— 该方法直接返回 EoEInstance(不是 Option),是否真正支持 EoE 可用 eoe.is_supported() 判断。

IP / MAC / DNS 配置

所有 getter / setter 都是邮箱事务,因此都带 timeout_ms 参数并返回 Result。IP 类参数用 u32(网络字节序),MAC 用 [u8; 6]

方法类型类别读写说明
ip(timeout_ms)Result<u32>网络配置只读IP 地址 (u32, 网络字节序)
subnet(timeout_ms)Result<u32>网络配置只读子网掩码
gateway(timeout_ms)Result<u32>网络配置只读默认网关
set_ip(ip, subnet, gateway, timeout_ms)Result<()>网络配置只写一次写入 IP/子网/网关
mac(timeout_ms)Result<[u8; 6]>网络配置只读MAC 地址 (6 字节)
mac_string(timeout_ms)Result<String>网络配置只读MAC 地址 ("AA:BB:CC:DD:EE:FF" 格式)
set_mac(&mac, timeout_ms)Result<()>网络配置只写写入 MAC 地址
dns(timeout_ms)Result<u32>网络配置只读DNS 服务器地址
set_dns(dns_ip, dns_name, timeout_ms)Result<()>网络配置只写写入 DNS 服务器 IP + 名称

示例:

let eoe = slave.eoe();
if !eoe.is_supported() {
println!("从站不支持 EoE");
return Ok(());
}

// 读取 (返回 Result, 需传超时)
println!("IP: 0x{:08X}", eoe.ip(5000)?);
println!("MAC: {}", eoe.mac_string(5000)?);

// 写入: set_ip 一次写 IP/子网/网关 (u32, 网络字节序)
let ip = u32::from_be_bytes([192, 168, 1, 100]);
let subnet = u32::from_be_bytes([255, 255, 255, 0]);
let gateway = u32::from_be_bytes([192, 168, 1, 1]);
eoe.set_ip(ip, subnet, gateway, 5000)?;

eoe.set_mac(&[0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF], 5000)?;
eoe.set_dns(u32::from_be_bytes([8, 8, 8, 8]), "dns.google", 5000)?;

地址过滤器 (ETG.1020)

get_address_filters()

pub fn get_address_filters(&self, timeout_ms: i32) -> Result<Vec<[u8; 6]>>

获取从站当前配置的 MAC 地址过滤器列表(最多 16 条),每条以 [u8; 6] 表示。

set_address_filters_batch()

pub fn set_address_filters_batch(&self, filters: &[[u8; 6]], timeout_ms: i32) -> Result<()>

一次性替换从站全部 MAC 地址过滤器。

示例:

// 替换为只允许两个上位机
eoe.set_address_filters_batch(&[
[0x00, 0x1B, 0x21, 0xAA, 0xBB, 0xCC],
[0x00, 0x1B, 0x21, 0xDD, 0xEE, 0xFF],
], 5000)?;

let filters = eoe.get_address_filters(5000)?;
for f in &filters {
println!("过滤器: {:02X?}", f);
}

以太网帧收发

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) — 超时时间(毫秒)
pub struct EoEPingResult {
pub success: bool, // 是否成功
pub round_trip_time_ms: f64, // 往返时间(毫秒)
pub target_address: String, // 目标地址
pub ttl: u8, // 生存时间
pub error_message: String, // 错误信息
}

示例:

let target = u32::from_be_bytes([192, 168, 1, 1]);
let result = eoe.ping(target, 5000);
if result.success {
println!("Ping 成功: {:.1}ms", result.round_trip_time_ms);
} else {
println!("Ping 失败: {}", result.error_message);
}

本地 ARP 缓存

EoEInstance 内置一份本机的 ARP 缓存(从收到的帧学习 IP↔MAC),不向从站写入。DLL 未导出 EOESetArpEntry,因此 SDK 不提供向从站设置 ARP 条目的方法。

clear_arp_cache()

pub fn clear_arp_cache(&mut self)

清空本地 ARP 缓存(重新创建空缓存)。注意需要 &mut 接收者。

完整示例

网络配置与 Ping

let eoe = slave.eoe();
if !eoe.is_supported() {
return Ok(());
}

println!("当前 IP: 0x{:08X}", eoe.ip(5000)?);
println!("当前 MAC: {}", eoe.mac_string(5000)?);

let ip = u32::from_be_bytes([10, 0, 0, 100]);
let subnet = u32::from_be_bytes([255, 255, 255, 0]);
let gateway = u32::from_be_bytes([10, 0, 0, 1]);
eoe.set_ip(ip, subnet, gateway, 5000)?;
eoe.set_dns(gateway, "", 5000)?;

let ping = eoe.ping(gateway, 5000);
if ping.success {
println!("Ping 成功: {:.1}ms", ping.round_trip_time_ms);
}

以太网帧收发

let eoe = slave.eoe();
if !eoe.is_supported() {
return Ok(());
}

// send_frame / receive_frame 返回 Result
let frame = vec![0xFF; 64];
eoe.send_frame(&frame, 5000)?;

let data = eoe.receive_frame(5000)?;
println!("收到帧: {} 字节", data.len());

异步接收 Hook (frame_received)

对应 C# EoEInstance.FrameReceived 事件。SDK 把底层推送的帧异步转发到用户闭包, 多帧可并发到达, 同主站只保留最近一次注册的回调。

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.master_dc_time();
eoe.send_frame_with_timestamp(&frame, dc_ns, 1000)?;
ARP 表条目设置不支持

DLL 未导出 EOESetArpEntry, Rust SDK 不提供向从站设置 ARP 条目的方法。本机 ARP 学习与解析由 EoEInstance 内置的本地 ARP 缓存完成 (见 本地 ARP 缓存), 不写入从站。