//! CoE 对象字典 (OD) 树遍历
//!
//! 封装 GetSlaveSDOList / GetSlavePointer_SDO_WithODList，
//! 提供类型化的 OD 条目读写、访问权限检查、诊断消息读取与人类可读的打印输出。
//!
//! 对齐 C# CoEInstance / ObjectEntry / ObjectDictionary / EcODList / OEDictionary
//!
//! # 异步使用 (对齐 C# SDOReadAsync / SDOWriteAsync)
//!
//! 本 crate 提供**双轨**异步 API, 覆盖"零依赖"和"tokio 生态"两个场景:
//!
//! ## 轨道 1: 原生 std::thread (无依赖, 默认启用)
//!
//! `CoEInstance::sdo_read_blocking` / `sdo_write_blocking` 返回
//! `std::thread::JoinHandle<Result<...>>`, 用户用 `.join()` 等待结果,
//! 或通过 `.is_finished()` 轮询。
//!
//! ```ignore
//! use ethercat::slave::coe::CoEInstance;
//!
//! let handle = CoEInstance::sdo_read_blocking(0, 1, 0x6041, 0, false);
//! // 此时 FFI 调用已在后台线程执行
//! let result = handle.join().unwrap();   // Result<Vec<u8>>
//! ```
//!
//! ## 轨道 2: tokio async (可选 feature `async-tokio`)
//!
//! 在 Cargo.toml 启用 feature:
//!
//! ```toml
//! [dependencies]
//! darra-ethercat = { version = "0.4", features = ["async-tokio"] }
//! ```
//!
//! 然后使用 `*_async` 方法, 返回 `Future`:
//!
//! ```ignore
//! let coe = slave.coe();  // CoEInstance
//! let data = coe.sdo_read_async(0x6041, 0, false).await?;
//! coe.sdo_write_async(0x6040, 0, false, vec![0x0F, 0x00]).await?;
//! ```
//!
//! ## 方案 3: 自行包装 (async-std / smol 等)
//!
//! ```ignore
//! let result = async_std::task::spawn_blocking(move || {
//!     let coe = CoEInstance::new(master_index, slave_index);
//!     coe.sdo_read(0x6041, 0, false)
//! }).await;
//! ```
//!
//! ## 超时 / 取消
//!
//! 使用 runtime 提供的 timeout 原语 (如 `tokio::time::timeout`) 包装
//! spawn_blocking future。注意: 底层 FFI 调用一旦启动**无法中断**,
//! timeout 只让 await 点返回, 后台线程仍会跑到 DLL 返回。
//! 这与 Python `run_in_executor` 和 C# `SDOReadAsync(CancellationToken)`
//! 在取消对 legacy 同步 API 的限制一致。
//!
//! ## Send 标记
//!
//! `CoE<'_>` 内部仅持有 `master_index` / `slave_index` 等标量, 不持有
//! 跨线程不安全的原始指针, 因此可安全跨线程传递 (`Send`)。
//! FFI 调用的线程安全性由 Darra.Core.dll 自身保证 (内部锁)。

use std::fmt;
use std::collections::HashMap;
use crate::data::error::{DarraError, Result};
use crate::utils::ffi;
use std::os::raw::c_int;

// ===================== 数据类型枚举 =====================

/// EtherCAT CoE 数据类型 (按 CANopen CiA 301 定义)
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum EcDataType {
    /// 未知类型
    Unknown    = 0x0000,
    /// 布尔值
    Boolean    = 0x0001,
    /// 有符号 8 位整数
    Integer8   = 0x0002,
    /// 有符号 16 位整数
    Integer16  = 0x0003,
    /// 有符号 32 位整数
    Integer32  = 0x0004,
    /// 无符号 8 位整数
    Unsigned8  = 0x0005,
    /// 无符号 16 位整数
    Unsigned16 = 0x0006,
    /// 无符号 32 位整数
    Unsigned32 = 0x0007,
    /// 32 位浮点数
    Real32     = 0x0008,
    /// 可见字符串
    VisibleString = 0x0009,
    /// 八位字节字符串
    OctetString = 0x000A,
    /// Unicode 字符串
    UnicodeString = 0x000B,
    /// 日期时间
    TimeOfDay  = 0x000C,
    /// 时间差
    TimeDifference = 0x000D,
    /// 域对象
    Domain     = 0x000F,
    /// 有符号 24 位整数
    Integer24  = 0x0010,
    /// 64 位浮点数
    Real64     = 0x0011,
    /// 有符号 40 位整数
    Integer40  = 0x0012,
    /// 有符号 48 位整数
    Integer48  = 0x0013,
    /// 有符号 56 位整数
    Integer56  = 0x0014,
    /// 有符号 64 位整数
    Integer64  = 0x0015,
    /// 无符号 24 位整数
    Unsigned24 = 0x0016,
    /// 无符号 40 位整数
    Unsigned40 = 0x0018,
    /// 无符号 48 位整数
    Unsigned48 = 0x0019,
    /// 无符号 56 位整数
    Unsigned56 = 0x001A,
    /// 无符号 64 位整数
    Unsigned64 = 0x001B,
    /// GUID
    Guid       = 0x001D,
    /// 字节
    Byte       = 0x001E,
    /// 字 (16 位)
    Word       = 0x001F,
    /// 双字 (32 位)
    DWord      = 0x0020,
    /// PDO 映射参数
    PdoMapping = 0x0021,
    /// 同步管理器通信类型
    SmComType  = 0x0022,
    /// 身份对象
    Identity   = 0x0023,
    /// 1 位 (位域)
    Bit1       = 0x0030,
    /// 2 位 (位域)
    Bit2       = 0x0031,
    /// 3 位 (位域)
    Bit3       = 0x0032,
    /// 4 位 (位域)
    Bit4       = 0x0033,
    /// 5 位 (位域)
    Bit5       = 0x0034,
    /// 6 位 (位域)
    Bit6       = 0x0035,
    /// 7 位 (位域)
    Bit7       = 0x0036,
    /// 8 位 (位域)
    Bit8       = 0x0037,
}

impl EcDataType {
    /// 从原始 u16 值创建数据类型
    pub fn from_raw(val: u16) -> Self {
        match val {
            0x0000 => Self::Unknown,
            0x0001 => Self::Boolean,
            0x0002 => Self::Integer8,
            0x0003 => Self::Integer16,
            0x0004 => Self::Integer32,
            0x0005 => Self::Unsigned8,
            0x0006 => Self::Unsigned16,
            0x0007 => Self::Unsigned32,
            0x0008 => Self::Real32,
            0x0009 => Self::VisibleString,
            0x000A => Self::OctetString,
            0x000B => Self::UnicodeString,
            0x000C => Self::TimeOfDay,
            0x000D => Self::TimeDifference,
            0x000F => Self::Domain,
            0x0010 => Self::Integer24,
            0x0011 => Self::Real64,
            0x0012 => Self::Integer40,
            0x0013 => Self::Integer48,
            0x0014 => Self::Integer56,
            0x0015 => Self::Integer64,
            0x0016 => Self::Unsigned24,
            0x0018 => Self::Unsigned40,
            0x0019 => Self::Unsigned48,
            0x001A => Self::Unsigned56,
            0x001B => Self::Unsigned64,
            0x001D => Self::Guid,
            0x001E => Self::Byte,
            0x001F => Self::Word,
            0x0020 => Self::DWord,
            0x0021 => Self::PdoMapping,
            0x0022 => Self::SmComType,
            0x0023 => Self::Identity,
            0x0030 => Self::Bit1,
            0x0031 => Self::Bit2,
            0x0032 => Self::Bit3,
            0x0033 => Self::Bit4,
            0x0034 => Self::Bit5,
            0x0035 => Self::Bit6,
            0x0036 => Self::Bit7,
            0x0037 => Self::Bit8,
            _ => Self::Unknown,
        }
    }

    /// 返回该类型对应的字节宽度
    /// 对于字符串类型返回 None（长度可变）
    pub fn byte_size(self) -> Option<usize> {
        match self {
            Self::Boolean | Self::Integer8 | Self::Unsigned8 | Self::Byte => Some(1),
            Self::Integer16 | Self::Unsigned16 | Self::Word => Some(2),
            Self::Integer24 | Self::Unsigned24 => Some(3),
            Self::Integer32 | Self::Unsigned32 | Self::Real32 | Self::DWord => Some(4),
            Self::Integer40 | Self::Unsigned40 => Some(5),
            Self::Integer48 | Self::Unsigned48 => Some(6),
            Self::Integer56 | Self::Unsigned56 => Some(7),
            Self::Integer64 | Self::Unsigned64 | Self::Real64 => Some(8),
            _ => None,
        }
    }
}

impl fmt::Display for EcDataType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let name = match self {
            Self::Unknown       => "UNKNOWN",
            Self::Boolean       => "BOOL",
            Self::Integer8      => "INT8",
            Self::Integer16     => "INT16",
            Self::Integer32     => "INT32",
            Self::Integer64     => "INT64",
            Self::Integer24     => "INT24",
            Self::Integer40     => "INT40",
            Self::Integer48     => "INT48",
            Self::Integer56     => "INT56",
            Self::Unsigned8     => "UINT8",
            Self::Unsigned16    => "UINT16",
            Self::Unsigned32    => "UINT32",
            Self::Unsigned24    => "UINT24",
            Self::Unsigned40    => "UINT40",
            Self::Unsigned48    => "UINT48",
            Self::Unsigned56    => "UINT56",
            Self::Unsigned64    => "UINT64",
            Self::Real32        => "FLOAT",
            Self::Real64        => "DOUBLE",
            Self::VisibleString => "STRING",
            Self::OctetString   => "OCTET_STRING",
            Self::UnicodeString => "UNICODE_STRING",
            Self::TimeOfDay     => "TIME_OF_DAY",
            Self::TimeDifference => "TIME_DIFF",
            Self::Domain        => "DOMAIN",
            Self::Guid          => "GUID",
            Self::Byte          => "BYTE",
            Self::Word          => "WORD",
            Self::DWord         => "DWORD",
            Self::PdoMapping    => "PDO_MAP",
            Self::SmComType     => "SM_COM_TYPE",
            Self::Identity      => "IDENTITY",
            Self::Bit1          => "BIT1",
            Self::Bit2          => "BIT2",
            Self::Bit3          => "BIT3",
            Self::Bit4          => "BIT4",
            Self::Bit5          => "BIT5",
            Self::Bit6          => "BIT6",
            Self::Bit7          => "BIT7",
            Self::Bit8          => "BIT8",
        };
        write!(f, "{}", name)
    }
}

// ===================== 访问权限 bitflags =====================

/// OD 条目访问权限位标志 (CiA 301 Table 67)
/// 对齐 C# CoEObjectAccess
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ObjAccess(pub u16);

impl ObjAccess {
    /// 无权限
    pub const NONE:          u16 = 0x0000;
    /// 预操作状态可读
    pub const READ_PREOP:    u16 = 0x0001;
    /// 安全操作状态可读
    pub const READ_SAFEOP:   u16 = 0x0002;
    /// 操作状态可读
    pub const READ_OP:       u16 = 0x0004;
    /// 预操作状态可写
    pub const WRITE_PREOP:   u16 = 0x0008;
    /// 安全操作状态可写
    pub const WRITE_SAFEOP:  u16 = 0x0010;
    /// 操作状态可写
    pub const WRITE_OP:      u16 = 0x0020;
    /// 可映射到 RxPDO
    pub const MAP_RXPDO:     u16 = 0x0040;
    /// 可映射到 TxPDO / 备份参数
    pub const MAP_TXPDO:     u16 = 0x0080;
    /// 可保存到 EEPROM / 配置参数
    pub const SETTINGS:      u16 = 0x0100;

    /// 任意状态可读
    pub const READ_ANY:  u16 = Self::READ_PREOP | Self::READ_SAFEOP | Self::READ_OP;
    /// 任意状态可写
    pub const WRITE_ANY: u16 = Self::WRITE_PREOP | Self::WRITE_SAFEOP | Self::WRITE_OP;

    /// 检查是否有读取权限（任意状态）(对齐 C# CanRead)
    pub fn can_read(self) -> bool {
        (self.0 & Self::READ_ANY) != 0
    }

    /// 检查是否有写入权限（任意状态）(对齐 C# CanWrite)
    pub fn can_write(self) -> bool {
        (self.0 & Self::WRITE_ANY) != 0
    }

    /// 是否只读 (对齐 C# IsReadOnly)
    pub fn is_read_only(self) -> bool {
        self.can_read() && !self.can_write()
    }

    /// PreOP 状态下是否可写 (对齐 C# CanWritePreOp)
    pub fn can_write_preop(self) -> bool {
        (self.0 & Self::WRITE_PREOP) != 0
    }

    /// SafeOP 状态下是否可写 (含 PreOP 继承, 对齐 C# CanWriteSafeOp)
    pub fn can_write_safeop(self) -> bool {
        (self.0 & (Self::WRITE_PREOP | Self::WRITE_SAFEOP)) != 0
    }

    /// OP 状态下是否可写 (含所有继承, 对齐 C# CanWriteOp)
    pub fn can_write_op(self) -> bool {
        (self.0 & Self::WRITE_ANY) != 0
    }

    /// 检查是否可映射到 RxPDO
    pub fn rxpdo_mappable(self) -> bool {
        (self.0 & Self::MAP_RXPDO) != 0
    }

    /// 检查是否可映射到 TxPDO
    pub fn txpdo_mappable(self) -> bool {
        (self.0 & Self::MAP_TXPDO) != 0
    }

    /// 兼容旧 API
    pub fn readable(self) -> bool { self.can_read() }
    /// 兼容旧 API
    pub fn writable(self) -> bool { self.can_write() }

    /// 获取访问权限中文描述 (对齐 C# AccessDescription)
    pub fn access_description(self) -> String {
        let can_read = self.can_read();
        let can_write = self.can_write();
        if can_read && can_write {
            let mut parts = Vec::new();
            if (self.0 & Self::WRITE_PREOP) != 0 { parts.push("PreOP"); }
            if (self.0 & Self::WRITE_SAFEOP) != 0 { parts.push("SafeOP"); }
            if (self.0 & Self::WRITE_OP) != 0 { parts.push("OP"); }
            if parts.is_empty() {
                "读写".to_string()
            } else {
                format!("读写({}可写)", parts.join(","))
            }
        } else if can_read {
            "只读".to_string()
        } else if can_write {
            let mut parts = Vec::new();
            if (self.0 & Self::WRITE_PREOP) != 0 { parts.push("PreOP"); }
            if (self.0 & Self::WRITE_SAFEOP) != 0 { parts.push("SafeOP"); }
            if (self.0 & Self::WRITE_OP) != 0 { parts.push("OP"); }
            if parts.is_empty() {
                "只写".to_string()
            } else {
                format!("只写({}可写)", parts.join(","))
            }
        } else {
            "无权限".to_string()
        }
    }
}

impl fmt::Display for ObjAccess {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut parts = Vec::new();
        if (self.0 & Self::READ_PREOP) != 0  { parts.push("R(PreOp)"); }
        if (self.0 & Self::READ_SAFEOP) != 0 { parts.push("R(SafeOp)"); }
        if (self.0 & Self::READ_OP) != 0     { parts.push("R(Op)"); }
        if (self.0 & Self::WRITE_PREOP) != 0  { parts.push("W(PreOp)"); }
        if (self.0 & Self::WRITE_SAFEOP) != 0 { parts.push("W(SafeOp)"); }
        if (self.0 & Self::WRITE_OP) != 0     { parts.push("W(Op)"); }
        if (self.0 & Self::MAP_RXPDO) != 0   { parts.push("RxPDO"); }
        if (self.0 & Self::MAP_TXPDO) != 0   { parts.push("TxPDO"); }
        write!(f, "[{}]", parts.join("|"))
    }
}

// ===================== 诊断消息 (对齐 C# DiagnosticMessage) =====================

/// ETG.1510 诊断消息
#[derive(Debug, Clone)]
pub struct DiagnosticMessage {
    /// 子索引
    pub sub_index: u8,
    /// 诊断代码
    pub diag_code: u32,
    /// 标志位
    pub flags: u16,
    /// 文本索引
    pub text_index: u16,
    /// 原始数据
    pub raw_data: Vec<u8>,
}

impl fmt::Display for DiagnosticMessage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Diag[{}]: Code=0x{:08X}, Flags=0x{:04X}",
               self.sub_index, self.diag_code, self.flags)
    }
}

// ===================== CoE 访问权限异常 (对齐 C# CoE_AccessDeniedException) =====================

/// CoE 访问权限错误
#[derive(Debug, Clone)]
pub struct CoEAccessDenied {
    pub index: u16,
    pub sub_index: u8,
    pub entry_name: String,
    pub is_read_operation: bool,
    pub obj_access: u16,
    pub message: String,
}

impl CoEAccessDenied {
    /// 创建访问被拒绝错误
    pub fn new(index: u16, sub_index: u8, entry_name: &str,
               is_read_operation: bool, obj_access: u16) -> Self {
        let operation = if is_read_operation { "读取" } else { "写入" };
        let access_desc = ObjAccess(obj_access).access_description();
        let message = format!(
            "对象条目 0x{:04X}:{:02X} ({}) 不支持{}操作。访问权限: {}",
            index, sub_index, entry_name, operation, access_desc
        );
        Self { index, sub_index, entry_name: entry_name.to_string(),
               is_read_operation, obj_access, message }
    }

    /// 创建带自定义消息的错误 (对齐 C# CreateWithCustomMessage)
    pub fn create_with_custom_message(
        index: u16, sub_index: u8, custom_message: &str,
        is_read_operation: bool, obj_access: u16,
    ) -> Self {
        Self { index, sub_index, entry_name: custom_message.to_string(),
               is_read_operation, obj_access, message: custom_message.to_string() }
    }
}

impl fmt::Display for CoEAccessDenied {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.message)
    }
}

impl std::error::Error for CoEAccessDenied {}

// ===================== 对象条目 =====================

/// OD 对象条目 (Object Entry)，对应 CoE OE 子索引
/// 对齐 C# ObjectEntry
#[derive(Debug, Clone)]
pub struct ObjectEntry {
    /// 父对象索引 (对齐 C# ODIndex)
    pub od_index: u16,
    /// 子索引
    pub sub_index: u8,
    /// 条目名称
    pub name: String,
    /// 数据类型 (对齐 C# Datatype / DataType)
    pub data_type: EcDataType,
    /// 位长度
    pub bit_length: u16,
    /// 访问权限 (对齐 C# ObjAccess)
    pub access: ObjAccess,
    /// ValueInfo 标志
    pub value_info: u8,
}

impl ObjectEntry {
    /// 字节长度 (向上取整)
    pub fn byte_length(&self) -> usize {
        ((self.bit_length as usize) + 7) / 8
    }

    /// 数据类型 (别名, 对齐 C# Datatype)
    pub fn datatype(&self) -> EcDataType {
        self.data_type
    }

    // ---- 访问权限便捷方法 (对齐 C#) ----

    /// 是否可读 (对齐 C# CanRead)
    pub fn can_read(&self) -> bool { self.access.can_read() }

    /// 是否可写 (对齐 C# CanWrite)
    pub fn can_write(&self) -> bool { self.access.can_write() }

    /// 是否只读
    pub fn is_read_only(&self) -> bool { self.access.is_read_only() }

    /// PreOP 下是否可写 (对齐 C# CanWritePreOp)
    pub fn can_write_preop(&self) -> bool { self.access.can_write_preop() }

    /// SafeOP 下是否可写 (对齐 C# CanWriteSafeOp)
    pub fn can_write_safeop(&self) -> bool { self.access.can_write_safeop() }

    /// OP 下是否可写 (对齐 C# CanWriteOp)
    pub fn can_write_op(&self) -> bool { self.access.can_write_op() }

    /// 获取访问权限描述 (对齐 C# AccessDescription)
    pub fn access_description(&self) -> String {
        self.access.access_description()
    }

    // ---- SDO 读写 ----

    /// 通过 SDO 读取该条目的原始字节
    pub fn read_raw(&self, master_index: u16, slave_index: u16) -> Result<Vec<u8>> {
        if !self.can_read() {
            return Err(DarraError::SdoReadFailed {
                index: self.od_index, subindex: self.sub_index,
            });
        }
        let mut size: c_int = 0;
        let ptr = unsafe {
            ffi::SDOread(master_index, slave_index, self.od_index, self.sub_index, 0, &mut size)
        };
        if ptr.is_null() || size <= 0 {
            if !ptr.is_null() {
                unsafe { ffi::FreeMemory(ptr as *mut _) };
            }
            return Err(DarraError::SdoReadFailed {
                index: self.od_index, subindex: self.sub_index,
            });
        }
        let data = unsafe { std::slice::from_raw_parts(ptr, size as usize).to_vec() };
        unsafe { ffi::FreeMemory(ptr as *mut _) };
        Ok(data)
    }

    /// 读取原始字节 (Bytes 属性, 对齐 C# Bytes getter)
    /// 优先尝试 Complete Access，失败时回退
    pub fn bytes(&self, master_index: u16, slave_index: u16) -> Result<Vec<u8>> {
        self.read_raw(master_index, slave_index)
    }

    /// 通过 SDO 读取并转换为 u8
    pub fn read_u8(&self, master_index: u16, slave_index: u16) -> Result<u8> {
        let data = self.read_raw(master_index, slave_index)?;
        data.first().copied().ok_or(DarraError::SdoReadFailed {
            index: self.od_index, subindex: self.sub_index,
        })
    }

    /// 通过 SDO 读取并转换为 u16
    pub fn read_u16(&self, master_index: u16, slave_index: u16) -> Result<u16> {
        let data = self.read_raw(master_index, slave_index)?;
        if data.len() < 2 {
            return Err(DarraError::SdoReadFailed { index: self.od_index, subindex: self.sub_index });
        }
        Ok(u16::from_le_bytes([data[0], data[1]]))
    }

    /// 通过 SDO 读取并转换为 u32
    pub fn read_u32(&self, master_index: u16, slave_index: u16) -> Result<u32> {
        let data = self.read_raw(master_index, slave_index)?;
        if data.len() < 4 {
            return Err(DarraError::SdoReadFailed { index: self.od_index, subindex: self.sub_index });
        }
        Ok(u32::from_le_bytes([data[0], data[1], data[2], data[3]]))
    }

    /// 通过 SDO 读取并转换为 u64
    pub fn read_u64(&self, master_index: u16, slave_index: u16) -> Result<u64> {
        let data = self.read_raw(master_index, slave_index)?;
        if data.len() < 8 {
            return Err(DarraError::SdoReadFailed { index: self.od_index, subindex: self.sub_index });
        }
        Ok(u64::from_le_bytes([
            data[0], data[1], data[2], data[3],
            data[4], data[5], data[6], data[7],
        ]))
    }

    /// 通过 SDO 读取并转换为 i32
    pub fn read_i32(&self, master_index: u16, slave_index: u16) -> Result<i32> {
        Ok(self.read_u32(master_index, slave_index)? as i32)
    }

    /// 通过 SDO 读取并转换为 i64
    pub fn read_i64(&self, master_index: u16, slave_index: u16) -> Result<i64> {
        Ok(self.read_u64(master_index, slave_index)? as i64)
    }

    /// 通过 SDO 读取并转换为 f32
    pub fn read_f32(&self, master_index: u16, slave_index: u16) -> Result<f32> {
        let bits = self.read_u32(master_index, slave_index)?;
        Ok(f32::from_bits(bits))
    }

    /// 通过 SDO 读取并转换为 f64
    pub fn read_f64(&self, master_index: u16, slave_index: u16) -> Result<f64> {
        let bits = self.read_u64(master_index, slave_index)?;
        Ok(f64::from_bits(bits))
    }

    /// 通过 SDO 读取并转换为字符串
    ///
    /// [2026-05-08 修复乱码] CoE SDO 字符串走多编码兜底 (UTF-8/ASCII/Latin-1).
    pub fn read_string(&self, master_index: u16, slave_index: u16) -> Result<String> {
        let data = self.read_raw(master_index, slave_index)?;
        let end = data.iter().position(|&b| b == 0).unwrap_or(data.len());
        Ok(crate::utils::help::decode_ethercat_string(&data[..end]))
    }

    /// 根据数据类型自动读取并返回值 (对齐 C# GetValue)
    pub fn get_value(&self, master_index: u16, slave_index: u16) -> Result<CoEValue> {
        match self.data_type {
            EcDataType::Boolean => {
                let v = self.read_u8(master_index, slave_index)?;
                Ok(CoEValue::Bool(v != 0))
            }
            EcDataType::Integer8 => {
                let v = self.read_u8(master_index, slave_index)?;
                Ok(CoEValue::I8(v as i8))
            }
            EcDataType::Unsigned8 | EcDataType::Byte => {
                Ok(CoEValue::U8(self.read_u8(master_index, slave_index)?))
            }
            EcDataType::Integer16 => {
                let v = self.read_u16(master_index, slave_index)?;
                Ok(CoEValue::I16(v as i16))
            }
            EcDataType::Unsigned16 | EcDataType::Word => {
                Ok(CoEValue::U16(self.read_u16(master_index, slave_index)?))
            }
            EcDataType::Integer32 | EcDataType::Integer24 => {
                Ok(CoEValue::I32(self.read_i32(master_index, slave_index)?))
            }
            EcDataType::Unsigned32 | EcDataType::Unsigned24 | EcDataType::DWord => {
                Ok(CoEValue::U32(self.read_u32(master_index, slave_index)?))
            }
            EcDataType::Integer64 => {
                Ok(CoEValue::I64(self.read_i64(master_index, slave_index)?))
            }
            EcDataType::Unsigned64 => {
                Ok(CoEValue::U64(self.read_u64(master_index, slave_index)?))
            }
            EcDataType::Real32 => {
                Ok(CoEValue::F32(self.read_f32(master_index, slave_index)?))
            }
            EcDataType::Real64 => {
                Ok(CoEValue::F64(self.read_f64(master_index, slave_index)?))
            }
            EcDataType::VisibleString | EcDataType::UnicodeString => {
                Ok(CoEValue::Str(self.read_string(master_index, slave_index)?))
            }
            _ => {
                Ok(CoEValue::Raw(self.read_raw(master_index, slave_index)?))
            }
        }
    }

    /// 通过 SDO 写入原始字节
    pub fn write_raw(&self, master_index: u16, slave_index: u16, data: &[u8]) -> Result<()> {
        if !self.can_write() {
            return Err(DarraError::SdoWriteFailed {
                index: self.od_index, subindex: self.sub_index,
            });
        }
        let ok = unsafe {
            ffi::SDOwrite_raw(master_index, slave_index, self.od_index, self.sub_index,
                              0, data.as_ptr(), data.len() as c_int)
        };
        if ok != 0 {
            Ok(())
        } else {
            Err(DarraError::SdoWriteFailed { index: self.od_index, subindex: self.sub_index })
        }
    }

    /// 通过 SDO 写入 u8
    pub fn write_u8(&self, master_index: u16, slave_index: u16, value: u8) -> Result<()> {
        self.write_raw(master_index, slave_index, &[value])
    }

    /// 通过 SDO 写入 u16 (小端)
    pub fn write_u16(&self, master_index: u16, slave_index: u16, value: u16) -> Result<()> {
        self.write_raw(master_index, slave_index, &value.to_le_bytes())
    }

    /// 通过 SDO 写入 u32 (小端)
    pub fn write_u32(&self, master_index: u16, slave_index: u16, value: u32) -> Result<()> {
        self.write_raw(master_index, slave_index, &value.to_le_bytes())
    }

    /// 通过 SDO 写入 u64 (小端)
    pub fn write_u64(&self, master_index: u16, slave_index: u16, value: u64) -> Result<()> {
        self.write_raw(master_index, slave_index, &value.to_le_bytes())
    }

    /// 通过 SDO 写入 i32 (小端)
    pub fn write_i32(&self, master_index: u16, slave_index: u16, value: i32) -> Result<()> {
        self.write_raw(master_index, slave_index, &value.to_le_bytes())
    }

    /// 通过 SDO 写入 i64 (小端)
    pub fn write_i64(&self, master_index: u16, slave_index: u16, value: i64) -> Result<()> {
        self.write_raw(master_index, slave_index, &value.to_le_bytes())
    }

    /// 通过 SDO 写入 f32 (小端)
    pub fn write_f32(&self, master_index: u16, slave_index: u16, value: f32) -> Result<()> {
        self.write_raw(master_index, slave_index, &value.to_bits().to_le_bytes())
    }

    /// 通过 SDO 写入 f64 (小端)
    pub fn write_f64(&self, master_index: u16, slave_index: u16, value: f64) -> Result<()> {
        self.write_raw(master_index, slave_index, &value.to_bits().to_le_bytes())
    }
}

impl fmt::Display for ObjectEntry {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f,
            "  [{:02X}] {:40} {:12} {}bit {}",
            self.sub_index, self.name, self.data_type, self.bit_length, self.access
        )
    }
}

// ===================== CoE 值包装 (对齐 C# ValueWrapper) =====================

/// CoE 值类型封装
#[derive(Debug, Clone)]
pub enum CoEValue {
    Bool(bool),
    I8(i8),
    U8(u8),
    I16(i16),
    U16(u16),
    I32(i32),
    U32(u32),
    I64(i64),
    U64(u64),
    F32(f32),
    F64(f64),
    Str(String),
    Raw(Vec<u8>),
}

impl fmt::Display for CoEValue {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            CoEValue::Bool(v) => write!(f, "{}", v),
            CoEValue::I8(v) => write!(f, "{}", v),
            CoEValue::U8(v) => write!(f, "{}", v),
            CoEValue::I16(v) => write!(f, "{}", v),
            CoEValue::U16(v) => write!(f, "{}", v),
            CoEValue::I32(v) => write!(f, "{}", v),
            CoEValue::U32(v) => write!(f, "{}", v),
            CoEValue::I64(v) => write!(f, "{}", v),
            CoEValue::U64(v) => write!(f, "{}", v),
            CoEValue::F32(v) => write!(f, "{:.6}", v),
            CoEValue::F64(v) => write!(f, "{:.6}", v),
            CoEValue::Str(v) => write!(f, "{}", v),
            CoEValue::Raw(v) => {
                let hex: Vec<String> = v.iter().map(|b| format!("{:02X}", b)).collect();
                write!(f, "{}", hex.join(" "))
            }
        }
    }
}

// ===================== 对象字典对象 =====================

/// OD 对象 (Object)，对应 CoE 索引的一个顶层条目
/// 对齐 C# ObjectDictionary
#[derive(Debug, Clone)]
pub struct OdObject {
    /// 对象索引
    pub index: u16,
    /// 对象名称
    pub name: String,
    /// 对象代码 (0x07=VAR, 0x08=ARRAY, 0x09=RECORD)
    pub object_code: u8,
    /// 数据类型 (对齐 C# Datatype)
    pub data_type: EcDataType,
    /// 最大子索引数
    pub max_sub: u8,
    /// 子条目列表
    pub entries: Vec<ObjectEntry>,
}

impl OdObject {
    /// 子索引条目数量 (对齐 C# Count)
    pub fn count(&self) -> usize {
        self.entries.len()
    }

    /// 数据类型原始值 (对齐 C# Datatype)
    pub fn datatype(&self) -> EcDataType {
        self.data_type
    }

    /// 返回是否为变量对象 (VAR)
    pub fn is_var(&self) -> bool {
        self.object_code == 0x07
    }

    /// 返回是否为数组对象 (ARRAY)
    pub fn is_array(&self) -> bool {
        self.object_code == 0x08
    }

    /// 返回是否为记录对象 (RECORD)
    pub fn is_record(&self) -> bool {
        self.object_code == 0x09
    }

    /// 按子索引查找条目
    pub fn entry(&self, sub_index: u8) -> Option<&ObjectEntry> {
        self.entries.iter().find(|e| e.sub_index == sub_index)
    }

    /// 检查子索引是否存在 (对齐 C# ContainsKey)
    pub fn contains_key(&self, sub_index: u8) -> bool {
        self.entries.iter().any(|e| e.sub_index == sub_index)
    }

    /// 按名称查找条目
    pub fn entry_by_name(&self, name: &str) -> Option<&ObjectEntry> {
        let name_lower = name.to_lowercase();
        self.entries.iter().find(|e| e.name.to_lowercase() == name_lower)
    }

    /// 按位置获取条目 (对齐 C# GetByPosition)
    pub fn get_by_position(&self, position: usize) -> Option<&ObjectEntry> {
        self.entries.get(position)
    }

    /// 批量读取所有子索引数据 (对齐 C# ReadAll)
    pub fn read_all(&self, master_index: u16, slave_index: u16) -> HashMap<u8, Vec<u8>> {
        let mut result = HashMap::new();
        for entry in &self.entries {
            if let Ok(data) = entry.read_raw(master_index, slave_index) {
                if !data.is_empty() {
                    result.insert(entry.sub_index, data);
                }
            }
        }
        result
    }

    /// 创建缓存副本 (对齐 C# Copy)
    pub fn copy(&self) -> Self {
        self.clone()
    }
}

impl fmt::Display for OdObject {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let kind = match self.object_code {
            0x07 => "VAR   ",
            0x08 => "ARRAY ",
            0x09 => "RECORD",
            _    => "OTHER ",
        };
        writeln!(f, "[0x{:04X}] {} {} ({}条目)", self.index, kind, self.name, self.entries.len())?;
        for entry in &self.entries {
            writeln!(f, "{}", entry)?;
        }
        Ok(())
    }
}

// ===================== 对象字典列表 =====================

/// 从站对象字典 (Object Dictionary)
/// 对齐 C# EcODList
pub struct OdList {
    /// 主站索引
    pub master_index: u16,
    /// 从站索引
    pub slave_index: u16,
    /// 所有对象
    pub objects: Vec<OdObject>,
}

impl OdList {
    /// 从 DLL 加载完整 OD (对齐 C# LoadODList / LoadStructure)
    pub fn load(master_index: u16, slave_index: u16) -> Result<Self> {
        let odlist_ptr = unsafe { ffi::GetSlaveSDOList(master_index, slave_index) };
        if odlist_ptr.is_null() {
            return Err(DarraError::SdoReadFailed { index: 0, subindex: 0 });
        }

        let objects = Self::parse_odlist_objects(master_index, slave_index, odlist_ptr, true)?;
        // 释放 DLL 分配的 OD 列表内存
        unsafe { ffi::FreeMemory(odlist_ptr as *mut std::os::raw::c_void) };
        Ok(Self { master_index, slave_index, objects })
    }

    /// 从 DLL 加载基础 OD (仅索引和数据类型, 不含子条目详情)
    ///
    /// 对应 DLL GetSlaveSDOListBasic, 比 load() 更快, 适用于快速浏览。
    pub fn load_basic(master_index: u16, slave_index: u16) -> Result<Self> {
        let odlist_ptr = unsafe { ffi::GetSlaveSDOListBasic(master_index, slave_index) };
        if odlist_ptr.is_null() {
            return Err(DarraError::SdoReadFailed { index: 0, subindex: 0 });
        }

        let objects = Self::parse_odlist_objects(master_index, slave_index, odlist_ptr, false)?;
        unsafe { ffi::FreeMemory(odlist_ptr as *mut _) };
        Ok(Self { master_index, slave_index, objects })
    }

    /// 从原始 DLL 指针解析 OD 列表 (内部使用, 用于批量操作)
    pub(crate) fn from_raw_ptr(
        master_index: u16, slave_index: u16, odlist_ptr: *const std::ffi::c_void,
    ) -> Result<Self> {
        if odlist_ptr.is_null() {
            return Err(DarraError::SdoReadFailed { index: 0, subindex: 0 });
        }
        let objects = Self::parse_odlist_objects(master_index, slave_index, odlist_ptr, true)?;
        Ok(Self { master_index, slave_index, objects })
    }

    /// 解析 OD 列表指针为对象列表 (内部辅助方法)
    fn parse_odlist_objects(
        master_index: u16, slave_index: u16,
        odlist_ptr: *const std::ffi::c_void, load_entries: bool,
    ) -> Result<Vec<OdObject>> {
        let base = odlist_ptr as *const u8;

        let entries_count = unsafe {
            let p = base.add(2) as *const u16;
            std::ptr::read_unaligned(p) as usize
        };
        let entries_count = entries_count.min(1024);

        let mut objects = Vec::with_capacity(entries_count);

        for i in 0..entries_count {
            let idx_val = unsafe {
                let p = base.add(4 + i * 2) as *const u16;
                std::ptr::read_unaligned(p)
            };
            let dt_raw = unsafe {
                let p = base.add(2052 + i * 2) as *const u16;
                std::ptr::read_unaligned(p)
            };
            let obj_code = unsafe { *base.add(4100 + i) };
            let max_sub = unsafe { *base.add(5124 + i) };
            let name = {
                let name_ptr = unsafe { base.add(6148 + i * 41) };
                let name_slice = unsafe { std::slice::from_raw_parts(name_ptr, 41) };
                let end = name_slice.iter().position(|&b| b == 0).unwrap_or(41);
                // [2026-05-08 修复乱码] OD 对象名走多编码兜底 (UTF-8/ASCII/Latin-1)
                crate::utils::help::decode_ethercat_string(&name_slice[..end])
            };

            let mut entries = Vec::new();
            if load_entries {
                let oe_ptr = unsafe {
                    ffi::GetSlavePointer_SDO_WithODList(master_index, slave_index, i as u16, odlist_ptr)
                };
                if !oe_ptr.is_null() {
                    entries = parse_oe_entries(oe_ptr, idx_val, max_sub);
                    unsafe { ffi::FreeMemory(oe_ptr as *mut std::os::raw::c_void) };
                }
            }

            objects.push(OdObject {
                index: idx_val,
                name,
                object_code: obj_code,
                data_type: EcDataType::from_raw(dt_raw),
                max_sub,
                entries,
            });
        }

        Ok(objects)
    }

    /// 对象数量 (对齐 C# Count)
    pub fn count(&self) -> usize {
        self.objects.len()
    }

    /// 按索引查找对象
    pub fn find(&self, index: u16) -> Option<&OdObject> {
        self.objects.iter().find(|o| o.index == index)
    }

    /// 检查索引是否存在 (对齐 C# ContainsKey)
    pub fn contains_key(&self, index: u16) -> bool {
        self.objects.iter().any(|o| o.index == index)
    }

    /// 按名称模糊查找对象
    pub fn find_by_name(&self, name: &str) -> Vec<&OdObject> {
        let name_lower = name.to_lowercase();
        self.objects.iter()
            .filter(|o| o.name.to_lowercase().contains(&name_lower))
            .collect()
    }

    /// 按索引查找条目，直接通过 SDO 读取
    pub fn read_entry_raw(
        &self, index: u16, sub_index: u8,
    ) -> Result<Vec<u8>> {
        let mut size: c_int = 0;
        let ptr = unsafe {
            ffi::SDOread(self.master_index, self.slave_index, index, sub_index, 0, &mut size)
        };
        if ptr.is_null() || size <= 0 {
            if !ptr.is_null() {
                unsafe { ffi::FreeMemory(ptr as *mut _) };
            }
            return Err(DarraError::SdoReadFailed { index, subindex: sub_index });
        }
        let data = unsafe { std::slice::from_raw_parts(ptr, size as usize).to_vec() };
        unsafe { ffi::FreeMemory(ptr as *mut _) };
        Ok(data)
    }

    /// 按位置获取对象 (对齐 C# GetByPosition)
    pub fn get_by_position(&self, position: usize) -> Option<&OdObject> {
        self.objects.get(position)
    }

    /// 获取对象总数
    pub fn len(&self) -> usize {
        self.objects.len()
    }

    /// 检查是否为空
    pub fn is_empty(&self) -> bool {
        self.objects.is_empty()
    }

    /// 创建缓存副本 (对齐 C# Copy)
    pub fn copy(&self) -> Self {
        Self {
            master_index: self.master_index,
            slave_index: self.slave_index,
            objects: self.objects.clone(),
        }
    }

    /// 返回所有 OD 索引列表
    pub fn keys(&self) -> Vec<u16> {
        self.objects.iter().map(|o| o.index).collect()
    }
}

impl fmt::Display for OdList {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "从站 {} 对象字典 ({} 个对象):", self.slave_index, self.objects.len())?;
        writeln!(f, "{}", "=".repeat(72))?;
        for obj in &self.objects {
            write!(f, "{}", obj)?;
        }
        Ok(())
    }
}

// ===================== 诊断消息读取 (对齐 C# ReadDiagnosticMessages) =====================

/// 读取诊断历史消息 (0x10F3, ETG.1510)
pub fn read_diagnostic_messages(master_index: u16, slave_index: u16) -> Vec<DiagnosticMessage> {
    let mut messages = Vec::new();

    // 读取 0x10F3:00 获取消息数量
    let mut size: c_int = 0;
    let ptr = unsafe {
        ffi::SDOread(master_index, slave_index, 0x10F3, 0, 0, &mut size)
    };
    if ptr.is_null() || size < 1 {
        if !ptr.is_null() {
            unsafe { ffi::FreeMemory(ptr as *mut _) };
        }
        return messages;
    }
    let count = unsafe { *ptr } as usize;
    unsafe { ffi::FreeMemory(ptr as *mut _) };

    // 读取每条诊断消息
    for i in 1..=count.min(20) {
        let mut msg_size: c_int = 0;
        let msg_ptr = unsafe {
            ffi::SDOread(master_index, slave_index, 0x10F3, i as u8, 0, &mut msg_size)
        };
        if msg_ptr.is_null() || msg_size < 8 {
            if !msg_ptr.is_null() {
                unsafe { ffi::FreeMemory(msg_ptr as *mut _) };
            }
            continue;
        }

        let data = unsafe { std::slice::from_raw_parts(msg_ptr, msg_size as usize).to_vec() };
        unsafe { ffi::FreeMemory(msg_ptr as *mut _) };

        let diag_code = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
        let flags = u16::from_le_bytes([data[4], data[5]]);
        let text_index = u16::from_le_bytes([data[6], data[7]]);

        messages.push(DiagnosticMessage {
            sub_index: i as u8,
            diag_code,
            flags,
            text_index,
            raw_data: data,
        });
    }

    messages
}

/// 读取设备协议编号 (0x1000 Device Type 低 16 位)
pub fn get_device_profile(master_index: u16, slave_index: u16) -> u16 {
    let mut size: c_int = 0;
    let ptr = unsafe {
        ffi::SDOread(master_index, slave_index, 0x1000, 0, 0, &mut size)
    };
    if ptr.is_null() || size < 4 {
        if !ptr.is_null() {
            unsafe { ffi::FreeMemory(ptr as *mut _) };
        }
        return 0;
    }
    let data = unsafe { std::slice::from_raw_parts(ptr, 4) };
    let device_type = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
    unsafe { ffi::FreeMemory(ptr as *mut _) };
    (device_type & 0xFFFF) as u16
}

// ===================== 内部辅助函数 =====================

/// 从 OE 指针解析子条目列表
fn parse_oe_entries(oe_ptr: *const std::ffi::c_void, od_index: u16, max_sub: u8) -> Vec<ObjectEntry> {
    let base = oe_ptr as *const u8;

    let oe_entries = unsafe {
        let p = base as *const u16;
        std::ptr::read_unaligned(p) as usize
    };
    let count = oe_entries.min(max_sub as usize + 1).min(256);

    let mut entries = Vec::with_capacity(count);
    for i in 0..count {
        let value_info = unsafe { *base.add(2 + i) };
        let dt_raw = unsafe {
            let p = base.add(258 + i * 2) as *const u16;
            std::ptr::read_unaligned(p)
        };
        let bit_length = unsafe {
            let p = base.add(770 + i * 2) as *const u16;
            std::ptr::read_unaligned(p)
        };
        let access_raw = unsafe {
            let p = base.add(1282 + i * 2) as *const u16;
            std::ptr::read_unaligned(p)
        };
        let name = {
            let name_ptr = unsafe { base.add(1794 + i * 41) };
            let name_slice = unsafe { std::slice::from_raw_parts(name_ptr, 41) };
            let end = name_slice.iter().position(|&b| b == 0).unwrap_or(41);
            // [2026-05-08 修复乱码] OD entry 名走多编码兜底 (UTF-8/ASCII/Latin-1)
            crate::utils::help::decode_ethercat_string(&name_slice[..end])
        };

        entries.push(ObjectEntry {
            od_index,
            sub_index: i as u8,
            name,
            data_type: EcDataType::from_raw(dt_raw),
            bit_length,
            access: ObjAccess(access_raw),
            value_info,
        });
    }
    entries
}

// ===================== CoE 实例 (对齐 C# CoEInstance) =====================

use crate::data::types::SDOError;
use std::cell::Cell;

/// CoE 协议实例，封装 SDO 读写并跟踪最后错误码
/// 对齐 C# CoEInstance
pub struct CoEInstance {
    master_index: u16,
    slave_index: u16,
    /// 最后一次 SDO 操作的错误码
    last_sdo_error: Cell<SDOError>,
}

impl CoEInstance {
    /// 创建 CoE 实例
    pub fn new(master_index: u16, slave_index: u16) -> Self {
        Self {
            master_index,
            slave_index,
            last_sdo_error: Cell::new(SDOError::NoError),
        }
    }

    /// 获取最后一次 SDO 操作的错误码 (对齐 C# `LastSdoError` / Java `getLastSdoError`)
    ///
    /// # 路由优先级 (managed 实装)
    /// 1. FFI `GetLastSdoError` (DLL 端 thread-local 缓存) — 解析到则用 DLL
    /// 2. Managed: 本实例 `Cell<SDOError>` 缓存 (sdo_read/sdo_write 失败时
    ///    自动设置, 成功后重置为 `NoError`)
    ///
    /// 与 Java/Python 一致, managed 缓存避免了 thread-local 跨 FFI 边界的
    /// 复杂性. 当 sdo_read/sdo_write 失败时可通过此方法获取 SDO 中止码 (Abort Code).
    pub fn last_sdo_error(&self) -> SDOError {
        // 1) 优先 FFI 路径 (DLL 实装)
        if let Some(f) = ffi::dynamic_ffi::ffi_gap().get_last_sdo_error {
            let code = unsafe { f(self.master_index, self.slave_index) };
            if code != 0 {
                return SDOError::from_u32(code);
            }
        }
        // 2) Managed: 本实例缓存
        self.last_sdo_error.get()
    }

    /// 从站是否支持 CoE 邮箱协议 (对齐 C# CoEInstance.IsSupported).
    ///
    /// 读取 SII mbx_proto bit 2 (ECT_MBXPROT_COE = 0x04) 判断支持情况.
    /// DLL 调用失败时保守返回 true (未知能力视为支持, 不阻塞用户调用).
    pub fn is_supported(&self) -> bool {
        let proto = unsafe { ffi::GetSlaveMailboxProto(self.master_index, self.slave_index) };
        (proto & 0x04) != 0
    }

    /// SDO 读取 (原始字节)
    /// 读取失败时自动更新 last_sdo_error()
    pub fn sdo_read(&self, index: u16, subindex: u8, complete_access: bool) -> Result<Vec<u8>> {
        let mut size: c_int = 0;
        let ptr = unsafe {
            ffi::SDOread(self.master_index, self.slave_index, index, subindex,
                         if complete_access { 1 } else { 0 }, &mut size)
        };
        if ptr.is_null() || size <= 0 {
            // 负值可能编码了 SDO abort code
            if size < 0 {
                let abort_code = (-size) as u32;
                self.last_sdo_error.set(SDOError::from_u32(abort_code));
            } else {
                self.last_sdo_error.set(SDOError::GeneralError);
            }
            if !ptr.is_null() { unsafe { ffi::FreeMemory(ptr as *mut _) }; }
            return Err(DarraError::SdoReadFailed { index, subindex });
        }
        let data = unsafe { std::slice::from_raw_parts(ptr, size as usize).to_vec() };
        unsafe { ffi::FreeMemory(ptr as *mut _) };
        self.last_sdo_error.set(SDOError::NoError);
        Ok(data)
    }

    /// SDO 写入 (原始字节)
    /// 写入失败时自动更新 last_sdo_error()
    pub fn sdo_write(&self, index: u16, subindex: u8, complete_access: bool, data: &[u8]) -> Result<()> {
        let ok = unsafe {
            ffi::SDOwrite_raw(self.master_index, self.slave_index, index, subindex,
                              if complete_access { 1 } else { 0 }, data.as_ptr(), data.len() as c_int)
        };
        if ok == 0 {
            self.last_sdo_error.set(SDOError::GeneralError);
            // 尝试通过读取触发错误栈获取精确的 abort code
            let mut probe_size: c_int = 0;
            let probe = unsafe {
                ffi::SDOread(self.master_index, self.slave_index, index, subindex, 0, &mut probe_size)
            };
            if probe.is_null() && probe_size < 0 {
                let abort_code = (-probe_size) as u32;
                self.last_sdo_error.set(SDOError::from_u32(abort_code));
            } else if !probe.is_null() {
                unsafe { ffi::FreeMemory(probe as *mut _) };
            }
            Err(DarraError::SdoWriteFailed { index, subindex })
        } else {
            self.last_sdo_error.set(SDOError::NoError);
            Ok(())
        }
    }

    /// 批量 SDO 读取
    ///
    /// 一次性读取多个 (index, subindex) 对应的 SDO 数据。
    /// 成功的条目返回 `Some(Vec<u8>)`，失败的条目返回 `None`。
    ///
    /// # 示例
    /// ```no_run
    /// let coe = CoEInstance::new(0, 1);
    /// let entries = [(0x1000, 0x00), (0x1008, 0x00), (0x1018, 0x01)];
    /// let results = coe.read_multiple(&entries);
    /// for ((idx, sub), data) in &results {
    ///     if let Some(bytes) = data {
    ///         println!("0x{:04X}:{:02X} = {} 字节", idx, sub, bytes.len());
    ///     }
    /// }
    /// ```
    pub fn read_multiple(&self, entries: &[(u16, u8)]) -> HashMap<(u16, u8), Option<Vec<u8>>> {
        let mut results = HashMap::new();
        for &(index, subindex) in entries {
            let data = self.sdo_read(index, subindex, false).ok();
            results.insert((index, subindex), data);
        }
        results
    }

    /// 主站索引
    pub fn master_index(&self) -> u16 { self.master_index }
    /// 从站索引
    pub fn slave_index(&self) -> u16 { self.slave_index }
}

// ===================== CoE 错误类型 (诊断历史专用) =====================

/// CoE 附加操作错误类型 (对齐 C# CoE.cs 异常通道).
///
/// 仅覆盖本模块新增的 Diagnosis History 专用错误,
/// 通用 SDO 读写继续使用 `DarraError::SdoReadFailed` / `SdoWriteFailed`.
#[derive(Debug, Clone)]
pub enum CoeError {
    /// DLL 调用失败 (带 SDO Abort Code, 0 表示无中止码)
    DllFailed { abort_code: u32 },
    /// 参数越界
    InvalidParameter(String),
    /// 底层返回数据长度异常
    InvalidResponse(String),
}

impl std::fmt::Display for CoeError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::DllFailed { abort_code } => {
                if *abort_code != 0 {
                    write!(f, "CoE DLL 失败 (Abort Code 0x{:08X})", abort_code)
                } else {
                    write!(f, "CoE DLL 失败")
                }
            }
            Self::InvalidParameter(msg) => write!(f, "CoE 无效参数: {}", msg),
            Self::InvalidResponse(msg) => write!(f, "CoE 响应异常: {}", msg),
        }
    }
}

impl std::error::Error for CoeError {}

impl From<CoeError> for DarraError {
    fn from(e: CoeError) -> Self {
        DarraError::Other(e.to_string())
    }
}

// ===================== CoE Diagnosis History 0x10F3 (ETG.1020 §16) =====================

/// 0x10F3 诊断历史元数据 (ETG.1020 Table 48/49).
///
/// 对齐 C# `CoEInstance.DiagMeta`.
#[derive(Debug, Clone, Copy, Default)]
pub struct DiagMeta {
    /// 0x10F3:01 MaxMessages — 从站支持的最大消息数 (通常 <= 250)
    pub max_messages: u8,
    /// 0x10F3:02 NewestMessage — 最新消息所在 subindex
    pub newest_message: u8,
    /// 0x10F3:03 NewestAcknowledged — 用户已确认的最新 subindex
    pub newest_acknowledged: u8,
    /// 0x10F3:05 Flags — bit4=Ring/Linear 模式, bit5=Overrun 标志
    pub flags: u16,
}

impl DiagMeta {
    /// 是否为 Ring Buffer 模式 (bit4=1).
    /// false=Linear (一次性填满后停止).
    pub fn is_ring_buffer(&self) -> bool {
        (self.flags & 0x0010) != 0
    }

    /// 是否发生过 Overrun (bit5=1, 消息丢失).
    pub fn has_overrun(&self) -> bool {
        (self.flags & 0x0020) != 0
    }
}

impl CoEInstance {
    /// 快速轮询 0x10F3:04 "NewAvailable" 标志 (ETG.1020 §16.2).
    ///
    /// 无新消息时不读完整历史, 适合高频轮询.
    /// 对齐 C# `CoEInstance.PollHasNewDiagnostic`.
    ///
    /// 返回: 1=有新消息, 0=无, -1=通信失败.
    /// (与 C# 的 `bool` 不同, 此处返回 i32 便于区分"无"和"失败")
    pub fn poll_has_new_diagnostic(&self) -> i32 {
        let mut abort_code: u32 = 0;
        unsafe {
            ffi::coe_diag_poll_new_available(self.master_index, self.slave_index, &mut abort_code)
        }
    }

    /// 读 0x10F3 元数据 (MaxMessages / NewestMessage / NewestAcked / Flags).
    ///
    /// 对齐 C# `CoEInstance.ReadDiagnosticMeta`.
    pub fn read_diagnostic_meta(&self) -> std::result::Result<DiagMeta, CoeError> {
        let mut max_msgs: u8 = 0;
        let mut newest: u8 = 0;
        let mut acknowledged: u8 = 0;
        let mut flags: u16 = 0;
        let mut abort_code: u32 = 0;
        let rc = unsafe {
            ffi::coe_diag_read_meta(
                self.master_index,
                self.slave_index,
                &mut max_msgs,
                &mut newest,
                &mut acknowledged,
                &mut flags,
                &mut abort_code,
            )
        };
        if rc == 1 {
            Ok(DiagMeta {
                max_messages: max_msgs,
                newest_message: newest,
                newest_acknowledged: acknowledged,
                flags,
            })
        } else {
            Err(CoeError::DllFailed { abort_code })
        }
    }

    /// 读取指定 subindex (6..255) 的诊断消息原始字节 (Octet String).
    ///
    /// 对齐 C# `CoEInstance.ReadDiagnosticMessage`.
    ///
    /// # 参数
    /// - `msg_idx`: 消息子索引 (6..255)
    /// - `buf_size`: 接收缓冲区容量 (通常 512 字节足够, 按 ETG Table 49 最大 512)
    ///
    /// # 返回
    /// 成功: `Vec<u8>` 长度 = 实际消息字节数.
    pub fn read_diagnostic_message(
        &self,
        msg_idx: u16,
        buf_size: usize,
    ) -> std::result::Result<Vec<u8>, CoeError> {
        if !(6..=255).contains(&msg_idx) {
            return Err(CoeError::InvalidParameter(format!(
                "msg_idx 必须在 6..255 之间, 实际={}",
                msg_idx
            )));
        }
        if buf_size == 0 || buf_size > 64 * 1024 {
            return Err(CoeError::InvalidParameter(format!(
                "buf_size 必须在 1..65536 之间, 实际={}",
                buf_size
            )));
        }

        let mut buf = vec![0u8; buf_size];
        let mut out_len: c_int = 0;
        let mut abort_code: u32 = 0;
        let rc = unsafe {
            ffi::coe_diag_read_message(
                self.master_index,
                self.slave_index,
                msg_idx as u8,
                buf.as_mut_ptr(),
                buf_size as c_int,
                &mut out_len,
                &mut abort_code,
            )
        };
        if rc == 1 {
            if out_len < 0 || out_len as usize > buf_size {
                return Err(CoeError::InvalidResponse(format!(
                    "DLL 返回 out_len={}, buf_size={}",
                    out_len, buf_size
                )));
            }
            buf.truncate(out_len as usize);
            Ok(buf)
        } else {
            Err(CoeError::DllFailed { abort_code })
        }
    }

    /// 确认指定 subindex (6..255) 已处理, 写入 0x10F3:03 NewestAcknowledged.
    ///
    /// 对齐 C# `CoEInstance.AcknowledgeDiagnostic`.
    /// 从站按 Ring 模式清理 <= ack_subidx 的消息.
    pub fn acknowledge_diagnostic(&self, msg_idx: u16) -> std::result::Result<(), CoeError> {
        if !(6..=255).contains(&msg_idx) {
            return Err(CoeError::InvalidParameter(format!(
                "msg_idx 必须在 6..255 之间, 实际={}",
                msg_idx
            )));
        }
        let mut abort_code: u32 = 0;
        let rc = unsafe {
            ffi::coe_diag_acknowledge(
                self.master_index,
                self.slave_index,
                msg_idx as u8,
                &mut abort_code,
            )
        };
        if rc == 1 {
            Ok(())
        } else {
            Err(CoeError::DllFailed { abort_code })
        }
    }
}

// 注: SDO Block Transfer 已按 ETG.1000.6 §5.6.2 audit 结论删除
// (标准只定义 Expedited + Normal, 无 Block Transfer). DLL 侧 6 个 DLL_EXPORT 撤除,
// Rust 侧对应方法 / 常量 / 模块级函数 / re-export 一并移除. Segmented Transfer
// 覆盖所有 >4 B 场景, 性能已足够 (>1500 B 无 Block 分块加速).

// ===================== Complete Access 数据解析 =====================

/// Complete Access 解析用对象字典条目
///
/// 区别于 OD 树中的 ObjectEntry, 此结构体仅用于 parse_complete_access_data 函数。
pub struct CaObjectEntry {
    /// 子索引号
    pub sub_index: u8,
    /// 数据类型原始值 (EcDataType)
    pub data_type: u16,
    /// 位长度
    pub bit_length: u16,
}

/// 解析 Complete Access 读取的原始字节数据，按子索引拆分
///
/// 规则 (ETG.1000.6 §5.6.2):
/// - SubIndex 0 始终填充到 16 位 (2字节)
/// - BIT 类型连续打包，下一个非 BIT 类型从下一个字节边界开始
///
/// 参数:
/// - `data`: Complete Access 读取的原始字节数据
/// - `entries`: 对象字典条目列表 (按子索引顺序)
///
/// 返回:
/// - `Vec<(u8, Vec<u8>)>`: (子索引号, 数据) 的列表
pub fn parse_complete_access_data(data: &[u8], entries: &[CaObjectEntry]) -> Vec<(u8, Vec<u8>)> {
    let mut result = Vec::new();
    if data.is_empty() || entries.is_empty() {
        return result;
    }

    // SubIndex 0 始终填充到 16 位 (2字节), 按 ETG 规范
    let si0_bytes = 2usize;
    if data.len() >= si0_bytes {
        result.push((0, data[..si0_bytes].to_vec()));
    }
    let mut bit_offset = si0_bytes * 8;

    // 解析后续子索引
    for i in 1..entries.len() {
        let oe = &entries[i];
        let dt = oe.data_type;
        let is_bit_type = dt >= 0x0030 && dt <= 0x0037;

        let bit_size = if is_bit_type {
            (dt - 0x0030 + 1) as usize // BIT1=1, BIT2=2, ...
        } else if oe.bit_length > 0 {
            oe.bit_length as usize
        } else {
            8
        };

        // 非 BIT 类型从下一个字节边界开始
        if !is_bit_type && (bit_offset % 8) != 0 {
            bit_offset = ((bit_offset + 7) / 8) * 8;
        }

        let byte_start = bit_offset / 8;
        let bytes_needed = (bit_size + 7) / 8;

        if byte_start + bytes_needed <= data.len() {
            // 使用实际子索引值而非位置索引
            result.push((oe.sub_index, data[byte_start..byte_start + bytes_needed].to_vec()));
        }

        bit_offset += bit_size;
    }

    result
}

// ===================== 异步 API (轨道 1: std::thread, 无依赖) =====================

impl CoEInstance {
    /// SDO 读取 (std::thread 异步包装, 无依赖)
    ///
    /// 返回 `JoinHandle`, 用户通过 `.join()` 等待结果。
    /// 关联函数设计: 不持有 `&self` 生命周期, 可跨线程 `move` 使用。
    ///
    /// # 示例
    /// ```ignore
    /// use ethercat::slave::coe::CoEInstance;
    /// let handle = CoEInstance::sdo_read_blocking(0, 1, 0x6041, 0, false);
    /// let data = handle.join().unwrap()?;
    /// ```
    pub fn sdo_read_blocking(
        master_index: u16,
        slave_index: u16,
        index: u16,
        subindex: u8,
        complete_access: bool,
    ) -> std::thread::JoinHandle<Result<Vec<u8>>> {
        std::thread::spawn(move || {
            let coe = CoEInstance::new(master_index, slave_index);
            coe.sdo_read(index, subindex, complete_access)
        })
    }

    /// SDO 写入 (std::thread 异步包装, 无依赖)
    ///
    /// `data` 需用 `Vec<u8>` 而非 `&[u8]` (满足 `'static` 生命周期要求)。
    pub fn sdo_write_blocking(
        master_index: u16,
        slave_index: u16,
        index: u16,
        subindex: u8,
        complete_access: bool,
        data: Vec<u8>,
    ) -> std::thread::JoinHandle<Result<()>> {
        std::thread::spawn(move || {
            let coe = CoEInstance::new(master_index, slave_index);
            coe.sdo_write(index, subindex, complete_access, &data)
        })
    }
}

// ===================== 异步 API (轨道 2: tokio, feature 门控) =====================

#[cfg(feature = "async-tokio")]
impl CoEInstance {
    /// SDO 读取 (tokio async)
    ///
    /// 使用 `tokio::task::spawn_blocking`, 同步 FFI 在 blocking pool 执行,
    /// 不阻塞 async runtime。
    ///
    /// # 注意
    /// - 需启用 feature `async-tokio`
    /// - 底层 DLL 调用无法中断, `tokio::time::timeout` 只让 await 返回,
    ///   后台线程仍运行到 DLL 返回
    pub async fn sdo_read_async(
        &self,
        index: u16,
        subindex: u8,
        complete_access: bool,
    ) -> Result<Vec<u8>> {
        let master = self.master_index;
        let slave = self.slave_index;
        tokio::task::spawn_blocking(move || {
            let coe = CoEInstance::new(master, slave);
            coe.sdo_read(index, subindex, complete_access)
        })
        .await
        .map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
    }

    /// SDO 写入 (tokio async)
    pub async fn sdo_write_async(
        &self,
        index: u16,
        subindex: u8,
        complete_access: bool,
        data: Vec<u8>,
    ) -> Result<()> {
        let master = self.master_index;
        let slave = self.slave_index;
        tokio::task::spawn_blocking(move || {
            let coe = CoEInstance::new(master, slave);
            coe.sdo_write(index, subindex, complete_access, &data)
        })
        .await
        .map_err(|e| DarraError::Other(format!("tokio join error: {}", e)))?
    }
}

// ===================== IMailboxProtocol trait impl (对齐 C# CoEInstance) =====================

impl crate::abstractions::MailboxProtocol for CoEInstance {
    fn protocol_type(&self) -> u8 { 0x03 }
    fn protocol_name(&self) -> &'static str { "CoE" }

    fn is_supported(&self) -> bool {
        CoEInstance::is_supported(self)
    }

    fn last_error_code(&self) -> u32 {
        self.last_sdo_error.get() as u32
    }

    fn statistics(&self) -> crate::abstractions::MailboxStatistics {
        let mut stats = ffi::EcMbxStatsC::default();
        let rc = unsafe {
            ffi::mbx_get_stats_by_master(
                self.master_index, self.slave_index, 0x03, &mut stats,
            )
        };
        if rc == 1 {
            stats.into()
        } else {
            crate::abstractions::MailboxStatistics::empty()
        }
    }

    fn reset_statistics(&self) {
        unsafe {
            ffi::mbx_reset_stats_by_master(self.master_index, self.slave_index, 0x03);
        }
    }
}
