danielwippermann/resol-vbus

View on GitHub
examples/customizer2/src/customizer.js

Summary

Maintainability
D
1 day
Test Coverage
/*! resol-vbus | Copyright (c) 2023-present, Daniel Wippermann | MIT license */

const logger = require('./logger');
const { hex } = require('./utils');
const { ValueInfoCache } = require('./value-info-cache');


async function performTransaction(connection, fn) {
    let result;

    logger.debug('Waiting for free bus...');

    const dgram1 = await connection.waitForFreeBus();
    if (dgram1) {
        const peerAddress = dgram1.sourceAddress;

        logger.debug(`Got bus offer from 0x${hex(peerAddress, 4)}, getting changeset...`);

        const dgram2 = await connection.getValueById(peerAddress, 0);
        const changeset = dgram2 ? dgram2.value : 0;

        if (dgram2) {
            logger.debug(`Got changeset 0x${hex(changeset, 8)}!`);
        } else {
            logger.debug(`Unable to get changeset, using 0 as default`);
        }

        const cache = new ValueInfoCache(peerAddress, changeset);

        logger.debug(`Loading value info cache from ${cache.filename}...`);

        await cache.loadFromFile();

        logger.debug(`Cache loaded! Cached entry count: ${cache.cache.length}`);

        try {
            result = await fn({
                connection,
                peerAddress,
                changeset,
                cache,
            });
        } finally {
            logger.debug(`Releasing bus...`);

            await connection.releaseBus(peerAddress);

            logger.debug('Bus released!');

            await cache.saveToFile();
        }
    }

    return result;
}

async function updateCacheForValues(connection, peerAddress, cache, values) {
    let knownValues = 0, unknownValues = 0;
    for (const value of values) {
        const { valueIndex } = cache.getValueInfoByIndexOrId(value.valueIndex, value.valueId);
        if (valueIndex) {
            knownValues += 1;
        } else {
            unknownValues += 1;
        }
    }

    logger.debug(`Known values: ${knownValues}, unknown values: ${unknownValues}`);

    if (unknownValues > 0) {
        logger.debug(`Value index lookup necessary!`);

        for (const value of values) {
            const { valueId } = value;

            const { valueIdHash, valueIndex } = cache.getValueInfoById(valueId);

            if (!valueIndex) {
                logger.debug(`Getting value index for "${valueId}" (0x${hex(valueIdHash, 8)})...`);

                const dgram3 = await connection.getValueIdByIdHash(peerAddress, valueIdHash);

                const valueIndex = dgram3 ? dgram3.valueId : 0;
                if (valueIndex) {
                    logger.debug(`Got value index 0x${hex(valueIndex, 4)}...`);

                    cache.setValueIndexById(valueId, valueIndex);

                    knownValues += 1;
                    unknownValues -= 1;
                } else {
                    logger.debug(`Unable to get value index, skipping value!`);
                }
            }
        }

        logger.debug(`Value index lookup completed, resyncing...`);

        await connection.getValueById(peerAddress, 0);

        logger.debug(`Resynced!`);

        logger.debug(`Known values: ${knownValues}, unknown values: ${unknownValues}`);
    }
}

async function loadConfiguration(connection, values) {
    return await performTransaction(connection, async ({ peerAddress, cache }) => {
        await updateCacheForValues(connection, peerAddress, cache, values);

        let validValues = 0, invalidValues = 0, unknownValues = 0;

        for (const jobValue of values) {
            const { valueId } = jobValue;

            const { valueIndex } = cache.getValueInfoByIndexOrId(jobValue.valueIndex, valueId);

            if (valueIndex) {
                logger.debug(`Getting value for "${valueId}" (0x${hex(valueIndex, 4)})...`);

                const dgram3 = await connection.getValueById(peerAddress, valueIndex);
                if (dgram3) {
                    const { value } = dgram3;

                    logger.debug(`Got value ${value} (0x${hex(value, 8)})!`);

                    jobValue.value = value;

                    validValues += 1;
                } else {
                    logger.debug(`Unable to get value, skipping!`);

                    jobValue.value = null;

                    invalidValues += 1;
                }
            } else {
                logger.debug(`Skipping value without value index: "${valueId}"!`);

                jobValue.value = null;

                unknownValues += 1;
            }
        }

        logger.debug(`Valid values: ${validValues}, invalid values: ${invalidValues}, unknown values: ${unknownValues}!`);

        return values;
    });
}

async function saveConfiguration(connection, values) {
    return await performTransaction(connection, async ({ peerAddress, cache }) => {
        await updateCacheForValues(connection, peerAddress, cache, values);

        let validValues = 0, invalidValues = 0, unknownValues = 0;

        for (const jobValue of values) {
            const { valueId, value } = jobValue;

            const { valueIndex } = cache.getValueInfoByIndexOrId(jobValue.valueIndex, valueId);

            if (valueIndex && (value != null)) {
                logger.debug(`Setting value for "${valueId}" (0x${hex(valueIndex, 4)}) to ${value} (0x${hex(value, 8)})...`);

                const dgram3 = await connection.setValueById(peerAddress, valueIndex, value);
                if (dgram3) {
                    const { value } = dgram3;

                    logger.debug(`Got value ${value} (0x${hex(value, 8)})!`);

                    jobValue.value = value;

                    validValues += 1;
                } else {
                    logger.debug(`Unable to set value, skipping!`);

                    jobValue.value = null;

                    invalidValues += 1;
                }
            } else {
                logger.debug(`Skipping value without value index: "${valueId}"!`);

                jobValue.value = null;

                unknownValues += 1;
            }
        }

        logger.debug(`Valid values: ${validValues}, invalid values: ${invalidValues}, unknown values: ${unknownValues}!`);

        return values;
    });
}


module.exports = {
    performTransaction,
    updateCacheForValues,
    loadConfiguration,
    saveConfiguration,
};