"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseSiiBuffer = exports.resolveSiiCategoryType = exports.SiiCategoryType = void 0;
const python_struct_1 = require("python-struct");
var SiiCategoryType;
(function (SiiCategoryType) {
    SiiCategoryType[SiiCategoryType["NOP"] = 0] = "NOP";
    SiiCategoryType[SiiCategoryType["DEVICE_SPECIFIC"] = 1] = "DEVICE_SPECIFIC";
    SiiCategoryType[SiiCategoryType["STRINGS"] = 10] = "STRINGS";
    SiiCategoryType[SiiCategoryType["DATA_TYPES"] = 20] = "DATA_TYPES";
    SiiCategoryType[SiiCategoryType["GENERAL"] = 30] = "GENERAL";
    SiiCategoryType[SiiCategoryType["FMMU"] = 40] = "FMMU";
    SiiCategoryType[SiiCategoryType["SYNC_M"] = 41] = "SYNC_M";
    SiiCategoryType[SiiCategoryType["FMMUX"] = 42] = "FMMUX";
    SiiCategoryType[SiiCategoryType["SYNC_UNIT"] = 43] = "SYNC_UNIT";
    SiiCategoryType[SiiCategoryType["TXPDO"] = 50] = "TXPDO";
    SiiCategoryType[SiiCategoryType["RXPDO"] = 51] = "RXPDO";
    SiiCategoryType[SiiCategoryType["DC"] = 60] = "DC";
    SiiCategoryType[SiiCategoryType["TIMEOUTS"] = 70] = "TIMEOUTS";
    SiiCategoryType[SiiCategoryType["DICTIONARY"] = 80] = "DICTIONARY";
    SiiCategoryType[SiiCategoryType["HARDWARE"] = 90] = "HARDWARE";
    SiiCategoryType[SiiCategoryType["VENDOR_INFORMATION"] = 100] = "VENDOR_INFORMATION";
    SiiCategoryType[SiiCategoryType["IMAGES"] = 110] = "IMAGES";
    SiiCategoryType[SiiCategoryType["VENDOR_SPECIFIC"] = 2048] = "VENDOR_SPECIFIC";
    SiiCategoryType[SiiCategoryType["APPLICATION_SPECIFIC"] = 8192] = "APPLICATION_SPECIFIC";
    SiiCategoryType[SiiCategoryType["END"] = 65535] = "END";
})(SiiCategoryType = exports.SiiCategoryType || (exports.SiiCategoryType = {}));
/**
 * Resolve a raw SII category type value into a normalized {@link SiiCategoryType}.
 *
 * @param value Raw 16-bit category type value from SII.
 * @returns Resolved category type, or `undefined` if not recognized.
 *
 * @remarks
 * - The SII specification defines both fixed category IDs and value ranges:
 *
 *   **Mapped ranges:**
 *   - 0x0001–0x0009 → DEVICE_SPECIFIC
 *   - 0x0800–0x1FFF → VENDOR_SPECIFIC
 *   - 0x2000–0x2FFF → APPLICATION_SPECIFIC
 *   - 0x3000–0xFFFE → VENDOR_SPECIFIC
 *
 * - For values outside these ranges, a direct enum lookup is attempted:
 *   - Matches known categories such as STRINGS, GENERAL, FMMU, etc.
 *
 * - The END marker (0xFFFF) is handled via direct enum lookup.
 *
 * @note
 * - Range-based values are normalized to a single enum representative
 *   (e.g. all vendor-specific values map to `VENDOR_SPECIFIC`).
 * - The original numeric value is not preserved.
 * - Unknown values return `undefined` and should be treated as unsupported categories.
 */
function resolveSiiCategoryType(value) {
    if (value >= 1 && value <= 9) {
        return SiiCategoryType.DEVICE_SPECIFIC;
    }
    else if (value >= 0x0800 && value <= 0x1fff) {
        return SiiCategoryType.VENDOR_SPECIFIC;
    }
    else if (value >= 0x2000 && value <= 0x2fff) {
        return SiiCategoryType.APPLICATION_SPECIFIC;
    }
    else if (value >= 0x3000 && value <= 0xfffe) {
        return SiiCategoryType.VENDOR_SPECIFIC;
    }
    if (SiiCategoryType[value] !== undefined) {
        return value;
    }
    return;
}
exports.resolveSiiCategoryType = resolveSiiCategoryType;
/**
 * Parse a raw EtherCAT Slave Information Interface (SII) EEPROM buffer.
 *
 * @param buffer Full SII EEPROM content.
 * @returns Parsed {@link SlaveInformationInterface}.
 *
 * @remarks
 * The SII structure consists of:
 *
 * 1. **Fixed header (first 128 bytes)**
 *    Parsed using:
 *    `@HHHHH4xHLLLL8xHHHHHHHHH66xHHHH`
 *
 *    Contains:
 *    - PDI configuration and control
 *    - Vendor/product identification
 *    - Mailbox configuration
 *    - EEPROM size and version
 *
 * 2. **Category section (starting at offset 128)**
 *    A sequence of variable-length categories:
 *
 *    Category layout:
 *    - uint16 categoryType
 *    - uint16 categoryWordSize (number of 16-bit words)
 *    - payload (categoryWordSize * 2 bytes)
 *
 *    Parsing continues sequentially until:
 *    - Category type `0xFFFF` (END), or
 *    - Category type resolves to {@link SiiCategoryType.NOP}
 *
 * - Recognized categories are dispatched to dedicated parsers:
 *   - STRINGS → {@link parseSiiCategoryStrings}
 *   - GENERAL → {@link parseSiiCategoryGeneral}
 *   - FMMU → {@link parseSiiCategoryFmmu}
 *   - SYNC_M → {@link parseSiiCategorySyncManager}
 *   - RXPDO / TXPDO → {@link parseSiiCategoryPdo}
 *   - DC → {@link parseSiiCategoryDc}
 *
 * - Multiple PDO categories are supported and accumulated.
 *
 * @note
 * - Unrecognized categories are skipped.
 * - No validation is performed for:
 *   - Buffer size correctness
 *   - Category alignment or integrity
 *   - Semantic correctness of parsed values
 *
 * - String indices in parsed structures refer to entries from the STRINGS category.
 *
 * - This function assumes little-endian layout and native alignment as used by `python-struct`.
 */
function parseSiiBuffer(buffer) {
    let strings = [];
    let general = {
        groupIdx: 0,
        imgIdx: 0,
        orderIdx: 0,
        nameIdx: 0,
        coeDetails: 0,
        foeDetails: 0,
        eoeDetails: 0,
        soeChannels: 0,
        ds402Channels: 0,
        sysmanClass: 0,
        flags: 0,
        currentOnEBus: 0,
        physicalPort: 0,
        physicalMemoryAddress: 0,
    };
    let fmmus = [];
    let syncManagers = [];
    let rxPdos = [];
    let txPdos = [];
    let dcs = [];
    const data = (0, python_struct_1.unpack)('@HHHHH4xHLLLL8xHHHHHHHHH66xHHHH', buffer);
    const info = {
        pdiControl: data[0],
        pdiConfiguration: data[1],
        syncImpulseLen: data[2],
        pdiConfiguration2: data[3],
        configuredStationAlias: data[4],
        checksum: data[5],
        vendorId: data[6],
        productCode: data[7],
        revisionNumber: data[8],
        serialNumber: data[9],
        bootstrapReceiveMailboxOffset: data[10],
        bootstrapReceiveMailboxSize: data[11],
        bootstrapSendMailboxOffset: data[12],
        bootstrapSendMailboxSize: data[13],
        standardReceiveMailboxOffset: data[14],
        standardReceiveMailboxSize: data[15],
        standardSendMailboxOffset: data[16],
        standardSendMailboxSize: data[17],
        mailboxProtocol: data[18],
        size: data[19],
        version: data[20],
    };
    const categoriesBuffer = buffer.subarray(128);
    let categoriesOffsetIndex = 0;
    while (true) {
        const categoryBuffer = categoriesBuffer.subarray(categoriesOffsetIndex);
        if (categoryBuffer[0] === 0xff && categoryBuffer[1] === 0xff) {
            break;
        }
        let [categoryTypeNumber, categoryWordSize] = (0, python_struct_1.unpack)('@HH', categoryBuffer);
        const categoryType = resolveSiiCategoryType(categoryTypeNumber);
        const categoryDataBuffer = categoryBuffer.subarray(4, categoryWordSize * 2 + 4);
        if (categoryType === SiiCategoryType.STRINGS) {
            strings = parseSiiCategoryStrings(categoryDataBuffer);
        }
        else if (categoryType === SiiCategoryType.GENERAL) {
            general = parseSiiCategoryGeneral(categoryDataBuffer);
        }
        else if (categoryType === SiiCategoryType.FMMU) {
            fmmus = parseSiiCategoryFmmu(categoryDataBuffer);
        }
        else if (categoryType === SiiCategoryType.SYNC_M) {
            syncManagers = parseSiiCategorySyncManager(categoryDataBuffer);
        }
        else if (categoryType === SiiCategoryType.RXPDO) {
            rxPdos.push(...parseSiiCategoryPdo(categoryDataBuffer));
        }
        else if (categoryType === SiiCategoryType.TXPDO) {
            txPdos.push(...parseSiiCategoryPdo(categoryDataBuffer));
        }
        else if (categoryType === SiiCategoryType.DC) {
            dcs = parseSiiCategoryDc(categoryDataBuffer);
        }
        if (categoryType === SiiCategoryType.NOP || categoryType === SiiCategoryType.END) {
            break;
        }
        categoriesOffsetIndex += categoryWordSize * 2 + 4;
    }
    return {
        info,
        category: {
            strings,
            general,
            fmmus,
            syncManagers,
            rxPdos,
            txPdos,
            distributedClocks: dcs,
        },
    };
}
exports.parseSiiBuffer = parseSiiBuffer;
/**
 * Parse string table from SII (Category 10).
 *
 * @param buffer Raw category payload buffer.
 * @returns Array of decoded strings.
 *
 * @remarks
 * - The buffer layout is:
 *   - uint8 numberOfStrings
 *   - Repeated `numberOfStrings` times:
 *     - uint8 length
 *     - `length` bytes ASCII string (no null terminator)
 *
 * - Strings are stored sequentially and referenced by index from other categories.
 * - The parser advances linearly using a pointer (`p`).
 *
 * - No bounds checking is performed:
 *   - Assumes `numberOfStrings` matches actual content.
 *   - Assumes each string length fits within the buffer.
 *
 * @note
 * - Returned array is zero-based, while SII indices typically start from 1.
 *   Consumers may need to adjust indexing accordingly.
 * - Encoding is assumed to be ASCII; no decoding/validation is performed.
 */
function parseSiiCategoryStrings(buffer) {
    const numberOfStrings = buffer[0];
    let p = 1;
    const strings = [];
    for (let i = 0; i < numberOfStrings; i++) {
        const len = buffer[p];
        const [str] = (0, python_struct_1.unpack)(`@${len}s`, buffer.subarray(p + 1));
        strings.push(str);
        p += len + 1;
    }
    return strings;
}
/**
 * Parse General device information from SII (Category 30).
 *
 * @param buffer Raw category payload buffer.
 * @returns Parsed general device information structure.
 *
 * @remarks
 * - The structure is decoded using the format string `@12BhB1xHH12x`.
 * - Layout (little-endian, native alignment via `@`):
 *   - uint8  groupIdx              Index into string table (group)
 *   - uint8  imgIdx                Index into string table (image)
 *   - uint8  orderIdx              Index into string table (order number)
 *   - uint8  nameIdx               Index into string table (device name)
 *   - uint8  coeDetails            CoE capability flags
 *   - uint8  foeDetails            FoE capability flags
 *   - uint8  eoeDetails            EoE capability flags
 *   - uint8  soeChannels           Number of SoE channels
 *   - uint8  ds402Channels         Number of DS402 channels
 *   - uint8  sysmanClass           Sync manager class
 *   - uint8  flags                 General device flags
 *   - uint8  currentOnEBus         Current consumption on E-Bus (mA)
 *   - uint16 physicalPort          Physical port configuration
 *   - uint16 physicalMemoryAddress Physical memory address
 *   - 12 bytes padding/reserved
 *
 * - The function assumes the buffer is correctly sized and aligned.
 * - No validation is performed on buffer length or field values.
 *
 * @note
 * - String indices reference entries from the STRINGS category (Category 10).
 * - Capability fields (CoE, FoE, EoE) are bitfields and may require further decoding.
 */
function parseSiiCategoryGeneral(buffer) {
    const data = (0, python_struct_1.unpack)('@12BhB1xHH12x', buffer);
    return {
        groupIdx: data[0],
        imgIdx: data[1],
        orderIdx: data[2],
        nameIdx: data[3],
        coeDetails: data[4],
        foeDetails: data[5],
        eoeDetails: data[6],
        soeChannels: data[7],
        ds402Channels: data[8],
        sysmanClass: data[9],
        flags: data[10],
        currentOnEBus: data[11],
        physicalPort: data[12],
        physicalMemoryAddress: data[13],
    };
}
/**
 * Parse FMMU configuration entries from SII (Category 40).
 *
 * @param buffer Raw category payload buffer.
 * @returns Array of 16-bit FMMU values.
 *
 * @remarks
 * - The buffer is interpreted as a contiguous array of uint16 values.
 * - Each value represents part of the FMMU configuration as defined in the SII.
 * - The total number of entries is `buffer.length / 2`.
 * - No structural decoding is performed; values are returned as raw words.
 *
 * @note
 * - The caller is responsible for interpreting the returned values according
 *   to the EtherCAT FMMU configuration layout.
 * - Buffer length must be even; otherwise unpacking will be invalid.
 */
function parseSiiCategoryFmmu(buffer) {
    return (0, python_struct_1.unpack)(`@${buffer.length / 2}H`, buffer);
}
/**
 * Parse Sync Manager definitions from SII (Category 41).
 *
 * @param buffer Raw category payload buffer.
 * @returns Array of parsed Sync Manager elements.
 *
 * @remarks
 * - Each Sync Manager entry is 8 bytes long.
 * - Layout (little-endian, native alignment via `@`):
 *   - uint16 physicalStartAddress   Start address in DPRAM
 *   - uint16 length                 Length in bytes
 *   - uint8  controlRegister        Sync Manager control register value
 *   - uint8  statusRegister         Sync Manager status register value
 *   - uint8  enableSyncManager      Enable flag (non-zero = enabled)
 *   - uint8  syncManagerType        Type (e.g. mailbox, process data)
 *
 * - The function iterates linearly over the buffer in 8-byte steps.
 * - No validation is performed to ensure buffer length is a multiple of 8.
 *   Caller is responsible for providing a well-formed buffer.
 */
function parseSiiCategorySyncManager(buffer) {
    const elements = [];
    for (let i = 0; i < buffer.length; i += 8) {
        const data = (0, python_struct_1.unpack)('@HHBBBB', buffer.subarray(i));
        elements.push({
            physicalStartAddress: data[0],
            length: data[1],
            controlRegister: data[2],
            statusRegister: data[3],
            enableSyncManager: data[4],
            syncManagerType: data[5],
        });
    }
    return elements;
}
/**
 * Parse PDO definitions (RxPDO or TxPDO) from an SII category buffer.
 *
 * @param buffer Raw category payload buffer.
 * @returns Array of parsed PDO elements.
 *
 * @remarks
 * - The buffer contains a sequence of PDO blocks.
 * - Each PDO consists of:
 *
 *   PDO header (8 bytes):
 *   - uint16 pdoIndex         PDO index (object dictionary)
 *   - uint8  nEntries         Number of mapped entries
 *   - uint8  syncM            Assigned Sync Manager
 *   - uint8  synchronization  Synchronization type
 *   - uint8  nameIdx          Index into string table (PDO name)
 *   - uint16 flags            PDO flags
 *
 *   Followed by `nEntries` PDO entry descriptors:
 *
 *   PDO entry (8 bytes each):
 *   - uint16 entryIndex       Object index
 *   - uint8  subindex         Object subindex
 *   - uint8  entryNameIdx     Index into string table
 *   - uint8  dataType         EtherCAT data type
 *   - uint8  bitLen           Bit length of the entry
 *   - uint16 flags            Entry flags
 *
 * - Parsing continues until the buffer is exhausted.
 * - A safety check prevents reading beyond buffer bounds for entries.
 * - If the buffer is truncated, remaining entries in the current PDO are skipped.
 *
 * @note
 * - `nEntry` reflects the declared number of entries, even if fewer were parsed
 *   due to buffer truncation.
 * - No validation is performed for semantic correctness of indices or types.
 */
function parseSiiCategoryPdo(buffer) {
    const pdos = [];
    let offset = 0;
    while (offset + 8 <= buffer.length) {
        const pdoHeader = (0, python_struct_1.unpack)('@HBBBBH', buffer.subarray(offset, offset + 8));
        const nEntries = pdoHeader[1];
        const entries = [];
        offset += 8;
        for (let i = 0; i < nEntries; i++) {
            if (offset + 8 > buffer.length) {
                break; // Safety check
            }
            const entryData = (0, python_struct_1.unpack)('@HBBBBH', buffer.subarray(offset, offset + 8));
            entries.push({
                entryIndex: entryData[0],
                subindex: entryData[1],
                entryNameIdx: entryData[2],
                dataType: entryData[3],
                bitLen: entryData[4],
                flags: entryData[5],
            });
            offset += 8;
        }
        pdos.push({
            pdoIndex: pdoHeader[0],
            nEntry: nEntries,
            syncM: pdoHeader[2],
            synchronization: pdoHeader[3],
            nameIdx: pdoHeader[4],
            flags: pdoHeader[5],
            entries,
        });
    }
    return pdos;
}
/**
 * Parse Distributed Clocks (DC) configuration entries from SII (Category 60).
 *
 * @param buffer Raw category payload buffer.
 * @returns Array of parsed Distributed Clock elements.
 *
 * @remarks
 * - Each DC entry is 24 bytes long.
 * - Layout (little-endian, native alignment via `@`):
 *   - uint32 cycleTime0         Sync0 cycle time (ns)
 *   - uint32 shiftTime0         Sync0 shift time (ns)
 *   - uint32 shiftTime1         Sync1 shift time (ns)
 *   - int16  sync1CycleFactor   Sync1 cycle factor
 *   - uint16 assignActivate     DC assign/activate register value
 *   - int16  sync0CycleFactor   Sync0 cycle factor
 *   - uint8  nameIdx            Index into string table (name)
 *   - uint8  descIdx            Index into string table (description)
 *   - 4 bytes padding
 *
 * - The function iterates linearly over the buffer in 24-byte steps.
 * - No bounds validation is performed beyond stride stepping.
 *   Caller must ensure buffer length is a multiple of 24.
 */
function parseSiiCategoryDc(buffer) {
    const elements = [];
    for (let i = 0; i < buffer.length; i += 24) {
        const data = (0, python_struct_1.unpack)('@IIIhHhBB4x', buffer.subarray(i));
        elements.push({
            cycleTime0: data[0],
            shiftTime0: data[1],
            shiftTime1: data[2],
            sync1CycleFactor: data[3],
            assignActivate: data[4],
            sync0CycleFactor: data[5],
            nameIdx: data[6],
            descIdx: data[7],
        });
    }
    return elements;
}
//# sourceMappingURL=sii.js.map