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

Введение

Что такое RISDK

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

RISDK предоставляет высокоуровневый интерфейс для популярных языков программирования - Python, C, C++, PHP, Golang, и т.д.

RISDK является кроссплатформенным ПО, и работает под управлением всех операционных систем на наиболее популярных архитектурах процессора.

На данный момент библиотека является проприетарной, и разрабатывается ООО НПО "Интеллект".

Руководящие принципы разработки RI SDK

При проектировании и разработке RISDK разработчики руководствуются определенными принципами, которые и определяют архитектуру библиотеки:

  • для каждого электронного компонента предоставляется исчерпывающий человекопонятный API;
  • платформонезависимость - библиотека работает на устройствах с различными архитектурами (amd64, i368, aarch64) и операционными системами (Linux, Windows);
  • возможность встраивания в программы на популярных высокоуровневых языках программирования (Python, PHP, Golang, C++, C);
  • высокое быстродействие при низком потреблении памяти;
  • расширение разработчиками и пользователями за счёт плагинов и специализированного API;
  • возможность запуска на IOT устройствах;
  • возможность реализации автоматизированных систем, управляемых на основе событий между отдельными электронными устройствами;
  • асинхронное взаимодействие с электронными компонентами;
  • поддержка интерфейсов I2C, SPI, UART, GPIO;
  • низкий порог вхождения;
  • длительная поддержка разработчками.

Архитектурные принципы

Для того, чтобы наиболее эффективно применять RISDK при решении своих задач полезно понимать архитектуру этой библиотеки, хотя бы на базовом уровне. Ниже перечислены основные архитектурные принципы, которые характеризуют RISDK.

Иерархическая организация компонентов

Для того, чтобы управлять физическим устройством, подключенному к компьютеру, необходимо проинициализировать соответствующий компонент RISDK. RISDK разделяется на несколько уровней.

  1. Ядро. Этот уровень не входит в иеарархию компонентов, однако именно на нём реализованы основные объекты ядра:
    • реестр компонентов - хранилище указателей на созданные объекты компонентов;
    • генератор дескрипторов, ответственный за генерацию уникальных целочисленных идентификаторов, по которым можно вызывать API компонентов из программ на различных ЯП;
    • диспетчер связывания, котрый реализует алгоритм виртуального связывания компонетов друг с другом.
  2. Базовый компонент. Все компоненты встраивают в себя базовый компонент, так как в нём опредлён дескриптор, который есть у любого компонента.
  3. Группы компонентов. Все компоненты относятся к одной из трёх групп:
    • коннекторы - устройства, которые передают управляющий сигнал другим устройствам (i2c адаптер, ШИМ);
    • исполнительные устройства, которые способны воздействовать на внешнюю среду (светодиод, реле, etc);
    • датчики - устройства, которые могут предоставлять информацию об окружающей среде (датчик влажности, гироскоп, датчик тока, etc).
  4. Типы компонентов. На этом уровне определены функции, доступные для конкретных компонетнов, и реализованы их алгоритмы;
  5. Модели компонентов. На этом уровне определены конкретные модели устройств, которые задают их характеристики. То есть алгоритмы не изменяются, так как определены выше, на уровне типов, но параметры, влияющие на вычисления в этих алгоритмах, могут изменяться. При желании пользователь может задавать собственные характеристики устройств.

На данный момент в RISDK представлены следующией компоненты:

  • Коннекторы
    • I2C адаптер
      • ch341
      • cp2112
    • ШИМ
      • PCA9685
  • Исполнительные устройства
    • Сервопривод
      • mg90s
      • a0090
      • mg996
      • Corona DS929MG
      • Corona SB-9039
      • Corona DS843MG
      • Corona SD238MG
    • Сервопривод вращения
      • mg996r
    • Светодиод
      • ky016
  • Датчик
    • Датчик тока, напряжения и мощности
      • ina219

Обобщенное описание сигнатуры функций API RISDK

Каждая функция API в обязательном порядке принимает последним параметром указатель на массив char, в который будет записана ошибка в случае некорректного выполнения функции. Каждая функция-конструктор принимает одним из параметров указатель на int, в который будет записан дескриптор созданного компонента для последующего использования во внешнем коде. Каждая функция взаимодействия с устройствами первым параметром принимает дескриптор компонента. Функции взаимодействия с устройствами имеют следующую типовую сигнатуру имени: RISDKгруппатипимяфункции, где RI_SDK - обязательный префикс, группа - функциональная группа компонента (коннектор, исполнительное устройство, или датчик), тип - тип физического устройства (сервопривод, ШИМ, светодиод, адаптер I2C), а имяфункции соответствует названию метода объекта компонента во внутренней реализации.

Инициализация RISDK

До выполнения любых функций компонентов правильно инициализировать библиотеку. Это действие необходимо для того, чтобы выделить место для внутренних объектов уровня ядра, в первую очередь реестра компонентов. В реестра компонентов будут храниться указатели всех созданных пользователем компонентов. Ключом доступа к этим указателям являются дескрипторы, возвращаемые в клиентский код в момент создания компонента.

Кроме того, на этом этапе определяется уровень логирования RISDK. Для того, чтобы выполнить инициализацию, необходимо вызвать функцию RI_SDK_InitSDK().

Создание компонентов и их логическое связывание друг с другом

Для того, чтобы программно управлять собранным автоматическим устройством, необходимо в RISDK инициализировать "виртуальные" компоненты, соответствующие реальным физическим компонентам. В момент инициализации происходит следующее:

  1. создаётся компонент заданной группы, типа, модели (если задана модель, то будут инициализированы конкретные характеристики компонента);
  2. генерируется уникальный дескриптор компонента, по которому можно будет вызывать API компонента;
  3. компонент сохраняется в реестре компонентов (который уже создан, так как была вызвана функция RI_SDK_InitSDK()), причём ключом записи компонета является сгенерированный на шаге 2 дескриптор;
  4. Дескриптор компонента возвращается в программу, которая вызвала функцию создания компонента. Программа может вызывать API компонента, передавая в качестве первого аргумента функций полученный дескриптор.

При инициализации компонента важно учитывать иеарархию компонентов в RISDK. Все компоненьы разделяются на группы (коннекторы, исполнительные устройства, и датчики). Внутри групп определены типы устройств (сервопривод, гироскоп, ШИМ), а каждый тип устройств представлен определенной моделью, которая определяет характеристики устройства. RISDK позволяет инициализировать компонеты на любом из уровней. Для этого в API RISDK представлено несколько функций:

  • RI_SDK_CreateBasic создаёт пустой объект, у которого есть только дескриптор. Этот объект можно через функции расширения расширить до объекта уровня группы (коннектора, исполнительного устройства или датчика);
  • RI_SDK_CreateGroupComponent создаёт объект уровня группы - коннектор, исполнительного устройство или датчик. У такого объекта нет детализации в виде реализованных алгоритмов или характеристик. Объекты этого уровня позволяют только вызвать функцию чтения состояния самого объекта. Объект уровня группы можно расширить до объекта уровня типа.
  • RI_SDK_CreateDeviceComponent создаёт объекты, в которых реализованы алгоритмы работы конкретных компонентов. Объект уровня типа можно расширить до объекта уровня модели;
  • RI_SDK_CreateModelComponent создаёт объекты, в которых не только реализованы алгоритмы работы, так как тип устройства уже известен, но и характеристики компонентов, которые опредляются значением модели.

После создания компонентов их необходимо логически связать друг с другом. В RISDK связывание - это процесс создания контроллеров между двумя компонентами, чтобы они могли обмениваться друг с другом данные и осуществлять определенные вызовы. Некоторые типы не являются самостоятельными устройствами, и могут получать управляющие сигналы только от другого устройства, например, коннектора. Для этого необходимо физически подключить исполнительное устройство или датчик, например, к ШИМ или адаптеру, и отразить физическое подключение через вызов функции связывания устройства с контроллером.

Важно, что именно при связывании открываются соединения с помощью определенных интерфейсов ОС, например I2C, или регистрируется подключение устройства на логическом порте коннектора типа ШИМ.

Устройства могут подключаться к контроллеру различным образом, поэтому для каждого типа устройства существует своя функция связывания:

Все устройства являются либо подключаемыми к другим устройствам (контроллерам), либо контроллерами для подключаемых устройств, либо и тем, и другим одновременно. Например, ШИМ подключается к I2C адаптеру (контроллеру), но и сам является контроллером для сервоприводов, к которым уже технически невозможно подключить другие устройства.

Внутренний механизм связывания основан на использовании трёх типов интерфейсов - интерфейс протокола, интерфейс контроллера (он же интерфейс вызова), и интерфейс связываемого (клиента).

Интерфейс протокола: все компоненты, имплементирующие интерфейсы этого рода обязаны принимать запросы на подключение, выполнять проверки возможности связывания, и возвращать интерфейс вызова (т.е. контроллер).

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

Интерфейс вызова (контроллера): все компоненты, имплементирующие интерфейсы этого рода реализуют методы передачи данных, специфичные для протокола.

Расширение компонентов RISDK

Каждый созданный компонент можно расширить в соответствие с иерархической моделью этого компонента, вызвав соответствующую функцию внешнего API. Например, можно создать компонент сервопривода уровня типа, а затем расширить его до конкретной модели MG90s уровня модели. При этом будут переопределены свойства компонента уровня сервопривода.

Заверешние работы компонентов и очистка памяти

При завершении работы с компонентом целесообразно корректно "разрушать" объект электронного компонента. Для этого предназначена функция RI_SDK_DestroyComponent. Она корректно завершает работу компонента в соответствие с типом компонета, например, для коннекторов она закроет все открытые соединения, а для сервопривода сбросит значение импульса на порте ШИМ, к которому подключен сервопривод. Затем эта функция удалит объект компонента, удалит запись о нём из реестра компонентов, и освободит занятиый этим компонентом дескриптор.

Завершение работы RISDK

При завершении работы пользовательской программы, например, на Python, необходимо вызывать функцию, обратную по сути RI_SDK_InitSDK - RI_SDK_DestroySDK. Эта функция завершит работу всех компонентов через вызов RI_SDK_DestroyComponent, а затем разрушит внутренние объекты ядра RISDK, такие как реестр компонентов и диспетчер связывания. Последующие вызовы любых функций, кроме RI_SDK_InitSDK() будут приводить к ошибкам.

Обработка ошибок

Любая функция RISDK всегда "сообщает" о том, корректно ли она выполнилась. Для этого каждая функция возвращает целое число. Если возвращаемое значение равно 0, то функция отработала корректно. Если в ходе выполнения функция возникла ошибка, будет возвращено положительное число, являющееся номером ошибки.

Номер ошибки состоит из шести регистров (ABCDXX), и формируется по следующиему принципу:

  • A - это регистр, по которому можно понять, произошла ошибка в ядре RISDK, или в объекте компонентов;
  • B - соответствующий номер объекта ядра или номер группы компонента;
  • C - всегда равен 0 для объектов ядра, или равен номеру типа объекта компонента;
  • D - всегда равен 0 для объектов ядра, или равен номеру модели объекта компонента;
  • XX - порядковый номер ошибки.

Например, код ошибки 211011 обозначает, что ошибка произошла в объекте компонетнов, группы коннекторов, типа I2C адаптеров, номер ошибки - 11.

Один из способов понять, какая произошла ошибка, это вывести в терминал значение ErrorText - последний параметр в любой функции RISDK. Представляет из себя передаваемый по ссылке массив байт, в который RISDK записывает соответствующий номеру текст ошибки.

Другой способ заключается в сверке полученного номера ошибки с реестром ошибок, где помимо описания ошибки так же приводятся рекомендации по её устранению.

Дорожная карта, планы на будущее

В этом разделе описаны крупные задачи, которые предстоит решить для полного соответствия обозначенным целям разработки RISDK.

  1. Вывод RISDK в OpenSource. Создание необходимых информационных ресурсов для сообщества.
  2. Рефакторинг группы компонентов "коннекторы". Разделение физических компонентов и интерфейсов ОС.
  3. Реализация компонента реле.
  4. Реализация новых датчиков.
    • Гироскоп.
    • Датчик освященности.
    • Датчик расстояния.
  5. Реализация событийно-ориентированного управления автоматизированными системами.
  6. Реализация механизма применения плагинов к RISDK в целях расширения её функционала.