Справочник узлов
Все виды узлов, которые могут быть в эксперименте, — по группам меню добавления: действия, ожидания, эмуляция, сбои, данные, проверки и сценарий. Как их добавлять и соединять, описано в разделе Редактор; signallab nodes выводит тот же каталог в виде JSON — для скриптов и ассистентов (Командная строка).
Как читать эту страницу
У каждого узла есть таблица его полей:
- Поле — название на панели свойств; В файле — ключ в JSON эксперимента.
- По умолчанию — то, что узел получает при добавлении в редакторе. Если файл вправе опустить ключ, значение, которое тогда принимается, указано как если ключа нет; остальные ключи в файле обязательны.
- Шаблоны: да — поле принимает
{{templates}}: параметры, переменные, заданные раньше, секреты и генераторы, которые разрешаются в момент выполнения шага (Данные и шаблоны). Только параметры — поле открывается до первого шага, когда известны лишь параметры. Нет — значение берётся как написано.
Время указывается в миллисекундах. Пределы проверяются до начала запуска; поле вне диапазона не даёт эксперименту запуститься и отмечается на узле.
Узел в файле
В файле эксперимента узел — это объект с id (уникальным в эксперименте), его type, местом на холсте (x, y, ноль и больше), его полями и используемыми им настройками (retry, repeat, load; если выключены, их нет). Связь — это ребро от выхода одного узла (port; если его нет — next) к другому узлу:
{
"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.bind | IP:port, с которого отправляют и на котором слушают; порт 0 означает любой свободный порт | 0.0.0.0:0 | Нет |
| Шаблон адреса ответа (OSC) | reply.address | Шаблон адреса ответа, как у узла Ждать OSC | /* | Да |
| Условия на аргументы (OSC) | reply.args | Условия на аргументы, как у узла Ждать OSC | нет; не более 16 | Значения: да |
| Содержимое ответа (UDP) | reply.mode | any, contains, regex или hex — см. Сопоставление содержимого | any (и если ключа нет) | Нет |
| Шаблон (UDP) | reply.pattern | Что ответ должен содержать или чему соответствовать | пусто; обязательно, кроме any | Да |
| Таймаут, мс | reply.timeout_ms | Сколько ждать | 2 000 (и если ключа нет); 1–120 000 | Нет |
| Переменная ответа | reply.variable | Переменная, в которую сохраняется ответ | reply (и если ключа нет) | Нет |
Порт ответа открывается до первого шага, как и у ожидания.
Действия
Узлы, которые отправляют. Ожидание после действия учитывает сообщения с момента начала этого действия.
HTTP-запрос
Отправляет один HTTP-запрос и сохраняет ответ для проверок, ветвлений и узлов Извлечь значение после него.
| Поле | В файле | Что это | По умолчанию и пределы | Шаблоны |
|---|---|---|---|---|
| Метод | request.method | GET, HEAD, POST, PUT, PATCH, DELETE или OPTIONS (в файле можно указать любой метод) | GET | Нет |
| URL | request.url | URL с 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 (по умолчанию включено, в разделе Параметры), то, что установили серверы, отправляется им обратно в последующих запросах запуска.
Выход: Выход. Настройки: повтор при ошибке, серия, нагрузка.
{ "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 | Да |
| Порт | port | 9000; 1–65 535 | Нет | |
| Таймаут (мс) | timeout_ms | На подключение, запись и ответ вместе | 4 000 (и если ключа нет); 1–120 000 | Нет |
| Данные | payload | Текст, записываемый после подключения, в UTF-8 | hello | Да |
Шаг завершается ошибкой, если в соединении отказано, имя не разрешается или время истекло. Выход: Выход. Настройки: повтор при ошибке, серия. Кнопка Отправить сейчас подключается и записывает данные один раз, а результат узла показывает, сколько байтов отправлено и сколько вернулось.
{ "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:port | target | IP: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 | Необязательно: отправить и дождаться ответа — см. Ожидание ответа | выкл. |
Выход: Выход; если ожидается ответ, по нему идут, только когда ответ пришёл. Настройки: повтор при ошибке, серия, ожидание ответа. Кнопка ⚡ Пропустить через помехи в свойствах узла ставит перед ним узел Сетевые помехи.
{ "id": "fader", "type": "osc", "x": 270, "y": 80, "target": "{{device}}", "address": "/fader/1",
"args": [{ "type": "float", "value": 0.75 }] }См. также OSC.
UDP-датаграмма
Отправляет текстовое содержимое одной UDP-датаграммой на одну или несколько целей.
| Поле | В файле | Что это | По умолчанию и пределы | Шаблоны |
|---|---|---|---|---|
| Цель host:port | target | IP:port или host:port; если их несколько, через запятую, точку с запятой или с новой строки, датаграмму получает каждая. Имя хоста разрешается в момент отправки шага, берётся его адрес IPv4, если он есть | 127.0.0.1:9000 | Да |
| Данные | text | Содержимое в UTF-8 | hello; не более 65 507 байт | Да |
| ждать ответ | reply | Необязательно: отправить и дождаться ответа — см. Ожидание ответа | выкл. |
Шаг завершается ошибкой, если какая-либо цель недоступна. Выход: Выход. Настройки: повтор при ошибке, серия, ожидание ответа.
{ "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 | Да |
| Порт | port | 1883; 1–65 535 | Нет | |
| Топик | topic | Без подстановочных символов (+, #) | lab/test | Да |
| Данные | payload | Сообщение в виде текста | hello | Да |
| QoS | qos | 0, 1 или 2 | 0 | Нет |
| Сохранить на брокере | retain | true: брокер сохраняет его как значение топика | false | Нет |
Все шесть ключей в файле обязательны. Шаг завершается ошибкой, если брокер недоступен или отказывает в соединении либо в сообщении. Выход: Выход. Настройки: повтор при ошибке, серия.
{ "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 и заголовки разрешаются, когда шаг выполняется, поэтому в них может быть токен, извлечённый раньше. Если узел выполняется снова — в узле Цикл, — он сначала закрывает прежнее соединение и открывает новое. Когда запуск заканчивается, как бы это ни произошло, его соединения закрываются кадром закрытия.
| Поле | В файле | Что это | По умолчанию и пределы | Шаблоны |
|---|---|---|---|---|
| URL | url | URL с ws:// или wss:// | ws://127.0.0.1:9001/ | Да |
| Заголовки запроса | headers | [[name, value], …], отправляются с запросом upgrade | нет | Да, имена и значения |
| Подпротоколы | protocols | Подпротоколы, которые предлагаются в порядке предпочтения; сервер выбирает один | нет | Нет |
| Таймаут (мс) | timeout_ms | На подключение и upgrade | 5 000 (10 000, если ключа нет); 1–120 000 | Нет |
wss:// доверяет тем же сертификатам, что и https://. Шаг завершается ошибкой, если не удалось подключиться или выполнить upgrade; статус сервера указан в причине. Выход: Выход. Настройки: повтор при ошибке (серии нет).
{ "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 ef | false (и если ключа нет) | Нет |
| Данные | text | Сообщение | hello; не более 16 МиБ | Да |
Подключение должно стоять перед отправкой на её пути; отправка, соединение которой не открыто, завершается ошибкой. Ответы считаются с момента записи сообщения. Выход: Выход. Настройки: повтор при ошибке, серия.
{ "id": "hello", "type": "ws_send", "x": 500, "y": 80, "connection": "socket",
"text": "{\"type\":\"ping\",\"id\":\"{{uuid}}\"}", "binary": false }Закрытие WebSocket
Закрывает соединение закрывающим рукопожатием. Лента показывает, кто его закрыл: этот шаг, сервер раньше (с его кодом) или соединение, которое оборвалось.
| Поле | В файле | Что это | По умолчанию и пределы | Шаблоны |
|---|---|---|---|---|
| Соединение | connection | Идентификатор узла Подключение WebSocket | первый | Нет |
| Код закрытия | code | 1000 (обычное) или 3000–4999 для кодов приложения | 1000 (и если ключа нет) | Нет |
| Причина | reason | Отправляется вместе с кодом | пусто; не более 123 байт, после разрешения шаблонов | Да |
Выход: Выход. Настроек нет.
{ "id": "bye", "type": "ws_close", "x": 960, "y": 80, "connection": "socket", "code": 1000, "reason": "done" }Лог / Отметка
Записывает строку в ленту и в отчёт — контрольную точку или значения, до которых дошёл запуск.
| Поле | В файле | Что это | По умолчанию и пределы | Шаблоны |
|---|---|---|---|---|
| Сообщение | message | Текст | Check point; не более 10 000 символов | Да |
Выход: Выход. Настроек нет.
{ "id": "ready", "type": "log", "x": 500, "y": 80, "message": "device {{device}} ready" }Ожидания
Группа Ожидание: узлы, которые ждут, пока что-нибудь придёт. У них общие правила:
- Слушают с начала запуска. Порт ожидания или подписка на брокере открываются до первого шага, поэтому устройство, которое отвечает быстрее, чем начинается следующий шаг, не будет пропущено. Два ожидания на одном адресе используют один сокет.
- Считают от последнего действия в своей ветке. Сообщение, пришедшее до последнего запроса ветки, не считается ответом на него; до любого действия учитывается всё, что пришло с начала запуска.
- Берётся первое подходящее сообщение. Сообщение, которое взяло одно ожидание, другое не увидит.
- Получено или Таймаут. При совпадении сообщение сохраняется в переменной ожидания, и запуск идёт по выходу Получено. Когда время истекает, он идёт по выходу Таймаут, если у того есть связь; иначе шаг завершается ошибкой, сообщая, сколько других сообщений пришло.
- Каждый сокет хранит последние 1 024 сообщения (и 64 МиБ); более старые отбрасываются, а таймаут сообщает, сколько их было.
- Кнопка Слушать сейчас слушает только на этом шаге — с текущего момента.
Выходы: Получено (обязателен), Таймаут (необязателен). Настройки: повтор при ошибке.
Сопоставление содержимого
Узлы Ждать UDP, Ждать MQTT, Ожидание WebSocket и ответ UDP выбирают, как должно выглядеть содержимое:
| Вариант | В файле | Подходит, когда содержимое |
|---|---|---|
| Любая датаграмма | any | любое |
| Содержит текст | contains | прочитанное как текст UTF-8, содержит шаблон (с учётом регистра) |
| Соответствует regex | regex | прочитанное как текст UTF-8, соответствует регулярному выражению |
| Содержит байты (hex) | hex | содержит байты, записанные парами hex: de ad be ef, deadbeef, 0xde,0xad, DE:AD |
Найденное сообщение сохраняется как объект. Последующие шаги читают его поля как {{reply.text}} (вместо reply подставляется имя переменной):
| Поле | Что это |
|---|---|
text | Содержимое как текст |
hex, bytes | Содержимое в hex (его первые 1 024 байта) и его размер в байтах |
match | Что совпало: текст, первая группа регулярного выражения (или всё совпадение) либо байты |
from | IP:port отправителя |
ms | Миллисекунды от последнего действия ветки (или от начала запуска) до сообщения |
topic | Ждать MQTT: топик, в который оно опубликовано |
json, kind | Ожидание WebSocket: сообщение, разобранное как JSON (null, если это не JSON), и text или binary |
Ждать OSC
Ждёт сообщение OSC, адрес которого соответствует шаблону, а аргументы удовлетворяют всем условиям. Из пакета (bundle) берётся первое подходящее сообщение.
| Поле | В файле | Что это | По умолчанию и пределы | Шаблоны |
|---|---|---|---|---|
| Слушать (IP:порт) | bind | IP: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_ms | 2 000 (и если ключа нет); 1–120 000 | Нет | |
| Переменная ответа | variable | Где сохраняется сообщение | reply (и если ключа нет) | Нет |
Аргумент сравнивается как текст: числа — как записаны, строки — без кавычек, true/false, blob — в hex. Условие на аргумент, которого нет в сообщении, не выполняется. В сохранённом сообщении есть address, args ({{reply.args[0]}}), from и ms.
{ "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:порт) | bind | IP:port, на котором слушать | 127.0.0.1:9001 | Нет |
| Содержимое | mode | См. Сопоставление содержимого | contains (any, если ключа нет) | Нет |
| Шаблон | pattern | Что содержимое должно содержать или чему соответствовать | pong; обязательно, кроме any | Да |
| Таймаут, мс | timeout_ms | 2 000 (и если ключа нет); 1–120 000 | Нет | |
| Переменная ответа | variable | reply (и если ключа нет) | Нет |
{ "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 | Только параметры |
| Порт | port | 1883; 1–65 535 | Нет | |
| Фильтр топика | topic | Фильтр: + — любой один уровень, # — всё ниже (только последним) | lab/# | Только параметры |
| Содержимое | mode | См. Сопоставление содержимого | any (и если ключа нет) | Нет |
| Шаблон | pattern | пусто; обязательно, кроме any | Да | |
| Таймаут, мс | timeout_ms | 2 000 (и если ключа нет); 1–120 000 | Нет | |
| Переменная ответа | variable | reply (и если ключа нет) | Нет |
{ "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:порт) | bind | IP:port | 127.0.0.1:18080 — там, где слушает новый узел Эмулятор | Нет |
| Метод | method | Метод или Любой (ANY); GET принимает и HEAD | ANY (и если ключа нет) | Нет |
| Путь | path | /hooks/:name даёт имя сегменту ({{request.params.name}}); /* в конце берёт остальное | /* (и если ключа нет); не более 512 символов | Да |
| Условия | when | [{ "on", "name", "op", "value" }, …] по header, параметру query, body или пути json; должно выполняться каждое | нет; не более 16 | Да, имена и значения |
| Таймаут, мс | timeout_ms | 5 000 (2 000, если ключа нет); 1–120 000 | Нет | |
| Переменная ответа | variable | request (и если ключа нет) | Нет |
В сохранённом запросе есть method, path, query, headers, body, json, params, from и ms: {{request.json.event}}, {{request.headers.x-key}}.
{ "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_ms | 2 000 (и если ключа нет); 1–120 000 | Нет | |
| Переменная ответа | variable | reply (и если ключа нет) | Нет |
Сообщение JSON читается по полям: {{reply.json.type}}. Подключение должно стоять перед ожиданием на его пути.
{ "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), его маршруты или правила и необязательный outage | HTTP API с именем API на 127.0.0.1:18080, отвечающий на /health |
Свойства показывают одной строкой, что он изображает. Кнопка Изменить… открывает его правила — тот же редактор, что и на экране Эмуляторы; кнопка В библиотеку сохраняет копию в библиотеке эмуляторов, а Из библиотеки заменяет этот эмулятор копией из неё. Правила — маршруты, ответы, сбои, отключения — описаны там.
- HTTP-эмулятор — это ещё и то, что читает узел Ждать HTTP-запрос на его адресе; эмулятор OSC или UDP делит свой порт с ожиданиями запуска на нём.
- Два эмулятора одного транспорта в запуске не могут делить порт.
- Узел Эмулятор: выкл./вкл. отключает его и возвращает обратно.
Выход: Выход. Настроек нет.
{ "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 и один и тот же трафик получают одну и ту же судьбу.
| Поле | В файле | Что это | По умолчанию и пределы | Шаблоны |
|---|---|---|---|---|
| Слушать | listen | IP:port, на который отправляет тестируемая система | 127.0.0.1:9010 | Только параметры |
| Пересылать | target | IP:port настоящего адресата или host:port — имя хоста разрешается при старте запуска, а имя, которое не найдено, останавливает запуск на этом узле | 127.0.0.1:9000 | Только параметры |
| Протокол | protocol | UDP (udp): у каждой датаграммы своя судьба; TCP (tcp): каждое соединение связывается с собственным соединением к цели, и оба потока ухудшаются | UDP (udp, если ключа нет) | Нет |
| Пресет и значения под ним | profile | Что реле делает с трафиком — см. Профиль | LAN (без помех, если ключа нет) | Нет |
Адрес, на котором слушает реле, не может быть другим сокетом запуска, а реле не могут пересылать друг другу по кругу. Цель, заданная именем, прослеживается после того, как имя разрешено, поэтому круг через имя останавливает запуск при его старте. Отчёт считает каждую фазу реле отдельно.
Выход: Выход. Настроек нет.
{ "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–1 | UDP |
| Искажения | corrupt | Вероятность того, что один бит датаграммы перевёрнут | 0–1 | UDP |
| Перестановка | reorder | Вероятность того, что датаграмма задержана и более поздние её обгоняют | 0–1 | UDP |
| Сброс соединения | reset | Вероятность того, что кусок потока вместо передачи сбрасывает соединение — обе стороны получают сброс | 0–1 | TCP |
| Полуоткрытое | stall | Вероятность того, что кусок оставляет соединение полуоткрытым: больше ничего не проходит ни в одну сторону, и ни одна сторона об этом не узнаёт | 0–1 | TCP |
Подробнее о реле, пресетах и о том, что они моделируют, — на странице Помехи.
Сменить помехи
Переключает одно из реле запуска — узлов Сетевые помехи — на другой профиль начиная с этого шага, не отпуская порт. Фаза, что шла до этого, закрывается и подсчитывается в отчёте.
| Поле | В файле | Что это | По умолчанию | Шаблоны |
|---|---|---|---|---|
| Помехи | relay | Идентификатор узла Сетевые помехи этого эксперимента | первый | Нет |
| Пресет и значения под ним | profile | Чем оно ухудшает трафик с этого момента — см. Профиль; реле читает значения своего протокола | Нет связи (без помех, если ключа нет) | Нет |
Шаг завершается ошибкой, если реле не работает — например, оно не смогло пересылать. Выход: Выход. Настроек нет.
{ "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 (и если ключа нет) |
Выход: Выход. Настроек нет. Шаблоны нигде не применяются.
{ "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, если есть) | expr | JSON-путь ($.data.token, $.items[0], $["first name"]), имя заголовка (в любом регистре) или регулярное выражение — его первая группа либо всё совпадение | $.token; для статуса и тела не используется | Нет |
Шаг завершается ошибкой, если брать нечего: тело не JSON, пути или заголовка нет, выражение не совпало или — для поля JSON либо всего тела — тело было длиннее сохранённых 256 КиБ. Статус сохраняется как число; остальное — как текст или как найденное значение JSON. Выход: Выход. Настроек нет.
{ "id": "token", "type": "extract", "x": 500, "y": 80, "variable": "token", "from": "json", "expr": "$.data.token" }Подробнее о переменных — в разделе Данные и шаблоны.
Проверки
Проверка либо проходит, либо проваливает запуск. Четыре проверки ответа читают последний HTTP-ответ на своём пути, поэтому на каждом пути перед ними должен стоять HTTP-запрос — не под нагрузкой.
Статус HTTP
Проходит, когда статус последнего ответа в точности равен заданному.
| Поле | В файле | Что это | По умолчанию и пределы | Шаблоны |
|---|---|---|---|---|
| Ожидаемый статус | status | 200; 100–599 | Нет |
Выход: Выход. Настроек нет.
{ "id": "ok", "type": "assert_status", "x": 500, "y": 80, "status": 200 }Текст ответа
Проходит, когда тело последнего ответа содержит этот текст точно (с учётом регистра). Сохраняются только первые 256 КиБ тела: если текст не найден в обрезанном теле, проверка проваливается именно с этой причиной.
| Поле | В файле | Что это | По умолчанию и пределы | Шаблоны |
|---|---|---|---|---|
| Содержит текст | contains | ok; обязательно | Да |
Выход: Выход. Настроек нет.
{ "id": "ready", "type": "assert_body", "x": 500, "y": 80, "contains": "ready" }Заголовок ответа
Проходит, когда в последнем ответе есть заголовок и его значение содержит этот текст. Имя заголовка сопоставляется в любом регистре, значение — точно.
| Поле | В файле | Что это | По умолчанию и пределы | Шаблоны |
|---|---|---|---|---|
| Имя заголовка | name | content-type; обязательно | Да | |
| Содержит текст | contains | Что должно содержать его значение; пусто — достаточно, чтобы заголовок был | application/json | Да |
Выход: Выход. Настроек нет.
{ "id": "json", "type": "assert_header", "x": 500, "y": 80, "name": "Content-Type", "contains": "json" }Время ответа
Проходит, когда последний ответ занял не больше этого времени — от отправки запроса до конца его тела.
| Поле | В файле | Что это | По умолчанию и пределы | Шаблоны |
|---|---|---|---|---|
| Максимальное время, мс | max_ms | 1 000; 1–120 000 | Нет |
Выход: Выход. Настроек нет.
{ "id": "fast", "type": "assert_latency", "x": 500, "y": 80, "max_ms": 250 }Проверка значения
Сравнивает значение — обычно переменную, записанную шаблоном, — с ожидаемым и проходит, когда сравнение выполняется.
| Поле | В файле | Что это | По умолчанию | Шаблоны |
|---|---|---|---|---|
| Значение | value | Что сравнивается: {{token}}, {{reply.args[0]}} | {{token}} | Да |
| Условие | op | См. Сравнения | не пусто | Нет |
| Ожидается | expected | Не используется условиями пусто и не пусто | пусто (и если ключа нет) | Да |
Выход: Выход. Настроек нет.
{ "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 | содержит ожидаемый текст |
| соответствует regex | matches | соответствует ожидаемому регулярному выражению |
| пусто, не пусто | empty, not_empty | пусто (пробелы считаются пустотой) / не пусто |
Сценарий
Узлы, которые решают, куда пойдёт запуск. Подробнее о ветках, слияниях и циклах — в разделе Как идёт запуск.
Старт
Здесь начинается запуск; в каждом эксперименте ровно один такой узел. У него нет ни входа, ни полей. Первая строка ленты показывает seed запуска.
Выход: Выход, обязателен. Несколько связей от него сразу запускают параллельные ветки.
{ "id": "start", "type": "start", "x": 40, "y": 80 }Финиш
Здесь запуск завершается; в каждом эксперименте ровно один такой узел, и выходов у него нет. К нему могут вести несколько веток: запуск проходит один раз, после того как закончила последняя ветка, и только если ни одна не завершилась ошибкой. Запуск, который так и не дошёл до узла Финиш, проваливается.
{ "id": "end", "type": "end", "x": 960, "y": 80 }Пауза
Ждёт заданное время перед следующим шагом.
| Поле | В файле | Что это | По умолчанию и пределы | Шаблоны |
|---|---|---|---|---|
| Задержка (мс) | ms | 300; 0–60 000 | Нет |
Выход: Выход. Настроек нет. Для более долгого ожидания поставьте несколько таких узлов подряд или в узел Цикл.
{ "id": "pause", "type": "delay", "x": 500, "y": 80, "ms": 500 }Ветвление по статусу
Выбирает выход Да, когда последний HTTP-ответ имеет этот статус, иначе — Нет. На каждом пути перед ним должен стоять HTTP-запрос.
| Поле | В файле | Что это | По умолчанию и пределы | Шаблоны |
|---|---|---|---|---|
| Ожидаемый статус | status | 200; 100–599 | Нет |
Выходы: Да и Нет, оба обязательны. Настроек нет.
{ "id": "branch", "type": "branch_status", "x": 500, "y": 80, "status": 200 }Ветвление по значению
Выбирает выход Да, когда сравнение выполняется, иначе — Нет. Его поля и сравнения те же, что у узла Проверка значения; сравнение, которое нельзя выполнить (lt для текста), завершает шаг ошибкой.
| Поле | В файле | Что это | По умолчанию | Шаблоны |
|---|---|---|---|---|
| Значение | value | Что сравнивается | {{token}} | Да |
| Условие | op | равно | Нет | |
| Ожидается | expected | пусто (и если ключа нет) | Да |
Выходы: Да и Нет, оба обязательны. Настроек нет.
{ "id": "ok", "type": "branch_value", "x": 730, "y": 80, "value": "{{reply.args[0]}}", "op": "eq", "expected": "ok" }Параллельный запуск
Выполняет то, что следует за выходами Поток 1 и Поток 2, одновременно, причём у каждой ветки своя копия переменных. У каждого выхода может быть больше связей — для большего числа веток. Полей нет.
Выходы: Поток 1 и Поток 2, оба обязательны.
{ "id": "split", "type": "fork", "x": 270, "y": 80 }Слияние потоков
Ждёт, пока будет достигнута каждая ведущая в него связь, затем продолжает один раз, слив переменные веток — если две ветки задают одну переменную, побеждает та, чья связь в файле идёт позже, — и последний HTTP-ответ из последней из них, у которой он был. Полей нет.
Здесь встречаются только ветки, которые выполняются все вместе: узел Слияние потоков, стоящий за узлом Ветвление по статусу (его выходы Да и Нет никогда не срабатывают оба сразу), никогда не продолжит; если ни один другой путь не достигает Финиш, запуск завершается ошибкой на этом узле, сообщая, скольких веток он всё ещё ждал.
Выход: Выход, обязателен.
Любой другой узел, в который ведёт несколько связей, выполняется по разу при каждом приходе.
{ "id": "joined", "type": "join", "x": 730, "y": 80 }Цикл
Снова и снова выполняет шаги на выходе Тело, которые ведут обратно к нему: не более заданного числа раз и, если у него есть условие выхода, пока оно не выполнится.
| Поле | В файле | Что это | По умолчанию и пределы | Шаблоны |
|---|---|---|---|---|
| Итераций не больше | max | Наибольшее число итераций | 5; 1–1 000 | Нет |
| выйти раньше, если | until | Необязательное условие выхода { "value", "op", "expected" }, как у узла Проверка значения | выкл. | Значение и ожидаемое: да |
- Тело всегда выполняется хотя бы один раз. Условие выхода читается после каждой итерации, поэтому тело может задать то, что оно проверяет.
- Выход Готово выбирается, когда условие выполняется — или, без условия, после последней итерации.
- Выход Лимит выбирается, когда итерации кончились раньше, чем выполнилось условие. Без связи на нём это завершает шаг ошибкой.
- Внутри тела
{{counter}}— номер итерации. - Тело выполняется как одна ветка: у каждого выхода внутри него одна связь; в нём нет узлов Старт, Финиш, Параллельный запуск, Слияние потоков и других узлов Цикл; в него входят только через Тело; и каждая связь в нём ведёт дальше в теле или обратно к циклу.
Выходы: Тело и Готово (обязательны), Лимит (необязателен). Настроек нет.
{ "id": "poll", "type": "loop", "x": 270, "y": 80, "max": 10,
"until": { "value": "{{status.args[0]}}", "op": "eq", "expected": "ready" } }