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

Эмуляторы ​

Эмулятор — это Signal Lab в роли API, устройства или сервиса, с которым говорит ваша система. Он слушает один адрес и отвечает по правилам: HTTP API — по маршрутам, OSC-, UDP- или TCP-устройство — по принципу «на это ответить тем-то», MQTT-брокер — как любой брокер, плюс собственные правила. Он умеет тормозить, сбоить и время от времени отключаться — так можно проверить, что делает ваша система, когда её зависимость ведёт себя плохо. Каждый обмен подсчитывается, попадает в список и отправляется в Инспектор.

Эмулятор — это один документ. Экран Эмуляторы хранит их библиотеку; тот же документ работает внутри эксперимента как узел Эмулятор, из командной строки через signallab emulate, через API и MCP — и везде отвечает одинаково.

Экран ​

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

Создание эмулятора ​

  1. Нажмите одну из кнопок вверху библиотеки:

    КнопкаСоздаётСлушаетС одним правилом, которое сразу работает
    + HTTP APIHTTP API127.0.0.1:18080GET /health → 200 {"status":"ok"}
    + OSC-устройствоOSC-устройство127.0.0.1:9100/ping → /pong со счётчиком в виде int
    + UDP-устройствоUDP-устройство127.0.0.1:7100датаграмма, содержащая PING → PONG 1, PONG 2, …
    + TCP-устройствоTCP-устройство127.0.0.1:7200строка, содержащая PING → PONG
    + MQTT-брокерMQTT-брокер127.0.0.1:1883публикация в lab/<name>/set → то же содержимое, retained, в lab/<name>/state

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

  2. Заполните поле Название (не больше 120 символов).

  3. В поле Слушать укажите IP:port. 127.0.0.1 отвечает только этому компьютеру; 0.0.0.0 — ещё и сети.

  4. Измените правила (см. ниже) и напишите в поле Заметка, что этот эмулятор заменяет.

Изменения сохраняются сами. Кнопка Дублировать создаёт копию на следующем свободном порту. Кнопка Удалить переспрашивает (Удалить?), останавливает эмулятор, если он запущен, и удаляет его из библиотеки.

Правила перебираются по порядку, от первого к последнему; отвечает первое подошедшее. В заголовке каждого правила — его краткое описание в одну строку; щёлкните заголовок, чтобы раскрыть или свернуть правило. Кнопки ↑ и ↓ перемещают правило, × удаляет его.

Запуск ​

  1. Выберите эмулятор и нажмите Запустить. Его порт открывается раньше, чем кнопка снова станет доступна: занятый порт или эмулятор с ошибкой отклоняются тут же, с указанием причины.
  2. Направьте на него свою систему. Для HTTP API кнопка Скопировать URL копирует его адрес (http://127.0.0.1:18080), а у каждого маршрута есть своя кнопка Скопировать URL (кроме маршрутов, путь которых содержит шаблон {{…}}).
  3. Смотрите, как заполняется список Пришло.
  4. Нажмите Остановить или остановите задачу эмулятора на полосе консоли.

Рядом с кнопками показано состояние: Не запущен, адрес, на котором эмулятор отвечает, или то, что он отключён.

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

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

Два эмулятора одного транспорта не могут делить порт: HTTP-, TCP- и MQTT-эмуляторы слушают TCP-порты, OSC- и UDP-эмуляторы — UDP-порты. HTTP API и OSC-устройство могут оба занять порт 8080; два HTTP API — нет. Второй эмулятор на занятом порту отклоняется при запуске.

TIP

В браузере, подключённом к серверу, эмулятор работает на сервере. До эмулятора, слушающего 0.0.0.0, добираются по имени сервера, и Скопировать URL копирует именно этот адрес; эмулятор на 127.0.0.1 отвечает только программам на самом сервере.

Что пришло ​

Пока эмулятор работает, панель Сейчас считает:

СчётчикЧто считает
ЗапросыВсё, что пришло: запросы, сообщения, строки.
Без правилаТо, что не взяло ни одно правило. HTTP-запрос без маршрута всё равно получает ответ (см. Запросы без маршрута); остальные — нет.
С ошибкойОбмены, в которых ответ не удалось составить или отправить.
Во время отключенияТо, что пришло, пока эмулятор был отключён. Показывается, если у него задано отключение или что-то застало его отключённым. Никогда не попадает в Без правила.
Не доставленоТолько MQTT и только если такое случилось: сообщения, которые клиент не смог принять, слишком отстав.

В заголовке каждого правила видно, сколько раз оно сработало с момента запуска.

Список Пришло показывает последние 300 обменов, новые сверху:

СтолбецЧто
ВремяКогда пришло.
ОткудаАдрес клиента.
ЗапросЧто пришло, в нотации протокола: GET /users/7, /ping 1, POWER?.
ПравилоПравило, которое это взяло (#2), или —.
ОтветЧто ушло обратно: 200 OK · 37 B, /pong 3, содержимое; удержан или закрыто при сбое; ошибка, если ответить не удалось; отключён, если пришло во время отключения.
мсОт прихода до отправки ответа, включая задержку.

Кнопка ⌕ в строке (Открыть в Инспекторе) открывает этот обмен в Инспекторе, если захват был включён. Если за пятую долю секунды приходит больше 200 обменов, список пропускает часть и пишет, сколько. Движок хранит последние 500 обменов каждого запущенного эмулятора вместе с тем, что пришло, — для командной строки, API и MCP.

HTTP API ​

Сервер HTTP/1.1. Каждому запросу отвечает первый маршрут, который его берёт.

Маршруты ​

Маршрут берёт запрос, когда совпадают его метод, путь и все условия.

ПолеЧто
МетодGET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS или Любой. Маршрут GET отвечает и на HEAD.
ПутьНачинается с /. Сегмент :name берёт любой один сегмент и читается как {{request.params.name}}; последний сегмент * берёт всё, что ниже. Завершающий / ничего не меняет; строка запроса в путь не входит.
УсловияДолжны выполняться все. Добавьте условие кнопкой + Условие.

Примеры путей:

ПутьБерётНе берёт
/health/health, /health//health/db, /Health
/users/:id/users/7 (params.id равен 7), /users/a%20b (a b)/users, /users/7/orders
/files/*/files, /files/a, /files/a/b/c/file, /other/files/a

Условие читает одну часть запроса (Где) и сравнивает её:

ГдеИмяЧто читает
ЗаголовокИмя заголовка, в любом регистреЗначение заголовка; если заголовок пришёл несколько раз — его значения через , .
Параметр запросаПараметр запросаЕго значение, декодированное; если он повторяется — первое.
Тело—Всё тело как текст.
JSONПуть JSON, например $.user.idЭто поле тела JSON.

Сравнения: равно, не равно, меньше, не больше, больше, не меньше, содержит, соответствует regex, пусто и не пусто. Числа сравниваются как числа, текст — точно. Отсутствующий заголовок, параметр или поле считаются пустыми. Сравнение, которое невозможно выполнить, — текста с числом — считается невыполненным.

Ответы ​

У маршрута от одного до 16 ответов (Ответы).

ПолеЧтоПо умолчанию
Статус100–599.200
СбойЧто-то иное, чем ответ; см. Сбои.Нет — ответить
Задержка, мсСколько ждать перед ответом, 0–60 000 мс.0
Разброс, мсСлучайная добавка к ожиданию, до этой величины, 0–60 000 мс.0
ВесДоля этого ответа, когда маршрут отвечает случайно. Показывается только в этом случае.1
ЗаголовкиДо 32. В именах можно использовать параметры; значения — шаблоны.нет
ТелоШаблон, до 256 КиБ в записанном виде.пусто

Без заголовка Content-Type тело, которое является корректным JSON, уходит как application/json, а любое другое — как text/plain; charset=utf-8.

Когда ответов два или больше, поле Какой ответ определяет, какой из них получит запрос:

Какой ответЧто получают запросыДля чего
По очереди, дальше последнийПервый, второй, …, а затем всё время последний: 500, 500, 200, 200, 200…Повторы: два сбоя, потом работает.
По кругуПосле последнего снова первый: 200, 500, 200, 500…Зависимость, которая регулярно сбоит время от времени.
Случайно, по весуКаждый раз выбирается по весу. Веса 8 и 2 дают первый ответ примерно в 80 % случаев. Хотя бы один вес должен быть больше 0.Реалистичная доля сбоев.

В списке Добавить ответ можно добавить к маршруту готовый ответ:

ПресетЧто добавляет
200 JSON200, {"ok":true}
201 Created201, {"id":"{{uuid}}"}, заголовок Location: {{request.path}}/{{counter}}
404 Not found404, {"error":"not found"}
500 Server error500, {"error":"internal"}
503 Unavailable503, {"error":"unavailable"}, заголовок Retry-After: 1
Медленно — 2 с200, {"ok":true} через 2000 мс
Без ответаСбой Без ответа
Обрыв соединенияСбой Закрыть соединение
Битый JSON200, {"items":[{"id":1},{"id":2}]} со сбоем Битое тело

Сбои ​

СбойЧто встречает клиент
Нет — ответитьОтвет.
Без ответаНичего. Запрос удерживается до 2 минут, затем соединение закрывается — так проверяется собственный тайм-аут клиента. Задержка не применяется.
Закрыть соединениеСоединение закрывается без ответа, после задержки.
Битое телоПолный HTTP-ответ с заданными статусом и заголовками, тело которого обрывается на середине: JSON, который не разбирается. Если всё тело было JSON, тип содержимого по-прежнему application/json.

Запросы без маршрута ​

Поле Запросы без маршрута определяет, что получает запрос, который не подошёл ни к одному маршруту:

  • 404 Not found — 404 с телом {"error":"no_route"};
  • Этот ответ — заданный вами ответ со всем, что есть у ответа маршрута. Его {{counter}} считает запросы, которые не взял ни один маршрут.

В обоих случаях запрос попадает в счётчик Без правила.

Что может прочитать HTTP-ответ ​

ШаблонЧто это
{{request.method}}GET, POST, …
{{request.path}}Путь, без строки запроса.
{{request.params.id}}Сегмент пути с именем :id.
{{request.query.page}}Параметр запроса, декодированный.
{{request.headers.x-key}}Заголовок; имена в нижнем регистре.
{{request.body}}Тело как текст: его первые 64 КиБ.
{{request.json.name}}Поле тела JSON, если тело — JSON и не больше 64 КиБ.
{{request.from}}IP:port клиента.

Запрос с телом больше 1 МиБ получает 413 и попадает в счётчик С ошибкой. Ответ, который не удалось составить, — шаблон ссылается на то, чего в запросе нет, — заменяется на 500 с ошибкой в теле и тоже попадает в счётчик С ошибкой.

OSC-устройство ​

Каждому пришедшему сообщению — и каждому сообщению бандла по отдельности — отвечает первое правило, к которому оно подходит. Датаграмма, которая не является OSC, попадает в счётчик Без правила.

ПолеЧто
Шаблон адресаШаблон адреса OSC 1.0: * — любые символы, ? — один символ, [a-z] — набор, {a,b} — любой из вариантов, каждый в пределах одного сегмента (см. OSC).
Условия на аргументыДо 16 условий на аргументы, как в узле Ждать OSC (см. Узлы).
ОтвечатьФлажок снят: принять сообщение и ничего не отвечать.
Адрес ответаАдрес ответа, шаблон.
Аргументы ответаДо 16 аргументов, у каждого Тип (int, float, str, long, double, bool, blob, nil) и Значение — шаблон.
Куда отвечатьПусто: обратно на адрес и порт отправителя. Иначе IP:port.
Задержка, мс, Разброс, мсПо 0–60 000 мс.

Значение аргумента читается как его тип после заполнения шаблона: {{request.args[0]}} возвращает первый аргумент числом, если тип числовой. bool принимает true, 1, yes, on или false, 0, no, off; blob — байты в hex; пустое значение — ноль этого типа.

Ответы уходят с собственного порта эмулятора, поэтому их слышит клиент, который слушает тот порт, с которого отправлял.

OSC-ответ может читать {{request.address}}, {{request.args[0]}} и {{request.from}}.

UDP-устройство ​

Каждой датаграмме отвечает первое правило, к которому она подходит.

ПолеЧто
СовпадениеЛюбая датаграмма, Содержит текст, Соответствует regex или Содержит байты (hex).
ШаблонТекст, регулярное выражение или байты, которые нужно найти.
ОтветПринять без ответа, Текст или Hex, а затем сам ответ — шаблон.
Куда отвечатьПусто: обратно отправителю. Иначе IP:port.
Задержка, мс, Разброс, мсПо 0–60 000 мс.

UDP- или TCP-ответ может читать:

ШаблонЧто это
{{request.text}}Содержимое как текст.
{{request.match}}Что совпало: текст, первая группа регулярного выражения (или всё совпадение), байты.
{{request.hex}}Содержимое в виде hex-байтов, первые 1024.
{{request.bytes}}Размер содержимого.
{{request.from}}IP:port отправителя.

Текстовый ответ — не больше 65 507 байт.

TCP-устройство ​

Устройство, которое обменивается строками по TCP-соединению, как проектор или матричный коммутатор. Каждому сообщению клиента отвечает первое правило, к которому оно подходит; ответ уходит обратно по тому же соединению.

ПолеЧто
Конец сообщенияЧто завершает сообщение и добавляется после каждого ответа и приветствия: LF (\n) (\r перед ним отбрасывается), CR LF (\r\n), CR (\r) или Нет — каждый пришедший кусок. Пустые строки пропускаются.
ПриветствиеОтправляется, когда клиент подключается; пусто — без приветствия. Может читать {{request.from}}.
Совпадение, Шаблон, ОтветКак у UDP-устройства.
Затем закрыть соединениеЗакрыть соединение после ответа этого правила — например, на QUIT.
Задержка, мс, Разброс, мсПо 0–60 000 мс.

Сообщение длиннее 64 КиБ без разделителя берётся как есть.

MQTT-брокер ​

Небольшой брокер MQTT 3.1.1 поверх обычного TCP. Он делает то же, что любой брокер: клиенты подключаются, подписываются с + и #, публикуют с QoS 0, 1 и 2, retained-сообщения и завещания (last will) работают, а второе подключение с тем же id клиента вытесняет первое. Сессии всегда чистые: клиент, который просит сохранить сессию, получает новую, и для отсутствующего клиента ничего не копится.

Вдобавок каждое опубликованное на нём сообщение проверяется по правилам: первое подошедшее ещё и публикует ответ — как устройство, которое сообщает, что сделало.

ПолеЧто
Имя пользователя, ПарольЕсли имя пользователя задано, клиент должен подключиться с ним и с паролем; пусто — подключиться может кто угодно. Пароль без имени пользователя отклоняется: MQTT 3.1.1 не может его передать.
СохранённыеДо 64 сообщений (Топик, Содержимое, QoS), которые хранятся с момента запуска, как будто опубликованы с retain: подписавшийся клиент получает их первыми.
Фильтр топикаКакие топики берёт правило: + — один уровень, # — все остальные, например lab/+/set.
Совпадение, ШаблонУсловие на содержимое, как у UDP-устройства.
ОтвечатьФлажок снят: принять сообщение и больше ничего не публиковать.
Топик ответа, Содержимое ответаШаблоны. В топике не может быть + или #.
QoS, Сохранить (retain)Для ответа.
Задержка, мс, Разброс, мсПо 0–60 000 мс.

MQTT-ответ может читать {{request.topic}}, {{request.levels[1]}} (уровни топика, начиная с 0), {{request.payload}}, {{request.json.state}}, {{request.match}}, {{request.qos}}, {{request.retain}}, {{request.client}} (id клиента) и {{request.from}}.

Шаблоны в ответах ​

Ответы пишутся на том же языке шаблонов, что и эксперименты, поэтому поле значит одно и то же и здесь, и там. Ответ может читать:

  • request — то, что пришло, как перечислено выше для каждого протокола;
  • {{counter}} — сколько сообщений взяло это правило с момента запуска эмулятора, включая текущее;
  • генераторы — {{uuid}}, {{now.iso}}, случайные значения и остальные; случайные значения берутся из seed эмулятора;
  • параметры — когда эмулятор работает в эксперименте или запущен через signallab emulate --param.

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

Некоторые поля фиксируются при запуске эмулятора, ещё до того, как что-то придёт: путь, условие, шаблон адреса, шаблон содержимого, фильтр топика, Куда отвечать, имя заголовка, retained-сообщения и учётные данные брокера. Они принимают только текст и параметры — без request и без генераторов.

Seed управляет случайным порядком ответов, разбросом задержки и случайными генераторами. На экране Эмуляторы каждый запуск получает новый seed; эксперимент использует seed запуска, а signallab emulate --seed берёт тот, что вы укажете.

Отключения ​

Чтобы проверить, что делает ваша система, когда зависимость то пропадает, то появляется, установите флажок Время от времени отключается:

ПолеЧтоПо умолчанию
Работает, мсСколько эмулятор отвечает, 10–3 600 000 мс.10 000
Отключён, мсСколько он отключён, 10–3 600 000 мс.3000
Пока отключёнТолько HTTP: что встречает запрос, пока эмулятор отключён.503 Недоступен

Расписание начинается с запуска эмулятора и повторяется: работает, отключён, работает, отключён… Пока он отключён:

ЭмуляторЧто встречает клиент
HTTP503 Недоступен: 503 с Retry-After, равным числу секунд до возвращения (не меньше 1). Закрыть соединение: соединение закрывается без ответа. Без ответа: запрос удерживается до 2 минут, затем соединение закрывается.
TCP-устройствоОткрытые соединения рвутся в течение 0,1 с; новые закрываются сразу при подключении.
MQTT-брокерВсе соединения рвутся; новые отклоняются (код возврата CONNACK 3, сервер недоступен).
OSC-, UDP-устройствоНичего не отвечает.

То, что приходит во время отключения, попадает в счётчик Во время отключения, а не в Без правила, и правила при этом не опрашиваются.

Кнопка Отключить делает то же самое по требованию, что бы ни говорило расписание, пока вы не нажмёте Включить; HTTP тогда встречает 503 без Retry-After. В эксперименте то же самое на нужном шаге запуска делает узел Эмулятор: выкл./вкл. (см. Узлы и Сбои).

Ошибки в эмуляторе ​

Пока вы редактируете, эмулятор проверяется через мгновение после каждого изменения, и ошибка показывается под его кнопками ещё до того, как вы нажмёте Запустить. Ошибка указывает место — правило, ответ или retained-сообщение и поле — и что не так: путь без /, регулярное выражение, которое не компилируется, шаблон ответа, который ссылается на что-то кроме request, параметров и генераторов, значение вне допустимого диапазона. Кнопка Запустить не запустит эмулятор с ошибкой.

Ограничения ​

ЧтоОграничениеПри достижении
Маршрутов или правил на эмулятор64Эмулятор отклоняется при проверке.
Ответов на маршрут16Отклоняется.
Условий на маршрут16Отклоняется.
Заголовков на ответ32Отклоняется.
Условий на аргументы, аргументов ответа (OSC)по 16Отклоняется.
Retained-сообщений (MQTT)64Отклоняется.
Тело, ответ или приветствие в записанном виде256 КиБОтклоняется.
Задержка или разброс60 000 мсОтклоняется.
Тело HTTP-запроса1 МиБ413.
HTTP-соединений одновременно512Лишние закрываются сразу при подключении.
Строка и заголовки HTTP-запроса30 сКлиент должен прислать их за это время.
TCP-соединений одновременно256Лишние закрываются сразу при подключении.
OSC- и UDP-ответов, ожидающих своей задержки1024Лишние отбрасываются и попадают в счётчик С ошибкой.
MQTT-клиентов одновременно256Лишние закрываются сразу при подключении.
MQTT-пакет256 КиБСоединение клиента разрывается.
MQTT-подписок на клиента100Лишние отклоняются.
Retained-топиков MQTT1000 топиков, 16 МиБНовое retained-сообщение доставляется, но не сохраняется.
MQTT-сообщений в очереди одного медленного клиента1024 сообщения, 8 МиБКлиент их пропускает; они попадают в счётчик Не доставлено.

Эмулятор из ответа ​

Чтобы сделать эмулятор из ответа, который сработал:

  1. На экране HTTP отправьте запрос и получите ответ — или нажмите Отправить сейчас на HTTP-узле эксперимента.
  2. Нажмите ⧉ Сымитировать рядом с ответом. Окно Сымитировать этот ответ покажет маршрут, который будет создан.
  3. В поле Добавить в выберите один из своих HTTP-эмуляторов или Новый эмулятор.
  4. Нажмите Добавить маршрут. Откроется экран Эмуляторы на этом эмуляторе.

Маршрут отвечает на метод и путь запроса (без строки запроса) статусом, заголовками и телом ответа. Заголовки, которые относятся только к тому обмену (Content-Length, Date, Server, ETag и подобные), не переносятся, а тело отправляется как было, даже если в нём есть {{. Новый эмулятор содержит только этот маршрут. При добавлении в существующий эмулятор маршрут встаёт первым, чтобы отвечать раньше более общего маршрута; запущенный эмулятор подхватит его, когда вы нажмёте Перезапустить.

Если ответ взят из эксперимента, URL, записанный с шаблонами, превращается в шаблон пути: его основа ({{api}}) отбрасывается, сегмент, который целиком является шаблоном (/orders/{{order_id}}), становится :order_id, а сегмент, лишь частично состоящий из шаблона, завершает путь звёздочкой *.

Стартовый набор ​

Впервые не найдя библиотеку эмуляторов, Signal Lab записывает пять эмуляторов, все на этом компьютере. Их названия и заметки пишутся на языке, выбранном в интерфейсе в этот момент.

ЭмуляторСлушаетЧто делает
Демо API127.0.0.1:8080GET /health → {"status":"ok","time":…}; GET /users/:id → пользователь с этим id; POST /users → 201 с Location; GET /slow → ответ через 1500 мс; /flaky → 503, 503, а дальше всё время 200.
Демо OSC-устройство127.0.0.1:9100/ping → /pong со счётчиком; /fader/* → /ack с полученным адресом; /cue/* принимается без ответа.
Демо UDP-устройство127.0.0.1:7100PING → PONG и счётчик; всё остальное → ACK и его размер в байтах.
Демо TCP-устройство127.0.0.1:7200Строки, оканчивающиеся на CR LF. Приветствует READY; POWER? → POWER=ON; POWER ON или POWER OFF → OK ON / OK OFF; QUIT → BYE, затем разрывает соединение.
Демо MQTT-брокер127.0.0.1:1883Хранит retained online в lab/status; ON или OFF, опубликованные в lab/<name>/set → то же самое, retained, в lab/<name>/state.

Стартовый сигнал Сервис работает? из библиотеки сигналов запрашивает http://127.0.0.1:8080/ — адрес эмулятора Демо API: маршрута для / у того нет, поэтому сигнал получает 404.

Файл библиотеки ​

Библиотека — это файл emulators.json в папке данных (см. Файлы); наведите указатель на счётчик под списком, чтобы увидеть его путь. Файл записывается целиком через 0,7 с после последнего изменения, через временный файл, так что неудачная запись оставляет прежний. Если файл не читается, список показывает ошибку с путём, строкой и столбцом, а сам файл остаётся как есть: исправьте его и нажмите Перечитать файл. Нажмите Перечитать файл и после того, как отредактировали файл вручную. Если файла нет, стартовый набор записывается снова.

json
{
  "version": 1,
  "emulators": [
    {
      "id": "orders-api",
      "note": "Stands in for the orders service.",
      "emulator": {
        "name": "Orders API",
        "bind": "127.0.0.1:18080",
        "protocol": "http",
        "routes": [
          { "method": "GET", "path": "/orders/:id",
            "responses": [{ "body": "{\"id\":\"{{request.params.id}}\",\"state\":\"open\"}" }] },
          { "method": "POST", "path": "/orders", "order": "sequence",
            "responses": [{ "status": 503 }, { "status": 201, "body": "{\"id\":\"{{uuid}}\"}" }] }
        ],
        "outage": { "up_ms": 20000, "down_ms": 2000, "fault": "unavailable" }
      }
    }
  ]
}

Объект emulator сам по себе — тоже документ, который читает signallab emulate.

В экспериментах и скриптах ​

  • В эксперименте узел Эмулятор открывает свой эмулятор до первого шага и отвечает, пока запуск не закончится; то, что он получил, подсчитывается в отчёте. HTTP-эмулятор там же — это и есть то, что слушает узел Ждать HTTP-запрос (Узлы), а OSC- или UDP-эмулятор делит свой порт с ожиданиями запуска. Два эмулятора одного транспорта в одном эксперименте не могут делить порт. См. Узлы и Сбои.
  • signallab emulate запускает эмуляторы из файлов или из этой библиотеки до Ctrl+C или до истечения --for и печатает, что они отвечают; см. Командная строка.