//! 描述信息辅助函数
//!
//! 提供 EtherCAT 各种状态码、错误码的文本描述。
//! 对应 C# `Static/Print.cs`。

use crate::data::error::EcState;
use crate::utils::ffi::{FsoeState, FsoeError};

// ==================== EcState 描述 ====================

/// 获取 EcState 的英文描述
pub fn state_description(state: EcState) -> &'static str {
    match state {
        EcState::None => "None",
        EcState::Init => "Init",
        EcState::PreOp => "PreOp",
        EcState::Boot => "Boot",
        EcState::SafeOp => "SafeOp",
        EcState::Operational => "OP",
    }
}

// ==================== AL Status Code 描述 ====================

/// 获取 AL Status Code 的英文描述 (ETG.1020)
pub fn al_status_description(code: u16) -> &'static str {
    match code {
        0x0000 => "No error",
        0x0001 => "Unspecified error",
        0x0002 => "No memory",
        0x0003 => "Invalid device setup",
        0x0004 => "Invalid revision",
        0x0006 => "SII/EEPROM information does not match firmware",
        0x0007 => "Firmware update not successful",
        0x000E => "License error",
        0x0011 => "Invalid requested state change",
        0x0012 => "Unknown requested state",
        0x0013 => "Bootstrap not supported",
        0x0014 => "No valid firmware",
        0x0015 => "Invalid mailbox configuration",
        0x0016 => "Invalid mailbox configuration",
        0x0017 => "Invalid sync manager configuration",
        0x0018 => "No valid inputs available",
        0x0019 => "No valid outputs",
        0x001A => "Synchronization error",
        0x001B => "Sync manager watchdog",
        0x001C => "Invalid sync manager types",
        0x001D => "Invalid output configuration",
        0x001E => "Invalid input configuration",
        0x001F => "Invalid watchdog configuration",
        0x0020 => "Slave needs cold start",
        0x0021 => "Slave needs INIT",
        0x0022 => "Slave needs PREOP",
        0x0023 => "Slave needs SAFEOP",
        0x0024 => "Invalid input mapping",
        0x0025 => "Invalid output mapping",
        0x0026 => "Inconsistent settings",
        0x0027 => "Freerun not supported",
        0x0028 => "Synchronisation not supported",
        0x0029 => "Freerun needs 3buffer mode",
        0x002A => "Background watchdog",
        0x002B => "No valid Inputs and Outputs",
        0x002C => "Fatal sync error",
        0x002D => "No sync error",
        0x002E => "Cycle time too small",
        0x0030 => "Invalid DC SYNC configuration",
        0x0031 => "Invalid DC latch configuration",
        0x0032 => "PLL error",
        0x0033 => "DC sync IO error",
        0x0034 => "DC sync timeout error",
        0x0035 => "DC invalid sync cycle time",
        0x0036 => "DC invalid sync0 cycle time",
        0x0037 => "DC invalid sync1 cycle time",
        0x0041 => "MBX_AOE",
        0x0042 => "MBX_EOE",
        0x0043 => "MBX_COE",
        0x0044 => "MBX_FOE",
        0x0045 => "MBX_SOE",
        0x004F => "MBX_VOE",
        0x0050 => "EEPROM no access",
        0x0051 => "EEPROM error",
        0x0052 => "External hardware not ready",
        0x0060 => "Slave restarted locally",
        0x0061 => "Device identification value updated",
        0x0070 => "Detected module ident list does not match",
        0x0080 => "Supply voltage too low",
        0x0081 => "Supply voltage too high",
        0x0082 => "Temperature too low",
        0x0083 => "Temperature too high",
        0x00F0 => "Application controller available",
        _ => "Unknown",
    }
}

/// 获取 AL Status Code 的中文描述 (ETG.1020)
pub fn al_status_description_cn(code: u16) -> &'static str {
    match code {
        0x0000 => "无错误",
        0x0001 => "未指定错误",
        0x0002 => "内存不足",
        0x0003 => "无效的设备设置",
        0x0004 => "无效的修订版本",
        0x0006 => "SII/EEPROM 信息与固件不匹配",
        0x0007 => "固件更新失败",
        0x000E => "许可证错误",
        0x0011 => "无效的状态转换请求",
        0x0012 => "未知的状态转换请求",
        0x0013 => "引导模式不支持",
        0x0014 => "无有效固件",
        0x0015 => "无效的邮箱配置 (Boot 状态)",
        0x0016 => "无效的邮箱配置 (PreOp 状态)",
        0x0017 => "无效的同步管理器配置",
        0x0018 => "无有效输入",
        0x0019 => "无有效输出",
        0x001A => "同步错误",
        0x001B => "同步管理器看门狗",
        0x001C => "无效的同步管理器类型",
        0x001D => "无效的输出配置",
        0x001E => "无效的输入配置",
        0x001F => "无效的看门狗配置",
        0x0020 => "从站需要冷启动",
        0x0021 => "从站需要 INIT 状态",
        0x0022 => "从站需要 PREOP 状态",
        0x0023 => "从站需要 SAFEOP 状态",
        0x0024 => "无效的输入映射",
        0x0025 => "无效的输出映射",
        0x0026 => "设置不一致",
        0x0027 => "FreeRun 模式不支持",
        0x0028 => "同步模式不支持",
        0x0029 => "FreeRun 需要 3 缓冲区模式",
        0x002A => "后台看门狗",
        0x002B => "无有效的输入输出数据",
        0x002C => "致命同步错误",
        0x002D => "无同步错误",
        0x002E => "周期时间过短",
        0x0030 => "无效的 DC SYNC 配置",
        0x0031 => "无效的 DC 锁存配置",
        0x0032 => "PLL 锁定错误",
        0x0033 => "DC IO 错误",
        0x0034 => "DC 超时错误",
        0x0035 => "DC 无效的同步周期长度",
        0x0036 => "DC 无效的 Sync0 周期长度",
        0x0037 => "DC 无效的 Sync1 周期长度",
        0x0041 => "MBX_AOE 错误",
        0x0042 => "MBX_EOE 错误",
        0x0043 => "MBX_COE 错误",
        0x0044 => "MBX_FOE 错误",
        0x0045 => "MBX_SOE 错误",
        0x004F => "MBX_VOE 错误",
        0x0050 => "EEPROM 无访问权限",
        0x0051 => "EEPROM 错误",
        0x0052 => "外部硬件未就绪",
        0x0060 => "从站重启",
        0x0061 => "设备标识值已更新",
        0x0070 => "检测到的模块标识列表不匹配",
        0x0080 => "供电电压过低",
        0x0081 => "供电电压过高",
        0x0082 => "温度过低",
        0x0083 => "温度过高",
        0x00F0 => "应用控制器可用",
        _ => {
            if code >= 0x8000 {
                "厂商特定错误"
            } else {
                "未知错误"
            }
        }
    }
}

// ==================== SDO 错误码描述 ====================

/// 获取 SDO 错误码描述 (英文)
pub fn sdo_error_description(code: u32) -> &'static str {
    match code {
        0x00000000 => "No error",
        0x05030000 => "Toggle bit not changed",
        0x05040000 => "SDO protocol timeout",
        0x05040001 => "Client/Server command specifier not valid or unknown",
        0x05040005 => "Out of memory",
        0x06010000 => "Unsupported access to an object",
        0x06010001 => "Attempt to read to a write only object",
        0x06010002 => "Attempt to write to a read only object",
        0x06010003 => "Subindex can not be written, SI0 must be 0 for write access",
        0x06010004 => "SDO Complete access not supported for variable length objects",
        0x06010005 => "Object length exceeds mailbox size",
        0x06010006 => "Object mapped to RxPDO, SDO download blocked",
        0x06020000 => "The object does not exist in the object directory",
        0x06040041 => "The object can not be mapped into the PDO",
        0x06040042 => "The number and length of the objects to be mapped would exceed the PDO length",
        0x06040043 => "General parameter incompatibility reason",
        0x06040047 => "General internal incompatibility in the device",
        0x06060000 => "Access failed due to a hardware error",
        0x06070010 => "Data type does not match, length of service parameter does not match",
        0x06070012 => "Data type does not match, length of service parameter too high",
        0x06070013 => "Data type does not match, length of service parameter too low",
        0x06090011 => "Subindex does not exist",
        0x06090030 => "Value range of parameter exceeded (only for write access)",
        0x06090031 => "Value of parameter written too high",
        0x06090032 => "Value of parameter written too low",
        0x06090033 => "Configured module list does not match detected module list",
        0x06090036 => "Maximum value is less than minimum value",
        0x08000000 => "General error",
        0x08000020 => "Data cannot be transferred or stored to the application",
        0x08000021 => "Data cannot be transferred or stored to the application because of local control",
        0x08000022 => "Data cannot be transferred or stored to the application because of the present device state",
        0x08000023 => "Object dictionary dynamic generation fails or no object dictionary is present",
        _ => "Unknown",
    }
}

// ==================== SoE 错误码描述 ====================

/// 获取 SoE 错误码描述 (英文)
pub fn soe_error_description(code: u16) -> &'static str {
    match code {
        0x0000 => "No error",
        0x1001 => "No IDN",
        0x1009 => "Invalid access to element 1",
        0x2001 => "No Name",
        0x2002 => "Name transmission too short",
        0x2003 => "Name transmission too long",
        0x2004 => "Name cannot be changed (read only)",
        0x2005 => "Name is write-protected at this time",
        0x3002 => "Attribute transmission too short",
        0x3003 => "Attribute transmission too long",
        0x3004 => "Attribute cannot be changed (read only)",
        0x3005 => "Attribute is write-protected at this time",
        0x4001 => "No units",
        0x4002 => "Unit transmission too short",
        0x4003 => "Unit transmission too long",
        0x4004 => "Unit cannot be changed (read only)",
        0x4005 => "Unit is write-protected at this time",
        0x5001 => "No minimum input value",
        0x5002 => "Minimum input value transmission too short",
        0x5003 => "Minimum input value transmission too long",
        0x5004 => "Minimum input value cannot be changed (read only)",
        0x5005 => "Minimum input value is write-protected at this time",
        0x6001 => "No maximum input value",
        0x6002 => "Maximum input value transmission too short",
        0x6003 => "Maximum input value transmission too long",
        0x6004 => "Maximum input value cannot be changed (read only)",
        0x6005 => "Maximum input value is write-protected at this time",
        0x7002 => "Operation data transmission too short",
        0x7003 => "Operation data transmission too long",
        0x7004 => "Operation data cannot be changed (read only)",
        0x7005 => "Operation data is write-protected at this time (state)",
        0x7006 => "Operation data is smaller than the minimum input value",
        0x7007 => "Operation data is smaller than the maximum input value",
        0x7008 => "Invalid operation data: Configured IDN will not be supported",
        0x7009 => "Operation data write protected by a password",
        0x700A => "Operation data is write protected, it is configured cyclically",
        0x700B => "Invalid indirect addressing",
        0x700C => "Operation data is write protected, due to other settings",
        0x7010 => "Procedure command already active",
        0x7011 => "Procedure command not interruptible",
        0x7012 => "Procedure command at this time not executable (state)",
        0x7013 => "Procedure command not executable (invalid or false parameters)",
        0x7014 => "No data state",
        0x8001 => "No default value",
        0x8002 => "Default value transmission too long",
        0x8004 => "Default value cannot be changed, read only",
        0x800A => "Invalid drive number",
        0x800B => "General error",
        0x800C => "No element addressed",
        _ => "Unknown",
    }
}

// ==================== 邮箱错误码描述 ====================

/// 获取邮箱错误码描述 (英文)
pub fn mailbox_error_description(code: u16) -> &'static str {
    match code {
        0x0000 => "No error",
        0x0001 => "Syntax of 6 octet Mailbox Header is wrong",
        0x0002 => "The mailbox protocol is not supported",
        0x0003 => "Channel Field contains wrong value",
        0x0004 => "The service is not supported",
        0x0005 => "Invalid mailbox header",
        0x0006 => "Length of received mailbox data is too short",
        0x0007 => "No more memory in slave",
        0x0008 => "The length of data is inconsistent",
        _ => "Unknown",
    }
}

// ==================== 邮箱协议类型描述 ====================

/// 获取邮箱协议类型描述
pub fn mailbox_type_description(mbx_type: u16) -> &'static str {
    match mbx_type {
        0x00 => "Error type",
        0x01 => "ADS over EtherCAT",
        0x02 => "Ethernet over EtherCAT",
        0x03 => "CANopen over EtherCAT",
        0x04 => "File over EtherCAT",
        0x05 => "Servo over EtherCAT",
        0x0F => "Vendor over EtherCAT",
        _ => "Unknown",
    }
}

// ==================== 对象访问权限描述 ====================

/// 对象访问位定义 (ETG1000.6)
const ATYPE_RPRE: u16 = 0x01;  // PreOp 状态下读
const ATYPE_RSAFE: u16 = 0x02; // SafeOp 状态下读
const ATYPE_ROP: u16 = 0x04;   // Op 状态下读
const ATYPE_WPRE: u16 = 0x08;  // PreOp 状态下写
const ATYPE_WSAFE: u16 = 0x10; // SafeOp 状态下写
const ATYPE_WOP: u16 = 0x20;   // Op 状态下写

/// 获取对象访问权限位描述 (RWRWRW 格式)
///
/// 每两位分别对应 PreOp / SafeOp / Op 状态的读写权限
pub fn obj_access_description(access: u16) -> String {
    let mut s = String::with_capacity(6);
    s.push(if access & ATYPE_RPRE != 0 { 'R' } else { '_' });
    s.push(if access & ATYPE_WPRE != 0 { 'W' } else { '_' });
    s.push(if access & ATYPE_RSAFE != 0 { 'R' } else { '_' });
    s.push(if access & ATYPE_WSAFE != 0 { 'W' } else { '_' });
    s.push(if access & ATYPE_ROP != 0 { 'R' } else { '_' });
    s.push(if access & ATYPE_WOP != 0 { 'W' } else { '_' });
    s
}

/// 获取对象访问权限的中文描述
pub fn obj_access_description_cn(access: u16) -> String {
    let can_read = (access & (ATYPE_RPRE | ATYPE_RSAFE | ATYPE_ROP)) != 0;
    let can_write = (access & (ATYPE_WPRE | ATYPE_WSAFE | ATYPE_WOP)) != 0;

    let mut writable_states = Vec::new();
    if access & ATYPE_WPRE != 0 { writable_states.push("PreOP"); }
    if access & ATYPE_WSAFE != 0 { writable_states.push("SafeOP"); }
    if access & ATYPE_WOP != 0 { writable_states.push("OP"); }

    if can_read && can_write {
        format!("读写({}可写)", writable_states.join(","))
    } else if can_read {
        "只读".to_string()
    } else if can_write {
        format!("只写({}可写)", writable_states.join(","))
    } else {
        "无权限".to_string()
    }
}

/// 获取写入条件描述
pub fn write_condition_description(access: u16) -> String {
    let mut conditions = Vec::new();
    if access & ATYPE_WPRE != 0 { conditions.push("PreOP"); }
    if access & ATYPE_WSAFE != 0 { conditions.push("SafeOP"); }
    if access & ATYPE_WOP != 0 { conditions.push("OP"); }

    if conditions.is_empty() {
        "不可写入".to_string()
    } else {
        format!("写入条件: {}", conditions.join("/"))
    }
}

/// 检查在指定状态下是否可写
pub fn can_write_in_state(access: u16, state: EcState) -> bool {
    match state {
        EcState::PreOp => (access & ATYPE_WPRE) != 0,
        EcState::SafeOp => (access & (ATYPE_WPRE | ATYPE_WSAFE)) != 0,
        EcState::Operational => (access & (ATYPE_WPRE | ATYPE_WSAFE | ATYPE_WOP)) != 0,
        _ => false,
    }
}

// ==================== 数据类型描述 ====================

/// 获取 EcDataType 的简短类型名 (用于 UI 显示)
pub fn data_type_short_name(dt: u16) -> &'static str {
    match dt {
        0x0001 => "bool",
        0x0002 => "int8",
        0x0003 => "int16",
        0x0004 => "int32",
        0x0005 => "uint8",
        0x0006 => "uint16",
        0x0007 => "uint32",
        0x0008 => "float",
        0x0009 => "string",
        0x000A => "byte[]",
        0x000B => "string",
        0x000C => "datetime",
        0x000D => "timespan",
        0x000F => "byte[]",
        0x0010 => "int24",
        0x0011 => "double",
        0x0015 => "int64",
        0x0016 => "uint24",
        0x001B => "uint64",
        0x0030 => "bit1",
        0x0031 => "bit2",
        0x0032 => "bit3",
        0x0033 => "bit4",
        0x0034 => "bit5",
        0x0035 => "bit6",
        0x0036 => "bit7",
        0x0037 => "bit8",
        _ => "unknown",
    }
}

// ==================== AL 状态码完整描述 ====================

/// 获取 AL Status Code 的完整描述 (含代码+英文+中文)
pub fn al_status_full_description(code: u16) -> String {
    format!("0x{:04X}: {} / {}", code, al_status_description(code), al_status_description_cn(code))
}

// ==================== 链路状态描述 ====================

/// 获取链路状态描述
pub fn link_state_description(state: u8) -> &'static str {
    match state {
        0 => "断开 (Disconnected)",
        1 => "已连接 (Connected)",
        3 => "仅主端口 (Primary Only)",
        4 => "仅副端口 (Secondary Only)",
        _ => "未知 (Unknown)",
    }
}

// ==================== 带状态的访问权限描述 ====================

/// 获取当前状态下的访问权限描述
pub fn access_description_with_state(access: u16, state: EcState) -> String {
    let can_read = match state {
        EcState::PreOp => (access & 0x01) != 0,
        EcState::SafeOp => (access & 0x02) != 0,
        EcState::Operational => (access & 0x04) != 0,
        _ => false,
    };
    let can_write = can_write_in_state(access, state);

    let state_name = match state {
        EcState::PreOp => "PreOP",
        EcState::SafeOp => "SafeOP",
        EcState::Operational => "OP",
        _ => "N/A",
    };

    if can_read && can_write {
        format!("{}: 可读写", state_name)
    } else if can_read {
        format!("{}: 只读", state_name)
    } else if can_write {
        format!("{}: 只写", state_name)
    } else {
        format!("{}: 不可访问", state_name)
    }
}

// ==================== CiA 402 状态/模式描述 ====================

/// 获取 CiA 402 驱动器状态描述
pub fn cia402_state_description(state: u8) -> &'static str {
    match state {
        0 => "初始化中 (Not Ready to Switch On)",
        1 => "驱动禁用 (Switch On Disabled)",
        2 => "准备就绪 (Ready to Switch On)",
        3 => "已开启 (Switched On)",
        4 => "运行使能 (Operation Enabled)",
        5 => "快速停止 (Quick Stop Active)",
        6 => "故障反应中 (Fault Reaction Active)",
        7 => "故障 (Fault)",
        _ => "未知 (Unknown)",
    }
}

/// 获取 CiA 402 操作模式描述
pub fn cia402_mode_description(mode: i8) -> &'static str {
    match mode {
        1 => "轮廓位置 (PP)",
        2 => "速度 (VL)",
        3 => "轮廓速度 (PV)",
        4 => "轮廓转矩 (PT)",
        6 => "回零 (HM)",
        7 => "插补位置 (IP)",
        8 => "周期同步位置 (CSP)",
        9 => "周期同步速度 (CSV)",
        10 => "周期同步转矩 (CST)",
        11 => "周期同步转矩加速度 (CSTCA)",
        _ => "未知 (Unknown)",
    }
}

// ==================== FSoE 状态/错误描述 ====================

/// 获取 FSoE 状态描述
pub fn fsoe_state_description(state: FsoeState) -> &'static str {
    match state {
        FsoeState::Reset => "复位 (Reset)",
        FsoeState::Session => "会话 (Session)",
        FsoeState::Connection => "连接 (Connection)",
        FsoeState::Parameter => "参数 (Parameter)",
        FsoeState::Data => "数据传输 (Data)",
        FsoeState::Failsafe => "失效安全 (Failsafe)",
    }
}

/// 获取 FSoE 错误描述
pub fn fsoe_error_description(error: FsoeError) -> &'static str {
    match error {
        FsoeError::None => "无错误",
        FsoeError::WrongCommand => "错误的命令",
        FsoeError::UnknownCommand => "未知命令",
        FsoeError::WrongConnId => "连接 ID 不匹配",
        FsoeError::Crc => "CRC 校验失败",
        FsoeError::Watchdog => "看门狗超时",
        FsoeError::WrongAddress => "错误的 FSoE 地址",
        FsoeError::WrongData => "无效数据",
        FsoeError::CommParamLength => "通信参数长度错误",
        FsoeError::CommParam => "通信参数错误",
        FsoeError::AppParamLength => "应用参数长度错误",
        FsoeError::AppParam => "应用参数错误",
        FsoeError::UnexpectedSession => "意外的会话命令",
        FsoeError::FailsafeData => "收到失效安全数据",
        FsoeError::NotInitialized => "FSoE 未初始化",
        FsoeError::MaxConnections => "达到最大连接数",
        FsoeError::InvalidState => "无效状态转换",
    }
}

// ==================== 端口/拓扑/PDI/设备/SM/FMMU 类型描述 ====================

/// 获取端口类型描述
pub fn port_type_description(port_type: u8) -> &'static str {
    match port_type {
        0 => "未使用 (Not Used)",
        1 => "MII",
        2 => "EBUS",
        3 => "EBUS+",
        _ => "未知 (Unknown)",
    }
}

/// 获取拓扑类型描述
pub fn topology_type_description(topo: u8) -> &'static str {
    match topo {
        0 => "无链接 (No Link)",
        1 => "端点 (End Point)",
        2 => "中间节点 (Line)",
        3 => "分支点 (Fork)",
        4 => "交叉点 (Cross)",
        _ => "未知 (Unknown)",
    }
}

/// 获取 PDI 类型描述
pub fn pdi_type_description(pdi: u8) -> &'static str {
    match pdi {
        0x00 => "无PDI (Interface deactivated)",
        0x01 => "4个数字输入",
        0x02 => "4个数字输出",
        0x03 => "2个数字输入 + 2个数字输出",
        0x04 => "数字I/O",
        0x05 => "SPI从站",
        0x06 => "过载保护输出",
        0x07 => "ESC同步管理器",
        0x08 => "uC async. 16bit",
        0x09 => "uC async. 8bit",
        0x0A => "uC sync. 16bit",
        0x0B => "uC sync. 8bit",
        0x10 => "32个数字输入/输出",
        0x11 => "24个数字输入 + 8个数字输出",
        0x12 => "16个数字输入 + 16个数字输出",
        0x13 => "8个数字输入 + 24个数字输出",
        0x80 => "On-chip bus",
        _ => "未知类型",
    }
}

/// 获取设备类型描述
pub fn device_type_description(dtype: u16) -> &'static str {
    match dtype {
        0 => "未定义",
        1 => "静态设备(无IO映射)",
        2 => "输入设备(无邮箱)",
        3 => "输出设备(无邮箱)",
        4 => "输入设备(有邮箱)",
        5 => "输出设备(有邮箱)",
        6 => "输入输出设备(无邮箱)",
        7 => "输入输出设备(有邮箱)",
        _ => "未知设备类型",
    }
}

/// 获取 SyncManager 类型描述
pub fn sync_manager_type_description(sm: u8) -> &'static str {
    match sm {
        0 => "未使用",
        1 => "邮箱输出 (MbxOut)",
        2 => "邮箱输入 (MbxIn)",
        3 => "过程数据输出 (RxPDO)",
        4 => "过程数据输入 (TxPDO)",
        _ => "未知",
    }
}

/// 获取 FMMU 类型描述
pub fn fmmu_type_description(fmmu: u8) -> &'static str {
    match fmmu {
        0 => "未使用",
        1 => "输出 (Logical->Physical)",
        2 => "输入 (Physical->Logical)",
        3 => "同步管理器状态",
        _ => "未知",
    }
}

// ==================== CoE/EoE 详情描述 ====================

/// 获取 CoE 详情标志位描述
pub fn coe_details_description(details: u8) -> String {
    let mut parts = Vec::new();
    if details & 0x01 != 0 { parts.push("SDO"); }
    if details & 0x02 != 0 { parts.push("SDO Info"); }
    if details & 0x04 != 0 { parts.push("PDO Assign"); }
    if details & 0x08 != 0 { parts.push("PDO Config"); }
    if details & 0x10 != 0 { parts.push("Startup"); }
    if details & 0x20 != 0 { parts.push("Complete Access"); }
    if parts.is_empty() {
        "无".to_string()
    } else {
        parts.join(", ")
    }
}

/// 获取 EoE 详情标志位描述
pub fn eoe_details_description(details: u8) -> String {
    let mut parts = Vec::new();
    if details & 0x01 != 0 { parts.push("发送帧"); }
    if details & 0x02 != 0 { parts.push("接收帧"); }
    if details & 0x04 != 0 { parts.push("设置IP"); }
    if details & 0x08 != 0 { parts.push("获取IP"); }
    if parts.is_empty() {
        "无".to_string()
    } else {
        parts.join(", ")
    }
}

// ==================== 数据类型描述 ====================

/// 获取 EcDataType 的完整类型名
pub fn data_type_name(dt: u16) -> &'static str {
    match dt {
        0x0001 => "Boolean",
        0x0002 => "Integer8",
        0x0003 => "Integer16",
        0x0004 => "Integer32",
        0x0005 => "Unsigned8",
        0x0006 => "Unsigned16",
        0x0007 => "Unsigned32",
        0x0008 => "Real32",
        0x0009 => "VisibleString",
        0x000A => "OctetString",
        0x000B => "UnicodeString",
        0x000C => "TimeOfDay",
        0x000D => "TimeDifference",
        0x000F => "Domain",
        0x0010 => "Integer24",
        0x0011 => "Real64",
        0x0012 => "Integer40",
        0x0013 => "Integer48",
        0x0014 => "Integer56",
        0x0015 => "Integer64",
        0x0016 => "Unsigned24",
        0x0018 => "Unsigned40",
        0x0019 => "Unsigned48",
        0x001A => "Unsigned56",
        0x001B => "Unsigned64",
        0x0030 => "Bit1",
        0x0031 => "Bit2",
        0x0032 => "Bit3",
        0x0033 => "Bit4",
        0x0034 => "Bit5",
        0x0035 => "Bit6",
        0x0036 => "Bit7",
        0x0037 => "Bit8",
        _ => "Unknown",
    }
}
