Перейти к содержимому

Справочник узлов ​

Все виды узлов, которые могут быть в эксперименте, — по группам меню добавления: действия, ожидания, эмуляция, сбои, данные, проверки и сценарий. Как их добавлять и соединять, описано в разделе Редактор; signallab nodes выводит тот же каталог в виде JSON — для скриптов и ассистентов (Командная строка).

Как читать эту страницу ​

У каждого узла есть таблица его полей:

  • Поле — название на панели свойств; В файле — ключ в JSON эксперимента.
  • По умолчанию — то, что узел получает при добавлении в редакторе. Если файл вправе опустить ключ, значение, которое тогда принимается, указано как если ключа нет; остальные ключи в файле обязательны.
  • Шаблоны: да — поле принимает {{templates}}: параметры, переменные, заданные раньше, секреты и генераторы, которые разрешаются в момент выполнения шага (Данные и шаблоны). Только параметры — поле открывается до первого шага, когда известны лишь параметры. Нет — значение берётся как написано.

Время указывается в миллисекундах. Пределы проверяются до начала запуска; поле вне диапазона не даёт эксперименту запуститься и отмечается на узле.

Узел в файле ​

В файле эксперимента узел — это объект с id (уникальным в эксперименте), его type, местом на холсте (x, y, ноль и больше), его полями и используемыми им настройками (retry, repeat, load; если выключены, их нет). Связь — это ребро от выхода одного узла (port; если его нет — next) к другому узлу:

json
{
  "nodes": [
    { "id": "start", "type": "start", "x": 40, "y": 80 },
    { "id": "ping", "type": "udp", "x": 270, "y": 80, "target": "127.0.0.1:9000", "text": "PING",
      "retry": { "attempts": 3, "delay_ms": 500, "backoff": "fixed" } },
    { "id": "end", "type": "end", "x": 500, "y": 80 }
  ],
  "edges": [
    { "from": "start", "to": "ping", "port": "next" },
    { "from": "ping", "to": "end", "port": "next" }
  ]
}

В примерах ниже показано по одному узлу — так, как его хранит файл.

Настройки, общие для многих узлов ​

Они включаются в нижней части свойств узла. Какой узел какие принимает, указано у каждого узла.

НастройкаКто её принимаетЧто делает
Повтор при ошибкеУзлы, которые отправляют или слушают: HTTP-запрос, TCP-сообщение, OSC-сообщение, UDP-датаграмма, Публикация MQTT, Подключение WebSocket, Отправка WebSocket и любое ожиданиеПробует снова, если шаг завершился ошибкой
Серия отправокУзлы, которые отправляют: HTTP-запрос, TCP-сообщение, OSC-сообщение, UDP-датаграмма, Публикация MQTT, Отправка WebSocketОтправляет снова и снова — заданное число раз или в течение заданного времени
НагрузкаHTTP-запросОтправляет запрос по профилю нагрузки, измеряет его и оценивает порогами
Ожидание ответаOSC-сообщение, UDP-датаграммаОтправляет и ждёт ответа на том же шаге

Повтор при ошибке ​

Флажок повторять при ошибке: если шаг завершился ошибкой — нет соединения, истёк таймаут, ожидание ничего не нашло, — он делает паузу и выполняется снова. Каждая неудачная попытка — строка в ленте; шаг завершается ошибкой, когда ею завершается последняя попытка. Шаблон, который не удаётся разрешить, не повторяется. Кнопка Стоп тоже прерывает паузу.

ПолеВ файлеЧто этоПо умолчанию и пределы
Попытокretry.attemptsВсего попыток, включая первую3; 2–10 в редакторе (в файле можно указать и 1)
Пауза, мсretry.delay_msПауза перед второй попыткой500; 0–60 000
Паузыretry.backoffодинаковые (fixed): каждый раз одна и та же пауза; удваиваются (exponential): после каждой неудачи вдвое длиннееfixed (и если ключа нет)

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

Серия отправок ​

Флажок отправлять серией: узел отправляет снова и снова — сигнал «жив», опрос, ровный поток — без цикла в схеме. Каждая отправка разрешает шаблоны заново ({{counter}} — её номер, {{now}} — её время), а повтор при ошибке, если он включён, применяется к каждой отправке. Шаг проходит, когда прошла каждая отправка; отправка, окончательно завершившаяся ошибкой, приводит к ошибке шага. Лента сообщает о ходе не чаще раза в секунду.

ПолеВ файлеЧто этоПо умолчанию и пределы
Повторятьrepeat.untilзаданное число раз (count) или в течение времени (duration)count (и если ключа нет)
Отправокrepeat.countВсего отправок, включая первую10 (и если ключа нет); 2–10 000
В течение, мсrepeat.duration_msКак долго отправлять, считая от первой отправки10 000 (и если ключа нет); 1–300 000
Каждые, мсrepeat.interval_msПауза между двумя отправками1 000; 10–60 000; в файле обязательно
Разброс, мсrepeat.jitter_msКаждая пауза длиннее не более чем на это значение, оно берётся из seed запуска0 (и если ключа нет); 0–60 000

Серии должны укладываться в 300 секунд запуска, а серия на время должна требовать менее 10 000 отправок (её время, делённое на интервал).

Нагрузка ​

Флажок отправлять под нагрузкой, только у узла HTTP-запрос: запрос отправляется по профилю — ровному темпу, росту, ступеням, всплеску или случайным приходам, — причём одновременно в пути может быть до 512 запросов (по умолчанию 32), и измеряется: задержки, ошибки, достигнутый темп. Пороги решают, проходит ли шаг. Нагрузка заменяет серию и повтор при ошибке (неудавшийся запрос подсчитывается, а не отправляется заново) и не оставляет ответа для проверок после неё. Её поля и результаты описаны в разделе Нагрузочное тестирование.

Ожидание ответа ​

Флажок ждать ответ, у узла OSC-сообщение или UDP-датаграмма: сообщение отправляется с порта, на котором ждут ответ, поэтому устройство, отвечающее отправителю, будет услышано, а шаг проходит, только если подходящий ответ пришёл вовремя. Нет ответа — шаг завершается ошибкой, а повтор при ошибке отправляет снова. Ответ сохраняется в переменной, как и у ожидания.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Ответ на (IP:порт)reply.bindIP:port, с которого отправляют и на котором слушают; порт 0 означает любой свободный порт0.0.0.0:0Нет
Шаблон адреса ответа (OSC)reply.addressШаблон адреса ответа, как у узла Ждать OSC/*Да
Условия на аргументы (OSC)reply.argsУсловия на аргументы, как у узла Ждать OSCнет; не более 16Значения: да
Содержимое ответа (UDP)reply.modeany, contains, regex или hex — см. Сопоставление содержимогоany (и если ключа нет)Нет
Шаблон (UDP)reply.patternЧто ответ должен содержать или чему соответствоватьпусто; обязательно, кроме anyДа
Таймаут, мсreply.timeout_msСколько ждать2 000 (и если ключа нет); 1–120 000Нет
Переменная ответаreply.variableПеременная, в которую сохраняется ответreply (и если ключа нет)Нет

Порт ответа открывается до первого шага, как и у ожидания.

Действия ​

Узлы, которые отправляют. Ожидание после действия учитывает сообщения с момента начала этого действия.

HTTP-запрос ​

Отправляет один HTTP-запрос и сохраняет ответ для проверок, ветвлений и узлов Извлечь значение после него.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Методrequest.methodGET, HEAD, POST, PUT, PATCH, DELETE или OPTIONS (в файле можно указать любой метод)GETНет
URLrequest.urlURL с http:// или https://http://127.0.0.1:8080/Да
Таймаут (мс)request.timeout_msНа весь обмен4 000 (10 000, если ключа нет); 1–120 000Нет
Заголовки запросаrequest.headers[[name, value], …]; строка с пустым именем пропускаетсянетДа, имена и значения
Телоrequest.bodyТекст или null, если тела нетnullДа
Аутентификацияrequest.authНет, Basic, Bearer-токен или Digest, с полями Имя пользователя и Пароль или ТокеннетДа
  • Любой ответ засчитывает шаг, включая 404 и 500: статус проверяйте узлом Статус HTTP или ветвитесь по нему узлом Ветвление по статусу. Запрос, на который нет ответа — отказ в соединении, таймаут, имя, которое не разрешается, недоверенный сертификат, — завершает шаг ошибкой.
  • Перенаправления выполняются, не более десяти. Сертификаты https:// проверяются.
  • Тело ответа сохраняется для проверок до 256 КиБ; более крупное обрезается на этой границе (проверки сообщают об этом, когда то, что они ищут, может оказаться за обрезкой).
  • Digest отвечает на запрос 401 от сервера и отправляет запрос снова. Учётные данные попадают только в запрос: шаги, отчёты и Инспектор никогда не показывают заголовок Authorization. Пишите пароль как {{secret.NAME}}.
  • Пока эксперимент хранит cookie (по умолчанию включено, в разделе Параметры), то, что установили серверы, отправляется им обратно в последующих запросах запуска.

Выход: Выход. Настройки: повтор при ошибке, серия, нагрузка.

json
{ "id": "cue", "type": "http", "x": 270, "y": 80,
  "request": { "method": "POST", "url": "{{api}}/cue", "headers": [["Content-Type", "application/json"]],
               "body": "{\"cue\": 1}", "timeout_ms": 5000,
               "auth": { "scheme": "bearer", "token": "{{secret.API_TOKEN}}" } } }

См. также HTTP.

TCP-сообщение ​

Подключается к хосту по TCP, записывает данные, ждёт до 250 мс первых байтов ответа (читает не более 1 024 байт, один раз) и закрывает соединение. Размер ответа сообщается, но не проверяется.

В Инспекторе шаг — это два кадра tcp с источником experiment: записанные данные и, если ответ пришёл, прочитанный ответ. Используемые секреты в обоих замаскированы, как в любом кадре.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
ХостhostИмя хоста или IP-адрес127.0.0.1Да
Портport9000; 1–65 535Нет
Таймаут (мс)timeout_msНа подключение, запись и ответ вместе4 000 (и если ключа нет); 1–120 000Нет
ДанныеpayloadТекст, записываемый после подключения, в UTF-8helloДа

Шаг завершается ошибкой, если в соединении отказано, имя не разрешается или время истекло. Выход: Выход. Настройки: повтор при ошибке, серия. Кнопка Отправить сейчас подключается и записывает данные один раз, а результат узла показывает, сколько байтов отправлено и сколько вернулось.

json
{ "id": "go", "type": "tcp", "x": 270, "y": 80, "host": "127.0.0.1", "port": 5000, "payload": "GO\r\n", "timeout_ms": 2000 }

OSC-сообщение ​

Отправляет одно сообщение OSC 1.0 по UDP.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Цель host:porttargetIP:port или host:port; имя хоста разрешается в момент отправки шага, берётся его адрес IPv4, если он есть127.0.0.1:9000Да
OSC-адресaddressНачинается с //testДа
Аргументыargs[{ "type", "value" }, …] — int, float, str, long, double, bool, blob (байты), nil (без значения)нетТекстовые (str) значения: да
ждать ответreplyНеобязательно: отправить и дождаться ответа — см. Ожидание ответавыкл.

Выход: Выход; если ожидается ответ, по нему идут, только когда ответ пришёл. Настройки: повтор при ошибке, серия, ожидание ответа. Кнопка ⚡ Пропустить через помехи в свойствах узла ставит перед ним узел Сетевые помехи.

json
{ "id": "fader", "type": "osc", "x": 270, "y": 80, "target": "{{device}}", "address": "/fader/1",
  "args": [{ "type": "float", "value": 0.75 }] }

См. также OSC.

UDP-датаграмма ​

Отправляет текстовое содержимое одной UDP-датаграммой на одну или несколько целей.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Цель host:porttargetIP:port или host:port; если их несколько, через запятую, точку с запятой или с новой строки, датаграмму получает каждая. Имя хоста разрешается в момент отправки шага, берётся его адрес IPv4, если он есть127.0.0.1:9000Да
ДанныеtextСодержимое в UTF-8hello; не более 65 507 байтДа
ждать ответreplyНеобязательно: отправить и дождаться ответа — см. Ожидание ответавыкл.

Шаг завершается ошибкой, если какая-либо цель недоступна. Выход: Выход. Настройки: повтор при ошибке, серия, ожидание ответа.

json
{ "id": "ping", "type": "udp", "x": 270, "y": 80, "target": "{{device}}", "text": "PING {{run.id}}",
  "reply": { "bind": "0.0.0.0:0", "mode": "contains", "pattern": "PONG", "timeout_ms": 1000, "variable": "pong" } }

Публикация MQTT ​

Подключается к брокеру MQTT, публикует одно сообщение и отключается. Соединение — MQTT 3.1.1 по обычному TCP, с чистой сессией, без имени пользователя и пароля. Подключение, публикация и подтверждение брокера должны уложиться в 15 секунд.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Адрес брокераhostИмя хоста или адрес брокера127.0.0.1Да
Портport1883; 1–65 535Нет
ТопикtopicБез подстановочных символов (+, #)lab/testДа
ДанныеpayloadСообщение в виде текстаhelloДа
QoSqos0, 1 или 20Нет
Сохранить на брокереretaintrue: брокер сохраняет его как значение топикаfalseНет

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

json
{ "id": "light", "type": "mqtt", "x": 270, "y": 80, "host": "{{broker}}", "port": 1883,
  "topic": "lab/light/1/set", "payload": "on", "qos": 1, "retain": false }

См. также MQTT.

Подключение WebSocket ​

Открывает WebSocket до конца запуска или до узла Закрытие WebSocket. Всё, что приходит с этого момента, сохраняется для шагов Ожидание WebSocket на нём. URL и заголовки разрешаются, когда шаг выполняется, поэтому в них может быть токен, извлечённый раньше. Если узел выполняется снова — в узле Цикл, — он сначала закрывает прежнее соединение и открывает новое. Когда запуск заканчивается, как бы это ни произошло, его соединения закрываются кадром закрытия.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
URLurlURL с ws:// или wss://ws://127.0.0.1:9001/Да
Заголовки запросаheaders[[name, value], …], отправляются с запросом upgradeнетДа, имена и значения
ПодпротоколыprotocolsПодпротоколы, которые предлагаются в порядке предпочтения; сервер выбирает одиннетНет
Таймаут (мс)timeout_msНа подключение и upgrade5 000 (10 000, если ключа нет); 1–120 000Нет

wss:// доверяет тем же сертификатам, что и https://. Шаг завершается ошибкой, если не удалось подключиться или выполнить upgrade; статус сервера указан в причине. Выход: Выход. Настройки: повтор при ошибке (серии нет).

json
{ "id": "socket", "type": "ws_connect", "x": 270, "y": 80, "url": "ws://127.0.0.1:9001/chat",
  "headers": [["Authorization", "Bearer {{token}}"]], "protocols": ["chat.v1"], "timeout_ms": 5000 }

См. также WebSocket.

Отправка WebSocket ​

Отправляет одно сообщение по соединению, которое открыл узел Подключение WebSocket.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
СоединениеconnectionИдентификатор узла Подключение WebSocket этого экспериментапервыйНет
ФорматbinaryТекст (false) или Двоичное (hex) (true): содержимое — байты, записанные в hex: de ad be effalse (и если ключа нет)Нет
ДанныеtextСообщениеhello; не более 16 МиБДа

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

json
{ "id": "hello", "type": "ws_send", "x": 500, "y": 80, "connection": "socket",
  "text": "{\"type\":\"ping\",\"id\":\"{{uuid}}\"}", "binary": false }

Закрытие WebSocket ​

Закрывает соединение закрывающим рукопожатием. Лента показывает, кто его закрыл: этот шаг, сервер раньше (с его кодом) или соединение, которое оборвалось.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
СоединениеconnectionИдентификатор узла Подключение WebSocketпервыйНет
Код закрытияcode1000 (обычное) или 3000–4999 для кодов приложения1000 (и если ключа нет)Нет
ПричинаreasonОтправляется вместе с кодомпусто; не более 123 байт, после разрешения шаблоновДа

Выход: Выход. Настроек нет.

json
{ "id": "bye", "type": "ws_close", "x": 960, "y": 80, "connection": "socket", "code": 1000, "reason": "done" }

Лог / Отметка ​

Записывает строку в ленту и в отчёт — контрольную точку или значения, до которых дошёл запуск.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
СообщениеmessageТекстCheck point; не более 10 000 символовДа

Выход: Выход. Настроек нет.

json
{ "id": "ready", "type": "log", "x": 500, "y": 80, "message": "device {{device}} ready" }

Ожидания ​

Группа Ожидание: узлы, которые ждут, пока что-нибудь придёт. У них общие правила:

  • Слушают с начала запуска. Порт ожидания или подписка на брокере открываются до первого шага, поэтому устройство, которое отвечает быстрее, чем начинается следующий шаг, не будет пропущено. Два ожидания на одном адресе используют один сокет.
  • Считают от последнего действия в своей ветке. Сообщение, пришедшее до последнего запроса ветки, не считается ответом на него; до любого действия учитывается всё, что пришло с начала запуска.
  • Берётся первое подходящее сообщение. Сообщение, которое взяло одно ожидание, другое не увидит.
  • Получено или Таймаут. При совпадении сообщение сохраняется в переменной ожидания, и запуск идёт по выходу Получено. Когда время истекает, он идёт по выходу Таймаут, если у того есть связь; иначе шаг завершается ошибкой, сообщая, сколько других сообщений пришло.
  • Каждый сокет хранит последние 1 024 сообщения (и 64 МиБ); более старые отбрасываются, а таймаут сообщает, сколько их было.
  • Кнопка Слушать сейчас слушает только на этом шаге — с текущего момента.

Выходы: Получено (обязателен), Таймаут (необязателен). Настройки: повтор при ошибке.

Сопоставление содержимого ​

Узлы Ждать UDP, Ждать MQTT, Ожидание WebSocket и ответ UDP выбирают, как должно выглядеть содержимое:

ВариантВ файлеПодходит, когда содержимое
Любая датаграммаanyлюбое
Содержит текстcontainsпрочитанное как текст UTF-8, содержит шаблон (с учётом регистра)
Соответствует regexregexпрочитанное как текст UTF-8, соответствует регулярному выражению
Содержит байты (hex)hexсодержит байты, записанные парами hex: de ad be ef, deadbeef, 0xde,0xad, DE:AD

Найденное сообщение сохраняется как объект. Последующие шаги читают его поля как {{reply.text}} (вместо reply подставляется имя переменной):

ПолеЧто это
textСодержимое как текст
hex, bytesСодержимое в hex (его первые 1 024 байта) и его размер в байтах
matchЧто совпало: текст, первая группа регулярного выражения (или всё совпадение) либо байты
fromIP:port отправителя
msМиллисекунды от последнего действия ветки (или от начала запуска) до сообщения
topicЖдать MQTT: топик, в который оно опубликовано
json, kindОжидание WebSocket: сообщение, разобранное как JSON (null, если это не JSON), и text или binary

Ждать OSC ​

Ждёт сообщение OSC, адрес которого соответствует шаблону, а аргументы удовлетворяют всем условиям. Из пакета (bundle) берётся первое подходящее сообщение.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Слушать (IP:порт)bindIP:port, на котором слушать; 0.0.0.0 — на всех сетевых картах127.0.0.1:9001Нет
Шаблон адресаaddress* — любые символы, ? — один, [0-9] — набор ([!0-9] — вне него), {ping,pong} — любое из двух; подстановки не выходят за пределы одного сегмента //pong; не более 512 символовДа
Условия на аргументыargs[{ "index", "op", "value" }, …]: аргумент index сравнивается со значением value операцией op (Сравнения); должны выполняться всенет; не более 16, индекс 0–63Значения: да
Таймаут, мсtimeout_ms2 000 (и если ключа нет); 1–120 000Нет
Переменная ответаvariableГде сохраняется сообщениеreply (и если ключа нет)Нет

Аргумент сравнивается как текст: числа — как записаны, строки — без кавычек, true/false, blob — в hex. Условие на аргумент, которого нет в сообщении, не выполняется. В сохранённом сообщении есть address, args ({{reply.args[0]}}), from и ms.

json
{ "id": "status", "type": "wait_osc", "x": 500, "y": 80, "bind": "0.0.0.0:9001", "address": "/status",
  "args": [{ "index": 0, "op": "eq", "value": "ready" }], "timeout_ms": 5000, "variable": "reply" }

Ждать UDP ​

Ждёт датаграмму UDP, содержимое которой подходит.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Слушать (IP:порт)bindIP:port, на котором слушать127.0.0.1:9001Нет
СодержимоеmodeСм. Сопоставление содержимогоcontains (any, если ключа нет)Нет
ШаблонpatternЧто содержимое должно содержать или чему соответствоватьpong; обязательно, кроме anyДа
Таймаут, мсtimeout_ms2 000 (и если ключа нет); 1–120 000Нет
Переменная ответаvariablereply (и если ключа нет)Нет
json
{ "id": "ready", "type": "wait_udp", "x": 500, "y": 80, "bind": "0.0.0.0:9002", "mode": "contains",
  "pattern": "READY", "timeout_ms": 5000, "variable": "reply" }

Ждать MQTT ​

Ждёт сообщение, опубликованное в топик на брокере, содержимое которого подходит. Запуск подключается и подписывается до первого шага. Retained-сообщения, которые брокер воспроизводит при подписке, игнорируются: учитывается только то, что опубликовано после начала запуска.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Адрес брокераhostБрокер127.0.0.1Только параметры
Портport1883; 1–65 535Нет
Фильтр топикаtopicФильтр: + — любой один уровень, # — всё ниже (только последним)lab/#Только параметры
СодержимоеmodeСм. Сопоставление содержимогоany (и если ключа нет)Нет
Шаблонpatternпусто; обязательно, кроме anyДа
Таймаут, мсtimeout_ms2 000 (и если ключа нет); 1–120 000Нет
Переменная ответаvariablereply (и если ключа нет)Нет
json
{ "id": "state", "type": "wait_mqtt", "x": 500, "y": 80, "host": "{{broker}}", "port": 1883,
  "topic": "lab/+/state", "mode": "contains", "pattern": "on", "timeout_ms": 5000, "variable": "reply" }

Ждать HTTP-запрос ​

Ждёт HTTP-запрос — вебхук, обратный вызов — к узлу Эмулятор запуска по этому адресу или, если HTTP-эмулятора там нет, к собственному слушателю запуска, который отвечает на каждый запрос кодом 204. Запрос должен соответствовать методу, пути и каждому условию.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Слушать (IP:порт)bindIP:port127.0.0.1:18080 — там, где слушает новый узел ЭмуляторНет
МетодmethodМетод или Любой (ANY); GET принимает и HEADANY (и если ключа нет)Нет
Путьpath/hooks/:name даёт имя сегменту ({{request.params.name}}); /* в конце берёт остальное/* (и если ключа нет); не более 512 символовДа
Условияwhen[{ "on", "name", "op", "value" }, …] по header, параметру query, body или пути json; должно выполняться каждоенет; не более 16Да, имена и значения
Таймаут, мсtimeout_ms5 000 (2 000, если ключа нет); 1–120 000Нет
Переменная ответаvariablerequest (и если ключа нет)Нет

В сохранённом запросе есть method, path, query, headers, body, json, params, from и ms: {{request.json.event}}, {{request.headers.x-key}}.

json
{ "id": "hook", "type": "wait_http", "x": 500, "y": 80, "bind": "127.0.0.1:18081", "method": "POST",
  "path": "/hooks/:name", "when": [{ "on": "json", "name": "$.event", "op": "eq", "value": "deploy" }],
  "timeout_ms": 5000, "variable": "request" }

Ожидание WebSocket ​

Ждёт сообщение по соединению, которое открыл узел Подключение WebSocket, с подходящим содержимым. Учитываются сообщения с момента последнего действия в ветке — самого подключения, отправки или любого другого запроса.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
СоединениеconnectionИдентификатор узла Подключение WebSocketпервыйНет
СодержимоеmodeСм. Сопоставление содержимогоany (и если ключа нет)Нет
Шаблонpatternпусто; обязательно, кроме anyДа
Таймаут, мсtimeout_ms2 000 (и если ключа нет); 1–120 000Нет
Переменная ответаvariablereply (и если ключа нет)Нет

Сообщение JSON читается по полям: {{reply.json.type}}. Подключение должно стоять перед ожиданием на его пути.

json
{ "id": "pong", "type": "wait_ws", "x": 730, "y": 80, "connection": "socket", "mode": "contains",
  "pattern": "pong", "timeout_ms": 3000, "variable": "reply" }

Эмуляция ​

Эмулятор ​

Изображает зависимость — HTTP API, устройство OSC, UDP или TCP, брокер MQTT — на весь запуск. Он открывается до первого шага и отвечает, пока запуск не закончится; в ходе запуска шаг проходит сразу. Всё, что он получил, подсчитывается по правилам в отчёте о запуске.

ПолеВ файлеЧто этоПо умолчанию
Изменить…emulatorЭмулятор: name, bind (IP:port), protocol (http, osc, udp, tcp, mqtt), его маршруты или правила и необязательный outageHTTP API с именем API на 127.0.0.1:18080, отвечающий на /health

Свойства показывают одной строкой, что он изображает. Кнопка Изменить… открывает его правила — тот же редактор, что и на экране Эмуляторы; кнопка В библиотеку сохраняет копию в библиотеке эмуляторов, а Из библиотеки заменяет этот эмулятор копией из неё. Правила — маршруты, ответы, сбои, отключения — описаны там.

  • HTTP-эмулятор — это ещё и то, что читает узел Ждать HTTP-запрос на его адресе; эмулятор OSC или UDP делит свой порт с ожиданиями запуска на нём.
  • Два эмулятора одного транспорта в запуске не могут делить порт.
  • Узел Эмулятор: выкл./вкл. отключает его и возвращает обратно.

Выход: Выход. Настроек нет.

json
{ "id": "api", "type": "emulator", "x": 270, "y": 80,
  "emulator": { "name": "Orders API", "bind": "127.0.0.1:18080", "protocol": "http",
    "routes": [{ "method": "GET", "path": "/orders/:id", "order": "sequence",
                 "responses": [{ "status": 503 }, { "status": 200, "body": "{\"id\":\"{{request.params.id}}\"}" }] }] } }

Сбои ​

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

Сетевые помехи ​

Реле помех на весь запуск: тестируемая система отправляет данные (или подключается) не на настоящую цель, а на адрес из поля Слушать; реле пересылает их на адрес из поля Пересылать, ответы возвращаются тем же путём, ухудшенные профилем. Оно открывается до первого шага и закрывается, когда запуск заканчивается, как бы это ни произошло, так что ничего не остаётся ухудшенным; в ходе запуска шаг проходит сразу. Каждое решение берётся из seed запуска: один и тот же seed и один и тот же трафик получают одну и ту же судьбу.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
СлушатьlistenIP:port, на который отправляет тестируемая система127.0.0.1:9010Только параметры
ПересылатьtargetIP:port настоящего адресата или host:port — имя хоста разрешается при старте запуска, а имя, которое не найдено, останавливает запуск на этом узле127.0.0.1:9000Только параметры
ПротоколprotocolUDP (udp): у каждой датаграммы своя судьба; TCP (tcp): каждое соединение связывается с собственным соединением к цели, и оба потока ухудшаютсяUDP (udp, если ключа нет)Нет
Пресет и значения под нимprofileЧто реле делает с трафиком — см. ПрофильLAN (без помех, если ключа нет)Нет

Адрес, на котором слушает реле, не может быть другим сокетом запуска, а реле не могут пересылать друг другу по кругу. Цель, заданная именем, прослеживается после того, как имя разрешено, поэтому круг через имя останавливает запуск при его старте. Отчёт считает каждую фазу реле отдельно.

Выход: Выход. Настроек нет.

json
{ "id": "relay", "type": "impairment", "x": 270, "y": 80, "listen": "127.0.0.1:9010", "target": "{{device}}",
  "profile": { "name": "lan", "latency_ms": 1, "jitter_ms": 1 } }

Профиль ​

Плашка пресета — LAN, Загруженный Wi-Fi, 4G, Спутник, С перебоями, Нет связи — заполняет все значения; любое из них можно потом изменить. Реле читает только значения своего протокола; в файле любой ключ можно опустить (ноль, выкл.).

ПолеВ файлеЧто этоПределыПротокол
—nameПодпись для ленты и отчёта: ключ пресета (lan, wifi, 4g, satellite, intermittent, offline) или ваша собственнаяне более 60 символовоба
Нет связи — ничего не проходитofflineНичего не проходитtrue / falseоба
Задержкаlatency_msЗадержка, добавляемая к каждому пакету или куску потока0–60 000 (ползунок доходит до 1 000)оба
Джиттерjitter_msСлучайная добавочная задержка до этого значения; поток TCP сохраняет порядок0–60 000 (ползунок доходит до 500)оба
Полоса, кбит/сrate_kbpsОграничение полосы, 0 — без ограничения. UDP: сверх секунды очереди датаграммы отбрасываются как превысившие ограничение; TCP: отправитель замедляется, ничего не отбрасывается0 или 8–10 000 000оба
Потери пакетовlossВероятность того, что датаграмма отброшена0–1 (ползунок показывает %)UDP
Пачки потерь, Длина пачки, датаграммburst_start, burst_lengthВероятность начала пачки потерь и сколько датаграмм она длится в среднем0–1; 1–1 000, когда пачки включеныUDP
ДублированиеduplicateВероятность того, что датаграмма отправлена дважды0–1UDP
ИскаженияcorruptВероятность того, что один бит датаграммы перевёрнут0–1UDP
ПерестановкаreorderВероятность того, что датаграмма задержана и более поздние её обгоняют0–1UDP
Сброс соединенияresetВероятность того, что кусок потока вместо передачи сбрасывает соединение — обе стороны получают сброс0–1TCP
ПолуоткрытоеstallВероятность того, что кусок оставляет соединение полуоткрытым: больше ничего не проходит ни в одну сторону, и ни одна сторона об этом не узнаёт0–1TCP

Подробнее о реле, пресетах и о том, что они моделируют, — на странице Помехи.

Сменить помехи ​

Переключает одно из реле запуска — узлов Сетевые помехи — на другой профиль начиная с этого шага, не отпуская порт. Фаза, что шла до этого, закрывается и подсчитывается в отчёте.

ПолеВ файлеЧто этоПо умолчаниюШаблоны
ПомехиrelayИдентификатор узла Сетевые помехи этого экспериментапервыйНет
Пресет и значения под нимprofileЧем оно ухудшает трафик с этого момента — см. Профиль; реле читает значения своего протоколаНет связи (без помех, если ключа нет)Нет

Шаг завершается ошибкой, если реле не работает — например, оно не смогло пересылать. Выход: Выход. Настроек нет.

json
{ "id": "cut", "type": "impairment_change", "x": 730, "y": 200, "relay": "relay",
  "profile": { "name": "offline", "offline": true } }

Эмулятор: выкл./вкл. ​

Отключает один из эмуляторов запуска или снова включает его. Пока он отключён, HTTP-эмулятор отвечает так, как сказано в поле Пока отключён; устройство TCP и брокер MQTT обрывают соединения и отказывают новым; устройства OSC и UDP ничего не отвечают. Включённый снова, эмулятор следует собственному расписанию отключений, если оно у него есть.

ПолеВ файлеЧто этоПо умолчанию
ЭмуляторemulatorИдентификатор узла Эмулятор этого экспериментапервый
СостояниеdownОтключён (true) или Работает (false)отключён (false, если ключа нет)
Пока отключёнfaultТолько HTTP: 503 Недоступен (unavailable), Закрыть соединение (reset: соединение закрывается без ответа) или Без ответа (timeout: запрос удерживается, пока клиент не сдастся, но не дольше 120 с)unavailable (и если ключа нет)

Выход: Выход. Настроек нет. Шаблоны нигде не применяются.

json
{ "id": "down", "type": "emulator_state", "x": 500, "y": 200, "emulator": "api", "down": true, "fault": "unavailable" }

Данные ​

Извлечь значение ​

Сохраняет часть последнего HTTP-ответа на своём пути как переменную — для последующих полей ({{token}}), проверок и ветвлений. На каждом пути перед ним должен стоять HTTP-запрос. Щелчок по значению в ответе на Отправить сейчас добавляет такой узел за вас.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
ПеременнаяvariableИмя: буквы, цифры и _, не начинается с цифры, не зарезервированное слово и не имя параметраtokenНет
Откуда взятьfromПоле JSON (json), Заголовок (header), Код статуса (status), Всё тело (body) или Регулярное выражение (regex)jsonНет
JSON-путь, Имя заголовка или Регулярное выражение (группа 1, если есть)exprJSON-путь ($.data.token, $.items[0], $["first name"]), имя заголовка (в любом регистре) или регулярное выражение — его первая группа либо всё совпадение$.token; для статуса и тела не используетсяНет

Шаг завершается ошибкой, если брать нечего: тело не JSON, пути или заголовка нет, выражение не совпало или — для поля JSON либо всего тела — тело было длиннее сохранённых 256 КиБ. Статус сохраняется как число; остальное — как текст или как найденное значение JSON. Выход: Выход. Настроек нет.

json
{ "id": "token", "type": "extract", "x": 500, "y": 80, "variable": "token", "from": "json", "expr": "$.data.token" }

Подробнее о переменных — в разделе Данные и шаблоны.

Проверки ​

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

Статус HTTP ​

Проходит, когда статус последнего ответа в точности равен заданному.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Ожидаемый статусstatus200; 100–599Нет

Выход: Выход. Настроек нет.

json
{ "id": "ok", "type": "assert_status", "x": 500, "y": 80, "status": 200 }

Текст ответа ​

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

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Содержит текстcontainsok; обязательноДа

Выход: Выход. Настроек нет.

json
{ "id": "ready", "type": "assert_body", "x": 500, "y": 80, "contains": "ready" }

Заголовок ответа ​

Проходит, когда в последнем ответе есть заголовок и его значение содержит этот текст. Имя заголовка сопоставляется в любом регистре, значение — точно.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Имя заголовкаnamecontent-type; обязательноДа
Содержит текстcontainsЧто должно содержать его значение; пусто — достаточно, чтобы заголовок былapplication/jsonДа

Выход: Выход. Настроек нет.

json
{ "id": "json", "type": "assert_header", "x": 500, "y": 80, "name": "Content-Type", "contains": "json" }

Время ответа ​

Проходит, когда последний ответ занял не больше этого времени — от отправки запроса до конца его тела.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Максимальное время, мсmax_ms1 000; 1–120 000Нет

Выход: Выход. Настроек нет.

json
{ "id": "fast", "type": "assert_latency", "x": 500, "y": 80, "max_ms": 250 }

Проверка значения ​

Сравнивает значение — обычно переменную, записанную шаблоном, — с ожидаемым и проходит, когда сравнение выполняется.

ПолеВ файлеЧто этоПо умолчаниюШаблоны
ЗначениеvalueЧто сравнивается: {{token}}, {{reply.args[0]}}{{token}}Да
УсловиеopСм. Сравненияне пустоНет
ОжидаетсяexpectedНе используется условиями пусто и не пустопусто (и если ключа нет)Да

Выход: Выход. Настроек нет.

json
{ "id": "state", "type": "assert_value", "x": 730, "y": 80, "value": "{{state}}", "op": "eq", "expected": "ready" }

Сравнения ​

Узлы Проверка значения и Ветвление по значению, условие выхода узла Цикл, условия на аргументы OSC и условия HTTP сравнивают одинаково:

ВариантВ файлеВыполняется, когда значение
равноeqравно ожидаемому — как числа, когда оба числа (200 = 200.0), иначе как точный текст
не равноneне равно ему по тому же правилу
меньше, не больше, больше, не меньшеlt, le, gt, geменьше, не больше, больше, не меньше — оба должны быть числами: иначе проверка, ветвление или узел Цикл завершает шаг ошибкой, а условие на аргумент или условие HTTP не выполняется
содержитcontainsсодержит ожидаемый текст
соответствует regexmatchesсоответствует ожидаемому регулярному выражению
пусто, не пустоempty, not_emptyпусто (пробелы считаются пустотой) / не пусто

Сценарий ​

Узлы, которые решают, куда пойдёт запуск. Подробнее о ветках, слияниях и циклах — в разделе Как идёт запуск.

Старт ​

Здесь начинается запуск; в каждом эксперименте ровно один такой узел. У него нет ни входа, ни полей. Первая строка ленты показывает seed запуска.

Выход: Выход, обязателен. Несколько связей от него сразу запускают параллельные ветки.

json
{ "id": "start", "type": "start", "x": 40, "y": 80 }

Финиш ​

Здесь запуск завершается; в каждом эксперименте ровно один такой узел, и выходов у него нет. К нему могут вести несколько веток: запуск проходит один раз, после того как закончила последняя ветка, и только если ни одна не завершилась ошибкой. Запуск, который так и не дошёл до узла Финиш, проваливается.

json
{ "id": "end", "type": "end", "x": 960, "y": 80 }

Пауза ​

Ждёт заданное время перед следующим шагом.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Задержка (мс)ms300; 0–60 000Нет

Выход: Выход. Настроек нет. Для более долгого ожидания поставьте несколько таких узлов подряд или в узел Цикл.

json
{ "id": "pause", "type": "delay", "x": 500, "y": 80, "ms": 500 }

Ветвление по статусу ​

Выбирает выход Да, когда последний HTTP-ответ имеет этот статус, иначе — Нет. На каждом пути перед ним должен стоять HTTP-запрос.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Ожидаемый статусstatus200; 100–599Нет

Выходы: Да и Нет, оба обязательны. Настроек нет.

json
{ "id": "branch", "type": "branch_status", "x": 500, "y": 80, "status": 200 }

Ветвление по значению ​

Выбирает выход Да, когда сравнение выполняется, иначе — Нет. Его поля и сравнения те же, что у узла Проверка значения; сравнение, которое нельзя выполнить (lt для текста), завершает шаг ошибкой.

ПолеВ файлеЧто этоПо умолчаниюШаблоны
ЗначениеvalueЧто сравнивается{{token}}Да
УсловиеopравноНет
Ожидаетсяexpectedпусто (и если ключа нет)Да

Выходы: Да и Нет, оба обязательны. Настроек нет.

json
{ "id": "ok", "type": "branch_value", "x": 730, "y": 80, "value": "{{reply.args[0]}}", "op": "eq", "expected": "ok" }

Параллельный запуск ​

Выполняет то, что следует за выходами Поток 1 и Поток 2, одновременно, причём у каждой ветки своя копия переменных. У каждого выхода может быть больше связей — для большего числа веток. Полей нет.

Выходы: Поток 1 и Поток 2, оба обязательны.

json
{ "id": "split", "type": "fork", "x": 270, "y": 80 }

Слияние потоков ​

Ждёт, пока будет достигнута каждая ведущая в него связь, затем продолжает один раз, слив переменные веток — если две ветки задают одну переменную, побеждает та, чья связь в файле идёт позже, — и последний HTTP-ответ из последней из них, у которой он был. Полей нет.

Здесь встречаются только ветки, которые выполняются все вместе: узел Слияние потоков, стоящий за узлом Ветвление по статусу (его выходы Да и Нет никогда не срабатывают оба сразу), никогда не продолжит; если ни один другой путь не достигает Финиш, запуск завершается ошибкой на этом узле, сообщая, скольких веток он всё ещё ждал.

Выход: Выход, обязателен.

Любой другой узел, в который ведёт несколько связей, выполняется по разу при каждом приходе.

json
{ "id": "joined", "type": "join", "x": 730, "y": 80 }

Цикл ​

Снова и снова выполняет шаги на выходе Тело, которые ведут обратно к нему: не более заданного числа раз и, если у него есть условие выхода, пока оно не выполнится.

ПолеВ файлеЧто этоПо умолчанию и пределыШаблоны
Итераций не большеmaxНаибольшее число итераций5; 1–1 000Нет
выйти раньше, еслиuntilНеобязательное условие выхода { "value", "op", "expected" }, как у узла Проверка значениявыкл.Значение и ожидаемое: да
  • Тело всегда выполняется хотя бы один раз. Условие выхода читается после каждой итерации, поэтому тело может задать то, что оно проверяет.
  • Выход Готово выбирается, когда условие выполняется — или, без условия, после последней итерации.
  • Выход Лимит выбирается, когда итерации кончились раньше, чем выполнилось условие. Без связи на нём это завершает шаг ошибкой.
  • Внутри тела {{counter}} — номер итерации.
  • Тело выполняется как одна ветка: у каждого выхода внутри него одна связь; в нём нет узлов Старт, Финиш, Параллельный запуск, Слияние потоков и других узлов Цикл; в него входят только через Тело; и каждая связь в нём ведёт дальше в теле или обратно к циклу.

Выходы: Тело и Готово (обязательны), Лимит (необязателен). Настроек нет.

json
{ "id": "poll", "type": "loop", "x": 270, "y": 80, "max": 10,
  "until": { "value": "{{status.args[0]}}", "op": "eq", "expected": "ready" } }