darra-ethercat-master 2.0.2

商业 EtherCAT 主站协议栈 · 实时内核驱动 · 抖动 1µs · Windows + Linux · 多编程语言 · 全协议 · 支持复杂拓扑 + 热插拔 · ethercat.darra.xyz · Commercial EtherCAT Master protocol stack · Real-time kernel driver · 1µs jitter · Multi-platform · Multi-language · Complex topology + hot-plug.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
//! ETG.1510 主站对象字典
//!
//! 提供符合 ETG.1510 规范的主站对象字典,用于诊断工具和 HMI 访问。
//! 通过 master.master_od() 获取实例。
//!
//! 支持的对象索引范围:
//! - 0x1000: Device Type
//! - 0x1008: Manufacturer Device Name
//! - 0x1009: Manufacturer Hardware Version
//! - 0x100A: Manufacturer Software Version
//! - 0x1018: Identity Object
//! - 0x8nnn: Configuration Data (从站配置数据)
//! - 0x9nnn: Information Data (从站信息数据)
//! - 0xAnnn: Diagnosis Data (从站诊断数据)
//! - 0xF002: Detect Modules Command
//! - 0xF120: Master Diag Data
//! - 0xF200: Diag Interface Control

use crate::utils::ffi;
use crate::master::core::EtherCATMaster;

use std::sync::atomic::{AtomicU32, Ordering};

/// 全局实例计数器,用于生成唯一序列号
static INSTANCE_COUNTER: AtomicU32 = AtomicU32::new(0);

/// 主站 Vendor ID (ETG 分配,固定值)
pub const VENDOR_ID: u32 = 0x00001164;
/// 主站 Product Code (固定值)
pub const PRODUCT_CODE: u32 = 0x00000001;

/// ETG.1510 主站对象字典
///
/// 用于诊断工具和 HMI 通过 SDO 访问主站诊断信息。
///
/// # 示例
/// ```no_run
/// let master = EtherCATMaster::new().unwrap();
/// let od = master.master_od();
/// // 读取 Device Type
/// let data = od.read_object(0x1000, 0);
/// // 获取支持的对象列表
/// let indices = od.supported_object_indices();
/// ```
pub struct MasterObjectDictionary<'a> {
    master: &'a EtherCATMaster,
    /// 实例序列号(多主站实例区分)
    serial_number: u32,
}

impl<'a> MasterObjectDictionary<'a> {
    /// 创建主站对象字典实例 (内部使用, 通过 master.master_od() 获取)
    pub(crate) fn new(master: &'a EtherCATMaster) -> Self {
        let serial_number = INSTANCE_COUNTER.fetch_add(1, Ordering::SeqCst);
        Self { master, serial_number }
    }

    /// 主站 Vendor ID
    pub fn vendor_id(&self) -> u32 {
        VENDOR_ID
    }

    /// 主站 Product Code
    pub fn product_code(&self) -> u32 {
        PRODUCT_CODE
    }

    /// 主站 Revision Number(从 DLL 版本派生: 高字=主版本, 低字=次版本)
    pub fn revision_number(&self) -> u32 {
        match EtherCATMaster::dll_version() {
            Some((major, minor, _, _)) => ((major as u32) << 16) | (minor as u32),
            None => 0,
        }
    }

    /// 主站 Serial Number(按实例自增)
    pub fn serial_number(&self) -> u32 {
        self.serial_number
    }

    /// 读取主站对象字典 (SDO Read)
    ///
    /// 根据 ETG.1510 规范实现主站诊断对象的读取。
    ///
    /// # 参数
    /// - `index`: 对象索引
    /// - `subindex`: 子索引
    ///
    /// # 返回
    /// 读取的数据,失败返回 None
    pub fn read_object(&self, index: u16, subindex: u8) -> Option<Vec<u8>> {
        match index {
            // Device Type (EtherCAT Master, 0x000011xx)
            0x1000 => {
                Some(0x000011A4u32.to_le_bytes().to_vec())
            }
            // Manufacturer Device Name
            0x1008 => {
                Some(b"Darra EtherCAT Master".to_vec())
            }
            // Manufacturer Hardware Version
            0x1009 => {
                Some(b"1.0".to_vec())
            }
            // Manufacturer Software Version
            0x100A => {
                match EtherCATMaster::dll_version() {
                    Some((major, minor, patch, build)) => {
                        let ver = format!("{}.{}.{}.{}", major, minor, patch, build);
                        Some(ver.into_bytes())
                    }
                    None => Some(b"0.0.0.0".to_vec()),
                }
            }
            // Identity Object
            0x1018 => {
                self.read_identity_object(subindex)
            }
            // Configuration Data (从站配置)
            0x8000..=0x8FFF => {
                let slave_idx = index & 0x0FFF;
                self.read_slave_config_data(slave_idx, subindex)
            }
            // Information Data (从站信息)
            0x9000..=0x9FFF => {
                let slave_idx = index & 0x0FFF;
                self.read_slave_info_data(slave_idx, subindex)
            }
            // Diagnosis Data (从站诊断)
            0xA000..=0xAFFF => {
                let slave_idx = index & 0x0FFF;
                self.read_slave_diag_data(slave_idx, subindex)
            }
            // Detect Modules Command
            0xF002 => {
                self.read_detect_modules(subindex)
            }
            // Master Diag Data
            0xF120 => {
                self.read_master_diag_data(subindex)
            }
            // Diag Interface Control
            0xF200 => {
                // 返回默认控制状态
                Some(vec![0u8; 4])
            }
            _ => None,
        }
    }

    /// 写入主站对象字典 (SDO Write)
    ///
    /// # 参数
    /// - `index`: 对象索引
    /// - `subindex`: 子索引
    /// - `data`: 要写入的数据
    ///
    /// # 返回
    /// 成功返回 true
    pub fn write_object(&self, index: u16, subindex: u8, data: &[u8]) -> bool {
        match index {
            // Detect Modules Command (可写)
            0xF002 => {
                // subindex 1 = 扫描命令
                subindex == 1 && !data.is_empty()
            }
            // Diag Interface Control (可写)
            0xF200 => {
                !data.is_empty()
            }
            // Diagnosis Data 中部分子索引可写 (重置诊断)
            0xA000..=0xAFFF => {
                // subindex 0 写入触发重置
                subindex == 0 && !data.is_empty()
            }
            // 其他对象均为只读
            _ => false,
        }
    }

    /// 获取对象名称
    pub fn object_name(&self, index: u16) -> &str {
        match index {
            0x1000 => "Device Type",
            0x1008 => "Manufacturer Device Name",
            0x1009 => "Manufacturer Hardware Version",
            0x100A => "Manufacturer Software Version",
            0x1018 => "Identity Object",
            0x8000..=0x8FFF => "Configuration Data",
            0x9000..=0x9FFF => "Information Data",
            0xA000..=0xAFFF => "Diagnosis Data",
            0xF002 => "Detect Modules Command",
            0xF120 => "Master Diag Data",
            0xF200 => "Diag Interface Control",
            _ => "Unknown",
        }
    }

    /// 获取对象的子索引数量
    pub fn subindex_count(&self, index: u16) -> u8 {
        match index {
            0x1000 | 0x1008 | 0x1009 | 0x100A => 0,
            0x1018 => 4,
            0x8000..=0x8FFF => 40,
            0x9000..=0x9FFF => 32,
            0xA000..=0xAFFF => 19,
            0xF002 => 3,
            0xF120 | 0xF200 => 16,
            _ => 0,
        }
    }

    /// 获取支持的对象索引列表
    pub fn supported_object_indices(&self) -> Vec<u16> {
        let mut indices = vec![
            0x1000, 0x1008, 0x1009, 0x100A, 0x1018,
            0xF002, 0xF120, 0xF200,
        ];

        // 添加从站相关对象 (每个从站有 3 个对象索引)
        let slave_count = self.slave_count();
        for i in 1..=slave_count {
            indices.push(0x8000 | i);  // Configuration Data
            indices.push(0x9000 | i);  // Information Data
            indices.push(0xA000 | i);  // Diagnosis Data
        }

        indices
    }

    // ===================== 内部辅助方法 =====================

    /// 获取从站数量
    fn slave_count(&self) -> u16 {
        // 通过组0的从站数量获取
        unsafe { ffi::GetGroupSlaveCount(self.master.index(), 0) }
    }

    /// 读取 Identity Object (0x1018)
    fn read_identity_object(&self, subindex: u8) -> Option<Vec<u8>> {
        match subindex {
            // 子索引数量
            0 => Some(vec![4]),
            // Vendor ID
            1 => Some(VENDOR_ID.to_le_bytes().to_vec()),
            // Product Code
            2 => Some(PRODUCT_CODE.to_le_bytes().to_vec()),
            // Revision Number
            3 => Some(self.revision_number().to_le_bytes().to_vec()),
            // Serial Number
            4 => Some(self.serial_number.to_le_bytes().to_vec()),
            _ => None,
        }
    }

    /// 读取从站配置数据 (0x8nnn)
    fn read_slave_config_data(&self, slave_idx: u16, subindex: u8) -> Option<Vec<u8>> {
        if slave_idx == 0 { return None; }

        let slave = crate::slave::Slave::new(self.master.index(), slave_idx);

        match subindex {
            // 子索引数量
            0 => Some(vec![40]),
            // 从站身份 (Vendor ID, Product Code, Revision, Serial)
            1 => {
                match slave.identity() {
                    Ok(id) => Some(id.vendor_id.to_le_bytes().to_vec()),
                    Err(_) => Some(vec![0; 4]),
                }
            }
            2 => {
                match slave.identity() {
                    Ok(id) => Some(id.product_code.to_le_bytes().to_vec()),
                    Err(_) => Some(vec![0; 4]),
                }
            }
            3 => {
                match slave.identity() {
                    Ok(id) => Some(id.revision_no.to_le_bytes().to_vec()),
                    Err(_) => Some(vec![0; 4]),
                }
            }
            4 => {
                match slave.identity() {
                    Ok(id) => Some(id.serial_no.to_le_bytes().to_vec()),
                    Err(_) => Some(vec![0; 4]),
                }
            }
            _ => Some(vec![0; 4]),
        }
    }

    /// 读取从站信息数据 (0x9nnn)
    fn read_slave_info_data(&self, slave_idx: u16, subindex: u8) -> Option<Vec<u8>> {
        if slave_idx == 0 { return None; }

        let slave = crate::slave::Slave::new(self.master.index(), slave_idx);

        match subindex {
            // 子索引数量
            0 => Some(vec![32]),
            // 从站当前状态
            1 => {
                let state = slave.state().map(|s| s as u8).unwrap_or(0);
                Some(vec![state])
            }
            // AL Status Code
            2 => Some(slave.error_code_raw().to_le_bytes().to_vec()),
            _ => Some(vec![0; 4]),
        }
    }

    /// 读取从站诊断数据 (0xAnnn)
    fn read_slave_diag_data(&self, slave_idx: u16, subindex: u8) -> Option<Vec<u8>> {
        if slave_idx == 0 { return None; }

        let slave = crate::slave::Slave::new(self.master.index(), slave_idx);

        match subindex {
            // 子索引数量
            0 => Some(vec![19]),
            // 从站当前状态
            1 => {
                let state = slave.state().map(|s| s as u8).unwrap_or(0);
                Some(vec![state])
            }
            // AL Status Code
            2 => Some(slave.error_code_raw().to_le_bytes().to_vec()),
            // 链路质量
            3 => {
                let quality = unsafe {
                    ffi::GetSlaveLinkQuality(self.master.index(), slave_idx)
                };
                Some(quality.to_le_bytes().to_vec())
            }
            _ => Some(vec![0; 4]),
        }
    }

    /// 读取 Detect Modules Command (0xF002)
    fn read_detect_modules(&self, subindex: u8) -> Option<Vec<u8>> {
        match subindex {
            // 子索引数量
            0 => Some(vec![3]),
            // 扫描命令状态 (1=就绪)
            1 => Some(vec![1]),
            // 已发现从站数量
            2 => {
                let count = self.slave_count();
                Some(count.to_le_bytes().to_vec())
            }
            // 扫描结果
            3 => Some(vec![0]),
            _ => None,
        }
    }

    /// 读取 Master Diag Data (0xF120)
    fn read_master_diag_data(&self, subindex: u8) -> Option<Vec<u8>> {
        match subindex {
            // 子索引数量
            0 => Some(vec![16]),
            _ => {
                // 尝试从 DLL 获取诊断数据
                let mut diag = ffi::MasterDiagData {
                    cyclic_lost_frames: 0,
                    acyclic_lost_frames: 0,
                    cyclic_frames_per_sec: 0,
                    acyclic_frames_per_sec: 0,
                    master_state: 0,
                };
                let ok = unsafe {
                    ffi::GetMasterDiagData(self.master.index(), &mut diag)
                };
                if ok == 0 { return Some(vec![0; 4]); }

                match subindex {
                    // 循环丢帧数
                    1 => Some(diag.cyclic_lost_frames.to_le_bytes().to_vec()),
                    // 非循环丢帧数
                    2 => Some(diag.acyclic_lost_frames.to_le_bytes().to_vec()),
                    // 每秒循环帧数
                    3 => Some(diag.cyclic_frames_per_sec.to_le_bytes().to_vec()),
                    // 每秒非循环帧数
                    4 => Some(diag.acyclic_frames_per_sec.to_le_bytes().to_vec()),
                    // 主站状态
                    5 => Some(diag.master_state.to_le_bytes().to_vec()),
                    _ => Some(vec![0; 4]),
                }
            }
        }
    }
}

impl<'a> std::fmt::Display for MasterObjectDictionary<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "ETG.1510 主站对象字典 (VendorID=0x{:08X}, Serial={})",
            self.vendor_id(),
            self.serial_number
        )
    }
}