Содержание
Программные зависимости
- 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;
