Первая программа по шагам

Эта страница — начало раздела. На маленьком учебном примере вы пройдёте весь путь, из которого состоит любая программа для панели: нажали кнопку → программа отправила команду устройству → устройство ответило → на панели поменялись подпись и подсветка. Когда понятна одна цепочка, остальные страницы раздела и большая программа «Демо-зал» отличаются только числом кнопок и устройств.

Программа «Первая программа» учит девяти приёмам. Каждому посвящён свой блок на вкладке (рабочем листе программы). Блоки названы буквами A–I; буквы есть только в этой инструкции, на вкладке блоки подписаны словами:

Блок

Что показывает

A. Старт: карты

как записать в память программы «таблицу соответствий» при запуске

B. Наглядно: правило на кнопку

самый простой способ: на каждую кнопку — своё правило и свои параметры

C. Компактно: карта входов

то же самое для любого числа кнопок: одно правило и таблица

D. Ответ матрицы: надписи и подсветка

как вернуть на панель то, что произошло на самом деле, и отфильтровать лишние ответы

E. Микрофон через switch

кнопка-переключатель: как узнать новое положение узлом switch

F. Микрофон через JSONata

то же одним узлом change и выражением

G. Переход из программы

как программа открывает другую страницу интерфейса

H. Звук

как менять кнопку, ползунок и надпись по ответу процессора и как отправить уровень обратно; зачем нужна подписка

I. ВКС

как менять текст и цвет по состоянию звонка и заполнять список из ответа кодека

Что получится

Интерфейс «Первая программа» состоит из трёх страниц. На главной четыре секции и кнопка «Устройства», на странице «Устройства» — ещё две секции (звук и ВКС), на странице «Подсказка» — подсказка, которую открывает программа.

../../_images/fp-panel.png

Интерфейс «Первая программа» на панели: 1 — секция «Наглядно» (кнопки HDMI 1–3 и подпись «Экран»), 2 — секция «Компактно» (кнопки HDMI 1–3 и подпись «Экран председателя»), 3 — две кнопки-переключателя микрофона, 4 — кнопка «Открыть подсказку»

Что делает каждая секция:

  1. Наглядно. Нажали «HDMI 2» — матрица подаёт вход 2 на выход 1, а в подписи «Экран» появляется «HDMI 2».

  2. Компактно. Нажали «HDMI 2» — матрица подаёт вход 2 на выход 2, а в подписи «Экран председателя» появляется «HDMI 2». Кнопку подсвечивает программа по ответу матрицы.

  3. Микрофон. Нажатие включает или выключает микрофон кодека ВКС. Две кнопки делают одно и то же двумя способами.

  4. Переход. «Открыть подсказку» — программа открывает страницу «Подсказка»; кнопка «Закрыть» на ней возвращает назад.

  5. Звук зала (страница «Устройства»). Кнопка «Звук» и ползунок громкости показывают то, что ответил звуковой процессор; надпись рядом с ползунком — уровень в децибелах. Оператор нажал кнопку или отпустил ползунок — программа отправила команду, а вид на панели поменялся по ответу.

  6. ВКС: звонок и контакты (страница «Устройства»). Плашка по ответу кодека показывает «Нет звонка» или «Идёт разговор» на зелёном фоне. Кнопка «Контакты» заполняет список из ответа кодека, выбранный контакт появляется в надписи «Кому звоним».

../../_images/s5-panel-devices.png

Страница «Устройства» после ответов устройств, звонка нет: 1 — кнопка «Звук», 2 — ползунок громкости, 3 — надпись с уровнем в дБ, 4 — плашка звонка, 5 — список контактов

../../_images/fp-help-page.png

Страница «Подсказка», которую открывает программа: 1 — надпись «Эту страницу открыла программа», 2 — кнопка «Закрыть» (ссылка на главную страницу, сделана в UI-Designer)

Что нужно заранее

  • Контроллер с UI-Designer (редактор интерфейсов), UI-Panel (страница, на которой оператор работает с интерфейсом) и U-Logic (редактор программы управления).

  • Для блоков B–D — матрица видео (драйвер avcit-phinx), для блоков E–F и I — кодек ВКС (драйвер vinteo-st), для блока H — звуковой процессор (драйвер Tesira). Без устройств события панели всё равно приходят в программу и проверяются отладкой. Но подписи «Экран», подсветка секции 2 и всё на странице «Устройства» меняются только по ответам устройств.

  • Интерфейс «Первая программа» на контроллере. Проще всего загрузить готовый проект, как описано в «UI-Designer»: demo-hall.zip. В архиве три интерфейса: «Демо-зал», «Рецепты» и «Первая программа». «Загрузить проект» заменяет весь проект на контроллере, поэтому сначала сохраните свой («Скачать проект»).

  • На вкладке U-Logic — узлы UI - Panel in и UI - Panel out с подключением UI-listener («Первый интерфейс»). В готовой вкладке они уже есть.

Если хотите собрать интерфейс сами

В таблице — элементы интерфейса «Первая программа» (размер 1280 × 800). Имена нужно вписать в UI-Designer буква в букву: по имени (ID элемента) программа находит элемент («Связь интерфейса с программой»). Остальное оформление — на ваш вкус.

Секция

Элемент

Имя

1. Наглядно

кнопки «HDMI 1», «HDMI 2», «HDMI 3» — в одной группе «Interlock»

Fp_Hdmi1_b, Fp_Hdmi2_b, Fp_Hdmi3_b

1. Наглядно

надпись «Экран:» и надпись-значение «—»

Fp_Screen_cap_lb, Fp_Screen_val_lb

2. Компактно

кнопки «HDMI 1», «HDMI 2», «HDMI 3» — без группы

Fp_Map_Hdmi1_b, Fp_Map_Hdmi2_b, Fp_Map_Hdmi3_b

2. Компактно

надпись «Экран председателя:» и надпись-значение «—»

Fp_Chair_cap_lb, Fp_Chair_val_lb

3. Микрофон

две кнопки с флажком «Отметить как toggle»: «через switch» и «через JSONata»

Fp_MicSwitch_b, Fp_MicJsonata_b

4. Переход

кнопка «Открыть подсказку» (без ссылки)

Fp_Open_b

шапка главной страницы

кнопка «Устройства» со ссылкой на страницу «Устройства»

Fp_Devices_b

страница «Устройства», 5. Звук зала

кнопка-переключатель (флажок «Отметить как toggle»): в нажатом состоянии текст «Звук включён», в обычном — «Звук выключен»

Fp_Mute_b

5. Звук зала

горизонтальный ползунок; «Отправлять команды после» — «Отпускание»

Fp_Vol_sl

5. Звук зала

надписи «Громкость:» и значение «—»

Fp_Vol_cap_lb, Fp_Vol_val_lb

5. Звук зала

подписи краёв шкалы «−60 дБ» и «0 дБ» (программа их не меняет)

Fp_Vol_min_lb, Fp_Vol_max_lb

6. ВКС: звонок и контакты

плашка-надпись «Нет звонка»

Fp_Call_val_lb

6. ВКС

кнопки «Позвонить» и «Завершить»

Fp_Call_b, Fp_Hangup_b

6. ВКС

надпись «Кому звоним:» и значение «Выберите контакт»

Fp_Target_cap_lb, Fp_Target_val_lb

6. ВКС

кнопка «Контакты» и список

Fp_Contacts_b, Fp_Contacts_ls

страница «Устройства»

кнопка «Назад» со ссылкой на главную страницу

Fp_Back_b

страница «Подсказка»

кнопка «Закрыть» со ссылкой на главную страницу

Fp_Close_b

Имена построены по схеме Группа_Объект_тип (_b — кнопка, _lb — надпись), поэтому секцию 2 можно поймать одним шаблоном Fp_Map_*_b. Как настроить группу «Interlock» и флажок «Отметить как toggle» — «Кнопки и состояния». Как добавить вторую страницу и ссылку — «UI-Designer» и «Первый интерфейс». Не забудьте сохранить и опубликовать интерфейс: иначе элементов не будет в списках узлов.

Группа «Interlock» в секции 1 — сознательный выбор: панель сама подсвечивает нажатую кнопку, и программа для подсветки не нужна. В секции 2 группы нет, подсветку ведёт программа по ответу матрицы: так на панели видно то, что действительно произошло.

Готовая вкладка

Если не хотите собирать программу, скачайте готовую:

  • first-program-flow.json — одна вкладка «Первая программа»; импортируется через меню U-Logic → «Импорт» так же, как «Демо-зал». Шаги — в разделе «Способ 2. Только поток» страницы «Программа «Демо-зал»: обзор». Особенно важен шаг 8: он про подключение «Демо: UI-Panel». Файл создаёт своё; если на контроллере такое уже есть, в узлах UI - Panel in и UI - Panel out надо выбрать существующее;

  • demo-hall-project.zip — проект целиком: две вкладки («Демо-зал» и «Первая программа») и четыре драйвера. «Загрузить проект» заменяет все потоки и драйверы контроллера, предупреждение — на той же странице.

После импорта впишите адреса своих устройств в подключения «Демо: матрица», «Демо: звуковой процессор» и «Демо: кодек ВКС» (меню → «Конфигурационные узлы»); кодеку ВКС укажите также логин и пароль устройства. В узлах блока H в параметрах команд стоят instanceTag DemoLevels и channel 1. Это адрес регулятора громкости зала в проекте процессора: впишите свои. Затем нажмите «Развернуть» (кнопка U-Logic, которая запускает правки программы в работу). Если вы уже загружали «Демо-зал», подключения «Демо: …» совпадут: окно импорта предложит заменить совпавшие узлы. После замены адреса нужно вписать заново.

Вкладка читается сверху вниз.

../../_images/fp-tab-abc.png

Вкладка «Первая программа», верх: 1 — блок A «Старт: карты», 2 — блок B «Наглядно: правило на кнопку», 3 — блок C «Компактно: карта входов»

../../_images/fp-tab-dg.png

Вкладка «Первая программа», середина: 1 — блок D «Ответ матрицы: надписи и подсветка», 2 — блок E «Микрофон через switch», 3 — блок F «Микрофон через JSONata», 4 — блок G «Переход из программы»

В каждом блоке свой UI - Panel in с одним и тем же подключением UI-listener: так блоки не зависят друг от друга, и их удобно читать по одному. В большой программе «Демо-зал» вход один на всю вкладку, а блоки подключены к нему парами link (см. «Программа «Демо-зал»: обзор»).

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

Как проверять: узел debug

Прежде чем разбирать блоки, запомните один приём: чтобы увидеть, что лежит в сообщении, подключите к выходу нужного узла узел debug. В его окне в поле «Выводить» выберите «весь msg объект сообщения», нажмите «Готово» и «Развернуть». Затем откройте на правой боковой панели вкладку отладки (значок «жук») и нажмите кнопку на панели: в отладке появится сообщение, в нём можно развернуть payload, parameters и другие поля. После проверки узел debug можно отключить кнопкой справа от него или удалить.

Блок A. Старт: карты

Зачем. Программе нужны таблицы соответствий: «какая кнопка — какой вход матрицы», «какой вход — какое название». Эти таблицы записывают один раз при запуске в память программы, а все остальные узлы только читают их. Блок A делает эту запись. Блок B таблиц не использует, а блоки C и D без блока A работать не будут.

Что такое «память программы». У программы есть переменные, которые живут, пока она запущена, и видны узлам. Они бывают двух видов, и в списках типов в окне узла они называются flow. и global.:

  • flow. — переменная вкладки: её видят только узлы той же вкладки. Так сделано в «Демо-зале»;

  • global. — глобальная переменная: её видят все узлы всех вкладок. Так сделано в «Первой программе».

Значением переменной может быть число, строка или целая таблица (объект JSON). Здесь хранятся таблицы: ключ — имя нажатой кнопки или номер входа, значение — то, что нужно отправить или показать.

Примечание

Переменные хранятся в памяти и пропадают при перезапуске программы. Поэтому таблицы записывает узел, который срабатывает при запуске, а не вы вручную. Если вы изменили таблицу, нажмите «Развернуть» и запустите узел inject кнопкой слева от него — иначе в памяти останется старая.

Шаги.

  1. Перетащите на вкладку узел inject. Откройте его. В поле «Имя» впишите «При запуске». Включите отметку «Отправить через» и впишите 1 («Отправить через 1 сек, затем» — запуск один раз после старта). Что именно он отправляет (по умолчанию — метку времени), неважно: он нужен только как «пускатель». Нажмите «Готово».

  2. Перетащите узел change и соедините с ним выход inject.

  3. Откройте change и в поле «Имя» впишите «Карты кнопок и входов». Нажмите «добавить» под списком «Правила» и задайте правило: «Установить», в первом списке выберите global., впишите имя fpInputs. В строке «to the value» выберите «JSON» и впишите таблицу «кнопка → параметры матрицы»:

    {"Fp_Map_Hdmi1_b": {"input": 1, "output": 2},
     "Fp_Map_Hdmi2_b": {"input": 2, "output": 2},
     "Fp_Map_Hdmi3_b": {"input": 3, "output": 2}}
    
  4. Нажмите «добавить» ещё раз и задайте второе правило: «Установить» global. fpTitles, «JSON»: таблица «вход → название»

    {"1": "HDMI 1", "2": "HDMI 2", "3": "HDMI 3"}
    
  5. Нажмите «добавить» и задайте третье правило: «Установить» global. fpButtons, «JSON»: таблица «вход → имя кнопки»

    {"1": "Fp_Map_Hdmi1_b", "2": "Fp_Map_Hdmi2_b", "3": "Fp_Map_Hdmi3_b"}
    
  6. Нажмите «Готово» и «Развернуть».

../../_images/fp-a-inject.png

Окно узла inject «При запуске»: 1 — отметка «Отправить через» и задержка 1 секунда

../../_images/fp-a-change.png

Окно узла change «Карты кнопок и входов»: 1 — правило «Установить» global.fpInputs, 2 — правило global.fpTitles, 3 — правило global.fpButtons

Что вы увидите. Ничего на панели: блок A только готовит данные. Работу блока вы проверите в блоке C: если msg.parameters там получил значение из таблицы, значит, таблица записана.

Блок B. Наглядно: правило на кнопку

Зачем. Это самый понятный способ: на каждую кнопку — своё правило в ui - switch и свой узел change с готовыми параметрами команды. Читается сразу: кнопка «HDMI 2» → вход 2 → выход 1. Подходит, когда кнопок две-три. Когда их десять, лучше компактный способ из блока C.

Цепочка блока: UI - Panel in → ui - switch (три выхода) → три узла change → один device - command.

Шаги.

  1. Перетащите UI - Panel in (если его ещё нет; «Имя» — «События панели») и соедините с ним ui - switch.

  2. Откройте ui - switch. В «Имя узла» впишите «Кнопки HDMI: по правилу». В «Интерфейс» выберите «Первая программа». В «Правила маршрутизации» добавьте три правила: элемент Fp_Hdmi1_b, Fp_Hdmi2_b и Fp_Hdmi3_b, у каждого событие push. Нажмите «Готово». У узла три выхода. Подробно про правила — «Приём событий».

  3. К каждому выходу подключите свой узел change. Назовите их в поле «Имя» «HDMI 1 → экран», «HDMI 2 → экран» и «HDMI 3 → экран». В первом нажмите «добавить» и задайте правило «Установить», в первом списке выберите msg., впишите parameters. В строке «to the value» выберите «JSON» и впишите {"input": 1, "output": 1}. Во втором — {"input": 2, "output": 1}, в третьем — {"input": 3, "output": 1}. Здесь input — номер входа матрицы, output — номер выхода (1 — экран зала).

  4. Перетащите device - command («Имя» — «Матрица: подать вход») и соедините с ним выходы всех трёх change. В окне выберите «Соединение» — подключение к вашей матрице («Демо: матрица») и в «Команда» — route. Нажмите «Готово».

  5. Нажмите «Развернуть».

../../_images/fp-b-switch.png

Окно узла ui - switch «Кнопки HDMI: по правилу»: 1 — «Интерфейс», 2 — три правила по одному на кнопку (Fp_Hdmi1_b, Fp_Hdmi2_b, Fp_Hdmi3_b), 3 — событие push

../../_images/fp-b-change.png

Окно узла change «HDMI 2 → экран»: 1 — правило «Установить» msg.parameters, 2 — тип «JSON» и значение {"input": 2, "output": 1}

Что вы увидите. Нажмите на панели «HDMI 2»: кнопка сразу станет подсвеченной (подсветку делает группа «Interlock» в самом интерфейсе), а матрица получит команду «вход 2 на выход 1». Подпись «Экран» поменяется чуть позже — когда матрица ответит (блок D).

Как проверить. Подключите debug («весь msg объект сообщения») к выходу узла change «HDMI 2 → экран» и нажмите «HDMI 2». В отладке: msg.parameters — { input: 2, output: 1 }. Если ничего не пришло, подключите debug к выходу ui - switch: там должен быть payload.object = Fp_Hdmi2_b.

Блок C. Компактно: карта входов

Зачем. Если кнопок много, на каждую заводить правило и узел change — утомительно и легко ошибиться. Решение: одно правило на все кнопки (по шаблону имени) и одна таблица «кнопка → параметры», которую записал блок A. Узел change по имени нажатой кнопки достаёт из таблицы нужные параметры. Если добавить четвёртую кнопку, достаточно дописать строку в таблицу, остальное не меняется.

Цепочка: UI - Panel in → ui - switch (одно правило по шаблону) → change → тот же device - command «Матрица: подать вход».

Шаги.

  1. Добавьте ещё один UI - Panel in («Имя» — «События панели») и соедините с ним ui - switch.

  2. Откройте ui - switch. В «Имя узла» впишите «Кнопки HDMI: по шаблону». В «Интерфейс» выберите «Первая программа». Добавьте правило, включите флажок «Шаблон», впишите Fp_Map_*_b, выберите режим wildcard (*, ?) и событие push. Нажмите «Готово». Теперь любая кнопка, имя которой начинается с Fp_Map_ и заканчивается _b, попадёт в этот выход.

  3. Подключите узел change («Имя» — «Кнопка → параметры по карте»). Нажмите «добавить» и задайте правило: «Установить» msg. parameters. В строке «to the value» выберите тип global. и впишите fpInputs[msg.payload.object]. Под значением включите флажок «Deep copy value» («глубокая копия»). Нажмите «Готово».

  4. Соедините выход change со входом того же device - command, что в блоке B (второй device - command не нужен: одна матрица — один узел).

  5. Нажмите «Развернуть».

../../_images/fp-c-switch.png

Окно узла ui - switch «Кнопки HDMI: по шаблону»: 1 — «Интерфейс», 2 — шаблон Fp_Map_*_b с флажком «Шаблон», 3 — режим wildcard (*, ?), 4 — событие push

../../_images/fp-c-change.png

Окно узла change «Кнопка → параметры по карте»: 1 — «Установить» msg.parameters, 2 — значение берётся из global. по ключу fpInputs[msg.payload.object], 3 — флажок «Deep copy value»

Как это работает

Выражение fpInputs[msg.payload.object] по частям:

  • fpInputs — имя переменной в памяти: таблица, которую записал блок A. Это строки вида «имя кнопки: параметры».

  • В квадратных скобках — ключ: какую строку таблицы взять. Ключ узел вычисляет сам из сообщения: msg.payload.object — это имя нажатой кнопки, оно пришло с панели и лежит в сообщении после ui - switch (в отладке вы видели его как object).

  • Значит, нажали Fp_Map_Hdmi2_b → ключ равен "Fp_Map_Hdmi2_b" → из таблицы берётся {"input": 2, "output": 2} → оно записывается в msg.parameters. А device - command подставляет msg.parameters в команду route.

Вот почему имя элемента в UI-Designer должно совпадать до буквы с ключом в таблице: ключ — это и есть имя элемента.

Зачем «глубокая копия». Без неё в сообщение попадёт не сама строка таблицы, а ссылка на неё в памяти. Если дальше по цепочке какой-то узел допишет или изменит msg.parameters, изменится и таблица в памяти — для всех следующих нажатий. С флажком сообщение получает свою копию, и таблица остаётся прежней. Включайте флажок всегда, когда берёте из памяти таблицу или список целиком.

Что вы увидите. Нажмите «HDMI 3» в секции 2: матрица получает «вход 3 на выход 2». Кнопка на панели пока не подсветится — подсветка придёт по ответу матрицы (блок D). Повторите нажатие несколько раз: таблица в памяти не портится.

Как проверить. Подключите debug к выходу change: msg.parameters — { input: 3, output: 2 }. Если там undefined — ключа нет в таблице: проверьте, что блок A развёрнут и запущен, и что имя кнопки совпадает с ключом.

Блок D. Ответ матрицы: надписи и подсветка

Зачем. Нажатие — это только намерение. Команда могла не дойти, матрицу могли переключить с пульта. Поэтому подписи и подсветку лучше менять по ответу устройства, как бы оно ни было переключено: на панели видно то, что произошло на самом деле. Блок D принимает ответ матрицы и раскладывает его: выход 1 — надпись «Экран», выход 2 — «Экран председателя» и подсветка кнопок секции 2.

Цепочка: device - response - listener → switch «Подтверждено?» → switch «Какой выход?» → узлы change → api - helper - 2 и interlock - 2 → UI - Panel out.

Шаги.

  1. Перетащите device - response - listener («Имя» — «Матрица: ответы»). «Соединение» — то же, что у device - command (матрица). «Выходы» — 2. В «Типы ответов» отметьте route и выберите для него «Выход 2» (первый выход останется для прочего). Нажмите «Готово». Подробнее — «Команды устройствам и ответы».

  2. К «Выходу 2» подключите switch «Подтверждено?»: свойство msg.payload.ack, одно правило «равно true». Драйвер матрицы сообщает об операции дважды: когда команда отправлена (ack: false) и когда матрица ответила (ack: true). Нужен только второй ответ: он и есть факт. Первый этот switch отбрасывает, иначе надпись обновилась бы раньше, чем матрица что-то сделала.

  3. Подключите второй switch «Какой выход?». Свойство — msg.payload.output, два правила «==» (число): 1 и 2. Это фильтр ответа: ответ несёт номер выхода, который переключился, и по нему узел решает, куда направить ответ.

  4. С выхода 1 («экран») подключите change «Вход → название экрана»: два правила «Установить». Первое — msg.topic как «JSON» {} (пустой объект). Второе — msg.topic.Fp_Screen_val_lb, значение типа global. — fpTitles[msg.payload.input]. Так в msg.topic получается запись {"Fp_Screen_val_lb": "HDMI 2"}: «имя надписи → её текст». Дальше подключите api - helper - 2 «Надпись: экран»: «Интерфейс» — «Первая программа». В «Объекты управления» добавлен элемент Fp_Screen_val_lb с событием «Текст». «Источник входных данных» оставлен по умолчанию (msg.topic). Подробно об этом узле — «Изменение вида на панели».

  5. С выхода 2 («председатель») подключите две ветки.

    • Название: такой же change «Вход → название председателя» (msg.topic.Fp_Chair_val_lb) и api - helper - 2 «Надпись: председатель» с элементом Fp_Chair_val_lb.

    • Подсветка: change «Вход → кнопка»: «Установить» msg.payload, значение типа global. — fpButtons[msg.payload.input]. Это имя кнопки секции 2, которая соответствует номеру входа в ответе. Затем interlock - 2 «Подсветка: карта входов»: «Интерфейс» — «Первая программа», «Тип события» — uiState, «Активное состояние» — selected, «Состояние по умолчанию» — default, «Источник входных данных» — msg.payload. В «Список кнопок» через «Кнопки интерфейса» и «Добавить» добавьте три кнопки Fp_Map_Hdmi1_b, Fp_Map_Hdmi2_b, Fp_Map_Hdmi3_b. Узел подсвечивает ту, чьё имя пришло, остальные гасит.

  6. Выходы всех api - helper - 2 и interlock - 2 соедините с UI - Panel out («Имя» — «Команды панели»). Нажмите «Развернуть».

../../_images/fp-d-switch-output.png

Окно узла switch «Какой выход?»: 1 — свойство msg.payload.output, 2 — правило «== 1» (экран, выход 1 узла), 3 — правило «== 2» (экран председателя, выход 2 узла)

Зачем фильтр «Какой выход?». Матрица одна на несколько выходов. Допустим, вы нажали в секции 2 «HDMI 1» — матрица ответила «выход 2 ← вход 1». Если не проверить номер выхода, этот ответ изменил бы и надпись «Экран»: ведь выход 2 к залу отношения не имеет. Выход 2 в нашем интерфейсе — экран председателя; обычный switch по номеру выхода направляет ответ в нужную ветку. Ответ по выходу, которого нет среди правил, не пойдёт никуда. Тот же приём стоит в «Демо-зале»: там выход 1 — экран зала, и ответы по другим выходам отброшены.

Что вы увидите. Нажмите в секции 1 «HDMI 2»: через мгновение «Экран:» покажет «HDMI 2». Нажмите в секции 2 «HDMI 3»: «Экран председателя:» покажет «HDMI 3», кнопка «HDMI 3» подсветится, остальные в секции 2 погаснут, а «Экран:» не изменится.

Как проверить. Подключите debug к выходу «Подтверждено?»: после нажатия там одно сообщение с payload.ack: true, payload.output и payload.input. Нет сообщения — ответ матрицы не дошёл: проверьте «Соединение» и тип ответа route в device - response - listener.

Блок E. Микрофон через switch

Зачем. Кнопка-переключатель (микрофон, звук) сама меняет вид на панели, а программе нужно узнать новое положение и отправить устройству «включить» или «выключить». Положение приходит событием value_changed.

Что приходит в программу, если нажать переключатель: сначала push, release, tap (как у любой кнопки), а следом — value_changed с полем value. При включении value равно "pressed" (кнопка нажата), при выключении — "default" (кнопка вернулась в обычное состояние). Поэтому в правиле ui - switch выбирают именно value_changed.

Цепочка: UI - Panel in → ui - switch → switch «Нажата?» → два change → device - command.

Шаги.

  1. Добавьте ui - switch («Имя узла» — «Микрофон: через switch»): «Интерфейс» — «Первая программа», правило на Fp_MicSwitch_b, событие value_changed.

  2. Добавьте switch «Нажата?»: свойство msg.payload.value, два правила «==» (строка): pressed и default.

  3. К выходу pressed подключите change «Микрофон: включить»: два правила «Установить»: msg.command — «строка» setMute; msg.parameters — «JSON» {"mute": false}. К выходу default подключите change «Микрофон: выключить»: то же, но {"mute": true}. Здесь mute — «звук отключён»: нажатая кнопка (зелёная, значок микрофона) означает, что микрофон включён.

  4. Оба change соедините с device - command («Имя» — «Кодек: микрофон»): «Соединение» — кодек ВКС. В поле «Команда» выберите setMute (поле обязательно; ту же команду задаёт msg.command).

  5. Нажмите «Развернуть».

../../_images/fp-e-switch-pressed.png

Окно узла switch «Нажата?»: 1 — свойство msg.payload.value, 2 — правило «== pressed» (строка), 3 — правило «== default» (строка)

Что вы увидите. Нажали «через switch» — кнопка зелёная, значок микрофона без перечёркивания, кодек получил setMute с mute: false. Нажали снова — кнопка белая, значок перечёркнут, кодек получил mute: true. Одна команда на нажатие.

Как проверить. Подключите debug к выходу ui - switch: на каждое нажатие по правилу придёт одно сообщение value_changed (не три). Подключите debug к «Нажата?»: сообщение выйдет из верхнего или нижнего выхода в зависимости от положения.

Блок F. Микрофон через JSONata

Зачем. Тот же микрофон, но короче: один узел change вместо switch и двух change. Здесь пригодится JSONata.

Что такое JSONata. Это короткий язык выражений: вы пишете формулу, а узел вычисляет её над сообщением и подставляет результат. Формула видит сообщение как набор полей: payload.value — это то же, что msg.payload.value. В окне узла change JSONata выбирается типом значения «выражение» (рядом со «строка», «JSON» и другими).

Шаги.

  1. Добавьте ui - switch («Имя узла» — «Микрофон: через JSONata»): правило на Fp_MicJsonata_b, событие value_changed.

  2. Добавьте один change («Имя» — «setMute по JSONata») с двумя правилами «Установить» (кнопка «добавить»):

    • msg.command — «строка» setMute;

    • msg.parameters — тип «выражение», значение

    {"mute": payload.value = "default"}
    
  3. Соедините change с device - command «Кодек: микрофон (JSONata)» (то же подключение — кодек ВКС). Это второй узел команды для того же кодека: блоки E и F — два независимых учебных варианта, и у каждого свой узел; в вашей программе хватит одного.

  4. Нажмите «Развернуть».

../../_images/fp-f-change-jsonata.png

Окно узла change «setMute по JSONata»: 1 — правило msg.command (строка setMute), 2 — правило msg.parameters с типом «выражение», 3 — выражение {"mute": payload.value = "default"}

Разбор выражения {"mute": payload.value = "default"}:

  • { … } — фигурные скобки создают объект: пары «имя: значение», как в JSON. Результат — объект, который уйдёт в msg.parameters.

  • "mute": — имя поля, которое ждёт команда setMute.

  • payload.value — значение, которое пришло от кнопки: "pressed" или "default".

  • = "default" — сравнение: «равно ли это значение "default"?». Результат — true или false.

Итого: пришло "default" (кнопку выключили) → {"mute": true}; пришло "pressed" (включили) → {"mute": false}. Одно выражение заменило switch и два change блока E.

Что вы увидите. Те же действия, что в блоке E: нажали — микрофон включён, нажали ещё раз — выключен, кодек получает по одной команде на нажатие.

Что выбрать. switch нагляднее и подойдёт, когда на каждое положение нужны разные действия. Выражение короче, когда различие только в одном значении. Работают обе записи.

Блок G. Переход из программы

Зачем. Иногда страницу должна открыть программа: по результату проверки, по событию устройства. Для этого есть тип «navigation» в api - helper - 2. Ограничение: он открывает только страницы интерфейса по имени. Оверлей (отдельная «подстраница» со своим холстом) во фрейме (область на странице, в которой показан оверлей) программа показывает другим способом — событием «Overlay» того же узла; см. «Сменить оверлей во фрейме».

Цепочка: UI - Panel in → ui - switch → api - helper - 2 (navigation) → UI - Panel out.

Шаги.

  1. Добавьте ui - switch («Имя узла» — «Открыть подсказку»): правило на Fp_Open_b, событие push.

  2. Добавьте api - helper - 2 («Имя узла» — «Перейти: Подсказка»): «Интерфейс (UI)» — «Первая программа»; «Тип сообщения» — navigation. Ниже «Объектов управления» появятся поля «Поиск:» и «Страница:»: найдите и выберите страницу «Подсказка». Входные данные для этого типа не нужны: узел срабатывает на любое сообщение.

  3. Выход соедините с UI - Panel out («Имя» — «Команды панели»). Нажмите «Развернуть».

../../_images/fp-g-navigation.png

Окно узла api - helper - 2 «Перейти: Подсказка»: 1 — «Интерфейс (UI)», 2 — «Тип сообщения» navigation, 3 — «Страница:» «Подсказка»

Что вы увидите. Нажали «Открыть подсказку» — на панели вместо главной страницы открылась «Подсказка». «Закрыть» вернуло главную: эта кнопка — ссылка из UI-Designer, программа для возврата не нужна.

Как проверить. Подключите debug к выходу api - helper - 2: после нажатия придёт сообщение, в котором есть имя страницы «Подсказка». Нет перехода — смотрите «Частые ошибки» ниже.

Блок H. Звук

Зачем. Блоки B–D показали путь «нажали → команда → ответ → подпись» для матрицы. Здесь тот же путь для звукового процессора, но с двумя новыми приёмами. Первый — ползунок: число из ответа превращается в положение ручки и подпись, а положение ручки — обратно в команду в децибелах. Второй — подписка: процессор присылает ответы только тем, кто об этом попросил. Что приходит от процессора, как устроены выражения и окно api - helper - 2 — пошагово в «Изменении вида на панели», примеры 1 и 2; здесь — как собрать блок целиком.

Блок состоит из трёх групп на вкладке: «5. Звук: кнопка и ползунок → процессор» (команды), «Звук: подписка на ответы процессора» и «Звук: ответ процессора → кнопка и ползунок». Цифра 5 в названии первой группы — номер секции интерфейса, а не блока: так же 6 в названии первой группы блока I.

Вкладка целиком, блок «Звук»:

../../_images/s5-tab-sound.png

Блок H «Звук» на вкладке (в кадре также группа подписки): 1 — ui - switch «Звук: кнопка и ползунок», 2 — change «Громкость: 0…1 → дБ», 3 — device - response - listener «Звук: ответы», 4 — switch «Какая подписка?», 5 — change «Уровень → ползунок и надпись», 6 — api - helper - 2 «Громкость: ползунок и надпись»

Группа 1. Команды: кнопка и ползунок → процессор

Цепочка: UI - Panel in → ui - switch (два выхода) → два change → device - command.

Шаги.

  1. Перетащите UI - Panel in («Имя» — «События панели») и соедините с ним ui - switch («Имя узла» — «Звук: кнопка и ползунок»). В «Интерфейс» выберите «Первая программа». Добавьте два правила: Fp_Mute_b с событием value_changed (выход 1) и Fp_Vol_sl с событием slide_end (выход 2). Кнопка Fp_Mute_b — переключатель, как микрофон в блоках E и F, поэтому для неё выбрано value_changed. slide_end приходит один раз, когда оператор отпустил ручку. Поле «Отправлять команды после» у ползунка должно стоять в положении «Отпускание» («Элементы интерфейса»).

  2. К выходу 1 подключите change «Звук: mute по кнопке» с двумя правилами «Установить»: msg.command — «строка» setMute; msg.parameters — «выражение» (в окне change этот тип отмечен значком J: перед полем; слово «выражение» видно только в меню выбора типа):

    {"instanceTag": "DemoLevels", "channel": 1, "state": payload.value = "default"}
    

    Это тот же приём, что в блоке F: payload.value — "pressed" или "default", а сравнение = "default" даёт true, когда кнопка стала обычной («Звук выключен»). state — «включить ли mute»; instanceTag и channel — адрес регулятора в процессоре.

  3. К выходу 2 подключите change «Громкость: 0…1 → дБ»: msg.command — «строка» setLevel; msg.parameters — «выражение» {"instanceTag": "DemoLevels", "channel": 1, "level": $round(payload.value * 60 - 60)}. Что значит это выражение — в «Изменении вида на панели», пример 2, раздел «Обратное направление».

  4. Оба change соедините с device - command («Имя» — «Звук: команда»): «Соединение» — «Демо: звуковой процессор», «Команда» — setMute (обязательное поле; нужную команду задаёт msg.command).

Группа 2. Подписка на ответы процессора

Зачем подписка. Нажатие на панели — это просьба к процессору. Но состояние звука и уровня может поменяться и без этой панели: другой панелью, пультом, сценарием. Процессор сам об этом не рассказывает, пока его не попросят. Подписка — просьба, которую программа отправляет один раз: «присылай мне новое значение этого параметра каждый раз, когда оно меняется, и называй его вот так». Название подписки приходит в каждом ответе (поле publishToken). По нему программа потом понимает, что это за ответ. Здесь две подписки: fpHallMute — звук включён или выключен, и fpHallLevel — уровень.

Когда подписываться и почему пауза. Подписка живёт, пока работает связь с процессором. Связь может прерваться и подняться снова, поэтому подписку отправляют каждый раз после подключения, а не один раз при запуске программы. Но сразу после сообщения «подключился» связь ещё не готова принимать команды, и первая же команда может потеряться. Поэтому между «подключился» и подпиской ставят паузу 2 секунды. Пока подписки нет, панель не узнает об ответах процессора: на странице «Устройства» останется то, что задано в UI-Designer («—», «Нет звонка»).

Цепочка: device - command «Звук: команда» (первый выход) → switch «Подключился?» → delay «Пауза 2 с» → два change → device - command «Звук: подписаться». Дополнительно — inject «При запуске»: при запуске программы подписка тоже должна уйти, а связь к тому времени уже может быть установлена.

Шаги.

  1. Первый выход device - command «Звук: команда» (состояние соединения) подключите к switch «Подключился?»: свойство msg.payload.status, одно правило «==» (строка) connected. Остальные состояния (disconnected, error) он отбрасывает.

  2. Выход switch подключите к delay («Имя» — «Пауза 2 с»): «Действие» — «Задержка каждого сообщения», «Фиксированная задержка» — 2 секунды.

  3. Добавьте inject («Имя» — «При запуске»): «Отправить через» — 3 секунды, один раз после запуска. Его выход и выход delay подключите к двум узлам change одновременно.

  4. Добавьте change «Подписка: mute зала»: правило «Установить» msg.parameters, тип «JSON»:

    {"instanceTag": "DemoLevels", "attribute": "mute", "channel": 1, "subscriptionName": "fpHallMute", "minInterval": 0}
    
  5. Добавьте change «Подписка: громкость зала» — то же, но "attribute": "level", "subscriptionName": "fpHallLevel", "minInterval": 200.

  6. Оба change подключите к device - command («Имя» — «Звук: подписаться»): «Соединение» — «Демо: звуковой процессор», «Команда» — subscribe.

Что значат поля подписки:

  • attribute — что отслеживать (mute — звук, level — уровень);

  • instanceTag и channel — какой регулятор;

  • subscriptionName — как назвать подписку (имя придёт в ответе как publishToken; выберите имя, которое не используют другие подписки на том же процессоре);

  • minInterval — не чаще одного уведомления за столько миллисекунд (0 — без ограничения).

Для уровня стоит 200: ползунок не будет «дрожать», когда уровень меняется быстро.

../../_images/s5-subscribe.png

Группа «Звук: подписка на ответы процессора»: 1 — switch «Подключился?», 2 — delay «Пауза 2 с», 3 — inject «При запуске», 4 — change «Подписка: mute зала» и «Подписка: громкость зала», 5 — device - command «Звук: подписаться»

Группа 3. Ответ процессора → кнопка и ползунок

Цепочка: device - response - listener → switch «Какая подписка?» → два change → два api - helper - 2 → UI - Panel out.

Шаги.

  1. Добавьте device - response - listener («Имя» — «Звук: ответы»): «Соединение» — «Демо: звуковой процессор», «Выходы» — 2. В «Типы ответов» отметьте subscribeNotification и выберите «Выход 2».

  2. К «Выходу 2» подключите debug («Имя» — «Что прислал процессор», «Выводить» — msg.payload; он выключен, пока вы не проверяете) и switch «Какая подписка?» (свойство msg.payload.publishToken, правила fpHallMute и fpHallLevel).

  3. Выход fpHallMute → change «Mute → вид кнопки» → api - helper - 2 «Кнопка: звук зала». Выход fpHallLevel → change «Уровень → ползунок и надпись» → api - helper - 2 «Громкость: ползунок и надпись». Выражения, поля и карточки — «Изменение вида на панели», примеры 1 и 2.

  4. Выходы обоих api - helper - 2 соедините с UI - Panel out («Имя» — «Команды панели»). Нажмите «Развернуть».

../../_images/s5-listener-sound.png

Окно узла device - response - listener «Звук: ответы»: 1 — «Соединение», 2 — «Выходы» (2), 3 — тип ответа subscribeNotification и «Выход 2»

Что вы увидите. Откройте на панели страницу «Устройства» (кнопка «Устройства» на главной). Через несколько секунд после запуска кнопка «Звук» и ползунок показывают то, что сейчас выставлено на процессоре, а надпись «Громкость:» — уровень в дБ. Нажмите «Звук»: процессор получает setMute, присылает уведомление fpHallMute, кнопка принимает вид по этому ответу. Подвиньте ручку и отпустите: процессор получает setLevel, присылает fpHallLevel, ручка и надпись встают по ответу. Измените звук или уровень на самом процессоре — панель последует за ним.

Как проверить. Включите debug «Что прислал процессор» и откройте боковую панель отладки. Сразу после запуска (через 3 секунды, а после переподключения — через 2) придут два сообщения: fpHallMute и fpHallLevel. Не пришли — подписка не ушла: проверьте статус device - command «Звук: команда» (подключён ли процессор), instanceTag и канал в параметрах и switch «Подключился?» (отладка за ним). Сообщение пришло, а вид не поменялся — отладка за change покажет, что лежит в msg.topic.

Блок I. ВКС

Зачем. Тот же принцип для кодека видеоконференцсвязи: состояние звонка (текст и цвет плашки) и список контактов приходят в ответах кодека. Блок учит ещё двум вещам: как поменять два свойства одного элемента одним сообщением и как превратить массив из ответа в строки списка выражением change. Разбор ответов и выражений — «Изменение вида на панели», примеры 3 и 4.

Блок состоит из двух групп: «6. ВКС: кнопки и список → кодек» (команды) и «ВКС: ответы кодека → надпись и список».

Вкладка целиком, блок «ВКС»:

../../_images/s5-tab-vcs.png

Блок I «ВКС» на вкладке: 1 — ui - switch «ВКС: кнопки и список», 2 — change «Строка → кому звоним», 3 — device - response - listener «Кодек: ответы», 4 — change «Контакты → строки списка», 5 — change «Звонок → текст и цвет», 6 — api - helper - 2 «Надпись: звонок»

Группа 1. Кнопки и список → кодек

Цепочка: UI - Panel in → ui - switch (четыре выхода) → change и switch → device - command и api - helper - 2.

Шаги.

  1. Соедините UI - Panel in с ui - switch («Имя узла» — «ВКС: кнопки и список»). Четыре правила: Fp_Contacts_b с событием push (выход 1), Fp_Contacts_ls с событием item_selected (выход 2), Fp_Call_b с событием push (выход 3), Fp_Hangup_b с событием push (выход 4).

  2. Выход 1 → change «Команда: адресная книга»: msg.command — «строка» getContacts. Он идёт в device - command «Кодек: команда» («Соединение» — «Демо: кодек ВКС», «Команда» — getContacts).

  3. Выход 2 → change «Строка → кому звоним» и api - helper - 2 «Надпись: кому звоним» (разбор — в примере 4 страницы «Изменение вида на панели»): номер выбранного контакта запоминается в памяти программы, а в надписи Fp_Target_val_lb появляется имя контакта.

  4. Выход 3 → switch «Контакт выбран?» (свойство — global. fpNumber, правило «не равно null»: пока контакт не выбран, звонить некому) → change «Команда: позвонить»: msg.command — «строка» dial, msg.parameters.number — значение типа global. fpNumber. Выход идёт в тот же device - command «Кодек: команда».

  5. Выход 4 → change «Команда: завершить» (msg.command — «строка» hangup) → тот же device - command.

Группа 2. Ответы кодека → надпись и список

Цепочка: inject → device - command (запрос состояния); device - response - listener → два change → два api - helper - 2 → UI - Panel out.

Шаги.

  1. Кодек о звонке сам не сообщает, поэтому его спрашивают. Добавьте inject («Имя» — «Раз в 3 с»): в списке «Повторять» выберите «с интервалом», впишите 3 и единицы «сек», а отметку «Отправить через» — 5 секунд после запуска. Подключите его к device - command («Имя» — «Кодек: состояние звонка»): «Соединение» — «Демо: кодек ВКС», «Команда» — getCallInfo.

  2. Добавьте device - response - listener («Имя» — «Кодек: ответы»): «Соединение» — «Демо: кодек ВКС», «Выходы» — 3. В «Типы ответов» отметьте contacts — «Выход 2» и callInfo — «Выход 3»; первый выход остаётся для прочих ответов.

  3. К каждому из выходов 2 и 3 подключите debug «Что прислал кодек» (общий для обоих выходов; выключен, пока вы не проверяете).

  4. «Выход 2» → change «Контакты → строки списка» → api - helper - 2 «Список контактов».

  5. «Выход 3» → change «Звонок → текст и цвет» → api - helper - 2 «Надпись: звонок».

  6. Выходы обоих api - helper - 2 соедините с UI - Panel out («Команды панели»). Нажмите «Развернуть».

../../_images/s5-listener-vcs.png

Окно узла device - response - listener «Кодек: ответы»: 1 — «Соединение», 2 — «Выходы» (3), 3 — тип contacts и «Выход 2», 4 — тип callInfo и «Выход 3»

Что вы увидите. На странице «Устройства» нажмите «Контакты»: в списке появятся контакты из адресной книги кодека. Выберите строку: в надписи «Кому звоним» — имя контакта. Нажмите «Позвонить»: кодек набирает номер, и в течение 3 секунд плашка меняется с «Нет звонка» на «Идёт разговор» на светло-зелёном фоне. Нажмите «Завершить»: плашка снова «Нет звонка». Позвоните или завершите звонок с самого кодека — плашка поменяется так же: вид ведёт ответ, а не нажатие.

Как проверить. Включите debug «Что прислал кодек»: раз в 3 секунды приходит callInfo с полем inCall, на «Контакты» — contacts со списком list. Нет callInfo — проверьте подключение к кодеку и inject «Раз в 3 с». callInfo есть, а плашка не меняется — отладка за change «Звонок → текст и цвет» покажет, что лежит в msg.topic.

Что дальше

Вы собрали все основные приёмы. Дальше:

Частые ошибки

Общий маршрут проверки по цепочке «панель → программа → устройство → панель» — на странице «Не работает: где смотреть».

  • На панели только точки загрузки. В программе нет развёрнутого UI - Panel in (или не нажато «Развернуть»). Создайте в поле Path подключение UI-listener с путём /web-client, нажмите «Готово» и «Развернуть», обновите страницу панели («Первый интерфейс»). Если в селекторе нет нужного интерфейса, проект не опубликован.

  • Нажимаю кнопку — ничего не происходит. Подключите debug к выходу UI - Panel in: приходит ли событие? Если приходит, смотрите выход нужного правила ui - switch. Если нет — не выбран «Интерфейс» в узле, имя элемента отличается от имени в правиле хоть на букву, или интерфейс не опубликован.

  • Действие выполняется три раза. В правиле оставлено «Любое событие»; выберите push (одно нажатие — три события: push, release, tap).

  • В msg.parameters лежит undefined (блок C). Блок A не развёрнут или не запускался, либо ключа нет в таблице. Запустите inject кнопкой слева от него и проверьте имя кнопки.

  • Таблица «портится» после нескольких нажатий. Не включена «глубокая копия» в change: следующие узлы меняют ту же таблицу в памяти.

  • Подписи «Экран» не меняются. Ответ матрицы не дошёл до блока D (нет «Соединения» или типа route) или отброшен: switch «Подтверждено?» пропускает только ответ с ack: true, а «Какой выход?» — только выходы 1 и 2.

  • «Экран» поменялся, хотя нажимали секцию 2. Нет фильтра «Какой выход?» или он пропускает ответ в неправильную ветку.

  • Кнопка секции 2 не подсвечивается. На вход interlock - 2 должна прийти строка с именем кнопки: проверьте change «Вход → кнопка» и список кнопок в узле. Пока группы «Interlock» в интерфейсе нет, подсвечивает только программа.

  • Переключатель шлёт команду дважды или не шлёт. В правиле выбрано push вместо value_changed; или кнопка не отмечена как toggle в UI-Designer; или она входит в группу «Interlock» — у такой кнопки value_changed не приходит.

  • Переход на «Подсказку» не работает. В узле не выбрана «Страница:» или выбран другой интерфейс. Перейти можно только на страницу; оверлей показывают событием «Overlay» («Изменение вида на панели»). Кроме того, не кладите объект в msg.topic перед этим узлом: по умолчанию он читает входные данные оттуда.

  • Команда не доходит. Не выбрано «Соединение» в device - command (статус «Не настроено соединение») или устройство ещё не подключилось.

  • Страница «Устройства» пустая: «Нет звонка» и «—». Ответы устройств не доходят. Подключите debug («Что прислал процессор» или «Что прислал кодек») и посмотрите, приходят ли ответы. Для звука — выполнена ли подписка: узел «Звук: команда» должен быть подключён к процессору, а после запуска и после каждого подключения через 2 секунды уходят подписки.

  • Ответ приходит, а ползунок и кнопка не меняются. В switch «Какая подписка?» не совпало имя подписки (fpHallMute, fpHallLevel) с тем, что написано в change подписки; или имя элемента в выражении change отличается от имени в UI-Designer.

  • Ползунок стоит не там. Min и Max в карточке api - helper - 2 не совпадают с диапазоном громкости (−60…0) или числа в выражении «Громкость: 0…1 → дБ» не такие же.

  • Плашка звонка меняется с задержкой. Кодек сам о звонке не сообщает: программа спрашивает его раз в 3 секунды. Это нормально.

  • «Позвонить» ничего не делает. Контакт не выбран: правило «не равно null» в switch «Контакт выбран?» не пропускает сообщение, пока в памяти нет номера. Сначала нажмите «Контакты» и выберите строку.

  • Изменения не действуют. Правки узлов работают после «Развернуть».