"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getEncoderResolutionBits = exports.getEncoderConfigurationExceptionCase = exports.getCirculoEncoderBatteryModeOptions = exports.getCirculoEncoderBatteryModeDefaultValue = exports.circuloEncoderBatteryModeMap = exports.mapCirculoEncoderRegisterValueToErrors = exports.circuloEncoderRegisterErrors = exports.CirculoEncoderRegisterAddress = void 0;
const semver_1 = require("semver");
const product_1 = require("./product");
/**
 * Enum of Circulo encoder register addresses.
 *
 * These constants represent the addresses of various encoder registers
 * used for status monitoring and data reading.
 */
var CirculoEncoderRegisterAddress;
(function (CirculoEncoderRegisterAddress) {
    CirculoEncoderRegisterAddress[CirculoEncoderRegisterAddress["STATUS0"] = 118] = "STATUS0";
    CirculoEncoderRegisterAddress[CirculoEncoderRegisterAddress["STATUS1"] = 119] = "STATUS1";
    CirculoEncoderRegisterAddress[CirculoEncoderRegisterAddress["OUT_ZERO_OUT_MSB"] = 17] = "OUT_ZERO_OUT_MSB";
})(CirculoEncoderRegisterAddress = exports.CirculoEncoderRegisterAddress || (exports.CirculoEncoderRegisterAddress = {}));
/**
 * Lookup table mapping encoder register error bits to detailed error information.
 *
 * Each element in the array corresponds to a specific encoder status register (e.g., STATUS0, STATUS1),
 * identified by its index. Within each object, the keys represent bit flags (powers of two) indicating
 * specific error conditions. The associated value is an `EncoderRegisterError` object describing
 * the error, its cause, and suggested remedy.
 *
 * Example usage:
 * ```ts
 * const errorsForStatus0 = circuloEncoderRegisterErrors[0][1]; // Gets the error for bit 1 of STATUS0
 * ```
 */
exports.circuloEncoderRegisterErrors = [
    {
        1: {
            name: 'AM_MIN',
            error: 'Signal error: poor level (master track)',
            description: 'Magnetic field strength is too low for encoder operation.',
            remedy: 'Check positioning of the encoder ring.',
            fullDescription: "An encoder magnetic ring is too far from the encoder chip or isn't detected. Check the mechanical assembly. In some rare cases can be provoked by demagnetized ring or field strength drop due to high temperatures. A magnetic ring is too far from the encoder chip. Check the mechanical assembly.",
        },
        2: {
            name: 'AM_MAX',
            error: 'Signal error: clipping (master track)',
            description: 'Magnetic field strength is too high.',
            remedy: 'Check mechanical assembly and external fields.',
            fullDescription: "The outer track of the encoder chip detects too strong a field. Typically, it can't be generated by the magnetic ring itself at normal conditions. Please check the influence of external fields, especially brakes. A magnetic ring is too close to the encoder chip. Check the mechanical assembly.",
        },
        4: {
            name: 'AN_MIN',
            error: 'Signal error: poor level (nonius track)',
            description: 'Magnetic field strength is too low for encoder operation.',
            remedy: 'Check positioning of the encoder ring.',
            fullDescription: "An encoder magnetic ring is too far from the encoder chip or isn't detected. Check the mechanical assembly. In some rare cases can be provoked by demagnetized ring or field strength drop due to high temperatures.",
        },
        8: {
            name: 'AN_MAX',
            error: 'Signal error: clipping (nonius track)',
            description: 'Magnetic field strength is too high.',
            remedy: 'Check mechanical assembly and external fields.',
            fullDescription: "The inner track of the encoder chip detects too strong a field. Typically, it can't be generated by the magnetic ring itself at normal conditions. Please check the influence of external fields, especially brakes.",
        },
        16: {
            name: 'STUP',
            error: 'Startup iC-MU',
            description: 'Encoder startup error. Can appear during Multiturn configuration.',
            remedy: 'Power cycle the drive.',
            fullDescription: 'Encoder initialization error. The typical reason is: not saved Multiturn Counter configuration. Connect the multiturn battery, check the battery voltage, configure the multiturn bits in OBLAC Drives, configure integrated encoder and save changes. Power cycle the drive. If the error is always present, contact support.',
        },
    },
    {
        2: {
            name: 'FRQ_CNV',
            error: 'Excessive signal frequency for internal 12 Bit converter',
            description: 'Encoder maximum speed was exceeded.',
            remedy: '',
            fullDescription: 'Encoder maximum speed was exceeded. Circulo 7 - 12000 RPM or Circulo 9 - 6000 RPM',
        },
        4: {
            name: 'FRQ_ABZ',
            error: 'Excessive signal frequency for ABZ-converter',
            description: 'Encoder maximum speed was exceeded.',
            remedy: '',
            fullDescription: 'Encoder maximum speed was exceeded. Circulo 7 - 12000 RPM or Circulo 9 - 6000 RPM',
        },
        8: {
            name: 'NON_CTR',
            error: 'Period counter consistency error: counted period <-> calculated Nonius position',
            description: 'Integrated Encoder calibration parameters are outdated or non-existing.',
            remedy: 'Please calibrate the integrated encoder.',
            fullDescription: "The absolute position value can't be computed reliably at the current position. This can be caused by: encoder wasn't calibrated, change of encoder mechanics, severe thermal expansion or magnetic target damage. Run diagnostics and repeat the calibration procedure. If this error prevents you from performing calibration - disable it or contact support.",
        },
        16: {
            name: 'MT_CTR',
            error: 'Multiturn data consistency error: counted multiturn <-> external MT data',
            description: 'Multiturn Counter synchronization error.',
            remedy: 'Check MT configuration, connect a battery. Power cycle the drive.',
            fullDescription: 'Can happen because of not saved Multiturn Counter configuration. Connect the multiturn battery, check the battery voltage, configure the multiturn bits in OBLAC Drives, configure integrated encoder and save changes. If the error is still present, please contact support.',
        },
        32: {
            name: 'MT_ERR',
            error: 'Multiturn communication error: MTD line not 1 when trying to read MT data, MTD line is not 0 right after the last clock pulse, or SSI error bit active on MT interface',
            description: 'Multiturn Counter communication error.',
            remedy: 'Check MT configuration, connect a battery. Power cycle the drive.',
            fullDescription: 'The Multiturn counter is in error state. Connect the multiturn battery, check the battery voltage, configure the multiturn bits in OBLAC Drives, configure integrated encoder and save changes. Power cycle the drive. If the error is still present, please contact support.',
        },
        64: {
            name: 'EPR_ERR',
            error: 'I2C communication error: No EEPROM or I2C communication error',
            description: 'Encoder EEPROM memory communication error.',
            remedy: 'Power Cycle the drive.',
            fullDescription: "Communication with the EEPROM memory of the chip is distorted. Power Cycle the drive. If this can't be reset, there is a hardware problem. Please contact support.",
        },
        128: {
            name: '',
            error: 'Invalid check sum internal RAM',
            description: 'Invalid check sum internal RAM',
            remedy: '',
            fullDescription: '',
        },
    },
    {
        1: {
            name: 'STUP_ERR',
            error: 'Startup error',
            description: 'Erroneous startup procedure, e.g., I2C stuck-at, EEPROM read error or invalid CRC checksum stored in the EEPROM.',
            remedy: 'Reboot iC-PVL.',
            fullDescription: 'Erroneous startup procedure, e.g., I2C stuck-at, EEPROM read error or invalid CRC checksum stored in the EEPROM. No position acquisition is performed. Interfaces are blocked. Please revise configuration and checksums or replace EEPROM. Reboot iC-PVL.',
        },
        2: {
            name: 'CFG_ERR',
            error: 'Internal configuration error',
            description: 'This error is raised when the configuration stored in the internal RAM doesn’t match the CRC checksum.',
            remedy: 'Reboot iC-PVL.',
            fullDescription: 'This error is raised when the configuration stored in the internal RAM doesn’t match the CRC checksum. This can be caused for example by changing the configuration data without updating the checksum accordingly, or an unexpected level flip of one or more bits in the RAM configuration area. Position is invalid. Reboot iC-PVL. It can also be caused by writing to RAM configuration via I2C. CFG_ERR can be cleared by a SCLR command.',
        },
        4: {
            name: 'CTR_ERR',
            error: 'Internal counter error',
            description: 'The value stored in the internal multiturn counter doesn’t match the CRC checksum.',
            remedy: 'Reboot iC-PVL.',
            fullDescription: 'The value stored in the internal multiturn counter doesn’t match the CRC checksum. This can be caused for example by changing the multiturn counter value without updating the checksum accordingly, or and unexpected level flip of one or more bits in the multiturn counter RAM area. Position is invalid. Reboot iC-PVL. It can also be caused by writing to MT_COUNT via I2C. CTR_ERR can be cleared by a SCLR command.',
        },
        8: {
            name: 'POS_ERR',
            error: 'Position error',
            description: 'The position encoding observed an unexpected position jump, caused e.g. by excessive speed or excessive acceleration of the magnetic disc or tape.',
            remedy: 'Reboot iC-PVL.',
            fullDescription: 'The position encoding observed an unexpected position jump, caused e.g. by excessive speed or excessive acceleration of the magnetic disc or tape. Alternatively, this error bit is set on weak, disturbed magnetic signals or complete loss of magnet. Position is invalid. Optimize magnet position and cross-check angular velocity/acceleration. Reboot iC-PVL.',
        },
        16: {
            name: 'BAT_ERR',
            error: 'Battery error',
            description: 'Battery undervoltage according to Elec. Char. No. 404.',
            remedy: 'Reboot iC-PVL.',
            fullDescription: 'Battery undervoltage according to Elec. Char. No. 404. Position is invalid. Change battery. Reboot iC-PVL. Battery monitoring is active during VDD supply.',
        },
        32: {
            name: 'BAT_WRN',
            error: 'Battery early warning',
            description: 'Battery voltage early warning according to Elec. Char. No. 405.',
            remedy: 'Switch off system, change battery and restart.',
            fullDescription: 'Battery voltage early warning according to Elec. Char. No. 405. Battery may be changed during main supply (VDD) as soon as possible. Alternatively, halt system, read current position and restore it. Switch off system, change battery and restart. Restored position may be set as counter preload. Battery monitoring is active during VDD supply.',
        },
        64: {
            name: 'PDR',
            error: 'Power down reset detected',
            description: 'A power down reset was performed, caused by undervoltage considerably lower than the battery error threshold.',
            remedy: '',
            fullDescription: 'A power down reset was performed, caused by undervoltage considerably lower than the battery error threshold. Another reason may be due to insufficient stabilizing capacitors at the supply lines VDD and VBAT. If a power down reset is performed, all internal circuitry is initialized to its default state. Counter position is set to zero and the working state is reset to the POWON State. For power down reset, the voltage at VDDS is relevant. Therefore, it is only performed if both, VDD and VBAT are low. The voltage threshold is typically between 1.5 V and 2.5 V.',
        },
        128: {
            name: 'PRESET',
            error: 'Pin preset detected',
            description: 'This bit is set whenever the chip is coming from a power-down condition or a pin preset has been detected.',
            remedy: '',
            fullDescription: 'This bit is set whenever the chip is coming from a power-down condition or a pin preset has been detected. It may be cleared by issuing a SCLR or RESET command. This can be interpreted as an acknowledgment that the power-down condition or pin preset was intentional. If the bit is set later during field operation, it is most likely unintentional. The cause may have been an external disruption, a short circuit error on the PRE line or a complete loss of power supply (VDD and VBAT). The PRESET bit is also set if the sleep mode has been enabled or an i2C REBOOT has been performed.',
        },
    },
];
/**
 * Maps a status register value to a list of corresponding encoder register errors.
 *
 * This function interprets each set bit in the given 8-bit `value` as an individual error flag.
 * It then looks up the corresponding `EncoderRegisterError` definitions from a predefined
 * error mapping (`circuloEncoderRegisterErrors`) based on the provided `statusIndex`.
 *
 * @param value - The 8-bit value read from the encoder's status register (typically 0–255).
 * @param statusIndex - The index of the status register (0 or 1), usually corresponding to
 *                      `CirculoEncoderRegisterAddress.STATUS0` or `STATUS1`.
 * @returns An array of `EncoderRegisterError` objects for each set error bit in the status value.
 *          Returns an empty array if no error bits are set or no matching definitions are found.
 */
function mapCirculoEncoderRegisterValueToErrors(value, statusIndex) {
    var _a;
    if (value === 0) {
        return [];
    }
    const errors = [];
    for (let bit = 1; bit <= 0x80; bit <<= 1) {
        if ((value & bit) !== 0) {
            const error = (_a = exports.circuloEncoderRegisterErrors[statusIndex]) === null || _a === void 0 ? void 0 : _a[bit];
            if (error) {
                errors.push(error);
            }
        }
    }
    return errors;
}
exports.mapCirculoEncoderRegisterValueToErrors = mapCirculoEncoderRegisterValueToErrors;
/**
 * Maps product groups to their supported battery modes for Circulo encoders.
 *
 * Each product group key (e.g., `'Circulo 7'`, `'Circulo 9'`) maps to an array of
 * `CirculoEncoderBatteryMode` objects, each describing a specific battery mode configuration.
 *
 * Battery modes specify constraints such as maximum acceleration and current,
 * along with a raw encoded `value` representing the mode setting.
 */
exports.circuloEncoderBatteryModeMap = new Map([
    [
        'Circulo 7',
        [
            { maxAcceleration: 3.5, maxCurrent: 6, value: 0x60 },
            { maxAcceleration: 14, maxCurrent: 10, value: 0x58 },
            { maxAcceleration: 56, maxCurrent: 18, value: 0x50 },
            { maxAcceleration: 226, maxCurrent: 36, value: 0x48 },
            { maxAcceleration: 895, maxCurrent: 72, value: 0x40 },
        ],
    ],
    [
        'Circulo 9',
        [
            { maxAcceleration: 1.7, maxCurrent: 6, value: 0x60 },
            { maxAcceleration: 7, maxCurrent: 10, value: 0x58 },
            { maxAcceleration: 28, maxCurrent: 18, value: 0x50 },
            { maxAcceleration: 113, maxCurrent: 36, value: 0x48 },
            { maxAcceleration: 447, maxCurrent: 72, value: 0x40 },
        ],
    ],
]);
/**
 * Returns the default battery mode encoded value for a given encoder shaft type.
 *
 * @param shaft - The type of encoder shaft. Can be `'motor'` or `'driving'`.
 *                If omitted or unrecognized, returns a default fallback value.
 * @returns The encoded default battery mode value corresponding to the shaft type:
 *          - `0x5d` for `'motor'`
 *          - `0x68` for `'driving'`
 *          - `0x00` if the shaft type is not specified or unknown
 */
function getCirculoEncoderBatteryModeDefaultValue(shaft) {
    if (shaft === 'motor') {
        return 0x5d;
    }
    else if (shaft === 'driving') {
        return 0x68;
    }
    else {
        return 0x00;
    }
}
exports.getCirculoEncoderBatteryModeDefaultValue = getCirculoEncoderBatteryModeDefaultValue;
/**
 * Retrieves the available battery mode options for a Circulo encoder product.
 *
 * This function identifies the product by its ID or hardware description and, if the product
 * belongs to the 'Circulo' family, returns an array of battery mode options specific to the product group.
 * Each option consists of a human-readable key describing the mode and its corresponding encoded value.
 *
 * The returned array always includes a "Default" option based on the provided shaft type.
 *
 * @param productId - The identifier of the product. Can be a number, string, or a `HardwareDescription` object.
 * @param shaft - Optional encoder shaft type (`'motor'` or `'driving'`) used to determine the default battery mode.
 * @returns An array of battery mode options with keys describing the mode and their associated encoded values.
 *          Returns an empty array if the product is not part of the Circulo family or no modes are found.
 */
function getCirculoEncoderBatteryModeOptions(productId, shaft) {
    const product = (0, product_1.findSomanetProductById)(productId);
    if ((product === null || product === void 0 ? void 0 : product.family) === 'Circulo') {
        const arr = exports.circuloEncoderBatteryModeMap.get(product.group);
        if (arr) {
            const defaultValue = getCirculoEncoderBatteryModeDefaultValue(shaft);
            return [
                { key: 'Default', value: defaultValue },
                ...arr.map(({ maxAcceleration, maxCurrent, value }) => ({
                    key: `${maxAcceleration} kRPM/s | ${maxCurrent} μA`,
                    value,
                })),
            ];
        }
    }
    return [];
}
exports.getCirculoEncoderBatteryModeOptions = getCirculoEncoderBatteryModeOptions;
/**
 * Selects the encoder configuration exception case based on the encoder ordinal, type, and product ID
 *
 * @param productId - The product ID to check against.
 * @param encoderPort - The encoder port to check.
 * @param encoderType - The encoder type to check.
 * @param firmwareMajorMinorVersion - The firmware version in major.minor format.
 * @returns The selected exception case or undefined if no case matches.
 * @link https://docs.google.com/spreadsheets/d/1E0HTiZQtGmby2BJ9kOGV0UwD3HT6xQ7QRLjNbXpx5V0/edit?usp=sharing
 */
function getEncoderConfigurationExceptionCase(productId, encoderPort, encoderType, firmwareMajorMinorVersion) {
    const firmwareVersion = `${firmwareMajorMinorVersion}.0`;
    // Case 1
    const case1EncoderTypes = (0, semver_1.satisfies)(firmwareVersion, '>=5.0 <5.1') ? [4] : [4, 5];
    if ((0, semver_1.satisfies)(firmwareVersion, '>=5.0') &&
        (0, product_1.isSomanetProductIdInRange)(productId, 'Circulo All') &&
        (0, product_1.isEncoderPortUsable)(encoderPort, productId) &&
        (0, product_1.isEncoderPortInternal)(encoderPort, productId) &&
        case1EncoderTypes.includes(encoderType)) {
        return 'case1';
    }
    // Case 2
    if ((0, semver_1.satisfies)(firmwareVersion, '>=5.1') &&
        (0, product_1.isSomanetProductIdInRange)(productId, 'Circulo Safe Motion') &&
        (0, product_1.isEncoderPortUsable)(encoderPort, productId) &&
        !(0, product_1.isEncoderPortInternal)(encoderPort, productId) &&
        encoderType === 4) {
        return 'case2';
    }
    // Case 3
    if ((0, semver_1.satisfies)(firmwareVersion, '>=5.2') &&
        (0, product_1.isSomanetProductIdInRange)(productId, 'Integro') &&
        (0, product_1.isEncoderPortUsable)(encoderPort, productId) &&
        (0, product_1.isEncoderPortInternal)(encoderPort, productId) &&
        encoderType === 5) {
        return 'case3';
    }
    return;
}
exports.getEncoderConfigurationExceptionCase = getEncoderConfigurationExceptionCase;
/**
 * Computes the encoder resolution in bits from the total number of increments per revolution.
 *
 * The resolution in bits is calculated as the ceiling of the base-2 logarithm
 * of the number of increments, which represents the minimum number of bits
 * needed to uniquely represent each encoder position.
 *
 * @param increments - Total number of increments (counts) per revolution.
 * @returns The encoder resolution in bits.
 */
function getEncoderResolutionBits(increments) {
    return Math.ceil(Math.log2(increments));
}
exports.getEncoderResolutionBits = getEncoderResolutionBits;
//# sourceMappingURL=encoder.js.map