
use std::fmt;

#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcErr {

    Ok = 0,

    AlreadyInitialized = 1,

    NotInitialized = 2,

    Timeout = 3,

    NoSlaves = 4,

    Nok = 5,
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcState {

    None = 0x00,

    Init = 0x01,

    PreOp = 0x02,

    Boot = 0x03,

    SafeOp = 0x04,

    OP = 0x08,

    Ack = 0x10,
}

impl EcState {

    pub fn from_value(v: u8) -> Self {
        match v & 0x0F {
            0x00 => Self::None,
            0x01 => Self::Init,
            0x02 => Self::PreOp,
            0x03 => Self::Boot,
            0x04 => Self::SafeOp,
            0x08 => Self::OP,
            _ => Self::None,
        }
    }

    pub fn has_error(raw: u8) -> bool {
        (raw & 0x10) != 0
    }

    pub fn format(raw: u8) -> String {
        let base = Self::from_value(raw);
        let name = match base {
            Self::None => "None",
            Self::Init => "Init",
            Self::PreOp => "PreOp",
            Self::Boot => "Boot",
            Self::SafeOp => "SafeOp",
            Self::OP => "OP",
            Self::Ack => "Ack",
        };
        if Self::has_error(raw) {
            format!("{}+Error", name)
        } else {
            name.to_string()
        }
    }
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcLinkState {

    Disconnected = 0,

    Connected = 1,

    PrimaryOnly = 3,

    SecondaryOnly = 4,
}

impl EcLinkState {

    pub fn from_value(v: u8) -> Self {
        match v {
            0 => Self::Disconnected,
            1 => Self::Connected,
            3 => Self::PrimaryOnly,
            4 => Self::SecondaryOnly,
            _ => Self::Disconnected,
        }
    }
}

#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RingMode {

    Inactive = 0,

    Dual = 1,

    Degraded = 2,
}

impl RingMode {

    pub fn from_value(v: i32) -> Self {
        match v {
            0 => Self::Inactive,
            1 => Self::Dual,
            2 => Self::Degraded,
            _ => Self::Inactive,
        }
    }
}

#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcBufState {

    Empty = 0x00,

    Alloc = 0x01,

    Tx = 0x02,

    Rcvd = 0x03,

    Complete = 0x04,
}

#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcDataType {

    Boolean = 0x0001,

    Integer8 = 0x0002,

    Integer16 = 0x0003,

    Integer32 = 0x0004,

    Unsigned8 = 0x0005,

    Unsigned16 = 0x0006,

    Unsigned32 = 0x0007,

    Real32 = 0x0008,

    VisibleString = 0x0009,

    OctetString = 0x000A,

    UnicodeString = 0x000B,

    TimeOfDay = 0x000C,

    TimeDifference = 0x000D,

    Domain = 0x000F,

    Integer24 = 0x0010,

    Real64 = 0x0011,

    Integer40 = 0x0012,

    Integer48 = 0x0013,

    Integer56 = 0x0014,

    Integer64 = 0x0015,

    Unsigned24 = 0x0016,

    Unsigned40 = 0x0018,

    Unsigned48 = 0x0019,

    Unsigned56 = 0x001A,

    Unsigned64 = 0x001B,

    Bit1 = 0x0030,

    Bit2 = 0x0031,

    Bit3 = 0x0032,

    Bit4 = 0x0033,

    Bit5 = 0x0034,

    Bit6 = 0x0035,

    Bit7 = 0x0036,

    Bit8 = 0x0037,
}

#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcCmdType {

    Nop = 0x00,

    Aprd = 0x01,

    Apwr = 0x02,

    Aprw = 0x03,

    Fprd = 0x04,

    Fpwr = 0x05,

    Fprw = 0x06,

    Brd = 0x07,

    Bwr = 0x08,

    Brw = 0x09,

    Lrd = 0x0A,

    Lwr = 0x0B,

    Lrw = 0x0C,

    Armw = 0x0D,

    Frmw = 0x0E,
}

#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcEcmdType {

    Nop = 0x0000,

    Read = 0x0100,

    Write = 0x0201,

    Reload = 0x0300,
}

#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogLevel {

    Info = 0,

    Debug = 1,

    Warn = 2,

    Error = 3,

    Fatal = 4,
}

#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcALState {

    NoError = 0x0000,

    UnspecifiedError = 0x0001,

    NoMemory = 0x0002,

    InvalidDeviceSetup = 0x0003,

    InvalidRevision = 0x0004,

    SiiEepromMismatch = 0x0006,

    FirmwareUpdateFailed = 0x0007,

    LicenseError = 0x000E,

    InvalidStateChange = 0x0011,

    UnknownRequestedState = 0x0012,

    BootstrapNotSupported = 0x0013,

    NoValidFirmware = 0x0014,

    InvalidMailboxConfig = 0x0015,

    InvalidMailBoxConfig = 0x0016,

    InvalidSyncManagerConfig = 0x0017,

    NoValidInputs = 0x0018,

    NoValidOutputs = 0x0019,

    SyncError = 0x001A,

    SyncManagerWatchdog = 0x001B,

    InvalidSyncManagerTypes = 0x001C,

    InvalidOutputConfig = 0x001D,

    InvalidInputConfig = 0x001E,

    InvalidWatchdogConfig = 0x001F,

    NeedsColdStart = 0x0020,

    NeedsInit = 0x0021,

    NeedsPreOp = 0x0022,

    NeedsSafeOp = 0x0023,

    InvalidInputMapping = 0x0024,

    InvalidOutputMapping = 0x0025,

    InconsistentSettings = 0x0026,

    FreerunNotSupported = 0x0027,

    SyncNotSupported = 0x0028,

    FreerunNeeds3Buffer = 0x0029,

    BackgroundWatchdog = 0x002A,

    NoValidIO = 0x002B,

    FatalSyncError = 0x002C,

    NoSyncError = 0x002D,

    CycleTimeTooSmall = 0x002E,

    InvalidDcSyncConfig = 0x0030,

    InvalidDcLatch = 0x0031,

    PllError = 0x0032,

    DcSyncIOError = 0x0033,

    DcSyncTimeout = 0x0034,

    InvalidDcSyncCycle = 0x0035,

    InvalidSync0Cycle = 0x0036,

    InvalidSync1Cycle = 0x0037,

    MailboxAoe = 0x0041,

    MailboxEoe = 0x0042,

    MailboxCoe = 0x0043,

    MailboxFoe = 0x0044,

    MailboxSoe = 0x0045,

    MailboxVoe = 0x004F,

    EepromNoAccess = 0x0050,

    EepromError = 0x0051,

    ExternalHardwareNotReady = 0x0052,

    SlaveRestarted = 0x0060,

    DeviceIdUpdated = 0x0061,

    ModuleIdentListMismatch = 0x0070,

    SupplyVoltageTooLow = 0x0080,

    SupplyVoltageTooHigh = 0x0081,

    TemperatureTooLow = 0x0082,

    TemperatureTooHigh = 0x0083,

    AppControllerAvailable = 0x00F0,

    Unknown = 0xFFFF,
}

impl EcALState {

    pub fn from_value(v: i32) -> Self {
        match v {
            0x0000 => Self::NoError,
            0x0001 => Self::UnspecifiedError,
            0x0002 => Self::NoMemory,
            0x0003 => Self::InvalidDeviceSetup,
            0x0004 => Self::InvalidRevision,
            0x0006 => Self::SiiEepromMismatch,
            0x0007 => Self::FirmwareUpdateFailed,
            0x000E => Self::LicenseError,
            0x0011 => Self::InvalidStateChange,
            0x0012 => Self::UnknownRequestedState,
            0x0013 => Self::BootstrapNotSupported,
            0x0014 => Self::NoValidFirmware,
            0x0015 => Self::InvalidMailboxConfig,
            0x0016 => Self::InvalidMailBoxConfig,
            0x0017 => Self::InvalidSyncManagerConfig,
            0x0018 => Self::NoValidInputs,
            0x0019 => Self::NoValidOutputs,
            0x001A => Self::SyncError,
            0x001B => Self::SyncManagerWatchdog,
            0x001C => Self::InvalidSyncManagerTypes,
            0x001D => Self::InvalidOutputConfig,
            0x001E => Self::InvalidInputConfig,
            0x001F => Self::InvalidWatchdogConfig,
            0x0020 => Self::NeedsColdStart,
            0x0021 => Self::NeedsInit,
            0x0022 => Self::NeedsPreOp,
            0x0023 => Self::NeedsSafeOp,
            0x0024 => Self::InvalidInputMapping,
            0x0025 => Self::InvalidOutputMapping,
            0x0026 => Self::InconsistentSettings,
            0x0027 => Self::FreerunNotSupported,
            0x0028 => Self::SyncNotSupported,
            0x0029 => Self::FreerunNeeds3Buffer,
            0x002A => Self::BackgroundWatchdog,
            0x002B => Self::NoValidIO,
            0x002C => Self::FatalSyncError,
            0x002D => Self::NoSyncError,
            0x002E => Self::CycleTimeTooSmall,
            0x0030 => Self::InvalidDcSyncConfig,
            0x0031 => Self::InvalidDcLatch,
            0x0032 => Self::PllError,
            0x0033 => Self::DcSyncIOError,
            0x0034 => Self::DcSyncTimeout,
            0x0035 => Self::InvalidDcSyncCycle,
            0x0036 => Self::InvalidSync0Cycle,
            0x0037 => Self::InvalidSync1Cycle,
            0x0041 => Self::MailboxAoe,
            0x0042 => Self::MailboxEoe,
            0x0043 => Self::MailboxCoe,
            0x0044 => Self::MailboxFoe,
            0x0045 => Self::MailboxSoe,
            0x004F => Self::MailboxVoe,
            0x0050 => Self::EepromNoAccess,
            0x0051 => Self::EepromError,
            0x0052 => Self::ExternalHardwareNotReady,
            0x0060 => Self::SlaveRestarted,
            0x0061 => Self::DeviceIdUpdated,
            0x0070 => Self::ModuleIdentListMismatch,
            0x0080 => Self::SupplyVoltageTooLow,
            0x0081 => Self::SupplyVoltageTooHigh,
            0x0082 => Self::TemperatureTooLow,
            0x0083 => Self::TemperatureTooHigh,
            0x00F0 => Self::AppControllerAvailable,
            _ => Self::Unknown,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ALErrorCategory {

    None,

    Transient,

    Configuration,

    Hardware,

    Unknown,
}

pub fn classify_al_error(code: u16) -> ALErrorCategory {
    match code {
        0x0000 => ALErrorCategory::None,

        0x0011 |
        0x001B |
        0x001D |
        0x0032 |
        0x0033 |
        0x0034
            => ALErrorCategory::Transient,

        0x0015 |
        0x0016 |
        0x0017 |
        0x0018 |
        0x0019 |
        0x001A |
        0x001C |
        0x001E |
        0x001F |
        0x0030 |
        0x0031
            => ALErrorCategory::Configuration,

        0x0042 |
        0x0043 |
        0x0050 |
        0x0051 |
        0x0060
            => ALErrorCategory::Hardware,
        _ => {
            if code >= 0x0040 && code <= 0x005F {
                ALErrorCategory::Hardware
            } else {
                ALErrorCategory::Unknown
            }
        }
    }
}

#[derive(Debug, Clone)]
pub struct DiagnosticMessage {

    pub sub_index: u8,

    pub diag_code: u32,

    pub flags: u16,

    pub text_index: u16,

    pub raw_data: Vec<u8>,
}

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

#[derive(Debug, Clone, Default)]
pub struct SlaveErrorCounters {

    pub slave_index: i32,

    pub port0_crc_errors: u32,

    pub port1_crc_errors: u32,

    pub port2_crc_errors: u32,

    pub port3_crc_errors: u32,

    pub frame_errors: u32,

    pub lost_frames: u32,
}

#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MailboxError {

    NoError = 0x0000,

    SyntaxError = 0x0001,

    ProtocolNotSupported = 0x0002,

    WrongChannelField = 0x0003,

    ServiceNotSupported = 0x0004,

    InvalidMailboxHeader = 0x0005,

    DataTooShort = 0x0006,

    NoMemoryInSlave = 0x0007,

    InconsistentDataLength = 0x0008,

    UnknownError = 0xFFFF,
}

#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MailboxType {

    ErrorMailbox = 0x00,

    ADSOverEtherCAT = 0x01,

    EthernetOverEtherCAT = 0x02,

    CANopenOverEtherCAT = 0x03,

    FileOverEtherCAT = 0x04,

    ServoOverEtherCAT = 0x05,

    VendorOverEtherCAT = 0x0F,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CoEObjectAccess(pub u16);

impl CoEObjectAccess {

    pub const NONE: Self = Self(0x00);

    pub const READ_PRE_OP: Self = Self(0x01);

    pub const READ_SAFE_OP: Self = Self(0x02);

    pub const READ_OP: Self = Self(0x04);

    pub const WRITE_PRE_OP: Self = Self(0x08);

    pub const WRITE_SAFE_OP: Self = Self(0x10);

    pub const WRITE_OP: Self = Self(0x20);

    pub const READ_ANY: Self = Self(0x07);

    pub const WRITE_ANY: Self = Self(0x38);

    pub const READ_WRITE_ALL: Self = Self(0x3F);

    pub fn contains(self, other: Self) -> bool {
        (self.0 & other.0) == other.0
    }

    pub fn union(self, other: Self) -> Self {
        Self(self.0 | other.0)
    }
}

#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SDOError {

    NoError = 0x00000000,

    ToggleBitNotChanged = 0x05030000,

    SDOProtocolTimeout = 0x05040000,

    InvalidCommandSpecifier = 0x05040001,

    OutOfMemory = 0x05040005,

    UnsupportedAccess = 0x06010000,

    ReadWriteOnlyError = 0x06010001,

    WriteReadOnlyError = 0x06010002,

    SubindexWriteError = 0x06010003,

    CompleteAccessNotSupported = 0x06010004,

    ObjectLengthExceeded = 0x06010005,

    ObjectMappedToRxPDO = 0x06010006,

    ObjectDoesNotExist = 0x06020000,

    CannotBeMappedToPDO = 0x06040041,

    ExceedsPDOLength = 0x06040042,

    ParameterIncompatibility = 0x06040043,

    InternalIncompatibility = 0x06040047,

    HardwareAccessError = 0x06060000,

    DataTypeMismatch = 0x06070010,

    DataTypeTooHigh = 0x06070012,

    DataTypeTooLow = 0x06070013,

    SubindexDoesNotExist = 0x06090011,

    ValueRangeExceeded = 0x06090030,

    ValueTooHigh = 0x06090031,

    ValueTooLow = 0x06090032,

    ModuleIdentListMismatch = 0x06090033,

    MaxLessThanMin = 0x06090036,

    GeneralError = 0x08000000,

    DataTransferError = 0x08000020,

    LocalControlError = 0x08000021,

    DeviceStateError = 0x08000022,

    DictionaryError = 0x08000023,

    UnknownError = 0xFFFFFFFF,
}

impl SDOError {

    pub fn from_u32(val: u32) -> Self {
        match val {
            0x00000000 => Self::NoError,
            0x05030000 => Self::ToggleBitNotChanged,
            0x05040000 => Self::SDOProtocolTimeout,
            0x05040001 => Self::InvalidCommandSpecifier,
            0x05040005 => Self::OutOfMemory,
            0x06010000 => Self::UnsupportedAccess,
            0x06010001 => Self::ReadWriteOnlyError,
            0x06010002 => Self::WriteReadOnlyError,
            0x06010003 => Self::SubindexWriteError,
            0x06010004 => Self::CompleteAccessNotSupported,
            0x06010005 => Self::ObjectLengthExceeded,
            0x06010006 => Self::ObjectMappedToRxPDO,
            0x06020000 => Self::ObjectDoesNotExist,
            0x06040041 => Self::CannotBeMappedToPDO,
            0x06040042 => Self::ExceedsPDOLength,
            0x06040043 => Self::ParameterIncompatibility,
            0x06040047 => Self::InternalIncompatibility,
            0x06060000 => Self::HardwareAccessError,
            0x06070010 => Self::DataTypeMismatch,
            0x06070012 => Self::DataTypeTooHigh,
            0x06070013 => Self::DataTypeTooLow,
            0x06090011 => Self::SubindexDoesNotExist,
            0x06090030 => Self::ValueRangeExceeded,
            0x06090031 => Self::ValueTooHigh,
            0x06090032 => Self::ValueTooLow,
            0x06090033 => Self::ModuleIdentListMismatch,
            0x06090036 => Self::MaxLessThanMin,
            0x08000000 => Self::GeneralError,
            0x08000020 => Self::DataTransferError,
            0x08000021 => Self::LocalControlError,
            0x08000022 => Self::DeviceStateError,
            0x08000023 => Self::DictionaryError,
            _ => Self::UnknownError,
        }
    }
}

#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SoEError {

    NoError = 0x0000,

    NoIDN = 0x1001,

    InvalidAccessToElement1 = 0x1009,

    NoName = 0x2001,

    NameTransmissionTooShort = 0x2002,

    NameTransmissionTooLong = 0x2003,

    NameCannotBeChanged = 0x2004,

    NameWriteProtected = 0x2005,

    AttributeTransmissionTooShort = 0x3002,

    AttributeTransmissionTooLong = 0x3003,

    AttributeCannotBeChanged = 0x3004,

    AttributeWriteProtected = 0x3005,

    NoUnits = 0x4001,

    UnitTransmissionTooShort = 0x4002,

    UnitTransmissionTooLong = 0x4003,

    UnitCannotBeChanged = 0x4004,

    UnitWriteProtected = 0x4005,

    NoMinimumInputValue = 0x5001,

    MinimumInputValueTransmissionTooShort = 0x5002,

    MinimumInputValueTransmissionTooLong = 0x5003,

    MinimumInputValueCannotBeChanged = 0x5004,

    MinimumInputValueWriteProtected = 0x5005,

    NoMaximumInputValue = 0x6001,

    MaximumInputValueTransmissionTooShort = 0x6002,

    MaximumInputValueTransmissionTooLong = 0x6003,

    MaximumInputValueCannotBeChanged = 0x6004,

    MaximumInputValueWriteProtected = 0x6005,

    NoOperationData = 0x7001,

    OperationDataTransmissionTooShort = 0x7002,

    OperationDataTransmissionTooLong = 0x7003,

    OperationDataCannotBeChanged = 0x7004,

    OperationDataWriteProtectedByState = 0x7005,

    OperationDataSmallerThanMinimum = 0x7006,

    OperationDataGreaterThanMaximum = 0x7007,

    InvalidOperationDataConfiguredIDNNotSupported = 0x7008,

    OperationDataWriteProtectedByPassword = 0x7009,

    OperationDataCyclicallyWriteProtected = 0x700A,

    InvalidIndirectAddressing = 0x700B,

    OperationDataWriteProtectedBySettings = 0x700C,

    Reserved700D = 0x700D,

    ProcedureCommandAlreadyActive = 0x7010,

    ProcedureCommandNotInterruptible = 0x7011,

    ProcedureCommandNotExecutableState = 0x7012,

    ProcedureCommandNotExecutableInvalidParameters = 0x7013,

    NoDataState = 0x7014,

    NoDefaultValue = 0x8001,

    DefaultValueTransmissionTooLong = 0x8002,

    DefaultValueTransmissionTooShort = 0x8003,

    DefaultValueCannotBeChanged = 0x8004,

    DefaultValueWriteProtected = 0x8005,

    InvalidDriveNumber = 0x800A,

    GeneralError = 0x800B,

    NoElementAddressed = 0x800C,

    Unknown = 0xFFFF,
}

#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcCommType {

    MailboxTx = 0,

    MailboxRx = 1,

    SdoRead = 2,

    SdoWrite = 3,

    Pdo = 4,

    Register = 5,

    Eeprom = 6,

    Dc = 7,

    Count = 8,
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcPortType {

    NotUsed = 0,

    MII = 1,

    EBUS = 2,

    EBUSEnhanced = 3,
}

impl fmt::Display for EcPortType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let desc = match self {
            Self::NotUsed => "未使用",
            Self::MII => "MII",
            Self::EBUS => "EBUS",
            Self::EBUSEnhanced => "EBUS+",
        };
        write!(f, "{}", desc)
    }
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcTopologyType {

    NoLink = 0,

    EndPoint = 1,

    Line = 2,

    Fork = 3,

    Cross = 4,
}

impl fmt::Display for EcTopologyType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let desc = match self {
            Self::NoLink => "无链接 (No Link)",
            Self::EndPoint => "端点 (End Point)",
            Self::Line => "中间节点 (Line)",
            Self::Fork => "分支点 (Fork)",
            Self::Cross => "交叉点 (Cross)",
        };
        write!(f, "{}", desc)
    }
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcPdiType {

    None = 0x00,

    DigitalInput4 = 0x01,

    DigitalOutput4 = 0x02,

    DigitalIO2x2 = 0x03,

    DigitalIO = 0x04,

    SPISlave = 0x05,

    OverloadOutput = 0x06,

    EscSyncManager = 0x07,

    UCAsync16 = 0x08,

    UCAsync8 = 0x09,

    UCSync16 = 0x0A,

    UCSync8 = 0x0B,

    DigitalIO32 = 0x10,

    DigitalIO24x8 = 0x11,

    DigitalIO16x16 = 0x12,

    DigitalIO8x24 = 0x13,

    OnChipBus = 0x80,

    Unknown = 0xFF,
}

impl fmt::Display for EcPdiType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let desc = match self {
            Self::None => "无PDI (Interface deactivated)",
            Self::DigitalInput4 => "4个数字输入",
            Self::DigitalOutput4 => "4个数字输出",
            Self::DigitalIO2x2 => "2个数字输入 + 2个数字输出",
            Self::DigitalIO => "数字I/O",
            Self::SPISlave => "SPI从站",
            Self::OverloadOutput => "过载保护输出",
            Self::EscSyncManager => "ESC同步管理器",
            Self::UCAsync16 => "uC async. 16bit",
            Self::UCAsync8 => "uC async. 8bit",
            Self::UCSync16 => "uC sync. 16bit",
            Self::UCSync8 => "uC sync. 8bit",
            Self::DigitalIO32 => "32个数字输入/输出",
            Self::DigitalIO24x8 => "24个数字输入 + 8个数字输出",
            Self::DigitalIO16x16 => "16个数字输入 + 16个数字输出",
            Self::DigitalIO8x24 => "8个数字输入 + 24个数字输出",
            Self::OnChipBus => "On-chip bus",
            Self::Unknown => "未知类型",
        };
        write!(f, "{}", desc)
    }
}

#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcDeviceType {

    Undefined = 0,

    Static = 1,

    InputNoMailbox = 2,

    OutputNoMailbox = 3,

    InputWithMailbox = 4,

    OutputWithMailbox = 5,

    IONoMailbox = 6,

    IOWithMailbox = 7,
}

impl fmt::Display for EcDeviceType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let desc = match self {
            Self::Undefined => "未定义",
            Self::Static => "静态设备(无IO映射)",
            Self::InputNoMailbox => "输入设备(无邮箱)",
            Self::OutputNoMailbox => "输出设备(无邮箱)",
            Self::InputWithMailbox => "输入设备(有邮箱)",
            Self::OutputWithMailbox => "输出设备(有邮箱)",
            Self::IONoMailbox => "输入输出设备(无邮箱)",
            Self::IOWithMailbox => "输入输出设备(有邮箱)",
        };
        write!(f, "{}", desc)
    }
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcSyncManagerType {

    NotUsed = 0,

    MailboxOut = 1,

    MailboxIn = 2,

    ProcessDataOut = 3,

    ProcessDataIn = 4,
}

impl fmt::Display for EcSyncManagerType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let desc = match self {
            Self::NotUsed => "未使用",
            Self::MailboxOut => "邮箱输出",
            Self::MailboxIn => "邮箱输入",
            Self::ProcessDataOut => "过程数据输出",
            Self::ProcessDataIn => "过程数据输入",
        };
        write!(f, "{}", desc)
    }
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcFmmuType {

    NotUsed = 0,

    Output = 1,

    Input = 2,

    SyncManagerStatus = 3,
}

impl fmt::Display for EcFmmuType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let desc = match self {
            Self::NotUsed => "未使用",
            Self::Output => "输出",
            Self::Input => "输入",
            Self::SyncManagerStatus => "同步管理器状态",
        };
        write!(f, "{}", desc)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EcCoEDetails(pub u8);

impl EcCoEDetails {

    pub const NONE: Self = Self(0x00);

    pub const SDO: Self = Self(0x01);

    pub const SDO_INFO: Self = Self(0x02);

    pub const PDO_ASSIGN: Self = Self(0x04);

    pub const PDO_CONFIG: Self = Self(0x08);

    pub const STARTUP: Self = Self(0x10);

    pub const COMPLETE_ACCESS: Self = Self(0x20);

    pub fn contains(self, other: Self) -> bool {
        (self.0 & other.0) == other.0
    }

    pub fn union(self, other: Self) -> Self {
        Self(self.0 | other.0)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EcEoEDetails(pub u8);

impl EcEoEDetails {

    pub const NONE: Self = Self(0x00);

    pub const SEND_FRAME: Self = Self(0x01);

    pub const RECEIVE_FRAME: Self = Self(0x02);

    pub const SET_IP_PARAM: Self = Self(0x04);

    pub const GET_IP_PARAM: Self = Self(0x08);

    pub fn contains(self, other: Self) -> bool {
        (self.0 & other.0) == other.0
    }

    pub fn union(self, other: Self) -> Self {
        Self(self.0 | other.0)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcStateFormat {

    Short,

    Long,

    WithHex,
}

pub fn format_ec_state(state: u8, format: EcStateFormat) -> String {
    let (short, long) = match state & 0x0F {
        0x01 => ("INIT", "Init"),
        0x02 => ("PREOP", "Pre-Operational"),
        0x03 => ("BOOT", "Bootstrap"),
        0x04 => ("SAFEOP", "Safe-Operational"),
        0x08 => ("OP", "Operational"),
        _ => ("???", "Unknown"),
    };
    let err = if state & 0x10 != 0 { "+ERR" } else { "" };
    match format {
        EcStateFormat::Short => format!("{}{}", short, err),
        EcStateFormat::Long => format!("{}{}", long, err),
        EcStateFormat::WithHex => format!("{}{} (0x{:02X})", short, err, state),
    }
}

#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DcSyncMode {

    FreeRun = 0,

    SmSynchron = 1,

    DcSynchron = 2,

    DcSynchron01 = 3,
}

#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StartupTransition {

    IP = 0,

    PS = 1,

    SO = 2,

    OS = 3,

    SP = 4,

    PI = 5,
}

#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StartupWriteTiming {

    BeforeTransition = 0,

    AfterTransition = 1,
}

pub mod esm_timeouts {

    pub const INIT_TO_PRE_OP: i32 = 2000;

    pub const PRE_OP_TO_SAFE_OP: i32 = 3000;

    pub const SAFE_OP_TO_OP: i32 = 8000;

    pub const OP_TO_SAFE_OP: i32 = 200;

    pub const SAFE_OP_TO_PRE_OP: i32 = 200;

    pub const PRE_OP_TO_INIT: i32 = 200;

    pub const TO_BOOT: i32 = 3000;

    pub const BOOT_TO_INIT: i32 = 3000;

    pub const SDO_TIMEOUT: i32 = 3000;

    pub const MAILBOX_TIMEOUT: i32 = 5000;

    pub const STATE_STABILIZE_DELAY: i32 = 100;

    pub fn get_transition_timeout(current: u8, target: u8) -> i32 {
        match (current, target) {
            (0x01, 0x02) => INIT_TO_PRE_OP,
            (0x02, 0x04) => PRE_OP_TO_SAFE_OP,
            (0x04, 0x08) => SAFE_OP_TO_OP,
            (0x08, 0x04) => OP_TO_SAFE_OP,
            (0x04, 0x02) => SAFE_OP_TO_PRE_OP,
            (0x02, 0x01) => PRE_OP_TO_INIT,
            (_, 0x03) => TO_BOOT,
            (0x03, 0x01) => BOOT_TO_INIT,
            _ => 3000,
        }
    }
}

#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StateCiA402 {

    NotReadyToSwitchOn = 0,

    SwitchOnDisabled = 1,

    ReadyToSwitchOn = 2,

    SwitchedOn = 3,

    OperationEnabled = 4,

    QuickStopActive = 5,

    FaultReactionActive = 6,

    Fault = 7,

    Unknown = 99,
}

#[repr(i8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModeCiA402 {

    PP = 1,

    VL = 2,

    PV = 3,

    PT = 4,

    HM = 6,

    IP = 7,

    CSP = 8,

    CSV = 9,

    CST = 10,

    CSTCA = 11,
}

#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogCategory {

    Error = 0,

    Warning = 1,

    Message = 2,

    Mailbox = 3,

    PDO = 4,

    Debug = 5,

    Local = 6,
}

#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LicenseStatus {

    NotVerified = 0,

    Verifying = 1,

    Verified = 2,

    Failed = 3,

    Expired = 4,

    MachineIdMismatch = 5,
}
