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}