VoE (Vendor over EtherCAT)
VoE 协议用于厂商自定义数据传输,支持 ETG.1000.6 Mailbox 通信规范(Mailbox Type 0x0F)。每个厂商可以定义自己的 VoE 头格式和数据结构。
通过 slave.voe_instance() 获取 VoEInstance 实例,或通过 VoEInstance::new(master_index, slave_index) 直接构造。
属性
| 项目 | 类型 | 说明 |
|---|---|---|
| default_timeout_ms (公共字段) | i32 | 默认超时时间(毫秒),默认 500 |
| VOE_HEADER_SIZE (常量) | usize | VoE 头部大小(6 字节:VendorID 4 + VendorType 2) |
| is_supported() | bool | 从站是否支持 VoE 协议 |
基本操作
send()
pub fn send(&self, vendor_id: u32, vendor_type: u16, data: &[u8]) -> Result<()>
发送 VoE 数据到从站(使用 default_timeout_ms)。
参数:
vendor_id(u32) — 厂商 ID (4字节)vendor_type(u16) — 厂商类型 (2字节)data(&[u8]) — 要发送的数据
返回值:
Result<()>— 成功或错误
send_with_timeout()
pub fn send_with_timeout(&self, vendor_id: u32, vendor_type: u16,
data: &[u8], timeout_ms: i32) -> Result<()>
发送 VoE 数据到从站(指定超时)。
示例:
let voe = slave.voe_instance();
voe.send(0x00000002, 0x0001, &[0x01, 0x02, 0x03])?;
receive()
pub fn receive(&self) -> Result<VoEResponse>
从从站接收 VoE 数据(使用 default_timeout_ms)。
返回值:
Result<VoEResponse>— VoE 响应对象
receive_with_timeout()
pub fn receive_with_timeout(&self, timeout_ms: i32) -> Result<VoEResponse>
从从站接收 VoE 数据(指定超时)。
send_and_receive()
pub fn send_and_receive(&self, vendor_id: u32, vendor_type: u16, data: &[u8]) -> Result<VoEResponse>
发送 VoE 数据并等待响应(使用 default_timeout_ms)。
参数:
vendor_id(u32) — 厂商 ID (4字节)vendor_type(u16) — 厂商类型 (2字节)data(&[u8]) — 要发送的数据
返回值:
Result<VoEResponse>— VoE 响应对象
相关结构:
pub struct VoEResponse {
pub vendor_id: u32, // 厂商 ID
pub vendor_type: u16, // 厂商类型
pub data: Vec<u8>, // 响应数据
}
impl VoEResponse {
/// 数据格式化为十六进制字符串(如 "01 02 03")
pub fn to_hex_string(&self) -> String;
}
impl std::fmt::Display for VoEResponse {
// 输出: "VoE Response: VendorID=0x00000002, VendorType=0x0001, DataLength=3"
}
示例:
// send_and_receive 取 3 个参数 (使用 default_timeout_ms), 直接返回 VoEResponse
let resp = voe.send_and_receive(0x00000002, 0x0001, &[0x10])?;
println!("厂商ID: 0x{:08X}", resp.vendor_id);
println!("数据: {}", resp.to_hex_string());
原始帧操作
send_raw()
pub fn send_raw(&self, frame_data: &[u8]) -> Result<()>
发送 VoE 原始帧(使用 default_timeout_ms,用户自行组织帧格式,包括 VoE 头)。
参数:
frame_data(&[u8]) — 完整的 VoE 帧数据(包括 VoE 头部)
返回值:
Result<()>— 成功或错误
send_raw_with_timeout()
pub fn send_raw_with_timeout(&self, frame_data: &[u8], timeout_ms: i32) -> Result<()>
发送 VoE 原始帧(指定超时)。
receive_raw()
pub fn receive_raw(&self) -> Result<Vec<u8>>
接收 VoE 原始帧(使用 default_timeout_ms)。
返回值:
Result<Vec<u8>>— 原始帧数据
receive_raw_with_timeout()
pub fn receive_raw_with_timeout(&self, timeout_ms: i32) -> Result<Vec<u8>>
接收 VoE 原始帧(指定超时)。
send_raw_and_receive()
pub fn send_raw_and_receive(&self, frame_data: &[u8]) -> Result<Vec<u8>>
发送原始帧并等待响应(使用 default_timeout_ms)。
参数:
frame_data(&[u8]) — 完整的 VoE 帧数据
返回值:
Result<Vec<u8>>— 响应的原始帧数据
示例:
// 构建自定义 VoE 帧
let frame = VoEInstance::build_frame(0x00000002, 0x0001, &[0x01, 0x02]);
voe.send_raw(&frame)?; // send_raw 取 1 个参数 (用 default_timeout_ms)
// 接收原始响应 (receive_raw 取 0 个参数, 返回 Result<Vec<u8>>)
let raw = voe.receive_raw()?;
if let Some(parsed) = VoEInstance::parse_frame(&raw) {
println!("响应: {}", parsed);
}
辅助方法
VoEInstance::build_frame()
/// 构建标准 VoE 帧(VoE 头 + 数据),关联函数(无需实例)
pub fn build_frame(vendor_id: u32, vendor_type: u16, data: &[u8]) -> Vec<u8>
返回值:
Vec<u8>— 构建的 VoE 帧
VoEInstance::parse_frame()
/// 解析 VoE 帧头部,关联函数(无需实例)
pub fn parse_frame(frame: &[u8]) -> Option<VoEResponse>
返回值:
Option<VoEResponse>— 解析后的 VoE 响应对象,解析失败返回None
异步通知监听
某些厂商设备会在事件发生时主动向主站推送 VoE 帧 (例如外部传感器告警、按键事件).
SDK 通过 VoENotificationListener trait + 后台监听线程把异步推送转成事件回调,
无需用户自己轮询 receive().
VoENotificationEventArgs / VoENotificationListener
通知事件参数与监听器 trait(对齐 C# VoENotificationEventArgs / event EventHandler<...>):
#[derive(Debug, Clone)]
pub struct VoENotificationEventArgs {
pub slave_index: u16,
pub vendor_id: u32,
pub vendor_type: u16,
pub data: Vec<u8>,
/// 接收时间戳 (相对 UNIX_EPOCH 的微秒数)
pub timestamp_us: u64,
}
pub trait VoENotificationListener: Send + Sync + 'static {
fn on_notification(&self, args: &VoENotificationEventArgs);
}
// 任意 Fn(&VoENotificationEventArgs) 闭包自动实现该 trait
impl<F> VoENotificationListener for F
where F: Fn(&VoENotificationEventArgs) + Send + Sync + 'static { /* ... */ }
start_notification_listener / stop_notification_listener / stop_all_listeners / is_listener_running
pub fn start_notification_listener<F>(&self, listener: F) -> bool
where F: Fn(&VoENotificationEventArgs) + Send + Sync + 'static
pub fn stop_notification_listener(&self)
pub fn stop_all_listeners() // 关联函数
pub fn is_listener_running() -> bool // 关联函数
start_notification_listener() 取一个监听闭包,启动 DLL 内部 VoE 监听线程并注册通配订阅 (vendor_id=0/vendor_type=0)。stop_notification_listener() 用启动时保存的订阅索引精准注销本 master 的订阅并移除监听器;stop_all_listeners() 停止整个监听线程。DLL 未导出对应符号时安全降级: start_notification_listener 返回 false,is_listener_running 返回 false。
示例:
let voe = slave.voe();
// 启动 master 级监听 + 通配订阅, 传入监听闭包
let started = voe.start_notification_listener(|args: &VoENotificationEventArgs| {
println!("Slave {} VoE 通知: VID=0x{:08X} 类型=0x{:04X} {} 字节",
args.slave_index, args.vendor_id, args.vendor_type, args.data.len());
});
if !started {
eprintln!("当前 DLL 不支持 VoE 异步通知, 退化为轮询 receive()");
}
// 监听线程是否在跑 (关联函数)
println!("listener running = {}", VoEInstance::is_listener_running());
// 应用主循环 ...
// 精准停止本 master 订阅 / 或整体停止监听线程
voe.stop_notification_listener();
VoEInstance::stop_all_listeners();
当前 Rust SDK 使用通配 vendor_id=0/vendor_type=0 订阅 (与 C# 默认行为一致); 多厂商共线时需在用户回调内自行按 args.vendor_id 分流。
完整示例
厂商命令交互
let voe = slave.voe();
if !voe.is_supported() {
return Ok(());
}
let resp = voe.send_and_receive(0x00000002, 0x0001, &[0x01, 0x00])?;
println!("{}", resp);
批量数据传输
let voe = slave.voe();
if !voe.is_supported() {
return Ok(());
}
let payload = vec![0u8; 1024];
voe.send(0x00000002, 0x1000, &payload)?;
let reply = voe.receive()?;
println!("收到 {} 字节响应", reply.data.len());
原始帧操作
let voe = slave.voe();
if !voe.is_supported() {
return Ok(());
}
// 构建并发送原始帧 (send_raw_and_receive 取 1 个参数, 返回 Result<Vec<u8>>)
let frame = VoEInstance::build_frame(0x00000002, 0x0001, &[0x10, 0x20]);
let raw_data = voe.send_raw_and_receive(&frame)?;
println!("原始响应: {} 字节", raw_data.len());
if let Some(parsed) = VoEInstance::parse_frame(&raw_data) {
println!("解析: 厂商ID=0x{:08X}, 数据={}", parsed.vendor_id, parsed.to_hex_string());
}