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

Как идёт запуск ​

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

Старт и финиш ​

В эксперименте ровно один узел Старт и ровно один узел Финиш.

  • У узла Старт нет входа. Он проходит сразу, а его строка в ленте запуска показывает seed запуска. У его выхода может быть несколько связей: тогда эксперимент начинается с параллельных веток.
  • Каждая ветка, дошедшая до узла Финиш, на нём останавливается. Финиш отображается как выполняющийся с первого прихода и проходит один раз, после того как закончила последняя ветка, — и не проходит вовсе, если какой-либо шаг завершился ошибкой. Именно это прохождение присваивает запуску статус Успешно.
  • Запуск, в котором все ветки закончились без ошибок, но ни одна не дошла до финиша, завершается ошибкой run.no_end.

Выходы и связи ​

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

УзелВыходы, которые должны быть соединеныВыходы, которые могут быть соединены
Финиш——
Параллельный запускПоток 1, Поток 2—
Ветвление по статусу, Ветвление по значениюДа, Нет—
Любое ожидание (Ждать OSC, Ждать UDP, Ждать MQTT, Ждать HTTP-запрос, Ожидание WebSocket)ПолученоТаймаут
ЦиклТело, ГотовоЛимит
Любой другой узелВыход—

Чтобы соединить узлы, перетащите выход на узел; если отпустить его на пустом холсте, там появится новый узел. Если перетащить выход, у которого уже есть связь, добавится ещё одна. Кнопка Добавить следующий, клавиша A и значок + на связи, наоборот, вставляют узел в уже существующую связь.

Незавершённая схема — это черновик: она сохраняется, но не запускается. Сообщение Завершите схему на панели инструментов говорит, чего не хватает, и показывает узел. См. что проверяется перед запуском.

Параллельные ветки ​

Несколько связей от одного выхода ​

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

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

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

Узел Слияние потоков ждёт каждую связь, которая в него ведёт, и затем продолжает как одна ветка, слив копии в порядке этих связей:

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

Решает порядок связей, а не то, какая ветка успела закончить первой.

Сливайте только то, что выполняется параллельно

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

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

Когда шаг завершается ошибкой ​

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

Ветвление ​

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

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

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

Шаг, который отправляет или слушает, может попробовать ещё раз, если завершился ошибкой: включите повторять при ошибке в его свойствах.

НастройкаЧто этоДиапазонНачальное значение
Попытоквсего попыток, включая первую1–103
Пауза, мспауза перед второй попыткой0–60 000 мс500
Паузыодинаковые: пауза всякий раз одна и та же; удваиваются: каждая следующая пауза вдвое длиннее предыдущей—одинаковые
  • Повтор действует для узлов HTTP-запрос, TCP-сообщение, Публикация MQTT, OSC-сообщение, UDP-датаграмма, Подключение WebSocket, Отправка WebSocket и для любого ожидания. Остальные узлы его не принимают (node.retry_unsupported), а у HTTP-запроса под нагрузкой повтора нет.
  • Ни одна пауза не бывает длиннее 60 с, как бы ни росло удвоение.
  • Каждая неудачная попытка появляется в ленте как Повтор с номером и причиной. Затем шаг либо проходит, либо завершается ошибкой по причине последней попытки.
  • Повторяется только выполнение. Поле, шаблон которого не разрешается, завершается ошибкой сразу.
  • Отправка, которая ждёт ответа, отправляет снова. Ожидание ждёт снова, отсчитывая, как и прежде, от последнего действия ветки.
  • Ожидание со связью на выходе Таймаут по таймауту не завершается ошибкой, поэтому не повторяется: оно идёт по выходу Таймаут.
  • Остановка сразу прерывает паузу.

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

Шаг, который отправляет, может отправлять снова и снова — сигнал «жив», опрос, ровный поток, — без цикла в схеме: включите отправлять серией.

НастройкаЧто этоДиапазонНачальное значение
Повторятьзаданное число раз или в течение времени—заданное число раз
Отправоквсего отправок, включая первую2–10 00010
В течение, мскак долго отправлять, считая от первой отправки1–300 000 мс10 000
Каждые, мспауза между двумя отправками10–60 000 мс1000
Разброс, мскаждая пауза случайно удлиняется не более чем на это значение0–60 000 мс0
  • Серия действует для узлов HTTP-запрос, TCP-сообщение, Публикация MQTT, OSC-сообщение, UDP-датаграмма и Отправка WebSocket (на остальных — node.repeat_unsupported). У HTTP-запроса бывает либо серия, либо нагрузка, но не обе сразу.
  • Каждая отправка выполняется так, как выполнялась бы единственная: её шаблоны разрешаются заново — {{counter}} равен номеру отправки, {{now}} — её времени, — а повтор при ошибке, если он включён, применяется к каждой отправке. Отправка, которая ждёт ответа, ждёт собственного.
  • Для серии на заданное время отправка делается, только если может начаться до истечения этого времени.
  • Разброс берётся из seed запуска: один и тот же seed даёт одни и те же паузы.
  • Лента сообщает о ходе как Серия не чаще раза в секунду. Шаг проходит после последней отправки, с исходом этой отправки; отправка, окончательно завершившаяся ошибкой, приводит к ошибке шага.
  • Ошибка в другой ветке прекращает отправки; Остановка сразу прерывает паузу.

Серия должна укладываться в один запуск: отправки и их самые длинные паузы ((count − 1) × (interval + jitter)) — не более 300 с (node.repeat_too_long), а серия на заданное время — не более 10 000 отправок (node.repeat_too_many).

Цикл ​

Узел Цикл снова и снова выполняет шаги на своём выходе Тело; последний из них соединён обратно с циклом.

НастройкаЧто этоДиапазон
Итераций не большенаибольшее число итераций1–1000
выйти раньше, еслиусловие выхода: Значение, Условие, Ожидается, как в узле Проверка значениянеобязательно
  1. Если в цикл пришли снаружи, он начинает итерацию 1 на выходе Тело.
  2. Каждый раз, когда тело возвращается, проверяется условие выхода — после итерации, поэтому тело выполняется как минимум один раз и может задать то, что проверяет условие.
  3. Когда условие выполняется, цикл выходит через Готово.
  4. Иначе начинается следующая итерация, пока они остаются.
  5. Если итерации заканчиваются раньше, цикл выходит через Лимит, когда к этому выходу подключена связь, а если нет — завершает запуск ошибкой loop.limit. Без условия тело выполняется на каждой итерации, а цикл выходит через Готово.

Внутри тела {{counter}} — номер итерации, поскольку каждый узел считает собственные выполнения. Условие и шаги после выхода Готово или Лимит могут использовать то, что задала каждая итерация тела, — например, статус, который тело извлекает; само тело видит только то, что было известно, когда запуск дошёл до цикла.

Шаблон Опрос до готовности запрашивает у устройства статус каждые 0,3 с, пока оно не ответит ready, но не более 10 раз.

Что может быть в теле ​

Тело выполняется как одна ветка, итерация за итерацией. Связь обратно к циклу — единственный цикл, который может быть в эксперименте; любой другой — graph.cycle.

ПравилоОшибка
Что-то на выходе Тело ведёт обратно к циклуloop.no_return
У каждого выхода в теле одна связьloop.body_parallel
Каждый выход в теле ведёт дальше в теле или обратно к циклуloop.body_leaves
В тело ведёт только выход Тело циклаloop.body_entered
В теле нет узлов Старт, Финиш, Параллельный запуск, Слияние потоков и других узлов Циклloop.body_unsupported

Ожидания ​

Ожидание проходит, когда приходит сообщение, которого оно ждёт: Ждать OSC, Ждать UDP, Ждать MQTT, Ждать HTTP-запрос и Ожидание WebSocket. Что именно сопоставляет каждое из них, описано в справочнике узлов; здесь — как они слушают.

Слушают с самого начала ​

Запуск открывает то, на чём слушают его ожидания, до первого шага, поэтому ответ, который пришёл быстрее следующего шага, не будет пропущен:

ОжиданиеЧто открывается до первого шага
OSC, UDPодин сокет UDP на каждый адрес Слушать (IP:порт), общий для всех ожиданий на нём
HTTP-запросодин слушатель на адрес — HTTP-эмулятор запуска, если он есть, иначе слушатель, отвечающий 204
MQTTодно подключение на брокер и фильтр топика, с подпиской; retained-сообщения, которые брокер при этом воспроизводит, игнорируются
WebSocketничего: оно читает соединение, которое открыл узел Подключение WebSocket, когда выполнялся

Поскольку всё это открывается первым, адреса известны ещё до запуска: ожидание OSC, UDP или HTTP слушает на буквальном IP:port с портом, отличным от 0, а брокер и топик ожидания MQTT принимают только параметры. Порт, который не удаётся открыть — он занят или не является адресом этого компьютера, — останавливает запуск до какого-либо трафика, на поле этого ожидания. Всё закрывается, когда запуск заканчивается, как бы это ни произошло.

Какие сообщения учитываются ​

Ожидание рассматривает сообщения, пришедшие после начала последнего действия в его ветке — последнего запроса, сообщения, публикации, подключения WebSocket или отправки, — а до всякого действия — после начала запуска. Сообщение, пришедшее до запроса, не учитывается, а пауза (Delay) или запись в лог (Log) между запросом и ожиданием не прячет ответ на него. После слияния учитывается самое раннее из последних действий слитых веток.

Ожидание берёт первое подходящее сообщение и расходует его: два ожидания никогда не получат одно и то же сообщение.

Каждый сокет, подписка или соединение хранит для своих ожиданий не более 1024 сообщений и 64 МиБ; сверх этого самые старые отбрасываются и подсчитываются.

Таймаут ​

Поле Таймаут, мс — от 1 до 120 000 мс, изначально 2000. Если за это время ничего не подошло:

  • при наличии связи на выходе Таймаут ожидание идёт по ней;
  • без неё шаг завершается ошибкой wait.timeout, которая сообщает, сколько других сообщений пришло за это время — неверный шаблон выглядит иначе, чем молчащее устройство, — а в подробностях — сколько более старых сообщений было отброшено, когда очередь переполнилась.

Переменная ожидания — reply, а для HTTP request — существует только после выхода Получено. Когда на вкладке Инспектор включён захват, шаг ещё и ссылается на кадр, с которым совпало сообщение: см. ленту запуска.

Ответ на том же шаге ​

Узел OSC-сообщение или UDP-датаграмма может ждать собственного ответа: установите флажок ждать ответ.

НастройкаЧто этоНачальное значение
Ответ на (IP:порт)адрес, на котором ждут ответ; порт 0 — любой свободный порт0.0.0.0:0
Шаблон адреса ответа (OSC), Содержимое ответа (UDP)каким должен быть ответ, как в соответствующем ожиданиилюбой
Таймаут, мс1–120 000 мс2000
Переменная ответапеременная, в которую записывается ответreply

Сокет, указанный в поле Ответ на (IP:порт), открывается до первого шага, как у ожидания, и сообщение уходит с него: устройство, отвечающее на порт отправителя, будет услышано, а то, что отвечает на фиксированный порт, — если это тот самый порт, который указан. Шаг проходит при подходящем ответе, а переменная существует после его выхода. Выхода Таймаут здесь нет: если ответа нет вовремя, шаг завершается ошибкой, а повтор при ошибке может отправить снова. Чтобы ветвиться по молчанию, используйте отдельное ожидание.

Что проверяется перед запуском ​

Редактор проверяет эксперимент по мере правки; кнопка запуска проверяет его ещё раз. В сообщении о проблеме указан узел и, если оно есть, поле.

ПравилоОшибка
У эксперимента есть имяdoc.name_required
От 1 до 64 узлов, ровно один Старт и один Финишdoc.node_count, doc.start_end_count
У старта нет входаgraph.start_input
Связь ведёт к другому существующему узлуdoc.connection_invalid
Одна и та же связь не повторяетсяdoc.connection_duplicate
Каждый выход, который должен быть соединён, соединёнgraph.outputs_required
У узла нет связи на выходе, которого у него нетgraph.port_unexpected
Любой узел достижим от стартаgraph.unreachable
Нет циклов, кроме обратной связи циклаgraph.cycle и правила тела
Перед проверкой или извлечением на каждом пути есть HTTP-запрос; запрос под нагрузкой не считаетсяgraph.needs_http
Каждый шаблон разбирается, а каждое имя в нём известно на каждом путиtemplate.*, name.* — см. данные
Каждое поле заполнено и в допустимых пределахnode.*
Узел, который указывает другой, — Сменить помехи, Эмулятор: выкл./вкл., узлы WebSocket, — указывает существующий, а узел WebSocket стоит после своего подключенияimpair.relay_unknown, emulator.node_unknown, ws.connection_unknown, ws.connection_after
Два сокета запуска не используют один портсм. сбои

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

Ограничение по времени ​

Запуск длится не более 300 с. Тот, что к этому моменту ещё идёт, останавливается и завершается ошибкой run.timeout. Из командной строки и через API предел можно сделать короче — от 1 до 300 с.

Остановка ​

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