Простой или работа? Отличить их поможет одна строка 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, открывается в визуальном редакторе как обычный сценарий — узел за узлом:

    Сценарий, который агент собрал через API, открыт в визуальном редакторе Navixy IoT Logic: источник данных, затем декодирование статус-слова, затем ветвление по признаку пустого простоя — обе ветви сходятся в одном выходном узле

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

    Один бит переворачивает вердикт при нулевой скорости

    Собрать граф — не все: нужно убедиться, что он вычисляет именно то, что вы задумали. Сохранился ли граф после записи, показывает обратное чтение; а как правило ведет себя на живых данных — это отдельный канал, Data Stream Analyzer (DSA). Подпишитесь на WebSocket-поток DSA, отправьте три пакета телеметрии — по одному на состояние, у каждого своя скорость и свое статус-слово, — и DSA независимо покажет, что вычислило правило.

    Три состояния PTO и простоя: что отправил один пакет и что вычислило правило на том же тике 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 останутся тем местом, где человек проверяет сборку, контролирует монтаж и отлаживает логику, — и тогда за человеком остается одна операция, которую нельзя делегировать: создать ключ и отозвать его.

    Поделиться