8.1.3.10. Виджет воспроизведения
Виджет воспроизведения появляется только в режиме воспроизведения (рис. 8.31). Виджет позволяет выбрать другой файл для воспроизведения без необходимости останавливать всю систему. В правом верхнем углу виджета есть кнопка, позволяющая выбрать любой файл для воспроизведения записей с плат OpenBCI (.txt или .csv). Выбор других типов файлов может вызвать ошибку программы.
Рис. 8.31. Виджет воспроизведения
8.1.3.11. Виджет пульса
Данный виджет будет отображаться только для плат Cyton в режиме Live. Виджет использует режим аналогового чтения для получения данных с вывода, отмеченного как D11 на плате Cyton. Используя полученные данные, виджет рассчитает количество ударов в минуту (BPM) и интервал между ударами (IBI). При использовании виджета «Сеть» расчитанные данные BPM и IBI можно отправлять в стороннее программное обеспечение.
8.1.3.12. Пользовательский виджет
Далее рассмотрим, как создавать собственные виджеты для графического интерфейса OpenBCI. Всего за несколько простых шагов можно добавить свои собственные мини-приложения в выпадающее меню графического интерфейса OpenBCI для запуска специализированных приложений в режиме реального времени. Эти виджеты могут напрямую подключаться к основной структуре данных графического интерфейса OpenBCI, поэтому можно экспериментировать с волнами ЭЭГ и другими биоданными прямо в графическом интерфейсе.
Для начала необходимо сделать дублирование и переименование файла
“W_template.pde”.
200
Файл “W_template.pde” предназначен для использования в качестве простой отправной точки для создания собственных виджетов. Данный файл необходимо скопировать и переименовать так, как будет называться новый виджет (например, “W_myNewWidget.pde”). Чтобы переименовать класс, необходимо убедиться, что он является уникальным и не совпадает с классом W_template.pde. Первая некомментированная строка файла должна выглядеть так:
class W_myNewWidget extends Widget {
Затем надо добавить новый виджет в файл “widgetManager.pde”. Перед добавлением любого необычного кода, который захватывает локальные структуры данных, надо убедиться, что новый виджет добавлен в файл “widgetManager.pde”. Сделав это, необходимо убедиться, что новый виджет добавлен в выпадающее меню каждого виджета. Это желательно делать для того, чтобы сразу смотреть, как добавляется график в виджет и продолжается его строение.
Далее надо следовать протоколу создания нового виджета в глобальных переменных:
// MAKE YOUR WIDGET GLOBALLY
W_timeSeries w_timeSeries;
W_fft w_fft;
W_networking w_networking;
W_BandPower w_bandPower;
W_accelerometer w_accelerometer;
W_ganglionImpedance w_ganglionImpedance;
W_headPlot w_headPlot;
W_template w_template1;
W_emg w_emg;
W_openBionics w_openbionics;
W_Focus w_focus;
W_PulseSensor w_pulsesensor;
W_myNewWidget w_myNewWidget;
Затем надо убедиться, что виджет настроен, добавлен в массив всех виджетов:
w_networking = new W_networking(_this); w_networking.setTitle("Networking"); addWidget(w_networking, w);
w_myNewWidget = new W_myNewWidget(_this); w_myNewWidget.setTitle("My New Widget"); addWidget(w_myNewWidget, w);
w_bandPower = new W_BandPower(_this); w_bandPower.setTitle("Band Power"); addWidget(w_bandPower, w);
201
Следующий шаг заключается в добавлении выпадающих списков, чтобы контролировать виджет, если это необходимо. Но прежде чем скомпилировать графический интерфейс, нужно переименовать глобальные функции внизу файла “W_myNewWidget.pde”, для того чтобы они отличались от глобальных функций внизу в файле “W_template.pde”. Во время работы графического интерфейса эти функции автоматически активируются, когда в навигационной панели виджета активируется соответствующий раскрывающийся список. Необходимо убедиться, что строка (например, «Dropdown1») в функции “addDropdown” конструктора - это та же строка, что и имя соответствующей глобальной функции в нижней части файла “.pde”. Индекс выбранного пункта меню - это то, что передается в функцию (т. е. «Int n»):
//These functions need to be global! These functions are activated when an item from the corresponding dropdown is selected
void Dropdown1(int n){
println("Item " + (n+1) + " selected from Dropdown 1"); if(n==0){
//do this
} else if(n==1){ //do this instead
}
closeAllDropdowns(); // do this at the end of all widget-activated functions to ensure proper widget interactivity ... we want to make sure a click makes the menu close
}
void Dropdown2(int n){
println("Item " + (n+1) + " selected from Dropdown 2"); closeAllDropdowns();
}
void Dropdown3(int n){
println("Item " + (n+1) + " selected from Dropdown 3"); closeAllDropdowns();
}
Далее можно запустить графический интерфейс и убедиться, что новый виджет виден в выпадающем меню виджетов в верхнем левом углу всех виджетов. Если его нет, то надо убедиться, что вышеуказанные шаги выполнены правильно.
После вышеуказанных шагов надо разобраться, как уже существующие виджеты обращаются к структурам данных, внутренним по отношению к программе GUI.
Каждый виджет имеет переменные x, y, w, h, которые соответствуют левому краю виджета (горизонтальное расположение пикселей), верхнему краю виджета (вертикальное расположение пикселей), ширине пикселей и высоте пикселей соответственно. Эти переменные обновляются автоматически при изменении размера экрана или при циклическом переключении виджетов через контейнеры макета. При добавлении графики в виджет все действия основываются на этих переменных.
202
Помимо настройки виджета в файле “widgetManager.pde”, весь код может находиться внутри файла “W_myNewWidget.pde” (если все сделано правильно).
Чтобы узнать, как получить доступ к глобальным структурам данных, таким как данные FFT (БПФ) и данные временных рядов, надо посмотреть, как другие виджеты обращаются к данным функциях update().
Одним из способов добавления в виджеты таких функций, как выпадающие списки и переключатели, является библиотека “ControlP5”. Чтобы узнать больше о том, как работают виджеты, надо изучить файл widget.pde.
Следующий шаг в разработке виджета это добавление любых выпадающих настроек в “SoftwareSettings.pde”. Начиная с версии v4 программы GUI, приложение пытается автоматически сохранить и автоматически загрузить настройки пользователя. Если этого не будет происходить, то пользователям придется каждый раз переконфигурировать графический интерфейс. Поэтому, если используются раскрывающиеся списки в новом виджете, то нужно включить эти настройки в файле “SoftwareSettings.pde” в
функциях save () и load ().
Вверху файла “SoftwareSettings.pde” есть несколько подробностей, которые могут быть полезны. Необходимо придерживаться стиля, который уже используется для сохранения и загрузки данных из “JSON”. Пролистав код в функциях save () или load (), можно найти настройки некоторых известных виджетов. Эти настройки и надо использовать в качестве примера.
После выполнения вышеуказанных шагов можно приступить к начальному тестированию. Если при отладке добавить какую-либо функцию println() или verbosePrint() в разработанный виджет, то можно будет использовать функцию “Console Log”, чтобы увидеть эти сообщения или просто посмотреть на консоль в IDE. Размеры экрана и некоторые объекты нужно будет изменить или повторно позиционировать при смене размера экрана графического интерфейса. Вот почему класс виджетов имеет
“screenResized()” метод. Как правило, объекты x, y, width и height объявляются при создании экземпляра виджета и затем снова при использовании “screenResized()” при относительном позиционировании.
После настроек необходимо проверить, что все работает. Настройки можно увидеть в файле настроек JSON для каждого режима данных в папке
“Documents/OpenBCI_GUI/Settings/”. Файлы JSON можно просматривать как текст. Необходимо убедиться, что новый объект JSON и данные записываются в файл. Наконец, надо убедиться, что настройки работают правильно при настройке виджета, сохранении и загрузке.
Одним из наиболее распространенных способов сбоя графического интерфейса, связанных с работой нового виджета, является рисование “null” данных или появление других ошибок, связанных с функцией draw(). Еще одна распространенная ошибка – это отображение “Exception” на консоли “Atom”, “VSCode” или “Processing IDE”.
203
8.1.4. Программа OpenBCI Hub
Программа OpenBCI Hub это промежуточное программное обеспечение, используемое для организации связи с платами OpenBCI через протокол команд TCP / IP. Версия v2.0.0 программы OpenBCI Hub выпущена в сентябре 2018 года, версия v1.0.0 выпущена 3 января 2017 года.
OpenBCI Electron Hub (или просто «Hub») это сервер TCP / IP, который прослушивает клиентов через порт 10996 на localhost по адресу 127.0.0.1. Broadcast/multicast никогда не используется для передачи информации клиентам. Исходящие данные передаются только запрашивающему клиенту. В программе используется семантическое управление версиями, протокол всегда указывается в первую очередь в любой документации. Необходимо следовать правилам семантического управления версиями, чтобы не нарушать изменения.
Концентратор может использоваться в двух режимах работы:
Собственно, приложение, такое как приложение обработки данных OpenBCI, запускает и останавливает концентратор.
Концентратор работает непрерывно, и сторонние приложения могут просто подключаться как клиенты, использовать платы OpenBCI для приема данных, отключаться и продолжать работу, не закрывая при этом концентратор.
Уникальный порт 10996 критически важен, потому что приложения должны иметь возможность подключаться к известному номеру порта на локальном компьютере. Спецификация по использованию порта доступна на ресурсе github.com (https://github.com/OpenBCI/OpenBCI_Hub).
Начиная с версии 2.0.0, концентратор использует формат JSON для передачи и получения сообщений по протоколу TCP. Каждая строка JSON должна заканчиваться символом “\n”. Необходимо выполнить синтаксический анализ по наличию «\n», а затем зачистить начало строк до символов \n и использовать те языки программирования, которые встроены в анализатор JSON. Содержимое буфера после этого можно считать сообщением. Буфер может быть очищен, а сообщение может быть обработано. Для анализа остальной части сообщения всегда используется клавишная команда с клавиатуры. Оставшаяся часть сообщения будет обработана по заданной команде на основе протокола, описанного в спецификации. Все команды, отправленные клиенту, будут отвечать асинхронно.
Например, если клиент отправляет запрос-сообщение {"type":"scan", "action":"start"}\n на концентратор 127.0.0.1:10996, то в ответ будет получено либо сообщение {"type":"scan", "action":"start", "code":200}\n с вопросом началось ли сканирование, либо сообщение об ошибке вида
{"type":"scan","code":412,"message":"unable to start scan"}\n, если не удалось запустить сканирование.
204