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"
}
示例:
let response = voe.send_and_receive(0x00000002, 0x0001, &[0x10], 500)?;
if let Some(resp) = response {
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, 500)?;
// 接收原始响应
if let Some(raw) = voe.receive_raw(500)? {
if let Some(parsed) = voe.parse_voe_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().
VoENotificationListener trait
pub trait VoENotificationListener: Send + Sync + 'static {
/// 收到新的 VoE 帧时被调用 (后台监听线程上下文)
fn on_voe_received(&self, slave_index: u16, response: &VoEResponse);
/// 监听过程中发生错误 (超时除外, 超时不会回调)
fn on_voe_error(&self, slave_index: u16, error: &DarraError) {
// 默认空实现
let _ = (slave_index, error);
}
}
说明:
- 监听线程默认每
poll_interval_ms轮询一次receive_with_timeout(small_timeout), 收到帧立即调on_voe_received; 超时不算错误. - 闭包形式可直接
impl VoENotificationListener for F where F: Fn(u16, &VoEResponse), 也可手动实现结构体.
start_notification_listener / stop_notification_listener / stop_all_listeners / is_listener_running
pub fn start_notification_listener(&self) -> bool
pub fn stop_notification_listener(&self)
pub fn stop_all_listeners()
pub fn is_listener_running() -> bool
启动 / 停止 DLL 内部 VoE 通知监听 (基于 VOEStartNotificationListener + VOERegisterNotification 通配订阅 vendor_id=0/vendor_type=0)。当前 Rust 实例级 stop_notification_listener 是占位 (需要保存 subscription_index 才能精准注销), 真正全局停止用 stop_all_listeners() 静态方法。DLL 未导出对应符号时所有方法安全降级: start_notification_listener 返回 false, is_listener_running 返回 false。
通知事件参数 (对齐 C# VoENotificationEventArgs):
#[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);
}
// 闭包自动 impl
impl<F> VoENotificationListener for F
where F: Fn(&VoENotificationEventArgs) + Send + Sync + 'static { /* ... */ }
示例:
let voe = slave.voe().ok_or("不支持 VoE")?;
// 启动 master 级监听 + 通配订阅
if !voe.start_notification_listener() {
eprintln!("当前 DLL 不支持 VoE 异步通知, 退化为轮询 receive()");
}
// 监听线程是否在跑 (静态方法)
println!("listener running = {}", VoEInstance::is_listener_running());
// 应用主循环 ...
// 整体停止 (优先) / 单实例停止 (占位)
VoEInstance::stop_all_listeners();
voe.stop_notification_listener(); // 当前为占位
当前 Rust SDK 使用通配 vendor_id=0/vendor_type=0 订阅 (与 C# 默认行为一致); allowed_vendor_ids / strict_vendor_filter 等过滤选项暂未在 Rust 暴露, 多厂商共线时需在用户回调内自行按 args.vendor_id 分流。
完整示例
厂商命令交互
let voe = slave.voe().ok_or("不支持 VoE")?;
if voe.is_supported() {
let response = voe.send_and_receive(0x00000002, 0x0001, &[0x01, 0x00], 500)?;
if let Some(resp) = response {
println!("{}", resp);
}
}
批量数据传输
let voe = slave.voe().ok_or("不支持 VoE")?;
let payload = vec![0u8; 1024];
voe.send(0x00000002, 0x1000, &payload, 500)?;
if let Some(reply) = voe.receive(500)? {
println!("收到 {} 字节响应", reply.data.len());
}
原始帧操作
let voe = slave.voe().ok_or("不支持 VoE")?;
// 构建并发送原始帧
let frame = VoEInstance::build_frame(0x00000002, 0x0001, &[0x10, 0x20]);
let response = voe.send_raw_and_receive(&frame, 500)?;
if let Some(raw_data) = response {
println!("原始响应: {} 字节", raw_data.len());
if let Some(parsed) = voe.parse_voe_frame(&raw_data) {
println!("解析: 厂商ID=0x{:08X}, 数据={}", parsed.vendor_id, parsed.to_hex_string());
}
}