跳到主要内容

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"
}

示例:

// 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 返回 falseis_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());
}