Изменение вида на панели: api - helper - 2

Оператор нажал кнопку, устройство ответило, и теперь на панели нужно что-то показать: подсветить кнопку, подвинуть ползунок, написать «Идёт разговор» на зелёной плашке, заполнить список. Для этого в U-Logic (редактор программы управления) есть узел (блок программы) api - helper - 2. Вы говорите ему «у элемента с таким именем измени вот это», а он собирает сообщение, понятное панели, и передаёт узлу UI - Panel out.

Главное: что выбираете вы и что приходит в сообщении

Узел делает работу в два приёма; не путайте, кто какой делает.

Что

Где задаётся

Пример

Что менять — какой элемент и какое его свойство (вид кнопки, текст, цвет фона, положение ползунка, строки списка)

в окне узла, в карточке «Объекты управления»: «Элемент:» и «Событие:»

элемент Fp_Mute_b, событие «Состояние кнопки»

Каким сделать — само значение

в сообщении, по умолчанию в msg.topic, записью {"имя элемента": значение}

{"Fp_Mute_b": "pressed"}

Значение не придумывают и не вписывают в карточку руками. Его готовит узел change из ответа устройства. Ответ пришёл, change взял из него нужное поле и положил в msg.topic в виде {"имя элемента": значение}. Узел api - helper - 2 по имени нашёл элемент в карточке и отправил панели готовую команду.

        flowchart LR
    DEV["Устройство"] --> RL["device - response - listener<br/>(ответ устройства)"]
    RL --> SW["switch<br/>(какой это ответ?)"]
    SW --> CH["change<br/>(готовит msg.topic)"]
    CH --> AH["api - helper - 2<br/>(что менять)"]
    AH --> OUT["UI - Panel out"]
    OUT --> P["Панель"]
    

Здесь разобраны четыре таких пути на живых ответах двух устройств: звукового процессора и кодека видеоконференцсвязи:

  1. состояние кнопки по ответу: «звук выключен» → вид кнопки;

  2. ползунок и надпись: уровень громкости из ответа → положение ручки и подпись в дБ, а в обратную сторону — ручку отпустили → команда в дБ;

  3. текст и цвет фона по состоянию звонка;

  4. список из ответа кодека и выбор строки.

Каждый пример построен одинаково: (1) что пришло от устройства — сообщение из отладки; (2) разбор — узлы switch и change, поля окон, выражение по частям; (3) окно api - helper - 2 — какая карточка; (4) что получилось на панели. Затем идут проверка без устройства, сводная таблица, переход на другую страницу и смена оверлея (отдельной «подстраницы» со своим холстом), в конце — частые ошибки.

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

  • интерфейс сохранён и опубликован в UI-Designer (редактор интерфейсов): иначе узел не увидит элементов (см. «Связь интерфейса с программой»);

  • на вкладке (рабочем листе программы) есть развёрнутый узел UI - Panel out (правки запущены в работу кнопкой «Развернуть»);

  • вы прошли «Первую программу»: знаете ui - switch, change и device - response - listener. Примеры ниже — это блоки «Звук» и «ВКС» той же вкладки «Первая программа» и страница «Устройства» её интерфейса. Как их собрать по шагам, описано в блоках H и I на странице «Первая программа по шагам».

Примечание

Выражения в change ниже записаны на JSONata — коротком языке формул, который вычисляется над сообщением. Если он вам ещё не знаком, прочитайте блок F в «Первой программе»: там разобрано, что такое payload.value, фигурные скобки и сравнение.

Как узел работает

  1. берёт данные — объект «имя элемента → значение» — из выбранного поля сообщения;

  2. сверяет имена с элементами выбранного интерфейса;

  3. кладёт в msg.payload готовое сообщение для панели; прежнее содержимое msg.payload при этом пропадает.

Поэтому данные для панели кладите в msg.topic, а не в msg.payload: после этого узла прежнего содержимого msg.payload уже не будет. Если оно нужно дальше, возьмите его до api - helper - 2.

В готовом сообщении два списка: payload.actions — все действия, по одному на событие, и payload.object — прежний формат для совместимости, где на один элемент остаётся одно событие. Если одному элементу вы задали два события (например, uiState и text), в payload.object виден только один из них: при отладке смотрите payload.actions.

Окно узла

../../_images/api-helper-text.png

Окно узла api - helper - 2: 1 — «Интерфейс (UI)», 2 — «Тип сообщения», 3 — «Источник входных данных», 4 — карточка элемента в списке «Объекты управления» с полями «Элемент:» и «Событие:»

  1. «Интерфейс (UI)» — интерфейс, которому адресовано сообщение. Без него панель не поймёт, куда применить изменения. Список интерфейсов появляется после «Сохранить» и «Опубликовать» в UI-Designer.

  2. «Тип сообщения» — выберите uiState: это «изменить вид элементов». Второй тип, navigation, открывает страницу интерфейса (см. раздел «Переход на другую страницу из программы» ниже).

  3. «Источник входных данных» — откуда узел берёт объект с данными. По умолчанию это msg.topic: оставьте так, и данные готовит change в msg.topic. Можно указать другое поле сообщения (например, msg.payload) или переменную потока или общую память.

  4. «Объекты управления» — список элементов, которыми управляет узел. Элемент добавляют полем «Поиск элемента для добавления…» и кнопкой «+». В карточке элемента выбирают «Элемент:» и «Событие:» (что именно менять); поле «Значение:» — запасное значение, которое уйдёт, если данные не скажут другого.

Примечание

Список элементов необязателен. Если в «Объектах управления» пусто, узел принимает имена прямо из данных, но тогда для каждого элемента надо указать, что менять: {"Fp_Mute_b": {"uiState": "pressed"}}. Если элемент есть в списке, достаточно короткой записи: {"Fp_Mute_b": "pressed"}. Исключение — ползунок: ему карточка обязательна (там задаются Min и Max).

Пример 1. Состояние кнопки по ответу: «Звук в зале»

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

Состояние

Что значит

default

обычное, ненажатое

selected

выбрана (в группе Interlock так подсвечена выбранная кнопка)

pressed

нажата

disabled

отключена: серая клавиша и серый текст, касания игнорируются

Здесь кнопка — переключатель: нажата (pressed, зелёная, «Звук включён») или обычная (default, «Звук выключен»).

1. Что пришло от устройства

Процессор присылает уведомление при каждой смене состояния звука. В отладке (узел debug «Что прислал процессор», включите его кнопкой справа от узла) вы увидите сообщение с такими полями в payload:

{"publishToken": "fpHallMute", "value": true, "valueType": "bool", "type": "subscribeNotification"}
../../_images/s5-debug-sound.png

Отладка «Что прислал процессор»: 1 — ответ о звуке (publishToken fpHallMute, value), 2 — ответ об уровне (fpHallLevel, value в дБ)

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

  • value — новое состояние: true — звук выключен (mute включён), false — звук включён.

  • valueType — тип значения (bool — «да или нет», number — число).

  • type — тип ответа; по нему device - response - listener направил сообщение на нужный выход.

2. Разбор

Ответ проходит два узла.

switch «Какая подписка?». Процессор присылает по одной линии и звук, и громкость, поэтому сначала их надо разделить. Свойство — msg.payload.publishToken, два правила «==» (строка): fpHallMute и fpHallLevel. Сообщение с fpHallMute выходит из первого выхода — в наш пример, с fpHallLevel — из второго (пример 2).

../../_images/s5-switch-subscription.png

Окно узла switch «Какая подписка?»: 1 — свойство msg.payload.publishToken, 2 — правило «== fpHallMute», 3 — правило «== fpHallLevel»

change «Mute → вид кнопки». Одно правило «Установить»: msg.topic, тип значения «выражение» (в окне он отмечен значком J: перед полем; слово «выражение» видно только в меню выбора типа), выражение

{"Fp_Mute_b": payload.value ? "default" : "pressed"}
../../_images/s5-change-mute.png

Окно узла change «Mute → вид кнопки»: 1 — «Установить» msg.topic, 2 — тип «выражение», 3 — выражение

По частям:

  • { … } — фигурные скобки собирают объект «имя: значение», ровно ту запись, которую ждёт api - helper - 2.

  • "Fp_Mute_b": — имя кнопки. Оно должно совпадать с именем в UI-Designer до буквы.

  • payload.value ? "default" : "pressed" — «если payload.value истинно, взять "default", иначе "pressed"». Это условие в форме «вопрос ? если да : если нет».

Итого: value: true (звук выключен) → {"Fp_Mute_b": "default"}, value: false (звук включён) → {"Fp_Mute_b": "pressed"}. Результат лежит в msg.topic; в отладке «весь msg» вы увидите его рядом с прежним payload.

3. Окно api - helper - 2 «Кнопка: звук зала»

  1. «Интерфейс (UI)» — «Первая программа».

  2. «Тип сообщения» — uiState.

  3. «Источник входных данных» — msg.topic (по умолчанию).

  4. В «Объекты управления» — карточка: «Элемент:» Fp_Mute_b, «Событие:» «Состояние кнопки» (группа «Состояние»), «Значение:» default.

../../_images/s5-helper-mute.png

Карточка узла api - helper - 2 «Кнопка: звук зала»: 1 — «Элемент:» Fp_Mute_b, 2 — «Событие:» «Состояние кнопки», 3 — «Значение:» (запасное)

Выбрано, что менять: состояние кнопки Fp_Mute_b. А каким оно станет, сказало сообщение: pressed или default.

Предупреждение

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

4. Что на панели

Выход api - helper - 2 подключён к UI - Panel out. После ответа процессора кнопка «Звук» меняет вид: при value: false — зелёная «Звук включён», при value: true — обычная «Звук выключен». Если звук выключили другой панелью или с пульта процессора, кнопка поменяется так же, потому что вид ведёт не нажатие, а ответ.

Если у кнопки уже то же состояние, панели ничего не уходит, и вы ничего не увидите.

Как оператор меняет звук с панели — в блоке H «Первой программы»: кнопка — переключатель, программа получает value_changed и отправляет команду setMute, как в блоках E и F.

Пример 2. Ползунок и надпись: значение из ответа

Зачем. Громкость задают двигая ручку, но показывать надо то, что выставил процессор: ручка встаёт по ответу, а рядом надпись -20 дБ. А если ручку отпустил оператор, процессору нужна не доля 0…1, а децибелы. Здесь два направления, и в каждом работает узел change.

1. Что пришло от устройства

Тот же канал отладки, второй вид ответа (на снимке в примере 1 это сообщение под номером 2):

{"publishToken": "fpHallLevel", "value": -20, "valueType": "number", "type": "subscribeNotification"}

value — уровень в децибелах (дБ). Диапазон этого канала — от −60 до 0 дБ.

2. Разбор

Сообщение выходит из второго выхода switch «Какая подписка?» (правило «== fpHallLevel») и попадает в change «Уровень → ползунок и надпись»: одно правило, msg.topic, тип «выражение»:

{"Fp_Vol_sl": payload.value, "Fp_Vol_val_lb": $round(payload.value) & " дБ"}
../../_images/s5-change-level.png

Окно узла change «Уровень → ползунок и надпись»: 1 — «Установить» msg.topic с типом «выражение», 2 — выражение

По частям:

  • "Fp_Vol_sl": payload.value — ползунку отдаём уровень как есть, в дБ: -20. Пересчитывать в положение ручки не нужно — это сделает api - helper - 2 (ниже).

  • "Fp_Vol_val_lb": — надпись рядом с ползунком.

  • $round(payload.value) — функция JSONata $round округляет число до целого: уровень, пришедший как -19.6, станет -20.

  • & " дБ" — знак & склеивает строки (число перед этим превращается в строку). Получается "-20 дБ".

Один change готовит данные сразу для двух элементов: в msg.topic лежит {"Fp_Vol_sl": -20, "Fp_Vol_val_lb": "-20 дБ"}.

3. Окно api - helper - 2 «Громкость: ползунок и надпись»

В узле две карточки.

Карточка ползунка. «Элемент:» Fp_Vol_sl, «Событие:» «Числовое значение» (группа «Специальные»). Для этого события в карточке три поля:

  • Min — -60: какое число соответствует нижнему краю ползунка;

  • Max — 0: какое число соответствует верхнему краю;

  • Steps — 60: на сколько шагов разбита шкала ползунка.

У пустых полей видны подсказки «Min (вход) — обязательно», «Max (вход) — обязательно» и «Steps (>=1, по умолчанию 100)»; когда вы впишете числа, подсказки пропадают, поэтому на снимке ниже видны только значения.

Поля Min и Max нужны: по ним узел пересчитывает число в положение ручки. Если поля оставить пустыми, при «Готово» окно узла впишет Min 0, Max 1 и Steps 100; тогда ползунок понимает только числа от 0 до 1. Если Min и Max равны, узел ползунок пропускает: ручка не двигается, а остальные элементы этого сообщения обновляются.

Карточка надписи. «Элемент:» Fp_Vol_val_lb, «Событие:» «Текст».

../../_images/s5-helper-slider.png

Карточки узла api - helper - 2 «Громкость: ползунок и надпись»: 1 — ползунок Fp_Vol_sl, «Числовое значение», 2 — поле Min (−60), 3 — поле Max (0), 4 — надпись Fp_Vol_val_lb, «Текст»

Зачем Min и Max и как узел пересчитывает. Панель принимает положение ручки как долю от 0 до 1: 0 — левый край, 1 — правый. Процессор же говорит в децибелах. Узел переводит одно в другое по вашим Min и Max:

положение = (значение − Min) / (Max − Min)

Для −20 дБ: (−20 − (−60)) / (0 − (−60)) = 40 / 60 ≈ 0,67 — ручка на две трети шкалы. Для −60 дБ получится 0 (край слева), для 0 дБ — 1 (край справа). Значение за пределами диапазона узел обрежет по краю. Steps округляет положение до ближайшего деления шкалы: при 60 шагах на диапазон в 60 дБ ручка стоит ровно на целом децибеле. Min и Max возьмите из свойств канала в самом устройстве (для процессора — его диапазон громкости), а не «на глаз»: иначе ручка и число разойдутся.

4. Что на панели

На странице «Устройства» ручка встаёт на две трети шкалы, надпись «Громкость:» показывает -20 дБ. Меняйте уровень любым другим способом: ручка и надпись последуют за ответом процессора.

Обратное направление: ручку отпустили → команда процессору

Оператор двигает ручку. Событие slide_end приходит, когда ручку отпустили; значение в нём — доля 0…1, как раз та, что панель использует внутри. Процессору нужен уровень в дБ, поэтому между ui - switch и device - command стоит ещё один change.

ui - switch. Правило на Fp_Vol_sl с событием slide_end, свой выход. У самого ползунка в UI-Designer поле «Отправлять команды после» стоит в положении «Отпускание» («Элементы интерфейса»). Событие slide_end выбрано не случайно: оно приходит один раз, когда ручку отпустили, и процессор получит одну команду, а не поток команд по ходу движения.

В отладке за ui - switch — сообщение с полями:

{"object": "Fp_Vol_sl", "event": "slide_end", "value": 0.25}

(0.25 — для примера: у вас будет то место, где оператор отпустил ручку.)

change «Громкость: 0…1 → дБ». Два правила «Установить»:

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

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

{"instanceTag": "DemoLevels", "channel": 1, "level": $round(payload.value * 60 - 60)}
../../_images/s5-change-vol-db.png

Окно узла change «Громкость: 0…1 → дБ» (правило 1 — msg.command = строка setLevel): 1 — правило 2, msg.parameters с типом «выражение», 2 — выражение этого правила

  • instanceTag и channel — адрес канала в процессоре; они остаются без изменений.

  • payload.value * 60 - 60 — обратный пересчёт: доля 0…1 умножается на ширину диапазона (60 дБ) и сдвигается на нижний край (−60). Доля 0,25 → −45 дБ, 0,9 → −6 дБ.

  • $round(…) — округляет до целых децибел.

Это ровно та же формула, что делает api - helper - 2 по Min и Max, но в обратную сторону. Min и Max в карточке и числа 60 и −60 в этом выражении — один и тот же диапазон. Если вы смените диапазон в одном месте, поменяйте и в другом.

Выход change подключён к device - command «Звук: команда». После команды процессор присылает уведомление fpHallLevel, и ручка с надписью встают по факту: по тому уровню, который выставил процессор.

../../_images/s5-debug-slider.png

Отладка: 1 — событие slide_end за ui - switch (value — доля 0…1), 2 — msg.parameters за change «Громкость: 0…1 → дБ» (level — уровень в дБ)

Пример 3. Текст и цвет фона по состоянию звонка

Зачем. Показать оператору слова и цвета: «Идёт разговор» на зелёной плашке, «Нет звонка» — на белой. Текст и цвет приходят не из карточки, а из ответа кодека. Два свойства одного элемента меняются одним сообщением.

1. Что пришло от устройства

Кодек сам о звонке не сообщает, поэтому программа раз в 3 секунды спрашивает его командой getCallInfo (узел inject «Раз в 3 с» → device - command «Кодек: состояние звонка»). Ответ приходит в device - response - listener «Кодек: ответы», тип callInfo, на третий выход. В отладке (debug «Что прислал кодек») — два вида сообщения. Звонка нет:

{"inCall": false, "info": null, "value": false, "type": "callInfo"}

Идёт разговор:

{"inCall": true, "info": {"abonents": [{"name": "Филиал «Север»", "number": "2001"}], "time": 1}, "value": true, "type": "callInfo"}
../../_images/s5-debug-codec.png

Отладка «Что прислал кодек»: 1 — callInfo, звонка нет (inCall: false), 2 — callInfo, идёт разговор (inCall: true, в info — абонент), 3 — ответ contacts (массив list)

Нам нужно одно поле, inCall: true или false. Остальное (имя абонента, время) на этой странице не используется.

2. Разбор

Ответ сразу идёт в change «Звонок → текст и цвет»: проверять нечего, это единственный ответ на этом выходе. Правило «Установить» msg.topic, тип «выражение»:

{"Fp_Call_val_lb": {"text": payload.inCall ? "Идёт разговор" : "Нет звонка", "backgroundColor": payload.inCall ? "#DCFCE7" : "#FFFFFF"}}
../../_images/s5-change-call.png

Окно узла change «Звонок → текст и цвет»: 1 — выражение (правило «Установить» msg.topic, тип «выражение»)

По частям:

  • "Fp_Call_val_lb": — имя надписи-плашки.

  • Внутри — вложенный объект: "text": … и "backgroundColor": …. Имена text и backgroundColor — это те же события «Текст» и «Цвет фона» в виде, который ждёт узел. С такой «полной записью» можно изменить сразу несколько свойств одного элемента.

  • payload.inCall ? "Идёт разговор" : "Нет звонка" — условие, как в примере 1: если разговор идёт — одна строка, иначе другая.

  • payload.inCall ? "#DCFCE7" : "#FFFFFF" — то же условие для цвета: светло-зелёный #DCFCE7 при разговоре, белый #FFFFFF без него. Цвет записывают как #DCFCE7 или RGB(220,252,231) — работают обе записи.

Результат в msg.topic при разговоре: {"Fp_Call_val_lb": {"text": "Идёт разговор", "backgroundColor": "#DCFCE7"}}.

3. Окно api - helper - 2 «Надпись: звонок»

Две карточки на один элемент Fp_Call_val_lb: событие «Текст» (группа «Текст и цвет») и событие «Цвет фона» (та же группа). Значения в карточках — запасные («Нет звонка» и #FFFFFF); данные из сообщения главнее.

../../_images/s5-helper-call.png

Карточки узла api - helper - 2 «Надпись: звонок»: 1 — «Элемент:» Fp_Call_val_lb, «Событие:» «Текст», 2 — тот же элемент, «Событие:» «Цвет фона»

Если нужен цвет самого текста — событие «Цвет текста» (в полной записи — fontColor).

4. Что на панели

Плашка «Нет звонка» на белом фоне после звонка превращается в «Идёт разговор» на светло-зелёном; после завершения возвращается. Если сообщение придёт с тем же состоянием, панели ничего не уходит.

../../_images/s5-panel-incall.png

Страница «Устройства» во время звонка: 1 — «Идёт разговор» на светло-зелёном фоне, 2 — надпись «Кому звоним» с именем контакта, 3 — список контактов

Видимость

Событие «Видимость» (группа «Состояние»): значение true — скрыть, false — показать. Свойство элемента так и называется: hidden, поэтому true значит «скрыто». Данные: {"Fp_Call_val_lb": true}.

Пример 4. Список из ответа кодека и выбор строки

Зачем. Список (в UI-Designer — «ListBox») показывает перечень, из которого оператор выбирает строку: контакты, историю звонков, рецепты. Содержимое списка — это то, что прислало устройство.

1. Что пришло от устройства

Оператор нажал «Контакты» — программа послала кодеку команду getContacts. Ответ приходит в тот же device - response - listener «Кодек: ответы», тип contacts, на второй выход (на снимке отладки в примере 3 это сообщение под номером 3):

{"list": [{"id": 1, "name": "Переговорная 2", "number": "1002", "status": null, "type": "SIP"}, {"id": 2, "name": "Филиал «Север»", "number": "2001", "status": null, "type": "SIP"}, {"id": 3, "name": "Учебный класс", "number": "1003", "status": null, "type": "H323"}, {"id": 4, "name": "Дежурная служба", "number": "1100", "status": null, "type": "SIP"}], "value": 4, "type": "contacts"}

В list — массив контактов; у каждого есть name (имя) и number (номер), а остальное нам не нужно.

2. Разбор

Строка списка на панели — объект из одной пары «имя → значение», например {"Переговорная 2": "1002"}. Ключ — это то, что видит оператор, и то, что вернётся в программу при выборе строки. Весь список — массив таких пар. Значит, из ответа нужно сделать [{"Переговорная 2": "1002"}, {"Филиал «Север»": "2001"}, …].

change «Контакты → строки списка» делает это двумя правилами «Установить», обоим тип «выражение»:

  1. msg.topic:

    {"Fp_Contacts_ls": [payload.list.{name: number}]}
    
  2. global. fpContacts:

    payload.list{name: number}
    
../../_images/s5-change-contacts.png

Окно узла change «Контакты → строки списка»: 1 — правило msg.topic (выражение), 2 — правило global.fpContacts (выражение)

Первое выражение:

  • payload.list — массив контактов из ответа;

  • .{name: number} — для каждого контакта собрать объект «его имя: его номер». Получается по одной паре на контакт;

  • [ … ] — квадратные скобки гарантируют, что результат будет массивом, даже если контакт один;

  • {"Fp_Contacts_ls": … } — имя списка в интерфейсе.

Результат:

{"Fp_Contacts_ls": [{"Переговорная 2": "1002"}, {"Филиал «Север»": "2001"}, {"Учебный класс": "1003"}, {"Дежурная служба": "1100"}]}

Второе выражение, payload.list{name: number} (фигурные скобки сразу после list, без точки), собирает один объект со всеми парами сразу: {"Переговорная 2": "1002", "Филиал «Север»": "2001", …}. Его правило записывает в память программы (global.fpContacts): когда оператор выберет строку, программа достанет по имени номер для звонка. Память, ключи и подстановка — как в блоках A и C «Первой программы».

3. Окно api - helper - 2 «Список контактов»

Карточка: «Элемент:» Fp_Contacts_ls, «Событие:» «Список (JSON)» (группа «Списки»). В «Значении:» — JSON [] (пустой список, запасное значение).

../../_images/s5-helper-list.png

Карточка узла api - helper - 2 «Список контактов»: 1 — «Элемент:» Fp_Contacts_ls, «Событие:» «Список (JSON)», «Значение:» — запасной пустой список

Каждое сообщение «Список (JSON)» заменяет весь список. Если вместо массива пришла строка с JSON, узел её разберёт; если пришло то, что нельзя разобрать как массив, список станет пустым.

4. Что на панели

Нажали «Контакты» — в списке появляются строки: имя слева, номер справа. Список и надпись «Кому звоним» видны на снимке страницы в примере 3 (пункты 3 и 2).

Выбор строки → надпись

Когда оператор выбирает строку, UI - Panel in получает событие item_selected, а в value — ключ выбранной строки (имя контакта — не номер и не позиция):

{"UI": {"Первая программа": {"object": "Fp_Contacts_ls", "event": "item_selected", "value": "Филиал «Север»"}}}
../../_images/debug-events.png

Боковая панель отладки: 1 — push кнопки, 2 — release той же кнопки, 3 — item_selected списка, в value — ключ выбранной строки

В ui - switch для этого — правило на Fp_Contacts_ls с событием item_selected (см. «Приём событий»). За ним — change «Строка → кому звоним» с двумя правилами «Установить»:

  • global. fpNumber — значение типа global. с ключом fpContacts[msg.payload.value]: из таблицы «имя → номер» берётся номер выбранного контакта и запоминается (кнопка «Позвонить» возьмёт его оттуда);

  • msg.topic — «выражение» {"Fp_Target_val_lb": payload.value}: в надпись «Кому звоним» попадает имя контакта.

Дальше — api - helper - 2 «Надпись: кому звоним»: карточка Fp_Target_val_lb, событие «Текст».

../../_images/s5-change-select.png

Окно узла change «Строка → кому звоним»: 1 — правило global.fpNumber со значением fpContacts[msg.payload.value], 2 — правило msg.topic (выражение)

Выбрать строку из программы нельзя. В api - helper - 2 нет события «выбрать строку», и панель выбранную строку не подсвечивает. Поэтому показывайте выбор отдельной надписью, как здесь.

Другие действия со списком

Заполнение целиком — самое частое действие, но не единственное. Для остальных нужны другие события в карточке. Если один элемент нужно менять разными действиями, делайте для каждого отдельный узел api - helper - 2 с одной карточкой этого элемента: данные не спутаются.

Что нужно

«Событие:»

Что подать

заполнить заново

«Список (JSON)»

{"Fp_Contacts_ls": [{"Переговорная 2": "1002"}, …]}

очистить

«Список (JSON)»

{"Fp_Contacts_ls": []}

добавить или заменить одну строку

«Элемент списка» (в карточке поля «key» и «value»)

{"Fp_Contacts_ls": {"Новый контакт": "1005"}} — есть такой ключ, значение заменится, нет — строка добавится; скаляр "1005" берёт ключ из поля «key» карточки

удалить строку

«Удалить из списка» (поле «Item Key»)

{"Fp_Contacts_ls": "Новый контакт"} — ключ удаляемой строки

../../_images/view-list-item-window.png

Карточка строки списка: 1 — «Элемент:», 2 — «Событие:» «Элемент списка», 3 — поля «key» и «value»

../../_images/view-list-remove-window.png

Карточка удаления строки: 1 — «Элемент:», 2 — «Событие:» «Удалить из списка», 3 — поле «Item Key»

Ключи в одном списке должны быть разными: строка с уже существующим ключом заменит прежнюю.

Как проверить без устройства

Когда устройства под рукой нет или нужно проверить одну карточку, значение можно подать вручную: не из ответа, а из узла inject. Самая короткая цепочка — три узла: inject → api - helper - 2 → UI - Panel out.

  1. Перетащите на рабочую область inject, api - helper - 2 и UI - Panel out и соедините по порядку. Если UI - Panel out на вкладке уже есть, берите его.

  2. Откройте inject. Для msg.payload выберите тип «JSON» и впишите данные: например, {"Fp_Mute_b": "pressed"}.

  3. Откройте api - helper - 2. Выберите интерфейс, «Тип сообщения» — uiState, в «Источнике входных данных» вместо topic впишите payload, чтобы узел читал данные из msg.payload.

  4. Добавьте карточку элемента (как в примерах выше).

  5. Нажмите «Развернуть», затем кнопку слева от узла inject.

../../_images/view-test-chain.png

Проверочная цепочка: 1 — inject с данными, 2 — api - helper - 2, 3 — UI - Panel out

Что вы увидите: на открытой панели элемент сразу меняется. Не меняется — раздел «Частые ошибки» в конце страницы.

Вручную проверяется только окно api - helper - 2. Чтобы проверить весь путь «ответ → разбор → панель», проверьте по очереди: приходит ли ответ в отладку (debug за device - response - listener), что лежит в msg.topic после change и что показывает панель.

Что можно изменить: коротко

События, которые вы выбираете в карточке («Событие:»). Значение подаётся в {"имя элемента": значение}.

Что менять

«Событие:»

Значение в данных

вид кнопки

«Состояние кнопки»

default, selected, pressed, disabled

текст (надпись или подпись кнопки)

«Текст»

строка

цвет текста, цвет фона

«Цвет текста», «Цвет фона»

#DCFCE7 или RGB(220,252,231)

показать или скрыть

«Видимость»

true — скрыть, false — показать

положение ползунка

«Числовое значение»

число из диапазона Min…Max карточки

строки списка

«Список (JSON)», «Элемент списка», «Удалить из списка»

см. таблицу выше

оверлей во фрейме

«Overlay»

см. ниже

положение и размер элемента

«X», «Y», «Ширина», «Высота» (группа «Позиция (пиксели)») или «X %», «Y %», «Ширина %», «Высота %» (группа «Позиция (проценты)»)

число: в пикселях — от левого верхнего угла страницы или оверлея, на котором лежит элемент; у «%» — доля от 0 до 1 от размера страницы или оверлея (0,5 — половина), а не проценты. Для одного элемента берите одну группу — пиксели или «%»

адрес и перезагрузка веб-элемента (Web URL)

«URL (WebView)», «Перезагрузка (WebView)»

адрес строкой; true или false

вид кнопки по состояниям

«Расширенная кнопка»

см. ниже

Особенности кнопки:

  • Кнопка в группе Interlock. Если группа задана в UI-Designer и кнопке пришло selected, остальные кнопки группы сами уходят в default. Состояние default снимает выбор только с этой кнопки. Подробнее про группы — «Переключатели и группы».

  • Состояние disabled. Кнопка выглядит неактивной (серая клавиша и серый текст) и не реагирует на касания: программа не получает от неё ни одного события, пока ей не вернут другое состояние.

  • Тип обратной связи кнопки. Если в UI-Designer у кнопки «Тип обратной связи» — «Momentary Passed» или «None», она игнорирует команды программы (см. «Кнопки и состояния»).

Расширенная кнопка

Событие «Состояние кнопки» переключает кнопку между готовыми видами. Когда нужно изменить сам вид какого-то состояния (например, подпись и цвет в состоянии selected), выберите событие «Расширенная кнопка» (группа «Специальные»). Оно работает для кнопок типа «расширенная кнопка»; в списке элементов узла они собраны в группе «Расширенные кнопки». В карточке у него вместо одного поля три: какое свойство менять (backgroundColor, text, fontColor, icon, iconColor), для какого состояния (defaultState, selectedState, pressedState, disabledState) и значение. Если нужно изменить несколько свойств или состояний, добавьте карточку на каждое.

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

{"Fp_Mute_b": {"defaultState": {"text": "ВЫКЛ", "backgroundColor": "#333"}, "selectedState": {"text": "ВКЛ", "fontColor": "#fff"}}}

Переход на другую страницу из программы

Зачем. Обычно страницы переключает оператор — кнопкой-ссылкой, которую вы настроили в UI-Designer. Но бывает, что страницу должна открыть программа: например, показать страницу с подсказкой или с ошибкой по событию устройства. Для этого служит тип «Тип сообщения» → navigation.

Ограничение: переход работает только со страницами интерфейса и только по имени страницы. Оверлей во фрейме открывается другим способом — см. ниже «Сменить оверлей во фрейме» (про фреймы — «Фреймы»).

Шаги

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

  2. В U-Logic перетащите api - helper - 2 и соедините с ним узел, который запускает переход (например, ui - switch на кнопку «Открыть подсказку», событие push).

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

  4. Нажмите «Готово». Подключите выход узла к UI - Panel out и нажмите «Развернуть».

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

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

Что вы увидите: когда сообщение дошло до узла, на панели вместо текущей страницы открывается «Подсказка». Входные данные для этого типа не нужны: узел использует страницу из настроек узла, а запускает его любое сообщение; только не кладите объект в msg.topic (см. «Частые ошибки»). Пример целиком — в «Первой программе», блок G.

Сменить оверлей во фрейме

Зачем. Фрейм в интерфейсе показывает оверлей-страницу поверх основной. Обычно оверлей показывает оператор, но программа тоже может его сменить или убрать: например, показать окно с предупреждением по событию устройства.

Здесь работает не navigation, а тот же тип «Тип сообщения» — uiState, что и для кнопок и надписей: оверлей задаётся событием «Overlay» (в списке событий — группа «Специальные»).

Шаги

  1. В UI-Designer должны быть фрейм и оверлей-страница, которую он показывает; интерфейс сохранён и опубликован.

  2. В U-Logic перетащите api - helper - 2 и соедините с ним узел, который запускает смену (например, ui - switch на кнопку, событие push).

  3. Откройте api - helper - 2. В «Интерфейс (UI)» выберите интерфейс, «Тип сообщения» — uiState. Добавьте элемент в «Объекты управления» и задайте: в «Элемент:» выберите фрейм, в «Событие:» — «Overlay».

  4. В поле «Значение:» выберите оверлей, который нужно показать, либо пункт снятия оверлея — тогда оверлей скроется. В новой карточке этот пункт называется «Скрыть оверлей» и выбран по умолчанию; когда вы откроете окно снова, у сохранённой карточки он будет подписан «Убрать оверлей»; это одно и то же. Нажмите «Готово».

  5. Подключите выход узла к UI - Panel out и нажмите «Развернуть».

../../_images/view-overlay-window.png

Окно узла api - helper - 2: 1 — «Событие:» Overlay, 2 — «Элемент:» — фрейм, 3 — «Значение:» — оверлей

Что вы увидите. Когда сообщение дошло до узла, во фрейме на панели появляется выбранный оверлей; со значением «Убрать оверлей» он скрывается.

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

  • Ничего не меняется на панели. Проверьте по порядку: выход api - helper - 2 подключён к UI - Panel out; выбран интерфейс; «Источник входных данных» указывает на то поле, куда change положил данные (msg.topic по умолчанию); имя элемента в данных совпадает с именем в UI-Designer до буквы; интерфейс сохранён и опубликован. Включите debug («весь msg») за change и посмотрите, что лежит в msg.topic.

  • В msg.topic пусто или undefined. Выражение в change не нашло поле в ответе: сверьте имена полей (payload.value, payload.inCall, payload.list) с тем, что пришло в отладке.

  • Узел игнорирует ключ. В короткой записи ({"Fp_Mute_b": "pressed"}) узел берёт только элементы из своего списка «Объекты управления». Для элемента без карточки подайте полную запись {"Fp_Mute_b": {"uiState": "pressed"}}.

  • Кнопка сама возвращается в default. В списке узла осталась карточка этой кнопки со значением default: оно уходит с каждым сообщением, которое кнопку не упоминает. Уберите карточку или замените её нужным значением.

  • Ползунок не двигается. Если Min и Max в карточке «Числовое значение» равны, узел пропускает ползунок, а остальное из сообщения уходит. С включённым в узле флажком «Debug» в отладке видно предупреждение «некорректные min/max» с именем ползунка. Если Min и Max не заполняли, узел подставил 0 и 1: значения вне этого диапазона ставят ручку в край. Значение должно быть числом.

  • Ручка стоит не там. Min и Max в карточке не совпадают с диапазоном устройства: узел считает долю по вашим числам.

  • Ползунок отправляет команды сыпью. В правиле ui - switch выбрано value_changed вместо slide_end.

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

  • Список заменился, а нужно было добавить. «Список (JSON)» заменяет весь список; для одной строки берите «Элемент списка».

  • Строки списка выглядят не так, как ждали. Строка — объект из одной пары «ключ → значение». Запись [{"key": "A", "value": "1"}] панель покажет как одну строку с ключом key.

  • Градиент в цвете списка. Панель не разбирает градиент в цвете строк и показывает ошибку вместо вкладки; задайте в UI-Designer сплошной цвет («Элементы интерфейса»).