====== Программные зависимости ====== * express - фреймворк web-приложений для Node.js * ioredis - библиотека для эффективной работы с Redis * fs - библиотека для работы с файловой системой (является частью Node.js) * moment - библиотека для работы с датами и временем * request - библиотека для выполнения HTTP-запросов в Node.js ====== Функции в коде ====== ^ Название функции ^ Назначение ^ | //getCalibrationNameAndSerial(calibrationData)// | функция возвращает имена приборов и их серийные номера, в случае, если для них есть калибровочные коэффициенты | | //readAndParseCalibrationFile(file)// | асинхронная функция предназначена для чтения и парсинга JSON-файла с данными о калибровке. | | //getCalibrationSensorNames(calibrationData)// | функция возвращает массив с названиями сенсоров для калибровки | | //getAllRedisKeys()// | асинхронная функция для получения всех ключей из Redis | | //getNecessaryKeys(startDate, endDate, keysArray)// | функция выполняет фильтрацию массива ключей (keysArray) на основе заданного временного диапазона | | //checkDateRangeInRedis(formattedStartDate, formattedEndDate, keysArray)// | функция проверяет, находится ли заданный временной диапазон внутри или частично пересекается с временным диапазоном, представленным в массиве keysArray | | //sortRedisKeys(keysArray)// | функция для сортировки массива ключей Redis | | //sortedAndFilteredKeys(formattedStartDate, formattedEndDate)// | асинхронная функция для получения отсортированных и отфильтрованных ключей из Redis | | //updateDataInRedis(url)// | асинхронная функция для добавления данных в Redis | | //processAndAggregateLastMeasurements(respondParsed, keysArray)// | функция для обработки данных (для запросов last_measurements) | | //processAndAggregateResponsesList(respondParsed, keysArray)// | функция для обработки данных (для запросов list) | | //downloadPage(url)// | асинхронная функция выполняет запрос по указанному URL и возвращает ответ | | //requestAdditionalDataIfMissing(formattedStartDate, formattedEndDate, filteredKeys)// | асинхронная функция для дозапроса данных | | //respondToDataQuery(keys)// | функция предназначена для получения значений по заданным ключам | | //respondToDataQueryParsed(keys)// | функция предназначена для получения значений по заданным ключам | | //applyCalibrationsToData(calibrationData, filteredResponse)// | функция выполняет калибровку данных | ====== Дополнительные объяснения по функциям ====== ===== updateDataInRedis(url) ===== В программе использована конвейерная обработка данных для сведения к минимуму задержек при множественных операциях. Конвейерная обработка в Redis представляет собой метод повышения эффективности, позволяющий одновременно выполнить несколько команд без ожидания ответа на каждую отдельную команду. ===== Шаг 1: Получаем ответ от внешнего источника данных const databaseResponse = await downloadPage(url); Шаг 2: Получаем массив ключей из ответа const keys = Object.keys(databaseResponse); Шаг 3: Объявляем конвейеры для проверки наличия ключей в Redis и добавления данных const existsPipeline = redis.pipeline(); const updatePipeline = redis.pipeline(); Шаг 4: Добавляем команды проверки наличия в Redis для каждого ключа в конвейер for (const key of keys) { const currentDate = databaseResponse[key].Date; existsPipeline.exists(currentDate); } Шаг 5: Запускаем конвейер проверки наличия и ждём результат const existsResults = await existsPipeline.exec(); Шаг 6: Добавляем данные для отсутствующих ключей for (let i = 0; i < keys.length; i++) { const key = keys[i]; const currentDate = databaseResponse[key].Date; const keyExistsInRedis = existsResults[i][1] === 1; if (!keyExistsInRedis) { const newDataArray = JSON.stringify(databaseResponse[key]); updatePipeline.set(currentDate, newDataArray, 'EX', REDIS_EXPIRY_SECONDS); } } Шаг 7: Запускаем конвейер добавления данных и ждём результат await updatePipeline.exec(); ===== requestAdditionalDataIfMissing(formattedStartDate, formattedEndDate, filteredKeys) ===== Шаг №1: Инициализация флага для отслеживания обновлений в Redis. let flag = false; Шаг №2: Задаем максимальную разницу по времени между записями в секундах const secondsThreshold = 360; Шаг №3: Проверяем и добавляем данные, если разница между первым ключом в Redis и начальной датой больше secondsThreshold const firstKey = filteredKeys[0]; const formattedFirstKey = moment(firstKey).format(DATE_FORMAT); const startDifference = moment(formattedFirstKey).diff(moment(formattedStartDate), 'seconds'); if (Math.abs(startDifference) > secondsThreshold) { flag = true; const dataUrl = `${URL_DBROBO}${formattedStartDate}${SDATE_QUERY_PARAM}${formattedFirstKey}`; await updateDataInRedis(dataUrl); } Шаг №4: Проверка и добавление данных для промежуточных ключей for (let i = 0; i < filteredKeys.length - 2; i++) { const firstDate = filteredKeys[i]; const secondDate = filteredKeys[i + 1]; const formattedFirstDate = moment(firstDate).format(DATE_FORMAT); const formattedSecondDate = moment(secondDate).format(DATE_FORMAT); const secondsDifference = moment(formattedSecondDate).diff(moment(formattedFirstDate), 'seconds'); if (secondsDifference > secondsThreshold) { flag = true; const dataUrl = `${URL_DBROBO}${formattedFirstDate}${SDATE_QUERY_PARAM}${formattedSecondDate}`; await updateDataInRedis(dataUrl); } } Шаг №5: Проверяем и добавляем данные, если разница между последним ключом в Redis и конечной датой больше secondsThreshold const lastKey = filteredKeys[filteredKeys.length - 1]; const formattedLastKey = moment(lastKey).format(DATE_FORMAT); const endDifference = moment(formattedEndDate).diff(moment(formattedLastKey), 'seconds'); if (Math.abs(endDifference) > secondsThreshold) { flag = true; const dataUrl = `${URL_DBROBO}${formattedLastKey}${SDATE_QUERY_PARAM}${formattedEndDate}`; await updateDataInRedis(dataUrl); } Шаг №6: Если флаг установлен в true, возвращение новых отсортированных и отфильтрованных ключей из Redis, иначе возвращаем filteredKeys return flag ? await sortedAndFilteredKeys(formattedStartDate, formattedEndDate) : filteredKeys; ===== checkDateRangeInRedis(formattedStartDate, formattedEndDate, keysArray) ===== Шаг №1: Инициализация переменных, получение начальной и конечной временных меток из Redis const startDate = keysArray[0]; const endDate = keysArray[keysArray.length - 1]; Шаг №2: Расчет временной разницы в минутах между конечной датой Redis и заданной начальной датой. let startDifference = moment(endDate).diff(moment(formattedStartDate), 'minutes'); Шаг №3: Проверка, находится ли заданная начальная дата внутри или частично пересекается с временным диапазоном Redis. if (startDate <= formattedStartDate && formattedStartDate < endDate && Math.abs(startDifference) >= 5) { // Если условие выполняется, возвращаем true, указывая на пересечение временных диапазонов. return true; } Шаг №4: Расчет временной разницы в минутах между заданной конечной датой массива и начальной датой Redis. let endDifference = moment(formattedEndDate).diff(moment(startDate), 'minutes'); Шаг №5: Если ни одно из условий не выполнилось, возвращаем false, указывая на отсутствие пересечения временных диапазонов, иначе true return formattedStartDate <= startDate && startDate < formattedEndDate && Math.abs(endDifference) >= 5;