uRPC - web интерфейс, при помощи которого создаётся, сохраняется и загружается протокол взаимодействия устройств, основанный на правилах протокола проекта ximc. По готовому протоколу, в свою очередь, возможно загрузить готовую и работающую прошивку, wiki описание протокола, исходные коды кроссплатформенной библиотеки управления с подготовленными скриптами её сборки, графический интерфейс для всех функций библиотеки, набор врапперов для управления библиотекой из разнообразных языков программирования, примеры программ управления библиотекой из разных языков программирования, и другие дополнительные решения. В результате, после создания на web сайте протокола, можно сгенерировать набор файлов, чтобы уже через 30 минут управлять микроконтроллером через GUI. Главная цель: облегченный запуск новых электронных устройств с микроконтроллером, работающих под управлением компьютера.
Все коммуникации между ПК и микроконтроллером осуществляются по принципу клиент-сервер. При этом микроконтроллер является сервером, а ПК – клиентом. Это значит, что все коммуникации инициируются компьютером. Микроконтроллер не может отправить какие-либо данные на ПК самостоятельно, для этого ему требуется соответствующий запрос клиента (компьютера).
URPC позволяет осуществлять коммуникации двух типов:
Коммуникация посредством команд осуществляется следующим образом:
Входные и выходные никак не зависят друг от друга и могут иметь формат. Единственное ограничение: суммарный объём всех входных данных, также как и суммарный объём всех выходных данных не должен превышать 250 байт.
Команды могут иметь входные и выходные аргументы, только входные аргументы, только выходные аргументы, а могут вообще не иметь аргументов.
Общение посредством аксессоров.
Аксессор – это специальная команда (а точнее set/get пара команд), предназначенная для работы с данными на сервере.
Отправка данных с клиента на сервер осуществляется посредством set-функции аксессора, получение данных – посредством вызова get-функции аксессора. Обе функции могут быть вызваны только клиентом (ПК).
Set-функция имеет только входные аргументы, get-функция – только выходные. Формат входных и выходных данных set и get функций одного аксессора должен быть одинаковым.
Также как и в случае команд после получения set и get запросов аксессора сервер (микроконтроллер) запускает соответствующие обработчики, содержание которых определяется пользователем.
Придумать и задать название проекта, версию и нажать Update
CID – это 4-буквенный идентификатор, который должен быть уникальным для каждой команды. CID не чувствителен к регистру, поэтому MYID и myId – это один и тот же идентификатор.
Name – это имя команды (такое же имя будет у функции вызова этой команды, а также будет включено в имена типов данных, связанных с этой командой). Рекомендуется писать имя команды в snake_case.
После добавления команды нажать Edit и добавить описание команды на русском и английском языках в блоке Edit command properties в полях english и russian.
Name – имя аргумента. После генерации проекта к аргументам можно будет обращаться как к полям структуры по их именам. Рекомендуется писать имена аргументов в CamelCase.
Length – длина массива. Если оставить это поле пустым, то аргумент будет обычной переменой. Если в поле Length указать число, то данный аргумент будет представлять собой массив из Length элементов.
Суммарная длина всех аргументов команды не должна превышать 250 байт.
Обработка команд производится с учётом их размера в байтах. Поэтому для обеспечения обратной совместимости с последующими версиями протокола необходимо, чтобы размер команды оставался постоянным. Для этого рекомендуется обеспечить запас по размеру команды путём добавления массива reserved.
Пример:
typedef struct
{
uint8_t foo;
uint8_t reserved[41];
}
my_command_v1;
// sizeof(my_command_v1) = 1 + 41 = 42
typedef struct
{
uint8_t foo;
uint8_t bar[3];
uint8_t reserved[38];
}
my_command_v1;
// sizeof(my_command_v2) = 1 + 3 + 38 = 42 = sizeof(my_command_v1)
Для этого нужно нажать на кнопку Edit рядом с соответствующим аргументом в списке.
В случае, если какой-то из аргументов будет представлять собой некоторый набор флагов, или же просто требуется определить универсальный набор констант для прошивки МК и программы для ПК, к каждому из аргументов можно создать набор констант, который будут зафиксированы в виде макроопределений в соответствующих заголовочных файлах.
Имена констант рекомендуется писать в верхнем регистре.
Чтобы добавить аксессоры, нужно перейти на главную страницу проекта. Это можно сделать, кликнув по ссылке Home слева вверху.
AID – это 3-буквенный идентификатор, который должен быть уникальным для каждого аксессора.
AID также не должен пересекаться с идентификаторами команд в формате G<AID> и S<AID>. То есть нельзя создавать аксессор с идентификатором MID, если уже определена команда с идентификатором GMID или SMID.
AID не чувствителен к регистру, поэтому MID и mId – это один и тот же идентификатор.
Name – это имя аксессора (такое же имя будет у функции вызова этого аксессора, а также будет включено в имена типов данных, связанных с этим аксессором). Рекомендуется писать имя команды в snake_case.
Если аксессор создаётся только для того, чтобы обеспечить существование хотя бы одного аксессора в проекте, в поле Name рекомендуется задать имя dummy.
После добавления аксессора нажать Edit и добавить описание аксессора на русском и английском языках в блоке Edit accessor properties в полях english и russian.
Важно! для корректной работы аксессор должен содержать хотя бы один аргумент
Процесс добавления аргументов аксессоров аналогичен добавлению аргументов команд.
Константы аргументов аксессоров аналогичны константам аргументов команд.
Чтобы сгенерировать исходный код, нужно перейти на главную страницу проекта. Это можно сделать, кликнув по ссылке Home слева вверху.
На панели справа в разделе Firmware из выпадающего списка выбрать микроконтроллер и нажать Generate.
Сохранить архив с кодом.
На панели справа в разделе Library нажать Generate.
Сохранить архив с кодом.
На панели справа в разделе Project нажать Save.
Сохранить JSON-файл.
Результатом работы этого генератора является клиентская библиотека, основная задача которой - предоставление удобного C API для отправки запросов контроллеру и ожидание ответа от него. Сама библиотека предоставляется в виде CMake или QMake -проекта, из которого могут быть сгенерированы файлы для конечной системы сборки(проект для Microsoft Visual Studio, Code::Blocks, XCode, обычный Makefile. QMake генерирует проект для Visual Studio и обычный Makefile и т.д.) Для лучшего ознакомления с CMake рекомендуется посетить официальной сайт.
Для работы библиотеки под Windows понадобятся распространяемые пакеты Visual Studio в зависимости от версии. Пакеты для Visual Studio скачиваются с официального сайта, для VS2013, например, здесь.
Заметьте, установщик зависит от битности системы.sudo apt-get install build-essential
cmake -D ENABLE_XINET=ON CMakeLists.txt
make
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:путь_к_директории_с_библиотекой
На основе описания протокола этот генератор способен создать полноценную прошивку для целевого микроконтроллера, тем самым уберегая программиста от участи написания одного и того же шаблонного кода из раза в раз.
C:\ti\StellarisWare
.C:\ti\TivaWare_C_Series-2.1.2.111
.cd C:\projects
hg clone https://bitbucket.org/vitkorob/emdr1986x-std-per-lib
cd C:\projects\emdr1986x-std-per-lib
hg update default
Qt-отладчик генерируется с помощью кнопки Qt Debugger
Для сборки требуется: сгенерированная и собранная библиотека
По описанию протокола генератор способен создать программу-дебаггер с графическим интерфейсом. Эта программа позволяет взаимодействовать (отправлять команды, смотреть результат) с реальным и виртуальным устройством, работающим по данному протоколу. В сгенерированном архиве лежат исходные коды этой программы и проект для сборки
C:\Qt\msvc2013\4.8.6_x64\bin\
для x64 и
C:\Qt\msvc2013\4.8.6\bin\
для x32 .C:\Qt\msvc2013\4.8.6_x64\bin\
<имя_проекта>.dll
, <имя_проекта>.lib
, <имя_проекта>.h
от предварительно скомпилированной библиотеки.uRPC_debugger.vcxproj
.sudo apt-get install libqt4-dev build-essential cmake
cmake CMakeLists.txt
make
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd` ./uRPC_debugger
Заметьте, для работы с реальным устройством могут потребоваться права root.Уточнение для Ubuntu 20.04
Ubuntu 20.04 не поддерживает Qt4. Для установки пакета нужно сначала выполнить sudo add-apt-repository ppa:rock-core/qt4 ,а уже затем sudo apt-get install libqt4-dev.
sudo ldconfig
для обновления кэша библиотекcom:\\.\COMxx
com:\\.\COM19
com:///dev/ttyACM19
Результатом работы этого генератора является документация протокола в формате Textile - простого языка разметки, позволяющего пользователям описывать сложные схемы форматирования текстовых документов без использования внешних средств (WYSIWYG-редакторов, TEX, HTML и т.д.). Более подробно о синтаксисе и семантике языка Textile можно узнать в онлайн-песочнице (там же можно и потестировать сгенерированные файлы).
Содержимое созданных этим генератором файлов можно напрямую вставлять во все предназанченные для Textile текстовые поля Redmine(wiki, тексты сообщений, новости и т.д.).
uRPC способен генерировать примеры использования команд протокола на других языках программирования. Пока что реализовано только для Python. Результатом работы этого генератора являются два py файла: testpython.py и p(имя_протокола).py. В файле py(имя_проекта).py описаны все константы библиотеки, структуры данных и переменная lib, по которой можно вызвать любую функцию библиотеки. В файле testpython.py показан пример открытия\закрытия устройства.
Пример программы, использующей функции протокола, приведён в файле testpython.py. Заметьте, она начинается с импорта файла py(имя_протокола).py - этот файл должен лежать в одной папке с файлом вашей программы.
Пример вызова функции протокола из python:
t = set_position_t()
t.Position = 5
lib.set_position(device_id, byref(t))
x_pos = get_position_t()
result = lib.get_position(device_id, byref(x_pos))
print("Result: " + repr(result))
if result == Result.Ok:
print("Position: " + repr(x_pos.Position))
Обратите внимание на передачу аргументов: структуры передаются по ссылке, для этого используют функцию byref()