//! 原始 C FFI 绑定 (unsafe extern "C")
//!
//! 直接映射 Darra.Core.dll 导出函数。
//! 上层模块通过安全封装调用，用户一般不需要直接使用。

use std::os::raw::{c_char, c_int, c_void};

// 原语义API: DarraProtocolSetStateWithPolicy
pub const CORE_OP_21: u32 = 0x44525421;
// 原语义API: DarraProtocolRequireState
pub const CORE_OP_22: u32 = 0x44525422;
// 原语义API: DarraProtocolBuildFromConfigJson
pub const CORE_OP_23: u32 = 0x44525423;
// 原语义API: DarraProtocolExecuteAutoStartup
pub const CORE_OP_24: u32 = 0x44525424;
// 原语义API: DarraProtocolConfigureSlaveStartup
pub const CORE_OP_25: u32 = 0x44525425;
// 原语义API: DarraProtocolApplyStartupTransitions
pub const CORE_OP_26: u32 = 0x44525426;
// 原语义API: DarraProtocolRecoverToOperational
pub const CORE_OP_27: u32 = 0x44525427;
// 原语义API: DarraProtocolRefreshDiagnostics
pub const CORE_OP_28: u32 = 0x44525428;
pub const CORE_FLAG_01: u32 = 0x00000001;

/// EtherCAT 从站状态
pub const EC_STATE_INIT: u8 = 0x01;
pub const EC_STATE_PRE_OP: u8 = 0x02;
pub const EC_STATE_BOOT: u8 = 0x03;
pub const EC_STATE_SAFE_OP: u8 = 0x04;
pub const EC_STATE_OPERATIONAL: u8 = 0x08;
pub const EC_STATE_ACK: u8 = 0x10;

/// 启动参数转换类型
pub const TRANS_IP: u8 = 0; // Init -> PreOp
pub const TRANS_PS: u8 = 1; // PreOp -> SafeOp
pub const TRANS_SO: u8 = 2; // SafeOp -> OP
pub const TRANS_OS: u8 = 3; // OP -> SafeOp
pub const TRANS_SP: u8 = 4; // SafeOp -> PreOp
pub const TRANS_PI: u8 = 5; // PreOp -> Init

/// 启动参数写入时序
pub const TIMING_BEFORE: u8 = 0;
pub const TIMING_AFTER: u8 = 1;

// ===================== 结构体定义 =====================

/// DLL 版本信息结构体
#[repr(C, packed)]
#[derive(Clone, Copy)]
pub struct DllVersionInfo {
    pub major: u16,
    pub minor: u16,
    pub patch: u16,
    pub build: u16,
    pub build_date: [u8; 32],
}

impl std::fmt::Debug for DllVersionInfo {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // packed 结构体不能直接引用字段，需要 read_unaligned 避免 UB
        let major = unsafe { std::ptr::addr_of!(self.major).read_unaligned() };
        let minor = unsafe { std::ptr::addr_of!(self.minor).read_unaligned() };
        let patch = unsafe { std::ptr::addr_of!(self.patch).read_unaligned() };
        let build = unsafe { std::ptr::addr_of!(self.build).read_unaligned() };
        let build_date = unsafe { std::ptr::addr_of!(self.build_date).read_unaligned() };
        f.debug_struct("DllVersionInfo")
            .field("major", &major)
            .field("minor", &minor)
            .field("patch", &patch)
            .field("build", &build)
            .field("build_date", &build_date)
            .finish()
    }
}

/// 启动参数结构体 (匹配 C DLL ec_startup_param_t)
#[repr(C, packed)]
#[derive(Clone, Copy)]
pub struct StartupParam {
    pub index: u16,
    pub sub_index: u8,
    pub data: [u8; 256],
    pub data_len: u16,
    pub transition: u8,
    pub timing: u8,
    pub priority: u16,
    pub complete_access: u8,
    pub is_register_write: u8,
}

impl std::fmt::Debug for StartupParam {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // packed 结构体不能直接引用字段，需要 read_unaligned 避免 UB
        let index = unsafe { std::ptr::addr_of!(self.index).read_unaligned() };
        let sub_index = unsafe { std::ptr::addr_of!(self.sub_index).read_unaligned() };
        let data_len = unsafe { std::ptr::addr_of!(self.data_len).read_unaligned() };
        let transition = unsafe { std::ptr::addr_of!(self.transition).read_unaligned() };
        let timing = unsafe { std::ptr::addr_of!(self.timing).read_unaligned() };
        let priority = unsafe { std::ptr::addr_of!(self.priority).read_unaligned() };
        let ca = unsafe { std::ptr::addr_of!(self.complete_access).read_unaligned() };
        let rw = unsafe { std::ptr::addr_of!(self.is_register_write).read_unaligned() };
        f.debug_struct("StartupParam")
            .field("index", &index)
            .field("sub_index", &sub_index)
            .field("data_len", &data_len)
            .field("transition", &transition)
            .field("timing", &timing)
            .field("priority", &priority)
            .field("complete_access", &ca)
            .field("is_register_write", &rw)
            .finish()
    }
}

/// 看门狗配置
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct WatchdogConfig {
    pub wd_divider: u16,
    pub wd_time_pdi: u16,
    pub wd_time_pd: u16,
}

/// 看门狗状态
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct WatchdogStatus {
    pub wd_status: u8,
    pub wd_counter: u8,
    pub wd_divider: u16,
    pub wd_time_pd: u16,
}

/// EMCY 紧急报文记录
#[repr(C, packed)]
#[derive(Clone, Copy)]
pub struct EmcyRecord {
    /// 错误码
    pub error_code: u16,
    /// 错误寄存器
    pub error_register: u8,
    /// 厂商特定数据
    pub data: [u8; 5],
    /// 从站索引
    pub slave_index: u16,
    /// 时间戳 (毫秒)
    pub timestamp_ms: u32,
}

impl Default for EmcyRecord {
    fn default() -> Self {
        Self {
            error_code: 0,
            error_register: 0,
            data: [0; 5],
            slave_index: 0,
            timestamp_ms: 0,
        }
    }
}

impl std::fmt::Debug for EmcyRecord {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // packed 结构体不能直接引用字段，需要 read_unaligned 避免 UB
        let error_code = unsafe { std::ptr::addr_of!(self.error_code).read_unaligned() };
        let error_register = unsafe { std::ptr::addr_of!(self.error_register).read_unaligned() };
        let data = unsafe { std::ptr::addr_of!(self.data).read_unaligned() };
        let slave_index = unsafe { std::ptr::addr_of!(self.slave_index).read_unaligned() };
        let timestamp_ms = unsafe { std::ptr::addr_of!(self.timestamp_ms).read_unaligned() };
        f.debug_struct("EmcyRecord")
            .field("error_code", &error_code)
            .field("error_register", &error_register)
            .field("data", &data)
            .field("slave_index", &slave_index)
            .field("timestamp_ms", &timestamp_ms)
            .finish()
    }
}

/// 拓扑节点信息
#[repr(C, packed)]
#[derive(Clone, Copy)]
pub struct TopologyNode {
    /// 从站索引
    pub slave_index: u16,
    /// 配置地址
    pub config_addr: u16,
    /// 父节点索引
    pub parent_index: u16,
    /// 入口端口
    pub entry_port: u8,
    /// 活动端口位图
    pub active_ports: u8,
    /// 拓扑类型
    pub topology: u8,
    /// 端口类型
    pub port_type: u8,
}

impl std::fmt::Debug for TopologyNode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // packed 结构体不能直接引用字段，需要 read_unaligned 避免 UB
        let slave_index = unsafe { std::ptr::addr_of!(self.slave_index).read_unaligned() };
        let config_addr = unsafe { std::ptr::addr_of!(self.config_addr).read_unaligned() };
        let parent_index = unsafe { std::ptr::addr_of!(self.parent_index).read_unaligned() };
        let entry_port = unsafe { std::ptr::addr_of!(self.entry_port).read_unaligned() };
        let active_ports = unsafe { std::ptr::addr_of!(self.active_ports).read_unaligned() };
        let topology = unsafe { std::ptr::addr_of!(self.topology).read_unaligned() };
        let port_type = unsafe { std::ptr::addr_of!(self.port_type).read_unaligned() };
        f.debug_struct("TopologyNode")
            .field("slave_index", &slave_index)
            .field("config_addr", &config_addr)
            .field("parent_index", &parent_index)
            .field("entry_port", &entry_port)
            .field("active_ports", &active_ports)
            .field("topology", &topology)
            .field("port_type", &port_type)
            .finish()
    }
}

/// FoE 扩展选项结构体
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct FoEOptions {
    /// 启用 CRC 校验
    pub enable_crc: u8,
    /// 严格模式
    pub strict_mode: u8,
    /// 自动追加 CRC 标记
    pub auto_append_crc: u8,
    /// 预期 CRC 值
    pub expected_crc: u32,
    /// CRC 进度回调 (可为 null)
    pub crc_progress_callback: Option<extern "C" fn(u32, u32, *mut c_void)>,
    /// 用户数据
    pub crc_callback_userdata: *mut c_void,
    /// 保留字段
    pub reserved: [u32; 8],
}

/// 从站身份信息
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct SlaveIdentity {
    pub vendor_id: u32,
    pub product_code: u32,
    pub revision_no: u32,
    pub serial_no: u32,
}

/// ESM 超时配置 (ETG.1020)
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct EsmTimeouts {
    /// Init -> PreOp (ms)
    pub ip: u32,
    /// PreOp -> SafeOp (ms)
    pub ps: u32,
    /// SafeOp -> OP (ms)
    pub so: u32,
    /// OP -> SafeOp (ms)
    pub os: u32,
    /// SafeOp -> PreOp (ms)
    pub sp: u32,
    /// PreOp -> Init (ms)
    pub pi: u32,
    /// Any -> Boot (ms)
    pub bi: u32,
    /// Boot -> Init (ms)
    pub ib: u32,
}

/// 冗余状态
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct RedundancyStatus {
    /// 冗余状态枚举值
    pub state: i32,
    /// 主链路是否连通
    pub primary_link_up: i32,
    /// 副链路是否连通
    pub secondary_link_up: i32,
    /// 主链路发送帧数
    pub primary_tx_frames: u32,
    /// 主链路接收帧数
    pub primary_rx_frames: u32,
    /// 副链路发送帧数
    pub secondary_tx_frames: u32,
    /// 副链路接收帧数
    pub secondary_rx_frames: u32,
    /// 故障切换次数
    pub failover_count: u32,
    /// 上次故障切换时间
    pub last_failover_time: u32,
}

/// 通信统计
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct CommunicationStats {
    pub total_cycles: u32,
    pub successful_cycles: u32,
    pub failed_cycles: u32,
    pub timeout_cycles: u32,
    pub average_cycle_time_us: f64,
    pub max_cycle_time_us: f64,
    pub min_cycle_time_us: f64,
}

// ===================== 回调函数类型 =====================

/// 日志回调
pub type LogCallback = extern "C" fn(category: c_int, message: *const c_char);
/// 崩溃通知回调
pub type CrashNotifyCallback = extern "C" fn(exception_code: c_int, message: *const c_char);
/// PDO 周期回调
pub type ProcessDataCyclicCallback = extern "C" fn(master_index: u16);
/// 从站状态变化回调
pub type SlaveStateChangeCallback = extern "C" fn(master_index: u16, slave_index: u16, old_state: c_int, new_state: c_int);
/// 紧急事件回调
pub type EmergencyEventCallback = extern "C" fn(master_index: u16, slave_index: u16, error_code: u16, error_reg: u16, b1: u8, w1: u16, w2: u16);
/// 从站发现/移除回调
pub type SlaveDiscoveryCallback = extern "C" fn(master_index: u16, slave_index: u16, is_found: i32);
/// 输入数据变化回调
pub type InputDataChangedCallback = extern "C" fn(master_index: u16, changed_slave_bits: *const u8, changed_count: u16);
/// 冗余模式变化回调
pub type RedundancyModeChangedCallback = extern "C" fn(master_index: u16, old_mode: c_int, new_mode: c_int);
/// PDO 帧丢失回调
pub type PDOFrameLossCallback = extern "C" fn(master_index: u16, group: u8, consecutive_lost: u32, total_lost: u32);
/// 从站 PreOp 重配置回调
pub type SlavePreOpReconfigCallback = extern "C" fn(master_index: u16, slave_index: u16);
/// 从站身份不符回调 (v2 热插拔自修复)
///
/// 触发时机: 断电重插从站后 ident FSM 读取到的 Vendor/Product 与配置不匹配,
///          或 Revision 低于配置 (向后兼容策略: actual >= configured 视为匹配).
/// 去重规则: 进入 IDENT_REJECTED 状态时仅触发一次, 直到用户调用
///          AcknowledgeSlaveReplacement 才会重新探测并可能再次触发.
pub type SlaveIdentityMismatchCallback = extern "C" fn(
    master_index: u16,
    slave_index: u16,
    expected_vendor: u32,
    expected_product: u32,
    expected_revision: u32,
    actual_vendor: u32,
    actual_product: u32,
    actual_revision: u32,
);
/// 从站端口链路变化回调 (断线检测)
///
/// 触发时机: 从站 DL Status (0x0110) 的 port link bit 从 1→0 (断开) 或 0→1 (恢复)
/// 每 1 秒在诊断周期检测, 每次 port link 翻转触发一次 (去重不重复触发)
/// is_up: 非 0 = link 恢复, 0 = link 断开; port: 0-3 对应 P0/P1/P2/P3
pub type SlavePortLinkChangedCallback = extern "C" fn(
    master_index: u16,
    slave_index: u16,
    port: u8,
    is_up: i32,
);
/// FoE 传输进度回调
pub type FoEProgressCallback = extern "C" fn(slave: u16, packet_number: c_int, data_size: c_int);
/// FoE BUSY 帧回调 (ETG.1000.6 Table 93 Done/Entire/BusyText).
///
/// 从站 Flash 烧写慢时周期返回 BUSY 帧, DLL 解析出 Done/Entire/可选 BusyText 后触发本回调.
/// `text` 为 UTF-8 字符串指针 (C 端 `const char*`), 无文本时为 null.
pub type FoEBusyCallback = extern "C" fn(
    slave: u16,
    done: u16,
    entire: u16,
    text: *const c_char,
    retry_idx: c_int,
);
/// DC 同步丢失回调
pub type DCSyncLostCallback = extern "C" fn(master_index: u16, slave_index: u16, diff_ns: c_int);

// ===================== AoE 通知回调类型 =====================

/// AoE 通知回调
pub type AOENotificationCallback = extern "C" fn(
    slave: u16, handle: u32, timestamp: u64,
    data: *const c_void, data_size: u32, user_data: *mut c_void,
);

// ===================== ETG.1510 结构体 =====================

/// 主站身份信息 (ETG.1510)
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct MasterIdentity {
    pub vendor_id: u32,
    pub product_code: u32,
    pub revision_no: u32,
    pub serial_no: u32,
    pub device_name: [u8; 64],
    pub hw_version: [u8; 32],
    pub sw_version: [u8; 32],
}

impl Default for MasterIdentity {
    fn default() -> Self {
        Self {
            vendor_id: 0,
            product_code: 0,
            revision_no: 0,
            serial_no: 0,
            device_name: [0; 64],
            hw_version: [0; 32],
            sw_version: [0; 32],
        }
    }
}

/// 主站诊断数据 (ETG.1510)
#[repr(C)]
#[derive(Debug, Clone, Copy, Default)]
pub struct MasterDiagData {
    pub cyclic_lost_frames: u32,
    pub acyclic_lost_frames: u32,
    pub cyclic_frames_per_sec: u32,
    pub acyclic_frames_per_sec: u32,
    pub master_state: u16,
}

// ===================== FSoE 类型定义 (ETG.5100/5120) =====================

/// FSoE 状态机状态
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FsoeState {
    /// 初始/重置状态
    Reset      = 0x100,
    /// 会话建立
    Session    = 0x101,
    /// 连接建立
    Connection = 0x102,
    /// 参数下载
    Parameter  = 0x103,
    /// 数据交换
    Data       = 0x104,
    /// 失效安全
    Failsafe   = 0x105,
}

/// FSoE 错误代码 (ETG.5001.4)
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FsoeError {
    /// 无错误
    None              = 0x0000,
    /// 错误的命令
    WrongCommand      = 0x0001,
    /// 未知命令
    UnknownCommand    = 0x0002,
    /// 连接ID不匹配
    WrongConnId       = 0x0003,
    /// CRC校验失败
    Crc               = 0x0004,
    /// 看门狗超时
    Watchdog          = 0x0005,
    /// 错误的FSoE地址
    WrongAddress      = 0x0006,
    /// 无效数据
    WrongData         = 0x0007,
    /// 通信参数长度错误
    CommParamLength   = 0x0008,
    /// 通信参数错误
    CommParam         = 0x0009,
    /// 应用参数长度错误
    AppParamLength    = 0x000A,
    /// 应用参数错误
    AppParam          = 0x000B,
    /// 意外的会话命令
    UnexpectedSession = 0x000C,
    /// 收到失效安全数据
    FailsafeData      = 0x000D,
    /// FSoE未初始化 (内部错误)
    NotInitialized    = 0x0100,
    /// 达到最大连接数 (内部错误)
    MaxConnections    = 0x0101,
    /// 无效状态转换 (内部错误)
    InvalidState      = 0x0102,
}

/// FSoE 连接配置 (对应 C fsoe_config_t)
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct FsoeConfig {
    /// 唯一连接标识符
    pub connection_id: u16,
    /// FSoE从站安全地址
    pub safety_address: u16,
    /// 看门狗超时(毫秒)
    pub watchdog_time_ms: u32,
    /// 安全输入数据大小(字节)
    pub safe_input_size: u16,
    /// 安全输出数据大小(字节)
    pub safe_output_size: u16,
    /// IOmap中输入偏移
    pub pdo_input_offset: u32,
    /// IOmap中输出偏移
    pub pdo_output_offset: u32,
}

/// FSoE 连接状态 (对应 C fsoe_status_t)
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct FsoeStatus {
    /// 当前FSoE状态
    pub state: FsoeState,
    /// 最后的错误代码
    pub last_error: FsoeError,
    /// 总错误计数
    pub error_count: u32,
    /// 发送帧数
    pub frames_sent: u32,
    /// 接收有效帧数
    pub frames_received: u32,
    /// CRC错误计数
    pub crc_errors: u32,
    /// 看门狗超时计数
    pub watchdog_errors: u32,
    /// 当前看门狗状态
    pub watchdog_expired: u8,
    /// 是否处于失效安全模式
    pub in_failsafe: u8,
    /// 当前切换位值
    pub toggle_bit: u8,
    /// 连接是否已初始化
    pub initialized: u8,
}

/// FSoE MDP 多连接配置 (对应 C fsoe_mdp_config_t)
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct SafeMdpConfig {
    /// 唯一连接标识符
    pub connection_id: u16,
    /// FSoE从站安全地址
    pub safety_address: u16,
    /// 看门狗超时(毫秒)
    pub watchdog_time_ms: u32,
    /// 安全输入数据大小(字节)
    pub safe_input_size: u16,
    /// 安全输出数据大小(字节)
    pub safe_output_size: u16,
    /// IOmap中输入偏移
    pub pdo_input_offset: u32,
    /// IOmap中输出偏移
    pub pdo_output_offset: u32,
    /// 模块编号 (槽位号)
    pub module_number: u16,
    /// 模块配置文件 (190, 195, 290, 790, 6900)
    pub module_profile: u16,
    /// 轴编号 (仅用于 Drive Connection)
    pub axis_number: u16,
    /// 连接类型: 0=Master, 1=Slave
    pub connection_type: u16,
}

/// Slave I/O 描述符 (匹配 C `slave_io_desc_t`)
///
/// 原平面字段 obits/obytes/outputs/ooffset/ostartbit (×2 输入/输出) 收敛为 sub-struct.
/// 嵌入到 `EcSlaveBlittable` 中段 (dtype 之后, sm 之前) 各 1 个 (output / input).
/// 自然对齐 (`#[repr(C)]`), 64 位下 24 字节: bits(2)+pad(2)+bytes(4)+ptr(8)+offset(4)+startbit(1)+pad(3).
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SlaveIoDescBlittable {
    /// I/O 位数
    pub bits: u16,
    /// I/O 字节数
    pub bytes: u32,
    /// I/O 指针 (IOmap 中, uint8_t*)
    pub ptr: usize,
    /// I/O 偏移 (IOmap 中)
    pub offset: u32,
    /// 第一个 I/O 字节的起始位
    pub startbit: u8,
}

/// Slave FSoE 子结构 (匹配 C `slave_fsoe_t` / C# `SlaveFsoe`)
///
/// Phase 2-E: 嵌入到 `EcSlaveBlittable` 中段 (alias_addr 之后, output 之前), 自然对齐.
/// 64 位下: capable (1) + 7 字节隐式填充 + connection (8) + sm_context (8) +
///          connection_id/safety_address/safe_input_size/safe_output_size (8) +
///          pdo_input_offset/pdo_output_offset (8) = 40 字节.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SlaveFsoeBlittable {
    /// FSoE 能力标志
    pub capable: u8,
    /// FSoE 连接指针
    pub connection: usize,
    /// FSoE 状态机上下文指针
    pub sm_context: usize,
    /// FSoE 连接 ID
    pub connection_id: u16,
    /// FSoE 安全地址
    pub safety_address: u16,
    /// FSoE 安全输入大小（字节）
    pub safe_input_size: u16,
    /// FSoE 安全输出大小（字节）
    pub safe_output_size: u16,
    /// FSoE PDO 输入偏移
    pub pdo_input_offset: u32,
    /// FSoE PDO 输出偏移
    pub pdo_output_offset: u32,
}

/// Slave 邮箱子结构 (匹配 C `slave_mailbox_t` / C# `SlaveMailbox`)
///
/// 原平面字段 mbx_l / mbx_wo / mbx_rl / mbx_ro / mbx_proto / mbx_cnt 收敛为 sub-struct.
/// 嵌入到 `EcSlaveBlittable` 中段 (FMMU 函数之后, hasdc 之前), 自然对齐 (`#[repr(C)]`).
/// 64 位下: 5 × u16 (10) + u8 (1) + 1 字节隐式填充 = 12 字节.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SlaveMailboxBlittable {
    /// 写邮箱长度 (mbx_l)
    pub length: u16,
    /// 写邮箱偏移 (mbx_wo)
    pub write_offset: u16,
    /// 读邮箱长度 (mbx_rl)
    pub read_length: u16,
    /// 读邮箱偏移 (mbx_ro)
    pub read_offset: u16,
    /// 支持的邮箱协议 ECT_MBXPROT_* (mbx_proto)
    pub supported_proto: u16,
    /// 邮箱链路层协议计数器 (mbx_cnt)
    pub cnt: u8,
}

/// Slave EEPROM 配置子结构 (匹配 C `slave_eeprom_config_t` / C# `SlaveEepromConfig`)
///
/// 原平面字段 SIIindex / eep_8byte / eep_pdi 收敛为 sub-struct.
/// 嵌入到 `EcSlaveBlittable` 中段 (dc_active 之后, coe_details 之前), 自然对齐 (`#[repr(C)]`).
/// 64 位下: u16 (2) + u8 (1) + u8 (1) = 4 字节.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SlaveEepromConfigBlittable {
    /// SII 配置索引 (SIIindex)
    pub sii_index: u16,
    /// 1=8字节读, 0=4字节读 (eep_8byte)
    pub read_8byte: u8,
    /// 0=EEPROM归主站, 1=归PDI (eep_pdi)
    pub pdi: u8,
}

/// Slave 邮箱处理器子结构 (匹配 C `slave_handler_t` / C# `SlaveHandler`)
///
/// 原平面字段 mbxhandlerstate / mbxrmpstate / mbxinstateex 收敛为 sub-struct.
/// 嵌入到 `EcSlaveBlittable` 中段 (po2so_config 之后, coe_mbx_in 之前), 自然对齐 (`#[repr(C)]`).
/// 64 位下: i32 (4) + i32 (4) + u16 (2) + 2 字节隐式填充 = 12 字节.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SlaveHandlerBlittable {
    /// 邮箱处理状态 (mbxhandlerstate, 0=no handler, 1=cyclic, 2=lost)
    pub state: i32,
    /// 邮箱 RMP 状态 (mbxrmpstate)
    pub rmp_state: i32,
    /// 邮箱扩展状态 (mbxinstateex)
    pub instate_ex: u16,
}

/// Slave Identity 子结构 (匹配 C `slave_identity_t`)
///
/// Phase 2-E: 把原平面 eep_man / eep_id / eep_rev / eep_ser / itype / dtype
/// 收敛进 metadata.identity 内. 自然对齐 (`#[repr(C)]`).
/// 4 × u32 (16) + 2 × u16 (4) = 20 字节.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SlaveIdentityBlittable {
    /// 厂商 ID (EEPROM, 原 eep_man)
    pub vendor_id: u32,
    /// 产品 ID (EEPROM, 原 eep_id)
    pub product_id: u32,
    /// 修订版本 (EEPROM, 原 eep_rev)
    pub revision: u32,
    /// 序列号 (EEPROM, 原 eep_ser)
    pub serial: u32,
    /// 接口类型 (原 itype)
    pub itype: u16,
    /// 设备类型 (原 dtype)
    pub dtype: u16,
}

/// Slave Metadata 子结构 (匹配 C `slave_metadata_t` / C# `SlaveMetadata`)
///
/// Phase 2-E: 头部新增 identity 子结构, 收敛 vendor/product/revision/serial/itype/dtype.
/// 嵌入到 `EcSlaveBlittable` 中段 (mbx_status 之后), 自然对齐 (`#[repr(C)]`).
#[repr(C)]
#[derive(Clone, Copy)]
pub struct SlaveMetadataBlittable {
    /// 厂商/产品/修订/序列/接口类型/设备类型 (Phase 2-E)
    pub identity: SlaveIdentityBlittable,
    /// 组名称 (EC_MAXNAME + 1)
    pub group_name: [u8; 41],
    /// 设备名称 (EC_MAXNAME + 1)
    pub device_name: [u8; 41],
    /// SM 数量 (sync_manager_count)
    pub sm_count: u16,
}

impl Default for SlaveMetadataBlittable {
    fn default() -> Self {
        Self {
            identity: SlaveIdentityBlittable::default(),
            group_name: [0; 41],
            device_name: [0; 41],
            sm_count: 0,
        }
    }
}

/// Slave Capabilities 子结构 (匹配 C `slave_capabilities_t` / C# `SlaveCapabilities`)
///
/// Darra 扩展字段 is_optional / supports_frame_repeat / mailbox_side 收敛为 sub-struct.
/// Phase 2-E: 追加 coe/foe/eoe/soe details (原平面字段).
/// C 端嵌入位置: 在 metadata 之后. 自然对齐 (`#[repr(C)]`), 7 字节 + 1 字节填充.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SlaveCapabilitiesBlittable {
    /// 0=必须从站, 1=可选 (缺席不影响 WKC)
    pub is_optional: u8,
    /// 0=不支持, 1=支持 ETG.1500 帧重复
    pub supports_frame_repeat: u8,
    /// 0=auto/dual, 1=primary_only, 2=secondary_only
    pub mailbox_side: u8,
    /// CoE 协议详情 (原 CoEdetails, Phase 2-E 移入)
    pub coe_details: u8,
    /// FoE 协议详情 (原 FoEdetails, Phase 2-E 移入)
    pub foe_details: u8,
    /// EoE 协议详情 (原 EoEdetails, Phase 2-E 移入)
    pub eoe_details: u8,
    /// SoE 协议详情 (原 SoEdetails, Phase 2-E 移入)
    pub soe_details: u8,
}

/// Slave Runtime 子结构 (匹配 C `slave_runtime_t`)
///
/// Phase 2-E: 把原平面 ebus_current / block_lrw / group / islost 收敛.
/// 嵌入位置: eeprom_config 之后. 自然对齐 (`#[repr(C)]`), 6 字节 (含尾部 1 字节填充).
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SlaveRuntimeBlittable {
    /// E-bus 电流 (mA)
    pub ebus_current: i16,
    /// 若 >0 禁止使用 LRW
    pub block_lrw: u8,
    /// 分组
    pub group: u8,
    /// 从站是否丢失 (FFI bool 不稳, 用 u8)
    pub is_lost: u8,
}

/// Slave SM/FMMU 配置 sub-struct (匹配 C `slave_sm_fmmu_t`)
///
/// Phase 2-E: 把原平面 SMtype[8] / SM_app_length[8] / FMMU{0..3}func / FMMUunused 收敛.
/// 嵌入位置: SM/FMMU 数组之后. 自然对齐 (`#[repr(C)]`),
/// 8 + 16 + 4 + 1 + 3 字节填充 = 32 字节.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SlaveSmFmmuBlittable {
    /// SM 类型 (原平面 SMtype[MAXSM])
    pub sm_type: [u8; 8],
    /// SM 应用长度 (原 SM_app_length)
    pub sm_app_length: [u16; 8],
    /// FMMU 0..3 功能类型 (原 FMMU{0..3}func)
    pub fmmu_func: [u8; 4],
    /// 第一个未使用的 FMMU 索引 (原 FMMUunused)
    pub fmmu_unused: u8,
}

/// Slave 协议邮箱状态 sub-struct (匹配 C `slave_proto_mbx_t`)
///
/// Phase 2-E: 把每个邮箱协议的平面 *embxin / *embxinfull / *embxoverrun 收敛.
/// 6 个协议 (CoE/SoE/FoE/EoE/VoE/AoE) 各持有一份.
/// 自然对齐 (`#[repr(C)]`), 64 位下: ptr (8) + u8 (1) + 3 字节填充 + i32 (4) = 16 字节.
/// `in` 是 Rust 关键字, 字段命名为 `in_ptr`.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SlaveProtoMbxBlittable {
    /// 邮箱输入缓冲区指针 (原 *embxin)
    pub in_ptr: usize,
    /// 邮箱输入标志 (原 *embxinfull)
    pub in_full: u8,
    /// 邮箱溢出计数 (原 *embxoverrun)
    pub overrun: i32,
}

/// Slave PDO 配置子结构 (匹配 C `slave_pdo_config_t` / C# `SlavePdoConfig`)
///
/// 原平面字段 pdo_assignment_enabled / pdo_configuration_enabled / pdo_config_initialized /
/// supports_complete_access 收敛为 sub-struct.
/// 嵌入到 EcSlaveBlittable 中段 (mbx_status 之后, 跟 metadata 相邻),
/// 自然对齐 (`#[repr(C)]`), 4 字节.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SlavePdoConfigBlittable {
    /// 是否写入 PDO Assignment 0x1C12/0x1C13 (pdo_assignment_enabled)
    pub assignment_enabled: u8,
    /// 是否写入 PDO Configuration (pdo_configuration_enabled)
    pub configuration_enabled: u8,
    /// PDO 配置是否已从 ESI 初始化 (pdo_config_initialized)
    pub config_initialized: u8,
    /// 是否支持 Complete Access (supports_complete_access)
    pub supports_complete_access: u8,
}

/// Slave 拓扑子结构 (匹配 C `slave_topology_t` / C# `SlaveTopology`)
///
/// 原平面字段 hasdc / ptype / topology / activeports / consumedports / parent / parentport /
/// entryport 收敛为 sub-struct.
/// 嵌入到 `EcSlaveBlittable` 中段 (mbx 之后, eeprom_config 之前), 自然对齐 (`#[repr(C)]`).
/// 64 位下: 5×u8 (5) + 1 字节填充 + u16 (2) + 2×u8 (2) = 10 字节.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SlaveTopologyBlittable {
    /// 是否支持 DC (hasdc)
    pub has_dc: u8,
    /// 物理端口类型 (ptype)
    pub phy_type: u8,
    /// 拓扑类型 (topology)
    pub link_count: u8,
    /// 活动端口位图 (activeports)
    pub active_ports: u8,
    /// 已使用端口位图 (consumedports)
    pub consumed_ports: u8,
    /// 父从站站地址 (parent)
    pub parent: u16,
    /// 父从站端口号 (parentport)
    pub parent_port: u8,
    /// 入口端口号 (entryport)
    pub entry_port: u8,
}

/// Slave DC 子结构 (匹配 C `slave_dc_t` / C# `SlaveDc`)
///
/// 原平面字段 DCrtA..D / pdelay / DCnext / DCprevious / DCcycle / DCcycle1 / DCshift /
/// DCactive 收敛为 sub-struct.
/// 嵌入到 `EcSlaveBlittable` 中段 (topo 之后, eeprom_config 之前), 自然对齐 (`#[repr(C)]`).
/// 64 位下: i32[4] (16) + i32 (4) + 2×u16 (4) + 3×i32 (12) + u16 (2) + 2 字节尾部填充 = 40 字节.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct SlaveDcBlittable {
    /// 端口 A/B/C/D DC 接收时间 (纳秒) (DCrtA..D)
    pub recvtime: [i32; 4],
    /// 传播延迟 (纳秒) (pdelay)
    pub propagation_delay: i32,
    /// DC 链中下一个从站 (DCnext)
    pub next: u16,
    /// DC 链中上一个从站 (DCprevious)
    pub prev: u16,
    /// DC SYNC0 周期 (纳秒) (DCcycle)
    pub cycle0: i32,
    /// DC SYNC1 周期 (纳秒) (DCcycle1)
    pub cycle1: i32,
    /// DC 相位偏移 (纳秒) (DCshift)
    pub shift: i32,
    /// DC AssignActivate 值, 0=禁用 (DCactive)
    pub active: u16,
}

/// 从站原生结构体 (匹配 C ec_slavet / C# ec_slave_blittable)
///
/// 通过 `GetSlave()` 获取指针后, 可直接读取字段。
/// 字段布局必须与 DLL 端完全一致 (Phase 2-E 重排).
///
/// Phase 2-E DLL 字段顺序基准:
/// state, ALstatuscode, configadr, aliasadr,
/// fsoe, output, input,
/// SM[8], FMMU[4],
/// sm_fmmu (sm_type[8]/sm_app_length[8]/fmmu_func[4]/fmmu_unused),
/// mbx, topo, dc, eeprom_config,
/// runtime (ebus_current/block_lrw/group/is_lost),
/// PO2SOconfig, handler,
/// coe, soe, foe, eoe, voe, aoe (各 slave_proto_mbx),
/// mbxstatus,
/// metadata (identity{vendor_id/product_id/revision/serial/itype/dtype} + group_name + device_name + sm_count),
/// pdo_config,
/// capabilities (is_optional/supports_frame_repeat/mailbox_side/coe_details/foe_details/eoe_details/soe_details).
#[repr(C)]
#[derive(Clone, Copy)]
pub struct EcSlaveBlittable {
    // ==================== 基本状态字段 ====================
    /// 当前 EtherCAT 状态
    pub state: u16,
    /// AL Status Code (错误码)
    pub al_status_code: u16,
    /// 物理配置地址
    pub config_addr: u16,
    /// 别名地址
    pub alias_addr: u16,

    // ==================== FSoE 子结构 (Phase 1) ====================
    /// FSoE 安全协议子结构 (slave_fsoe_t)
    pub fsoe: SlaveFsoeBlittable,

    // ==================== I/O 描述符 sub-struct ====================
    /// 输出 (PDO Tx from master), 24 字节
    pub output: SlaveIoDescBlittable,
    /// 输入 (PDO Rx to master), 24 字节
    pub input: SlaveIoDescBlittable,

    // ==================== SM/FMMU 数组 ====================
    /// SM 结构 - 8 条目, 每条 8 字节 = 64 字节
    pub sm_buffer: [u8; 64],
    /// FMMU 结构 - 4 条目, 每条 16 字节 = 64 字节
    pub fmmu_buffer: [u8; 64],

    // ==================== SM/FMMU 配置 sub-struct (Phase 2-E) ====================
    /// 替换原平面 SMtype[8] / SM_app_length[8] / FMMU{0..3}func / FMMUunused.
    pub sm_fmmu: SlaveSmFmmuBlittable,

    // ==================== 邮箱配置 sub-struct ====================
    /// 邮箱长度/偏移/协议/计数器
    pub mbx: SlaveMailboxBlittable,

    // ==================== 拓扑 sub-struct ====================
    /// 拓扑信息 (slave_topology_t)
    pub topo: SlaveTopologyBlittable,

    // ==================== DC sub-struct ====================
    /// DC 信息 (slave_dc_t)
    pub dc: SlaveDcBlittable,

    // ==================== EEPROM 配置 sub-struct ====================
    /// SII 配置索引 + EEPROM 读宽度 + PDI 标志
    pub eeprom_config: SlaveEepromConfigBlittable,

    // ==================== Runtime sub-struct (Phase 2-E) ====================
    /// 替换原平面 ebus_current / block_lrw / group / islost.
    pub runtime: SlaveRuntimeBlittable,

    // ==================== 函数指针 ====================
    /// PO->SO 配置函数指针
    pub po2so_config: usize,

    // ==================== 邮箱处理器 sub-struct ====================
    /// 邮箱处理器状态
    pub handler: SlaveHandlerBlittable,

    // ==================== 6 协议邮箱 (Phase 2-E 收敛为 SlaveProtoMbxBlittable) ====================
    /// CoE 邮箱状态
    pub coe: SlaveProtoMbxBlittable,
    /// SoE 邮箱状态
    pub soe: SlaveProtoMbxBlittable,
    /// FoE 邮箱状态
    pub foe: SlaveProtoMbxBlittable,
    /// EoE 邮箱状态
    pub eoe: SlaveProtoMbxBlittable,
    /// VoE 邮箱状态
    pub voe: SlaveProtoMbxBlittable,
    /// AoE 邮箱状态
    pub aoe: SlaveProtoMbxBlittable,

    // ==================== 邮箱状态 ====================
    /// 输出邮箱状态寄存器缓冲区指针
    pub mbx_status: usize,

    // ==================== Metadata 子结构 (Phase 2-E: 头部新增 identity) ====================
    /// 厂商/产品/修订/序列/itype/dtype + 组名/设备名/SM 数量
    pub metadata: SlaveMetadataBlittable,

    // ==================== PDO 配置子结构 ====================
    /// PDO Assignment/Config 开关 + CA 支持
    pub pdo_config: SlavePdoConfigBlittable,

    // ==================== Capabilities 子结构 (Phase 2-E: 追加 4 协议 details) ====================
    /// 可选标记 / 帧重复 / 邮箱单侧路由 / coe/foe/eoe/soe details
    pub capabilities: SlaveCapabilitiesBlittable,
}

impl EcSlaveBlittable {
    /// 从原生指针读取组名字符串 (从 metadata 子结构)
    ///
    /// [2026-05-08 修复乱码] 改走 `decode_ethercat_string` 多编码兜底
    /// (UTF-8 严格 → ASCII 严格 → Latin-1), 取代 `from_utf8_lossy`,
    /// 厂商名带 ™ © ± 等扩展 Latin-1 字符不再丢失.
    pub fn group_name_str(&self) -> String {
        let len = self.metadata.group_name.iter().position(|&b| b == 0).unwrap_or(41);
        crate::utils::help::decode_ethercat_string(&self.metadata.group_name[..len])
    }

    /// 从原生指针读取设备名字符串 (从 metadata 子结构)
    ///
    /// [2026-05-08 修复乱码] 同 `group_name_str`, 走多编码兜底.
    pub fn device_name_str(&self) -> String {
        let len = self.metadata.device_name.iter().position(|&b| b == 0).unwrap_or(41);
        crate::utils::help::decode_ethercat_string(&self.metadata.device_name[..len])
    }
}

#[link(name = "Darra.Core")]
extern "C" {
    // ===================== 初始化与生命周期 =====================
    #[link_name = "D_1547"]
    pub fn Initialize() -> u16;
    #[link_name = "D_1548"]
    pub fn InitializeSpecificMaster(master_index: u16) -> u16;
    #[link_name = "D_1549"]
    pub fn Dispose(master_index: u16);
    #[link_name = "D_1592"]
    pub fn Start(master_index: u16);
    #[link_name = "D_1593"]
    pub fn Stop(master_index: u16);
    #[link_name = "D_1047"]
    pub fn FreeMemory(ptr: *mut c_void);

    // ===================== 版本信息 =====================
    #[link_name = "D_1596"]
    pub fn GetDllVersionInfo() -> *const DllVersionInfo;

    // ===================== 网络配置 =====================
    #[link_name = "D_1550"]
    pub fn SetNetwork(master_index: u16, adapter: *const c_char, redundant: *const c_char) -> c_int;
    #[link_name = "D_1076"]
    pub fn GetNetworkInfo(is_redundant: i32, need_slaves_num: i32) -> c_int;
    #[link_name = "D_1077"]
    pub fn GetNetworksPointer() -> *const c_void;
    #[link_name = "D_1080"]
    pub fn QuickSlaveCount(adapter_name: *const c_char) -> c_int;
    #[link_name = "D_1084"]
    pub fn ReadSlaveInfo(adapter_name: *const c_char) -> c_int;
    #[link_name = "D_1085"]
    pub fn GetScannedSlaveCount() -> c_int;
    #[link_name = "D_1086"]
    pub fn GetScannedSlaveInfo(
        index: c_int, vendor_id: *mut u32, product_code: *mut u32,
        revision: *mut u32, serial: *mut u32,
        name: *mut c_char, name_size: c_int,
        config_addr: *mut u16, alias_addr: *mut u16,
        parent: *mut u16, topology: *mut u8, activeports: *mut u8,
        entryport: *mut u8, parentport: *mut u8, ptype: *mut u8,
    ) -> i32;
    #[link_name = "D_1082"]
    pub fn GetRingSlaveCount() -> c_int;

    // ===================== 状态管理 =====================
    #[link_name = "D_1551"]
    pub fn SetState(master_index: u16, state: c_int) -> i32;
    #[link_name = "D_1092"]
    pub fn SetStateWithTimeout(master_index: u16, state: c_int, timeout_ms: u32) -> i32;
    #[link_name = "D_1093"]
    pub fn SetSlaveStateWithTimeout(master_index: u16, slave_index: u16, state: c_int, timeout_ms: u32) -> i32;
    #[link_name = "D_1132"]
    pub fn SetStateSequence(master_index: u16, target_state: c_int, timeout_ms: u32) -> i32;
    #[link_name = "D_1131"]
    pub fn SetStateWithStartup(master_index: u16, target_state: c_int, timeout_ms: u32) -> i32;
    #[link_name = "D_1553"]
    pub fn GetMasterState(master_index: u16) -> *const c_void;
    #[link_name = "D_1555"]
    pub fn GetMasterStateCache(master_index: u16) -> *const c_void;
    #[link_name = "D_1552"]
    pub fn GetLinkStatus(master_index: u16) -> u8;
    #[link_name = "D_1094"]
    pub fn GetSlaveState(master_index: u16, slave_index: u16) -> u8;
    #[link_name = "D_1095"]
    pub fn GetSlaveALStatusCode(master_index: u16, slave_index: u16) -> u16;

    // ===================== IO 映射 =====================
    #[link_name = "D_1557"]
    pub fn GetIO(master_index: u16, slave: u16, out_byte_size: *mut c_int, out_byte: *mut *mut u8, in_byte_size: *mut c_int, in_byte: *mut *mut u8) -> u8;
    #[link_name = "D_1558"]
    pub fn LockIOmap(master_index: u16);
    #[link_name = "D_1559"]
    pub fn UnlockIOmap(master_index: u16);
    #[link_name = "D_1561"]
    pub fn WriteSlaveOutput(master_index: u16, slave_index: u16, data: *const u8, size: u32);
    #[link_name = "D_1562"]
    pub fn WriteSlaveOutputByte(master_index: u16, slave_index: u16, offset: u32, value: u8);
    #[link_name = "D_1597"]
    pub fn SetMutexProtection(master_index: u16, enable: i32);
    #[link_name = "D_1560"]
    pub fn GetMutexProtection(master_index: u16) -> i32;

    // ===================== SDO 读写 (CoE) =====================
    #[link_name = "D_1572"]
    pub fn SDOread(master_index: u16, slave: u16, index: u16, subindex: u8, ca: i32, out_byte_size: *mut c_int) -> *mut u8;
    #[link_name = "D_1571"]
    pub fn SDOwrite_raw(master_index: u16, slave: u16, index: u16, subindex: u8, ca: i32, bytes: *const u8, length: c_int) -> u8;

    // ===================== SDO 对象字典 =====================
    #[link_name = "D_1564"]
    pub fn GetSlaveSDOList(master_index: u16, slave_index: u16) -> *const c_void;
    #[link_name = "D_1563"]
    pub fn GetSlaveSDOListBasic(master_index: u16, slave_index: u16) -> *const c_void;
    #[link_name = "D_1569"]
    pub fn GetSlavePointer_SDO_Value(master_index: u16, slave_index: u16, od_index: u16, oe_index: u8, out_byte_size: *mut c_int) -> *mut u8;
    #[link_name = "D_1570"]
    pub fn GetSlavePointer_SDO_IndexValue(master_index: u16, slave_index: u16, index: u16, subidx: u8, out_byte_size: *mut c_int) -> *mut u8;
    #[link_name = "D_1556"]
    pub fn GetSlave(master_index: u16, slave_index: u16) -> *const c_void;
    #[link_name = "D_1330"]
    pub fn GetSlaveDetailedInfo(master_index: u16, slave_index: u16) -> *const c_void;

    // ===================== SoE (Servo over EtherCAT) =====================
    #[link_name = "D_1501"]
    pub fn SoERead(master_index: u16, slave: u16, drive_no: u8, element_flags: u8, idn: u16, data: *mut *mut c_void, data_size: *mut c_int, timeout: c_int) -> i32;
    #[link_name = "D_1502"]
    pub fn SoEWrite(master_index: u16, slave: u16, drive_no: u8, element_flags: u8, idn: u16, data: *const u8, data_size: c_int, timeout: c_int) -> i32;
    #[link_name = "D_1503"]
    pub fn SoEReadAttributes(master_index: u16, slave: u16, drive_no: u8, idn: u16, attributes: *mut u32, timeout: c_int) -> i32;
    #[link_name = "D_1504"]
    pub fn SoEReadName(master_index: u16, slave: u16, drive_no: u8, idn: u16, name: *mut *mut c_void, name_length: *mut c_int, timeout: c_int) -> i32;
    #[link_name = "D_1505"]
    pub fn SoEReadUnit(master_index: u16, slave: u16, drive_no: u8, idn: u16, unit: *mut *mut c_void, unit_length: *mut c_int, timeout: c_int) -> i32;
    #[link_name = "D_1506"]
    pub fn SoEReadIDNList(master_index: u16, slave: u16, drive_no: u8, idn_list: *mut *mut u16, list_count: *mut c_int, timeout: c_int) -> i32;
    #[link_name = "D_1507"]
    pub fn SoEReadMinMax(master_index: u16, slave: u16, drive_no: u8, idn: u16,
                         min_value: *mut *mut c_void, min_size: *mut c_int,
                         max_value: *mut *mut c_void, max_size: *mut c_int,
                         timeout: c_int) -> i32;

    // ===================== FoE (File over EtherCAT) =====================
    #[link_name = "D_1476"]
    pub fn FOERead(master_index: u16, slave: u16, filename: *const c_char, password: u32, file_data: *mut *mut c_void, file_size: *mut c_int, timeout: c_int) -> i32;
    #[link_name = "D_1477"]
    pub fn FOEWrite(master_index: u16, slave: u16, filename: *const c_char, password: u32, file_data: *const c_void, file_size: c_int, timeout: c_int) -> i32;
    #[link_name = "D_1480"]
    pub fn FOEReadEx(master_index: u16, slave: u16, filename: *const c_char, password: u32, file_data: *mut *mut c_void, file_size: *mut c_int, timeout: c_int, options: *mut FoEOptions) -> i32;
    #[link_name = "D_1481"]
    pub fn FOEWriteEx(master_index: u16, slave: u16, filename: *const c_char, password: u32, file_data: *const c_void, file_size: c_int, timeout: c_int, options: *mut FoEOptions) -> i32;
    #[link_name = "D_1478"]
    pub fn FOESetProgressHook(master_index: u16, callback: Option<FoEProgressCallback>) -> i32;
    #[link_name = "D_1479"]
    pub fn FOEClearProgressHook(master_index: u16) -> i32;

    // ===================== EoE (Ethernet over EtherCAT) =====================
    #[link_name = "D_1485"]
    pub fn EOESetIP(master_index: u16, slave: u16, port: u8, ip: u32, subnet: u32, gateway: u32, timeout: c_int) -> i32;
    #[link_name = "D_1486"]
    pub fn EOEGetIP(master_index: u16, slave: u16, port: u8, ip: *mut u32, subnet: *mut u32, gateway: *mut u32, timeout: c_int) -> i32;
    #[link_name = "D_1495"]
    pub fn EOESendFrame(master_index: u16, slave: u16, port: u8, frame_data: *const u8, frame_size: c_int, timeout: c_int) -> i32;
    #[link_name = "D_1496"]
    pub fn EOEReceiveFrame(master_index: u16, slave: u16, port: u8, frame_data: *mut *mut c_void, frame_size: *mut c_int, timeout: c_int) -> i32;
    #[link_name = "D_1487"]
    pub fn EOESetMAC(master_index: u16, slave: u16, port: u8, mac: *const u8, timeout: c_int) -> i32;
    #[link_name = "D_1488"]
    pub fn EOEGetMAC(master_index: u16, slave: u16, port: u8, mac: *mut u8, timeout: c_int) -> i32;

    // ===================== AoE (ADS over EtherCAT) =====================
    #[link_name = "D_1508"]
    pub fn AOESendCommand(master_index: u16, slave: u16, target_port: u16, command_id: u16, command_data: *const u8, command_size: u32, response_data: *mut *mut c_void, response_size: *mut u32, timeout: c_int) -> i32;
    #[link_name = "D_1509"]
    pub fn AOEReadWrite(master_index: u16, slave: u16, index_group: u32, index_offset: u32, read_length: u32, write_length: u32, write_data: *const u8, read_data: *mut *mut c_void, bytes_read: *mut u32, timeout: c_int) -> i32;
    #[link_name = "D_1510"]
    pub fn AOEReadDeviceInfo(master_index: u16, slave: u16, major_ver: *mut u8, minor_ver: *mut u8, build: *mut u16, device_name: *mut u8, name_size: c_int, timeout: c_int) -> i32;
    #[link_name = "D_1511"]
    pub fn AOEReadState(master_index: u16, slave: u16, ads_state: *mut u16, device_state: *mut u16, timeout: c_int) -> i32;
    #[link_name = "D_1512"]
    pub fn AOEWriteControl(master_index: u16, slave: u16, ads_state: u16, device_state: u16, data: *const u8, data_size: c_int, timeout: c_int) -> i32;
    #[link_name = "D_1513"]
    pub fn AOEAddNotification(master_index: u16, slave: u16,
                              index_group: u32, index_offset: u32, length: u32,
                              trans_mode: u32, max_delay: u32, cycle_time: u32,
                              handle: *mut u32, timeout: c_int) -> i32;
    #[link_name = "D_1514"]
    pub fn AOEDelNotification(master_index: u16, slave: u16, handle: u32, timeout: c_int) -> i32;
    #[link_name = "D_1515"]
    pub fn AOESetConfig(master_index: u16, slave: u16,
                        target_net_id: *const u8, target_port: u16,
                        source_net_id: *const u8, source_port: u16) -> i32;
    #[link_name = "D_1516"]
    pub fn AOEGetConfig(master_index: u16, slave: u16,
                        target_net_id: *mut u8, target_port: *mut u16,
                        source_net_id: *mut u8, source_port: *mut u16) -> i32;

    // ===================== VoE (Vendor over EtherCAT) =====================
    #[link_name = "D_1518"]
    pub fn VOESend(master_index: u16, slave: u16, vendor_id: u32, vendor_type: u16, data: *const u8, data_size: c_int, timeout: c_int) -> i32;
    #[link_name = "D_1519"]
    pub fn VOEReceive(master_index: u16, slave: u16, vendor_id: *mut u32, vendor_type: *mut u16, data: *mut *mut c_void, data_size: *mut c_int, timeout: c_int) -> i32;
    #[link_name = "D_1520"]
    pub fn VOESendRaw(master_index: u16, slave: u16, frame_data: *const u8, frame_size: c_int, timeout: c_int) -> i32;
    #[link_name = "D_1521"]
    pub fn VOEReceiveRaw(master_index: u16, slave: u16, frame_data: *mut *mut c_void, frame_size: *mut c_int, timeout: c_int) -> i32;
    #[link_name = "D_1517"]
    pub fn VOEIsSupported(master_index: u16, slave: u16) -> i32;

    // ===================== 高级 API: 一步初始化 =====================
    #[link_name = "D_1135"]
    pub fn EcInit(json_config: *const c_char) -> u16;
    #[link_name = "D_1136"]
    pub fn EcInitFromFile(json_file_path: *const c_char) -> u16;
    #[link_name = "D_1137"]
    pub fn EcClose(master_index: u16);
    #[link_name = "D_1133"]
    pub fn LoadConfigJson(master_index: u16, json_str: *const c_char) -> c_int;
    #[link_name = "D_1134"]
    pub fn AutoConfigureSM(master_index: u16, slave_index: u16) -> c_int;
    pub fn DarraCoreInvoke(master_index: u16, command: u32, arg0: u32, arg1: u32, arg2: u32) -> c_int;
    pub fn DarraCoreInvokeText(master_index: u16, command: u32, text: *const c_char, arg0: u32, arg1: u32, arg2: u32) -> c_int;

    // ===================== 启动参数管线 =====================
    #[link_name = "D_1125"]
    pub fn AddStartupParameter(master_index: u16, slave_index: u16, param: *const StartupParam) -> c_int;
    #[link_name = "D_1126"]
    pub fn AddStartupParameterBatch(master_index: u16, slave_index: u16, param_array: *const StartupParam, count: c_int) -> c_int;
    #[link_name = "D_1127"]
    pub fn ClearStartupParameters(master_index: u16, slave_index: u16) -> c_int;
    #[link_name = "D_1128"]
    pub fn GetStartupParameterCount(master_index: u16, slave_index: u16) -> c_int;
    #[link_name = "D_1129"]
    pub fn ApplyStartupParameters(master_index: u16, slave_index: u16, transition: u8, timing: u8) -> c_int;
    #[link_name = "D_1130"]
    pub fn ApplyStartupParametersAll(master_index: u16, transition: u8, timing: u8) -> c_int;

    // ===================== DC 分布式时钟 =====================
    #[link_name = "D_1087"]
    pub fn SetMasterDCCycleTime(master_index: u16, time_ns: u32);
    #[link_name = "D_1088"]
    pub fn SetMasterLoopCycleTime(master_index: u16, time_ns: u32);
    #[link_name = "D_1090"]
    pub fn SetSyncBySlaveIndex(master_index: u16, slave_index: u16, sync0_ns: u32, sync1_ns: u32, shift_ns: i32);
    #[link_name = "D_1278"]
    pub fn ConfigureDCAll(master_index: u16, sync0_ns: u32, sync1_ns: u32) -> c_int;
    #[link_name = "D_1274"]
    pub fn UpdatePropagationDelays(master_index: u16) -> c_int;
    #[link_name = "D_1277"]
    pub fn AutoCalculateDCShift(master_index: u16) -> c_int;
    #[link_name = "D_1275"]
    pub fn GetSlavePropagationDelay(master_index: u16, slave_index: u16) -> i32;
    #[link_name = "D_1276"]
    pub fn GetMaxPropagationDelay(master_index: u16) -> i32;
    #[link_name = "D_1279"]
    pub fn EnableContinuousMeasurement(master_index: u16, enable: i32, interval_sec: u32);
    #[link_name = "D_1280"]
    pub fn EnableDriftCompensation(master_index: u16, enable: i32, threshold_ns: i32, gain: i32);
    #[link_name = "D_1023"]
    pub fn SetDCAutoShiftEnabled(master_index: u16, enable: i32);
    #[link_name = "D_1024"]
    pub fn GetDCAutoShiftEnabled(master_index: u16) -> i32;

    // DC 同步窗口监控 (ETG.1500 5.13.3)
    #[link_name = "D_1281"]
    pub fn GetSlaveSyncWindowStatus(master_index: u16, slave_index: u16,
                                    diff_ns: *mut c_int, max_diff_ns: *mut c_int, min_diff_ns: *mut c_int,
                                    in_sync: *mut i32, out_of_sync_count: *mut u32) -> i32;
    #[link_name = "D_1283"]
    pub fn SetSyncWindowThreshold(master_index: u16, threshold_ns: c_int);
    #[link_name = "D_1284"]
    pub fn GetSyncWindowThreshold(master_index: u16) -> c_int;
    #[link_name = "D_1285"]
    pub fn ResetSlaveSyncWindowStats(master_index: u16, slave_index: u16);
    #[link_name = "D_1286"]
    pub fn GetMaxSyncDifference(master_index: u16) -> c_int;
    #[link_name = "D_1289"]
    pub fn IsAllSlavesInSync(master_index: u16) -> i32;

    // ===================== CPU/实时性能 =====================
    #[link_name = "D_1016"]
    pub fn GetAvailableCpuCores() -> c_int;
    #[link_name = "D_1017"]
    pub fn SetMasterCpuAffinity(master_index: u16, cpu_core: c_int) -> i32;
    #[link_name = "D_1018"]
    pub fn SetProcessCpuAffinity(cpu_core: c_int) -> i32;
    #[link_name = "D_1021"]
    pub fn SetPDOThreadCpuAffinity(master_index: u16, cpu_core: c_int) -> i32;
    #[link_name = "D_1022"]
    pub fn GetPDOThreadCpuAffinity(master_index: u16) -> c_int;
    #[link_name = "D_1595"]
    pub fn ApplyRealtimeOptimizations() -> i32;
    #[link_name = "D_1019"]
    pub fn RemoveRealtimeOptimizations() -> i32;
    #[link_name = "D_1020"]
    pub fn GetRealtimeOptimizationsStatus() -> i32;
    #[link_name = "D_1089"]
    pub fn GetTimingMode(master_index: u16) -> u32;

    // ===================== 组管理 =====================
    #[link_name = "D_1005"]
    pub fn SetSlaveGroup(master_index: u16, slave_index: u16, group: u8) -> i32;
    #[link_name = "D_1006"]
    pub fn GetSlaveGroup(master_index: u16, slave_index: u16) -> u8;
    #[link_name = "D_1007"]
    pub fn SetGroupCycleDivider(master_index: u16, group: u8, divider: u8) -> i32;
    #[link_name = "D_1008"]
    pub fn GetGroupCycleDivider(master_index: u16, group: u8) -> u8;
    #[link_name = "D_1009"]
    pub fn SetGroupEnabled(master_index: u16, group: u8, enabled: i32) -> i32;
    #[link_name = "D_1010"]
    pub fn GetGroupEnabled(master_index: u16, group: u8) -> i32;
    #[link_name = "D_1011"]
    pub fn GetGroupExpectedWKC(master_index: u16, group: u8) -> u16;
    #[link_name = "D_1014"]
    pub fn GetActiveGroupCount(master_index: u16) -> u8;
    #[link_name = "D_1015"]
    pub fn GetGroupSlaveCount(master_index: u16, group: u8) -> u16;

    // ===================== 看门狗 =====================
    #[link_name = "D_1117"]
    pub fn SetSlaveWatchdog(master_index: u16, slave_index: u16, timeout_ms: u32) -> i32;
    #[link_name = "D_1118"]
    pub fn SetSlavePdiWatchdog(master_index: u16, slave_index: u16, timeout_ms: u32) -> i32;
    #[link_name = "D_1119"]
    pub fn GetSlaveWatchdogConfig(master_index: u16, slave_index: u16, config: *mut WatchdogConfig) -> i32;
    #[link_name = "D_1120"]
    pub fn GetSlaveWatchdogStatus(master_index: u16, slave_index: u16, status: *mut WatchdogStatus) -> i32;
    #[link_name = "D_1121"]
    pub fn SetAllSlaveWatchdog(master_index: u16, timeout_ms: u32) -> c_int;
    #[link_name = "D_1122"]
    pub fn SetAllSlavePdiWatchdog(master_index: u16, timeout_ms: u32) -> c_int;

    // ===================== 从站验证 =====================
    #[link_name = "D_1103"]
    pub fn GetSlaveIdentity(master_index: u16, slave_index: u16, identity: *mut SlaveIdentity) -> i32;
    #[link_name = "D_1104"]
    pub fn VerifySlaveIdentity(master_index: u16, slave_index: u16, expected: *const SlaveIdentity, check_rev: i32, check_serial: i32) -> i32;
    #[link_name = "D_1113"]
    pub fn SetSlaveOptional(master_index: u16, slave_index: u16, is_optional: i32) -> i32;
    #[link_name = "D_1114"]
    pub fn GetSlaveOptional(master_index: u16, slave_index: u16) -> i32;
    #[link_name = "D_1115"]
    pub fn SetSlaveSupportsFrameRepeat(master_index: u16, slave_index: u16, supports: i32) -> i32;
    #[link_name = "D_1116"]
    pub fn GetSlaveSupportsFrameRepeat(master_index: u16, slave_index: u16) -> i32;

    // ===================== ESM 超时 (ETG.1020) =====================
    #[link_name = "D_1101"]
    pub fn GetSlaveEsmTimeouts(master_index: u16, slave_index: u16, timeouts: *mut EsmTimeouts) -> i32;
    #[link_name = "D_1102"]
    pub fn SetSlaveEsmTimeouts(master_index: u16, slave_index: u16, timeouts: *const EsmTimeouts) -> i32;

    // ===================== 寄存器/SM/FMMU 直接访问 =====================
    #[link_name = "D_1106"]
    pub fn WriteSlaveRegister(master_index: u16, slave_index: u16, reg_addr: u16, data: *const u8, len: u32) -> i32;
    #[link_name = "D_1107"]
    pub fn ReadSlaveRegister(master_index: u16, slave_index: u16, reg_addr: u16, data: *mut u8, len: u32) -> i32;
    #[link_name = "D_1263"]
    pub fn SIIReadWord(master_index: u16, slave_index: u16, word_addr: u16, out_value: *mut u16) -> i32;
    #[link_name = "D_1264"]
    pub fn SIIWriteWord(master_index: u16, slave_index: u16, word_addr: u16, value: u16) -> i32;
    #[link_name = "D_1111"]
    pub fn ConfigureSyncManager(master_index: u16, slave_index: u16,
                                sm_index: u8, start_addr: u16, length: u16,
                                control: u8, enable: i32) -> i32;
    #[link_name = "D_1112"]
    pub fn ConfigureFMMU(master_index: u16, slave_index: u16,
                         fmmu_index: u8, logical_addr: u32, length: u16,
                         logical_start_bit: u8, logical_end_bit: u8,
                         physical_addr: u16, physical_start_bit: u8,
                         fmmu_type: u8, enable: i32) -> i32;

    // ===================== 冗余模式 =====================
    #[link_name = "D_1538"]
    pub fn SetRedProcessdata(mode: c_int);
    #[link_name = "D_1539"]
    pub fn GetRedProcessdata() -> c_int;
    #[link_name = "D_1534"]
    pub fn EnableRedundancy(master_index: u16, enable: i32) -> i32;
    #[link_name = "D_1535"]
    pub fn GetRedundancyStatus(master_index: u16) -> *const c_void;
    #[link_name = "D_1536"]
    pub fn ForceRedundancyFailover(master_index: u16) -> i32;
    #[link_name = "D_1537"]
    pub fn CheckRedundancyHealth(master_index: u16) -> i32;

    // ===================== 日志控制 =====================
    #[link_name = "D_1026"]
    pub fn SetLogCallback(callback: LogCallback);
    #[link_name = "D_1004"]
    pub fn SetCrashCallback(callback: CrashNotifyCallback);
    #[link_name = "D_1028"]
    pub fn SetPDOLogging(enable: i32);
    #[link_name = "D_1029"]
    pub fn SetMailboxLogging(enable: i32);
    #[link_name = "D_1030"]
    pub fn SetDebugLogging(enable: i32);
    /// 关闭调试日志文件
    #[link_name = "D_1031"]
    pub fn CloseDebugLog();

    // ===================== 回调注册 =====================
    #[link_name = "D_1032"]
    pub fn RegisterProcessDataCyclicCallbackSync(callback: ProcessDataCyclicCallback);
    #[link_name = "D_1033"]
    pub fn RegisterProcessDataCyclicCallbackAsync(callback: ProcessDataCyclicCallback);
    #[link_name = "D_1034"]
    pub fn RegisterSlaveStateChangeCallbackSync(callback: SlaveStateChangeCallback);
    #[link_name = "D_1035"]
    pub fn RegisterSlaveStateChangeCallbackAsync(callback: SlaveStateChangeCallback);
    #[link_name = "D_1036"]
    pub fn RegisterEmergencyEventCallback(callback: EmergencyEventCallback);
    #[link_name = "D_1037"]
    pub fn RegisterSlaveDiscoveryCallbackSync(callback: SlaveDiscoveryCallback);
    #[link_name = "D_1038"]
    pub fn RegisterSlaveDiscoveryCallbackAsync(callback: SlaveDiscoveryCallback);
    #[link_name = "D_1043"]
    pub fn RegisterInputDataChangedCallback(callback: InputDataChangedCallback);
    #[link_name = "D_1040"]
    pub fn RegisterRedundancyModeChangedCallback(callback: RedundancyModeChangedCallback);
    #[link_name = "D_1039"]
    pub fn RegisterPDOFrameLossCallback(callback: PDOFrameLossCallback);
    #[link_name = "D_1041"]
    pub fn RegisterSlavePreOpReconfigCallback(callback: SlavePreOpReconfigCallback);
    /// v2 热插拔身份不符回调注册 (对应 C# RegisterSlaveIdentityMismatchCallback)
    #[link_name = "D_1042"]
    pub fn RegisterSlaveIdentityMismatchCallback(callback: SlaveIdentityMismatchCallback);
    /// 端口链路变化回调注册 (对应 C# RegisterSlavePortLinkChangedCallback)
    #[link_name = "D_1044"]
    pub fn RegisterSlavePortLinkChangedCallback(callback: SlavePortLinkChangedCallback);
    // AcknowledgeSlaveReplacement 通过运行时可选符号解析, 见文件末尾包装函数.
    /// 写入从站 DL Port 控制寄存器 (0x0101)
    /// dlport_value 取值: 0x00=Auto, 0x03=P0 关, 0x0C=P1 关, 0x30=P2 关, 0xC0=P3 关
    /// 返回非 0 (BOOL TRUE) = 写入成功
    #[link_name = "D_1108"]
    pub fn WriteSlaveDLPORT(master_index: u16, slave_index: u16, dlport_value: u8) -> c_int;
    /// 读取从站 DL Port 控制寄存器 (0x0101)
    /// 返回非 0 (BOOL TRUE) = 读取成功, 值写入 `*dlport_value`
    #[link_name = "D_1109"]
    pub fn ReadSlaveDLPORT(master_index: u16, slave_index: u16, dlport_value: *mut u8) -> c_int;
    #[link_name = "D_1282"]
    pub fn SetDCSyncLostCallback(callback: DCSyncLostCallback);

    // ===================== 诊断与统计 =====================
    #[link_name = "D_1313"]
    pub fn GetDetailedDiagnostics(master_index: u16) -> *const c_void;
    #[link_name = "D_1314"]
    pub fn ResetDiagnostics(master_index: u16);
    #[link_name = "D_1312"]
    pub fn GetDiagnosticsPointer(master_index: u16) -> *const c_void;
    #[link_name = "D_1316"]
    pub fn GetSummaryPointer(master_index: u16) -> *const c_void;
    #[link_name = "D_1290"]
    pub fn SetDiagnosticsEnabled(master_index: u16, enable: i32);
    #[link_name = "D_1291"]
    pub fn GetDiagnosticsEnabled(master_index: u16) -> i32;
    #[link_name = "D_1053"]
    pub fn GetCommunicationStats(master_index: u16) -> *const c_void;
    #[link_name = "D_1054"]
    pub fn ResetCommunicationStats(master_index: u16);

    // 诊断数据采集
    #[link_name = "D_1295"]
    pub fn GetExpectedWKC(master: u16) -> u16;
    #[link_name = "D_1311"]
    pub fn SetExpectedWKC(master: u16, expected_wkc: u16);

    /// [2026-04-27] 唯一丢包率/过慢率 (内核 5s 滑窗)
    #[link_name = "D_1303"]
    pub fn GetPacketLossRate(master: u16) -> f32;
    #[link_name = "D_1304"]
    pub fn GetLateFrameRate(master: u16) -> f32;
    #[link_name = "D_1315"]
    pub fn GetSlaveLinkQuality(master: u16, slave: u16) -> i16;
    #[link_name = "D_1628"]
    pub fn RecordCycleTime(master: u16, cycle_time_us: u32);
    #[link_name = "D_1293"]
    pub fn RecordPDOCycleStart(master: u16);
    #[link_name = "D_1294"]
    pub fn RecordWKC(master: u16, wkc: u16);
    #[link_name = "D_1292"]
    pub fn UpdateDiagnosticsSnapshot(master: u16);
    #[link_name = "D_1320"]
    pub fn ResetSlavePortErrorCounters(master: u16, slave: u16) -> i32;

    /// 获取主网口 WKC (冗余模式独立跟踪)
    #[link_name = "D_1296"]
    pub fn GetPrimaryWKC() -> u16;
    /// 获取副网口 WKC (冗余模式独立跟踪)
    #[link_name = "D_1297"]
    pub fn GetSecondaryWKC() -> u16;

    // ===================== WDK 实时驱动 =====================
    #[link_name = "D_1623"]
    pub fn IsWdkAvailable(master_index: u16) -> i32;
    #[link_name = "D_1624"]
    pub fn SetWdkMode(master_index: u16, enable: i32) -> i32;
    #[link_name = "D_1625"]
    pub fn GetWdkMode(master_index: u16) -> i32;
    #[link_name = "D_1626"]
    pub fn StartWdkRT(master_index: u16, cycle_us: u32, cpu_index: u32) -> i32;
    #[link_name = "D_1627"]
    pub fn StopWdkRT(master_index: u16) -> i32;

    // ===================== UDP 帧模式 =====================
    #[link_name = "D_1543"]
    pub fn SetUdpMode(master_index: u16, enable: i32) -> i32;
    #[link_name = "D_1544"]
    pub fn GetUdpMode(master_index: u16) -> i32;
    #[link_name = "D_1545"]
    pub fn IsUdpAvailable(master_index: u16) -> i32;

    // ===================== 中止控制 =====================
    #[link_name = "D_1072"]
    pub fn AbortScan();
    #[link_name = "D_1073"]
    pub fn ResetScanAbort();
    #[link_name = "D_1609"]
    pub fn AbortNetwork();
    #[link_name = "D_1610"]
    pub fn ResetAbortNetwork();
    #[link_name = "D_1612"]
    pub fn EmergencyCloseNics();

    // ===================== CiA 402 驱动器控制 =====================
    #[link_name = "D_1138"]
    pub fn CiA402_ParseState(statusword: u16) -> i32;
    #[link_name = "D_1139"]
    pub fn CiA402_GetEnableCommand(statusword: u16) -> u16;
    #[link_name = "D_1140"]
    pub fn CiA402_SetMode(master: u16, slave: u16, mode: i8) -> i32;
    #[link_name = "D_1141"]
    pub fn CiA402_GetMode(master: u16, slave: u16) -> i8;
    #[link_name = "D_1142"]
    pub fn CiA402_ReadStatusWord(master: u16, slave: u16) -> u16;
    #[link_name = "D_1143"]
    pub fn CiA402_WriteControlWord(master: u16, slave: u16, cw: u16) -> i32;
    #[link_name = "D_1144"]
    pub fn CiA402_Enable(master: u16, slave: u16, max_retries: i32) -> i32;
    #[link_name = "D_1145"]
    pub fn CiA402_FaultReset(master: u16, slave: u16) -> i32;

    // ===================== EMCY 紧急报文 =====================
    #[link_name = "D_1168"]
    pub fn EmcyGetHistory(master: u16, slave: u16, out: *mut EmcyRecord, max: i32) -> i32;
    #[link_name = "D_1169"]
    pub fn EmcyClearHistory(master: u16, slave: u16);
    #[link_name = "D_1170"]
    pub fn EmcyGetCount(master: u16, slave: u16) -> i32;

    // ===================== PDO 类型化读写 (新接口) =====================
    // 新接口直接使用 DLL 导出名称，写入函数返回 i32 状态码
    #[link_name = "D_1171"]
    pub fn PDOReadInputU8(master: u16, slave: u16, offset: u32) -> u8;
    #[link_name = "D_1172"]
    pub fn PDOReadInputI16(master: u16, slave: u16, offset: u32) -> i16;
    #[link_name = "D_1173"]
    pub fn PDOReadInputU16(master: u16, slave: u16, offset: u32) -> u16;
    #[link_name = "D_1174"]
    pub fn PDOReadInputI32(master: u16, slave: u16, offset: u32) -> i32;
    #[link_name = "D_1175"]
    pub fn PDOReadInputU32(master: u16, slave: u16, offset: u32) -> u32;
    #[link_name = "D_1176"]
    pub fn PDOReadInputF32(master: u16, slave: u16, offset: u32) -> f32;
    #[link_name = "D_1177"]
    pub fn PDOWriteOutputU8(master: u16, slave: u16, offset: u32, val: u8) -> i32;
    #[link_name = "D_1178"]
    pub fn PDOWriteOutputI16(master: u16, slave: u16, offset: u32, val: i16) -> i32;
    #[link_name = "D_1179"]
    pub fn PDOWriteOutputU16(master: u16, slave: u16, offset: u32, val: u16) -> i32;
    #[link_name = "D_1180"]
    pub fn PDOWriteOutputI32(master: u16, slave: u16, offset: u32, val: i32) -> i32;
    #[link_name = "D_1181"]
    pub fn PDOWriteOutputU32(master: u16, slave: u16, offset: u32, val: u32) -> i32;
    #[link_name = "D_1182"]
    pub fn PDOWriteOutputF32(master: u16, slave: u16, offset: u32, val: f32) -> i32;

    // ===================== 拓扑查询 =====================
    #[link_name = "D_1325"]
    pub fn GetTopology(master: u16, out: *mut TopologyNode, max: c_int) -> c_int;
    #[link_name = "D_1326"]
    pub fn GetSlaveActivePorts(master: u16, slave: u16) -> u8;
    #[link_name = "D_1327"]
    pub fn GetSlaveParent(master: u16, slave: u16) -> u16;
    #[link_name = "D_1183"]
    pub fn TopologyBuild(master: u16, out: *mut TopologyNode, max: c_int) -> c_int;
    #[link_name = "D_1184"]
    pub fn TopologyGetChildren(master: u16, parent_index: u16, out: *mut u16, max: c_int) -> c_int;
    #[link_name = "D_1185"]
    pub fn TopologyGetRoots(master: u16, out: *mut u16, max: c_int) -> c_int;

    // ===================== 授权与设备信息 =====================

    /// 获取设备序列号
    #[link_name = "D_1048"]
    pub fn GetSerialNumber() -> *const c_char;
    /// 获取设备名称
    #[link_name = "D_1049"]
    pub fn GetDeviceName() -> *const c_char;
    /// 获取用户邮箱
    #[link_name = "D_1050"]
    pub fn GetUserEmail() -> *const c_char;
    /// 获取 Windows 产品密钥
    #[link_name = "D_1051"]
    pub fn GetWindowsProductKey() -> *const c_char;
    /// 获取驱动列表 (JSON 格式)
    #[link_name = "D_1052"]
    pub fn GetDriverList() -> *const c_char;

    /// 验证授权 (实际导出名: CheckBufferIntegrity → D_1586)
    #[link_name = "D_1586"]
    pub fn VerifyLicense() -> c_int;

    /// 使授权失效 (实际导出名: FlushCachePool → D_1590)
    #[link_name = "D_1590"]
    pub fn InvalidateLicense();

    /// 检查授权是否有效 (实际导出名: IsCachePoolReady → D_1589)
    #[link_name = "D_1589"]
    pub fn IsLicenseValid() -> c_int;

    /// 激活授权 — 内核 WSK 走驱动多 NIC 群发 (3s 超时), SDK 不走 HTTP
    /// 返回: 0=成功, 1=格式错, 2=设备编码失败, 3=驱动未加载, 4=网络/超时,
    ///       5=响应格式错, 6=HTTP 非 200, 7=服务器拒绝, 8=数据无效,
    ///       9=写入失败, 10=自验失败, -1=异常
    /// (实际导出名: LicenseActivate → D_1599)
    #[link_name = "D_1599"]
    pub fn LicenseActivate(activation_code: *const c_char, err_buf: *mut c_char, err_buf_len: c_int) -> c_int;

    // ===================== 主站信息查询 =====================

    /// 获取最大主站实例数
    #[link_name = "D_1594"]
    pub fn GetMaxMasterInstances() -> c_int;
    /// 输出从站结构体偏移 (调试用)
    #[link_name = "D_1324"]
    pub fn DumpSlaveStructOffsets();

    // ===================== 状态缓存配置 =====================
    // SetStateCacheUpdateInterval / GetStateCacheUpdateInterval 通过运行时可选符号解析,
    // 见文件末尾包装函数.

    // ===================== 冗余适配器自动发现 =====================

    /// 批量查找冗余适配器对
    #[link_name = "D_1075"]
    pub fn QuickFindRedundantPairBatch(
        adapter_names: *const *const c_char,
        adapter_count: c_int,
        primary_idx: *mut c_int,
        secondary_idx: *mut c_int,
        out_ring_count: *mut c_int,
        out_total_count: *mut c_int,
    ) -> c_int;

    // ===================== 从站热插拔重配置 =====================

    /// 查询从站是否需要启动参数重配置
    #[link_name = "D_1002"]
    pub fn GetSlaveNeedsStartupReconfig(master_index: u16, slave_index: u16) -> i32;
    /// 清除从站启动参数重配置标志
    #[link_name = "D_1003"]
    pub fn ClearSlaveNeedsStartupReconfig(master_index: u16, slave_index: u16);

    // ===================== PDO 丢帧统计 =====================

    /// 获取 PDO 丢帧统计
    #[link_name = "D_1045"]
    pub fn GetPDOFrameLossStats(
        master_index: u16, group: u8,
        total_lost: *mut u32, consecutive_lost: *mut u32, max_consecutive_lost: *mut u32,
    );
    /// 重置 PDO 丢帧统计
    #[link_name = "D_1046"]
    pub fn ResetPDOFrameLossStats(master_index: u16, group: u8);

    // ===================== 定时器验证 =====================

    /// 验证定时器精度
    #[link_name = "D_1025"]
    pub fn DarraValidateTimerAccuracy(expected_usec: u32, tolerance_usec: u32) -> c_int;

    // ===================== SDO 批量操作 =====================

    /// 批量读取多个从站的 SDO 对象字典
    #[link_name = "D_1565"]
    pub fn GetMultiSlaveSDOList(
        master_index: u16,
        slave_indices: *const u16,
        count: c_int,
        results: *mut *const c_void,
    ) -> c_int;
    /// 释放批量 SDO 结果
    #[link_name = "D_1566"]
    pub fn FreeMultiSlaveSDOList(results: *mut *const c_void, count: c_int);

    /// 获取从站 SDO 条目指针
    #[link_name = "D_1567"]
    pub fn GetSlavePointer_SDO(master_index: u16, slave_index: u16, oe_index: u16) -> *const c_void;
    /// 获取从站 SDO 条目指针 (带已有 ODList)
    #[link_name = "D_1568"]
    pub fn GetSlavePointer_SDO_WithODList(
        master_index: u16, slave_index: u16, oe_index: u16, odlist: *const c_void,
    ) -> *const c_void;

    // ===================== PDO 直接读写 / 监控 =====================
    // PDOReadDirect/PDOWriteDirect/PDOBatchRead/PDOBatchWrite/GetPDOStats/ResetPDOStats/
    // EnablePDOMonitoring/IsPDOMonitoringEnabled/GetPDOAvgCycleTimeNs/GetPDOErrorCount/
    // IsPDOValid/IsPDOChanged/GetPDOExpectedSize/GetPDOMapping 通过运行时可选符号解析,
    // 见文件末尾包装函数.

    // ===================== OpOnly / DeviceEmulation 标志 =====================

    /// 启用从站输出 SyncManager
    #[link_name = "D_1096"]
    pub fn EnableOutputSyncManager(master_index: u16, slave_index: u16) -> i32;
    /// 禁用从站输出 SyncManager
    #[link_name = "D_1097"]
    pub fn DisableOutputSyncManager(master_index: u16, slave_index: u16) -> i32;
    /// 检查从站 OpOnly 标志
    #[link_name = "D_1098"]
    pub fn GetSlaveOpOnlyFlag(master_index: u16, slave_index: u16) -> i32;
    /// 设置从站错误确认位
    #[link_name = "D_1099"]
    pub fn SetSlaveErrorAck(master_index: u16, slave_index: u16, set_ack: i32) -> i32;
    /// 检查从站 Device Emulation 标志
    #[link_name = "D_1100"]
    pub fn GetSlaveDeviceEmulationFlag(master_index: u16, slave_index: u16) -> i32;

    // ===================== 批量身份验证 =====================

    /// 批量验证所有从站身份
    #[link_name = "D_1105"]
    pub fn VerifyAllSlaveIdentities(
        master_index: u16,
        expected: *const SlaveIdentity,
        slave_count: u32,
        check_revision: i32,
        check_serial: i32,
        mismatch_mask: *mut u64,
    ) -> i32;

    // ===================== AoE 通知监听 =====================

    /// 启动 AoE 通知监听线程
    #[link_name = "D_1528"]
    pub fn AOEStartNotificationListener(master_index: u16) -> i32;
    /// 停止 AoE 通知监听线程
    #[link_name = "D_1529"]
    pub fn AOEStopNotificationListener() -> i32;
    /// 检查 AoE 通知监听是否运行
    #[link_name = "D_1530"]
    pub fn AOEIsNotificationListening() -> i32;
    /// 注册 AoE 通知回调
    #[link_name = "D_1531"]
    pub fn AOERegisterNotification(
        slave: u16, handle: u32,
        index_group: u32, index_offset: u32, data_length: u32,
        callback: AOENotificationCallback,
        user_data: *mut c_void,
    ) -> c_int;
    /// 注销 AoE 通知回调
    #[link_name = "D_1532"]
    pub fn AOEUnregisterNotification(subscription_index: c_int) -> i32;

    // ===================== EoE 扩展功能 =====================

    /// 设置 DNS
    #[link_name = "D_1489"]
    pub fn EOESetDNS(master_index: u16, slave: u16, port: u8, dns_ip: u32, dns_name: *const c_char, timeout: c_int) -> i32;
    /// 获取 DNS
    #[link_name = "D_1492"]
    pub fn EOEGetDNS(master_index: u16, slave: u16, port: u8, dns_ip: *mut u32, dns_name: *mut c_char, timeout: c_int) -> i32;
    /// 获取全部 EoE 参数
    #[link_name = "D_1490"]
    pub fn EOEGetFullParam(
        master_index: u16, slave: u16, port: u8,
        ip: *mut u32, subnet: *mut u32, gateway: *mut u32,
        mac: *mut u8, dns_ip: *mut u32, dns_name: *mut c_char, timeout: c_int,
    ) -> i32;
    /// 设置全部 EoE 参数
    #[link_name = "D_1491"]
    pub fn EOESetFullParam(
        master_index: u16, slave: u16, port: u8,
        ip: u32, subnet: u32, gateway: u32,
        mac: *const u8, dns_ip: u32, dns_name: *const c_char, timeout: c_int,
    ) -> i32;
    /// 设置 EoE 地址过滤
    #[link_name = "D_1493"]
    pub fn EOESetAddressFilter(
        master_index: u16, slave: u16, port: u8,
        filter_count: u8, mac_filters: *const u8, timeout: c_int,
    ) -> i32;
    /// 获取 EoE 地址过滤
    #[link_name = "D_1494"]
    pub fn EOEGetAddressFilter(
        master_index: u16, slave: u16, port: u8,
        filter_count: *mut u8, mac_filters: *mut u8, max_filters: c_int, timeout: c_int,
    ) -> i32;

    // ===================== ETG.1510 主站诊断 =====================

    /// 获取主站身份信息
    #[link_name = "D_1123"]
    pub fn GetMasterIdentity(master_index: u16, identity: *mut MasterIdentity) -> i32;
    /// 获取主站诊断数据
    #[link_name = "D_1124"]
    pub fn GetMasterDiagData(master_index: u16, diag: *mut MasterDiagData) -> i32;

    // ===================== 故障点检测 =====================

    /// 获取故障点列表
    #[link_name = "D_1322"]
    pub fn GetBreakPoints(
        master_index: u16,
        out_slaves: *mut u16, out_ports: *mut u8, out_types: *mut u8,
        max_results: u16,
    ) -> c_int;
    /// 获取环拓扑冗余模式 (0=Inactive, 1=Dual, 2=Degraded)
    #[link_name = "D_1542"]
    pub fn GetRingMode(master_index: u16) -> c_int;
    /// 获取冗余链路状态
    #[link_name = "D_1533"]
    pub fn GetSecondaryLinkStatus(master_index: u16) -> i32;

    // ===================== 配置验证与导出 =====================

    /// 验证主站配置
    #[link_name = "D_1370"]
    pub fn ec_validate_config(master_index: u16) -> c_int;
    /// 导出性能统计到 CSV
    #[link_name = "D_1371"]
    pub fn ec_perf_export_csv(master_index: u16, filepath: *const c_char) -> c_int;

    // ===================== DC 调试 =====================

    /// 调试从站 DC 信息
    #[link_name = "D_1091"]
    pub fn DebugSlaveHasDC(master_index: u16, slave_index: u16);

    // ===================== ESC 端口错误诊断 (ETG.1000.4) =====================

    /// 读取从站端口错误计数器
    #[link_name = "D_1317"]
    pub fn ReadSlavePortErrorCounters(
        master_index: u16, slave_index: u16,
        rx_error: *mut u8, invalid_frame: *mut u8, lost_link: *mut u8,
    ) -> i32;
    /// 读取所有从站端口错误计数器
    #[link_name = "D_1318"]
    pub fn ReadAllSlavePortErrorCounters(master_index: u16) -> c_int;
    /// 获取从站端口错误统计 (返回内部指针，无需释放)
    #[link_name = "D_1319"]
    pub fn GetSlavePortErrorStats(master_index: u16, slave_index: u16) -> *const c_void;
    /// 用 ESC 端口错误更新诊断数据
    #[link_name = "D_1321"]
    pub fn UpdateDiagnosticsWithESCErrors(master_index: u16);

    // ===================== FSoE 功能安全 (ETG.5100/5120) =====================

    /// 初始化从站的 FSoE 连接
    #[link_name = "D_1335"]
    pub fn FSoEInitConnection(master_index: u16, slave_index: u16, config: *mut FsoeConfig) -> i32;
    /// 关闭 FSoE 连接
    #[link_name = "D_1336"]
    pub fn FSoECloseConnection(master_index: u16, slave_index: u16) -> i32;
    /// 获取 FSoE 连接状态
    #[link_name = "D_1337"]
    pub fn FSoEGetStatus(master_index: u16, slave_index: u16, status: *mut FsoeStatus) -> i32;
    /// 请求 FSoE 状态转换
    #[link_name = "D_1338"]
    pub fn FSoERequestState(master_index: u16, slave_index: u16, target_state: c_int) -> i32;
    /// 获取当前 FSoE 状态
    #[link_name = "D_1339"]
    pub fn FSoEGetState(master_index: u16, slave_index: u16) -> c_int;
    /// 重置 FSoE 连接到初始状态
    #[link_name = "D_1340"]
    pub fn FSoEReset(master_index: u16, slave_index: u16) -> i32;
    /// 写入安全输出数据
    #[link_name = "D_1341"]
    pub fn FSoEWriteSafeOutput(master_index: u16, slave_index: u16, data: *const u8, size: u32) -> i32;
    /// 读取安全输入数据
    #[link_name = "D_1342"]
    pub fn FSoEReadSafeInput(master_index: u16, slave_index: u16, data: *mut u8, size: *mut u32) -> i32;
    /// 下载安全参数 (在 PARAMETER 状态)
    #[link_name = "D_1343"]
    pub fn FSoEDownloadParameters(master_index: u16, slave_index: u16, param_data: *const u8, param_size: u32, sra_crc: *mut u32) -> i32;
    /// 设置失效安全输出值
    #[link_name = "D_1344"]
    pub fn FSoESetFailsafeOutput(master_index: u16, slave_index: u16, data: *const u8, size: u32) -> i32;
    /// 检查看门狗状态
    #[link_name = "D_1345"]
    pub fn FSoECheckWatchdog(master_index: u16, slave_index: u16) -> i32;
    /// 获取最后的 FSoE 错误
    #[link_name = "D_1346"]
    pub fn FSoEGetLastError(master_index: u16, slave_index: u16) -> c_int;
    /// 清除 FSoE 错误
    #[link_name = "D_1347"]
    pub fn FSoEClearError(master_index: u16, slave_index: u16);
    /// 检查从站是否支持 FSoE
    #[link_name = "D_1348"]
    pub fn FSoEIsSlaveCapable(master_index: u16, slave_index: u16) -> i32;
    /// 获取主站的 FSoE 连接数
    #[link_name = "D_1349"]
    pub fn FSoEGetConnectionCount(master_index: u16) -> u16;
    /// 处理 FSoE 帧 (由 PDO 循环调用)
    #[link_name = "D_1350"]
    pub fn FSoEProcessCycle(master_index: u16);

    // ===================== FSoE MDP 多连接模式 =====================

    /// 初始化 MDP 从站的指定连接
    #[link_name = "D_1351"]
    pub fn SafeMdpInitConnection(master_index: u16, slave_index: u16, connection_index: u16, config: *mut SafeMdpConfig) -> i32;
    /// 关闭 MDP 从站的指定连接
    #[link_name = "D_1352"]
    pub fn SafeMdpCloseConnection(master_index: u16, slave_index: u16, connection_index: u16) -> i32;
    /// 获取指定连接的状态
    #[link_name = "D_1353"]
    pub fn SafeMdpGetStatus(master_index: u16, slave_index: u16, connection_index: u16, status: *mut FsoeStatus) -> i32;
    /// 请求指定连接的状态转换
    #[link_name = "D_1354"]
    pub fn SafeMdpRequestState(master_index: u16, slave_index: u16, connection_index: u16, target_state: c_int) -> i32;
    /// 获取指定连接的当前状态
    #[link_name = "D_1355"]
    pub fn SafeMdpGetState(master_index: u16, slave_index: u16, connection_index: u16) -> c_int;
    /// 重置指定连接
    #[link_name = "D_1356"]
    pub fn SafeMdpReset(master_index: u16, slave_index: u16, connection_index: u16) -> i32;
    /// 写入指定连接的安全输出数据
    #[link_name = "D_1357"]
    pub fn SafeMdpWriteSafeOutput(master_index: u16, slave_index: u16, connection_index: u16, data: *const u8, size: u32) -> i32;
    /// 读取指定连接的安全输入数据
    #[link_name = "D_1358"]
    pub fn SafeMdpReadSafeInput(master_index: u16, slave_index: u16, connection_index: u16, data: *mut u8, size: *mut u32) -> i32;
    /// 下载指定连接的安全参数
    #[link_name = "D_1359"]
    pub fn SafeMdpDownloadParameters(master_index: u16, slave_index: u16, connection_index: u16, param_data: *const u8, param_size: u32, sra_crc: *mut u32) -> i32;
    /// 设置指定连接的失效安全输出值
    #[link_name = "D_1360"]
    pub fn SafeMdpSetFailsafeOutput(master_index: u16, slave_index: u16, connection_index: u16, data: *const u8, size: u32) -> i32;
    /// 检查指定连接的看门狗状态
    #[link_name = "D_1361"]
    pub fn SafeMdpCheckWatchdog(master_index: u16, slave_index: u16, connection_index: u16) -> i32;
    /// 获取指定连接的最后错误
    #[link_name = "D_1362"]
    pub fn SafeMdpGetLastError(master_index: u16, slave_index: u16, connection_index: u16) -> c_int;
    /// 清除指定连接的错误
    #[link_name = "D_1363"]
    pub fn SafeMdpClearError(master_index: u16, slave_index: u16, connection_index: u16);
    /// 获取从站的 FSoE 连接数量
    #[link_name = "D_1364"]
    pub fn SafeMdpGetSlaveConnectionCount(master_index: u16, slave_index: u16) -> u16;
    /// 检测从站是否为 MDP 设备并返回支持的 FSoE 连接数
    #[link_name = "D_1365"]
    pub fn SafeMdpDetectConnections(master_index: u16, slave_index: u16) -> u16;
    /// 读取从站的设备级 FSoE 安全地址 (0xF980:01)
    #[link_name = "D_1366"]
    pub fn SafeMdpGetDeviceAddress(master_index: u16, slave_index: u16, safety_address: *mut u16) -> i32;
    /// 读取指定模块的 FSoE 通信参数 (0x9nn1)
    #[link_name = "D_1367"]
    pub fn SafeMdpGetModuleCommParam(master_index: u16, slave_index: u16, module_number: u16, param_data: *mut u8, param_size: *mut u32) -> i32;
    /// 读取指定模块的 FSoE 诊断数据 (0xAnn0)
    #[link_name = "D_1368"]
    pub fn SafeMdpGetModuleDiagnosis(master_index: u16, slave_index: u16, module_number: u16, connection_state: *mut u16, connection_diagnosis: *mut u16) -> i32;

    // ===================== FSoE ConnID 校验 (ETG.5120 §5.2.3) =====================

    /// 校验 ConnID 是否可用 (未被活动连接占用且非 0).
    /// 返回 true (非零字节) = 可用, false = 已占用或 0.
    ///
    /// 对齐 C# `FSoEValidateConnId` P/Invoke (MarshalAs(UnmanagedType.I1) bool).
    #[link_name = "D_1369"]
    pub fn FSoEValidateConnId(conn_id: u16) -> u8;

    // ===================== CoE Diagnosis History 0x10F3 (ETG.1020 §16) =====================
    //
    // 4 个原语对应 DLL `coe_diag_*` 导出, 每个完成 1 次原子 SDO 操作,
    // 比走通用 SDORead 路径轻量 (避免完整邮箱 SDO 握手).

    /// 轮询 0x10F3:04 "NewAvailable" 标志.
    ///
    /// 返回: 1=有新消息, 0=无, -1=通信失败.
    /// `out_abort_code` 输出 SDO Abort Code (0=无错误).
    #[link_name = "D_1331"]
    pub fn coe_diag_poll_new_available(
        master_index: u16,
        slave_index: u16,
        out_abort_code: *mut u32,
    ) -> c_int;

    /// 读 0x10F3 元数据 (MaxMessages / NewestMessage / NewestAcked / Flags).
    ///
    /// 返回: 1=OK, 0=失败.
    /// - `out_max_msgs`: 0x10F3:01 从站支持的最大消息数
    /// - `out_newest`: 0x10F3:02 最新消息 subindex
    /// - `out_acknowledged`: 0x10F3:03 用户已确认的最新 subindex
    /// - `out_flags16`: 0x10F3:05 Flags (bit4=Ring/Linear 模式, bit5=Overrun)
    #[link_name = "D_1332"]
    pub fn coe_diag_read_meta(
        master_index: u16,
        slave_index: u16,
        out_max_msgs: *mut u8,
        out_newest: *mut u8,
        out_acknowledged: *mut u8,
        out_flags16: *mut u16,
        out_abort_code: *mut u32,
    ) -> c_int;

    /// 读 0x10F3:msg_subidx (6..255) 指定 Octet 诊断消息到 `out_buf`.
    ///
    /// 返回: 1=OK, 0=失败.
    /// - `msg_subidx`: 消息子索引 (6..255)
    /// - `out_buf` / `buf_cap`: 接收缓冲区
    /// - `out_len`: 实际写入字节数
    #[link_name = "D_1333"]
    pub fn coe_diag_read_message(
        master_index: u16,
        slave_index: u16,
        msg_subidx: u8,
        out_buf: *mut u8,
        buf_cap: c_int,
        out_len: *mut c_int,
        out_abort_code: *mut u32,
    ) -> c_int;

    /// 确认已处理的消息: 写 0x10F3:03 = ack_subidx.
    ///
    /// 返回: 1=OK, 0=失败.
    #[link_name = "D_1334"]
    pub fn coe_diag_acknowledge(
        master_index: u16,
        slave_index: u16,
        ack_subidx: u8,
        out_abort_code: *mut u32,
    ) -> c_int;

    // ===================== DC 时间查询 =====================

    /// 返回最近 FRMW 取回的 64-bit DC 系统时间 (ns, 2000-01-01 纪元).
    /// 0=DC 未激活.
    #[link_name = "D_1287"]
    pub fn GetMasterDCTime(master_index: u16) -> i64;

    /// 返回参考时钟从站索引 (1-based). 0=无 DC 从站.
    #[link_name = "D_1288"]
    pub fn GetReferenceClockSlaveIndex(master_index: u16) -> u16;

    // ===================== FoE BUSY Hook / Cancel (ETG.1000.6) =====================

    /// 设置 FoE BUSY 回调 (固件 Flash 烧写进度). `callback` 为 `None` 时清除.
    #[link_name = "D_1482"]
    pub fn FOESetBusyHook(master_index: u16, callback: Option<FoEBusyCallback>) -> u8;

    /// 请求取消 FoE 传输 (透传 ec_foe.c 取消标志, 下一次循环迭代起生效).
    #[link_name = "D_1483"]
    pub fn FOERequestCancel(master_index: u16, slave: u16) -> u8;

    /// 清除 FoE 取消标志 (Read/Write 入口自动清零, 仅异常复位用).
    #[link_name = "D_1484"]
    pub fn FOEClearCancel(master_index: u16, slave: u16) -> u8;

    // ===================== 从站 ESI/冗余/SM/FMMU 扩展 =====================

    /// ESI 文件是否已加载
    #[link_name = "D_1240"]
    pub fn GetSlaveHasEsi(master_index: u16, slave_index: u16) -> i32;
    /// 设置从站 ESI 文件
    #[link_name = "D_1242"]
    pub fn SetSlaveEsiFile(master_index: u16, slave_index: u16, name: *const c_char) -> i32;
    /// ESI 文件版本 (拷贝到用户缓冲区)
    #[link_name = "D_1243"]
    pub fn GetSlaveEsiVersion(master_index: u16, slave_index: u16, buf: *mut c_char, buf_len: c_int) -> i32;
    /// 是否支持 MDP (Modular Device Profile)
    #[link_name = "D_1241"]
    pub fn GetSlaveHasMDP(master_index: u16, slave_index: u16) -> i32;
    /// 厂商名称 (拷贝到用户缓冲区)
    #[link_name = "D_1244"]
    pub fn GetSlaveVendorName(master_index: u16, slave_index: u16, buf: *mut c_char, buf_len: c_int) -> i32;
    /// 冗余是否已激活
    #[link_name = "D_1245"]
    pub fn GetSlaveRedundancyActivated(master_index: u16, slave_index: u16) -> i32;
    /// 主线路是否断路
    #[link_name = "D_1246"]
    pub fn GetSlavePrimaryLinkBroken(master_index: u16, slave_index: u16) -> i32;
    /// 冗余线路是否断路
    #[link_name = "D_1247"]
    pub fn GetSlaveSecondaryLinkBroken(master_index: u16, slave_index: u16) -> i32;

    /// 读取从站 SII mbx_proto 邮箱协议位域 (ETG.1000.6).
    /// bit 0=AoE, bit 1=EoE, bit 2=CoE, bit 3=FoE, bit 4=SoE, bit 5=VoE.
    /// 各协议 Instance::is_supported() 据此判断从站能力.
    #[link_name = "D_1200"]
    pub fn GetSlaveMailboxProto(master_index: u16, slave_index: u16) -> u16;

    // ===================== 邮箱统计 (对齐 C# IMailboxProtocol.Statistics) =====================

    /// 读取指定从站指定协议的邮箱统计快照.
    /// 返回 1=成功, 0=失败 (结构体保留为 0).
    #[link_name = "D_1248"]
    pub fn mbx_get_stats_by_master(
        master_index: u16,
        slave_index: u16,
        protocol_type: u8,
        stats: *mut EcMbxStatsC,
    ) -> i32;

    /// 清零指定从站指定协议的邮箱统计计数器.
    #[link_name = "D_1249"]
    pub fn mbx_reset_stats_by_master(
        master_index: u16,
        slave_index: u16,
        protocol_type: u8,
    );

    // ===================================================================
    // 协议解码 / 错误码表 (DLL 端 protocol/ec_*.{c,h}, VMP 保护)
    // 字符串返回均为 const char* (C 端 UTF-8), 上层用 CStr::from_ptr + to_string_lossy
    // ===================================================================

    // --- AL_STATUS_CODE (ec_status_codes.h) ---
    #[link_name = "D_1472"]
    pub fn AL_StatusCode_GetDescription(code: u16) -> *const c_char;
    #[link_name = "D_1473"]
    pub fn AL_StatusCode_GetSeverity(code: u16) -> i32;
    #[link_name = "D_1474"]
    pub fn AL_StatusCode_GetRecoveryHint(code: u16) -> *const c_char;
    #[link_name = "D_1475"]
    pub fn AL_StatusCode_IsVendorSpecific(code: u16) -> i32;

    // --- SDO Abort Code (ec_sdo_abort.h) ---
    #[link_name = "D_1441"]
    pub fn SDOAbort_GetDescription(abort_code: u32) -> *const c_char;
    #[link_name = "D_1442"]
    pub fn SDOAbort_GetCategory(abort_code: u32) -> i32;
    #[link_name = "D_1443"]
    pub fn SDOAbort_IsRetryable(abort_code: u32) -> i32;
    #[link_name = "D_1444"]
    pub fn SDOAbort_GetHint(abort_code: u32) -> *const c_char;

    // --- EMCY Emergency Code (ec_emcy_codes.h) ---
    #[link_name = "D_1416"]
    pub fn EmcyCode_GetDescription(error_code: u16) -> *const c_char;
    #[link_name = "D_1417"]
    pub fn EmcyCode_GetClass(error_code: u16) -> i32;
    #[link_name = "D_1418"]
    pub fn EmcyCode_GetClassName(class_code: i32) -> *const c_char;
    #[link_name = "D_1419"]
    pub fn EmcyCode_FormatErrorRegister(error_register: u8, buf: *mut u8, buf_size: c_int);
    #[link_name = "D_1420"]
    pub fn EmcyCode_IsRecovery(error_code: u16) -> i32;

    // --- ESM 状态合法性 (ec_state_validator.h) ---
    #[link_name = "D_1464"]
    pub fn EcState_IsValidTransition(from: u16, to: u16) -> i32;
    #[link_name = "D_1465"]
    pub fn EcState_GetTransitionType(from: u16, to: u16) -> i32;
    #[link_name = "D_1466"]
    pub fn EcState_GetTransitionPath(from: u16, to: u16, path: *mut u16, path_capacity: c_int) -> i32;
    #[link_name = "D_1467"]
    pub fn EcState_IsBootstrapRequired(state: u16) -> i32;
    #[link_name = "D_1468"]
    pub fn EcState_GetName(state: u16) -> *const c_char;
    #[link_name = "D_1469"]
    pub fn EcState_GetNameEn(state: u16) -> *const c_char;
    #[link_name = "D_1470"]
    pub fn EcState_HasErrorAck(state: u16) -> i32;
    #[link_name = "D_1471"]
    pub fn EcState_StripErrorAck(state: u16) -> u16;

    // --- PDO Codec / 端口拓扑 (ec_pdo_codec.h) ---
    #[link_name = "D_1426"]
    pub fn EcPdoCodec_DataTypeBitSize(dt: c_int) -> c_int;
    #[link_name = "D_1427"]
    pub fn EcPdoCodec_DataTypeName(dt: c_int) -> *const c_char;
    #[link_name = "D_1428"]
    pub fn EcPdoCodec_ExtractU64(
        src: *const u8,
        src_size: c_int,
        bit_offset: c_int,
        bit_length: c_int,
        out_value: *mut u64,
    ) -> c_int;
    #[link_name = "D_1429"]
    pub fn EcPdoCodec_ExtractI64(
        src: *const u8,
        src_size: c_int,
        bit_offset: c_int,
        bit_length: c_int,
        out_value: *mut i64,
    ) -> c_int;
    #[link_name = "D_1430"]
    pub fn EcPdoCodec_InsertU64(
        dst: *mut u8,
        dst_size: c_int,
        bit_offset: c_int,
        bit_length: c_int,
        value: u64,
    ) -> c_int;
    #[link_name = "D_1431"]
    pub fn EcPdoCodec_InsertI64(
        dst: *mut u8,
        dst_size: c_int,
        bit_offset: c_int,
        bit_length: c_int,
        value: i64,
    ) -> c_int;
    #[link_name = "D_1432"]
    pub fn EcPdoCodec_ExtractReal32(
        src: *const u8,
        src_size: c_int,
        bit_offset: c_int,
        out_value: *mut f32,
    ) -> c_int;
    #[link_name = "D_1433"]
    pub fn EcPdoCodec_ExtractReal64(
        src: *const u8,
        src_size: c_int,
        bit_offset: c_int,
        out_value: *mut f64,
    ) -> c_int;
    #[link_name = "D_1434"]
    pub fn EcPdoCodec_InsertReal32(
        dst: *mut u8,
        dst_size: c_int,
        bit_offset: c_int,
        value: f32,
    ) -> c_int;
    #[link_name = "D_1435"]
    pub fn EcPdoCodec_InsertReal64(
        dst: *mut u8,
        dst_size: c_int,
        bit_offset: c_int,
        value: f64,
    ) -> c_int;
    #[link_name = "D_1436"]
    pub fn EcPdoCodec_CountActivePorts(active_ports: u8) -> c_int;
    #[link_name = "D_1437"]
    pub fn EcPdoCodec_GetTopology(active_ports: u8) -> c_int;
    #[link_name = "D_1438"]
    pub fn EcPdoCodec_GetTopologyName(topo: c_int) -> *const c_char;
    #[link_name = "D_1439"]
    pub fn EcPdoCodec_GetTopologyNameEn(topo: c_int) -> *const c_char;
    #[link_name = "D_1440"]
    pub fn EcPdoCodec_IsPortActive(active_ports: u8, port: c_int) -> c_int;

    // --- CiA402 模式 / 回零方法 (ec_cia402_modes.h) ---
    #[link_name = "D_1390"]
    pub fn CiA402Modes_ModeToSupportedBit(mode: i8) -> c_int;
    #[link_name = "D_1391"]
    pub fn CiA402Modes_IsModeSupportedInMask(mask: u32, mode: i8) -> c_int;
    #[link_name = "D_1392"]
    pub fn CiA402Modes_ExpandSupportedMask(mask: u32, modes: *mut i8, capacity: c_int) -> c_int;
    #[link_name = "D_1393"]
    pub fn CiA402Modes_GetModeName(mode: i8) -> *const c_char;
    #[link_name = "D_1394"]
    pub fn CiA402Modes_GetModeNameEn(mode: i8) -> *const c_char;
    #[link_name = "D_1395"]
    pub fn CiA402Modes_GetModeDescription(mode: i8) -> *const c_char;
    #[link_name = "D_1396"]
    pub fn CiA402Modes_IsCyclicSyncMode(mode: i8) -> c_int;
    #[link_name = "D_1397"]
    pub fn CiA402Modes_RequiresDC(mode: i8) -> c_int;
    #[link_name = "D_1398"]
    pub fn CiA402Modes_IsStandardHomingMethod(method: i8) -> c_int;
    #[link_name = "D_1399"]
    pub fn CiA402Modes_GetHomingMethodName(method: i8) -> *const c_char;
    #[link_name = "D_1400"]
    pub fn CiA402Modes_GetHomingTrigger(method: i8) -> c_int;
    #[link_name = "D_1401"]
    pub fn CiA402Modes_GetHomingDirection(method: i8) -> c_int;
    #[link_name = "D_1402"]
    pub fn CiA402Modes_ListStandardHomingMethods(array: *mut i8, capacity: c_int) -> c_int;

    // --- Mailbox 协议码 (ec_mailbox_codes.h) ---
    #[link_name = "D_1421"]
    pub fn EcMailbox_GetTypeName(mbx_type: u16) -> *const c_char;
    #[link_name = "D_1422"]
    pub fn EcMailbox_GetTypeNameEn(mbx_type: u16) -> *const c_char;
    #[link_name = "D_1423"]
    pub fn EcMailbox_GetErrorDescription(mbx_error_code: u16) -> *const c_char;
    #[link_name = "D_1424"]
    pub fn EcMailbox_NextCounter(current: u8) -> u8;

    // --- SoE 错误描述 (ec_mailbox_codes.h) ---
    #[link_name = "D_1425"]
    pub fn EcSoE_GetErrorDescription(soe_error: u16) -> *const c_char;

    // --- SII 解析 (ec_sii_parser.h) ---
    #[link_name = "D_1454"]
    pub fn EcSii_FindCategory(
        sii_data: *const u8,
        sii_size: c_int,
        cat_type: u16,
        out_size_bytes: *mut c_int,
    ) -> c_int;
    #[link_name = "D_1455"]
    pub fn EcSii_EnumerateCategories(
        sii_data: *const u8,
        sii_size: c_int,
        cat_types: *mut u16,
        capacity: c_int,
    ) -> c_int;
    #[link_name = "D_1458"]
    pub fn EcSii_GetStringByIndex(
        cat_data: *const u8,
        cat_size: c_int,
        idx: c_int,
        out_buf: *mut u8,
        buf_size: c_int,
    ) -> c_int;
    #[link_name = "D_1457"]
    pub fn EcSii_GetStringCount(cat_data: *const u8, cat_size: c_int) -> c_int;
    #[link_name = "D_1460"]
    pub fn EcSii_GetVendorId(sii_data: *const u8, sii_size: c_int) -> u32;
    #[link_name = "D_1461"]
    pub fn EcSii_GetProductCode(sii_data: *const u8, sii_size: c_int) -> u32;
    #[link_name = "D_1462"]
    pub fn EcSii_GetRevision(sii_data: *const u8, sii_size: c_int) -> u32;
    #[link_name = "D_1463"]
    pub fn EcSii_GetSerialNumber(sii_data: *const u8, sii_size: c_int) -> u32;
    #[link_name = "D_1459"]
    pub fn EcSii_GetConfiguredAlias(sii_data: *const u8, sii_size: c_int) -> u16;
    #[link_name = "D_1446"]
    pub fn EcSii_CoeEnabled(coe_details: u8) -> c_int;
    #[link_name = "D_1447"]
    pub fn EcSii_CoeSdoInfo(coe_details: u8) -> c_int;
    #[link_name = "D_1448"]
    pub fn EcSii_CoePdoAssign(coe_details: u8) -> c_int;
    #[link_name = "D_1449"]
    pub fn EcSii_CoePdoConfig(coe_details: u8) -> c_int;
    #[link_name = "D_1450"]
    pub fn EcSii_CoeUploadAtStartup(coe_details: u8) -> c_int;
    #[link_name = "D_1451"]
    pub fn EcSii_CoeCompleteAccess(coe_details: u8) -> c_int;
    #[link_name = "D_1452"]
    pub fn EcSii_FoeEnabled(foe_details: u8) -> c_int;
    #[link_name = "D_1453"]
    pub fn EcSii_EoeEnabled(eoe_details: u8) -> c_int;

    // --- 耦合器识别 (ec_coupler_id.h) ---
    #[link_name = "D_1403"]
    pub fn EcCouplerId_DetectDeviceType(vendor_id: u32, product_code: u32) -> c_int;
    #[link_name = "D_1404"]
    pub fn EcCouplerId_IsCoupler(vendor_id: u32, product_code: u32) -> c_int;
    #[link_name = "D_1405"]
    pub fn EcCouplerId_IsTerminal(vendor_id: u32, product_code: u32) -> c_int;
    #[link_name = "D_1406"]
    pub fn EcCouplerId_GetVendorName(vendor_id: u32) -> *const c_char;
    #[link_name = "D_1407"]
    pub fn EcCouplerId_GetVendorNameEn(vendor_id: u32) -> *const c_char;
    #[link_name = "D_1408"]
    pub fn EcCouplerId_GetDeviceTypeName(dtype: c_int) -> *const c_char;

    // --- 诊断翻译 (ec_diag_strings.h) ---
    #[link_name = "D_1412"]
    pub fn EcDiagStrings_TopologyDescription(topo: u8) -> *const c_char;
    #[link_name = "D_1413"]
    pub fn EcDiagStrings_TimingMode(mode: u32) -> *const c_char;
    #[link_name = "D_1414"]
    pub fn EcDiagStrings_BreakpointType(bp: u8) -> *const c_char;
    #[link_name = "D_1415"]
    pub fn EcDiagStrings_FormatBreakpoint(
        slave_idx: u16,
        port: u8,
        bp: u8,
        buf: *mut u8,
        buf_size: c_int,
    );

    // --- ESI 解析与启动参数 (ec_esi_parser.h) ---
    // 注: SetSlaveEsiFile 已在前文 (line 1571) 声明, 此处不再重复
    #[link_name = "D_1615"]
    pub fn EcEsi_LoadFile(file_path: *const c_char) -> i32;
    #[link_name = "D_1616"]
    pub fn EcEsi_LoadDirectory(dir_path: *const c_char) -> i32;
    #[link_name = "D_1617"]
    pub fn EcEsi_Clear();
    #[link_name = "D_1618"]
    pub fn EcEsi_GetLoadedCount() -> i32;
    #[link_name = "D_1619"]
    pub fn EcEsi_BindToSlave(mi: u16, si: u16, file_path: *const c_char) -> i32;
    #[link_name = "D_1620"]
    pub fn EcEsi_AutoMatchAll(mi: u16) -> i32;
    #[link_name = "D_1621"]
    pub fn EcEsi_RegisterStartupParameters(mi: u16, si: u16) -> i32;
    #[link_name = "D_1622"]
    pub fn EcEsi_ApplyAllSlaves(mi: u16) -> i32;

    // --- Default PDO 自动发现 (协议下沉, 见 native default_pdo.c, 2026-05-02) ---
    // SDK 公开层不再循环 SDORead 0x1C12/0x1C13; 由 native 处理协议细节.
    // direction: 0=RxPDO, 1=TxPDO; 返回写入数, -1=失败
    #[link_name = "D_1602"]
    pub fn EnumerateDefaultPdo(
        master_index: u16,
        slave_index: u16,
        direction: c_int,
        out_indices: *mut u16,
        max_count: c_int,
    ) -> c_int;

    // --- DC SyncManager 同步类型配置 (协议下沉, 见 native dc_sync_mode.c, 2026-05-02) ---
    // SDK 公开层不再 SDOWrite 0x1C32/0x1C33; 由 native 处理 ETG.1020 §23.1.2 协议细节.
    // sync_type 编码 (与 DcSyncMode enum 对齐):
    //   0 = FreeRun        1 = SmSynchron
    //   2 = DcSynchron     3 = DcSynchron01
    /// 配置从站 SyncManager 同步类型. 返回 0=失败, 非 0=成功 (Win32 BOOL).
    #[link_name = "D_1600"]
    pub fn SetDcSyncMode(master_index: u16, slave_index: u16, sync_type: u8) -> c_int;

    /// 读取从站当前 SyncManager 同步类型. 返回 0..3 (失败/不支持时返回 0=FreeRun).
    #[link_name = "D_1601"]
    pub fn GetDcSyncMode(master_index: u16, slave_index: u16) -> u8;

    // --- ESI Device 字段直读 API (Step 1, D_1603-1608, 2026-05-02) ---
    // 协议下沉: 6 SDK 不再独立解析 ESI XML, 改 native 直接拿 Device 字段.
    // 全部 by (master_index, slave_index); Slave 必须先 EcEsi_BindToSlave / AutoMatchAll.

    /// 列出 Device PDO Index 列表 (direction: 0=RxPDO, 1=TxPDO).
    /// 返回写入 out_indices 的数量, -1=Slave 未绑定 ESI / 参数非法.
    #[link_name = "D_1603"]
    pub fn EcEsi_GetDevicePdoIndices(
        master_index: u16,
        slave_index: u16,
        direction: c_int,
        out_indices: *mut u16,
        max_count: c_int,
    ) -> c_int;

    /// 获取指定 PDO 的总位长 (含 Padding); 未找到返回 -1.
    #[link_name = "D_1604"]
    pub fn EcEsi_GetDevicePdoSizeBits(master_index: u16, slave_index: u16, pdo_index: u16) -> c_int;

    /// 获取 SyncManager 信息 (sm_idx 0..3); 返回 0=成功, 非 0=失败.
    #[link_name = "D_1605"]
    pub fn EcEsi_GetDeviceSmInfo(
        master_index: u16,
        slave_index: u16,
        sm_idx: u8,
        start_addr: *mut u16,
        default_len: *mut u16,
        control_byte: *mut u8,
    ) -> c_int;

    /// 获取 Device 的 DC Sync 模式 (与 ec_dc_sync_mode_t 对齐).
    #[link_name = "D_1606"]
    pub fn EcEsi_GetDeviceDcSyncMode(master_index: u16, slave_index: u16) -> c_int;

    /// 获取邮箱 Request/Response 超时 (毫秒); 返回 0=成功, 非 0=失败.
    #[link_name = "D_1607"]
    pub fn EcEsi_GetDeviceMailboxTimeout(
        master_index: u16,
        slave_index: u16,
        request_ms: *mut c_int,
        response_ms: *mut c_int,
    ) -> c_int;

    /// 获取 Vendor/Product/Revision/Name (UTF-8 写入 name_buf); 返回 0=成功, 非 0=失败.
    #[link_name = "D_1608"]
    pub fn EcEsi_GetDeviceIdentity(
        master_index: u16,
        slave_index: u16,
        vendor_id: *mut u32,
        product_code: *mut u32,
        revision_no: *mut u32,
        name_buf: *mut u8,
        name_buf_size: c_int,
    ) -> c_int;

    // ===================================================================
    // 内核驱动可用性探测 (DarraRT_Eth.sys 等), VMP 保护
    // 字符串返回均为 const char* (C 端 UTF-8 静态常量, 不需 free)
    // 详见 utils/kernel_probe.h / KernelGuard.cs
    // ===================================================================

    /// 探测 EtherCAT 内核驱动状态.
    /// 返回值与 `DarraKernelStatus` 一一对应:
    /// 0=Ok, 1=NotInstalled, 2=Stopped, 3=AccessDenied, 4=SignatureFail,
    /// 5=Blocked, 6=DeviceMissing, 7=Disabled, 99=UnknownError.
    #[link_name = "D_1629"]
    pub fn DarraEcat_KernelProbe() -> c_int;

    /// 把 KernelProbe 状态码转成 UTF-8 中文消息 (DLL 内静态常量, 不需 free).
    #[link_name = "D_1630"]
    pub fn DarraEcat_KernelStatusMessage(status: c_int) -> *const c_char;

    /// 获取安装包下载链接 (UTF-8, 默认 https://www.darrart.com/downloads/drivers,
    /// 可被环境变量 DARRA_INSTALLER_URL 覆盖). 字符串由 DLL 拥有, 不需 free.
    #[link_name = "D_1631"]
    pub fn DarraEcat_KernelInstallerUrl() -> *const c_char;
}

// ===================== 邮箱统计 C 结构 (对齐 ec_mbx_stats_t) =====================

/// 与 C 层 `ec_mbx_stats_t` 二进制兼容的结构 (x64 自然对齐, 72 字节).
///
/// **重要**: 此结构也在 [`crate::abstractions::mailbox_statistics::EcMbxStatsC`] 中定义;
/// 为避免重复, abstractions 模块 `pub(crate) use` 此处的定义, 两处实际为同一类型.
#[repr(C)]
#[derive(Debug, Clone, Copy, Default)]
pub struct EcMbxStatsC {
    /// 累计发送事务数
    pub total_sent: u64,
    /// 累计收到响应数 (含错误帧)
    pub total_received: u64,
    /// 累计超时次数
    pub total_timeout: u64,
    /// 累计收到邮箱错误帧数
    pub total_mbx_error: u64,
    /// 累计协议错误次数
    pub total_proto_error: u64,
    /// 累计被取消的事务数
    pub total_cancelled: u64,
    /// 最近一次错误码
    pub last_error_code: u32,
    /// x64 对齐 padding
    pub _pad1: u32,
    /// 最近一次错误时刻 (UNIX epoch 微秒)
    pub last_error_time_us: u64,
    /// 累计延迟总和 (微秒, 求平均用)
    pub total_latency_us: u64,
}

// ============================================================================
// FFI Gap 运行时动态绑定 (libloading)
// ============================================================================
//
// 本节绑定 C# DLL.cs 已通过 P/Invoke 直连、但 Darra.Core.dll 当前可能尚未导出
// (或在不同版本可能缺失) 的符号. 因为 Rust `extern "C"` 块是静态链接,
// 一旦目标符号缺失会导致 **链接失败** (vs C# 在 P/Invoke 时按需懒加载),
// 因此这些"FFI gap"符号必须走 libloading 运行时解析.
//
// 设计:
//   - `dynamic_ffi::ffi_gap()` 全局唯一实例, OnceLock 懒加载 `Darra.Core.dll`
//   - 每个符号解析为 `Option<extern "C" fn(...)>`, 解析失败保留 None
//   - 上层模块调用时可显式判断 None 走 fallback (返回 Result::Err / 默认值)
//   - DLL 卸载策略: `std::mem::forget(lib)` 让其常驻直到进程退出
//
// 跨平台:
//   - Windows: `Darra.Core.dll`
//   - Linux  : `libDarraCore.so` (build.rs 已支持)
// ----------------------------------------------------------------------------

/// FFI Gap 模块: 运行时解析 DLL 中可能缺失的符号 (HotConnect / VoE Notify /
/// EoE ReceiveHook / ENI Save / CoE LastSdoError / Diag ETG.1510 / ESI DC Sync 等).
///
/// 上层模块通过 `dynamic_ffi::ffi_gap()` 取得 `&FfiGap` 引用, 各方法返回:
///   - `Some(retval)` 表示符号存在且已调用,
///   - `None` 表示 DLL 未导出该符号, 调用方应优雅降级.
#[doc(hidden)]
pub mod dynamic_ffi {
    use super::{c_char, c_int, c_void};
    use std::sync::OnceLock;

    // ---------- 函数指针类型别名 (对齐 C# DLL.cs 各 P/Invoke 签名) ----------

    // HotConnect (6 个)
    pub type HotConnectAddGroupFn = unsafe extern "C" fn(
        master_index: u16, group_id: u16, alias: u16, vendor_id: u32, product_code: u32,
    ) -> c_int;
    pub type HotConnectRemoveGroupFn = unsafe extern "C" fn(
        master_index: u16, group_id: u16,
    ) -> c_int;
    pub type HotConnectGetGroupStatusFn = unsafe extern "C" fn(
        master_index: u16, group_id: u16,
    ) -> c_int;
    pub type HotConnectClearAllFn = unsafe extern "C" fn(master_index: u16);
    pub type HotConnectGetGroupCountFn = unsafe extern "C" fn(master_index: u16) -> c_int;
    pub type HotConnectEnumerateFn = unsafe extern "C" fn(
        master_index: u16, out_buf: *mut c_void, max: c_int,
    ) -> c_int;

    // EoE 异步接收 Hook (3 个)
    pub type EOEFrameCallback = unsafe extern "C" fn(
        master_index: u16, slave: u16, frame_data: *const u8, frame_size: c_int,
    );
    pub type EOESetReceiveHookFn = unsafe extern "C" fn(
        master_index: u16, callback: Option<EOEFrameCallback>,
    ) -> u8;  // C# bool -> u8 (0/1)
    pub type EOEClearReceiveHookFn = unsafe extern "C" fn(master_index: u16) -> u8;

    // VoE Notification Listener (5 个)
    pub type VOENotificationCallback = unsafe extern "C" fn(
        slave: u16, vendor_id: u32, vendor_type: u16,
        data: *const u8, data_size: u32, user_data: *mut c_void,
    );
    pub type VOEStartNotificationListenerFn = unsafe extern "C" fn(master_index: u16) -> u8;
    pub type VOEStopNotificationListenerFn = unsafe extern "C" fn() -> u8;
    pub type VOEIsNotificationListeningFn = unsafe extern "C" fn() -> u8;
    pub type VOERegisterNotificationFn = unsafe extern "C" fn(
        slave: u16, vendor_id: u32, vendor_type: u16,
        callback: Option<VOENotificationCallback>, user_data: *mut c_void,
    ) -> c_int;
    pub type VOEUnregisterNotificationFn = unsafe extern "C" fn(subscription_index: c_int) -> u8;

    // DC ESI 解析 (1 个 — 与 has_dc 区分: 来自 ESI 而非 ESC 硬件位)
    pub type GetSlaveHasEsiDcSyncFn = unsafe extern "C" fn(
        master_index: u16, slave_index: u16,
    ) -> c_int;

    // CoE 最后 SDO 错误码 (1 个)
    pub type GetLastSdoErrorFn = unsafe extern "C" fn(
        master_index: u16, slave_index: u16,
    ) -> u32;

    // ENI 保存 (1 个) — 类型名加 `#[allow(non_camel_case_types)]` 保留 DLL 符号风格
    #[allow(non_camel_case_types)]
    pub type EcEniSaveFn = unsafe extern "C" fn(
        master_index: u16, file_path: *const c_char,
    ) -> c_int;

    // 主站 Diag ETG.1510 SDO 直读/写 (2 个)
    pub type DiagEtg1510ReadFn = unsafe extern "C" fn(
        master_index: u16, index: u16, sub_index: u8,
        out_buf: *mut u8, buf_cap: u32, out_size: *mut u32,
    ) -> c_int;
    pub type DiagEtg1510WriteFn = unsafe extern "C" fn(
        master_index: u16, index: u16, sub_index: u8,
        in_buf: *const u8, in_size: u32,
    ) -> c_int;

    // 从站替换确认 / 状态缓存 (裸符号, 运行时解析)
    pub type AcknowledgeSlaveReplacementFn = unsafe extern "C" fn(
        master_index: u16,
        slave_index: u16,
    ) -> c_int;
    pub type SetStateCacheUpdateIntervalFn = unsafe extern "C" fn(
        master_index: u16,
        interval_ms: u32,
    );
    pub type GetStateCacheUpdateIntervalFn = unsafe extern "C" fn(master_index: u16) -> u32;

    // PDO 直接读写 / 统计 / 诊断 (裸符号, 运行时解析)
    pub type PdoReadDirectFn = unsafe extern "C" fn(
        master_index: u16,
        slave_index: u16,
        pdo_index: u16,
        data_buffer: *mut c_void,
        buffer_size: u32,
        bytes_read: *mut u32,
    ) -> c_int;
    pub type PdoWriteDirectFn = unsafe extern "C" fn(
        master_index: u16,
        slave_index: u16,
        pdo_index: u16,
        data_buffer: *const c_void,
        data_size: u32,
    ) -> c_int;
    pub type PdoBatchReadFn = unsafe extern "C" fn(
        master_index: u16,
        slave_indices: *const u16,
        slave_count: u32,
        data_buffers: *mut *mut c_void,
        buffer_sizes: *const u32,
        bytes_read: *mut u32,
    ) -> u32;
    pub type PdoBatchWriteFn = unsafe extern "C" fn(
        master_index: u16,
        slave_indices: *const u16,
        slave_count: u32,
        data_buffers: *const *const c_void,
        data_sizes: *const u32,
    ) -> u32;
    pub type GetPdoStatsFn = unsafe extern "C" fn(master_index: u16, slave_index: u16) -> *const c_void;
    pub type ResetPdoStatsFn = unsafe extern "C" fn(master_index: u16, slave_index: u16);
    pub type EnablePdoMonitoringFn = unsafe extern "C" fn(enable: c_int);
    pub type IsPdoMonitoringEnabledFn = unsafe extern "C" fn() -> c_int;
    pub type GetPdoAvgCycleTimeNsFn = unsafe extern "C" fn(master_index: u16) -> u64;
    pub type GetPdoErrorCountFn = unsafe extern "C" fn(master_index: u16) -> u32;
    pub type IsPdoValidFn = unsafe extern "C" fn(master_index: u16) -> c_int;
    pub type IsPdoChangedFn = unsafe extern "C" fn(master_index: u16) -> c_int;
    pub type GetPdoExpectedSizeFn = unsafe extern "C" fn(master_index: u16) -> u32;
    pub type GetPdoMappingFn = unsafe extern "C" fn(
        master_index: u16,
        slave_index: u16,
        pdo_type: u16,
        mapping_buffer: *mut c_void,
        buffer_size: u32,
        mapping_count: *mut u32,
    ) -> c_int;

    // ---------- FfiGap: 全部可选符号集合 ----------

    /// FFI Gap 符号集合. 每项 `Option<fn>`, `None` = DLL 未导出.
    pub struct FfiGap {
        // HotConnect
        pub hot_connect_add_group: Option<HotConnectAddGroupFn>,
        pub hot_connect_remove_group: Option<HotConnectRemoveGroupFn>,
        pub hot_connect_get_group_status: Option<HotConnectGetGroupStatusFn>,
        pub hot_connect_clear_all: Option<HotConnectClearAllFn>,
        pub hot_connect_get_group_count: Option<HotConnectGetGroupCountFn>,
        pub hot_connect_enumerate: Option<HotConnectEnumerateFn>,
        // EoE
        pub eoe_set_receive_hook: Option<EOESetReceiveHookFn>,
        pub eoe_clear_receive_hook: Option<EOEClearReceiveHookFn>,
        // VoE
        pub voe_start_notification_listener: Option<VOEStartNotificationListenerFn>,
        pub voe_stop_notification_listener: Option<VOEStopNotificationListenerFn>,
        pub voe_is_notification_listening: Option<VOEIsNotificationListeningFn>,
        pub voe_register_notification: Option<VOERegisterNotificationFn>,
        pub voe_unregister_notification: Option<VOEUnregisterNotificationFn>,
        // DC / CoE / ENI / Diag
        pub get_slave_has_esi_dc_sync: Option<GetSlaveHasEsiDcSyncFn>,
        pub get_last_sdo_error: Option<GetLastSdoErrorFn>,
        pub ec_eni_save: Option<EcEniSaveFn>,
        pub diag_etg1510_read: Option<DiagEtg1510ReadFn>,
        pub diag_etg1510_write: Option<DiagEtg1510WriteFn>,
        // 替换确认 / 状态缓存
        pub acknowledge_slave_replacement: Option<AcknowledgeSlaveReplacementFn>,
        pub set_state_cache_update_interval: Option<SetStateCacheUpdateIntervalFn>,
        pub get_state_cache_update_interval: Option<GetStateCacheUpdateIntervalFn>,
        // PDO
        pub pdo_read_direct: Option<PdoReadDirectFn>,
        pub pdo_write_direct: Option<PdoWriteDirectFn>,
        pub pdo_batch_read: Option<PdoBatchReadFn>,
        pub pdo_batch_write: Option<PdoBatchWriteFn>,
        pub get_pdo_stats: Option<GetPdoStatsFn>,
        pub reset_pdo_stats: Option<ResetPdoStatsFn>,
        pub enable_pdo_monitoring: Option<EnablePdoMonitoringFn>,
        pub is_pdo_monitoring_enabled: Option<IsPdoMonitoringEnabledFn>,
        pub get_pdo_avg_cycle_time_ns: Option<GetPdoAvgCycleTimeNsFn>,
        pub get_pdo_error_count: Option<GetPdoErrorCountFn>,
        pub is_pdo_valid: Option<IsPdoValidFn>,
        pub is_pdo_changed: Option<IsPdoChangedFn>,
        pub get_pdo_expected_size: Option<GetPdoExpectedSizeFn>,
        pub get_pdo_mapping: Option<GetPdoMappingFn>,
        /// 标记 DLL 是否成功加载 (失败时全部为 None).
        pub loaded: bool,
    }

    impl FfiGap {
        const fn empty() -> Self {
            Self {
                hot_connect_add_group: None,
                hot_connect_remove_group: None,
                hot_connect_get_group_status: None,
                hot_connect_clear_all: None,
                hot_connect_get_group_count: None,
                hot_connect_enumerate: None,
                eoe_set_receive_hook: None,
                eoe_clear_receive_hook: None,
                voe_start_notification_listener: None,
                voe_stop_notification_listener: None,
                voe_is_notification_listening: None,
                voe_register_notification: None,
                voe_unregister_notification: None,
                get_slave_has_esi_dc_sync: None,
                get_last_sdo_error: None,
                ec_eni_save: None,
                diag_etg1510_read: None,
                diag_etg1510_write: None,
                acknowledge_slave_replacement: None,
                set_state_cache_update_interval: None,
                get_state_cache_update_interval: None,
                pdo_read_direct: None,
                pdo_write_direct: None,
                pdo_batch_read: None,
                pdo_batch_write: None,
                get_pdo_stats: None,
                reset_pdo_stats: None,
                enable_pdo_monitoring: None,
                is_pdo_monitoring_enabled: None,
                get_pdo_avg_cycle_time_ns: None,
                get_pdo_error_count: None,
                is_pdo_valid: None,
                is_pdo_changed: None,
                get_pdo_expected_size: None,
                get_pdo_mapping: None,
                loaded: false,
            }
        }
    }

    /// DLL 名称 (Windows / Linux 自动选择, 编译期 XOR 混淆隐藏明文).
    #[doc(hidden)]
    #[inline(always)]
    fn dll_name() -> String {
        #[cfg(target_os = "windows")]
        { obfstr::obfstr!("Darra.Core.dll").to_string() }
        #[cfg(not(target_os = "windows"))]
        { obfstr::obfstr!("libDarraCore.so").to_string() }
    }

    static INSTANCE: OnceLock<FfiGap> = OnceLock::new();

    /// 获取 FFI Gap 单例 (首次调用懒加载 DLL + 解析符号).
    ///
    /// 解析失败的符号保留 `None`, 调用方需自行判断并降级.
    /// DLL 加载失败 (例如未在 PATH 中) 时返回的 `FfiGap.loaded == false`,
    /// 所有字段为 None.
    pub fn ffi_gap() -> &'static FfiGap {
        INSTANCE.get_or_init(load_ffi_gap)
    }

    fn load_ffi_gap() -> FfiGap {
        let mut gap = FfiGap::empty();

        // 加载主 DLL. libloading::Library::new 在 Windows 上等价 LoadLibraryW,
        // 在 Linux 上等价 dlopen.
        let dll_name = dll_name();
        let lib = match unsafe { libloading::Library::new(&dll_name) } {
            Ok(l) => l,
            Err(_) => {
                // DLL 不可用 (常见于单元测试或者裸 cargo build) → 全部 None
                return gap;
            }
        };

        // 符号名通过 obfstr 编译期 XOR 混淆隐藏明文; 运行时解码到栈上 String 再传 libloading::get.
        // 注意: libloading::Library::get 接受 `&[u8]`, 必须以 NUL 结尾.
        unsafe {
            // 小辅助: obfstr! 解出明文 + 末尾追加 NUL → 返回 Vec<u8>, 给 libloading::get 用.
            #[inline(always)]
            fn sym(s: &str) -> Vec<u8> {
                let mut v = s.as_bytes().to_vec();
                v.push(0);
                v
            }

            // ---- HotConnect ----
            gap.hot_connect_add_group = lib.get::<HotConnectAddGroupFn>(&sym(obfstr::obfstr!("HotConnectAddGroup"))).ok().map(|s| *s);
            gap.hot_connect_remove_group = lib.get::<HotConnectRemoveGroupFn>(&sym(obfstr::obfstr!("HotConnectRemoveGroup"))).ok().map(|s| *s);
            gap.hot_connect_get_group_status = lib.get::<HotConnectGetGroupStatusFn>(&sym(obfstr::obfstr!("HotConnectGetGroupStatus"))).ok().map(|s| *s);
            gap.hot_connect_clear_all = lib.get::<HotConnectClearAllFn>(&sym(obfstr::obfstr!("HotConnectClearAll"))).ok().map(|s| *s);
            gap.hot_connect_get_group_count = lib.get::<HotConnectGetGroupCountFn>(&sym(obfstr::obfstr!("HotConnectGetGroupCount"))).ok().map(|s| *s);
            gap.hot_connect_enumerate = lib.get::<HotConnectEnumerateFn>(&sym(obfstr::obfstr!("HotConnectEnumerate"))).ok().map(|s| *s);

            // ---- EoE Receive Hook ----
            gap.eoe_set_receive_hook = lib.get::<EOESetReceiveHookFn>(&sym(obfstr::obfstr!("EOESetReceiveHook"))).ok().map(|s| *s);
            gap.eoe_clear_receive_hook = lib.get::<EOEClearReceiveHookFn>(&sym(obfstr::obfstr!("EOEClearReceiveHook"))).ok().map(|s| *s);

            // ---- VoE Notification Listener ----
            gap.voe_start_notification_listener = lib.get::<VOEStartNotificationListenerFn>(&sym(obfstr::obfstr!("VOEStartNotificationListener"))).ok().map(|s| *s);
            gap.voe_stop_notification_listener = lib.get::<VOEStopNotificationListenerFn>(&sym(obfstr::obfstr!("VOEStopNotificationListener"))).ok().map(|s| *s);
            gap.voe_is_notification_listening = lib.get::<VOEIsNotificationListeningFn>(&sym(obfstr::obfstr!("VOEIsNotificationListening"))).ok().map(|s| *s);
            gap.voe_register_notification = lib.get::<VOERegisterNotificationFn>(&sym(obfstr::obfstr!("VOERegisterNotification"))).ok().map(|s| *s);
            gap.voe_unregister_notification = lib.get::<VOEUnregisterNotificationFn>(&sym(obfstr::obfstr!("VOEUnregisterNotification"))).ok().map(|s| *s);

            // ---- DC ESI 解析 ----
            gap.get_slave_has_esi_dc_sync = lib.get::<GetSlaveHasEsiDcSyncFn>(&sym(obfstr::obfstr!("GetSlaveHasEsiDcSync"))).ok().map(|s| *s);

            // ---- CoE LastSdoError ----
            gap.get_last_sdo_error = lib.get::<GetLastSdoErrorFn>(&sym(obfstr::obfstr!("GetLastSdoError"))).ok().map(|s| *s);

            // ---- ENI Save ----
            gap.ec_eni_save = lib.get::<EcEniSaveFn>(&sym(obfstr::obfstr!("EcEni_Save"))).ok().map(|s| *s);

            // ---- Diag ETG.1510 ----
            gap.diag_etg1510_read = lib.get::<DiagEtg1510ReadFn>(&sym(obfstr::obfstr!("Diag_Etg1510Read"))).ok().map(|s| *s);
            gap.diag_etg1510_write = lib.get::<DiagEtg1510WriteFn>(&sym(obfstr::obfstr!("Diag_Etg1510Write"))).ok().map(|s| *s);

            // ---- 替换确认 / 状态缓存 ----
            gap.acknowledge_slave_replacement = lib.get::<AcknowledgeSlaveReplacementFn>(&sym(obfstr::obfstr!("AcknowledgeSlaveReplacement"))).ok().map(|s| *s);
            gap.set_state_cache_update_interval = lib.get::<SetStateCacheUpdateIntervalFn>(&sym(obfstr::obfstr!("SetStateCacheUpdateInterval"))).ok().map(|s| *s);
            gap.get_state_cache_update_interval = lib.get::<GetStateCacheUpdateIntervalFn>(&sym(obfstr::obfstr!("GetStateCacheUpdateInterval"))).ok().map(|s| *s);

            // ---- PDO 直接读写 / 统计 / 诊断 ----
            gap.pdo_read_direct = lib.get::<PdoReadDirectFn>(&sym(obfstr::obfstr!("PDOReadDirect"))).ok().map(|s| *s);
            gap.pdo_write_direct = lib.get::<PdoWriteDirectFn>(&sym(obfstr::obfstr!("PDOWriteDirect"))).ok().map(|s| *s);
            gap.pdo_batch_read = lib.get::<PdoBatchReadFn>(&sym(obfstr::obfstr!("PDOBatchRead"))).ok().map(|s| *s);
            gap.pdo_batch_write = lib.get::<PdoBatchWriteFn>(&sym(obfstr::obfstr!("PDOBatchWrite"))).ok().map(|s| *s);
            gap.get_pdo_stats = lib.get::<GetPdoStatsFn>(&sym(obfstr::obfstr!("GetPDOStats"))).ok().map(|s| *s);
            gap.reset_pdo_stats = lib.get::<ResetPdoStatsFn>(&sym(obfstr::obfstr!("ResetPDOStats"))).ok().map(|s| *s);
            gap.enable_pdo_monitoring = lib.get::<EnablePdoMonitoringFn>(&sym(obfstr::obfstr!("EnablePDOMonitoring"))).ok().map(|s| *s);
            gap.is_pdo_monitoring_enabled = lib.get::<IsPdoMonitoringEnabledFn>(&sym(obfstr::obfstr!("IsPDOMonitoringEnabled"))).ok().map(|s| *s);
            gap.get_pdo_avg_cycle_time_ns = lib.get::<GetPdoAvgCycleTimeNsFn>(&sym(obfstr::obfstr!("GetPDOAvgCycleTimeNs"))).ok().map(|s| *s);
            gap.get_pdo_error_count = lib.get::<GetPdoErrorCountFn>(&sym(obfstr::obfstr!("GetPDOErrorCount"))).ok().map(|s| *s);
            gap.is_pdo_valid = lib.get::<IsPdoValidFn>(&sym(obfstr::obfstr!("IsPDOValid"))).ok().map(|s| *s);
            gap.is_pdo_changed = lib.get::<IsPdoChangedFn>(&sym(obfstr::obfstr!("IsPDOChanged"))).ok().map(|s| *s);
            gap.get_pdo_expected_size = lib.get::<GetPdoExpectedSizeFn>(&sym(obfstr::obfstr!("GetPDOExpectedSize"))).ok().map(|s| *s);
            gap.get_pdo_mapping = lib.get::<GetPdoMappingFn>(&sym(obfstr::obfstr!("GetPDOMapping"))).ok().map(|s| *s);

            gap.loaded = true;
            // 防止 DLL 被卸载 (函数指针必须始终有效, 直到进程退出)
            std::mem::forget(lib);
        }

        gap
    }
}

// ============================================================================
// 运行时可选符号包装 (与 ffi 模块同名, 调用方无感知)
// 标 #[no_mangle] 不可, 因为这些函数仅供 ffi:: 路径下使用, 不导出 ABI 符号.
// 缺少运行时符号时只返回明确失败值, 不伪装成功.
// ============================================================================

/// AcknowledgeSlaveReplacement: 符号存在时调用 native, 否则返回 0 (BOOL FALSE).
#[allow(non_snake_case)]
pub unsafe fn AcknowledgeSlaveReplacement(master_index: u16, slave_index: u16) -> c_int {
    match dynamic_ffi::ffi_gap().acknowledge_slave_replacement {
        Some(f) => f(master_index, slave_index),
        None => 0,
    }
}

/// SetStateCacheUpdateInterval: 符号存在时调用 native, 否则 no-op.
#[allow(non_snake_case)]
pub unsafe fn SetStateCacheUpdateInterval(master_index: u16, interval_ms: u32) {
    if let Some(f) = dynamic_ffi::ffi_gap().set_state_cache_update_interval {
        f(master_index, interval_ms);
    }
}

/// GetStateCacheUpdateInterval: 符号存在时调用 native, 否则返回 0.
#[allow(non_snake_case)]
pub unsafe fn GetStateCacheUpdateInterval(master_index: u16) -> u32 {
    match dynamic_ffi::ffi_gap().get_state_cache_update_interval {
        Some(f) => f(master_index),
        None => 0,
    }
}

/// PDOReadDirect: 符号存在时调用 native, 否则返回 0 (BOOL FALSE).
#[allow(non_snake_case)]
pub unsafe fn PDOReadDirect(
    master_index: u16, slave_index: u16, pdo_index: u16,
    data_buffer: *mut c_void, buffer_size: u32, bytes_read: *mut u32,
) -> i32 {
    match dynamic_ffi::ffi_gap().pdo_read_direct {
        Some(f) => f(master_index, slave_index, pdo_index, data_buffer, buffer_size, bytes_read),
        None => 0,
    }
}

/// PDOWriteDirect: 符号存在时调用 native, 否则返回 0 (BOOL FALSE).
#[allow(non_snake_case)]
pub unsafe fn PDOWriteDirect(
    master_index: u16, slave_index: u16, pdo_index: u16,
    data_buffer: *const c_void, data_size: u32,
) -> i32 {
    match dynamic_ffi::ffi_gap().pdo_write_direct {
        Some(f) => f(master_index, slave_index, pdo_index, data_buffer, data_size),
        None => 0,
    }
}

/// PDOBatchRead: 符号存在时调用 native, 否则返回 0 (无成功条目).
#[allow(non_snake_case)]
pub unsafe fn PDOBatchRead(
    master_index: u16, slave_indices: *const u16, slave_count: u32,
    data_buffers: *mut *mut c_void, buffer_sizes: *const u32, bytes_read: *mut u32,
) -> u32 {
    match dynamic_ffi::ffi_gap().pdo_batch_read {
        Some(f) => f(master_index, slave_indices, slave_count, data_buffers, buffer_sizes, bytes_read),
        None => 0,
    }
}

/// PDOBatchWrite: 符号存在时调用 native, 否则返回 0.
#[allow(non_snake_case)]
pub unsafe fn PDOBatchWrite(
    master_index: u16, slave_indices: *const u16, slave_count: u32,
    data_buffers: *const *const c_void, data_sizes: *const u32,
) -> u32 {
    match dynamic_ffi::ffi_gap().pdo_batch_write {
        Some(f) => f(master_index, slave_indices, slave_count, data_buffers, data_sizes),
        None => 0,
    }
}

/// GetPDOStats: 符号存在时调用 native, 否则返回 null 指针.
#[allow(non_snake_case)]
pub unsafe fn GetPDOStats(master_index: u16, slave_index: u16) -> *const c_void {
    match dynamic_ffi::ffi_gap().get_pdo_stats {
        Some(f) => f(master_index, slave_index),
        None => core::ptr::null(),
    }
}

/// ResetPDOStats: 符号存在时调用 native, 否则 no-op.
#[allow(non_snake_case)]
pub unsafe fn ResetPDOStats(master_index: u16, slave_index: u16) {
    if let Some(f) = dynamic_ffi::ffi_gap().reset_pdo_stats {
        f(master_index, slave_index);
    }
}

/// EnablePDOMonitoring: 符号存在时调用 native, 否则 no-op.
#[allow(non_snake_case)]
pub unsafe fn EnablePDOMonitoring(enable: i32) {
    if let Some(f) = dynamic_ffi::ffi_gap().enable_pdo_monitoring {
        f(enable);
    }
}

/// IsPDOMonitoringEnabled: 符号存在时调用 native, 否则返回 0 (false).
#[allow(non_snake_case)]
pub unsafe fn IsPDOMonitoringEnabled() -> i32 {
    match dynamic_ffi::ffi_gap().is_pdo_monitoring_enabled {
        Some(f) => f(),
        None => 0,
    }
}

/// GetPDOAvgCycleTimeNs: 符号存在时调用 native, 否则返回 0.
#[allow(non_snake_case)]
pub unsafe fn GetPDOAvgCycleTimeNs(master_index: u16) -> u64 {
    match dynamic_ffi::ffi_gap().get_pdo_avg_cycle_time_ns {
        Some(f) => f(master_index),
        None => 0,
    }
}

/// GetPDOErrorCount: 符号存在时调用 native, 否则返回 0.
#[allow(non_snake_case)]
pub unsafe fn GetPDOErrorCount(master_index: u16) -> u32 {
    match dynamic_ffi::ffi_gap().get_pdo_error_count {
        Some(f) => f(master_index),
        None => 0,
    }
}

/// IsPDOValid: 符号存在时调用 native, 否则返回 0 (false).
#[allow(non_snake_case)]
pub unsafe fn IsPDOValid(master_index: u16) -> i32 {
    match dynamic_ffi::ffi_gap().is_pdo_valid {
        Some(f) => f(master_index),
        None => 0,
    }
}

/// IsPDOChanged: 符号存在时调用 native, 否则返回 0 (false).
#[allow(non_snake_case)]
pub unsafe fn IsPDOChanged(master_index: u16) -> i32 {
    match dynamic_ffi::ffi_gap().is_pdo_changed {
        Some(f) => f(master_index),
        None => 0,
    }
}

/// GetPDOExpectedSize: 符号存在时调用 native, 否则返回 0.
#[allow(non_snake_case)]
pub unsafe fn GetPDOExpectedSize(master_index: u16) -> u32 {
    match dynamic_ffi::ffi_gap().get_pdo_expected_size {
        Some(f) => f(master_index),
        None => 0,
    }
}

/// GetPDOMapping: 符号存在时调用 native, 否则返回 0 (BOOL FALSE).
#[allow(non_snake_case)]
pub unsafe fn GetPDOMapping(
    master_index: u16, slave_index: u16, pdo_type: u16,
    mapping_buffer: *mut c_void, buffer_size: u32, mapping_count: *mut u32,
) -> i32 {
    match dynamic_ffi::ffi_gap().get_pdo_mapping {
        Some(f) => f(master_index, slave_index, pdo_type, mapping_buffer, buffer_size, mapping_count),
        None => 0,
    }
}
