use crate::data::error::{DarraError, Result};
use crate::utils::ffi;
use std::os::raw::c_void;
pub const VOE_HEADER_SIZE: usize = 6;
#[derive(Debug, Clone)]
pub struct VoEResponse {
pub vendor_id: u32,
pub vendor_type: u16,
pub data: Vec<u8>,
}
impl VoEResponse {
pub fn to_hex_string(&self) -> String {
self.data.iter()
.map(|b| format!("{:02X}", b))
.collect::<Vec<_>>()
.join(" ")
}
pub fn data_length(&self) -> usize {
self.data.len()
}
}
impl std::fmt::Display for VoEResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "VoE Response: VendorID=0x{:08X}, VendorType=0x{:04X}, DataLength={}",
self.vendor_id, self.vendor_type, self.data.len())
}
}
pub struct VoEInstance {
master_index: u16,
slave_index: u16,
pub default_timeout_ms: i32,
}
impl VoEInstance {
pub(crate) fn new(master_index: u16, slave_index: u16) -> Self {
Self {
master_index,
slave_index,
default_timeout_ms: 500,
}
}
pub fn is_supported(&self) -> bool {
let proto = unsafe { ffi::GetSlaveMailboxProto(self.master_index, self.slave_index) };
(proto & 0x20) != 0
}
pub fn send(&self, vendor_id: u32, vendor_type: u16, data: &[u8]) -> Result<()> {
self.send_with_timeout(vendor_id, vendor_type, data, self.default_timeout_ms)
}
pub fn send_with_timeout(&self, vendor_id: u32, vendor_type: u16, data: &[u8], timeout_ms: i32) -> Result<()> {
let ret = unsafe {
ffi::VOESend(
self.master_index, self.slave_index,
vendor_id, vendor_type,
data.as_ptr(), data.len() as i32,
timeout_ms * 1000, )
};
if ret != 0 { Ok(()) } else { Err(DarraError::VoeFailed) }
}
pub fn receive(&self) -> Result<VoEResponse> {
self.receive_with_timeout(self.default_timeout_ms)
}
pub fn receive_with_timeout(&self, timeout_ms: i32) -> Result<VoEResponse> {
let mut vendor_id: u32 = 0;
let mut vendor_type: u16 = 0;
let mut data_ptr: *mut c_void = std::ptr::null_mut();
let mut data_size: i32 = 0;
let ret = unsafe {
ffi::VOEReceive(
self.master_index, self.slave_index,
&mut vendor_id, &mut vendor_type,
&mut data_ptr, &mut data_size,
timeout_ms * 1000,
)
};
if ret != 0 {
let data = if !data_ptr.is_null() && data_size > 0 {
let mut buf = vec![0u8; data_size as usize];
unsafe {
std::ptr::copy_nonoverlapping(data_ptr as *const u8, buf.as_mut_ptr(), data_size as usize);
ffi::FreeMemory(data_ptr);
}
buf
} else {
if !data_ptr.is_null() {
unsafe { ffi::FreeMemory(data_ptr) };
}
Vec::new()
};
Ok(VoEResponse { vendor_id, vendor_type, data })
} else {
Err(DarraError::VoeFailed)
}
}
pub fn send_and_receive(&self, vendor_id: u32, vendor_type: u16, data: &[u8]) -> Result<VoEResponse> {
self.send(vendor_id, vendor_type, data)?;
self.receive()
}
pub fn send_raw(&self, frame_data: &[u8]) -> Result<()> {
self.send_raw_with_timeout(frame_data, self.default_timeout_ms)
}
pub fn send_raw_with_timeout(&self, frame_data: &[u8], timeout_ms: i32) -> Result<()> {
if frame_data.is_empty() {
return Err(DarraError::InvalidParameter("帧数据不能为空".into()));
}
let ret = unsafe {
ffi::VOESendRaw(
self.master_index, self.slave_index,
frame_data.as_ptr(), frame_data.len() as i32,
timeout_ms * 1000,
)
};
if ret != 0 { Ok(()) } else { Err(DarraError::VoeFailed) }
}
pub fn receive_raw(&self) -> Result<Vec<u8>> {
self.receive_raw_with_timeout(self.default_timeout_ms)
}
pub fn receive_raw_with_timeout(&self, timeout_ms: i32) -> Result<Vec<u8>> {
let mut frame_ptr: *mut c_void = std::ptr::null_mut();
let mut frame_size: i32 = 0;
let ret = unsafe {
ffi::VOEReceiveRaw(
self.master_index, self.slave_index,
&mut frame_ptr, &mut frame_size,
timeout_ms * 1000,
)
};
if ret != 0 && !frame_ptr.is_null() && frame_size > 0 {
let mut data = vec![0u8; frame_size as usize];
unsafe {
std::ptr::copy_nonoverlapping(frame_ptr as *const u8, data.as_mut_ptr(), frame_size as usize);
ffi::FreeMemory(frame_ptr);
}
Ok(data)
} else {
if !frame_ptr.is_null() {
unsafe { ffi::FreeMemory(frame_ptr) };
}
Err(DarraError::VoeFailed)
}
}
pub fn send_raw_and_receive(&self, frame_data: &[u8]) -> Result<Vec<u8>> {
self.send_raw(frame_data)?;
self.receive_raw()
}
pub fn build_frame(vendor_id: u32, vendor_type: u16, data: &[u8]) -> Vec<u8> {
let mut frame = Vec::with_capacity(VOE_HEADER_SIZE + data.len());
frame.push((vendor_id & 0xFF) as u8);
frame.push(((vendor_id >> 8) & 0xFF) as u8);
frame.push(((vendor_id >> 16) & 0xFF) as u8);
frame.push(((vendor_id >> 24) & 0xFF) as u8);
frame.push((vendor_type & 0xFF) as u8);
frame.push(((vendor_type >> 8) & 0xFF) as u8);
frame.extend_from_slice(data);
frame
}
pub fn parse_frame(frame: &[u8]) -> Option<VoEResponse> {
if frame.len() < VOE_HEADER_SIZE {
return None;
}
let vendor_id = u32::from_le_bytes([frame[0], frame[1], frame[2], frame[3]]);
let vendor_type = u16::from_le_bytes([frame[4], frame[5]]);
let data = frame[VOE_HEADER_SIZE..].to_vec();
Some(VoEResponse { vendor_id, vendor_type, data })
}
}
impl VoEInstance {
pub fn send_blocking(
master_index: u16,
slave_index: u16,
vendor_id: u32,
vendor_type: u16,
data: Vec<u8>,
timeout_ms: i32,
) -> std::thread::JoinHandle<Result<()>> {
std::thread::spawn(move || {
let voe = VoEInstance::new(master_index, slave_index);
voe.send_with_timeout(vendor_id, vendor_type, &data, timeout_ms)
})
}
pub fn receive_blocking(
master_index: u16,
slave_index: u16,
timeout_ms: i32,
) -> std::thread::JoinHandle<Result<VoEResponse>> {
std::thread::spawn(move || {
let voe = VoEInstance::new(master_index, slave_index);
voe.receive_with_timeout(timeout_ms)
})
}
pub fn send_raw_blocking(
master_index: u16,
slave_index: u16,
frame_data: Vec<u8>,
timeout_ms: i32,
) -> std::thread::JoinHandle<Result<()>> {
std::thread::spawn(move || {
let voe = VoEInstance::new(master_index, slave_index);
voe.send_raw_with_timeout(&frame_data, timeout_ms)
})
}
}
#[cfg(feature = "async-tokio")]
impl VoEInstance {
pub async fn send_async(&self, vendor_id: u32, vendor_type: u16, data: Vec<u8>) -> Result<()> {
let master = self.master_index;
let slave = self.slave_index;
let timeout_ms = self.default_timeout_ms;
tokio::task::spawn_blocking(move || {
let voe = VoEInstance::new(master, slave);
voe.send_with_timeout(vendor_id, vendor_type, &data, timeout_ms)
})
.await
.map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
}
pub async fn receive_async(&self) -> Result<VoEResponse> {
let master = self.master_index;
let slave = self.slave_index;
let timeout_ms = self.default_timeout_ms;
tokio::task::spawn_blocking(move || {
let voe = VoEInstance::new(master, slave);
voe.receive_with_timeout(timeout_ms)
})
.await
.map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
}
pub async fn send_raw_async(&self, frame_data: Vec<u8>) -> Result<()> {
let master = self.master_index;
let slave = self.slave_index;
let timeout_ms = self.default_timeout_ms;
tokio::task::spawn_blocking(move || {
let voe = VoEInstance::new(master, slave);
voe.send_raw_with_timeout(&frame_data, timeout_ms)
})
.await
.map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
}
}
#[derive(Debug, Clone)]
pub struct VoENotificationEventArgs {
pub slave_index: u16,
pub vendor_id: u32,
pub vendor_type: u16,
pub data: Vec<u8>,
pub timestamp_us: u64,
}
impl VoENotificationEventArgs {
pub fn new(slave_index: u16, vendor_id: u32, vendor_type: u16, data: Vec<u8>) -> Self {
let ts = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_micros() as u64)
.unwrap_or(0);
Self { slave_index, vendor_id, vendor_type, data, timestamp_us: ts }
}
}
pub trait VoENotificationListener: Send + Sync + 'static {
fn on_notification(&self, args: &VoENotificationEventArgs);
}
impl<F> VoENotificationListener for F
where
F: Fn(&VoENotificationEventArgs) + Send + Sync + 'static,
{
fn on_notification(&self, args: &VoENotificationEventArgs) { self(args); }
}
impl VoEInstance {
pub fn start_notification_listener(&self) -> bool {
let gap = ffi::dynamic_ffi::ffi_gap();
let start = match gap.voe_start_notification_listener {
Some(f) => f,
None => return false,
};
let register = match gap.voe_register_notification {
Some(f) => f,
None => return false,
};
let started = unsafe { start(self.master_index) } != 0;
if !started {
return false;
}
let _idx = unsafe {
register(self.slave_index, 0, 0, None, std::ptr::null_mut())
};
true
}
pub fn stop_notification_listener(&self) {
}
pub fn stop_all_listeners() {
if let Some(f) = ffi::dynamic_ffi::ffi_gap().voe_stop_notification_listener {
unsafe { let _ = f(); }
}
}
pub fn is_listener_running() -> bool {
match ffi::dynamic_ffi::ffi_gap().voe_is_notification_listening {
Some(f) => {
let v = unsafe { f() };
v != 0
}
None => false,
}
}
}
impl crate::abstractions::MailboxProtocol for VoEInstance {
fn protocol_type(&self) -> u8 { 0x0F }
fn protocol_name(&self) -> &'static str { "VoE" }
fn is_supported(&self) -> bool {
VoEInstance::is_supported(self)
}
fn statistics(&self) -> crate::abstractions::MailboxStatistics {
let mut stats = ffi::EcMbxStatsC::default();
let rc = unsafe {
ffi::mbx_get_stats_by_master(
self.master_index, self.slave_index, 0x0F, &mut stats,
)
};
if rc == 1 {
stats.into()
} else {
crate::abstractions::MailboxStatistics::empty()
}
}
fn reset_statistics(&self) {
unsafe {
ffi::mbx_reset_stats_by_master(self.master_index, self.slave_index, 0x0F);
}
}
}