跳到主要内容

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 (常量)usizeVoE 头部大小(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());
}
}