use std::collections::HashMap;
use std::time::{Duration, Instant};
use crate::data::error::{DarraError, Result};
use crate::utils::ffi;
use std::os::raw::c_int;
pub fn internet_checksum(data: &[u8]) -> u16 {
let mut sum: u32 = 0;
let mut i = 0;
while i + 1 < data.len() {
sum += u16::from_be_bytes([data[i], data[i + 1]]) as u32;
i += 2;
}
if i < data.len() {
sum += (data[i] as u32) << 8;
}
while (sum >> 16) != 0 {
sum = (sum & 0xFFFF) + (sum >> 16);
}
!(sum as u16)
}
fn build_ethernet_frame(dst_mac: &[u8; 6], src_mac: &[u8; 6], ethertype: u16, payload: &[u8]) -> Vec<u8> {
let mut frame = Vec::with_capacity(14 + payload.len());
frame.extend_from_slice(dst_mac);
frame.extend_from_slice(src_mac);
frame.extend_from_slice(ðertype.to_be_bytes());
frame.extend_from_slice(payload);
while frame.len() < 60 {
frame.push(0);
}
frame
}
fn build_ip_header(src_ip: u32, dst_ip: u32, protocol: u8, payload_len: u16) -> Vec<u8> {
let total_len = 20u16 + payload_len;
let mut header = vec![
0x45, 0x00, (total_len >> 8) as u8, (total_len & 0xFF) as u8, 0x00, 0x01, 0x40, 0x00, 0x40, protocol, 0x00, 0x00, ((src_ip >> 24) & 0xFF) as u8,
((src_ip >> 16) & 0xFF) as u8,
((src_ip >> 8) & 0xFF) as u8,
( src_ip & 0xFF) as u8,
((dst_ip >> 24) & 0xFF) as u8,
((dst_ip >> 16) & 0xFF) as u8,
((dst_ip >> 8) & 0xFF) as u8,
( dst_ip & 0xFF) as u8,
];
let checksum = internet_checksum(&header);
header[10] = (checksum >> 8) as u8;
header[11] = (checksum & 0xFF) as u8;
header
}
fn build_icmp_echo(id: u16, seq: u16, data: &[u8]) -> Vec<u8> {
let mut icmp = Vec::with_capacity(8 + data.len());
icmp.push(8); icmp.push(0); icmp.extend_from_slice(&[0, 0]); icmp.extend_from_slice(&id.to_be_bytes());
icmp.extend_from_slice(&seq.to_be_bytes());
icmp.extend_from_slice(data);
let checksum = internet_checksum(&icmp);
icmp[2] = (checksum >> 8) as u8;
icmp[3] = (checksum & 0xFF) as u8;
icmp
}
#[derive(Debug, Clone)]
pub struct PingResult {
pub success: bool,
pub rtt: Option<Duration>,
pub seq: u16,
}
pub fn eoe_ping(
master_index: u16,
slave_index: u16,
port: u8,
src_mac: &[u8; 6],
dst_mac: &[u8; 6],
src_ip: u32,
dst_ip: u32,
seq: u16,
payload: &[u8],
timeout_us: i32,
) -> Result<PingResult> {
let icmp = build_icmp_echo(0xDA12, seq, payload);
let mut ip = build_ip_header(src_ip, dst_ip, 1, icmp.len() as u16);
ip.extend_from_slice(&icmp);
let frame = build_ethernet_frame(dst_mac, src_mac, 0x0800, &ip);
let start = Instant::now();
let ok = unsafe {
ffi::EOESendFrame(master_index, slave_index, port,
frame.as_ptr(), frame.len() as c_int, timeout_us)
};
if ok == 0 {
return Ok(PingResult { success: false, rtt: None, seq });
}
let mut resp_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut resp_size: c_int = 0;
let recv_ok = unsafe {
ffi::EOEReceiveFrame(master_index, slave_index, port,
&mut resp_ptr, &mut resp_size, timeout_us)
};
let rtt = start.elapsed();
if recv_ok == 0 || resp_ptr.is_null() || resp_size < 42 {
if !resp_ptr.is_null() { unsafe { ffi::FreeMemory(resp_ptr) }; }
return Ok(PingResult { success: false, rtt: None, seq });
}
let resp = unsafe {
std::slice::from_raw_parts(resp_ptr as *const u8, resp_size as usize).to_vec()
};
unsafe { ffi::FreeMemory(resp_ptr) };
if resp.len() < 36 {
return Ok(PingResult { success: false, rtt: None, seq });
}
let ethertype = u16::from_be_bytes([resp[12], resp[13]]);
if ethertype != 0x0800 {
return Ok(PingResult { success: false, rtt: None, seq });
}
let protocol = resp[23];
if protocol != 1 {
return Ok(PingResult { success: false, rtt: None, seq });
}
let icmp_type = resp[34];
let resp_seq = u16::from_be_bytes([resp[38], resp[39]]);
let success = icmp_type == 0 && resp_seq == seq;
Ok(PingResult { success, rtt: Some(rtt), seq })
}
#[derive(Debug, Clone)]
pub struct ArpEntry {
pub ip: u32,
pub mac: [u8; 6],
pub last_seen: Instant,
pub dynamic: bool,
}
impl ArpEntry {
pub fn is_expired(&self, ttl: Duration) -> bool {
self.last_seen.elapsed() > ttl
}
}
pub struct ArpCache {
entries: HashMap<u32, ArpEntry>,
ttl: Duration,
}
impl ArpCache {
pub fn new() -> Self {
Self {
entries: HashMap::new(),
ttl: Duration::from_secs(300),
}
}
pub fn set_ttl(&mut self, ttl: Duration) {
self.ttl = ttl;
}
pub fn add_static(&mut self, ip: u32, mac: [u8; 6]) {
self.entries.insert(ip, ArpEntry {
ip, mac,
last_seen: Instant::now(),
dynamic: false,
});
}
pub fn lookup(&self, ip: u32) -> Option<[u8; 6]> {
self.entries.get(&ip).and_then(|e| {
if e.dynamic && e.is_expired(self.ttl) {
None
} else {
Some(e.mac)
}
})
}
pub fn learn_from_frame(&mut self, frame: &[u8]) {
if frame.len() < 42 { return; }
if frame[12] != 0x08 || frame[13] != 0x06 { return; }
let opcode = u16::from_be_bytes([frame[20], frame[21]]);
if opcode != 2 { return; } let mut mac = [0u8; 6];
mac.copy_from_slice(&frame[22..28]);
let ip = u32::from_be_bytes([frame[28], frame[29], frame[30], frame[31]]);
self.entries.insert(ip, ArpEntry {
ip, mac,
last_seen: Instant::now(),
dynamic: true,
});
}
pub fn resolve(
&mut self,
master_index: u16,
slave_index: u16,
port: u8,
src_mac: &[u8; 6],
src_ip: u32,
target_ip: u32,
timeout_us: i32,
) -> Result<Option<[u8; 6]>> {
if let Some(mac) = self.lookup(target_ip) {
return Ok(Some(mac));
}
let arp_frame = build_arp_request(src_mac, src_ip, target_ip);
let broadcast_mac = [0xFF; 6];
let frame = build_ethernet_frame(&broadcast_mac, src_mac, 0x0806, &arp_frame);
let send_ok = unsafe {
ffi::EOESendFrame(master_index, slave_index, port,
frame.as_ptr(), frame.len() as c_int, timeout_us)
};
if send_ok == 0 {
return Err(DarraError::EoeFailed);
}
let mut resp_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut resp_size: c_int = 0;
let recv_ok = unsafe {
ffi::EOEReceiveFrame(master_index, slave_index, port,
&mut resp_ptr, &mut resp_size, timeout_us)
};
if recv_ok == 0 || resp_ptr.is_null() {
if !resp_ptr.is_null() { unsafe { ffi::FreeMemory(resp_ptr) }; }
return Ok(None);
}
let resp = unsafe {
std::slice::from_raw_parts(resp_ptr as *const u8, resp_size as usize).to_vec()
};
unsafe { ffi::FreeMemory(resp_ptr) };
self.learn_from_frame(&resp);
Ok(self.lookup(target_ip))
}
pub fn purge_expired(&mut self) {
let ttl = self.ttl;
self.entries.retain(|_, e| !e.dynamic || !e.is_expired(ttl));
}
pub fn remove(&mut self, ip: u32) {
self.entries.remove(&ip);
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
impl Default for ArpCache {
fn default() -> Self {
Self::new()
}
}
pub fn foe_estimate_packet_count(file_size: u32, max_data_size: u32) -> u32 {
if max_data_size == 0 {
return 0;
}
(file_size + max_data_size - 1) / max_data_size
}
#[derive(Debug, Clone)]
pub struct EoEPingResult {
pub success: bool,
pub round_trip_time_ms: f64,
pub target_address: String,
pub ttl: u8,
pub error_message: String,
}
impl std::fmt::Display for EoEPingResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.success {
write!(f, "来自 {} 的回复: 时间={:.2}ms TTL={}", self.target_address, self.round_trip_time_ms, self.ttl)
} else {
write!(f, "Ping {} 失败: {}", self.target_address, self.error_message)
}
}
}
pub struct EoEInstance {
master_index: u16,
slave_index: u16,
port: u8,
pub arp_cache: ArpCache,
}
impl EoEInstance {
pub(crate) fn new(master_index: u16, slave_index: u16, port: u8) -> Self {
Self { master_index, slave_index, port, arp_cache: ArpCache::new() }
}
pub fn master_index(&self) -> u16 { self.master_index }
pub fn slave_index(&self) -> u16 { self.slave_index }
pub fn is_supported(&self) -> bool {
let proto = unsafe { ffi::GetSlaveMailboxProto(self.master_index, self.slave_index) };
(proto & 0x02) != 0
}
pub fn ip(&self, timeout_ms: i32) -> Result<u32> {
let mut ip: u32 = 0;
let mut subnet: u32 = 0;
let mut gateway: u32 = 0;
let ret = unsafe {
ffi::EOEGetIP(self.master_index, self.slave_index, self.port,
&mut ip, &mut subnet, &mut gateway, timeout_ms * 1000)
};
if ret != 0 { Ok(ip) } else { Err(DarraError::EoeFailed) }
}
pub fn subnet(&self, timeout_ms: i32) -> Result<u32> {
let mut ip: u32 = 0;
let mut subnet: u32 = 0;
let mut gateway: u32 = 0;
let ret = unsafe {
ffi::EOEGetIP(self.master_index, self.slave_index, self.port,
&mut ip, &mut subnet, &mut gateway, timeout_ms * 1000)
};
if ret != 0 { Ok(subnet) } else { Err(DarraError::EoeFailed) }
}
pub fn gateway(&self, timeout_ms: i32) -> Result<u32> {
let mut ip: u32 = 0;
let mut subnet: u32 = 0;
let mut gateway: u32 = 0;
let ret = unsafe {
ffi::EOEGetIP(self.master_index, self.slave_index, self.port,
&mut ip, &mut subnet, &mut gateway, timeout_ms * 1000)
};
if ret != 0 { Ok(gateway) } else { Err(DarraError::EoeFailed) }
}
pub fn set_ip(&self, ip: u32, subnet: u32, gateway: u32, timeout_ms: i32) -> Result<()> {
let ret = unsafe {
ffi::EOESetIP(self.master_index, self.slave_index, self.port,
ip, subnet, gateway, timeout_ms * 1000)
};
if ret != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
}
pub fn mac(&self, timeout_ms: i32) -> Result<[u8; 6]> {
let mut mac = [0u8; 6];
let ret = unsafe {
ffi::EOEGetMAC(self.master_index, self.slave_index, self.port,
mac.as_mut_ptr(), timeout_ms * 1000)
};
if ret != 0 { Ok(mac) } else { Err(DarraError::EoeFailed) }
}
pub fn mac_string(&self, timeout_ms: i32) -> Result<String> {
let mac = self.mac(timeout_ms)?;
Ok(format!("{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]))
}
pub fn set_mac(&self, mac: &[u8; 6], timeout_ms: i32) -> Result<()> {
let ret = unsafe {
ffi::EOESetMAC(self.master_index, self.slave_index, self.port,
mac.as_ptr(), timeout_ms * 1000)
};
if ret != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
}
pub fn dns(&self, timeout_ms: i32) -> Result<u32> {
let mut dns_ip: u32 = 0;
let mut dns_name = vec![0i8; 33];
let ret = unsafe {
ffi::EOEGetDNS(self.master_index, self.slave_index, self.port,
&mut dns_ip, dns_name.as_mut_ptr(), timeout_ms * 1000)
};
if ret != 0 { Ok(dns_ip) } else { Err(DarraError::EoeFailed) }
}
pub fn set_dns(&self, dns_ip: u32, dns_name: &str, timeout_ms: i32) -> Result<()> {
let c_name = std::ffi::CString::new(dns_name).unwrap_or_default();
let ret = unsafe {
ffi::EOESetDNS(self.master_index, self.slave_index, self.port,
dns_ip, c_name.as_ptr(), timeout_ms * 1000)
};
if ret != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
}
pub fn send_frame(&self, frame: &[u8], timeout_ms: i32) -> Result<()> {
let ret = unsafe {
ffi::EOESendFrame(self.master_index, self.slave_index, self.port,
frame.as_ptr(), frame.len() as c_int, timeout_ms * 1000)
};
if ret != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
}
pub fn receive_frame(&self, timeout_ms: i32) -> Result<Vec<u8>> {
let mut frame_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut frame_size: c_int = 0;
let ret = unsafe {
ffi::EOEReceiveFrame(self.master_index, self.slave_index, self.port,
&mut frame_ptr, &mut frame_size, timeout_ms * 1000)
};
if ret != 0 && !frame_ptr.is_null() && frame_size > 0 {
let data = unsafe {
std::slice::from_raw_parts(frame_ptr as *const u8, frame_size as usize).to_vec()
};
unsafe { ffi::FreeMemory(frame_ptr) };
Ok(data)
} else {
Err(DarraError::EoeFailed)
}
}
pub fn send_frame_with_timestamp(&self, frame: &[u8], timestamp: u64, timeout_ms: i32) -> Result<()> {
let ts32 = timestamp as u32;
let mut frame_with_ts = Vec::with_capacity(frame.len() + 4);
frame_with_ts.extend_from_slice(frame);
frame_with_ts.extend_from_slice(&ts32.to_le_bytes());
let ret = unsafe {
ffi::EOESendFrame(
self.master_index, self.slave_index, self.port,
frame_with_ts.as_ptr(), frame_with_ts.len() as c_int,
timeout_ms * 1000,
)
};
if ret != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
}
pub fn get_address_filters(&self, timeout_ms: i32) -> Result<Vec<[u8; 6]>> {
let mut filter_count: u8 = 0;
let mut mac_buffer = vec![0u8; 16 * 6];
let ret = unsafe {
ffi::EOEGetAddressFilter(
self.master_index, self.slave_index, self.port,
&mut filter_count, mac_buffer.as_mut_ptr(), 16, timeout_ms * 1000,
)
};
if ret == 0 {
return Err(DarraError::EoeFailed);
}
let count = filter_count as usize;
let mut filters = Vec::with_capacity(count);
for i in 0..count {
let mut mac = [0u8; 6];
mac.copy_from_slice(&mac_buffer[i * 6..(i + 1) * 6]);
filters.push(mac);
}
Ok(filters)
}
pub fn set_address_filters_batch(&self, filters: &[[u8; 6]], timeout_ms: i32) -> Result<()> {
let mac_data: Vec<u8> = filters.iter().flat_map(|m| m.iter().copied()).collect();
let ret = unsafe {
ffi::EOESetAddressFilter(
self.master_index, self.slave_index, self.port,
filters.len() as u8, mac_data.as_ptr(), timeout_ms * 1000,
)
};
if ret != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
}
pub fn clear_arp_cache(&mut self) {
self.arp_cache = ArpCache::new();
}
pub fn ping(&self, target_ip: u32, timeout_ms: i32) -> EoEPingResult {
let ip = match self.ip(timeout_ms) {
Ok(ip) => ip,
Err(_) => return EoEPingResult {
success: false, round_trip_time_ms: 0.0,
target_address: format_ip(target_ip), ttl: 0,
error_message: "本地网络配置无效".to_string(),
},
};
let mac = match self.mac(timeout_ms) {
Ok(mac) => mac,
Err(_) => return EoEPingResult {
success: false, round_trip_time_ms: 0.0,
target_address: format_ip(target_ip), ttl: 0,
error_message: "无法获取MAC地址".to_string(),
},
};
let dst_mac = self.arp_cache.lookup(target_ip).unwrap_or([0xFF; 6]);
match eoe_ping(self.master_index, self.slave_index, self.port,
&mac, &dst_mac, ip, target_ip, 1, &[0u8; 32], timeout_ms * 1000) {
Ok(result) => EoEPingResult {
success: result.success,
round_trip_time_ms: result.rtt.map(|r| r.as_secs_f64() * 1000.0).unwrap_or(0.0),
target_address: format_ip(target_ip),
ttl: 64,
error_message: if result.success { String::new() } else { "超时".to_string() },
},
Err(e) => EoEPingResult {
success: false, round_trip_time_ms: 0.0,
target_address: format_ip(target_ip), ttl: 0,
error_message: format!("{:?}", e),
},
}
}
}
fn format_ip(ip: u32) -> String {
format!("{}.{}.{}.{}",
(ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF)
}
fn build_arp_request(src_mac: &[u8; 6], src_ip: u32, target_ip: u32) -> Vec<u8> {
let mut arp = Vec::with_capacity(28);
arp.extend_from_slice(&1u16.to_be_bytes());
arp.extend_from_slice(&0x0800u16.to_be_bytes());
arp.push(6);
arp.push(4);
arp.extend_from_slice(&1u16.to_be_bytes());
arp.extend_from_slice(src_mac);
arp.extend_from_slice(&src_ip.to_be_bytes());
arp.extend_from_slice(&[0u8; 6]);
arp.extend_from_slice(&target_ip.to_be_bytes());
arp
}
impl EoEInstance {
pub fn send_frame_blocking(
master_index: u16,
slave_index: u16,
port: u8,
frame: Vec<u8>,
timeout_ms: i32,
) -> std::thread::JoinHandle<Result<()>> {
std::thread::spawn(move || {
let eoe = EoEInstance::new(master_index, slave_index, port);
eoe.send_frame(&frame, timeout_ms)
})
}
pub fn receive_frame_blocking(
master_index: u16,
slave_index: u16,
port: u8,
timeout_ms: i32,
) -> std::thread::JoinHandle<Result<Vec<u8>>> {
std::thread::spawn(move || {
let eoe = EoEInstance::new(master_index, slave_index, port);
eoe.receive_frame(timeout_ms)
})
}
pub fn ping_blocking(
master_index: u16,
slave_index: u16,
port: u8,
target_ip: u32,
timeout_ms: i32,
) -> std::thread::JoinHandle<EoEPingResult> {
std::thread::spawn(move || {
let eoe = EoEInstance::new(master_index, slave_index, port);
eoe.ping(target_ip, timeout_ms)
})
}
}
use std::sync::{Mutex as StdMutex, OnceLock as StdOnceLock, Arc};
type EoEReceiveHook = Arc<dyn Fn(u16, u16, &[u8]) + Send + Sync + 'static>;
fn eoe_hook_registry() -> &'static StdMutex<HashMap<u16, EoEReceiveHook>> {
static REG: StdOnceLock<StdMutex<HashMap<u16, EoEReceiveHook>>> = StdOnceLock::new();
REG.get_or_init(|| StdMutex::new(HashMap::new()))
}
unsafe extern "C" fn eoe_receive_trampoline(
master_index: u16,
slave: u16,
frame_data: *const u8,
frame_size: c_int,
) {
if frame_data.is_null() || frame_size <= 0 {
return;
}
let slice = unsafe { std::slice::from_raw_parts(frame_data, frame_size as usize) };
let cb_opt = {
match eoe_hook_registry().lock() {
Ok(g) => g.get(&master_index).cloned(),
Err(p) => p.into_inner().get(&master_index).cloned(),
}
};
if let Some(cb) = cb_opt {
cb(master_index, slave, slice);
}
}
impl EoEInstance {
pub fn set_receive_hook<F>(&self, callback: F) -> Result<()>
where
F: Fn(u16, u16, &[u8]) + Send + Sync + 'static,
{
let f = ffi::dynamic_ffi::ffi_gap()
.eoe_set_receive_hook
.ok_or_else(|| DarraError::Other(
obfstr::obfstr!("EOESetReceiveHook 未在 Darra.Core.dll 中导出").to_string()
))?;
match eoe_hook_registry().lock() {
Ok(mut g) => { g.insert(self.master_index, Arc::new(callback)); }
Err(p) => { p.into_inner().insert(self.master_index, Arc::new(callback)); }
}
let ok = unsafe { f(self.master_index, Some(eoe_receive_trampoline)) };
if ok == 0 {
if let Ok(mut g) = eoe_hook_registry().lock() {
g.remove(&self.master_index);
}
return Err(DarraError::Other(
obfstr::obfstr!("EOESetReceiveHook 返回 false (DLL 拒绝注册)").to_string()
));
}
Ok(())
}
pub fn clear_receive_hook(&self) -> Result<()> {
let f = ffi::dynamic_ffi::ffi_gap()
.eoe_clear_receive_hook
.ok_or_else(|| DarraError::Other(
obfstr::obfstr!("EOEClearReceiveHook 未在 Darra.Core.dll 中导出").to_string()
))?;
let _ok = unsafe { f(self.master_index) };
if let Ok(mut g) = eoe_hook_registry().lock() {
g.remove(&self.master_index);
}
Ok(())
}
}
#[cfg(feature = "async-tokio")]
impl EoEInstance {
pub async fn send_frame_async(&self, frame: Vec<u8>, timeout_ms: i32) -> Result<()> {
let master = self.master_index;
let slave = self.slave_index;
let port = self.port;
tokio::task::spawn_blocking(move || {
let eoe = EoEInstance::new(master, slave, port);
eoe.send_frame(&frame, timeout_ms)
})
.await
.map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
}
pub async fn receive_frame_async(&self, timeout_ms: i32) -> Result<Vec<u8>> {
let master = self.master_index;
let slave = self.slave_index;
let port = self.port;
tokio::task::spawn_blocking(move || {
let eoe = EoEInstance::new(master, slave, port);
eoe.receive_frame(timeout_ms)
})
.await
.map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
}
pub async fn ping_async(&self, target_ip: u32, timeout_ms: i32) -> EoEPingResult {
let master = self.master_index;
let slave = self.slave_index;
let port = self.port;
match tokio::task::spawn_blocking(move || {
let eoe = EoEInstance::new(master, slave, port);
eoe.ping(target_ip, timeout_ms)
})
.await
{
Ok(r) => r,
Err(e) => EoEPingResult {
success: false,
round_trip_time_ms: 0.0,
target_address: format_ip(target_ip),
ttl: 0,
error_message: format!("tokio join error: {}", e),
},
}
}
}
impl crate::abstractions::MailboxProtocol for EoEInstance {
fn protocol_type(&self) -> u8 { 0x02 }
fn protocol_name(&self) -> &'static str { "EoE" }
fn is_supported(&self) -> bool {
EoEInstance::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, 0x02, &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, 0x02);
}
}
}