Простой или работа? Отличить их поможет одна строка JEXL

TL;DR. Грузовик с работающим двигателем на стоянке — это либо впустую сожженное топливо, либо оплаченная работа через вал отбора мощности (PTO), а скорость в обоих случаях показывает ноль. Отличить одно от другого помогает одна строка выражения IoT Logic, которая читает нужный бит статус-слова. В этом разборе агент собирает правило через API целиком: читает живую схему, строит граф, включает его и запускает поток телеметрии, — а затем независимый канал подтверждает, что правило вычислило именно то, что нужно. Визуальный холст остается за человеком — для проверки, одобрения включения и отладки.
Грузовик стоит десять минут с работающим двигателем. Простой или работа? Может быть, водитель греется в кабине и впустую жжет топливо. А может, двигатель крутит вал отбора мощности — качает насос, поднимает стрелу крана, — и это работа, за которую платят.
По скорости не отличить: в обоих случаях она нулевая. Расход топлива почти не помогает — показания похожи. Разница спрятана в одном бите статус-слова, который передает шлюз машины.
Вот и весь фокус, если коротко: одно выражение IoT Logic считывает этот бит и превращает «стоит, двигатель работает» в два противоположных вердикта, а независимый поток — тот самый, что не писал правило, — доказывает, что оно вычислило именно то, что вы имели в виду.
Сразу договоримся об одном. У IoT Logic есть визуальный drag-and-drop редактор: вы размещаете узлы на холсте и соединяете их — это удобно, и это настоящая сила платформы. Но есть и документированный программный доступ: сценарий — это типизированная схема, которую вы читаете и записываете через REST; в документации есть гайд по генерации сценариев силами ИИ; а платформа отдельно отдает read-only MCP-сервер для аудита. Именно этот доступ позволяет агенту работать по контракту, а не через экран.
В примере ниже агент проходит весь цикл на одноразовом тестовом аккаунте: читает живую схему, собирает граф, включает сценарий для теста, запускает поток телеметрии и снова его выключает. Холст при этом никуда не пропадает: человек открывает тот же сценарий и Data Stream Analyzer, чтобы проверить граф, одобрить включение в продакшене, проверить монтаж на грузовике и отладить логику. Сборка достается агенту, а включение и проверка остаются за человеком.
Ответ — в одном бите статус-слова
Вот что приходит с провода — одно целое число, упакованное статус-слово:
can_status_word = 4 # 0b100 — PTO выключен
can_status_word = 5 # 0b101 — PTO включен (бит 0 установлен)
Число приходит в платформу как обычное числовое поле телеметрии. Шлюз пакует статус-слово — разбор шины и упаковка происходят на устройстве, — поэтому внутри правила вы работаете с уже готовым значением: ни строчки прошивки, ни внешнего сервиса.
Это синтетическое статус-слово — упакованное так, как его упаковал бы CAN-шлюз; реальную битовую раскладку возьмите из контракта вашего устройства или DBC, а не из этого поста. Бит 0 — иллюстративная позиция, выбранная, чтобы показать технику: офсет своего реального сигнала PTO вы подставите сами.
util:checkBit — вся декодировка в одной строке
В IoT Logic есть язык выражений — тот же JEXL, который вы наверняка видели в десятке других инструментов, плюс несколько функций для работы с битами. Нужный бит достается так:
util:checkBit(can_status_word, 0)
Добавляем скорость — и получаем три вычисляемых атрибута; смысл каждого — в одной фразе справа:
pto_engaged = util:checkBit(can_status_word, 0)
wasteful_idle = speed < 3 && !util:checkBit(can_status_word, 0) # стоит, PTO выключен
productive_idle = speed < 3 && util:checkBit(can_status_word, 0) # стоит, PTO включен
В продакшене к speed < 3 добавили бы через && сигнал зажигания или отметку «двигатель работает» — тогда «стоит» действительно значит, что двигатель включен. Здесь speed < 3 — временная замена. Она держит фокус на главном: бите PTO, том самом, который скорость сама по себе не показывает.
Вот и весь «декодер»: три выражения в узле правила — не отдельный микросервис и не парсер в вашем бэкенде, который надо поддерживать. Каждое выражение возвращает булево значение, по которому ветвится сценарий: wasteful_idle со значением true — то самое условие, на которое вы бы реагировали. Превратить булево значение в реальное уведомление — отдельный шаг (датчик или правило поверх этого); сам сценарий только вычисляет и направляет поток. Синтаксис и полный список битовых операций — в справочнике выражений Navixy.
Само правило — это простой граф из четырех узлов, собранный через REST: источник данных, узел вычисляемых атрибутов, узел логики и один выходной узел. Ни action, ни webhook — правило вычисляет и направляет поток, но не отдает ни одной команды. Первая запись уходит с enabled:false, поэтому даже корректный граф не тронет данные до отдельного, проверенного включения.
Агент строит граф — вы открываете его, чтобы проверить
Тот же граф, который агент собрал через API, открывается в визуальном редакторе как обычный сценарий — узел за узлом:
Никто не тянул узлы мышкой. Но и скрытого здесь ничего нет: человек открывает этот экран, видит те же четыре узла и ветвление, может отредактировать любой из них или выключить сценарий одним переключателем. Холст — место, где работу агента можно увидеть и проверить.
Один бит переворачивает вердикт при нулевой скорости
Собрать граф — не все: нужно убедиться, что он вычисляет именно то, что вы задумали. Сохранился ли граф после записи, показывает обратное чтение; а как правило ведет себя на живых данных — это отдельный канал, Data Stream Analyzer (DSA). Подпишитесь на WebSocket-поток DSA, отправьте три пакета телеметрии — по одному на состояние, у каждого своя скорость и свое статус-слово, — и DSA независимо покажет, что вычислило правило.
| Что отправил один пакет | Что показал DSA на том же тике |
|---|---|
speed=45, can_status_word=4 — едет, PTO выключен |
pto=false, wasteful=false, productive=false, flagged=false |
speed=0, can_status_word=4 — стоит, PTO выключен |
pto=false, wasteful=true, productive=false, flagged=true |
speed=0, can_status_word=5 — стоит, PTO включен |
pto=true, wasteful=false, productive=true, flagged=false |
Читайте сверху вниз. Движение — это вообще не простой, поэтому ничего не отмечено. Грузовик встал с выключенным PTO — wasteful становится true, а с ним и flagged: вот он, пустой простой. Меняем один бит статус-слова, скорость оставляем нулевой — и картина переворачивается: flagged гаснет, productive становится true. Та же стоянка, противоположный вердикт — именно та разница, которую по скорости не увидеть.
Есть одна деталь, из-за которой это доказательство, а не совпадение. Каждый пакет несет уникальный маркер, и проверка ищет ровно один тик DSA с этим маркером, а затем сравнивает внутри этого тика отправленные и вычисленные поля. Скорость из одного сообщения не может случайно склеиться со статус-словом из другого. Вот на чем держится вся дисциплина: доказательство приходит из канала, который ничего не писал.
Вот весь прогон, сведенный к четырем значениям — синтетический прогон на одноразовом тестовом аккаунте: три состояния совпали на своих тиках, а правило после проверки осталось выключенным:
{
"status": "succeeded",
"dsa_verified": true,
"final_disabled_verified": true,
"inventory_verified": true
}
Кто отвечает за что: агент пишет, холст проверяет
Каждый участник отвечает за свою часть — поэтому ни один ответ не принимается на веру за все остальные:
- Агент через REST пишет конфигурацию и обратным чтением подтверждает, что граф и
enabled:trueдействительно сохранились. - NGP принимает телеметрию; его HTTP 200 говорит только о доставке.
- DSA независимо показывает вычисленный результат на потоке.
- User MCP остается на стороне чтения — сервер User MCP читает и аудирует, а любое изменение проходит через REST.
- Человек работает в визуальном редакторе: проверяет сценарий, собранный агентом, одобряет включение, проверяет данные на установленном грузовике, отлаживает через DSA, держит переключатель и владеет ключом. Это и есть точка контроля — человек в контуре поверх агента.
На практике разумный дефолт такой: правило можно считать рабочим не в момент, когда create вернул успех, а когда канал, не участвовавший в записи, показал ожидаемый результат. У маршрута через прямые REST-вызовы есть и родственный путь, встроенный прямо в продукт, — Navixy AI Assistant, где сценарий собирает ассистент, а человек подтверждает. Оба ведут к одному и тому же объекту на одном и том же холсте.
Когда бит декодировать не нужно
У этой техники узкая область применения. Если ваш шлюз уже отдает готовый флаг «PTO включен» отдельным полем — возьмите это поле как есть и ничего не декодируйте. Если вопрос разовый, а данных немного, выгрузка в таблицу и десять минут работы обгонят любое правило.
Разбор битов в IoT Logic оправдывает себя, когда сигнал приходит упакованным, потоком, непрерывно, а вам нужно различать состояния на лету — а не восстанавливать их из логов постфактум.
Ваш сигнал: что поменять, что оставить
Замените can_status_word и демонстрационный бит на настоящее числовое поле вашего трекера или шлюза, а офсет возьмите из контракта устройства или DBC — тот, что относится к нужному вам сигналу. Остальной порядок сохраните: сначала живая схема, затем сборка с enabled:false, обратное чтение, полное обновление при включении и проверка в DSA по уникальному маркеру.
Единственная команда, которую стоит выполнить руками до всего остального, — preflight-проверка: прочитать живую схему и убедиться, что в вашем окружении есть нужные типы узлов. Ключ создается один раз в Account Settings → API Keys и считывается из переменной окружения:
curl -fsS \
-H "Authorization: NVX ${NAVIXY_API_KEY}" \
-H 'Accept: application/json' \
'https://api.us.navixy.com/v2/iot/logic/flow/schema'
После этого — три выражения выше и обратное чтение после каждой записи. Обратное чтение подтверждает, что сценарий сохранился таким, каким его записали; независимая проверка в DSA подтверждает, что он вычисляет именно те состояния, которые вы имели в виду.
Все это остается набором отдельных вызовов с обратным чтением и проверкой в DSA не случайно: у каждого шага появляется машинно проверяемый критерий «прошло / не прошло» — одно совпадение при поиске по метке, один тик DSA с уникальным маркером, финальное enabled:false, подтвержденное чтением. Типизированная схема, гайд по генерации сценариев силами ИИ и read-only MCP существуют именно для того, чтобы этот цикл мог выполнять агент.
Редактор — привычный способ собрать сценарий, и это удобно. Но я думаю, что сборка графов будет все больше переходить к агентам, а холст и DSA останутся тем местом, где человек проверяет сборку, контролирует монтаж и отлаживает логику, — и тогда за человеком остается одна операция, которую нельзя делегировать: создать ключ и отозвать его.
- Ответ — в одном бите статус-слова
- util:checkBit — вся декодировка в одной строке
- Агент строит граф — вы открываете его, чтобы проверить
- Один бит переворачивает вердикт при нулевой скорости
- Кто отвечает за что: агент пишет, холст проверяет
- Когда бит декодировать не нужно
- Ваш сигнал: что поменять, что оставить