Работа со встроенными модулями

Во время проектирования процесса обработки данных и создания обработчика у пользователя может возникнуть потребность в подключении и использовании вспомогательных библиотек. Помимо основных встроенных библиотек в архитектурный слой обработки данных добавлены модули, представляющие собой дополнительные библиотеки, которые могут понадобиться при создании алгоритма обработки данных с целью его оптимизации. Каждый из этих модулей обладает богатым набором возможностей, который является достаточно удобным, что облегчает сам процесс разработки.

Встроенные библиотеки необходимо специально запрашивать. Функция импортирования встроенных модулей создана для того, чтобы в процессе запуска и выполнения алгоритма, заданного в обработчике, не приходилось обращаться к избыточным ресурсам. Те ресурсы, которые были вызваны, но которые не используются, могут замедлять процесс обработки, чего лучше стараться избегать.

Вызов любого из встроенных модулей осуществляется следующим образом. В начале необходимо создать новую переменную типа данных const, затем ей необходимо присвоить функцию на запрос, в которой указывается наименование необходимого модуля или указатель на него:

const m = require('name_of_module');

В процессе разработки исходного кода для обработчика могут быть добавлены следующие модули:

Примеры загрузки и использования встроенных модулей приведены в репозитории на GitHub.

Math.js

Math.js — это расширенная библиотека, находящаяся в открытом доступе и используемая для JavaScript и Node.js. Она обладает широким набором встроенных функций и математических констант, позволяя производить огромный комплекс различных математических операций над большими числовыми данными, комплексными числами, дробями, матрицами и т. д. Возможности библиотеки являются достаточно обширными, что позволяет упрощать математические вычисления в ходе выполнения алгоритма.

В процессе обработки данных зачастую требуется произвести расчет некой новой величины, данные по которой не были напрямую получены от устройства. Поэтому использование данного встроенного модуля в ряде случаев сможет облегчить процесс обработки.

Для того чтобы загрузить данный встроенный модуль, необходимо локально в функции или глобально вызвать его:

const math = require('mathjs');

Далее обращение к библиотеке осуществляется через переменную math.

После вызова модуля можно использовать все функции, встроенные в данную библиотеку.

Например, при обработке данных необходимо вычислить производную какой-либо величины. Тогда в обработчике нужно:

  • загрузить библиотеку math.js;
  • вычислить производную;
  • присвоить ее переменной типа let, значение которой можно изменить;
  • в полученное выражение подставить значение входной переменной;
  • преобразовать полученный формат данных object в строку для вывода и получения конечного результата.
// calculates a derivative

const math = require('mathjs');

function process(a) {
  let derivative = math.derivative('a^3 + 4*a^2 + 7', 'a');     //3*a^2 + 8*a
  derivative = derivative.eval({a});
  const da = derivative.toString();
  return { da };
}

Buffer

Этот модуль представляет собой класс Buffer, представленный как часть API Node.js, которая позволяет работать и взаимодействовать с потоками различных двоичных данных. Данный класс работает с объектами типа TypedArray, которые визуально похожи на массивы двоичных данных, но применяются именно при работе с буферами. Модуль Buffer может оказаться полезным при обработке данных именно по причине работы с потоками. Ведь, когда речь идет о потоках, подразумевается, что происходит отправка и перемещение данных. Например, зачастую необходимо передать данные на обработку, сделать их доступными для считывания. В таких случаях, как правило, возникают очереди пакетов данных, запрошенных на обработку. Эта очередь выстраивается в буфере.

Буфер представляет собой некую часть оперативной памяти, в которой накапливаются данные, выстраиваются в очередь в соответствии со временем отправки запроса и на выходе отправляются на обработку друг за другом. Технологии Node.js предусмотрены так, что они позволяют создать и отправить запрос, однако не могут контролировать потоки данных. С этой целью уже применяются буферы.

При работе с технологиями Интернета вещей может возникнуть необходимость в обработке данных по определенному протоколу. Например, от устройства по протоколу могут быть отправлены данные в виде последовательности шестнадцатеричных чисел. Они требуют дальнейшей обработки, в частности, парсинга. Данные могут накапливаться и ожидать своей очереди на обработку. Для того чтобы эту очередь выстроить и управлять ею, можно воспользоваться обработчиком, внутри которого реализована работа с буфером.

Она начинается с вызова встроенного модуля Buffer:

const { Buffer } = require('buffer');

Внутри функции создается буфер. При его создании могут быть указаны входные данные в качестве содержимого буфера, кодировка данных, размерность буфера (количество байт). В рассматриваемом примере при создании двух буферов в них изначально записываются входные данные, т. е. значения определенных параметров модели. Далее суммируются их размерности. После чего буферы объединяются в один. Для корректного вывода содержимого буфера его необходимо декодировать.

// creates two buffers and combines them

const { Buffer }  = require('buffer');

function process(a, b) {
  const buf_a = Buffer.from(a);
  const buf_b = Buffer.from(b);
  const totalLength = buf_a.length + buf_b.length;
  const buf_sum = Buffer.concat([buf_a, buf_b], totalLength).toString();
  return { buf_sum };
}

History

Встроенный модуль history позволяет управлять историей и забирать последние пакеты данных. Он возвращает массив объектов, в котором содержатся только данные, отправленные ранее, не включая текущий пакет. Количество пакетов, которые извлекаются из истории, по умолчанию равно 50. Однако при необходимости пользователь может сам задать нужное ему число пакетов. Модуль history может быть полезен при анализе данных, при сравнении текущих данных с предыдущими, для некоторых расчетов на основе предыдущих значений параметров и т. п.

При работе с данным модулем изначально необходимо его вызвать. Стоит учитывать, что вызов встроенных модулей reject, history и split осуществляется через указатель @ric/handler/. Например,

const history = require('@ric/handler/history');

Далее загружаются пакеты данных из истории. В методе history.get() для выгрузки нескольких пакетов данных указываются одна или несколько переменных, соответствующие определенным входным или выходным параметрам функции, и число выгружаемых пакетов, одинаковое для всех указанных параметров. Метод history.last() позволяет выгрузить только последний пакет данных, хранящийся в истории. При этом любая из двух указанных функций может вернуть пустой массив объектов, если история для указанного параметра на текущий момент еще не была записана.

Входными данными для функции, приведенной в примере использования метода history.get(), является переменная a. Ей соответствует определенный параметр модели объекта. В указанном примере рассчитывается среднее арифметическое десяти значений переменной a, поэтому можно считать, что ей присваивается значение числового аргумента. Соответственно, для данного параметра выгружается девять последних пакетов из истории. Далее рассчитывается среднее арифметическое значений числового аргумента на основе одного текущего и девяти предыдущих значений.

// calculates the average value

const history = require('@ric/handler/history');

function process(a) {
  const packets = history.get(['a'], 10);
  let avg = a;
  for (let i = 0; i < packets.length; i++) {
      avg += packets[i].a;;
  }
  avg /= (packets.length + 1)
  return { avg }
}

В примере использования метода history.last() рассчитывается значение скорости, скалярное значение которого вычисляется как разность перемещения, деленная на время. В данном случае переменная времени является параметром по умолчанию, ее значение фиксировано. Разность перемещения рассчитывается как разность текущего и предыдущего значения. При этом осуществляется проверка на наличие поля в объекте. Если оно есть и объект не пустой, то тогда переменной prevS присваивается значение, хранящееся в последнем пакете данного параметра в истории. Если же объект пустой и история еще не была сохранена, то тогда значение этой переменной останется равным нулю.

// calculates a speed

const history = require('@ric/handler/history');

function process(t = 5, s) {
  let prevS = 0;  
  const last = history.last(['s']);
  if ('s' in last) prevS = +last.s;
  const v = (s - prevS) / t;
  return { v };
}

Модуль history позволяет выгружать массив объектов. Соответственно, нужно помнить, что к каждому загруженному значению обращение осуществляется как к элементу массива. При этом необходимо учитывать особенности работы с объектами.