Работа с историческими данными

Добрый день друзья и соратники.

В логике автоматики часто приходится обращаться к историческим данным.
Как я не искал – не смог найти в WB-Rules реализованного способа оперировать историческими данными, которые контроллер хранит. Единственный способ, который я нашел – вызов консольного клиента и парсинг текстового результата (что не очень удобно, на самом деле). Кроме того, функция runShellCommand асинхронна, и если задача предполагает какую-то последующую передачу полученных результатов (например среднего значения показателя за период) то это становится вообще проблемой.

Как-то не вериться, что столь фундаментальный и основополагающий для любых систем управления функционал в системе не реализован. Возможно существует “человеческий” способ оперировать историческими данными – буду благодарен за наводку.
Если же его все таки нет (ПОЧЕМУ, черт подери), то может кто-то подскажет, как реализовать синхронный обработчик исторических данных, который возвращал бы результат как функция.

Сейчас я реализовал это костылями, через вызов по CRON, записи в отдельный контрол виртуального устройства и правилом на изменение значение этого контрола, но это очень не удобно, тяжело читать и отлаживать. Попытался написать модуль, но не понимаю, как дождаться выполнения асинхронного вызова и выдернуть результат из callback (очень далек от JS и честно говоря местами он причиняет мне боль).

И еще просьба к разработчикам. Не знаю как для других пользователей, но мне остро не хватает возможности получить предидущее (до изменения) значение параметра при вызове правила по триггеру whenChanged, чтобы можно было просто отслеживать на сколько значение изменилось.

Если выходить за рамки логики “Если нажали кнопку нужно замкнуть реле”, то работа с историческим контекстом становится важной и нужной, а учитывая общую гибкость системы при ее простоте (за что разработчикам отдельный респект), которая позволяет в принципе реализовать управление запуском космического корабля, так и хочется реализовывать сложные многоуровневые сценарии, но некоторые вещи приходится так костылять, что код становится совершенно не поддерживаемым и с ним приходится буквально бороться.

1 лайк

Описаан RPC метод: wb-mqtt-db/README.md at master · wirenboard/wb-mqtt-db · GitHub

Клиент использует именно этот метод.

Спасибо за общую наводку. Поразбираюсь с этим методом.

При беглом знакомстве он не кажется удобнее предидущего))) Но собственно проблема: на сколько я понимаю, метод trackMQTT, с помощью которого я могу получить результат обратно, также асинхронный и проблему не решает.

Кроме того, по вашей наводке зашел посмотреть что в этом топике, и с ужасом выяснил, что там остается каждый уникальный запрос сделанный через консольного клиента. За полгода работы контроллера у меня их там накопилось попросту неприличное количество. Существует ли способ их (топики) массово удалить?

Очень хотел бы попросить разработчиков реализовать методы внутри WB-Rules для работы с историческими данными. Это ОЧЕНЬ сильно упростит жизнь.

Добрый день!
Если я вас верно понял, то для очистки очереди сообщений вам понадобится вот эта статья из документации.

1 лайк

Да, похоже это то что нужно. Спасибо.

1 лайк

А какие методы? Работа с RPC - довольно тривиальна, запрос и разбор ответа.
Для примера Dooya DT82 (Onviz MR-2234F) - #15 от пользователя BrainRoot

Ознакомился с примером. Метод взаимодействия я понял правильно.
Сказать по правде, на мой дилетантский взгляд такой код (да и метод в целом) тривиальным не назвать, к сожалению.

Я подразумевал методы в рамках WB-Rules вида:

getHistoryData(devName, cellName, fromTime, toTime)
который бы синхронно возвращал данные вида:
{ time : { min: val, avg: val, max: val}}

getLastValue(devName,cellName)
который бы вернул последнее значение в архиве в виде:
{ time: value }

getNearestValue(devName, cellName, time)
который бы вернул ближайшее к time значение в архиве в виде:
{ time: value }

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

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

Один из банальных примеров:
Есть виртуальный термостат. Например мне необходимо принимать решение о тех или иных действиях на основании того, на сколько эффективно работает текущий режим. А для этого нужно оценить, на сколько изменилась температура за последние сколько-то времени. Причем время для оценки роста температуры зависит от того, какой метод ее изменения в данный момент применяется (включен теплый пол, включен радиатор, включен кондиционер, работает ли вентиляция).
Каким образом в правило, принимающее решение о том, нужно ли задействовать новый метод воздействия на температуру можно передать данные о изменении температуры в исторической ретроспективе?

Или, например, для управления смесителем отопления нужно округлять данные скользящим окном, чтобы не гонять смеситель “туда-сюда” на выбросах возникающих при тактовании котла или переключении на БКН.

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

При текущем подходе, если мне нужно обращаться к историческим данным от случая к случаю, я либо должен их постоянно рассчитывать “впрок” создавая дополнительную нагрузку на систему, либо под каждую задачу прямо в правиле писать логику запроса и кучу вложенных callback-ов.

Чтобы получить последнее значение сейчас нужно делать два запроса.
А получить ближайшее значение ко времени вообще гарантированного способа нет, так как может получится так, что значение параметра долго не менялось и тогда нужно будет увеличивать окно запроса пока не попадется значение. (да, надуманная ситуация, но реальная).

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

А что вы тут имеете в виду? В топике хранится (если хранится) только одно значение.

Добрый день.
Вот сначала хотел написать про то что можно реализовать запрос исторических данных модулем wb-rules.
Потом задумался - сам я ведь так не делаю. Применяю именно что хранение в массивах в самих скриптах. Как раз по той причине что история - событийная.

Каждый запрос через клиента создает уникальный топик в соответствующей ветке, который используется фактически 1 раз. Если делать это из кода, то этим можно манипулировать, чтобы не создавать лишние сущности, но я это делаю вызывая консольный клиент, который безальтернативно каждому запросу присваивает уникальный ID (что в общем-то логично, так как клиент может быть вызван параллельно несколько раз).

В итоге получается, что в этой ветке хранятся топики содержащие результаты всех запросов за все время. У меня их накопилось уже больше 29 тысяч. При этом каждый запрос в топике reply хранит JSON, который может содержать несколько сотен (а в редких случаях и тысяч) блоков.

Я эту ветку MQTT Explorer-ом даже открыть не могу – он виснет.

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

P.S. Я почему-то всегда думал, что MQTT работает как брокер сообщений, то есть фактически никаких данных не хранит, а клиент видит только те топики и данные которые были отправлены в период его подключения.

В зависимости от retained признака топика. Если установлен - да, будет храниться.

Собственно я тоже в какой-то момент решил вынести это в модуль, так как количество повторяющегося кода стало очень велико. Но так и не придумал, как засунуть результат в return. Попробовал прикрутить Promise, но то ли не разобрался как это правильно работает, то-ли движок не поддерживает.

В процессе этого обсуждения пришел к выводу, что оптимальным для меня будет действительно реализовать в отдельном модуле структуры хранящие данные в PersistentStorage и обвязку “складирующую” туда данные и отлеживающую глубину хранения. В идеале конечно оформить как класс, чтобы в коде скрипта просто создавать такие хранилища со всей обвязкой как объекты, но как я понял WB-Rules создание классов не поддерживает.

Будет разминка для ума в общем.

1 лайк