Skip to main content

darra_ethercat/slave/
coe.rs

1
2use std::fmt;
3use std::collections::HashMap;
4use crate::data::error::{DarraError, Result};
5use crate::utils::ffi;
6use std::os::raw::c_int;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[repr(u16)]
10pub enum EcDataType {
11
12    Unknown    = 0x0000,
13
14    Boolean    = 0x0001,
15
16    Integer8   = 0x0002,
17
18    Integer16  = 0x0003,
19
20    Integer32  = 0x0004,
21
22    Unsigned8  = 0x0005,
23
24    Unsigned16 = 0x0006,
25
26    Unsigned32 = 0x0007,
27
28    Real32     = 0x0008,
29
30    VisibleString = 0x0009,
31
32    OctetString = 0x000A,
33
34    UnicodeString = 0x000B,
35
36    TimeOfDay  = 0x000C,
37
38    TimeDifference = 0x000D,
39
40    Domain     = 0x000F,
41
42    Integer24  = 0x0010,
43
44    Real64     = 0x0011,
45
46    Integer40  = 0x0012,
47
48    Integer48  = 0x0013,
49
50    Integer56  = 0x0014,
51
52    Integer64  = 0x0015,
53
54    Unsigned24 = 0x0016,
55
56    Unsigned40 = 0x0018,
57
58    Unsigned48 = 0x0019,
59
60    Unsigned56 = 0x001A,
61
62    Unsigned64 = 0x001B,
63
64    Guid       = 0x001D,
65
66    Byte       = 0x001E,
67
68    Word       = 0x001F,
69
70    DWord      = 0x0020,
71
72    PdoMapping = 0x0021,
73
74    SmComType  = 0x0022,
75
76    Identity   = 0x0023,
77
78    Bit1       = 0x0030,
79
80    Bit2       = 0x0031,
81
82    Bit3       = 0x0032,
83
84    Bit4       = 0x0033,
85
86    Bit5       = 0x0034,
87
88    Bit6       = 0x0035,
89
90    Bit7       = 0x0036,
91
92    Bit8       = 0x0037,
93}
94
95impl EcDataType {
96
97    pub fn from_raw(val: u16) -> Self {
98        match val {
99            0x0000 => Self::Unknown,
100            0x0001 => Self::Boolean,
101            0x0002 => Self::Integer8,
102            0x0003 => Self::Integer16,
103            0x0004 => Self::Integer32,
104            0x0005 => Self::Unsigned8,
105            0x0006 => Self::Unsigned16,
106            0x0007 => Self::Unsigned32,
107            0x0008 => Self::Real32,
108            0x0009 => Self::VisibleString,
109            0x000A => Self::OctetString,
110            0x000B => Self::UnicodeString,
111            0x000C => Self::TimeOfDay,
112            0x000D => Self::TimeDifference,
113            0x000F => Self::Domain,
114            0x0010 => Self::Integer24,
115            0x0011 => Self::Real64,
116            0x0012 => Self::Integer40,
117            0x0013 => Self::Integer48,
118            0x0014 => Self::Integer56,
119            0x0015 => Self::Integer64,
120            0x0016 => Self::Unsigned24,
121            0x0018 => Self::Unsigned40,
122            0x0019 => Self::Unsigned48,
123            0x001A => Self::Unsigned56,
124            0x001B => Self::Unsigned64,
125            0x001D => Self::Guid,
126            0x001E => Self::Byte,
127            0x001F => Self::Word,
128            0x0020 => Self::DWord,
129            0x0021 => Self::PdoMapping,
130            0x0022 => Self::SmComType,
131            0x0023 => Self::Identity,
132            0x0030 => Self::Bit1,
133            0x0031 => Self::Bit2,
134            0x0032 => Self::Bit3,
135            0x0033 => Self::Bit4,
136            0x0034 => Self::Bit5,
137            0x0035 => Self::Bit6,
138            0x0036 => Self::Bit7,
139            0x0037 => Self::Bit8,
140            _ => Self::Unknown,
141        }
142    }
143
144    pub fn byte_size(self) -> Option<usize> {
145        match self {
146            Self::Boolean | Self::Integer8 | Self::Unsigned8 | Self::Byte => Some(1),
147            Self::Integer16 | Self::Unsigned16 | Self::Word => Some(2),
148            Self::Integer24 | Self::Unsigned24 => Some(3),
149            Self::Integer32 | Self::Unsigned32 | Self::Real32 | Self::DWord => Some(4),
150            Self::Integer40 | Self::Unsigned40 => Some(5),
151            Self::Integer48 | Self::Unsigned48 => Some(6),
152            Self::Integer56 | Self::Unsigned56 => Some(7),
153            Self::Integer64 | Self::Unsigned64 | Self::Real64 => Some(8),
154            _ => None,
155        }
156    }
157}
158
159impl fmt::Display for EcDataType {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        let name = match self {
162            Self::Unknown       => "UNKNOWN",
163            Self::Boolean       => "BOOL",
164            Self::Integer8      => "INT8",
165            Self::Integer16     => "INT16",
166            Self::Integer32     => "INT32",
167            Self::Integer64     => "INT64",
168            Self::Integer24     => "INT24",
169            Self::Integer40     => "INT40",
170            Self::Integer48     => "INT48",
171            Self::Integer56     => "INT56",
172            Self::Unsigned8     => "UINT8",
173            Self::Unsigned16    => "UINT16",
174            Self::Unsigned32    => "UINT32",
175            Self::Unsigned24    => "UINT24",
176            Self::Unsigned40    => "UINT40",
177            Self::Unsigned48    => "UINT48",
178            Self::Unsigned56    => "UINT56",
179            Self::Unsigned64    => "UINT64",
180            Self::Real32        => "FLOAT",
181            Self::Real64        => "DOUBLE",
182            Self::VisibleString => "STRING",
183            Self::OctetString   => "OCTET_STRING",
184            Self::UnicodeString => "UNICODE_STRING",
185            Self::TimeOfDay     => "TIME_OF_DAY",
186            Self::TimeDifference => "TIME_DIFF",
187            Self::Domain        => "DOMAIN",
188            Self::Guid          => "GUID",
189            Self::Byte          => "BYTE",
190            Self::Word          => "WORD",
191            Self::DWord         => "DWORD",
192            Self::PdoMapping    => "PDO_MAP",
193            Self::SmComType     => "SM_COM_TYPE",
194            Self::Identity      => "IDENTITY",
195            Self::Bit1          => "BIT1",
196            Self::Bit2          => "BIT2",
197            Self::Bit3          => "BIT3",
198            Self::Bit4          => "BIT4",
199            Self::Bit5          => "BIT5",
200            Self::Bit6          => "BIT6",
201            Self::Bit7          => "BIT7",
202            Self::Bit8          => "BIT8",
203        };
204        write!(f, "{}", name)
205    }
206}
207
208#[derive(Debug, Clone, Copy, PartialEq, Eq)]
209pub struct ObjAccess(pub u16);
210
211impl ObjAccess {
212
213    pub const NONE:          u16 = 0x0000;
214
215    pub const READ_PREOP:    u16 = 0x0001;
216
217    pub const READ_SAFEOP:   u16 = 0x0002;
218
219    pub const READ_OP:       u16 = 0x0004;
220
221    pub const WRITE_PREOP:   u16 = 0x0008;
222
223    pub const WRITE_SAFEOP:  u16 = 0x0010;
224
225    pub const WRITE_OP:      u16 = 0x0020;
226
227    pub const MAP_RXPDO:     u16 = 0x0040;
228
229    pub const MAP_TXPDO:     u16 = 0x0080;
230
231    pub const SETTINGS:      u16 = 0x0100;
232
233    pub const READ_ANY:  u16 = Self::READ_PREOP | Self::READ_SAFEOP | Self::READ_OP;
234
235    pub const WRITE_ANY: u16 = Self::WRITE_PREOP | Self::WRITE_SAFEOP | Self::WRITE_OP;
236
237    pub fn can_read(self) -> bool {
238        (self.0 & Self::READ_ANY) != 0
239    }
240
241    pub fn can_write(self) -> bool {
242        (self.0 & Self::WRITE_ANY) != 0
243    }
244
245    pub fn is_read_only(self) -> bool {
246        self.can_read() && !self.can_write()
247    }
248
249    pub fn can_write_preop(self) -> bool {
250        (self.0 & Self::WRITE_PREOP) != 0
251    }
252
253    pub fn can_write_safeop(self) -> bool {
254        (self.0 & (Self::WRITE_PREOP | Self::WRITE_SAFEOP)) != 0
255    }
256
257    pub fn can_write_op(self) -> bool {
258        (self.0 & Self::WRITE_ANY) != 0
259    }
260
261    pub fn rxpdo_mappable(self) -> bool {
262        (self.0 & Self::MAP_RXPDO) != 0
263    }
264
265    pub fn txpdo_mappable(self) -> bool {
266        (self.0 & Self::MAP_TXPDO) != 0
267    }
268
269    pub fn readable(self) -> bool { self.can_read() }
270
271    pub fn writable(self) -> bool { self.can_write() }
272
273    pub fn access_description(self) -> String {
274        let can_read = self.can_read();
275        let can_write = self.can_write();
276        if can_read && can_write {
277            let mut parts = Vec::new();
278            if (self.0 & Self::WRITE_PREOP) != 0 { parts.push("PreOP"); }
279            if (self.0 & Self::WRITE_SAFEOP) != 0 { parts.push("SafeOP"); }
280            if (self.0 & Self::WRITE_OP) != 0 { parts.push("OP"); }
281            if parts.is_empty() {
282                "读写".to_string()
283            } else {
284                format!("读写({}可写)", parts.join(","))
285            }
286        } else if can_read {
287            "只读".to_string()
288        } else if can_write {
289            let mut parts = Vec::new();
290            if (self.0 & Self::WRITE_PREOP) != 0 { parts.push("PreOP"); }
291            if (self.0 & Self::WRITE_SAFEOP) != 0 { parts.push("SafeOP"); }
292            if (self.0 & Self::WRITE_OP) != 0 { parts.push("OP"); }
293            if parts.is_empty() {
294                "只写".to_string()
295            } else {
296                format!("只写({}可写)", parts.join(","))
297            }
298        } else {
299            "无权限".to_string()
300        }
301    }
302}
303
304impl fmt::Display for ObjAccess {
305    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
306        let mut parts = Vec::new();
307        if (self.0 & Self::READ_PREOP) != 0  { parts.push("R(PreOp)"); }
308        if (self.0 & Self::READ_SAFEOP) != 0 { parts.push("R(SafeOp)"); }
309        if (self.0 & Self::READ_OP) != 0     { parts.push("R(Op)"); }
310        if (self.0 & Self::WRITE_PREOP) != 0  { parts.push("W(PreOp)"); }
311        if (self.0 & Self::WRITE_SAFEOP) != 0 { parts.push("W(SafeOp)"); }
312        if (self.0 & Self::WRITE_OP) != 0     { parts.push("W(Op)"); }
313        if (self.0 & Self::MAP_RXPDO) != 0   { parts.push("RxPDO"); }
314        if (self.0 & Self::MAP_TXPDO) != 0   { parts.push("TxPDO"); }
315        write!(f, "[{}]", parts.join("|"))
316    }
317}
318
319#[derive(Debug, Clone)]
320pub struct DiagnosticMessage {
321
322    pub sub_index: u8,
323
324    pub diag_code: u32,
325
326    pub flags: u16,
327
328    pub text_index: u16,
329
330    pub raw_data: Vec<u8>,
331}
332
333impl fmt::Display for DiagnosticMessage {
334    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335        write!(f, "Diag[{}]: Code=0x{:08X}, Flags=0x{:04X}",
336               self.sub_index, self.diag_code, self.flags)
337    }
338}
339
340#[derive(Debug, Clone)]
341pub struct CoEAccessDenied {
342    pub index: u16,
343    pub sub_index: u8,
344    pub entry_name: String,
345    pub is_read_operation: bool,
346    pub obj_access: u16,
347    pub message: String,
348}
349
350impl CoEAccessDenied {
351
352    pub fn new(index: u16, sub_index: u8, entry_name: &str,
353               is_read_operation: bool, obj_access: u16) -> Self {
354        let operation = if is_read_operation { "读取" } else { "写入" };
355        let access_desc = ObjAccess(obj_access).access_description();
356        let message = format!(
357            "对象条目 0x{:04X}:{:02X} ({}) 不支持{}操作。访问权限: {}",
358            index, sub_index, entry_name, operation, access_desc
359        );
360        Self { index, sub_index, entry_name: entry_name.to_string(),
361               is_read_operation, obj_access, message }
362    }
363
364    pub fn create_with_custom_message(
365        index: u16, sub_index: u8, custom_message: &str,
366        is_read_operation: bool, obj_access: u16,
367    ) -> Self {
368        Self { index, sub_index, entry_name: custom_message.to_string(),
369               is_read_operation, obj_access, message: custom_message.to_string() }
370    }
371}
372
373impl fmt::Display for CoEAccessDenied {
374    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375        write!(f, "{}", self.message)
376    }
377}
378
379impl std::error::Error for CoEAccessDenied {}
380
381#[derive(Debug, Clone)]
382pub struct ObjectEntry {
383
384    pub od_index: u16,
385
386    pub sub_index: u8,
387
388    pub name: String,
389
390    pub data_type: EcDataType,
391
392    pub bit_length: u16,
393
394    pub access: ObjAccess,
395
396    pub value_info: u8,
397}
398
399impl ObjectEntry {
400
401    pub fn byte_length(&self) -> usize {
402        ((self.bit_length as usize) + 7) / 8
403    }
404
405    pub fn datatype(&self) -> EcDataType {
406        self.data_type
407    }
408
409    pub fn can_read(&self) -> bool { self.access.can_read() }
410
411    pub fn can_write(&self) -> bool { self.access.can_write() }
412
413    pub fn is_read_only(&self) -> bool { self.access.is_read_only() }
414
415    pub fn can_write_preop(&self) -> bool { self.access.can_write_preop() }
416
417    pub fn can_write_safeop(&self) -> bool { self.access.can_write_safeop() }
418
419    pub fn can_write_op(&self) -> bool { self.access.can_write_op() }
420
421    pub fn access_description(&self) -> String {
422        self.access.access_description()
423    }
424
425    pub fn read_raw(&self, master_index: u16, slave_index: u16) -> Result<Vec<u8>> {
426        if !self.can_read() {
427            return Err(DarraError::SdoReadFailed {
428                index: self.od_index, subindex: self.sub_index, abort_code: None });
429        }
430        let mut size: c_int = 0;
431        let ptr = unsafe {
432            ffi::SDOread(master_index, slave_index, self.od_index, self.sub_index, 0, &mut size)
433        };
434        if ptr.is_null() || size <= 0 {
435            if !ptr.is_null() {
436                unsafe { ffi::FreeMemory(ptr as *mut _) };
437            }
438            return Err(DarraError::SdoReadFailed {
439                index: self.od_index, subindex: self.sub_index, abort_code: None });
440        }
441        let data = unsafe { std::slice::from_raw_parts(ptr, size as usize).to_vec() };
442        unsafe { ffi::FreeMemory(ptr as *mut _) };
443        Ok(data)
444    }
445
446    pub fn bytes(&self, master_index: u16, slave_index: u16) -> Result<Vec<u8>> {
447        self.read_raw(master_index, slave_index)
448    }
449
450    pub fn read_u8(&self, master_index: u16, slave_index: u16) -> Result<u8> {
451        let data = self.read_raw(master_index, slave_index)?;
452        data.first().copied().ok_or(DarraError::SdoReadFailed {
453            index: self.od_index, subindex: self.sub_index, abort_code: None })
454    }
455
456    pub fn read_u16(&self, master_index: u16, slave_index: u16) -> Result<u16> {
457        let data = self.read_raw(master_index, slave_index)?;
458        if data.len() < 2 {
459            return Err(DarraError::SdoReadFailed { index: self.od_index, subindex: self.sub_index, abort_code: None });
460        }
461        Ok(u16::from_le_bytes([data[0], data[1]]))
462    }
463
464    pub fn read_u32(&self, master_index: u16, slave_index: u16) -> Result<u32> {
465        let data = self.read_raw(master_index, slave_index)?;
466        if data.len() < 4 {
467            return Err(DarraError::SdoReadFailed { index: self.od_index, subindex: self.sub_index, abort_code: None });
468        }
469        Ok(u32::from_le_bytes([data[0], data[1], data[2], data[3]]))
470    }
471
472    pub fn read_u64(&self, master_index: u16, slave_index: u16) -> Result<u64> {
473        let data = self.read_raw(master_index, slave_index)?;
474        if data.len() < 8 {
475            return Err(DarraError::SdoReadFailed { index: self.od_index, subindex: self.sub_index, abort_code: None });
476        }
477        Ok(u64::from_le_bytes([
478            data[0], data[1], data[2], data[3],
479            data[4], data[5], data[6], data[7],
480        ]))
481    }
482
483    pub fn read_i32(&self, master_index: u16, slave_index: u16) -> Result<i32> {
484        Ok(self.read_u32(master_index, slave_index)? as i32)
485    }
486
487    pub fn read_i64(&self, master_index: u16, slave_index: u16) -> Result<i64> {
488        Ok(self.read_u64(master_index, slave_index)? as i64)
489    }
490
491    pub fn read_f32(&self, master_index: u16, slave_index: u16) -> Result<f32> {
492        let bits = self.read_u32(master_index, slave_index)?;
493        Ok(f32::from_bits(bits))
494    }
495
496    pub fn read_f64(&self, master_index: u16, slave_index: u16) -> Result<f64> {
497        let bits = self.read_u64(master_index, slave_index)?;
498        Ok(f64::from_bits(bits))
499    }
500
501    pub fn read_string(&self, master_index: u16, slave_index: u16) -> Result<String> {
502        let data = self.read_raw(master_index, slave_index)?;
503        let end = data.iter().position(|&b| b == 0).unwrap_or(data.len());
504        Ok(crate::utils::help::decode_ethercat_string(&data[..end]))
505    }
506
507    pub fn get_value(&self, master_index: u16, slave_index: u16) -> Result<CoEValue> {
508        match self.data_type {
509            EcDataType::Boolean => {
510                let v = self.read_u8(master_index, slave_index)?;
511                Ok(CoEValue::Bool(v != 0))
512            }
513            EcDataType::Integer8 => {
514                let v = self.read_u8(master_index, slave_index)?;
515                Ok(CoEValue::I8(v as i8))
516            }
517            EcDataType::Unsigned8 | EcDataType::Byte => {
518                Ok(CoEValue::U8(self.read_u8(master_index, slave_index)?))
519            }
520            EcDataType::Integer16 => {
521                let v = self.read_u16(master_index, slave_index)?;
522                Ok(CoEValue::I16(v as i16))
523            }
524            EcDataType::Unsigned16 | EcDataType::Word => {
525                Ok(CoEValue::U16(self.read_u16(master_index, slave_index)?))
526            }
527            EcDataType::Integer32 | EcDataType::Integer24 => {
528                Ok(CoEValue::I32(self.read_i32(master_index, slave_index)?))
529            }
530            EcDataType::Unsigned32 | EcDataType::Unsigned24 | EcDataType::DWord => {
531                Ok(CoEValue::U32(self.read_u32(master_index, slave_index)?))
532            }
533            EcDataType::Integer64 => {
534                Ok(CoEValue::I64(self.read_i64(master_index, slave_index)?))
535            }
536            EcDataType::Unsigned64 => {
537                Ok(CoEValue::U64(self.read_u64(master_index, slave_index)?))
538            }
539            EcDataType::Real32 => {
540                Ok(CoEValue::F32(self.read_f32(master_index, slave_index)?))
541            }
542            EcDataType::Real64 => {
543                Ok(CoEValue::F64(self.read_f64(master_index, slave_index)?))
544            }
545            EcDataType::VisibleString | EcDataType::UnicodeString => {
546                Ok(CoEValue::Str(self.read_string(master_index, slave_index)?))
547            }
548            _ => {
549                Ok(CoEValue::Raw(self.read_raw(master_index, slave_index)?))
550            }
551        }
552    }
553
554    pub fn write_raw(&self, master_index: u16, slave_index: u16, data: &[u8]) -> Result<()> {
555        if !self.can_write() {
556            return Err(DarraError::SdoWriteFailed {
557                index: self.od_index, subindex: self.sub_index, abort_code: None });
558        }
559        let ok = unsafe {
560            ffi::SDOwrite_raw(master_index, slave_index, self.od_index, self.sub_index,
561                              0, data.as_ptr(), data.len() as c_int)
562        };
563        if ok != 0 {
564            Ok(())
565        } else {
566            Err(DarraError::SdoWriteFailed { index: self.od_index, subindex: self.sub_index, abort_code: None })
567        }
568    }
569
570    pub fn write_u8(&self, master_index: u16, slave_index: u16, value: u8) -> Result<()> {
571        self.write_raw(master_index, slave_index, &[value])
572    }
573
574    pub fn write_u16(&self, master_index: u16, slave_index: u16, value: u16) -> Result<()> {
575        self.write_raw(master_index, slave_index, &value.to_le_bytes())
576    }
577
578    pub fn write_u32(&self, master_index: u16, slave_index: u16, value: u32) -> Result<()> {
579        self.write_raw(master_index, slave_index, &value.to_le_bytes())
580    }
581
582    pub fn write_u64(&self, master_index: u16, slave_index: u16, value: u64) -> Result<()> {
583        self.write_raw(master_index, slave_index, &value.to_le_bytes())
584    }
585
586    pub fn write_i32(&self, master_index: u16, slave_index: u16, value: i32) -> Result<()> {
587        self.write_raw(master_index, slave_index, &value.to_le_bytes())
588    }
589
590    pub fn write_i64(&self, master_index: u16, slave_index: u16, value: i64) -> Result<()> {
591        self.write_raw(master_index, slave_index, &value.to_le_bytes())
592    }
593
594    pub fn write_f32(&self, master_index: u16, slave_index: u16, value: f32) -> Result<()> {
595        self.write_raw(master_index, slave_index, &value.to_bits().to_le_bytes())
596    }
597
598    pub fn write_f64(&self, master_index: u16, slave_index: u16, value: f64) -> Result<()> {
599        self.write_raw(master_index, slave_index, &value.to_bits().to_le_bytes())
600    }
601}
602
603impl fmt::Display for ObjectEntry {
604    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
605        write!(f,
606            "  [{:02X}] {:40} {:12} {}bit {}",
607            self.sub_index, self.name, self.data_type, self.bit_length, self.access
608        )
609    }
610}
611
612#[derive(Debug, Clone)]
613pub enum CoEValue {
614    Bool(bool),
615    I8(i8),
616    U8(u8),
617    I16(i16),
618    U16(u16),
619    I32(i32),
620    U32(u32),
621    I64(i64),
622    U64(u64),
623    F32(f32),
624    F64(f64),
625    Str(String),
626    Raw(Vec<u8>),
627}
628
629impl fmt::Display for CoEValue {
630    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
631        match self {
632            CoEValue::Bool(v) => write!(f, "{}", v),
633            CoEValue::I8(v) => write!(f, "{}", v),
634            CoEValue::U8(v) => write!(f, "{}", v),
635            CoEValue::I16(v) => write!(f, "{}", v),
636            CoEValue::U16(v) => write!(f, "{}", v),
637            CoEValue::I32(v) => write!(f, "{}", v),
638            CoEValue::U32(v) => write!(f, "{}", v),
639            CoEValue::I64(v) => write!(f, "{}", v),
640            CoEValue::U64(v) => write!(f, "{}", v),
641            CoEValue::F32(v) => write!(f, "{:.6}", v),
642            CoEValue::F64(v) => write!(f, "{:.6}", v),
643            CoEValue::Str(v) => write!(f, "{}", v),
644            CoEValue::Raw(v) => {
645                let hex: Vec<String> = v.iter().map(|b| format!("{:02X}", b)).collect();
646                write!(f, "{}", hex.join(" "))
647            }
648        }
649    }
650}
651
652#[derive(Debug, Clone)]
653pub struct OdObject {
654
655    pub index: u16,
656
657    pub name: String,
658
659    pub object_code: u8,
660
661    pub data_type: EcDataType,
662
663    pub max_sub: u8,
664
665    pub entries: Vec<ObjectEntry>,
666}
667
668impl OdObject {
669
670    pub fn count(&self) -> usize {
671        self.entries.len()
672    }
673
674    pub fn datatype(&self) -> EcDataType {
675        self.data_type
676    }
677
678    pub fn is_var(&self) -> bool {
679        self.object_code == 0x07
680    }
681
682    pub fn is_array(&self) -> bool {
683        self.object_code == 0x08
684    }
685
686    pub fn is_record(&self) -> bool {
687        self.object_code == 0x09
688    }
689
690    pub fn entry(&self, sub_index: u8) -> Option<&ObjectEntry> {
691        self.entries.iter().find(|e| e.sub_index == sub_index)
692    }
693
694    pub fn contains_key(&self, sub_index: u8) -> bool {
695        self.entries.iter().any(|e| e.sub_index == sub_index)
696    }
697
698    pub fn entry_by_name(&self, name: &str) -> Option<&ObjectEntry> {
699        let name_lower = name.to_lowercase();
700        self.entries.iter().find(|e| e.name.to_lowercase() == name_lower)
701    }
702
703    pub fn get_by_position(&self, position: usize) -> Option<&ObjectEntry> {
704        self.entries.get(position)
705    }
706
707    pub fn read_all(&self, master_index: u16, slave_index: u16) -> HashMap<u8, Vec<u8>> {
708        let mut result = HashMap::new();
709        for entry in &self.entries {
710            if let Ok(data) = entry.read_raw(master_index, slave_index) {
711                if !data.is_empty() {
712                    result.insert(entry.sub_index, data);
713                }
714            }
715        }
716        result
717    }
718
719    pub fn copy(&self) -> Self {
720        self.clone()
721    }
722}
723
724impl fmt::Display for OdObject {
725    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
726        let kind = match self.object_code {
727            0x07 => "VAR   ",
728            0x08 => "ARRAY ",
729            0x09 => "RECORD",
730            _    => "OTHER ",
731        };
732        writeln!(f, "[0x{:04X}] {} {} ({}条目)", self.index, kind, self.name, self.entries.len())?;
733        for entry in &self.entries {
734            writeln!(f, "{}", entry)?;
735        }
736        Ok(())
737    }
738}
739
740pub struct OdList {
741
742    pub master_index: u16,
743
744    pub slave_index: u16,
745
746    pub objects: Vec<OdObject>,
747}
748
749impl OdList {
750
751    pub fn load(master_index: u16, slave_index: u16) -> Result<Self> {
752        let odlist_ptr = unsafe { ffi::GetSlaveSDOList(master_index, slave_index) };
753        if odlist_ptr.is_null() {
754            return Err(DarraError::SdoReadFailed { index: 0, subindex: 0, abort_code: None });
755        }
756
757        let objects = Self::parse_odlist_objects(master_index, slave_index, odlist_ptr, true)?;
758
759        unsafe { ffi::FreeMemory(odlist_ptr as *mut std::os::raw::c_void) };
760        Ok(Self { master_index, slave_index, objects })
761    }
762
763    pub fn load_basic(master_index: u16, slave_index: u16) -> Result<Self> {
764        let odlist_ptr = unsafe { ffi::GetSlaveSDOListBasic(master_index, slave_index) };
765        if odlist_ptr.is_null() {
766            return Err(DarraError::SdoReadFailed { index: 0, subindex: 0, abort_code: None });
767        }
768
769        let objects = Self::parse_odlist_objects(master_index, slave_index, odlist_ptr, false)?;
770        unsafe { ffi::FreeMemory(odlist_ptr as *mut _) };
771        Ok(Self { master_index, slave_index, objects })
772    }
773
774    pub(crate) fn from_raw_ptr(
775        master_index: u16, slave_index: u16, odlist_ptr: *const std::ffi::c_void,
776    ) -> Result<Self> {
777        if odlist_ptr.is_null() {
778            return Err(DarraError::SdoReadFailed { index: 0, subindex: 0, abort_code: None });
779        }
780        let objects = Self::parse_odlist_objects(master_index, slave_index, odlist_ptr, true)?;
781        Ok(Self { master_index, slave_index, objects })
782    }
783
784    fn parse_odlist_objects(
785        master_index: u16, slave_index: u16,
786        odlist_ptr: *const std::ffi::c_void, load_entries: bool,
787    ) -> Result<Vec<OdObject>> {
788        let base = odlist_ptr as *const u8;
789
790        let entries_count = unsafe {
791            let p = base.add(2) as *const u16;
792            std::ptr::read_unaligned(p) as usize
793        };
794        let entries_count = entries_count.min(1024);
795
796        let mut objects = Vec::with_capacity(entries_count);
797
798        for i in 0..entries_count {
799            let idx_val = unsafe {
800                let p = base.add(4 + i * 2) as *const u16;
801                std::ptr::read_unaligned(p)
802            };
803            let dt_raw = unsafe {
804                let p = base.add(2052 + i * 2) as *const u16;
805                std::ptr::read_unaligned(p)
806            };
807            let obj_code = unsafe { *base.add(4100 + i) };
808            let max_sub = unsafe { *base.add(5124 + i) };
809            let name = {
810                let name_ptr = unsafe { base.add(6148 + i * 41) };
811                let name_slice = unsafe { std::slice::from_raw_parts(name_ptr, 41) };
812                let end = name_slice.iter().position(|&b| b == 0).unwrap_or(41);
813
814                crate::utils::help::decode_ethercat_string(&name_slice[..end])
815            };
816
817            let mut entries = Vec::new();
818            if load_entries {
819                let oe_ptr = unsafe {
820                    ffi::GetSlavePointer_SDO_WithODList(master_index, slave_index, i as u16, odlist_ptr)
821                };
822                if !oe_ptr.is_null() {
823                    entries = parse_oe_entries(oe_ptr, idx_val, max_sub);
824                    unsafe { ffi::FreeMemory(oe_ptr as *mut std::os::raw::c_void) };
825                }
826            }
827
828            objects.push(OdObject {
829                index: idx_val,
830                name,
831                object_code: obj_code,
832                data_type: EcDataType::from_raw(dt_raw),
833                max_sub,
834                entries,
835            });
836        }
837
838        Ok(objects)
839    }
840
841    pub fn count(&self) -> usize {
842        self.objects.len()
843    }
844
845    pub fn find(&self, index: u16) -> Option<&OdObject> {
846        self.objects.iter().find(|o| o.index == index)
847    }
848
849    pub fn contains_key(&self, index: u16) -> bool {
850        self.objects.iter().any(|o| o.index == index)
851    }
852
853    pub fn find_by_name(&self, name: &str) -> Vec<&OdObject> {
854        let name_lower = name.to_lowercase();
855        self.objects.iter()
856            .filter(|o| o.name.to_lowercase().contains(&name_lower))
857            .collect()
858    }
859
860    pub fn read_entry_raw(
861        &self, index: u16, sub_index: u8,
862    ) -> Result<Vec<u8>> {
863        let mut size: c_int = 0;
864        let ptr = unsafe {
865            ffi::SDOread(self.master_index, self.slave_index, index, sub_index, 0, &mut size)
866        };
867        if ptr.is_null() || size <= 0 {
868            if !ptr.is_null() {
869                unsafe { ffi::FreeMemory(ptr as *mut _) };
870            }
871            return Err(DarraError::SdoReadFailed { index, subindex: sub_index, abort_code: None });
872        }
873        let data = unsafe { std::slice::from_raw_parts(ptr, size as usize).to_vec() };
874        unsafe { ffi::FreeMemory(ptr as *mut _) };
875        Ok(data)
876    }
877
878    pub fn get_by_position(&self, position: usize) -> Option<&OdObject> {
879        self.objects.get(position)
880    }
881
882    pub fn len(&self) -> usize {
883        self.objects.len()
884    }
885
886    pub fn is_empty(&self) -> bool {
887        self.objects.is_empty()
888    }
889
890    pub fn copy(&self) -> Self {
891        Self {
892            master_index: self.master_index,
893            slave_index: self.slave_index,
894            objects: self.objects.clone(),
895        }
896    }
897
898    pub fn keys(&self) -> Vec<u16> {
899        self.objects.iter().map(|o| o.index).collect()
900    }
901
902    pub fn load_via_coe_h2(master_index: u16, slave_index: u16) -> Result<Self> {
903        let odlist_ptr = unsafe { ffi::coe_get_od_list(master_index, slave_index) };
904        if odlist_ptr.is_null() {
905            return Err(DarraError::SdoReadFailed { index: 0, subindex: 0, abort_code: None });
906        }
907
908        let mut objects: Vec<OdObject> = Vec::new();
909        unsafe {
910            let base = odlist_ptr as *const u8;
911            let entries_count = {
912                let p = base.add(2) as *const u16;
913                std::ptr::read_unaligned(p) as usize
914            }.min(1024);
915
916            for i in 0..entries_count {
917                let idx_val = {
918                    let p = base.add(4 + i * 2) as *const u16;
919                    std::ptr::read_unaligned(p)
920                };
921                if idx_val == 0 { continue; }
922                let dt_raw = {
923                    let p = base.add(2052 + i * 2) as *const u16;
924                    std::ptr::read_unaligned(p)
925                };
926                let obj_code = *base.add(4100 + i);
927                let max_sub = *base.add(5124 + i);
928                let name = {
929                    let name_ptr = base.add(6148 + i * 41);
930                    let name_slice = std::slice::from_raw_parts(name_ptr, 41);
931                    let end = name_slice.iter().position(|&b| b == 0).unwrap_or(41);
932                    crate::utils::help::decode_ethercat_string(&name_slice[..end])
933                };
934
935                let mut entries = Vec::new();
936                for s in 0..=max_sub {
937                    let oe_ptr = ffi::coe_get_entry_desc(master_index, slave_index, idx_val, s);
938                    if oe_ptr.is_null() { continue; }
939                    let ob = oe_ptr as *const u8;
940                    let edt = {
941                        let p = ob.add(258) as *const u16;
942                        std::ptr::read_unaligned(p)
943                    };
944                    let bit_length = {
945                        let p = ob.add(770) as *const u16;
946                        std::ptr::read_unaligned(p)
947                    };
948                    let access_raw = {
949                        let p = ob.add(1282) as *const u16;
950                        std::ptr::read_unaligned(p)
951                    };
952                    let ename = {
953                        let name_ptr = ob.add(1794);
954                        let name_slice = std::slice::from_raw_parts(name_ptr, 41);
955                        let end = name_slice.iter().position(|&b| b == 0).unwrap_or(41);
956                        crate::utils::help::decode_ethercat_string(&name_slice[..end])
957                    };
958                    entries.push(ObjectEntry {
959                        od_index: idx_val,
960                        sub_index: s,
961                        name: ename,
962                        data_type: EcDataType::from_raw(edt),
963                        bit_length,
964                        access: ObjAccess(access_raw),
965                        value_info: 0,
966                    });
967                    ffi::coe_free_oelist(oe_ptr);
968                }
969
970                objects.push(OdObject {
971                    index: idx_val,
972                    name,
973                    object_code: obj_code,
974                    data_type: EcDataType::from_raw(dt_raw),
975                    max_sub,
976                    entries,
977                });
978            }
979
980            ffi::coe_free_odlist(odlist_ptr);
981        }
982        Ok(Self { master_index, slave_index, objects })
983    }
984
985    pub fn get_object_description(master_index: u16, slave_index: u16, index: u16)
986        -> Option<OdObject>
987    {
988        let p = unsafe { ffi::coe_get_object_desc(master_index, slave_index, index) };
989        if p.is_null() { return None; }
990        let result = unsafe {
991            let base = p as *const u8;
992            let dt_raw = {
993                let pp = base.add(2052) as *const u16;
994                std::ptr::read_unaligned(pp)
995            };
996            let obj_code = *base.add(4100);
997            let max_sub = *base.add(5124);
998            let name = {
999                let name_ptr = base.add(6148);
1000                let name_slice = std::slice::from_raw_parts(name_ptr, 41);
1001                let end = name_slice.iter().position(|&b| b == 0).unwrap_or(41);
1002                crate::utils::help::decode_ethercat_string(&name_slice[..end])
1003            };
1004            OdObject {
1005                index,
1006                name,
1007                object_code: obj_code,
1008                data_type: EcDataType::from_raw(dt_raw),
1009                max_sub,
1010                entries: Vec::new(),
1011            }
1012        };
1013        unsafe { ffi::coe_free_odlist(p); }
1014        Some(result)
1015    }
1016
1017    pub fn get_entry_description(
1018        master_index: u16, slave_index: u16, index: u16, subindex: u8,
1019    ) -> Option<ObjectEntry> {
1020        let p = unsafe { ffi::coe_get_entry_desc(master_index, slave_index, index, subindex) };
1021        if p.is_null() { return None; }
1022        let result = unsafe {
1023            let base = p as *const u8;
1024            let edt = {
1025                let pp = base.add(258) as *const u16;
1026                std::ptr::read_unaligned(pp)
1027            };
1028            let bit_length = {
1029                let pp = base.add(770) as *const u16;
1030                std::ptr::read_unaligned(pp)
1031            };
1032            let access_raw = {
1033                let pp = base.add(1282) as *const u16;
1034                std::ptr::read_unaligned(pp)
1035            };
1036            let name = {
1037                let name_ptr = base.add(1794);
1038                let name_slice = std::slice::from_raw_parts(name_ptr, 41);
1039                let end = name_slice.iter().position(|&b| b == 0).unwrap_or(41);
1040                crate::utils::help::decode_ethercat_string(&name_slice[..end])
1041            };
1042            ObjectEntry {
1043                od_index: index,
1044                sub_index: subindex,
1045                name,
1046                data_type: EcDataType::from_raw(edt),
1047                bit_length,
1048                access: ObjAccess(access_raw),
1049                value_info: 0,
1050            }
1051        };
1052        unsafe { ffi::coe_free_oelist(p); }
1053        Some(result)
1054    }
1055}
1056
1057impl fmt::Display for OdList {
1058    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1059        writeln!(f, "从站 {} 对象字典 ({} 个对象):", self.slave_index, self.objects.len())?;
1060        writeln!(f, "{}", "=".repeat(72))?;
1061        for obj in &self.objects {
1062            write!(f, "{}", obj)?;
1063        }
1064        Ok(())
1065    }
1066}
1067
1068pub fn read_diagnostic_messages(master_index: u16, slave_index: u16) -> Vec<DiagnosticMessage> {
1069    let mut messages = Vec::new();
1070
1071    let mut size: c_int = 0;
1072    let ptr = unsafe {
1073        ffi::SDOread(master_index, slave_index, 0x10F3, 0, 0, &mut size)
1074    };
1075    if ptr.is_null() || size < 1 {
1076        if !ptr.is_null() {
1077            unsafe { ffi::FreeMemory(ptr as *mut _) };
1078        }
1079        return messages;
1080    }
1081    let count = unsafe { *ptr } as usize;
1082    unsafe { ffi::FreeMemory(ptr as *mut _) };
1083
1084    for i in 1..=count.min(20) {
1085        let mut msg_size: c_int = 0;
1086        let msg_ptr = unsafe {
1087            ffi::SDOread(master_index, slave_index, 0x10F3, i as u8, 0, &mut msg_size)
1088        };
1089        if msg_ptr.is_null() || msg_size < 8 {
1090            if !msg_ptr.is_null() {
1091                unsafe { ffi::FreeMemory(msg_ptr as *mut _) };
1092            }
1093            continue;
1094        }
1095
1096        let data = unsafe { std::slice::from_raw_parts(msg_ptr, msg_size as usize).to_vec() };
1097        unsafe { ffi::FreeMemory(msg_ptr as *mut _) };
1098
1099        let diag_code = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
1100        let flags = u16::from_le_bytes([data[4], data[5]]);
1101        let text_index = u16::from_le_bytes([data[6], data[7]]);
1102
1103        messages.push(DiagnosticMessage {
1104            sub_index: i as u8,
1105            diag_code,
1106            flags,
1107            text_index,
1108            raw_data: data,
1109        });
1110    }
1111
1112    messages
1113}
1114
1115pub fn get_device_profile(master_index: u16, slave_index: u16) -> u16 {
1116    let mut size: c_int = 0;
1117    let ptr = unsafe {
1118        ffi::SDOread(master_index, slave_index, 0x1000, 0, 0, &mut size)
1119    };
1120    if ptr.is_null() || size < 4 {
1121        if !ptr.is_null() {
1122            unsafe { ffi::FreeMemory(ptr as *mut _) };
1123        }
1124        return 0;
1125    }
1126    let data = unsafe { std::slice::from_raw_parts(ptr, 4) };
1127    let device_type = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
1128    unsafe { ffi::FreeMemory(ptr as *mut _) };
1129    (device_type & 0xFFFF) as u16
1130}
1131
1132fn parse_oe_entries(oe_ptr: *const std::ffi::c_void, od_index: u16, max_sub: u8) -> Vec<ObjectEntry> {
1133    let base = oe_ptr as *const u8;
1134
1135    let oe_entries = unsafe {
1136        let p = base as *const u16;
1137        std::ptr::read_unaligned(p) as usize
1138    };
1139    let count = oe_entries.min(max_sub as usize + 1).min(256);
1140
1141    let mut entries = Vec::with_capacity(count);
1142    for i in 0..count {
1143        let value_info = unsafe { *base.add(2 + i) };
1144        let dt_raw = unsafe {
1145            let p = base.add(258 + i * 2) as *const u16;
1146            std::ptr::read_unaligned(p)
1147        };
1148        let bit_length = unsafe {
1149            let p = base.add(770 + i * 2) as *const u16;
1150            std::ptr::read_unaligned(p)
1151        };
1152        let access_raw = unsafe {
1153            let p = base.add(1282 + i * 2) as *const u16;
1154            std::ptr::read_unaligned(p)
1155        };
1156        let name = {
1157            let name_ptr = unsafe { base.add(1794 + i * 41) };
1158            let name_slice = unsafe { std::slice::from_raw_parts(name_ptr, 41) };
1159            let end = name_slice.iter().position(|&b| b == 0).unwrap_or(41);
1160
1161            crate::utils::help::decode_ethercat_string(&name_slice[..end])
1162        };
1163
1164        entries.push(ObjectEntry {
1165            od_index,
1166            sub_index: i as u8,
1167            name,
1168            data_type: EcDataType::from_raw(dt_raw),
1169            bit_length,
1170            access: ObjAccess(access_raw),
1171            value_info,
1172        });
1173    }
1174    entries
1175}
1176
1177use crate::data::types::SDOError;
1178use std::cell::Cell;
1179
1180#[derive(Debug, Clone)]
1181pub struct SdoReadResultEx {
1182
1183    pub data: Option<Vec<u8>>,
1184
1185    pub abort_code: u32,
1186
1187    pub wkc: i32,
1188}
1189
1190impl SdoReadResultEx {
1191
1192    pub fn is_ok(&self) -> bool { self.data.is_some() }
1193}
1194
1195#[derive(Debug, Clone, Copy)]
1196pub struct SdoWriteResultEx {
1197
1198    pub ok: bool,
1199
1200    pub abort_code: u32,
1201
1202    pub wkc: i32,
1203}
1204
1205pub struct CoEInstance {
1206    master_index: u16,
1207    slave_index: u16,
1208
1209    last_sdo_error: Cell<SDOError>,
1210}
1211
1212impl CoEInstance {
1213
1214    pub fn new(master_index: u16, slave_index: u16) -> Self {
1215        Self {
1216            master_index,
1217            slave_index,
1218            last_sdo_error: Cell::new(SDOError::NoError),
1219        }
1220    }
1221
1222    pub fn last_sdo_error(&self) -> SDOError {
1223        self.last_sdo_error.get()
1224    }
1225
1226    pub fn is_supported(&self) -> bool {
1227        let proto = unsafe { ffi::GetSlaveMailboxProto(self.master_index, self.slave_index) };
1228        (proto & 0x04) != 0
1229    }
1230
1231    pub fn sdo_read(&self, index: u16, subindex: u8, complete_access: bool) -> Result<Vec<u8>> {
1232        let mut size: c_int = 0;
1233        let ptr = unsafe {
1234            ffi::SDOread(self.master_index, self.slave_index, index, subindex,
1235                         if complete_access { 1 } else { 0 }, &mut size)
1236        };
1237        if ptr.is_null() || size <= 0 {
1238
1239            let abort = if size < 0 {
1240                let abort_code = (-size) as u32;
1241                let e = SDOError::from_u32(abort_code);
1242                self.last_sdo_error.set(e);
1243                Some(e)
1244            } else {
1245                self.last_sdo_error.set(SDOError::GeneralError);
1246                Some(SDOError::GeneralError)
1247            };
1248            if !ptr.is_null() { unsafe { ffi::FreeMemory(ptr as *mut _) }; }
1249            return Err(DarraError::SdoReadFailed { index, subindex, abort_code: abort });
1250        }
1251        let data = unsafe { std::slice::from_raw_parts(ptr, size as usize).to_vec() };
1252        unsafe { ffi::FreeMemory(ptr as *mut _) };
1253        self.last_sdo_error.set(SDOError::NoError);
1254        Ok(data)
1255    }
1256
1257    pub fn sdo_write(&self, index: u16, subindex: u8, complete_access: bool, data: &[u8]) -> Result<()> {
1258        let ok = unsafe {
1259            ffi::SDOwrite_raw(self.master_index, self.slave_index, index, subindex,
1260                              if complete_access { 1 } else { 0 }, data.as_ptr(), data.len() as c_int)
1261        };
1262        if ok == 0 {
1263            self.last_sdo_error.set(SDOError::GeneralError);
1264            let mut abort_opt: Option<SDOError> = Some(SDOError::GeneralError);
1265
1266            let mut probe_size: c_int = 0;
1267            let probe = unsafe {
1268                ffi::SDOread(self.master_index, self.slave_index, index, subindex, 0, &mut probe_size)
1269            };
1270            if probe.is_null() && probe_size < 0 {
1271                let abort_code = (-probe_size) as u32;
1272                let e = SDOError::from_u32(abort_code);
1273                self.last_sdo_error.set(e);
1274                abort_opt = Some(e);
1275            } else if !probe.is_null() {
1276                unsafe { ffi::FreeMemory(probe as *mut _) };
1277            }
1278            Err(DarraError::SdoWriteFailed { index, subindex, abort_code: abort_opt })
1279        } else {
1280            self.last_sdo_error.set(SDOError::NoError);
1281            Ok(())
1282        }
1283    }
1284
1285    pub fn sdo_read_ex(&self, index: u16, subindex: u8, complete_access: bool) -> SdoReadResultEx {
1286        let mut size: c_int = 0;
1287        let mut abort_code: u32 = 0;
1288        let mut wkc: c_int = 0;
1289        let ptr = unsafe {
1290            ffi::SDOread_ex(
1291                self.master_index, self.slave_index, index, subindex,
1292                if complete_access { 1 } else { 0 },
1293                &mut size, &mut abort_code, &mut wkc,
1294            )
1295        };
1296        if ptr.is_null() || size <= 0 {
1297            if !ptr.is_null() { unsafe { ffi::FreeMemory(ptr as *mut _) }; }
1298            self.last_sdo_error.set(SDOError::from_u32(abort_code));
1299            return SdoReadResultEx { data: None, abort_code, wkc };
1300        }
1301        let data = unsafe { std::slice::from_raw_parts(ptr, size as usize).to_vec() };
1302        unsafe { ffi::FreeMemory(ptr as *mut _) };
1303        self.last_sdo_error.set(SDOError::NoError);
1304        SdoReadResultEx { data: Some(data), abort_code, wkc }
1305    }
1306
1307    pub fn sdo_write_ex(&self, index: u16, subindex: u8, complete_access: bool, data: &[u8]) -> SdoWriteResultEx {
1308        let mut abort_code: u32 = 0;
1309        let mut wkc: c_int = 0;
1310        let ret = unsafe {
1311            ffi::SDOwrite_ex(
1312                self.master_index, self.slave_index, index, subindex,
1313                if complete_access { 1 } else { 0 },
1314                data.as_ptr(), data.len() as c_int,
1315                &mut abort_code, &mut wkc,
1316            )
1317        };
1318        let ok = ret != 0;
1319        if ok {
1320            self.last_sdo_error.set(SDOError::NoError);
1321        } else {
1322            self.last_sdo_error.set(SDOError::from_u32(abort_code));
1323        }
1324        SdoWriteResultEx { ok, abort_code, wkc }
1325    }
1326
1327    pub fn read_multiple(&self, entries: &[(u16, u8)]) -> HashMap<(u16, u8), Option<Vec<u8>>> {
1328        let mut results = HashMap::new();
1329        for &(index, subindex) in entries {
1330            let data = self.sdo_read(index, subindex, false).ok();
1331            results.insert((index, subindex), data);
1332        }
1333        results
1334    }
1335
1336    pub fn master_index(&self) -> u16 { self.master_index }
1337
1338    pub fn slave_index(&self) -> u16 { self.slave_index }
1339}
1340
1341#[derive(Debug, Clone)]
1342pub enum CoeError {
1343
1344    DllFailed { abort_code: u32 },
1345
1346    InvalidParameter(String),
1347
1348    InvalidResponse(String),
1349}
1350
1351impl std::fmt::Display for CoeError {
1352    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1353        match self {
1354            Self::DllFailed { abort_code } => {
1355                if *abort_code != 0 {
1356                    write!(f, "CoE DLL 失败 (Abort Code 0x{:08X})", abort_code)
1357                } else {
1358                    write!(f, "CoE DLL 失败")
1359                }
1360            }
1361            Self::InvalidParameter(msg) => write!(f, "CoE 无效参数: {}", msg),
1362            Self::InvalidResponse(msg) => write!(f, "CoE 响应异常: {}", msg),
1363        }
1364    }
1365}
1366
1367impl std::error::Error for CoeError {}
1368
1369impl From<CoeError> for DarraError {
1370    fn from(e: CoeError) -> Self {
1371        DarraError::Other(e.to_string())
1372    }
1373}
1374
1375#[derive(Debug, Clone, Copy, Default)]
1376pub struct DiagMeta {
1377
1378    pub max_messages: u8,
1379
1380    pub newest_message: u8,
1381
1382    pub newest_acknowledged: u8,
1383
1384    pub flags: u16,
1385}
1386
1387impl DiagMeta {
1388
1389    pub fn is_ring_buffer(&self) -> bool {
1390        (self.flags & 0x0010) != 0
1391    }
1392
1393    pub fn has_overrun(&self) -> bool {
1394        (self.flags & 0x0020) != 0
1395    }
1396}
1397
1398impl CoEInstance {
1399
1400    pub fn poll_has_new_diagnostic(&self) -> i32 {
1401        let mut abort_code: u32 = 0;
1402        unsafe {
1403            ffi::coe_diag_poll_new_available(self.master_index, self.slave_index, &mut abort_code)
1404        }
1405    }
1406
1407    pub fn read_diagnostic_meta(&self) -> std::result::Result<DiagMeta, CoeError> {
1408        let mut max_msgs: u8 = 0;
1409        let mut newest: u8 = 0;
1410        let mut acknowledged: u8 = 0;
1411        let mut flags: u16 = 0;
1412        let mut abort_code: u32 = 0;
1413        let rc = unsafe {
1414            ffi::coe_diag_read_meta(
1415                self.master_index,
1416                self.slave_index,
1417                &mut max_msgs,
1418                &mut newest,
1419                &mut acknowledged,
1420                &mut flags,
1421                &mut abort_code,
1422            )
1423        };
1424        if rc == 1 {
1425            Ok(DiagMeta {
1426                max_messages: max_msgs,
1427                newest_message: newest,
1428                newest_acknowledged: acknowledged,
1429                flags,
1430            })
1431        } else {
1432            Err(CoeError::DllFailed { abort_code })
1433        }
1434    }
1435
1436    pub fn read_diagnostic_message(
1437        &self,
1438        msg_idx: u16,
1439        buf_size: usize,
1440    ) -> std::result::Result<Vec<u8>, CoeError> {
1441        if !(6..=255).contains(&msg_idx) {
1442            return Err(CoeError::InvalidParameter(format!(
1443                "msg_idx 必须在 6..255 之间, 实际={}",
1444                msg_idx
1445            )));
1446        }
1447        if buf_size == 0 || buf_size > 64 * 1024 {
1448            return Err(CoeError::InvalidParameter(format!(
1449                "buf_size 必须在 1..65536 之间, 实际={}",
1450                buf_size
1451            )));
1452        }
1453
1454        let mut buf = vec![0u8; buf_size];
1455        let mut out_len: c_int = 0;
1456        let mut abort_code: u32 = 0;
1457        let rc = unsafe {
1458            ffi::coe_diag_read_message(
1459                self.master_index,
1460                self.slave_index,
1461                msg_idx as u8,
1462                buf.as_mut_ptr(),
1463                buf_size as c_int,
1464                &mut out_len,
1465                &mut abort_code,
1466            )
1467        };
1468        if rc == 1 {
1469            if out_len < 0 || out_len as usize > buf_size {
1470                return Err(CoeError::InvalidResponse(format!(
1471                    "DLL 返回 out_len={}, buf_size={}",
1472                    out_len, buf_size
1473                )));
1474            }
1475            buf.truncate(out_len as usize);
1476            Ok(buf)
1477        } else {
1478            Err(CoeError::DllFailed { abort_code })
1479        }
1480    }
1481
1482    pub fn acknowledge_diagnostic(&self, msg_idx: u16) -> std::result::Result<(), CoeError> {
1483        if !(6..=255).contains(&msg_idx) {
1484            return Err(CoeError::InvalidParameter(format!(
1485                "msg_idx 必须在 6..255 之间, 实际={}",
1486                msg_idx
1487            )));
1488        }
1489        let mut abort_code: u32 = 0;
1490        let rc = unsafe {
1491            ffi::coe_diag_acknowledge(
1492                self.master_index,
1493                self.slave_index,
1494                msg_idx as u8,
1495                &mut abort_code,
1496            )
1497        };
1498        if rc == 1 {
1499            Ok(())
1500        } else {
1501            Err(CoeError::DllFailed { abort_code })
1502        }
1503    }
1504}
1505
1506pub struct CaObjectEntry {
1507
1508    pub sub_index: u8,
1509
1510    pub data_type: u16,
1511
1512    pub bit_length: u16,
1513}
1514
1515pub fn parse_complete_access_data(data: &[u8], entries: &[CaObjectEntry]) -> Vec<(u8, Vec<u8>)> {
1516    let mut result = Vec::new();
1517    if data.is_empty() || entries.is_empty() {
1518        return result;
1519    }
1520
1521    let si0_bytes = 2usize;
1522    if data.len() >= si0_bytes {
1523        result.push((0, data[..si0_bytes].to_vec()));
1524    }
1525    let mut bit_offset = si0_bytes * 8;
1526
1527    for i in 1..entries.len() {
1528        let oe = &entries[i];
1529        let dt = oe.data_type;
1530        let is_bit_type = dt >= 0x0030 && dt <= 0x0037;
1531
1532        let bit_size = if is_bit_type {
1533            (dt - 0x0030 + 1) as usize
1534        } else if oe.bit_length > 0 {
1535            oe.bit_length as usize
1536        } else {
1537            8
1538        };
1539
1540        if !is_bit_type && (bit_offset % 8) != 0 {
1541            bit_offset = ((bit_offset + 7) / 8) * 8;
1542        }
1543
1544        let byte_start = bit_offset / 8;
1545        let bytes_needed = (bit_size + 7) / 8;
1546
1547        if byte_start + bytes_needed <= data.len() {
1548
1549            result.push((oe.sub_index, data[byte_start..byte_start + bytes_needed].to_vec()));
1550        }
1551
1552        bit_offset += bit_size;
1553    }
1554
1555    result
1556}
1557
1558impl CoEInstance {
1559
1560    pub fn sdo_read_blocking(
1561        master_index: u16,
1562        slave_index: u16,
1563        index: u16,
1564        subindex: u8,
1565        complete_access: bool,
1566    ) -> std::thread::JoinHandle<Result<Vec<u8>>> {
1567        std::thread::spawn(move || {
1568            let coe = CoEInstance::new(master_index, slave_index);
1569            coe.sdo_read(index, subindex, complete_access)
1570        })
1571    }
1572
1573    pub fn sdo_write_blocking(
1574        master_index: u16,
1575        slave_index: u16,
1576        index: u16,
1577        subindex: u8,
1578        complete_access: bool,
1579        data: Vec<u8>,
1580    ) -> std::thread::JoinHandle<Result<()>> {
1581        std::thread::spawn(move || {
1582            let coe = CoEInstance::new(master_index, slave_index);
1583            coe.sdo_write(index, subindex, complete_access, &data)
1584        })
1585    }
1586}
1587
1588#[cfg(feature = "async-tokio")]
1589impl CoEInstance {
1590
1591    pub async fn sdo_read_async(
1592        &self,
1593        index: u16,
1594        subindex: u8,
1595        complete_access: bool,
1596    ) -> Result<Vec<u8>> {
1597        let master = self.master_index;
1598        let slave = self.slave_index;
1599        tokio::task::spawn_blocking(move || {
1600            let coe = CoEInstance::new(master, slave);
1601            coe.sdo_read(index, subindex, complete_access)
1602        })
1603        .await
1604        .map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
1605    }
1606
1607    pub async fn sdo_write_async(
1608        &self,
1609        index: u16,
1610        subindex: u8,
1611        complete_access: bool,
1612        data: Vec<u8>,
1613    ) -> Result<()> {
1614        let master = self.master_index;
1615        let slave = self.slave_index;
1616        tokio::task::spawn_blocking(move || {
1617            let coe = CoEInstance::new(master, slave);
1618            coe.sdo_write(index, subindex, complete_access, &data)
1619        })
1620        .await
1621        .map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
1622    }
1623}
1624
1625impl crate::abstractions::MailboxProtocol for CoEInstance {
1626    fn protocol_type(&self) -> u8 { 0x03 }
1627    fn protocol_name(&self) -> &'static str { "CoE" }
1628
1629    fn is_supported(&self) -> bool {
1630        CoEInstance::is_supported(self)
1631    }
1632
1633    fn last_error_code(&self) -> u32 {
1634        self.last_sdo_error.get() as u32
1635    }
1636
1637    fn statistics(&self) -> crate::abstractions::MailboxStatistics {
1638        let mut stats = ffi::EcMbxStatsC::default();
1639        let rc = unsafe {
1640            ffi::mbx_get_stats_by_master(
1641                self.master_index, self.slave_index, 0x03, &mut stats,
1642            )
1643        };
1644        if rc == 1 {
1645            stats.into()
1646        } else {
1647            crate::abstractions::MailboxStatistics::empty()
1648        }
1649    }
1650
1651    fn reset_statistics(&self) {
1652        unsafe {
1653            ffi::mbx_reset_stats_by_master(self.master_index, self.slave_index, 0x03);
1654        }
1655    }
1656}