use crate::utils::ffi;
use crate::master::core::EtherCATMaster;
use std::sync::atomic::{AtomicU32, Ordering};
static INSTANCE_COUNTER: AtomicU32 = AtomicU32::new(0);
pub const VENDOR_ID: u32 = 0x00001164;
pub const PRODUCT_CODE: u32 = 0x00000001;
pub struct MasterObjectDictionary<'a> {
master: &'a EtherCATMaster,
serial_number: u32,
}
impl<'a> MasterObjectDictionary<'a> {
pub(crate) fn new(master: &'a EtherCATMaster) -> Self {
let serial_number = INSTANCE_COUNTER.fetch_add(1, Ordering::SeqCst);
Self { master, serial_number }
}
pub fn vendor_id(&self) -> u32 {
VENDOR_ID
}
pub fn product_code(&self) -> u32 {
PRODUCT_CODE
}
pub fn revision_number(&self) -> u32 {
match EtherCATMaster::dll_version() {
Some((major, minor, _, _)) => ((major as u32) << 16) | (minor as u32),
None => 0,
}
}
pub fn serial_number(&self) -> u32 {
self.serial_number
}
pub fn read_object(&self, index: u16, subindex: u8) -> Option<Vec<u8>> {
match index {
0x1000 => {
Some(0x000011A4u32.to_le_bytes().to_vec())
}
0x1008 => {
Some(b"Darra EtherCAT Master".to_vec())
}
0x1009 => {
Some(b"1.0".to_vec())
}
0x100A => {
match EtherCATMaster::dll_version() {
Some((major, minor, patch, build)) => {
let ver = format!("{}.{}.{}.{}", major, minor, patch, build);
Some(ver.into_bytes())
}
None => Some(b"0.0.0.0".to_vec()),
}
}
0x1018 => {
self.read_identity_object(subindex)
}
0x8000..=0x8FFF => {
let slave_idx = index & 0x0FFF;
self.read_slave_config_data(slave_idx, subindex)
}
0x9000..=0x9FFF => {
let slave_idx = index & 0x0FFF;
self.read_slave_info_data(slave_idx, subindex)
}
0xA000..=0xAFFF => {
let slave_idx = index & 0x0FFF;
self.read_slave_diag_data(slave_idx, subindex)
}
0xF002 => {
self.read_detect_modules(subindex)
}
0xF120 => {
self.read_master_diag_data(subindex)
}
0xF200 => {
Some(vec![0u8; 4])
}
_ => None,
}
}
pub fn write_object(&self, index: u16, subindex: u8, data: &[u8]) -> bool {
match index {
0xF002 => {
subindex == 1 && !data.is_empty()
}
0xF200 => {
!data.is_empty()
}
0xA000..=0xAFFF => {
subindex == 0 && !data.is_empty()
}
_ => false,
}
}
pub fn object_name(&self, index: u16) -> &str {
match index {
0x1000 => "Device Type",
0x1008 => "Manufacturer Device Name",
0x1009 => "Manufacturer Hardware Version",
0x100A => "Manufacturer Software Version",
0x1018 => "Identity Object",
0x8000..=0x8FFF => "Configuration Data",
0x9000..=0x9FFF => "Information Data",
0xA000..=0xAFFF => "Diagnosis Data",
0xF002 => "Detect Modules Command",
0xF120 => "Master Diag Data",
0xF200 => "Diag Interface Control",
_ => "Unknown",
}
}
pub fn subindex_count(&self, index: u16) -> u8 {
match index {
0x1000 | 0x1008 | 0x1009 | 0x100A => 0,
0x1018 => 4,
0x8000..=0x8FFF => 40,
0x9000..=0x9FFF => 32,
0xA000..=0xAFFF => 19,
0xF002 => 3,
0xF120 | 0xF200 => 16,
_ => 0,
}
}
pub fn supported_object_indices(&self) -> Vec<u16> {
let mut indices = vec![
0x1000, 0x1008, 0x1009, 0x100A, 0x1018,
0xF002, 0xF120, 0xF200,
];
let slave_count = self.slave_count();
for i in 1..=slave_count {
indices.push(0x8000 | i); indices.push(0x9000 | i); indices.push(0xA000 | i); }
indices
}
fn slave_count(&self) -> u16 {
unsafe { ffi::GetGroupSlaveCount(self.master.index(), 0) }
}
fn read_identity_object(&self, subindex: u8) -> Option<Vec<u8>> {
match subindex {
0 => Some(vec![4]),
1 => Some(VENDOR_ID.to_le_bytes().to_vec()),
2 => Some(PRODUCT_CODE.to_le_bytes().to_vec()),
3 => Some(self.revision_number().to_le_bytes().to_vec()),
4 => Some(self.serial_number.to_le_bytes().to_vec()),
_ => None,
}
}
fn read_slave_config_data(&self, slave_idx: u16, subindex: u8) -> Option<Vec<u8>> {
if slave_idx == 0 { return None; }
let slave = crate::slave::Slave::new(self.master.index(), slave_idx);
match subindex {
0 => Some(vec![40]),
1 => {
match slave.identity() {
Ok(id) => Some(id.vendor_id.to_le_bytes().to_vec()),
Err(_) => Some(vec![0; 4]),
}
}
2 => {
match slave.identity() {
Ok(id) => Some(id.product_code.to_le_bytes().to_vec()),
Err(_) => Some(vec![0; 4]),
}
}
3 => {
match slave.identity() {
Ok(id) => Some(id.revision_no.to_le_bytes().to_vec()),
Err(_) => Some(vec![0; 4]),
}
}
4 => {
match slave.identity() {
Ok(id) => Some(id.serial_no.to_le_bytes().to_vec()),
Err(_) => Some(vec![0; 4]),
}
}
_ => Some(vec![0; 4]),
}
}
fn read_slave_info_data(&self, slave_idx: u16, subindex: u8) -> Option<Vec<u8>> {
if slave_idx == 0 { return None; }
let slave = crate::slave::Slave::new(self.master.index(), slave_idx);
match subindex {
0 => Some(vec![32]),
1 => {
let state = slave.state().map(|s| s as u8).unwrap_or(0);
Some(vec![state])
}
2 => Some(slave.error_code_raw().to_le_bytes().to_vec()),
_ => Some(vec![0; 4]),
}
}
fn read_slave_diag_data(&self, slave_idx: u16, subindex: u8) -> Option<Vec<u8>> {
if slave_idx == 0 { return None; }
let slave = crate::slave::Slave::new(self.master.index(), slave_idx);
match subindex {
0 => Some(vec![19]),
1 => {
let state = slave.state().map(|s| s as u8).unwrap_or(0);
Some(vec![state])
}
2 => Some(slave.error_code_raw().to_le_bytes().to_vec()),
3 => {
let quality = unsafe {
ffi::GetSlaveLinkQuality(self.master.index(), slave_idx)
};
Some(quality.to_le_bytes().to_vec())
}
_ => Some(vec![0; 4]),
}
}
fn read_detect_modules(&self, subindex: u8) -> Option<Vec<u8>> {
match subindex {
0 => Some(vec![3]),
1 => Some(vec![1]),
2 => {
let count = self.slave_count();
Some(count.to_le_bytes().to_vec())
}
3 => Some(vec![0]),
_ => None,
}
}
fn read_master_diag_data(&self, subindex: u8) -> Option<Vec<u8>> {
match subindex {
0 => Some(vec![16]),
_ => {
let mut diag = ffi::MasterDiagData {
cyclic_lost_frames: 0,
acyclic_lost_frames: 0,
cyclic_frames_per_sec: 0,
acyclic_frames_per_sec: 0,
master_state: 0,
};
let ok = unsafe {
ffi::GetMasterDiagData(self.master.index(), &mut diag)
};
if ok == 0 { return Some(vec![0; 4]); }
match subindex {
1 => Some(diag.cyclic_lost_frames.to_le_bytes().to_vec()),
2 => Some(diag.acyclic_lost_frames.to_le_bytes().to_vec()),
3 => Some(diag.cyclic_frames_per_sec.to_le_bytes().to_vec()),
4 => Some(diag.acyclic_frames_per_sec.to_le_bytes().to_vec()),
5 => Some(diag.master_state.to_le_bytes().to_vec()),
_ => Some(vec![0; 4]),
}
}
}
}
}
impl<'a> std::fmt::Display for MasterObjectDictionary<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"ETG.1510 主站对象字典 (VendorID=0x{:08X}, Serial={})",
self.vendor_id(),
self.serial_number
)
}
}