1 Установка и настройка компилятора и интерпретатора

1.1 Быстрый старт

Видео-руководство по быстрому старту RCML для Windows

Видео-руководство по быстрому старту RCML для Linux

Перейдите на страницу проекта на SourceForge по ссылке:

http://sourceforge.net/projects/rcmlang/files/

Из папки нужной Вам ОС скачайте архив с файлами компилятора rcml_build_X.zip и архив с файлами модулей rcml_modules_build_X.zip, где X – версия компилятора. Для этих двух архивов версии должны совпадать.

Распакуйте оба скачанных архива в одну папку.

Перейдите в папку с распакованными файлами и создайте в ней файл config.ini со следующим содержимым:

[robot_modules]
module = test
[function_modules]
[control_modules]
[repository]
[lib_search_paths]

В текущей папке создайте ещё один файл с именем hello.rcml и со следующим содержимым:

function main() {
    robot_test->print("Hello world!\n", 0);
}

Это будет первая Ваша программа на языке RCML, состоящая из одного оператора – вызова функции вывода в консоль строки “Hello world!\n” у тестового виртуального робота robot_test с задержкой в 0 сек.

Для компиляции программы запустите редактор командной строки в текущей папке и выполните следующую команду:

rcml_compiler.exe hello.rcml hello.rcml.pc

В текущей папке появится файл скомпилированной программы hello.pc, как показано на рисунке 1.

Рисунок 1 Результат компилирования программы

Чтобы выполнить скомпилированную программу, выполните команду:

rcml_intepreter.exe hello.rcml

Обратите внимание, что тут не указывается расширение pc у файла скомпилированной программы. Пример успешного выполнения файла показан на рисунке 2.

Рисунок 2 Результат успешного выполнения программы

1.2 Описание процесса подготовки программы на RCML

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

Расширение rcml носит рекомендательный характер, на деле файл может иметь любое имя, поддерживаемое файловой системой вашей ОС.

В последующем данный файл компилируется компилятором языка RCML, в процессе компиляции выполняется ряд процедур по подготовке программы к исполнению, а именно:

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

При успешной компиляции программы компилятором языка RCML создается файл с байт-кодом программы, для которого рекомендуется двойное расширение rcml.pc (PC – pseudo code). Под файлом с байт-кодом понимается уже бинарное содержимое, по своей структуре близкое к машинным кодам процессора, исполняемое собственной виртуальной машиной в виду некоторых особенностей языка RCML.

Непосредственное выполнение программы выполняется интерпретатором языка RCML, как следует из названия, интерпретирующим инструкции байт-кода посредством внутренней виртуальной машины в реальные команды процессора.

1.3 Установка и настройка компилятора и интерпретатора RCML

Компилятор и интерпретатор можно скачать со страницы проекта на SourceForge:

http://sourceforge.net/projects/rcmlang/files/

Компилятор и интерпретатор представляют собой два исполняемых файла, которые необходимо поместить в одну папку. Дополнительно в данной папке требуется создать текстовый файл config.ini, это конфигурационный файл среды RCML. Конфигурация едина для компилятора и интерпретатора.

Данный файл может содержать следующие секции:

  • [robot_modules] – секция подключения модулей роботов;
  • [function_modules] – секция подключения функциональных модулей;
  • [control_modules] – секция подключения модулей управления;
  • [choice_modules] – секция подключения модулей выбора робота.

Имя подключаемого модуля указывается в свойстве module в секции такого же типа, что и модуль. Если подключаемых модулей одного типа несколько, то на каждый из них отводится отдельная строка. Загрузка модулей определенного типа происходит в том порядке, в котором они были указаны в рамках своей секции.

Пример задания подключаемых модулей в config.ini:

[robot_modules]
module = test
module = tarakan
module = uarm
module = lego_ev3
[function_modules]
module = math
[control_modules]
module = test
module = keyboard
module = gamepad
module = myo
[choice_modules]
module = avg

Для модулей всех типов в папке с компилятором и интерпретатором должны быть созданы папки по названию секции (robot_modules, function_modules, control_modules или choice_modules). Для каждого модуля в соответствующей его типу папке должна быть создана директория с таким же именем, как и имя модуля, далее данная директория будет называться директорией модуля. Сам модуль представлен файлом динамически подключаемой библиотеки, который должен именоваться так же, как и модуль, но с добавлением суффикса module через нижнее подчеркивание. Файл модуля должен находиться в папке модуля. В папке модуля могут находиться прочие файлы, необходимые для работы модуля, например, файлы конфигурации.

Дополнительно в файле config.ini могут быть следующие секции:

  • [statisctic] – данная секция может иметь только одно свойство - db_path – путь к файлу базы данных куда будет записываться статистика выполнения роботами своих функций. Если файла базы не существует, он будет создан. Если база уже существует, то она будет дополнена. Если это свойство будет отсутствовать или будет пустым, статистика не будет записываться.
  • [lib_search_paths] – пути поиска скомпилированных библиотек RCML. В данной секции в свойстве path может быть указан путь по умолчанию где искать библиотеки для RCML. Путей может быть несколько, тогда каждый путь задается через отдельное свойство path. Поиск требуемой библиотеки по указанным путям производится в том же порядке, в каком были указаны пути в данной секции.

  • [locale_settings] – секция настроек текущей локали, может содержать только одно совйство – locale – в котором задаются параметры текущей локали для RCML в виде строки по правилам, принятым в Linux среде. По умолчанию текущая локаль является английской и задается значением en_US.UTF-8.

Например, чтобы переключить текущую локаль на русский язык необходимо указать ru_RU.UTF-8.

Важно! Опция locale_settings поддерживается только в ОС Windows.

Файлы всех поддерживаемых локалей можно скачать из соответствующей директории на официальной странице загрузок проекта на SourceForge:

https://sourceforge.net/projects/rcmlang/files/translations/

О том, как добавить свою собственную локаль в проект RCML можно прочитать на странице репозитория локалей для RCML:

https://github.com/RobotControlTechnologies/RCML_translations

1.4 Подробнее о модулях роботов

Модули роботов занимают одно из ключевых положений в языке RCML, поскольку именно через них осуществляется связь и передача команд физическому роботу, см. рисунок 3.

Рисунок 3 Роль модулей роботов в связи с физическими роботами

Модуль робота отвечает за передачу команд от интерпретатора языка RCML одному или нескольким роботам одного класса (или типа), которые объединены под этим модулем. Рекомендуется для каждого класса или типа робота использовать отдельный модуль. Интерпретатор RCML через задекларированный API устанавливает связь с модулем робота, который в свою очередь устанавливает связь с каждым закрепленным за ним роботом. Таким образом, через модуль робота скрывается реализация связи и управления роботом от интерпретатора, что позволяет подключать к нему самых разных роботов. О том, как написать свой модуль робота и подключить его к интерпретатору, читайте в разделе “Создание собственных модулей для языка RCML”

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

1.5 Подробнее о модулях функций

Через модули функций возможно добавление в язык RCML новых функций, которые не целесообразно или невозможно реализовывать на данном языке, например, какие-либо сложные вычисления. Таким образом, через отдельный API модули функции позволяют реализовать связь RCML с прочим ПО.

1.6 Подробнее о модулях управления

Модули данного типа используются для принятия решения о выборе очередного робота при необходимости выполнения той или иной функции. Как вариант выбор может быть основан на накопленных ранее статистических данных, об этой функции, роботе или программе. Через модули выбора возможно произвести выбор робота по различным алгоритмам, заложенным в них (модулях). Обычно отдельный модуль представляет отдельный алгоритм выбора робота.

Без данных модулей выбор роботов происходит по решению модулей роботов. Подробнее о работе механизма выбора робота в разделе “Использование модуля выбора робота”.

1.7 Компиляция и запуск программы RCML

Как отмечалось ранее, написанная на языке RCML программа сначала должна быть откомпилирована в байт-код, и лишь только потом она может быть запущена на выполнение интерпретатором RCML.

Для компиляции программы нужно запустить компилятор RCML, наименование его исполняемого файла по умолчанию (без расширения, так как в зависимости от ОС оно может быть различным) - rcml_compiler. При запуске компилятора ему нужно передать 2 параметра:

rcml_compiler <rcml_text_file> <rcml_pc_file>

В качестве первого параметра rcml_text_file указывается путь до файла с RCML программой, а в качестве второго параметра rcml_pc_file - путь до файла, в который нужно записать полученный байт-код данной программы.

Если компиляция будет выполнена успешно, то файл с байт-кодом будет создан или перезаписан, если он уже имеется. И теперь его можно запустить на выполнение через интерпретатор RCML, имя исполняемого файла которого по умолчанию - rcml_interpreter. Компилятор при запуске ожидает всего один параметр – файл с байт-кодом. Синтаксис команды запуска интерпретатора:

rcml_interpreter <rcml_pc_file>

Важно! К пути rcml_pc_file интерпретатор автоматически дописывает расширение .pc

1.8 Параметры командной строки RCML

Командная строка RCML компилятора имеет следующий шаблон:

rcml_compiler [--version] [--logfile log_file_path] <rcml_text_file> <rcml_pc_file>
  • version - вывод текущей версии RCML компилятора, списка версий, поддерживаемого API модулей, а также даты сборки. При указании этого флага компилятор выводит информацию и сразу же завершает работу;
  • logfile log_file_path - переключение вывода RCML компилятора в файл по пути log_file_path;
  • rcml_text_file - путь до файла с RCML программой в текстовом представлении;
  • rcml_pc_file - путь до файла, в который записать скомпилированную программу.

Командная строка RCML интерпретатора имеет следующий шаблон:

rcml_interpreter [--version] [--logfile log_file_path] <rcml_pc_file> [rcml_params]
  • version - вывод текущей версии RCML интерпретатора, списка версий, поддерживаемого API модулей, а также даты сборки. При указании этого флага интерпретатор выводит информацию и сразу же завершает работу;
  • logfile log_file_path - переключение вывода RCML интерпретатора в файл по пути log_file_path;
  • rcml_pc_file - путь до файла скомпилированной RCML программы;
  • rcml_params - параметры запускаемой RCML программы. Подробнее о параметрах RCML интерпретатора в разделе “Передача параметров в программу на RCML”.

Важно! К rcml_pc_file интерпретатор автоматически дописывает расширение .pc


2 Основы построения программ на RCML

2.1 Алфавит языка и специфика использования символов

Алфавит языка RCML составляют:

  1. Символы, используемые для составления идентификаторов:

    • латинские строчные или прописные буквы;
    • арабские цифры от 0 до 9;
    • символ подчеркивания «_»;
  2. Символы-разделители:

    • символ пробела;
    • символ табуляции;
    • символы переноса строки;
  3. Специальные символы – символы, выполняющие определенные функции при построении различных конструкций языка: + - * / = { } ( ) < > , ; : ~ # @ ! “
  4. Составные символы – группа символов, которые воспринимаются компилятором как единое целое: :: -> == >= <= !=
  5. «Неиспользуемые» символы – символы, которые не входят во множество выше обозначенных, но, тем не менее, могут быть использованы в комментариях или для задания значений констант и строк;
  6. Зарезервированные слова, рассмотренные далее.

2.2 Правила составления идентификаторов

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

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

2.3 Общая структура программ в RCML

Программа на языке RCML состоит из двух ключевых разделов: раздел включений и раздел функций.

Пример программы на языке RCML:

// Раздел включений
include “function.rcml
include “C:/robot/robot.rcml”

// Раздел функций
function sum(a, b) {
    c = a + b;
    return c;
}
function main() {
    s = sum(1, 2);
    echo(“sum = ”,s,”\n”);
}

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

Раздел функций начинается после первого упоминания ключевого слова function. В разделе функций указывается основной код программы, который состоит из функций.

По аналогии с языками программирования C и C++, выполнение начинается с функции с именем main, которая обязательно должна присутствовать. Данное утверждение относится к исполняемым программам на RCML, не являющимся библиотеками. Разница между библиотекой и исполняемой программой будет рассмотрена далее.

2.4 Включение дополнительных файлов RCML в программу

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

Синтаксис:

include “путь_к_файлу”

Примеры:

include “function.rcml”
includeC:/robot/robot.rcml”
include “../libs/robot.rcml”

Путь до включаемого файла может быть абсолютным или относительным. В случае если путь относительный, то компилятор сначала вычисляет абсолютный путь до включаемого файла относительно абсолютного пути файла, в котором встречена данная конструкция включения. Если файл по полученному пути не найден, то компилятор вычисляет абсолютный путь относительно каждого варианта пути из параметра path из раздела lib_search_paths в конфигурационном файле config.ini в том порядке, в котором эти пути были указаны в файле конфигурации.

Данный параметр path из раздела lib_search_paths конфигурационного файла называется «путь поиска по умолчанию». Синтаксис задания путей поиска по умолчанию:

[lib_search_paths]
path = путь_1
path = путь_2

Порядок включения дополнительных файлов с кодом такой же, в каком они были указаны в исходном rcml файле.

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

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

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

2.5 Включение файлов библиотек RCML в программу

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

Синтаксис:

include_lib имя_библиотеки “путь_до_файла_библиотеки”

Пример программы, использующей включение библиотеки math:

include_lib math "../export_library/etalon.rcml.pc"
function main() {
    s = math.sum(1,2);
    system.echo("\n1 + 2 =",s,"\n");
}

Процесс создания библиотеки описан в разделе “Создание файла библиотеки RCML”.

Процесс поиска файла библиотеки компилятором аналогичен поиску включаемого файла. Однако включения кода библиотеки в исполняемый код программы не происходит. В исполняемой программе создается ссылка на файл библиотеки, и для запуска такой программы также потребуется файл скомпилированной библиотеки, как и при компиляции.

Допускается включение одного и того же файла библиотеки под разными именами-идентификаторами.

Стоит отметить, что библиотеки могут ссылаться на другие библиотеки, т.е. при компиляции библиотеки допускается включение в неё других библиотек.

2.6 Функции

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

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

Задание новой функции начинается с ключевого слова function, далее через разделитель следует наименование функции, затем в круглых скобках список параметров, который может быть пустым. После списка параметров в фигурных скобках следует код функции – список её операторов.

Синтаксис задания функции:

function имя_функции(список_параметров) {
    список_операторов
}

Пример функции:

function sum(a, b) {
    c = a + b;
    return c;
}

Функция в языке RCML может как возвращать значение, так и не возвращать. Возврат значения, как и выход из функции происходит через оператор return.

В конце функции данный оператор указывать необязательно, если значение не возвращается. Если функция не возвращает значения, то по умолчанию возвращаемое значение считается нулем - 0.0.

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

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

Параметры в основной функции (main) имеют особое значение, подробнее в разделе “Передача параметров в программу на RCML”

2.7 Комментарии

В RCML доступны однострочные и многострочные комментарии. Однострочный комментарий начинается с двойного знака косой черты // и следует до конца строки.

Пример:

//Это комментарий

Многострочный комментарий начинается со комбинации символов /* и следует до следующей комбинации символов */

Пример:

/* Это
многострочный
комментарий */

2.8 Типы данных

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

Стоит отметить, что на данном этапе развития RCML иные типы данных, в том числе и массивы, пока не предусматриваются.

2.8.1 Явные типы данных

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

2.8.2 Неявные типы данных

К неявным типам данных на языке RCML относятся логический и строковый типы.

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

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

  • \n – переход на новую строку;
  • \” – вставка символа двойной кавычки;
  • \ \ - вставка символа обратной косой черты (обратный слэш).

2.9 Переменные

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

Пример использования переменных в функции:

function sum_by_abs(a, b) {
    c = a + b;
    if (c < 0) {
        с = -с;
    }
    return c;
}

В RCML нет глобальных переменных.

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

2.10 Выражения и операции

Выражения в RCML могут состоять из числовых констант, переменных и возвращаемых результатов функций, которые могут быть использованы в качестве операндов математических и логических операций в составе выражения. Операции, в свою очередь, могут быть бинарными или унарными. Приоритет операций в выражениях задается круглыми скобками, так же, как это принято в математике. Результат выражения может быть присвоен в переменную или передан как параметр функции, что возможно для тех параметров функций, которые работают с данными типами данных. Использование особых переменных для связи с роботом в контексте выражений запрещено на уровне синтаксических правил.

Примеры выражений:

a = 1 + 1;
a5;
c = a * b;
d = sum(a, с) * 10 * (a / c);
e = -d + 15;
f = d >= 10;
!f;

2.10.1 Математические операции

В RCML доступны следующие бинарные математические операции:

  • «+» - операция сложения;
  • «-» - операция вычитания;
  • «*» - операция умножения;
  • «/» - операция деления без остатка;
  • «%» - взятие остатка от операции деления.

И одна унарная операция:

  • «-» - смена знака.

2.10.2 Логические операции

Из логических операций в RCML доступны следующие бинарные операции:

  • «==» -проверка равенства;
  • «!=» -проверка неравенства;
  • «>=» - проверка больше или равно;
  • «<=» - проверка меньше или равно;
  • «>» - проверка больше;
  • «<» - проверка меньше;
  • «&&» - конъюнкция, логическое «И»;
  • «||» - дизъюнкция, логическое «ИЛИ».

И одна унарная операция:

  • «!» - логическое отрицание.

2.11 Преобразование типов данных

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

При преобразовании вещественного типа в логический выполняется следующее правило:

если элемент не равен нулю, то результат - истина, иначе - ложь.

При обратном преобразовании выполняется правило:

если истина, то результат будет 1.0, если ложь, то результат 0.0.

2.12 Управляющие конструкции

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

2.12.1 Условные управляющие конструкции

В RCML представлен всего один оператор условного перехода - if. Синтаксис его использования следующий:

if (выражение) {
    //набор операторов, если результат выражения истина
} else {
    //набор операторов, если результат выражения ложь
}

Результат выражения, заключенного в круглых скобках, приводится к логическому типу, и если результат – истина, то выполняется первый блок операторов в фигурных скобках, иначе — второй блок операторов после ключевого слова else. Второй блок операторов вместе с ключевым словом else может опускаться при задании условного оператора.

2.12.2 Циклические управляющие конструкции

Циклический оператор в RCML так же единствен, это оператор loop. Это более упрощенный оператор цикла по сравнению с аналогичными операторами в традиционных языках программирования. Он является безусловным, и его блок операторов будет выполняться бесконечно, если в нем не будет соответствующей конструкции выхода с предварительным условием или без него. Синтаксис оператора loop следующий:

loop {
    // блок операторов цикла
}

2.12.3 Прочие управляющие конструкции

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

Оператор перехода на начало цикла – continue. При его достижении происходит переход на начало текущего цикла. Используется без параметров.

Оператор выхода из цикла – break. При его достижении происходит прерывание текущего цикла. Используется без параметров.

Оператор выхода из функции – return. Может указываться как с выражением, так и без него, в первом случае результатом выполнения функции будет результат, возвращаемый выражением, во втором случае функция будет возвращать значение по умолчанию – 0.0.

Синтаксис использования оператора return с возвратом выражения:

return выражение;

Без возврата выражения:

return;

Примеры использования оператора return:

return 2+2; //выход из функции в возвратом значения
return; //выход из функции без возврата значения

В случае если оператор return будет указан в функции main, то его выполнение приведет к завершению работы RCML программы, а значение, переданное через данный оператор, будет передано в ОС как код завершения программы. Однако при этом передаваемое значение будет округлено до целых посредством отброса дробной части.

Оператор выхода из программы – exit. При достижении этого оператора происходит завершение выполнения программы независимо от того, в какой функции он указан. Данный оператор также, как и оператор return может вызываться как с выражением, так и без него. В первом случае в ОС возвращается результат выражения как код завершения программы, во втором случае код завершения программы считается равным 0.

Синтаксис использования оператора exit с возвратом выражения:

exit выражение;

Без возврата выражения:

exit;

2.13 Исключения

В RCML возможна обработка исключительных ситуаций, возникающих в определенном блоке кода, как и в некоторых других языках программирования. Общий вид конструкции обработки исключения начинается с оператора try, затем за ним следует блок операторов, в котором может возникнуть исключение в процессе исполнения операторов данного блока, далее может следовать оператор catch и блок операторов, который необходимо выполнить в случае появления исключения. Общий вид конструкции обработки исключений следующий:

try {
    //блок операторов, в котором может возникнуть исключение
} catch {
    //блок операторов для обработки ситуации с исключением
}

При появлении исключения в блоке оператора try выполнение программы переходит на блок оператора catch. Следует отметить, что оператор catch и его блок может быть опущен, и тогда выполнение программы перейдет на оператор, следующий за блоком оператора try. Конструкция обработки исключений может быть вложена одна в другую, и, в случае возникновения исключения, оно будет обрабатываться текущей конструкцией.

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

Работа механизма исключений на примере простой программы деления чисел:

function main() {
    try {
        echo(“this simple division program\n”);
        echo(“c = a / b \n”);
        try {
            echo(“input a\n”);
            a = input();
            echo(“input b\n”);
            b = input();
            if (!b) { // если b ноль, то делить нельзя
                throw; //бросаем исключение
            }
            c = a / b;
            echo(“c = ”, c, “\n”);
        } catch {
            echo(“b is zero!”); //обработка исключения будет в этом блоке
        }
    } catch {
        //этот блок никогда не будет выполнен
    }
}

Однако в языке RCML, ориентированном на робототехнику, в отличие от других языков программирования, оператор try может принимать параметры, указывающие, как именно ему работать. Первый параметр, который принимает оператор try - это строковая константа с указанием режима работы, в зависимости от указанного режима может указываться второй параметр вещественного типа данных.

Всего у оператора try три режима работы:

  • “error_default” - режим работы по умолчанию, как обычный оператор try. В этом случае второй параметр оператора try не указывается. Если параметры оператора try опускаются как в вышеприведенном примере, то оператор try работает именно в этом режиме.
  • “error_time_limit” – режим работы с отсчетом лимита времени, за который должен быть выполнен блок кода оператора try. В данном случае указывается второй параметр, который задает количество миллисекунд, являющийся лимитом на выполнение блока кода оператора try. В случае если данный блок не будет выполнен за указанное время, будет брошено исключение. В случае если исключение будет брошено раньше, отсчет времени будет прекращен, а само исключение будет обработано в обычном режиме.
  • “error_try_count” – режим работы с отсчетом количества попыток, данных для выполнения блока оператора try. В данном режиме второй параметр принимает количество допустимых попыток исполнения данного блока. При каждом брошенном исключении в блоке оператора try счетчик количества попыток будет уменьшаться на 1, и если он достигнет нуля, то будет произведена обычная обработка исключения.

Несмотря на то, что оператор try может принимать параметры, он не является функцией и не возвращает значение.

Пример использования указанных режимов для обработки успешности выполнения роботом своей функции с выдачей ему трёх попыток с лимитом времени по 2 секунды на каждую:

try(“error_try_count”, 3) {
    try(“error_time_limit”, 2000) {
        robot->do_something();
    } catch { //если время вышло
        throw; //то бросаем исключение, чтобы исчерпать попытку
    }
} catch {
    //этот блок выполнится, когда все попытки будут исчерпаны, 
    //а результат так и не будет получен
}

Через оператор throw с исключением можно передать какое-либо значение (значение исключения). В данном случае синтаксис оператора throw будет следующий:

throw выражение;

Для обработки значения выброшенного исключения оператор try будет иметь немного иной синтаксис, в параметрах оператора catch должен быть указан идентификатор - имя переменной, в которую будет записано исключение:

try {
    //блок операторов, в котором может возникнуть исключение
} catch (имя_переменной) {
    //блок операторов для обработки ситуации с исключением
}

Если на момент появления исключения указанная переменная не существует, то она будет создана, в противном случае перезаписана.

try {
    throw 3;
} catch (E) {
    system.echo(“E = ”, E, “\n”); //выведет E = 3
}

Переменная, в которую записывается значение исключения, будет доступна не только в блоке catch, но и за его пределами до конца функции.

Пример с созданием переменной:

system.echo(“E = ”, E, “\n”); //ошибка E ещё не существует
try {
    throw 3;
} catch (E) {
    system.echo(“E = ”, E, “\n”); //выведет E = 3
}
system.echo(“E = ”, E, “\n”); //выведет E = 3, т.к. E была создана в блоке catch

Пример с перезаписью значения:

E = 5;
system.echo(“E = ”, E, “\n”); // выведет E = 5
try {
    throw 3;
} catch (E) {
    system.echo(“E = ”, E, “\n”); //выведет E = 3
}
system.echo(“E = ”, E, “\n”); //выведет E = 3

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

Пример:

try {
    robot_test->throw_value(10);
} catch (E) {
    system.echo(“E = ”, E, “\n”); //выведет E = 10
}

Если выброшенное исключение не было перехвачено в RCML программе, то произойдет завершение работы RCML программы с кодом 1, т.е. с ошибкой, что может служить средством коммуникации ОС и RCML программы (подробнее в разделе “Коммуникация с ОС”). При этом значение исключения будет утрачено.

2.14 Вызов функций

Функции на языке RCML могут быть внутренними и внешними.

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

Внешние функции - это функции уже скомпилированных библиотек RCML, подключаемых к текущей программе, либо функции функциональных модулей, т.е. код таких функций находится вне текущей программы на RCML.

Синтаксически вызов внутренней функции в языке RCML выглядит так же, как и в других языках программирования. Сначала следует идентификатор – имя функции, затем в круглых скобках список передаваемых в неё аргументов:

идентификатор(аргумент1, аргумент2, аргумент3)

Результат вызова любой функции может быть присвоен переменной или использован в выражении. Стоит отметить, что при вызове функции должны быть переданы через аргументы все параметры, указанные в её описании, за исключением некоторых системных функций языка RCML.

Вызов внешней функции имеет иной синтаксис:

имя_источника.имя_функции(аргумент1, аргумент2, аргумент3)

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

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

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

2.15 Системные функции языка RCML

Системные функции языка RCML доступны в среде RCML через системный модуль system. Доступ к данным функциям происходит так же, как ко внешним функциям модуля с указанием имени system.

Системный модуль имеет следующие функции:

  • system.input – функция считывания числа из строки, полученной через стандартный ввод. Считываемая строка должна состоять из цифр и опционально из знака + или -. Затем она преобразовывается в число, которое возвращается как результат вызова функции. Данная функция параметров не имеет.
  • system.echo – функция вывода данных, переданных в качестве аргументов в стандартный вывод. Может принимать любое переменное число параметров, среди которых допускаются строковые константы и числа.
  • system.set – функция изменения системных параметров среды RCML. Принимает два аргумента: название системного параметра и его новое значение. Название системного параметра задается строковой константой, а новое значения этого параметра зависит от указываемого системного параметра. Сейчас у RCML-среды существует только один системный параметр:
    • “behavior” – режим выполнения функций по умолчанию. В качестве второго параметра передается флаг режима, знак ~ или #.
      Подробнее о режимах выполнения читайте в разделе “Пакетная передача команд роботам”.
  • system.sleep – временная пауза в процессе выполнения программы. Принимает один числовой параметр – время паузы в миллисекундах.
  • system.hand_control – включение режима ручного управления для робота. Функция переводит указанного робота в режим ручного управления посредством указанного модуля управления. Выполнение основной программы на период ручного управления приостанавливается. Данная функция принимает переменное число параметров:

    • первый параметр – специальная переменная, связанная с нужным физическим роботом. Обязательный параметр;
    • второй параметр – строковая константа – наименование модуля управления. Обязательный параметр;
    • третий параметр – строковая константа – ось робота;
    • четвертый параметр – наименование оси управляющего устройства в виде строковой константы или выражения, которые будут задавать значения для оси робота, указанной в предыдущем параметре.
    • пятый и шестой, седьмой и восьмой и т.д. параметры задаются всегда попарно: ось робота и источник значений для неё.
  • system.send_package – команда для отправки накопленного пакета команд для роботов. В качестве единственного параметра передается флаг режима выполнения команд из пакета, ~ или #. Подробнее о данной функции в разделе «Указание режимов выполнения функций».

2.16 Макросы

Язык RCML реализует поддержку макросов. Макросом является связка идентификатора – имени макроса - с произвольным текстом – содержание макроса. Макросы объявляются в разделе включений через ключевое слово define, после которого следует разделитель, затем следует идентификатор, являющийся именем макроса, снова разделитель и текст макроса.

Синтаксис задания макроса:

define имя_макроса содержимое_макроса

Все вхождения имени макроса в тексте раздела функций будут заменены на его содержание. После определения макроса его невозможно переопределить другим макросом.

Содержимым макроса считается любой текст между символом-разделителем, следующим после имени макроса, и концом строки. Макрос может иметь многострочное содержимое, для его задания нужно поставить символ «\», т.е. экранировать конец строки, так же, как это делается в многострочных макросах на С++. Тогда содержимым макросом будет считаться всё до конца строки, следующей за строкой, оканчивающийся на данный символ. Символы перевода строки в многострочных макросах сохраняются.

Пример использования макросов:

define ONE 1
define TWO 2
define TREE ONE + \
TWO
define TEST_MS "test message"

function main(){
    system.echo("1 = ",ONE,"\n");
    system.echo("2 = ",TWO,"\n");
    system.echo("3 = ",TREE,"\n");
    system.echo("Test print > ",TEST_MS,"\n");
}

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

function main(){
    system.echo("1 = ",1,"\n");
    system.echo("2 = ",2,"\n");
    system.echo("3 = ",1 + 
2 ,"\n");
    system.echo("Test print > ","test message","\n");
}

Полученный текст уже будет компилироваться в байт-код. Результат выполнения данной программы будет выглядеть следующим образом:

1 = 1.000000
2 = 2.000000
3 = 3.000000
Test print > test message
Важно! Опасно использовать макросы, которые рекурсивно ссылаются друг на друга, в данном случае образуется бесконечная рекурсия, которая приведет к зависанию компилятора. На данном этапе развития языка обнаружение замкнутой рекурсии в макросах не происходит.

Пример замкнутой рекурсии:

define ONE TWO
define TWO ONE

3 Особенности взаимодействия программы с роботом

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

3.1 Понятие робота в RCML

Робот в RCML представляется как некий исполнительный ресурс, который может быть задействован для выполнения определенной задачи (функции), а затем освобожден для повторного задействования, но, например, в уже другой задаче. Ближайшая аналогия — вычислительное ядро процессора, которое в конкретный момент времени может быть занято только одним процессом. Однако средствами ОС, выделяющей очень малые кванты процессорного времени разным программным процессам или задачам, возможно создание иллюзии, что одно ядро обрабатывает сразу несколько задач. В RCML же управлением роботом как ресурсом занимается интерпретатор, но робот задействован не на период времени, а на выполнение конкретной своей функции или нескольких, и, таким образом, время его задействования может быть разным.

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

Модуль робота как таковой предоставляет среде RCML описание класса робота, закрепленного за ним, предполагается, что локально в объектном мире, где используется RCML, может быть, как один, так и несколько роботов одного класса, закрепленных за одним модулем робота. Причем в среде RCML в рамках модуля робота присутствует два ключевых типа объектов, в соответствии с рисунком 4:

  • модуль робота исполняет функции контроллера роботов вверенного ему класса, то есть выполняет функции выбора свободного робота для задействования и освобождения робота после работы;
  • представление робота – отражение конкретного физического робота в виртуальной среде RCML, через него транслируются команды физическому роботу.

Рисунок 4 Виртуальное представление классов объектов в среде RCML

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

3.2 Использование робота в программе

Чтобы задействовать робота в программе, необходимо указать его класс и функцию, которую он должен выполнить. Именование класса робота совпадает с именованием модуля робота в файле config.ini, но класс робота в RCML программе должен указываться через ключевое слово robot и знак подчеркивания.

Синтаксис задействования робота:

robot_класс_робота

Например, нужно вызвать робота из модуля test, тогда указание его класса будет иметь вид:

robot_test

Встретив наименование класса робота в тексте программы, система пошлет запрос к соответствующему модулю робота и остановит выполнение программы, пока не будет найден свободный робот требуемого класса.

3.3 Вызов функции робота

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

Вызов функции визуально похож на вызов метода объекта в С-подобных языках программирования. Следует указание класса робота, затем через знак указателя -> указывается обозначается требуемая функция, затем в круглых скобках перечисляется список аргументов этой функции.

Cинтаксис вызова функции робота:

robot_класс_робота->функция_робота(аргументы);

Например, из робота класса test нужно вызвать функцию do_something с одним аргументом 1000:

robot_test->do_something(``1000``);

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

Следует отметить, что при таком указании вызова функции робота интерпретатор дождется подтверждения выполнения функции от представления робота и только затем продолжит выполнение остальной программы. Подробнее об флагах выполнения функции рассказано в разделе «Указание режимов выполнения функций».

В зависимости от того, как и где описано исполнение функций робота, их (функции) можно разделить на 3 типа:

  • пользовательские функции – функции, описанные на языке RCML в текущем файле исходного кода программы или во включаемых файлах с исходным кодом на RCML. Код таких функций находится внутри текущей программы и будет скомпилирован в исполняемый файл текущей программы;
  • библиотечные функции – функции уже скомпилированных библиотек RCML, подключаемых к текущей программе, т.е. код таких функций находится вне текущей программы на RCML;
  • системные функции – функции, которые предоставляет в среду RCML модуль робота. Код исполнения таких функций находится в модуле робота или в ПО самого робота.

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

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

Поиск функции производится только по её имени без учета числа параметров. Если найденная функция имеет количество параметров, отличное от количества передаваемых аргументов в вызове функции, то будет выдана ошибка.

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

Однако иногда перед программистом встаёт необходимость использовать именно системную функцию, которая переопределена библиотечной функцией. Данная ситуация возможна, когда библиотечная реализация функции является неэффективной, исходного кода библиотеки нет, а необходимость использовать данную библиотеку есть ввиду наличия в ней других функций. Прямое обращение к системной функции возможно, если указать перед наименованием функции символ точки «.», синтаксис вызова функции будет следующий:

robot_класс_робота->.функция_робота(аргументы);

И рассмотренный пример примет вид:

robot_test->.do_something(`1000`);

3.4 Работа с конкретным экземпляром робота

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

Наиболее эффективно и рационально задействовать робота единожды и передавать ему команды так, как это необходимо, а затем его освободить, таким образом реализовав сеанс работы робота. Для этого требуется задействовать робота нужного класса и запомнить связь с задействованным конкретным роботом. Это можно сделать, сохраняя робота в специальный тип переменных, перед идентификатором которых должен быть поставлен символ «@». Например, задействуем робота класса test и сохраним связь с конкретным полученным экземпляром в переменной @r:

@r = robot_test;

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

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

@r->do_something(1000);

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

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

@r = robot_test;
@r2 = @r;
@r4 = @r3 = @r2;

В результате все переменные @r, @r2, @r3 и @r4 будут указывать на одного и того же робота.

В переменную, уже указывающую на конкретного робота, может быть присвоен указатель на совершенно другого робота, и даже другого класса робота:

@r = robot_test;
@r2 = @r;
@r = robot_tarakan;

Теперь @r указывает на робота класса tarakan, а @r2 на робота класса test. Следует отметить, что при смене робота, на которого указывает переменная, робот, на которого указывала она ранее, не будет освобожден.

3.5 Высвобождение задействованного робота

Робота, «сохраненного в особую переменную», можно освободить, используя специальный оператор delete, когда это потребуется. С данным оператором должна указываться специальная переменная, хранящая указатель на освобождаемого робота. Пример освобождения робота, указатель на которого был ранее присвоен в переменную @r:

delete @r;

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

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

В разделе “Особенности выполнения функций роботами в разных режимах” есть важное дополнение про механизм освобождения задействованного робота.

3.6 Автоматический выбор робота

Одна из возможностей языка RCML— это автоматический подбор робота под задачу. Чтобы использовать данную возможность, нужно указывать только ключевое слово robot вместо конкретного класса робота в тех местах, где требуется указание класса робота: вызовы функций робота или присвоение робота в переменную. Например:

robot->do_something(1000);
@r = robot;
@r->do_something();

Использование только ключевого слова robot вместо полного имени класса робота далее будет называться абстрактным роботом.

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

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

3.7 Использование модуля выбора робота

В способе задействования робота, описанном в разделе “Использование робота в программе”
, выбор конкретного физического робота, а точнее связанного с ним представления робота, производится самим модулем робота, который может ничего не знать о контексте использования задействованного робота, а потому вероятно произвести не самый оптимальный выбор робота-исполнителя. Чаще всего таким способом задействования первый доступный робот, в рамках конкретного модуля роботов, если был указан конкретный требуемый класс роботов. Или если был указан вызов абстрактного робота, то первый свободный робот у первого модуля роботов, имеющего свободных роботов, в порядке перечисления модулей роботов в config.ini.

В процессе работы интерпретатора RCML, если была включена опция записи статистики (см. раздел “Установка и настройка компилятора и интерпретатора RCML”), накапливается статистическая информация о работе роботов и скорости выполнения ими своих функций, подробнее о накапливаемых данных в разделе “Работа с RCML статистикой”. Модули выбора роботов могут иметь доступ к данной информации и на основе её могут принять более рациональное решение по выбору робота.

Подключение модуля, или нескольких модулей выбора указывается в операторе задействования робота в угловых скобках <>, с перечислением модулей выбора через символ запятой. Синтаксис данной конструкции следующий:

robot_класс_робота<список_модулей_выбора>

Пример подключения модуля выбора avg к выбору робота класса tarakan:

robot_tarakan<avg>

В данном случае от модуля робота tarakan будут запрошены все представления свободных физических роботов, список которых будет передан модулю выбора avg. Из переданного списка модуль выбора выберет одного наиболее подходящего робота, который и будет задействован. Алгоритм выбора определяется модулем выбора.

Следует отметить, что выбор робота с помощью модуля выбора обычно происходит дольше чем без него, т.к. модулю требуется выполнить дополнительные действия связанные с принятием решения (запросы к БД статистики и пр.).

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

Указание модуля выбора возможно и при задействовании абстрактного робота. Например, указание модуля статистики avg, с абстрактным роботом:

robot<avg>

В данном случае RCML получит список всех представлений свободных роботов, от всех подходящих под данный вызов робота модулей роботов. Из списка представлений роботов также будет выбран только один робот конкретного класса.

В случае если при задействовании робота было указано несколько модулей статистики. Например:

robot_tarakan<test1, test2, test3>

В данном случае каждому модулю выбора будет передан список представлений свободных роботов, из которого каждый модуль выбирает одного робота. В данном случае порядок указания модулей выбора определяет очерёдность принятия решения модулями, а также приоритетность выбранного робота конкретным модулем выбора, которую можно выразить в баллах. Первый указанный модуль выбора первым производит выбор робота и выбранный им робот будет иметь максимальный приоритет по баллам равный количеству указанных модулей выбора. Соответственно, последний указанный модуль принимает решение последним и выбранный им робот будет иметь приоритет равный одному баллу. Если модули выбора выберут из списка свободных роботов разных роботов для задействования, то будет задействован робот, набравший максимальное число баллов, т.е. робот с максимальным приоритетом. В случае если у выбранных для задействования роботов одинаковое количество баллов, то будет выбран робот, выбранный первым модулем выбора.

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


4 Режим ручного управления

4.1 Общие сведения

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

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

Рисунок 5 Пример осей робота и управляющего устройства

В данном примере есть гусеничный робот (изображен слева), который может переходить в свое новое абсолютное положение на плоскости посредством ряда изменений своего положения по двум осям: оси передвижения R (вперед или назад) и оси вращения A (влево или право). И есть простое управляющее устройство по типу джойстика (изображено справа), которое может отклоняться в плоскости от своего начального положения по двум осям X и Y. Соответственно, через RCML возможно связать оси джойстика и робота так, чтобы отклонение джойстика приводило к движению робота. Например, отклонение джойстика по оси Y в положительную сторону приводило к движению вперед, а отклонение джойстика по оси X в отрицательную сторону приводило к повороту робота влево. Предположим, что данный робот представлен в среде RCML модулем tarakan, а джойстик, соответственно, модулем управления joy, тогда RCML код для их связи в режиме ручного управления для получения эффекта, приведенного в примере, будет следующим:

@r = robot_tarakan;
hand_control(@r, “joy”, “R”, “Y”, “A”, “Y”);

4.2 Принцип передачи значений

Таким образом, ось управляющего устройства является источником значений, ось робота — приемником, а сам робот — исполняющим устройством. Разумеется, масштабы или диапазоны значений осей робота и управляющего устройства могут не совпадать. Среда RCML автоматически производит приведение значения, получаемого от оси управляющего устройства, в диапазон значений для оси робота, соблюдая соотношение.

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

Данный принцип может быть расширен не только на реальные оси передвижения робота или его манипулятора, а на любые его функции, которые так или иначе могут принимать значения и менять состояние робота в режиме реального времени (в рамках возможностей ОС), например, включение световой индикации, блокировка передвижений робота и т.д. Единственное требование - они должны быть задекларированы в модуле робота и в документации к нему.

Дополнительно возможно создание так называемых дискретных осей, которые принимают небольшой диапазон значений, и каждое значение может соответствовать какому-либо состоянию робота или его механизма. Частный случай этого - «бинарные» оси, продуцирующие или принимающие значения 0 или 1, по аналогии с «вкл» или «выкл». Ярким примером тут может быть обычная компьютерная клавиатура, имеющая 101 клавишу и соответственно 101 бинарную ось. У робота такой осью может быть включение ламп и огней, или издание какого-либо звука, или включение режима голосовой связи. Вариации ограничиваются лишь фантазией разработчиков роботов и управляющих устройств, а также программистов на языке RCML, которые могут связывать оси тех или других устройств в произвольном порядке. Благодаря приведению значений к целым числам возможно связывание бинарных осей как робота, так и управляющего устройства с «небинарными» осями того или иного устройства.

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

4.3 Рекомендации по выбору осей робота для режима ручного управления

Данный раздел адресован разработчикам роботов и разработчикам модулей роботов для среды RCML. При выборе осей робота, для которых предоставляется управление, следует помнить о так называемой проблеме «альфа-5», справедливой для промышленных роботов, использующих шестиосевую кинематику, когда для незначительного перехода в декартовых координатах робот выполняет значительный переход в координатах осей двигателей. Причем часто это наиболее справедливо для двигателя пятой оси робота, откуда и следует название. Однако принцип этой проблемы может быть распространен на довольно широкий диапазон роботов, в том числе даже на такого простого гусеничного робота, рассмотренного ранее.

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

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

В приведенном примере с гусеничным роботом данный принцип соблюдается. Однако если его нарушить и выбрать осями робота, например, стандартные декартовые оси пространства X и Y, описывающие плоскость, в которой передвигается робот, см. рисунок 6, то можно получить следующий ряд негативных эффектов

Рисунок 6 Пример выбора осей робота

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

Дополнительно выбор таких осей перемещения для робота ведет к неопределенности, поскольку в новое положение можно перейти разными путями:

  • повернуть вправо на четверть оборота и проехать вперед;
  • повернуть влево на три четверти оборота и проехать назад.

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


5 Написание собственных новых функций для роботов

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

5.1 Написание функции для конкретного класса роботов

Задание новой функции робота начинается с ключевого слова function, далее через разделитель следует наименование класса робота, для которого создается новая функция, затем следует знак «::», затем следует имя новой функции и потом в круглых скобках список параметров, который может быть пустым. После списка параметров в фигурных скобках следует код функции – список её операторов. Синтаксис задания функции робота:

function класс_робота::имя_функции(список_параметров) {
    список_операторов
}

Пример задания новой функции sum для типа робота класса test:

function robot_test::sum(a, b) {
    c = a + b;
    robot->do_something(c);
    return c;
}

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

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

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

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

В функции робота можно менять режимы выполнения других функций робота обычными способами. Подробнее об режимах выполнения функций и способах их изменения читайте в разделе «Указание режимов выполнения функций».

5.2 Написание функции для нескольких классов роботов

Задание новой функции сразу для нескольких классов роботов аналогично заданию новой функции для одного робота, за исключением того, что требуемые классы роботов указываются через запятую. Пример задания функции сразу для двух классов роботов test и tarakan:

function robot_test, robot_tarakan::sum(a, b) {

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


6 Указание режимов выполнения функций

Исполнение функций, написанных на языке RCML, может выполняться в двух основных режимах:

  • с ожиданием выполнения функции – в данном случае интерпретатор, встретив команду вызова функции, просто переходит на начало вызываемой функции и последовательно выполняет её код;
  • без ожидания выполнения функции – в данном случае запускается дочерний поток, который начинает выполнять код вызываемой функции. Исполнение функции, в которой произошел вызов, продолжается с места следующим за вызовом.

В случае выполнения функции «без ожидания», создаваемый поток может быть перенесен в отдельное вычислительное ядро средствами ОС, и таким образом может быть получен эффект параллельного выполнения кода на языке RCML.

По умолчанию все функции вызываются в режиме с ожиданием выполнения функции. Этот режим является режимом по умолчанию. Сменить режим выполнения функций можно несколькими способами.

К первому способу относится применение флагов режима, каждый из которых является одним символом и которых, так же, как и режимов, всего два:

  • .# - флаг выполнения функции с ожиданием.
  • ~ - флаг выполнения функции без ожидания.

Флаг режима должен указываться при вызове функции перед указанием имени функции. Примеры использования:

~do_something(1000);
#do_anything(1000);

Ко второму способу изменения режима выполнения функции относится использование системной функции set с указанием в качестве первого параметра строковой константы “behavior”, а в качестве второго параметра - флага режима # или ~. Вызов данной функции с такими параметрами переопределяет режим выполнения функций по умолчанию, то есть, если флаг режима не будет указан явно в вызове функции, то функция будет выполняться в режиме, заданном вторым параметром функции set. Пример использования функции set:

set(“behavior”,~);
//все последующие вызовы функций будут выполняться
//без ожидания завершения
do_something(1000);
do_something(1000);
do_something(1000);
//режим не меняется, так как флаг совпадает со значением по умолчанию
~do_something(1000);
//явное изменение режима, но только для этого конкретного вызова функции
#do_something(1000);

Следует отметить, что функция не может быть вызвана в режиме без ожидания выполнения, если результат её вызова где-либо используется, например, сохраняется в переменную или участвует в выражении. В данном случае такая функция будет принудительно вызвана в режиме с ожиданием выполнения, независимо от настроек режима по умолчанию. Простановка же флага режима перед вызовом функции, результат которой используется, запрещена на уровне синтаксических правил, т.е. при попытке выполнения следующего примера компилятор выдаст ошибку:

function sum(a, b) {
    return a+b;
}
function main() {
    r = ~sum(1,2);
}

Выполнение системных функций (из модуля system) кроме функции send_package, о которой будет подробнее рассказано в следующем разделе “Особенности выполнения функций роботами в разных режимах”, также всегда происходит в режиме «с ожиданием выполнения» и игнорированием флагов режима перед вызовом функции.

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


7 Особенности выполнения функций роботами в разных режимах

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

  • с ожиданием выполнения функции – при вызове функции робота интерпретатор RCML помещает команду в очередь команд представления робота и ожидает от него ответ об успешности или не успешности выполнения именно этой команды;
  • без ожидания выполнения функции – в данном режиме интерпретатор RCML также помещает команду в очередь команд представления робота, но не дожидается сообщения о её выполнении и продолжает выполнение основной программы.

Очевидно, что режимы выполнения функций роботом совпадают с режимами выполнения функций в среде RCML, поэтому управлять режимом исполнения функций роботом можно теми же средствами, что и для управления режимом исполнения функций средой RCML.

Пример использования флагов режима:

~robot_test->do_something(1000);
 #robot_test->do_something(1000);
@r = robot_test;
~@r->do_something(1000);
#@r->do_something(1000);

Пример использования системной функции set:

@r = robot_test;
set(“behavior”,~);
//все последующие вызовы функций робота будут выполняться
//без ожидания завершения
@r->do_something(1000);
@r->do_something(1000);
@r->do_something(1000);
//режим не меняется, так как флаг совпадает со значением по умолчанию
~@r->do_something(1000);
//явное изменение режима, но только для этого конкретного вызова функции
#@r->do_something(1000);

Следует отметить, что вполне возможна ситуация, когда очередь команд представления робота заполнена командами без ожидания выполнения, и при этом поступает команда с ожиданием выполнения. В таком случае интерпретатор RCML дождется сначала выполнения всех команд, предшествующих команде с ожиданием выполнения, а затем выполнения и этой команды, и только потом продолжит выполнение основной программы.

По аналогии с вызовом обычных функций, функция робота не может быть вызвана в режиме без ожидания выполнения, если результат её вызова где-либо используется.

Оператор освобождения робота, как и вызов функции робота, представляет собой передачу команды роботу, только уже системного типа – на освобождение. Поэтому к оператору освобождения робота тоже применим весь механизм флагов выполнения:

~delete @r;
#delete @r;

Ранее в разделе “Высвобождение задействованного робота”, отмечалось, что при достижении конца функции интерпретатором роботы, использованные в ней и не освобожденные через оператор delete, будут освобождены интерпретатором автоматически. Такое освобождение робота тождественно передаче роботу команды на освобождение с ожиданием выполнения, т.е. интерпретатор при достижении конца функции дождется завершения выполнения всех команд всеми задействованными в этой функции роботами. Чтобы интерпретатор не дожидался завершения выполнения роботом всех переданных ему (роботу) команд, нужно явно передать команду роботу на освобождение без ожидания её выполнения. Такое действие как бы вычеркивает робота из списка роботов, освобождения которых нужно дождаться. Однако при достижении конца исполняемой программы, интерпретатор, прежде чем завершить свою работу, дождется освобождения всех роботов, независимо от того, в каком режиме они выполняют свои функции.


8 Пакетная передача команд роботам

Как отмечалось ранее, у представления робота в среде RCML есть очередь команд, которая наполняется командами путем вызова функций робота из кода на RCML. При поступлении команды в пустую очередь она (команда) почти сразу же будет передана роботу на исполнение. Пока исполняется первая команда, все вновь поступившие команды становятся в очередь. Такое происходит потому, что робот выполняет функцию в материальном мире обычно медленнее, чем RCML интерпретатор успевает выполнить очередной код на RCML и дойти до следующего вызова функции робота, т.е. обычно действия робота “медлительнее” действий вычислительного процессора.

Однако бывает иная ситуация, когда роботу необходимо выполнить серию быстрых перемещений без остановки, а расчет параметров этих перемещений занимает больше времени, чем выполняется перемещение. Тогда эффективнее заранее рассчитать параметры перемещений и передать роботу сразу пакет команд с рассчитанными параметрами, чтобы робот не «подвисал» в ожидании очередной команды. Бывают ситуации, что сам механизм вызова функций работает медленнее, чем робот исполняет команды, и возникает задержка в быстрой серии перемещений.

Чтобы скомпенсировать этот эффект, был введен механизм пакетной передачи команд роботу. Команды, получаемые посредством вызовов функций робота, можно скомпоновать в единый пакет и передать его целиком представлению робота. Чтобы отправить команду в пакет, нужно поставить перед вызовом функции символ «>». Чтобы отправить пакет на выполнение, нужно вызвать системную функцию send_package().

Пример:

//отправка команды в пакет
>robot_test->do_something(1000);
//отправка пакета на выполнение
system.send_package();

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

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

@r = robot_tarakan;
>@r->move(1, 100);
>@r->move(1, 100);
>@r->move(1, 100);
system.send_package();

После выполнения пакета команд кода, робот, связанный с переменной @r, не будет освобожден. Его можно будет использовать далее и освободить явно через оператор delete, или неявно и автоматически при достижении конца функции интерпретатором. Однако команду освобождения робота также можно включить в пакет:

@r = robot_tarakan;
>@r->move(1, 100);
>@r->move(1, 100);
>@r->move(1, 100);
>delete @r;
system.send_package();

Теперь робот будет освобожден в ходе выполнения пакета, и в коде после вызова функции send_package, робот не будет более доступен.

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

>robot_test->do_something(1000);
@r = robot_test;
>@r->do_something(1000);
>delete @r;
system.send_package();

Другой пример:

>robot_test->do_something(1000);
>robot_test->do_something(1000);
system.send_package();

Согласно данному примеру, будут задействованы два робота одновременно, а не один и тот же два раза подряд. В таком случае выполнение первых команд в очереди каждого представления робота начнётся одновременно.

Данный механизм позволяет синхронизировать начало выполнения разными роботами своих команд, в отдельных случаях добиться синхронизации действий (в рамках возможностей используемого робототехнического комплекса, включая ОС и прочее ПО). Например:

@r1 = robot_test;
@r2 = robot_test;
>@r1->do_something(1000);
>@r2->do_something(1000);
system.send_package();
>@r1->do_anything(1000);
>@r2->do_anything(1000);
system.send_package();

В каждом пакете содержится по одной функции для каждого робота, интерпретатор отправляет команды на выполнение одновременно и ждет завершения выполнения команд обоими представлениями роботов перед сбором команд в следующий пакет.

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

Важно отметить, что пакет команд существует только в контексте текущей функции, т.е. у каждой функции он свой:

function foo() {
    >robot_test->do_something(1000);
    >robot_test->do_something(1000);
}
function main() {
    foo();
    system.send_package();
}

В ходе выполнения данного примера команды, помещаемые в пакет в функции foo, никогда не будут выполнены, т.к. пакет будет уничтожен с завершением выполнения данной функции. А функции main будет отправлен пустой пакет. При этом два робота будут задействованы и до конца программы будут бездействовать, т.к. команды на их освобождение были помещены в пакет, который не был исполнен. Это очередной пример утечки роботов на RCML.

Ранее отмечалось, что функция send_package принимает один параметр, это флаг режима выполнения, который указывает интерпретатору, как выполнить данный пакет:
* .# - интерпретатор дождется выполнения всего пакета команд:

system.send_package(#);
  • ~ - интерпретатор не будет ждать выполнения пакета команд и сразу после отправки пакета продолжит выполнение кода текущей функции:
system.send_package(~);

Дополнительно функция send_package единственная системная функция, которая учитывает флаг выполнения перед её вызовом, в том числе установленный флаг выполнения по умолчанию:

// складываем команду в пакет
>robot_test->do_something(1000);
// отправляем пакет без ожидания завершения
~system.send_package();
// формируем новый пакет, хотя предыдущий ещё выполняется
>robot_test->do_something(1000);
// меняем флаг выполнения по умолчанию
system.set(“behavior”, ~);
// снова отправляем пакет без ожидания, уже два пакета в работе
system.send_package();
// формируем очередной пакет
>robot_test->do_something(1000);
// а теперь отправляем пакет с ожиданием его выполнения
system.send_package(#);

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


9 Коммуникация с ОС

9.1 Передача параметров в программу на RCML

В RCML программу можно передавать данные из ОС через параметры командной строки, однако таким образом на данном этапе развития языка передаются только вещественные типы данных. Чтобы иметь доступ к переданным через командную строку параметрам в коде RCML, нужно в функции main добавить необходимые параметры.

Передача значений в эти параметры функции main осуществляет путем добавления значения параметра в команду вызова RCML интерпретатора после указания файла с PC кодом. Общий вид передачи параметра со значением следующий:

-Pимя_параметра=значение_параметра

В качестве примера был создан файл test.rcml со следующим содержимым:

function main(foo, bar) {
    sum = foo + bar;
    system.echo(“foo + bar = ”, sum, “\n”);
}

Команда компиляции RCML файла:

rcml_compiler test.rcml test.pc

Команда выполнения RCML программы с передачей значения параметру foo равным 1, и bar равным 3.5:

rcml_interpreter test –Pfoo=1 –Pbar=3.5

Результатом выполнения программы будет:

foo + bar = 4.5

При этом порядок передачи значений параметров не важен, т.к. они передаются по имени. Параметры, значения для которых не были переданы, будут равны 0.0.

При выполнении следующего примера:

rcml_interpreter test –Pfoo=1

Будет получен результат:

foo + bar = 1

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

Следующий пример содержит ошибку, т.к. параметра err не существует:

rcml_interpreter test –Pfoo=1 –Perr=2.35

9.2 Передача значений из программы из RCML в ОС

Передача данных из RCML в ОС возможна двумя путями:

9.3 Передача исключений (ошибки) из программы в ОС

Передача исключений (ошибки) происходит посредством выброса исключения без его перехвата до самого верхнего уровня, т.е. до функции main включительно. При этом, произойдет завершение работы RCML программы с кодом 1, значение исключения передано не будет. О механизме обработки и передачи исключений подробнее в разделе “Исключения”.


10 Создание файла библиотеки RCML

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

Самой первой строкой должен быть указан идентификатор интерфейса библиотеки (IID) - уникальный набор байт длиной 32 байта, характеризующий версию интерфейса библиотеки, т.е. текущий набор функций, которые предоставляет библиотека, и их параметров. Данный IID должен быть уникален, т.к. интерпретатор различает разные библиотеки только по данному параметру. Для придания большей уникальности рекомендуется использовать все 32 байта допустимой длины. Прочие байты, следующие по счету за 32-м, будут отброшены.

Синтаксис:

IID “идентификатор_интерфейса”

Пример названия идентификатора библиотеки:

IID “test_lib_v1.0”

При написании функций библиотеки программист может указать, какие из них следует экспортировать, т.е. делать доступными для вызова из других программ, как в разделе «Вызов функции робота». Пометить функцию для экспорта можно, проставляя ключевое слово export перед ключевым словом function:

export function имя_функции(список_параметров) {
    список_операторов
}

Экспорт функций роботов происходит аналогично:

export function класс_робота::имя_функции(список_параметров) {
        список_операторов
}

Пример листинга библиотеки:

IID "test_lib_v1.0"
export function sum(a,b){
    c = a + b;
    return c;
}
export function robot_test::user_function(s) {
    robot->print("Robot delay\n", 1000);
    robot->print("Robot Func All right\n", 0);
}

Следует помнить, что включение библиотеки в другую программу создает в этой программе зависимость от всех использованных во включаемой библиотеке модулей и прочих библиотек. Т.е. для исполнения такой программы интерпретатору потребуются модули и библиотеки с таким же IID, которые были использованы при её (программы) компиляции. Зависимость от IID не есть зависимость от конкретного файла модуля или библиотеки. Библиотека или модуль может быть изменена, улучшена или ухудшена, но если набор функций и их параметров, экспортируемых в среду RCML (т.е. IID), остался неизменным, то такая библиотека считается равной исходной. Подробнее об механизме IID в разделе “Подробнее об интерфейсных идентификаторах”.


11 Перечень зарезервированных ключевых слов

break catch continue define delete else
exit export function if IID include
include_lib loop return robot throw try

12 Синтаксическая карта языка RCML

ПРОГРАММА: IID ЗАГОЛОВКИ

IID:
|   IID_KEYWORD СТРОКА

ЗАГОЛОВКИ:
|   ЗАГОЛОВКИ INCLUDE
|   ЗАГОЛОВКИ DEFINE 

INCLUDE:
    include СТРОКА
    include_lib ИДЕНТИФИКАТОР СТРОКА

ПРОГРАММА: ФУНКЦИИ

ФУНКЦИИ:
    ФУНКЦИИ ФУНКЦИЯ

ФУНКЦИЯ:   ФЛАГ_ЭКСПОРТА function СПИСОК_РОБОТОВ :: ИДЕНТИФИКАТОР '(' ЛИСТ_ИДЕНТИФИКАТОРОВ ')' БЛОК_КОДА
|          ФЛАГ_ЭКСПОРТА function ИДЕНТИФИКАТОР '(' ЛИСТ_ИДЕНТИФИКАТОРОВ ')' БЛОК_КОДА

ФЛАГ_ЭКСПОРТА:
    export

СПИСОК_РОБОТОВ: ROBOT_ID
    СПИСОК_РОБОТОВ ',' ROBOT_ID

ЛИСТ_ИДЕНТИФИКАТОРОВ:
|   ИДЕНТИФИКАТОР
|   ЛИСТ_ИДЕНТИФИКАТОРОВ ',' ИДЕНТИФИКАТОР

БЛОК_КОДА: '{' ОПЕРАТОРЫ '}'

ОПЕРАТОРЫ: ОПЕРАТОР
|   ОПЕРАТОРЫ ОПЕРАТОР

ОПЕРАТОР: OPER ';'
|   if '(' ВЫРАЖЕНИЕ ')' БЛОК_КОДА
|   if '(' ВЫРАЖЕНИЕ ')' БЛОК_КОДА else БЛОК_КОДА
|   loop БЛОК_КОДА
|   try БЛОК_КОДА
|   try БЛОК_АРГУМЕНТОВ БЛОК_КОДА
|   try БЛОК_КОДА catch БЛОК_КОДА
|   try БЛОК_АРГУМЕНТОВ БЛОК_КОДА catch БЛОК_КОДА
|   try БЛОК_КОДА catch '(' ИДЕНТИФИКАТОР ')' БЛОК_КОДА
|   try БЛОК_АРГУМЕНТОВ БЛОК_КОДА catch '(' ИДЕНТИФИКАТОР ')' БЛОК_КОДА

OPER:   ФЛАГ_РЕЖИМА_ВЫПОЛНЕНИЯ ССЫЛКА_НА_РОБОТА -> ИДЕНТИФИКАТОР БЛОК_АРГУМЕНТОВ
|   ФЛАГ_РЕЖИМА_ВЫПОЛНЕНИЯ ССЫЛКА_НА_РОБОТА -> '.' ИДЕНТИФИКАТОР БЛОК_АРГУМЕНТОВ
|   ФЛАГ_РЕЖИМА_ВЫПОЛНЕНИЯ ИДЕНТИФИКАТОР БЛОК_АРГУМЕНТОВ
|   ФЛАГ_РЕЖИМА_ВЫПОЛНЕНИЯ ИДЕНТИФИКАТОР '.' ИДЕНТИФИКАТОР БЛОК_АРГУМЕНТОВ
|   ССЫЛКА_НА_РОБОТА -> ИДЕНТИФИКАТОР БЛОК_АРГУМЕНТОВ
|   ССЫЛКА_НА_РОБОТА -> '.' ИДЕНТИФИКАТОР БЛОК_АРГУМЕНТОВ
|   ИДЕНТИФИКАТОР БЛОК_АРГУМЕНТОВ
|   ИДЕНТИФИКАТОР '.' ИДЕНТИФИКАТОР БЛОК_АРГУМЕНТОВ
|   ВЫРАЖЕНИЕ
|   break
|   continue
|   exit
|   exit ВЫРАЖЕНИЕ
|   return
|   return ВЫРАЖЕНИЕ
|   delete ПЕРЕМЕННАЯ_РОБОТА
|   ФЛАГ_РЕЖИМА_ВЫПОЛНЕНИЯ delete ПЕРЕМЕННАЯ_РОБОТА
|   throw
|   throw ВЫРАЖЕНИЕ
|   ПРИСВОЕНИЕ_РОБОТА

ПРИСВОЕНИЕ_РОБОТА: ПЕРЕМЕННАЯ_РОБОТА '=' ПРИСВОЕНИЕ_РОБОТА
|   ПЕРЕМЕННАЯ_РОБОТА '=' ССЫЛКА_НА_РОБОТА

ВЫРАЖЕНИЕ:   ВЫРАЖЕНИЕ1
|   ИДЕНТИФИКАТОР '=' ВЫРАЖЕНИЕ

ВЫРАЖЕНИЕ1:  ВЫРАЖЕНИЕ2
|   ВЫРАЖЕНИЕ1 == ВЫРАЖЕНИЕ2
|   ВЫРАЖЕНИЕ1 <= ВЫРАЖЕНИЕ2
|   ВЫРАЖЕНИЕ1 >= ВЫРАЖЕНИЕ2
|   ВЫРАЖЕНИЕ1 != ВЫРАЖЕНИЕ2
|   ВЫРАЖЕНИЕ1 '>' ВЫРАЖЕНИЕ2
|   ВЫРАЖЕНИЕ1 '<' ВЫРАЖЕНИЕ2
|   ВЫРАЖЕНИЕ1 && ВЫРАЖЕНИЕ2
|   ВЫРАЖЕНИЕ1 || ВЫРАЖЕНИЕ2

ВЫРАЖЕНИЕ2:  ТЕРМ
|   ВЫРАЖЕНИЕ2 '+' ТЕРМ
|   ВЫРАЖЕНИЕ2 '-' ТЕРМ

ТЕРМ:   ЗНАЧЕНИЕ
|   ТЕРМ '*' ЗНАЧЕНИЕ
|   ТЕРМ '/' ЗНАЧЕНИЕ
|   ТЕРМ '%' ЗНАЧЕНИЕ

ЗНАЧЕНИЕ:    NUM
|   '-' ЗНАЧЕНИЕ
|   '!' ЗНАЧЕНИЕ
|   '(' ВЫРАЖЕНИЕ1 ')'
|   ИДЕНТИФИКАТОР
|   ИДЕНТИФИКАТОР БЛОК_АРГУМЕНТОВ
|   ИДЕНТИФИКАТОР '.' ИДЕНТИФИКАТОР БЛОК_АРГУМЕНТОВ
|   ССЫЛКА_НА_РОБОТА -> ИДЕНТИФИКАТОР БЛОК_АРГУМЕНТОВ
|   ССЫЛКА_НА_РОБОТА -> '.' ИДЕНТИФИКАТОР БЛОК_АРГУМЕНТОВ

ССЫЛКА_НА_РОБОТА: ПЕРЕМЕННАЯ_РОБОТА
|   ROBOT_ID МАРКЕР_ВЫБОРА
|   robot МАРКЕР_ВЫБОРА

МАРКЕР_ВЫБОРА:
|   '<' ЛИСТ_ИДЕНТИФИКАТОРОВ '>'

ФЛАГ_РЕЖИМА_ВЫПОЛНЕНИЯ:  #
|   ~
|   '>'

БЛОК_АРГУМЕНТОВ: '(' АРГУМЕНТЫ ')'

АРГУМЕНТЫ:
|   АРГУМЕНТ
|   АРГУМЕНТЫ ',' АРГУМЕНТ

АРГУМЕНТ:   ВЫРАЖЕНИЕ
|   СТРОКА
|   ФЛАГ_РЕЖИМА_ВЫПОЛНЕНИЯ
|   ССЫЛКА_НА_РОБОТА

13 Создание собственных модулей для языка RCML

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

https://github.com/RobotControlTechnologies/module_headers

Все типы модулей в среде RCML создаются посредством динамически подключаемых библиотек (dll файлы для ОС семейства Windows, или so файлы для ОС семейства Linux), через них реализуются различные API взаимодействия с роботом, которые описаны ниже.

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

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

Библиотека модуля, независимо от типа модуля, должна экспортировать всего две функции:

  • функцию, возвращающую версию API поддерживаемого модулем;
  • функцию, возвращающую указатель на потомка абстрактного класса соответствующего типа модуля.
    Однако имена этих функций, как и возвращаемый результат, зависят от типа модуля. Данные функции не принимают параметров, и их имена должны быть строго определенными.

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

13.1 Модуль робота

Как отмечалось ранее, чтобы иметь возможность передачи реальному физическому роботу команд посредством языка RCML, нужно, чтобы этот робот был представлен в среде RCML посредством модуля робота.

Библиотека модуля робота должна экспортировать следующие функции:

unsigned short getRobotModuleApiVersion();
RobotModule* getRobotModuleObject();

Функция getRobotModuleApiVersion возвращает в виде положительного целого числа версию используемого API модуля робота в данном конкретном модуле. Лучше всего определить данную функцию следующим кодом:

unsigned short getRobotModuleApiVersion() { return ROBOT_MODULE_API_VERSION; }

Константа ROBOT_MODULE_API_VERSION определена в подключаемом файле robot_module.h, который необходим при компиляции модуля робота. Таким образом можно не заботиться о проставлении версии вручную, и она будет соответствовать используемому интерфейсу модуля.

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

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

Описание класса RobotModule на языке C++:

class RobotModule {
 protected:
  RobotModule() {}

 public:
  // init
  virtual const struct ModuleInfo& getModuleInfo() = 0;
  virtual void prepare(colorPrintfModule_t *colorPrintf_p, colorPrintfModuleVA_t *colorPrintfVA_p) = 0;

  // compiler only
  virtual FunctionData **getFunctions(unsigned int *count_functions) = 0;
  virtual AxisData **getAxis(unsigned int *count_axis) = 0;
  virtual void *writePC(unsigned int *buffer_length) = 0;

  // intepreter - devices
  virtual int init() = 0;
  virtual void final() = 0;

  // intepreter - program & lib
  virtual int readPC(int pc_index, void *buffer, unsigned int buffer_length) = 0;

  // intepreter - program
  virtual int startProgram(int run_index, int pc_index) = 0;
  virtual AviableRobotsResult *getAviableRobots() = 0;
  virtual Robot *robotRequire(Robot *robot) = 0;
  virtual void robotFree(Robot *robot) = 0;
  virtual int endProgram(int run_index) = 0;

  // destructor
  virtual void destroy() = 0;
  virtual ~RobotModule() {}
};

Метод getModuleInfo должен возвращать структуру описывающую данный модуль. Описание данной структуры приведено ниже. Данный метод может вызываться множество раз за один сеанс работы.

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

Функции, на которые передаются указатели, имеют следующее описание:

void colorPrintfFromModuleVA(void *module, ConsoleColor colors, const char *mask, va_list args);
void colorPrintfFromModule(void *module, ConsoleColor colors, const char *mask,...);

В качестве параметра module должен быть передан указатель на текущий экземпляр модуля робота, т.е. this. В качестве параметра colors должна быть передана структура ConsoleColor, этот параметр задает цвет вывода в консоли. Третий параметр mask задает маску форматирования, по аналогии с функцией printf из стандартной библиотеки C++. Далее в зависимости от функции вывода следует, согласно заданной маске форматирования, обычное перечисление параметров или перечисление параметров в виде структуры va_list из std_arg.h.

Метод getFunctions вызывается только компилятором, должен возвращать указатель на массив указателей на структуры FunctionData, описывающие каждую доступную функцию робота. Причем данный метод принимает по ссылке один параметр count_functions, в который должно быть записано количество элементов массива с указателями на FunctionData, подробнее см. ниже. В случае если модуль робота не предоставляет никаких функций, то данный метод должен вернуть NULL в качестве указателя, а в параметр count_functions записать 0.

Метод getAxis также вызывается только компилятором, должен возвращать указатель на массив указателей на структуры AxisData, описывающие каждую доступную ось управления робота для режима ручного управления. Данный метод, по аналогии с предыдущим, принимает по ссылке один параметр count_axis, в который должно быть записано количество элементов массива с указателями на AxisData, подробнее см. ниже. В случае если модуль робота не поддерживает режима ручного управления, данный метод должен вернуть NULL в качестве указателя, а в параметр count_axis записать 0.

Метод writePC вызывается только компилятором, данный метод позволяет записать модулю в файл компилируемой программы произвольные данные, указатель на область которых должен быть возвращён методом в качестве результата, а длина данных записана в параметр buffer_length, передаваемый по ссылке.

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

Метод final служит для выполнения модулем робота процедур, необходимых для корректного окончания работы, например, сохранение каких-то промежуточных значений, выключение роботов и т.д. Данный метод, как и метод init, вызывается только интерпретатором единожды при завершении выполнения основной программы.

Метод readPC вызывается только интерпретатором при загрузке очередной программы или библиотеки в память, если в ней имеются данные записанные данным модулем, то в параметре buffer будет передан указатель на область этих данных, а в параметре buffer_length будет передан размер этих данных. В параметре pc_index указывается уникальный индекс загружаемого PCode файла (этот индекс используется в других методах, см. далее). Если данных записанных модулем в программе нет, то данный метод не будет вызван.

Метод startProgram вызывается только интерпретатором перед началом выполнения очередной программы на языке RCML, в качестве параметра run_index будет передано уникальное число – идентификатор запуска программы, а в параметре pc_index уникальный индекс загруженного PCode файла, из которого выполняется программа. Файл может быть загружен единожды, но программа в нем может запускаться на выполнение несколько раз подряд и даже в параллельном исполнении. В случае если данный метод возвращает значение 0, данная программа будет передана на выполнение. В противном случае, получая любое другое значение, отличное от 0, среда RCML не будет выполнять данную программу и выдаст ошибку с сообщением о том, что данный модуль запрещает запуск текущей RCML программы. Данный метод позволяет запретить выполнение программы на основе данных полученных в методе readPC.

Метод getAviableRobots возвращает указатель на структуру AviableRobotsResult содержащую в себе информацию о всех свободных роботах данного модуля на момент запроса.

Метод robotRequire вызывается только интерпретатором в той точке выполнения программы, когда возникла необходимость задействовать робота какого-либо класса, сопоставленного данному модулю робота. В параметре robot указывается указатель, полученный ранее от метода getAviableRobots, на экземпляр класса, унаследованного от абстрактного класса Robot, сопоставленного конкретному физическому запрашиваемому роботу. Модуль робота должен проверить что запрашиваемый робот свободен, и если это действительно так, задействовать запрашиваемого робота и вернуть указатель на него, в противном случае вернуть NULL, что означает что данный робот не может быть задействован. В случае если параметр robot равен NULL, то модуль робота может вернуть любого свободного робота или NULL если свободных роботов нет.

Важно! Следует отметить, что данный метод не должен задерживать выполнение с целью ожидания освобождения робота, это будет сделано внутренними механизмами среды RCML.

Метод robotFree вызывается только интерпретатором, когда конкретный задействованный робот перестал быть нужным конкретной программе и его можно освободить. В качестве параметра будет передан указатель, который был получен ранее от метода robotRequire. Гарантируется, что данный указатель не будет изменен в процессе выполнения программы.

Метод endProgram также вызывается только интерпретатором после завершения выполнения программы на RCML, в качестве параметра run_index будет передан тоn же самый идентификатор запуска программы, характеризующий данную программу, который был передан при вызове метода startProgram. В случае если данный метод возвращает значение 0, считается, что данная программа отработала без ошибок, и код её завершения изменен не будет. При возвращении любого другого числа считается, что программа отработала с ошибками, и код её завершения будет отличным от 0. Данный метод позволяет модулю при завершении программы сообщить, что работа данной программы с данным модулем не была корректна и в ходе её работы были ошибки.

Метод destroy является алиасом деструктора модуля робота, в нём должны происходить вызов деструктора объекта модуля робота, если это необходимо, и освобождение занятых ресурсов, так как среда RCML не вызывает метод деструктора из-за особенностей работы кода, созданного различными компиляторами. Данный метод вызывается компилятором и интерпретатором единожды при завершении работы, перед тем как будет выгружен модуль из памяти конкретным экземпляром интерпретатора или компилятора (через вызов функции FreeLibrary).

Следует помнить, что возможна работа одновременно нескольких экземпляров интерпретаторов и/или компиляторов, решающих разные задачи, поэтому каждый экземпляр вызовет функцию getRobotModuleObject из файла модуля, и, соответственно, методы init, final, destroy и другие в процессе своей работы. Причем, учитывая нюансы многопоточности, следует отметить, что их вызов может быть одновременным по времени

Поэтому модуль робота должен быть потокобезопасным во всех отношениях!

При реализации модуля робота рекомендуется применить паттерн проектирования singletone, учитывая, что обращения к методам модуля робота может быть из разных потоков и даже одновременно.

Описание ранее упомянутой структуры ModuleInfo, описывающей модуль:

struct ModuleInfo {
  char *iid;
  enum Modes { PROD, SPEC } mode;
  unsigned short version;
  char *digest;
};

В данной структуре обозначены следующие элементы:

iid – идентификатор интерфейса данного модуля. Данная строка не должна быть длиннее 32 байт. Подробнее см. раздел “Подробнее об интерфейсных идентификаторах”.

mode – тип версии данного файла модуля, может быть PROD – промышленное («продакшен») исполнение, или SPEC – спецификационное исполнение. Промышленное исполнение предполагает полноценный файл модуля, декларирующий функции модуля и содержащий код для их выполнения. Спецификационное исполнение предполагает, что файл модуля только декларирует функции модуля в среде RCML, но не имеет в себе кода для их выполнения. Соответственно, промышленный вариант исполнения модуля может быть использован и для компиляции программ на RCML с данным модулем, и для их исполнения, а спецификационный вариант исполнения может быть использован только для компиляции. Интерпретатор не будет работать со спецификационным исполнением модуля. Данный механизм позволяет разработчикам модулей роботов декларировать интерфейс модуля робота, не передавая его основной код. Это может быть полезно при работе с подрядчиками, пишущими ПО под данный модуль. Они могут иметь средства проверки правильности использования интерфейса, но не будут иметь возможности получения реального модуля.

version – отражает текущую версию модуля в виде целого положительного числа, если модуль не планируется распространять через сервис Репозиторий, то данное поле не играет большой роли. Однако в противном случае нужно учитывать тот факт, что Репозиторий требует, чтобы очередная версия загружаемого модуля была выше загруженной ранее.

digest – указатель на цифровую подпись разработчика модуля, используется только сервисом Репозиторий.

Структура FunctionData, служит для описания каждой функции робота доступной для использования в языке RCML. Описание структуры на языке C++:

struct FunctionData {
  enum ParamTypes { STRING, FLOAT };

  system_value command_index;
  unsigned int count_params;
  ParamTypes *params;
  const char *name;
  FunctionData() : command_index(0), count_params(0), params(NULL), name(NULL) {}
  FunctionData(system_value command_index, system_value count_params, ParamTypes *params, const char *name) : command_index(command_index), count_params(count_params), params(params), name(name) {}
};

В данной структуре обозначены следующие элементы:

command_index – уникальный идентификатор команды в рамках модуля, данное значение будет в дальнейшем использоваться и передаваться представлению робота интерпретатором, чтобы указать, какую команду должен выполнить конкретный робот. Идентификатор команды должен быть целым числом больше нуля, значение нуля и отрицательные значения зарезервированы и являются системными;

count_params – количество параметров функции, равное количеству элементов в массиве params;

params – указатель на массив типов параметров, каждый элемент массива должен быть представлен элементом перечисления ParamTypes: STRING – строка или FLOAT – число. Детальное описание механизма передачи параметров см. ниже;

name – указатель на имя функции, которое должен будет указать программист на языке RCML, чтобы вызвать данную функцию.

В свою очередь структура AxisData служит для описания каждой оси управления робота, доступной для использования в системной функции hand_control языка RCML. Описание данной структуры на языке C++:

struct AxisData {
  system_value axis_index;
  variable_value upper_value;
  variable_value lower_value;
  const char *name;
  AxisData() : axis_index(0), upper_value(0), lower_value(0), name(NULL) {}
  AxisData(system_value axis_index, variable_value upper_value, variable_value lower_value, const char *name) : axis_index(axis_index), upper_value(upper_value), lower_value(lower_value), name(name) {}
};

В данной структуре обозначены следующие элементы:

axis_index – уникальный идентификатор оси в рамках модуля, данное значение будет в дальнейшем использоваться и передаваться представлению робота интерпретатором, чтобы указать значение какой оси нужно изменить. По аналогии с уникальными идентификаторами команд робота, идентификатор оси должен быть целым числом больше нуля;

upper_value – верхнее граничное значение, которое можно передать для данной оси;

lower_value –нижнее граничное значение, которое можно передать для данной оси;

name – указатель на имя оси, которое должен будет указать программист на языке RCML в вызове системной функции hand_control, чтобы связать данную ось.

Описание ранее упомянутой структуры AviableRobotsResult, описывающей список свободных модулей:

struct AviableRobotsResult {
  Robot **robots;
  unsigned int count_robots;
  AviableRobotsResult(Robot **robots, unsigned int count_robots) : robots(robots), count_robots(count_robots) {}
  void destroy() { delete this; }
  ~AviableRobotsResult() { delete[] robots; }
};

robots – массив указателей на свободных роботов;

count_robots – количество элементов в массиве robots.

Далее под классом, на экземпляр которого возвращается указатель методом robotRequire, будет пониматься представление робота, причем этот класс должен быть унаследован от абстрактного класса Robot, далее именуемый как абстрактное представление робота. По сути, экземпляр представления робота является тем самым представлением физического робота в среде RCML, которое упоминалось при описании синтаксиса языка и через которое происходит взаимодействие среды RCML и реального конкретного робота. Представление робота должно реализовывать все публичные методы абстрактного представления робота.

Описание абстрактного класса Robot на языке C++:

class Robot {
 protected:
  Robot() {}

 public:
  virtual void prepare(colorPrintfRobot_t *colorPrintf_p, colorPrintfRobotVA_t *colorPrintfVA_p) = 0;
  virtual FunctionResult *executeFunction(CommandMode mode, system_value command_index, void **args) = 0;
  virtual void axisControl(system_value axis_index, variable_value value) = 0;
  virtual ~Robot() {}
};

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

void colorPrintfFromRobotVA(void *robot, const char *uniq_name, ConsoleColor colors, const char *mask, va_list args);
void colorPrintfFromRobot(void *robot, const char *uniq_name, ConsoleColor colors, const char *mask, ...);

В качестве параметра robot должен быть передан указатель на текущий экземпляр представления робота, т.е. this. В параметре uniq_name может быть передан указатель на строку – имя робота, либо может быть передано значение NULL, тогда в выводе вместо имени робота будет подставлен его порядковый номер. В качестве параметра colors должна быть передана структура ConsoleColor, этот параметр задает цвет вывода в консоли. Третий параметр mask задает маску форматирования, по аналогии с функцией printf из стандартной библиотеки C++. Далее в зависимости от функции вывода следует, согласно заданной маске форматирования, обычное перечисление параметров или перечисление параметров в виде структуры va_list из std_arg.h.

Метод executeFunction вызывается интерпретатором, когда конкретный физический робот, сопоставленный данному представлению робота, должен выполнить требуемую функцию. При этом в параметр mode будет передано одно из значений перечисления CommandMode, через данный параметр представление робота может узнать, как именно вызывается текущая функция. Однако действия представления робота никак ни влияют на ожидание или не ожидание среды RCML выполнения вызываемой функции. Значение данного параметра могут быть следующие:
* wait – команда выполняется с ожидаем выполнения;
* not_wait – команда выполняется без ожидания выполнения;
* package – команда передана в пакете, и при этом является не последней командой в пакете;
* end_of_package_wait – последняя команда в пакете, пакет выполняется с ожиданием выполнения;
* end_of_package_no_wait – последняя команда в пакете, пакет выполняется без ожидания выполнения.

В параметр command_index будет передан уникальный идентификатор функции, а в параметр args передан указатель на массив параметров вызываемой функции. Элементов в данном массиве будет ровно столько, сколько параметров принимает данная функция, т.е. сколько было указано в свойстве count_params экземпляре структуры FunctionData соответствующей данной функции, которую, как упоминалось ранее, среда RCML получит через метод getFunctions. Порядок следования значений параметров в массиве – прямой, так же, как они были указаны в текстовом файле программы на языке RCML, и так же, как были указаны типы этих параметров в свойстве params. Указатель на значение параметра, который должен принимать текстовые данные, будет указывать на строку (char ), и, соответственно, указатель на значение параметра, принимающего числовые данные - на число типа *variable_value (double**). Данный метод должен вернуть результат функции, который представляет собой указатель на структуру FunctionResult. В случае если вызываемая функция не принимает параметров, в параметре args данного метода будет передано NULL.

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

ROBOT_COMMAND_FREE – передается, когда данный конкретный робот перестанет требоваться программе. Следует отметить, что данный параметр применяется лишь для уведомления конкретного робота, что далее он будет освобожден, но это не является командой освобождения робота для модуля робота. Для освобождения робота вызывается метод robotFree у модуля робота;

ROBOT_COMMAND_HAND_CONTROL_BEGIN – подготовка робота к переходу в режим ручного управления, после обработки этой команды роботу перестанут передаваться команды и начнут передаваться значения осей через метод axisControl;

ROBOT_COMMAND_HAND_CONTROL_END – выход из режима ручного управления.

Метод axisControl вызывается интерпретатором только в режиме ручного управления, в качестве первого параметра данного метода передается уникальный идентификатор оси робота из структуры AxisData, а в качестве второго параметра новое значение для этой оси, механизм передачи описан в разделе «Принцип передачи значений».

Описание структуры FunctionResult на языке C++:

class FunctionResult {
 public:
  enum Types { EXCEPTION, VALUE };
 private:
  Types type;
  variable_value result;

 public:
  FunctionResult(Types type) : type(type), result(0.0f) {}
  FunctionResult(Types type, variable_value result) : type(type), result(result) {}
  virtual Types getType() { return type; }
  virtual variable_value getResult() { return result; }
  virtual void destroy() { delete this; }
  virtual ~FunctionResult(){};
};

В данной структуре обозначены следующие элементы:

type – тип возвращаемого значения функцией робота. Если тип равен значению EXCEPTION, то считается, что функция бросила исключение, которое может быть обработано оператором try, в противном случае считается, что функция вернула значение;

result – значение, возвращаемое функцией робота, будет прочитано, только если type не равен EXCEPTION. Данное значение может быть только числом.

Важно! При работе компилятора методы модуля робота getModuleInfo, prepare, getFunctions, getAxis, writePC и destroy вызываются из одного и того же потока. При работе интерпретатора методы модуля робота prepare, init, final, readPC, startProgram, endProgram, destroy вызываются последовательно из одного потока. Методы getAviableRobots, robotRequire и robotFree вызываются в рамках одного потока, но это не тот же самый поток, что в предыдущем случае. Метод getModuleInfo вызывается из разных потоков в произвольные моменты времени.

13.2 Модуль функций

Модули функций служат для расширения набора системных функций среды RCML (не следует путать с функциями системного модуля system). Благодаря тому, что модули функций могут создаваться с помощью традиционных языков и средств программирования, появляется возможность интеграции среды RCML с другим ПО и технологиями, а значит, и интеграции этого с робототехникой посредством среды RCML.

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

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

unsigned short getFunctionModuleApiVersion();
FunctionModule *getFunctionModuleObject();

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

Описание класса FunctionModule на языке C++:

class FunctionModule {
 protected:
  FunctionModule() {}

 public:
  // init
  virtual const struct ModuleInfo& getModuleInfo() = 0;
  virtual void prepare(colorPrintfModule_t *colorPrintf_p, colorPrintfModuleVA_t *colorPrintfVA_p) = 0;

  // compiler only
  virtual FunctionData **getFunctions(unsigned int *count_functions) = 0;
  virtual void *writePC(unsigned int *buffer_length) = 0;

  // intepreter - devices
  virtual int init() = 0;
  virtual void final() = 0;

  // intepreter - program & lib
  virtual int readPC(int pc_index, void *buffer, unsigned int buffer_length) = 0;

  // intepreter - program
  virtual int startProgram(int run_index, int pc_index) = 0;
  virtual FunctionResult *executeFunction(system_value function_index, void **args) = 0;
  virtual int endProgram(int run_index) = 0;

  // destructor
  virtual void destroy() = 0;
  virtual ~FunctionModule() {}
};

Методы getModuleInfo, prepare, writePC, readPC, init, final, startProgram, endProgram и destroy полностью аналогичны одноименным методам модуля робота.

Метод getFunctions аналогичен одноименному методу в модуле робота, при его вызове модуль функций должен возвращать список функций, которые он предоставляет для вызова из среды RCML.

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

Описание структур FunctionData и FunctionResult приводилось в предыдущем разделе.

13.3 Модуль управления

Модули управления служат для подключения управляющих устройств к среде RCML и могут быть использованы только в системной функции перехода в режим ручного управления. По своему устройству модули управления близки к модулям роботов, поэтому рекомендуется предварительно ознакомиться с разделом “Модуль робота”.

Библиотека модуля управления должна экспортировать следующие функции:

unsigned short getControlModuleApiVersion();
ControlModule *getControlModuleObject();

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

Описание класса ControlModule на языке C++:

class ControlModule {
 protected:
  ControlModule() {}

 public:
  // init
  virtual const struct ModuleInfo& getModuleInfo() = 0;
  virtual void prepare(colorPrintfModule_t *colorPrintf_p, colorPrintfModuleVA_t *colorPrintfVA_p) = 0;

  // compiler only
  virtual AxisData **getAxis(unsigned int *count_axis) = 0;
  virtual void *writePC(unsigned int *buffer_length) = 0;

  // intepreter - devices
  virtual int init() = 0;
  virtual void execute(sendAxisState_t sendAxisState) = 0;
  virtual void final() = 0;

  // intepreter - program & lib
  virtual int readPC(int pc_index, void *buffer, unsigned int buffer_length) = 0;

  // intepreter - program
  virtual int startProgram(int run_index, int pc_index) = 0;
  virtual int endProgram(int run_index) = 0;

  // destructor
  virtual void destroy() = 0;
  virtual ~ControlModule() {}
};

Методы getModuleInfo, prepare, writePC, readPC, init, final, startProgram, endProgram и destroy полностью аналогичны одноименным методам модуля робота.

Метод getAxis аналогичен одноименному методу в модуле робота, но при его вызове модуль управления должен возвращать список осей управления, которые он предоставляет для связывания в вызове системной функции hand_control.

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

typedef void (*sendAxisState_t)(ControlModule *, system_value, variable_value);

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

Описание структуры AxisData приводилось в разделе “Модуль робота”.

13.4 Модуль выбора

Модули выбора предоставляют алгоритмы принятия решений по выбору. Как и прочие модули, модули выбора имеют общие для всех модулей методы, которые впервые были описаны в “13.1 Модуль робота”.

Библиотека модуля выбора должна экспортировать следующую функции:

unsigned short getChoiceModuleApiVersion();
ControlModule *getChoiceModuleObject();

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

Описание класса ChoiceModule на языке C++:

 class ChoiceModule {
 protected:
  ChoiceModule() {}

 public:
  // init
  virtual const struct ModuleInfo& getModuleInfo() = 0;
  virtual void prepare(colorPrintfModule_t *colorPrintf_p, colorPrintfModuleVA_t *colorPrintfVA_p) = 0;

  // compiler only
  virtual void *writePC(unsigned int *buffer_length) = 0;

  // intepreter - devices
  virtual int init() = 0;
  virtual void final() = 0;

  // intepreter - program & lib
  virtual int readPC(int pc_index, void *buffer, unsigned int buffer_length) = 0;

  // intepreter - program
  virtual int startProgram(int run_index, int pc_index) = 0;
  virtual const ChoiceRobotData *makeChoice(const ChoiceFunctionData** function_data, unsigned int count_functions, const ChoiceRobotData** robots_data, unsigned int count_robots) = 0;
  virtual int endProgram(int run_index) = 0;

  // destructor
  virtual void destroy() = 0;
  virtual ~ChoiceModule() {}
};

Методы getModuleInfo, prepare, writePC, readPC, init, final, startProgram, endProgram и destroy полностью аналогичны одноименным методам модуля робота.

Метод makeChoice вызывается, когда от модуля выбора требуется принятие решения по выбору робота. Именно в этом методе должна быть заложена логика по выбору робота. В данный метод передаются на вход следующие параметры:

  • function_data - массив указателей на структуры ChoiceFunctionData, представляющий последовательность вызываемых функций у робота, который будет выбран.
  • count_functions - количество элементов в массиве function_data;
  • robots_data - массив указателей на структуры ChoiceRobotData, данный массив есть список указателей на описания свободных роботов полученных от модулей роботов;
  • count_robots - количество элементов в массиве robots_data.

Метод makeChoice должен вернуть указатель на описание представления ChoiceRobotData выбранного робота. В случае если решение о выборе робота принять невозможно, метод makeChoice должен вернуть NULL.

Важно! В случае если планируется работа модуля выбора с БД статистики RCML, то в разделе “Работа с RCML статистикой” есть важное дополнение об этом.

struct ChoiceFunctionData {
  enum CallType {
    Unknow = 0,
    Guarante = 1,
    Probably = 2
  };
  const char *name;
  const char *context_hash;
  system_value position;
  CallType call_type;
}; 

В данной структуре обозначены следующие свойства:

  • name - имя функции, которая будет вызвана у выбранного робота;
  • context_hash - хеш файла RCML программы в виде строчного представления бинарных данных, в котором встречен вызов данной функции;
  • position - уникальная позиция вызова данной функции в файле RCML программы. Данное свойство не следует путать с номером строки, в которой происходит вызов данной функции, в исходном коде RCML программы;
  • call_type - тип вызова функции, может принимать следующий значения:
    • Unknow - RCML интерпретатор не может вычислить вызов данной функции, это исключительная ситуация приравниваемая к внутренней ошибке;
    • Guarante - функция гарантированно будет вызвана, т.е. её вызов не зависит от каких либо условий;
    • Probably - функция вероятно будет вызвана, т.е. на пути к данному вызову функции есть условия-ветвления, выполнение которых может привести к тому, что данная функция вызвана не будет. Следует отметить, что в данном контексте условием считается не оператор if, а условный переход - эквивалент ассемблерной команды jz с вычисляемым условием. Безусловные переходы под данное определение не попадают.

Структура ChoiceRobotData, описывающая представление робота, имеет следующее описание на языке С++:

struct ChoiceRobotData {
  const ChoiceModuleData *module_data;
  const char *robot_uid;
};

В данной структуре обозначены следующие свойства:

  • module_data - указатель на структуру ChoiceModuleData, описывающую модуль робота;
  • robot_uid - имя робота, которое должно быть уникальным в рамках модуля робота, однако не обязательно может быть так. В том числе данное поле может равняться пустой строке “”.

Структура же ChoiceModuleData имеет вид:

struct ChoiceModuleData {
  const char *iid;
  const char *hash;
  unsigned short version;
};

И содержит следующие элементы:

  • iid - интерфейсный идентификатор модуля, в виде строки;
  • hash - хеш от файла модуля в виде строчного представления бинарных данных;
  • version - версия модуля.

14 Подробнее об интерфейсных идентификаторах

Строка, указатель на которую хранится в свойстве iid структуры ModuleInfo возвращаемой методом getModuleInfo, независимо от модуля, является уникальным идентификатором версии интерфейса модуля, то есть она может использоваться как для придания уникальности модулю, так и конкретной его версии. Аналогично для RCML библиотек.

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

Для придания большей уникальности рекомендуется задавать уникальный идентификатор максимально допустимого размера - 32 байта.

Уникальный идентификатор может в какой-то мере служить инструментом разработчику модуля или библиотеки, указывая среде RCML на совместимость различных версий разрабатываемого продукта. Меняя код и не меняя данный идентификатор, разработчик обозначает преемственность новой версии модуля или библиотеки по отношению к старой, то есть новая версия может использоваться для исполнения RCML программ, скомпилированных со старой версией. Так рекомендуется поступать только в случае, если не изменились уникальные индексы функций модуля или имена функций библиотеки, их положение в коде, число их параметров и поведение программы при исполнении данных функций. В противном же случае рекомендуется указать новый уникальный интерфейсный идентификатор, чтобы указать среде RCML, что новая версия модуля или библиотеки имеет значительные отличия от старой и поведение выполняемой RCML программы, скомпилированной со старой версией, не определено для новой версии.


15 Работа с RCML статистикой

15.1 Общие сведения и рекомендации

RCML пишет статистику во внешний файл БД, который указывается в файле конфигурации config.ini (подробнее в разделе “Установка и настройка компилятора и интерпретатора RCML”.

Для работы с БД иcпользуется встраиваемый движок баз данных SQLite (http://www.sqlite.org/), таким образом нет никакого разграничения прав и доступа к БД, поэтому для исключения конфликтов доступа к БД всему отличному от RCML программному обеспечению, в том числе модулям выбора роботов, рекомендуется обращаться к БД только в режиме чтения без модификации данных.

Важно отметить, что статистика пишется RCML в режиме runtime, т.е. постоянно, поэтому не рекомендуется кешировать запросы к БД, чтобы не потерять актуальности данных.

15.2 Структура базы данных статистики

На рис. 7. приведено графическое отображение структуры БД статистики.

Основной таблицей хранящей статистику по исполнению роботами функций является таблица function_calls. Каждая запись в данной таблице представляет собой информацию о выполнении конкретным роботом конкретной функции в ходе работы RCML.

Таблица function_calls имеет следующие поля:

  • id - уникальный идентификатор записи, первичный ключ;
  • robot_id - идентификатор робота выполнившего данную функцию, внешний ключ, ссылается на поле id в таблице robot_uids;
  • function_id - идентификатор выполненной функции, внешний ключ, ссылается на поле id в таблице functions;
  • run_id - идентификатор запуска RCML программы, внешний ключ, ссылается на поле id в таблице runs;
  • start - временная метка начала выполнения функции, количество микросекунд с момента запуска выполнения RCML программы, в которой была вызвана функция описываемая данной записью;
  • end - временная метка окончания выполнения функции, количество микросекунд с момента запуска выполнения RCML программы.

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

Таблица, хранящая информацию о контекстах, называется contexts и содержит следующие поля:

  • id - уникальный идентификатор контекста, первичный ключ;
  • filename - имя файла контекста;
  • hash - хеш от файла контекста в виде строчного представления бинарных данных;
  • iid - интерфейсный идентификатор контекста, в виде строки, указывается только для контекстов, которые являются RCML библиотеками;
  • version - версия контекста, указывается только для контекстов, которые являются RCML библиотеками.

Таблица functions отражает положение конкретного вызова функции робота в контексте, имеют следующие поля:

  • id - уникальный идентификатор места вызова функции, первичный ключ;
  • context_id - идентификатор контекста, в котором вызывается функция, внешний ключ, ссылается на поле id в таблице contexts;
  • name - имя вызываемой функции;
  • position - уникальная позиция вызова данной функции в контексте. Данное свойство не следует путать с номером строки, в которой происходит вызов данной функции, в исходном коде RCML программы.

Таблица runs отражает хронологию запуска на выполнение тех контекстов функций, которые являются RCML программами. Данная таблица содержит:

  • id - уникальный идентификатор запуска контекста на выполнение, первичный ключ;
  • context_id - идентификатор контекста, в котором вызывается функция, внешний ключ, ссылается на поле id в таблице contexts;
  • run_at - временная метка запуска RCML программы, отражающая количество микросекунд прошедших с начала эпохи 1900.01.01 00:00:00.000.000 в локальном часовом поясе машины.

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

Таблица, описывающая источники функций, называется sources и содержит следующие поля:

  • id - уникальный идентификатор источника, первичный ключ;
  • type - тип источника:
    • 1 - RCML библиотека;
    • 2 - модуль робота.
  • hash - хеш от файла источника в виде строчного представления бинарных данных;
  • iid - интерфейсный идентификатор источника, в виде строки;
  • version - версия источника функций.

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

  • id - уникальный идентификатор робота, первичный ключ;
  • source_id - идентификатор источника, в данном случае источником может быть только модуль роботов, внешний ключ, ссылается на поле id в таблице sources;
  • uid - имя робота, которое должно рекомендуется делать уникальным, однако оно таковым может не являться.

16 Список принятых сокращений

Ниже приведен список принятых сокращений в данной книге в порядке первого их упоминания в тексте:

RCML - Robot Control Meta Language;

API – Application Programming Interface;

БД – база данных;

ПО – программное обеспечение;

ОС – операционная система;

PC – pseudo code.


17 Примеры подключения модулей к RCML

RCML позволяет скооперировать разных роботов в рамках одной задачи. Таким образом, возможно реализовать одновременное выполнение группой роботов нескольких задач, а также переключение одного робота между несколькими задачами.

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

Среда RСML имеет ряд точек ввода/вывода (API) для интеграции с программными продуктами и различными системами, включая роботов. Дополнительно может быть расширен функционал самого языка через добавление в него новых функций путем подключения модулей.

Взаимодействие RCML с устройствами управления и исполнения

Модули расширения RCML бывают трех типов:

Далее более подробно будут рассмотрены примеры подключения каждого типа модуля.

17.1 Пример подключения модулей роботов

17.1.1 Быстрое подключение модуля робота test к RCML

Модуль робота test предназначен для имитации физического робота и проверки подключенных компонентов и программ.

Для включения модуля робота test необходимо в секции [robot_modules] конфигурационного файла RCML добавить свойство module и установить его равным значению test в соответствии с разделом “Установка и настройка компилятора и интерпретатора RCML”. Дополнительной конфигурации модулю test не требуется.

Далее приведен текст программы на языке RCML. Программа обращается к тестовому роботу, и отправляет ему команду на выполнение функции do_ something(). Более подробно о том, что означает эта функция написано далее

function main() {
    @lr = robot_test; 
    @lr->do_something(1000);
}

Во время выполнения данной программы, тестовый робот имитирует физическое выполнение действия, которое бы заняло 1 секунду времени.

После подключения модуля программисту доступны следующие функции:

Наименование Кол-во параметров Возвращает значение Бросает исключение Описание
none 0 Нет Нет Ничего не делающая функция.
do_something 1 Нет Нет В качестве параметра функция принимает кол-во миллисекунд, на которые будет остановлен процесс выполнения программы.
get_some_value 1 Да Нет Возвращает значение, переданное на вход в качестве параметра.
throw_exception 0 Нет Да При вызове бросает исключение.
print 2 Нет Нет Принимает на вход, в качестве первого, строковый параметр и выводит его в консоли, спустя задержку, указанную в миллисекундах во втором параметре.

Кроме этого, тестовый робот имеет также оси:

Наименование Кол-во параметров Возвращает значение Бросает исключение
Х 100 -100
Y 1 0 Бинарная
Z 100 0

17.1.2 Быстрое подключение модуля lego_ev3 к RCML

Lego Mindstorms EV3 – третье поколение конструктора Lego.

Более подробно о конструкторе и его возможностях на официальном сайте компании Lego:

Модуль робота lego_ev3 используется для подключения блоков Lego Ev3 к RCML.

  1. Для включения модуля lego_ev3 необходимо добавить запись в секцию [robot_modules] конфигурационного файла RCML свойство module и установить его равным lego_ev3 в соответствии с разделом “Установка и настройка компилятора и интерпретатора RCML”
  2. Следует произвести сопряжение Bluetooth-адаптера с модулем Lego Ev3.
  3. После сопряжения, следует зайти в свойства добавленного устройства. Необходимо узнать адрес COM-порта, с которым осуществляется взаимодействие.

  1. Далее следует произвести настройку модуля. А именно необходимо указать, через какой COM-порт следует взаимодействовать с модулем Ev3. Нужно перейти в каталог с модулями роботов (robot_modules), а затем в каталог модуля Ev3 (lego_ev3).
  2. В директории модуля lego_ev3 нужно создать файл config.ini со следующим содержанием:
[connections]
connection = COM6
[options]
dynamic_connection = 0

Файл настроек config.ini включает в себя следующие свойства, разнесенные на две секции [connections] и [options]:
В секции [connections] отражаются номера портов подключенных роботов, на каждого робота заводится надпись вида:
connection = COMX, где X это номер порта, который используется в свойствах подключения.
Секция [options] содержит свойство dynamic_connection.
dynamic_connection используется для динамического подключения роботов, то есть позволяет менять состав используемых роботов прямо в момент исполнения программы. Однако каждое задействование робота занимает время на подключение к нему. Если значение dynamic_connection установлено равным 1, то Bluetooth соединение с роботом будет устанавливаться в момент запроса робота и разрываться при его освобождении, что может вызывать значительную временную задержку при задействовании робота в RCML программе.
Если значение dynamic_connection установлено равным 0, то Bluetooth соединение с роботом будет устанавливаться в момент старта программы и разрываться при ее завершении.
По умолчанию будет присвоено значение 0.
Далее приведен пример текста программы на языке RCML. Программа обращается к роботу Lego и взаимодействует с двигателем, подключенным к блоку Ev3. Текст программы выглядит следующим образом:

function main() {
    @lr = robot_lego_ev3;
    @lr->motorMoveTo("B",10,200,0);
}

Первая строка создаст переменную, к которой будет прикреплен Lego робот.
Вторая строка переместит мотор B в позицию 200 со скоростью 10 не используя тормоз.
Кроме этого Lego робот, может быть представлен для RCML, как «Track Vehicle», что позволит обращаться к двум моторам робота Lego, как к единому целому и открывает дополнительные функции управления. Функция setTrackVehicle создана специально для роботов особых конфигураций, с двумя ведущими колесами и одним поворотным колесом/упором, или конфигураций роботов на гусеничном ходу, по 1 мотору на каждое колесо или гусеницу. Такой режим автоматизирует управление моторами для маневрирования робота. Для инициализации робота в режиме «Track Vehicle» необходимо использовать в программном коде функцию setTrackVehicle.

Далее приведен пример текста программы на языке RCML в которой создаётся «Track Vehicle» и происходит управление им.

function main() {
    @lr = robot_lego_ev3; 
    @lr->setTrackVehicle("B","C",1,1);
    @lr->trackVehicleForward(10);
    system.sleep(1000);
    @lr->trackVehicleOff();
}

В результате исполнения программы робот совершит симметричное вращение обоими двигателями, подключенными к портам B и С со скоростью 10. Более подробный список команд представлен далее.

В случае, если в коде программы не будет вызвана функция создания «Track Vehicle» setTrackVehicle, то при выполнении функции связанных с использованием «Track Vehicle» произойдет выброс исключения, который можно обработать при помощи конструкции try/catch, более подробно о котором написано в разделе “Исключения”.

Существуют особенности, связанные с входящими значениями для функций модуля.

Для обращения к моторам A,B,C,D используются буквы “A”,”B”,”C” и “D” соответственно;

Для обращения к датчикам под номерами 1,2,3,4 используются соответствующие номера;

На параметры функций speed и percent наложены следующие ограничения:

  • speed принимает значения [-100;100];
  • percent принимает значения [0;100].

При превышении указанных ограничений вызываемая функция бросает исключение, которое можно обработать с помощью конструкции языка RCML. Более подробно об обработке исключений написано в разделе “Исключения”.

Доступные функции робота приведены в таблице:

Определение Описание Пример
motorSetSpeed(motor,speed) Задает мотору motor скорость speed @lr->motorSetSpeed (“B”,20); Задает мотору B скорость 20.
motorOff(motor) Выключает мотор motor без использования тормозов, т.е. мотор остановится не сразу. @lr->motorOff(“C”); Остановит моторC.
motorBrake(motor) Останавливает мотор motor, используя тормоза. Мгновенная остановка. @lr->motorBrake(“D”); Остановит мотор D.
motorSetDirection(motor,direction) Устанавливает направление мотора motor. direction=0 задает направление мотора “назад”. direction != 0 задает направление мотора “вперед” @lr->motorSetDirection(“A”,0); Установит направление мотора А”назад”.
motorGetDirection(motor) Возвращает 1 если направление мотора motor установлено как “вперед”, возвращает 0 если направление мотора motor установлено как “назад”. Мотор остановится не сразу. RecievedDirection = @lr->motorGetDirection(“C”);
motorMoveTo(motor,speed,position,brake) Перемещает мотор motor в положение position со скоростью speed. brake = 0 - не использовать тормоз, brake != 0 - использовать тормоз. @lr->motorMoveTo(“B”,10,200,0); Переместит мотор B в позицию 200 со скоростью 10 не используя тормоз.
motorGetTacho(motor) Возвращает позицию мотора motor. RecievedTacho = @lr->motorGetTacho(“C”);
motorResetTacho(motor) Устанавливает текущую позицию мотора motor как нулевую. @lr->motorResetTacho(“C”);
waitMotorToStop(motor) Ждет пока остановится мотор motor. @lr->waitMotorToStop(“C”);
waitMultiMotorsToStop(motorA,motorB,motorC,motorD) Ждет пока остановится несколько моторов. Если motor != 0, то ждет остановки, если motor = 0, то не ждет. @lr->waitMultiMotorsToStop(1,0,0,1); Ждет пока остановятся моторы А иD.
setTrackVehicle(motorLeft,motorRight,LeftReverse,RightReverse) Логически соединяет 2 мотора motorLeft иmotorRight в Track Vehicle (гусеничный транспорт). Позволяет обращаться к этим моторам как к единому целому и открывает дополнительные функции управления Track Vehicle, имеющие префикс trackVehicle. Left и Right Reverse при значении != 0 задает моторам обратное направление. @lr->setTrackVehicle(“B”,”C”,1,0); Логически соединяет моторы B и C в Track, при этом мотор B изменяет свое направление на обратное.
trackVehicleBackward(speed) Track Vehicle движется назад со скоростью speed. @lr->trackVehicleBackward(20);
trackVehicleForward(speed) Track Vehicle движется вперед со скоростью speed. @lr->trackVehicleForward(40);
trackVehicleOff() Выключает моторы Track Vehicle. Моторы остановятся не сразу. @lr->trackVehicleOff();
trackVehicleBrake() Останавливает моторы Track Vehicle, используя тормоза. @lr->trackVehicleBrake();
trackVehicleSpinLeft(speed) Track Vehicle поворачивается влево вокруг своей вертикальной оси. @lr->trackVehicleSpinLeft(10);
trackVehicleSpinRight(speed) Track Vehicle поворачивается вправо вокруг своей вертикальной оси. @lr->trackVehicleSpinRight(90);
trackVehicleTurnLeftForward(speed,percent) Track Vehicle поворачивает влево, двигаясь вперед. Правый мотор будет иметь со скорость speed, а левый (speed - speed*percent/100). Таким образом если percent=0 то моторы будут иметь одинаковую скорость, а при percent=100 левый мотор будет иметь нулевую скорость. @lr->trackVehicleTurnLeftForward(20, 50);
trackVehicleTurnRightForward(speed,percent) Track Vehicle поворачивает влево, двигаясь вперед. Левый мотор будет иметь со скорость speed, а правый (speed - speed*percent/100). Таким образом если percent=0 то моторы будут иметь одинаковую скорость, а при percent=100 правый мотор будет иметь нулевую скорость. @lr->trackVehicleTurnRightForward(10, 90);
trackVehicleTurnLeftReverse(speed,percent) Track Vehicle поворачивает влево, двигаясь назад. Скорость правого мотора равна speed, а левого (speed - speed*percent/100) @lr->trackVehicleTurnLeftReverse(10,20);
trackVehicleTurnRightReverse(speed,percent) Track Vehicle поворачивает вправо, двигаясь назад. Скорость левого мотора равна speed, а правого (speed - speed*percent/100) @lr->trackVehicleTurnRightReverse(50,50);
readSensor(sensor,mode) Считывает показания сенсора под номером sensor в режиме mode. Для разных датчиков существует разное количество режимов. @lr->readSensor(2,1); Считывает показания датчика подключенного к порту 2 в режиме 1.
isMotorRunning(motor,sleep) Возвращает 1 если мотор motor включен и 0 если нет. Если sleep отлично от 0, то мотор будет опрошен с задержкой в 200 микросекунд. @lr->isMotorRunning(“B”,1);
getMotorSpeed(motor,sleep) Возвращает значение скорости мотора motor. Если sleep отлично от 0, то мотор будет опрошен с задержкой в 200 микросекунд. @lr->getMotorSpeed(“B”,1);

Для модуля lego_ev3 доступно подключение датчиков 8-ми типов, индексы которых используются в командах языка RCML. Типы датчиков и их индексы представлены в таблице:

Датчик Режим
Ультразвуковой 1 - режим определения расстояния в сантиметрах 2 - режим определения расстояния в дюйма 3 - режим прослушивания
Инфракрасный 1 - режим определения расстояния 2 - режим определения местоположения инфракрасного дистанционного пульта 3- режим определения нажатия кнопок на инфракрасном пульте
Цветовой 1 - режим определения интенсивности света 2 - режим определения цвета (см. таблицу цветов) 3- режим определения raw значения отраженного света 4 - режим определения отраженного света
Гироскопический 1 - режим определения угла в градусах 2 - режим определения угловой скорости в градусах в секунду
Датчик нажатия 1 - режим отслеживания нажатия 2 - режим вывода количества нажатий
Датчик Температуры 1 - режим измерения температуры по шкале Цельсия 2 - режим измерения температуры по шкале Фаренгейта
Датчик Звука 1 - режим измерения громкости звука в dB 2 - режим измерения громкости звука в dBA
Датчик Света 1 - режим измерения интенсивности 2 - режим измерения отраженного света

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

Цвет Возвращаемое значение
Черный 1
Синий 2
Зеленый 3
Желтый 4
Красный 5
Белый 6
Коричневый 7

Всегда доступны следующие оси для управления блоком Ev3:

Ось Верхняя граница Нижняя граница Описание
locked 1 0 Бинарная ось. Если её значение равно 1, то робот считается заблокированным и игнорирует изменение значений по любым другим осям.
speedMotorA 100 -100 Значение оси задает скорость мотора, подключенного к порту A.
speedMotorB 100 -100 Значение оси задает скорость мотора, подключенного к порту B.
speedMotorC 100 -100 Значение оси задает скорость мотора, подключенного к порту C.
speedMotorD 100 -100 Значение оси задает скорость мотора, подключенного к порту D.
moveMotorA 1000 -1000 Значение оси задает позицию, на которую повернется мотор A со скоростью 50.
moveMotorB 1000 -1000 Значение оси задает позицию, на которую повернется мотор B со скоростью 50.
moveMotorC 1000 -1000 Значение оси задает позицию, на которую повернется мотор C со скоростью 50.
moveMotorD 1000 -1000 Значение оси задает позицию, на которую повернется мотор D со скоростью 50.

В случае, использования робота Lego в режиме «Track Vehicle», робот имеет следующие оси робота, к которым можно подключиться для управления:

straight 100 -100
rotation 100 -100

Робот Lego должен быть инициализирован в режим «Track Vehicle» прежде чем будет задействована ось straight или rotation, т.е. до включения режима ручного управления!

17.1.3 Быстрое подключение модуля lego_nxt к RCML

Lego Mindstorms NXT – второе поколение конструктора Lego.

Подробнее на официальном сайте конструктора:

Модуль робота lego_nxt используется для подключения блоков Lego NXT к RCML.

  1. Для включения модуля lego_nxt необходимо добавить запись в секцию [robot_modules] конфигурационного файла RCML свойство module и установить его равным lego_nxt в соответствии с разделом “Установка и настройка компилятора и интерпретатора RCML”
  2. Следует произвести сопряжение Bluetooth-адаптера с модулем Lego NXT.
  3. После сопряжения, следует зайти в свойства добавленного устройства. Необходимо узнать адрес COM-порта, с которым осуществляется взаимодействие.

  1. Далее следует произвести настройку модуля. А именно необходимо указать, через какой COM-порт следует взаимодействовать с модулем NXT. Нужно перейти в каталог с модулями роботов (robot_modules), а затем в каталог модуля NXT (lego_nxt).
  2. В директории модуля lego_nxt нужно создать файл config.ini со следующим содержанием:
[connections]
connection = COM6
[options]
dynamic_connection = 0

Файл настроек config.ini включает в себя следующие свойства, разнесенные на две секции [connections] и [options]:

В секции [connections] отражаются номера портов подключенных роботов, на каждого робота заводится надпись вида:

connection = COMX, где X это номер порта, который используется в свойствах подключения.

Секция [options] содержит свойство dynamic_connection.

dynamic_connection используется для динамического подключения роботов, то есть позволяет менять состав используемых роботов прямо в момент исполнения программы. Однако каждое задействование робота занимает время на подключение к нему. Если значение dynamic_connection установлено равным 1, то Bluetooth соединение с роботом будет устанавливаться в момент запроса робота и разрываться при его освобождении, что может вызывать значительную временную задержку при задействовании робота в RCML программе.

Если значение dynamic_connection установлено равным 0, то Bluetooth соединение с роботом будет устанавливаться в момент старта программы и разрываться при ее завершении.

По умолчанию будет присвоено значение 0.

Далее приведен пример текста программы на языке RCML. Программа обращается к роботу Lego и взаимодействует с двигателем, подключенным к блоку NXT. Текст программы выглядит следующим образом:

function main() {
    @lr = robot_lego_nxt;
    @lr->motorMoveTo("B",10,200,0);
}

Первая строка создаст переменную, к которой будет прикреплен Lego робот.

Вторая строка переместит мотор B в позицию 200 со скоростью 10 не используя тормоз.

Кроме этого Lego робот, может быть представлен для RCML, как «Track Vehicle», что позволит обращаться к двум моторам робота Lego, как к единому целому и открывает дополнительные функции управления. Функция setTrackVehicle создана специально для роботов особых конфигураций, с двумя ведущими колесами и одним поворотным колесом/упором, или конфигураций роботов на гусеничном ходу, по 1 мотору на каждое колесо или гусеницу. Такой режим автоматизирует управление моторами для маневрирования робота. Для инициализации робота в режиме «Track Vehicle» необходимо использовать в программном коде функцию setTrackVehicle.

Далее приведен пример текста программы на языке RCML в которой создаётся «Track Vehicle» и происходит управление им.

function main() {
    @lr = robot_lego_nxt; 
    @lr->setTrackVehicle("B","C",1,1);
    @lr->trackVehicleForward(10);
    system.sleep(1000);
    @lr->trackVehicleOff();
}

В результате исполнения программы робот совершит симметричное вращение обоими двигателями, подключенными к портам B и С со скоростью 10. Более подробный список команд представлен далее.

В случае, если в коде программы не будет вызвана функция создания «Track Vehicle» setTrackVehicle, то при выполнении функции связанных с использованием «Track Vehicle» произойдет выброс исключения, который можно обработать при помощи конструкции try/catch, более подробно о котором написано в разделе “Исключения”.

Существуют особенности, связанные с входящими значениями для функций модуля.

Для обращения к моторам A,B,C,D используются буквы “A”,”B”,”C” и “D” соответственно;

Для обращения к датчикам под номерами 1,2,3,4 используются соответствующие номера;

На параметры функций speed и percent наложены следующие ограничения:

  • speed принимает значения [-100;100];
  • percen*t принимает значения [0;100]*.

При превышении указанных ограничений вызываемая функция бросает исключение, которое можно обработать с помощью конструкции языка RCML. Более подробно об обработке исключений написано в разделе “Исключения”.

Доступные функции робота приведены в таблице:

Определение Описание Пример
motorSetSpeed(motor,speed) Задает мотору motor скорость speed @lr->motorSetSpeed (“B”,20); Задает мотору B скорость 20.
motorOff(motor) Выключает мотор motor без использования тормозов, т.е. мотор остановится не сразу. @lr->motorOff(“C”); Остановит моторC.
motorBreak(motor) Останавливает мотор motor, используя тормоза. Мгновенная остановка. @lr->motorBreak(“A”); Остановит мотор A.
motorSetDirection(motor,direction) Устанавливает направление мотораmotor. direction=0 задает направление мотора “назад”. direction != 0 задает направление мотора “вперед” @lr->motorSetDirection(“A”,0); Установит направление мотора А”назад”.
motorGetDirection(motor) Возвращает 1 если направление мотораmotor установлено как “вперед”, возвращает 0 если направление мотораmotor установлено как “назад”. Мотор остановится не сразу. RecievedDirection = @lr->motorGetDirection(“C”);
motorMoveTo(motor,speed,position,brake) Перемещает мотор motor в положениеposition со скоростью speed. brake = 0 - не использовать тормоз, brake != 0 - использовать тормоз. @lr->motorMoveTo(“B”,10,200,0); Переместит мотор B в позицию 200 со скоростью 10 не используя тормоз.
motorGetTacho(motor) Возвращает позицию мотора motor. RecievedTacho = @lr->motorGetTacho(“C”);
motorResetTacho(motor) Устанавливает текущую позицию мотораmotor как нулевую. @lr->motorResetTacho(“C”);
waitMotorToStop(motor) Ждет пока остановится мотор motor. @lr->waitMotorToStop(“C”);
waitMultiMotorsToStop(motorA,motorB,motorC) Ждет пока остановится несколько моторов. Если motor != 0, то ждет остановки, если motor = 0, то не ждет. @lr->waitMultiMotorsToStop(1,0,1); Ждет пока остановятся моторы А иС.
setTrackVehicle(motorLeft,motorRight,LeftReverse,RightReverse) Логически соединяет 2 мотора motorLeft иmotorRight в Track Vehicle (гусеничный транспорт). Позволяет обращаться к этим моторам как к единому целому и открывает дополнительные функции управления Track Vehicle, имеющие префикс trackVehicle. Left и Right Reverse при значении != 0 задает моторам обратное направление. @lr->setTrackVehicle(“B”,”C”,1,0); Логически соединяет моторы B и C в Track, при этом мотор B изменяет свое направление на обратное.
trackVehicleBackward(speed) Track Vehicle движется назад со скоростьюspeed. @lr->trackVehicleBackward(20);
trackVehicleForward(speed) Track Vehicle движется вперед со скоростью speed. @lr->trackVehicleForward(40);
trackVehicleOff() Выключает моторы Track Vehicle. Моторы остановится не сразу. @lr->trackVehicleOff();
trackVehicleBrake() Останавливает моторы Track Vehicle, используя тормоза. @lr->trackVehicleBrake();
trackVehicleSpinLeft(speed) Track Vehicle поворачивается влево вокруг своей вертикальной оси. @lr->trackVehicleSpinLeft(10);
trackVehicleSpinRight(speed) Track Vehicle поворачивается вправо вокруг своей вертикальной оси. @lr->trackVehicleSpinRight(90);
trackVehicleTurnLeftForward(speed,percent) Track Vehicle поворачивает влево, двигаясь вперед. Правый мотор будет иметь со скорость speed, а левый (speed - speed*percent/100). Таким образом еслиpercent=0 то моторы будут иметь одинковую скорость, а при percent=100 левый мотор будет иметь нулевую скорость. @lr->trackVehicleTurnLeftForward(20, 50);
trackVehicleTurnRightForward(speed,percent) Track Vehicle поворачивает влево, двигаясь вперед. Левый мотор будет иметь со скорость speed, а правый (speed - speed*percent/100). Таким образом еслиpercent=0 то моторы будут иметь одинковую скорость, а при percent=100 правый мотор будет иметь нулевую скорость. @lr->trackVehicleTurnRightForward(10, 90);
trackVehicleTurnLeftReverse(speed,percent) Track Vehicle поворачивает влево, двигаясь назад. Скорость правого мотора равна speed, а левого (speed - speed*percent/100) @lr->trackVehicleTurnLeftReverse(10,20);
trackVehicleTurnRightReverse(speed,percent) Track Vehicle поворачивает вправо, двигаясь назад. Скорость левого мотора равна speed, а правого (speed - speed*percent/100) @lr->trackVehicleTurnRightReverse(50,50);
isMotorRunning(motor,sleep) Возвращает 1 если мотор motor включен и 0 если нет. Если sleep отлично от 0, то мотор будет опрошен с задержкой в 200 микросекунд. @lr->isMotorRunning(“B”,1);

Для модуля lego_nxt доступны следующие функции опроса датчиков:

Определение Описание Режимы
readHiTecColor( port,mode) Считывает показания HiTecColor сенсора на порту port в режиме mode. 1 - Возвращает значение красного цвета [0;25 2 - Возвращает значение зеленого цвета [0;25] 3 - Возвращает значение синего цвета [0;255]
readHiTecCompass(port) Считывает показания HiTecCompass сенсора на порту port. Возвращает значение в градусах
readHiTecGyro( port) Считывает показания HiTecGyro сенсора на порту port. Возвращает угловое ускорение в градусах в секунду
readHiTecTilt( port,mode) Считывает показания HiTecTilt сенсора на порту port в режиме mode. 1 - Возвращает целочисленное значение положения датчика по оси x 2 - Возвращает целочисленное значение положения датчика по оси y 3 - Возвращает целочисленное значение положения датчика по оси z
readNXTColor( port,mode) Считывает показания NXTColor сенсора на порту port в режиме mode. 1 - Возвращат целочисленное значение, соответствующее таблице цветов 2 - Возвращает интенсивность красного цвета в процентах 3 - Возвращает интенсивность зеленого цвета в процентах 4 - Возвращает интенсивность синего цвета в процентах 5 - Возвращает интенсивность света в процентах
readNXTLight( port,mode) Считывает показания NXTLight сенсора на порту port в режиме mode. 1 - Возвращает интенсивность света в виде целочисленного значения 2 - Возвращает интенсивность света в процентах
readNXTSonar( port,mode) Считывает показания NXTSonar сенсора на порту port в режиме mode. 1 - режим определения расстояния в сантиметрах 2 - режим определения расстояния в дюймах
readNXTSound( port,mode) Считывает показания NXTSound сенсора на порту port в режиме mode 1 - режим измерения громкости звука в dB 2 - режим измерения громкости звука в dBA
readNXTTouch( port,mode) Считывает показания NXTTouch сенсора на порту port в режиме mode. 1 - режим отслеживания нажатия 2 - режим вывода количества нажатий
readRCXLight( port,mode) Считывает показания RCXLight сенсора на порту port в режиме mode. 1 - режим измерения интенсивности 2 - режим измерения отраженного света
readRCXRotation(port) Считывает показания RCXRotation сенсора на порту port. Возвращает шаг поворота [-16;+16]
readRCXTemperature(port, mode) Считывает показания RCXTemperature сенсора на порту port в режиме mode. 1 - режим измерения температуры по шкале Цельсия 2 - режим измерения температуры по шкале Фаренгейта

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

Цвет Возвращаемое значение
Черный 1
Синий 2
Зеленый 3
Желтый 4
Красный 5
Белый 6

Всегда доступны следующие оси для управления блоком NXT:

Ось Верхняя граница Нижняя граница Описание
locked 1 0 Бинарная ось. Если её значение равно 1, то робот считается заблокированным и игнорирует изменение значений по любым другим осям.
speedMotorA 100 -100 Значение оси задает скорость мотора, подключенного к порту A
speedMotorB 100 -100 Значение оси задает скорость мотора, подключенного к порту B.
speedMotorC 100 -100 Значение оси задает скорость мотора, подключенного к порту C.
moveMotorA 1000 -1000 Значение оси задает позицию, на которую повернется мотор A со скоростью 50.
moveMotorB 1000 -1000 Значение оси задает позицию, на которую повернется мотор B со скоростью 50.
moveMotorC 1000 -1000 Значение оси задает позицию, на которую повернется мотор C со скоростью 50.

В случае, использования робота Lego в режиме «Track Vehicle», робот имеет следующие оси робота, к которым можно подключиться для управления:

Ось Верхняя граница Нижняя граница Описание
straight 100 -100 Значение оси задает скорость, с которой прямолинейно перемещается Track Vehicle.
rotation 100 -100 Значение оси задает скорость, с которой Track Vehicle вращается вокруг своей вертикальной оси.

Робот Lego должен быть инициализирован в режим «Track Vehicle» прежде чем будет задействована ось straight или rotation, т.е. до включения режима ручного управления!

17.1.4 Быстрое подключение модуля uarm к RCML

uArm – это 4-х осевой манипулятор, миниатюрная версия промышленного робота ABB PalletPack IRB460.

Изучить подробности о манипуляторе uArm можно на официальном сайте:

Модуль робота uarm используется для подключения робота uArm к RCML.

  1. Необходимо выполнить подключение робота uArm к компьютеру. Нужно перейти на сайт производителя в раздел Download Center, далее следует перейти в раздел Instructions и перейти в раздел Getting Started и далее выполнить инструкцию для подходящей модели uArm.
  2. Далее необходимо сменить программное обеспечение робота для работы с SDK С#. Инструкция быстрого старта по работе с SDK С#.
  3. Для включения модуля uarm необходимо добавить запись в секцию [robot_modules] конфигурационного файла RCML свойство module и установить его значение равным uarm в соответствии с разделом “Установка и настройка компилятора и интерпретатора RCML”.
  4. Далее следует произвести настройку модуля. Необходимо указать, через какой COM-порт следует взаимодействовать с роботом. Для этого нужно перейти в каталог с модулями роботов (robot_modules), а затем в каталог модуля uarm.
  5. В директории модуля uarm нужно создать файл config.ini со следующим содержанием:
[main]
count_robots = 1
is_debug = 0
robot_port_0 = com3

Файл конфигурации модуля состоит из секции [main], которая включает в себя следующие свойства:

  • count_robots – свойство, качестве параметра принимающее количество роботов uArm (число) к которым необходимо будет подключиться.
  • is_debug – отображает в консоль дополнительную информацию о исполняемых функциях роботом uArm. Может принимать значение 0 или 1.
  • robot_port_X – содержит имя COM-порта, к которому подключен робот Х, причем отсчет роботов начинается с 0. Адрес порта должен быть указан в формате comY, где Y – порядковый номер порта, который был ранее использован в пунктах 1 и 2.

Далее приведен текст программы на языке RCML. Программа обращается к роботу uArm и включает вакуумную помпу на 5 секунд и выключает её:

function main() {
    @ur = robot_uarm;
    @ur-> pump_on();
    system.sleep(1000);
    @ur-> pump_off();
}

Доступные функции робота приведены в таблице:

Определение Описание Пример
move_to(X, Y, Z) перемещает захват робота в точку с координатами X, Y, Z. Движение происходит с максимально возможной скоростью перемещения. @Robot->move_to (10, 10, 10); перемещает захват робота в точку с координатами X=10, Y=10, Z=10
pump_on() включает помпу захвата @Robot->pump_on(); включает помпу захвата
pump_off() вкылючает помпу захвата @Robot->pump_off(); выключает помпу захвата
find_x() возвращает значение координаты X точки, в которой находится захват робота X = @Robot->find_x(); получает значение координаты X точки, в которой находится захват робота и сохраняет ее в переменную X
find_y() возвращает значение координаты Y точки, в которой находится захват робота Y = @Robot->find_y(); получает значение координаты Y точки, в которой находится захват робота и сохраняет ее в переменную Y
find_z() возвращает значение координаты Z точки, в которой находится захват робота Z = @Robot->find_z(); получает значение координаты Z точки, в которой находится захват робота и сохраняет ее в переменную Z
move_to_at_once(x,Y, Z) перемещает захват робота в точку с координатами X, Y, Z. Движение происходит с максимально возможной скоростью перемещения. @Robot->move_to_at_once (10, 10, 10); перемещает захват робота в точку с координатами X=10, Y=10, Z=10
move(x, Y, Z) перемещает захват робота из точки с текущими координатами X_текущее,Y_текущее, Z_текущее в точку с координатами X_текущее + X,Y_текущее + Y, Z_текущее + Z. Движение происходит плавно. @Robot->move(1, 1, 1); изменит координаты точки в которой находится захват робота на указанные значения (X_текущее + 1, Y_текущее + 1, Z_текущее + 1)
move_to_1(x, Y, Z,T) перемещает захват робота в точку с координатами X, Y, Z с задержками между промежуточными шагами на время T. Время указывается в миллисекундах @Robot->move_to_1(1, 1, 1, 50); переместит захват робота в точку с координатами X=1, Y=1, Z=1, с 50 миллисекундной задержкой между промежуточными перемещениями
move_to_2(x, Y, Z,T, R, Th4) перемещает захват робота в точку с координатами X, Y, Z с задержками между промежуточными шагами на время T. Так же поворачивает захват роботоа вокруг оси на Th4 градусов @Robot->move_to_2(1, 1, 1, 1, 1, 30); переместит захват робота в точку с координатами X=1, Y=1, Z=1, с 50 миллисекундной задержкой между промежуточными перемещениями. Так же повернет захват робота вокруг оси на 30 градусов
write_angles(Th1,Th2, Th3, Th4) изменяет углы положения сервоприводов на указанные значенияTh1, Th2, Th3, Th4 @Robot->write_angles(10, 10, 10, 10); изменит углы положения сервоприводов на 10 градусов
rotate_servo4(Th4) изменит угол положения сервопривода захвата робота на указанное значениеTh4 @Robot->rotate_servo4(15); изменит угол положения сервопривода захвата робота на 15 градусов

Доступны следующие оси управления роботом uArm:

Ось Верхняя граница Нижняя граница Описание
locked 1 0 Бинарная ось. Если её значение равно 1, то робот считается заблокированным и игнорирует изменение значений по любым другим осям.
pump 1 0 Бинарная ось. Если её значение равно 1, то будет включена помпа робота. При значении равном 0 помпа будет отключена.
servo1 180 0 ось изменения угла первого сервопривода, находящегося в основании.
servo2 180 0 ось изменения угла второго сервопривода, действующий на угол между основанием и нижним плечом робота.
servo3 180 0 ось изменения угла третьего сервопривода - между плечами робота.
servo4 180 0 ось изменения угла четвертого сервопривода, изменяющего угол поворота захвата.

17.1.5 Быстрое подключение модуля smc к RCML

Модуль робота smc позволяет в RCML управлять промышленными электромеханическими захватами серии LEH производства компании SMC, посредством контроллера LECP6 этого же производителя. Каждый захват представляется в RCML как робот, которого соответственно можно задействовать и освободить. Контроллер LECP6 подключается к ПК, на котором работает RCML, через фирменный кабель из комплекта SMC Controller setting kit LEC-W2, используемый для управления захватом с ПК.

Промышленные электромеханические захваты серии LEH производства SMC

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

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

Для управления захватом SMC LEH через RCML необходимо:

  1. Произвести установку и подключение захвата к контроллеру в соответствии с инструкциями производителя.
  2. Подключить контроллер к ПК.
  3. Добавить запись в секцию [robot_modules] конфигурационного файла RCML свойство module и установить его равным smc в соответствии с разделом “Установка и настройка компилятора и интерпретатора RCML”.
  4. Далее следует произвести настройку модуля. Необходимо указать, через какой COM-порт следует взаимодействовать с контроллером захвата. Для этого нужно перейти в каталог с модулями роботов (robot_modules), а затем в каталог модуля SMC.
  5. В директории модуля smc нужно создать файл config.ini со следующим содержанием:
[main]
count_robots = 1
is_debug = 0
robot_port_0 = \\.\\COM3

Файл конфигурации модуля состоит из секции [main], которая включает в себя следующие свойства:

  • count_robots – свойство, в качестве параметра принимающее количество контроллеров SMC, к которым необходимо будет подключиться.
  • is_debug – отображает в консоль пакеты данных передаваемых контроллеру LECP6. Может принимать значение 0 или 1.
  • robot_port_X – содержит имя COM-порта, к которому подключен робот Х. Адрес порта должен быть указан в формате \ \ .\ \ COMY, где Y – порядковый номер порта. Отсчет должен начинаться с 0.

Далее приведен текст программы на языке RCML. Программа обращается к захвату SMC, передает команду развести губки на 1 см со скоростью 1 мм/с, а затем отправляет команду на возврат в позицию по умолчанию:

function main() {
 @r = robot_smc;
 @r-> move_to(1000, 1);
 @ur->return_to_origin();
}

Доступные функции модуля приведены в таблице:

Определение Описание Пример
move_to(pos, spd) устанавливает расстояние между губками захвата равное pos (в сотых долях миллиметра). Перемещение губок захвата происходит с заданной скоростью spd (мм/с) @Robot->move_to (200, 10); устанавливает расстояние между губками захвата равное 2мм. Скорсоть движения губок захвата 10мм/с
return_to_origin() возвращает губки захвата в положение по умолчанию (расстояние между губками 1мм) @Robot->return_to_origin();
get_cur_position() возвращает текущее расстояние между губками захвата (сотые доли миллиметра) @Robot->get_cur_position();
get_cur_speed() возвращает текущую скорость перемещения губок захвата в мм/с X = @Robot->find_x(); получает значение координаты X точки, в которой находится захват робота и сохраняет ее в переменную X
get_cur_thrust() возвращает значение текущей нагрузки (%) thrust = @Robot->get_cur_thrust();
move_to2( move_mode, spd, pos, accel, decel, pushing_force, trigger_level, pushing_speed, moving_force, area_output1, area_output2, in_position) устанавливает расстояние между губками захвата равное pos (сотые миллиметра); move_mode - тип перемещения (1 - абсолютное перемещение, 2 - относительное перемещение); spd - скорость (мм/с); accel – ускорение при начале движения (мм/с^2); decel – замедление при остановке (мм/с^2); pushing_force (0 - позиционирование, [1-100] - воздействие с заданным моментом (%); trigger_level - значение воздействия, при котором флаг INP примет 1; pushing_speed - скорость перемещения при воздействии (мм/с); moving_force - максимальное усилие при перемещении (%); area_output1, area_output2 - если текущая позиция находится между заданными значениями, флаг AREA примет 1; in_position - если текущая позиция совпадает с заданным значением, флаг INP примет 1
set_hand_control_params(spd, accel, decel) устанавливает скорость spd в мм/с, ускорение accel в мм/с^2 и замедление decel в мм/с^2 для режима ручного управления захватом @Robot->set_hand_control_params(20, 50, 50);

Доступные оси для режима ручного управления:

Ось Верхняя граница Нижняя граница Описание
locked 1 0 Бинарная ось. Если её значение равно 1, то захват считается заблокированным и игнорирует изменение значений по любым другим осям.
servo1 1000 100 Ось, задающая расстояние между губками захвата.

17.1.6 Быстрое подключение модуля FANUC к RCML

Для управления промышленными роботами FANUC с помощью RCML, существует одноименный модуль робота – FANUC. Данный модуль совместим с контроллерами производства FANUC серий A-cabinet и B-cabinet, имеющих включенную программную опцию Modbus TCP. Соответственно модуль FANUC поддерживает всех роботов совместимых с данными контроллерами.

Подробнее о роботах FANUC можно узнать на официальном сайте компании:

С помощью данного модуля возможно управлением робота с ПК через RCML, runtime задание произвольных траекторий перемещения роботу в том числе с поддержкой управления дополнительных групп осей (поворотный стол и пр.)

Низкоуровневый функционал модуля FANUC заключается в возможности передачи контроллеру из RCML данных различных типов: биты, целые числа, вещественные числа, а также запуск программ по механизму PNS.

Коммуникация ПК с RCML и контроллера FANUC осуществляется по FTP Ethernet кабелю. Предварительно робот, контроллер и ПК c RCML должны быть настроены соответствующем образом. Информация о процедуре настройки и подготовки высылается по запросу в произвольной форме, отправленному на info@robotct.ru

Для подключения модуля FANUC необходимо выполнить следующие шаги:

  1. Произвести необходимую предварительную настройку робота и контроллера;
  2. Произвести настройку ПК с RCML для установки связи с контроллером;
  3. Подключить контроллер FANUC к ПК с RCML по FTP Ethernet кабелю, если роботов несколько, то они могут быть подключены в одну сеть с помощью соответствующего сетевого оборудования.
  4. Добавить запись в секцию [robot_modules] конфигурационного файла RCML свойство module и установить его равным fanuc в соответствии с разделом “Установка и настройка компилятора и интерпретатора RCML”
  5. Далее следует произвести настройку самого модуля FANUC, для этого в директории модуля нужно создать файл config.ini и заполнить его по указанным ниже правилам.

В конфигурационном файле обязательно должна присутствовать секция main со свойством count_robots, в котором указывается количество управляемых роботов FANUC.

Для каждого управляемого робота создается секция с именем вида robot_X, где X - порядковый номер робота, отсчет начинается с 1. Данная секция называется секцией робота. В секции робота должно быть 4 свойства:

  • robot_name – уникальное отображаемое имя робота, строка;
  • ip – IP адрес контроллера робота в сети, строка;
  • port – порт робота, число;
  • slave_id – номер робота в сети Modbus.

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

Поддерживаются следующие типы данных:

  • Логический. Указывается как logic_%name% = X, где %name% - имя ячейки, а X – номер входа. Данный тип задействует всего 1 вход.
  • Целочисленный. Указывается как integer_%name% = X, где %name% - имя ячейки, а X – номер входа, с которого начинается отсчет разрядов для передачи числа. Данный тип имеет размер 16 разрядов и соответственно задействует 16 входов. При этом для начала отсчета разрядов можно выбирать только те входа, номер которых при делении на 16 дает в остатке 1 (1, 17, 33, 49 и т.д.). Целочисленный тип может передавать только целые не отрицательные числа.
  • Вещественный. Указывается как real_%name% = A,B,C, где* %name%* - имя ячейки, а A – номер входа, для передачи знака числа, B- номер входа, с которого начинается отсчет разрядов для передачи целой части вещественного числа, С - номер входа, с которого начинается отсчет разрядов для передачи дробной части вещественного числа,. Данный имеет размер 1+16+16 = 33 разряда и задействует столько же входов. При этом привило выбора номера входа для B и C части, такие же как для целочисленного типа данных.
  • Расширенный целочисленный. Указывается как big_integer_%name% = A,B, где %name% - имя ячейки, а A - номер входа, с которого начинается отсчет разрядов для передачи первой части числа, B - номер входа, с которого начинается отсчет разрядов для передачи второй части вещественного числа,. Данный имеет размер 16+16 = 32 разряда и задействует столько же входов. При этом привило выбора номера входа для A и B части, такие же как для целочисленного типа данных. Основное назначение данного типа - передача больших целых не отрицательных чисел.

Для задания конфигурации цифровых выходов робота – DO, применимы такие же типы данных, правила их описания такие же. Единственное отличие – конфигурация цифровых выходов задается в секции с именем DO.

Модуль FANUC поддерживает механизм запуска PNS программ в роботе, однако для этого нужно предусмотреть выделение дополнительных входов/выходов со следующими именами.

Для DI:

  • logic_cycle_stop
  • logic_fault_reset
  • logic_start
  • logic_enable
  • logic_input_PNS1
  • logic_input_PNS2
  • logic_input_PNS3
  • logic_input_PNS4
  • logic_input_PNS5
  • logic_input_PNS6
  • logic_input_PNS7
  • logic_input_PNS8
  • logic_PNS_strobe
  • logic_prod_start

Для DO:

  • logic_cmdenabled
  • logic_prg_running
  • logic_prg_paused
  • logic_motion_held
  • logic_fault
  • logic_busy
  • logic_out_PNS1
  • logic_out_PNS2
  • logic_out_PNS3
  • logic_out_PNS4
  • logic_out_PNS5
  • logic_out_PNS6
  • logic_out_PNS7
  • logic_out_PNS8
  • logic_snack

При задании всех этих входов/выходов у модуля будут доступны функции run_program и run_program_soft использующие механизм PNS.

Доступные функции модуля приведены в таблице:

Определение Описание
run_program(number) Запускает PNS программу под номером num в роботе и ждёт её завершения. При нормальном завершении возвращает значение по умолчанию – 0.0, при ошибке бросает исключение. Данная функция доступна, только если была проведена конфигурация входов выходов, необходимых для работы PNS механизма. В случае если конфигурация не была проведена, функция бросает исключение.
run_program_soft(num) Запускает PNS программу под номером num в роботе и не ожидает её завершения. Завершение запущенной программы возможно только вручную через оперирование соответствующими входами/выходами, отвечающими за управление механизмом PNS. Данная функция доступна, только если была проведена конфигурация входов выходов, необходимых для работы PNS механизма. В случае если конфигурация не была проведена, функция бросает исключение.
set_logic_di(name, value) Установка значения value для логического входа под именем name. Функция бросает исключение, если вход под данным именем не найден. На вход подается положительный сигнал если значение value отлично от нуля, в противном случае отрицательный.
get_logic_di(name) Получение значения логического входа под именем name. Функция бросает исключение, если вход под данным именем не найден. Функция возвращает 1, если сигнал на входе положительный и 0, если отрицательный.
get_logic_do(name) Получение значения логического выхода под именем name. Функция бросает исключение, если вход под данным именем не найден. Функция возвращает 1, если сигнал на входе положительный и 0, если отрицательный.
set_integer_di(name, value) Установка значения value для целочисленного входа под именем name. Функция бросает исключение, если вход под данным именем не найден. Если при установке значение value вещественное, то оно округляется по математическим правилам.
get_integer_di(name) Получение значения целочисленного входа под именем name. Функция бросает исключение, если вход под данным именем не найден.
get_integer_do(name) Получение значения целочисленного выхода под именем name. Функция бросает исключение, если вход под данным именем не найден.
set_real_di(name, value) Установка значения value для вещественного входа под именем name. Функция бросает исключение, если вход под данным именем не найден. В передаваемом значении value знаки, следующие после 3-го знака после запятой, будут отброшены
get_real_di(name) Получение значения вещественного входа под именем name. Функция бросает исключение, если вход под данным именем не найден.
get_real_do(name) Получение значения вещественного выхода под именем name. Функция бросает исключение, если вход под данным именем не найден.
set_big_integer_di(name, value) Установка значения value для расширенного целочисленного входа под именем name. Функция бросает исключение, если вход под данным именем не найден. Если при установке значение value вещественное, то оно округляется по математическим правилам.
get_big_integer_di(name) Получение значения расширенного целочисленного входа под именем name. Функция бросает исключение, если вход под данным именем не найден.
get_big_integer_do(name) Получение значения расширенного целочисленного выхода под именем name. Функция бросает исключение, если вход под данным именем не найден.

Модуль робота FANUC не имеет осей доступных оси для режима ручного управления.

17.2 Пример подключения модулей функций

17.2.1 Быстрое подключение модуля math к RCML

Функциональный модуль math используется для доступа к математическим функциями, аналогичным библиотеке math.h в С++.

  1. Для включения модуля math необходимо добавить запись в разделе [function_modules] конфигурационного файла RCML в соответствии с разделом “Установка и настройка компилятора и интерпретатора RCML”. Дополнительных настроек модуль не требует.
  2. Теперь, в Вашем распоряжении, есть следующие функции:
Определение Описание Выброс исключения, если
pow(a, b) Возведение a в степень b.
sqrt(a) Возвращает квадратный корень из a. a не может быть меньше 0. a < 0
abs(a) Возвращает модуль числа a.
rand(a,b) Возвращает случайное целое число в пределах [b;a+b). a не может быть меньше или равно 0. a ≤ 0
sin(a) Функции sin, cos, tan принимают значения в радианах. Возвращает cинус a.
cos(a) Функции sin, cos, tan принимают значения в радианах. Возвращает коcинус a.
tan(a) Функции sin, cos, tan принимают значения в радианах. Возвращает тангенс a.
asin(a) Возвращает аркcинус a. Из-за свойств функции аргумент a должен быть в пределах [-1;1]. Функции asin, acos, atan возвращают значения в радианах a ∉ [-1;1]
acos(a) Возвращает арккоcинус a. Из-за свойств функции аргумент a должен быть в пределах [-1;1]. Функции asin, acos, atan возвращают значения в радианах. a ∉ [-1;1]
atan(a) Возвращает арктангенс a. Функции asin, acos, atan возвращают значения в радианах.
exp(a) Возвращает e в степени a.
log(a) Возвращает натуральный логарифм числа a. Из-за свойств функции аргумент a должен быть больше 0. a ≤ 0
log10(a) Возвращает десятичный логарифм числа a. Из-за свойств функции аргумент a должен быть больше 0. a ≤ 0

1. В соответствии с разделом “Вызов функций” синтаксис программы с использованием модуля math выглядит следующим образом:

function main() {
    r = 9;
    r = math.sqrt(r);
    system.echo(r);
}

Результатом работы этой программы является число 3.0000
1. Следующий пример демонстрирует выброс исключения в случае нарушения входных данных.

function main() {
    try {
        r=math.log(0);
    } catch {
        system.echo("Throw exception log(0) undefined");
    }
}

Более подробно об использовании исключений описано в разделе “Исключения”

17.3 Пример подключения модулей управления

17.3.1 Быстрое подключение тестового модуля управления test к RCML

Тестовый модуль управления test используется для имитации получения значений осей от устройств управления.

Для включения модуля test необходимо добавить свойство module и присвоить ему значение test в секции [control_modules] конфигурационного файла RCML в соответствии с разделом “Установка и настройка компилятора и интерпретатора RCML”. Дополнительной конфигурации модуль тестового управления не требует.

Далее приведен пример использования модуля тестового управления при взаимодействии с тестовым роботом:

function main() {
    @lr = robot_test; 
    system.hand_control(@lr,"test","X","X");
}

При выполнении данной программы в течении 10 секунд, раз в секунду посылается случайное значение по оси Х. Для функционирования представленной программы должен быть подключен модуль тестового робота, более подробно о подключении написано в разделе “Быстрое подключение модуля робота test к RCML”

Тестовый модуль управления имеет следующие доступные оси:

Наименование Верхняя граница Нижняя граница Примечание
X 100 -100
Y 1 0 Бинарная
Z 100 0

Значения осей, передаваемые каждую секунду времени:

Наименование 1 2 3 4 5 6 7 8 9 10
X 100 -30.1 -2.58 48.9 99.01 -100 12.0 -36.9 0.25 0
Y 0 0 0 1 1 0.5 0.25 0.1 0.75 0.35
Z 4.56 0 78.9 100 50 48.8 66.7 32.4 40 20

17.3.2 Быстрое подключение модуля keyboard к RCML

Модуль управления keyboard используется для ручного управления роботом при помощи клавиатуры.

  1. Для включения модуля keyboard необходимо добавить в секцию [control_modules] конфигурационного файла RCML свойство module и установить его значение равным keyboard в соответствии с разделом “Установка и настройка компилятора и интерпретатора RCML”.
  2. В папке модуля keyboard, находящего в директории control_modules необходимо создать файл конфигурации модуля config.ini.
  3. Файл конфигурации модуля keyboard описывает, какие оси доступны программисту, для осуществления взаимодействия с роботом в режиме ручного управления. Для добавления новой оси, следует придерживаться следующего правила её описания:

3.1. При добавлении новой оси, необходимо в секцию [mapped_axis] добавить свойство имени оси и присвоить ему значение кнопки клавиатуры в HEX формате, при этом количество значений кнопок клавиатуры для одной оси может быть несколько. В общем случае запись в секцию [mapped_axis] будет выглядеть следующим образом:

имя_оси = значение_кнопки_клавиатуры_в_HEX_формате

3.2. Необходимо установить максимальное и минимальное значение, которое может принимать ось. Для этого следует с новой строки добавить секцию в конфигурационного файла config.ini, одноименную с именем оси, и задать свойства upper_value и lower_value передать значения максимума и минимума оси. В общем виде секция выглядит следующим образом:

[имя_оси]

upper_value = максимальное_значение_оси

lower_value = минимальное_значение_оси

3.3. Далее следует определить, какое значение будет иметь ось в случае нажатия кнопки на клавиатуре, которая ранее была прикреплена к ней. Определение значений происходит посредством создания секции, название которой состоит из имени оси и значения кнопки клавиатуры в Hex формате, разделенные между собой нижним подчеркиванием. Для задания значения по умолчанию (в не нажатом) и нажатом состоянии используются свойства unpressed_value и pressed_value, в которые передаются значения. Общий вид секции в таком случае выглядит следующим образом:

[имя_оси_значение_кнопки_клавиатуры_в_HEX_формате]

pressed_value = значение_оси_при_нажатой_клавише

unpressed_value = значение_оси_при_отжатой_клавише

Для наглядности приведен пример конфигурации модуля keyboard с комментариями:

;Секция для работы в Linux
[options]
input_path = /dev/input/event2 ;Путь до файла потока ввода

;Обязательная секция
[mapped_axis]
;название_оси = код_клавиши (в HEX формате)
go = 0x26 ;стрелка_вверх
go = 0x28 ;стрелка_вниз

;Описание оси go, всегда должно иметь оба ключа
[go]
upper_value = 1 ;Верхняя граница значений оси
lower_value = -1 ;Нижняя граница значений оси

;Описание поведения оси go для клавиши *стрелка_вверх* (0x26)
[go_0x26]
pressed_value = 1 ;При нажатии клавиши *стрелка_вверх* значение оси задать равным 1
unpressed_value = 0 ;При отпускании клавиши *стрелка_вверх* значение оси задать равным 0

;Описание поведения оси go для клавиши *стрелка_вниз* (0x28)
[go_0x28]
pressed_value = -1 ;При нажатии клавиши *стрелка_вниз* значение оси задать равным -1
unpressed_value = 0 ;При отпускании клавиши *стрелка_вниз* значение оси задать равным 0

Пример описания конфигурации модуля включает в себя одну ось go, которая вернёт максимальное значение 1 при нажатии стрелки вверх и 0 при её отпускании или -1 при нажатии стрелки вниз и 0 при её отпускании. Более подробно об осях и ручном управлении написано в разделе “Общие сведения”.

В качестве примера, приведен расширенный файл конфигурации модуля keyboard в котором инициализированы следующие оси:

  • [go] – в качестве кнопок управлению используются стрелки вверх-вниз
  • [rotate] – ось управляется стрелками влево-вправо
  • [locked] – используется для блокировки робота, нажатием кнопки «А» на клавиатуре.

Далее приведен листинг файла конфигурации модуля keyboard с тремя осями:

[mapped_axis]
go = 0x26
go = 0x28
rotate = 0x25
rotate = 0x27
locked = 0x41

[go]
upper_value = 1
lower_value = -1

[rotate]
upper_value = 1
lower_value = -1

[locked]
upper_value = 1
lower_value = 0

[locked_0x41]
pressed_value = 1
unpressed_value = 0

[go_0x26]
pressed_value = 1
unpressed_value = 0

[go_0x28]
pressed_value = -1
unpressed_value = 0

[rotate_0x25]
pressed_value = 1
unpressed_value = 0

[rotate_0x27]
pressed_value = -1
unpressed_value = 0

Далее приведен пример текста программы на языке RCML. Программа обращается к тестовому роботу, который был подключен в разделе “Быстрый старт” и выглядит следующим образом:

function main() {
    @tr = robot_test; 
    system.hand_control(@tr,"keyboard","X","go");
}

Первой строкой программы создаётся переменная, которая прикрепляется к тестовому роботу, о том, что такое тестовый робот и как его подключить, написано в разделе “Быстрое подключение модуля робота test к RCML”.

Вторая строка переводит тестового робота в ручной режим управления. Как было описано ранее ось go, принимает значение в зависимости от нажатия стрелок вверх и вниз и имеет максимальное значение 1 и минимальное значение -1, которое передаёт их ось робота X. Однако, во время выполнения программы значение оси управляющего устройства не будет равно значению оси робота. Автоматически происходит процесс масштабирования оси, то есть максимальное значение клавиши, преобразовывается в максимальное значение оси робота. Аналогичная ситуация происходит и во время нажатия стрелки вниз. Модуль управления keyboard получает минимальное значение оси клавиатуры и преобразует в минимальное значение оси робота. Таким образом, при нажатии стрелки вверх, тестовый робот сообщит, что он перейдет в максимальное положение 100, а при нажатии стрелки вниз в -100.

У тестового робота существует ещё две оси Y,Z. Используя файл конфигурации модуля keyboard описанного на шаге 3, добавим ещё одну ось управления роботом. Пусть по мимо существующей передачи значений в ось Z тестового робота, будет приходить значение оси rotate. Программный код в этом случае будет выглядеть следующим образом:

function main() {
    @tr = robot_test; 
    system.hand_control(@tr,"keyboard","X","go","Y","rotate");
}

Во время выполнения программы, нажатие на стрелки вверх или вниз вызовет сообщения от тестового робота, о том, что была получена команда на смену значения по оси X, и по оси Y, при нажатии клавиш влево-вправо. Причем принимаемые роботом значения будут максимально допустимые для этих осей.

Больше информации о ручном управлении находиться в разделе 4 «Режим ручного управления».

17.3.3 Быстрое подключение модуля gamepad к RCML

Модуль управления gamepad обеспечивает ручное управление роботом при помощи геймпада.

Количество используемых осей зависит от содержимого файла конфигурации модуля config.ini. Максимальное количество осей, которые могут быть определены в файле конфигурации 18, из них:

  • 11 бинарных осей, соответствующих кнопкам.
  • 4 оси для стиков.
  • 2 оси для крестовины (D-pad).
  • 1 ось с названием “Exit” назначает на кнопку функцию завершения режима ручного управления.

    1. Для включения модуля gamepad необходимо добавить в секцию [control_modules] конфигурационного файла RCML свойство module со значением gamepad в соответствии с разделом “Установка и настройка компилятора и интерпретатора RCML”.
    2. Далее следует перейти в каталог модулей RCML, в котором располагаются модули управления (control_modules), далее в каталог gamepad.
    3. Нужно создать пустой файл config.ini в каталоге gamepad. Далее в этот файл будут помещены настройки конфигурации джойстика.
    4. Далее необходимо настроить конфигурационный файл модуля config.ini. Предлагается использовать одну из двух готовых конфигураций модуля, которые подходят для большинства геймпадов:

4.1. Универсальный файл конфигурации модуля для взаимодействия с геймпадом №1:

[axis]
Exit = 9
B1 = 1
B2 = 2
B3 = 3
B4 = 4
L1 = 7
L2 = 5
R1 = 8
R2 = 6
start = 10
T1 = 11
T2 = 12
RTUD = 13
RTLR = 16
LTUD = 15
LTLR = 14
arrowsUD = 17
arrowsLR = 18

[B1]
upper_value = 1
lower_value = 0

[B2]
upper_value = 1
lower_value = 0

[B3]
upper_value = 1
lower_value = 0

[B4]
upper_value = 1
lower_value = 0

[L1]
upper_value = 1
lower_value = 0

[L2]
upper_value = 1
lower_value = 0

[R1]
upper_value = 1
lower_value = 0

[R2]
upper_value = 1
lower_value = 0

[start]
upper_value = 1
lower_value = 0

[T1]
upper_value = 1
lower_value = 0

[T2]
upper_value = 1
lower_value = 0

[RTUD]
upper_value = 0
lower_value = 65535

[RTLR]
upper_value = 0
lower_value = 65535

[LTUD]
upper_value = 0
lower_value = 65535

[LTLR]
upper_value = 0
lower_value = 65535

[arrowsUD]
upper_value = 1
lower_value = -1

[arrowsLR]
upper_value = 1
lower_value = -1

4.2. Универсальный файл конфигурации модуля для взаимодействия с геймпадом № 2:

[axis]
Exit = 9
B1 = 4
B2 = 2
B3 = 1
B4 = 3
L1 = 5
L2 = 7
R1 = 6
R2 = 8
start = 10
T1 = 11
T2 = 12
RTUD = 16
RTLR = 13
LTUD = 15
LTLR = 14
arrowsUD = 17
arrowsLR = 18

[B1]
upper_value = 1
lower_value = 0

[B2]
upper_value = 1
lower_value = 0

[B3]
upper_value = 1
lower_value = 0

[B4]
upper_value = 1
lower_value = 0

[L1]
upper_value = 1
lower_value = 0

[L2]
upper_value = 1
lower_value = 0

[R1]
upper_value = 1
lower_value = 0

[R2]
upper_value = 1
lower_value = 0

[start]
upper_value = 1
lower_value = 0

[T1]
upper_value = 1
lower_value = 0

[T2]
upper_value = 1
lower_value = 0

[RTUD]
upper_value = 0
lower_value = 65535

[RTLR]
upper_value = 0
lower_value = 65535

[LTUD]
upper_value = 0
lower_value = 65535

[LTLR]
upper_value = 0
lower_value = 65535

[arrowsUD]
upper_value = 1
lower_value = -1

[stick_zero_positions]
axis_1 = 31487
axis_2 = 31487
axis_3 = 31487
axis_4 = 31487
arrows = 4294967295

[options]
input_path = /dev/input/js1

[arrowsLR]
upper_value = 1
lower_value = -1

В случае использования файлов готовых конфигураций модуля, шаги 5-8 следует пропустить. Если файлы конфигурации не обеспечивают требуемого уровня управления, то следует дополнительно выполнить пропущенные шаги.

Рекомендуется, прежде чем приступить к управлению реальным физическим роботом, проверить конфигурацию геймпада на тестовом роботе, о том как подключить тестового робота к RCML написано в разделе “Быстрое подключение модуля робота test к RCML”.

  1. Необходимо убедиться в том, что геймпад подключен к компьютеру. В противном случае, при дальнейшем запуске ‘calibrator.exe’, будет сформирована ошибка ‘cant CreateDevice’

  2. Для заполнения конфигурационного файла нужно запустить файл ‘calibrator.exe’

Это консольное приложение запустит мастера калибровки, инструкциям которого необходимо следовать для наполнения файла конфигурации config.ini.

Для пропуска назначения кнопки достаточно подождать время, заданное в качестве параметра. (по умолчанию – 5 секунд)

  1. Если нажатие нужной кнопки было пропущено или нажата не та клавиша, то следует закрыть окно, перезапустить ‘calibrator.exe’ и выполнить все инструкции более внимательно.

  2. Таким образом, будет создана конфигурация модуля gamepad. Если открыть этот файл, то он будет содержать названия осей и их пороговые значения.

  3. Далее приведена таблица с расшифровкой каждой доступной оси геймпада:

Наименование Верхняя граница Нижняя граница Примечание
B1 1 0 Бинарная ось. Соответствует любой кнопке из группы кнопок ABXY геймпада x-box.
B2 1 0 Бинарная ось. Соответствует любой кнопке из группы кнопок ABXY геймпада x-box.
B3 1 0 Бинарная ось. Соответствует любой кнопке из группы кнопок ABXY геймпада x-box.
B4 1 0 Бинарная ось. Соответствует любой кнопке из группы кнопок ABXY геймпада x-box.
L1 1 0 Бинарная ось. Соответсвует кнопке с таким же названием геймпада x-box
L2 1 0 Бинарная ось. Соответсвует кнопке с таким же названием геймпада x-box
R1 1 0 Бинарная ось. Соответсвует кнопке с таким же названием геймпада x-box
R2 1 0 Бинарная ось. Соответсвует кнопке с таким же названием геймпада x-box
start 1 0 Бинарная ось. Соответсвует кнопке с таким же названием геймпада x-box
T1 1 0 Бинарная ось. Соответсвует кнопке с таким же названием геймпада x-box
T2 1 0 Бинарная ось. Соответсвует кнопке с таким же названием геймпада x-box
RTUD Назначается Назначается Дискретная ось. Соответствует правому стику.
RTLR Назначается Назначается Дискретная ось. Соответствует правому стику.
LTUD Назначается Назначается Дискретная ось. Соответствует левому стику.
LTLR Назначается Назначается Дискретная ось. Соответствует левому стику.
arrowsUD 1 -1 Дискретная ось. Соответствует крестовине(D-pad) геймпада.
arrowsLR 1 -1 Дискретная ось. Соответствует крестовине(D-pad) геймпада.

Далее приведен пример текста программы на языке RCML. Программа обращается к тестовому роботу, который был подключен при выполнении раздела “Быстрый старт” и выглядит следующим образом:

function main() {
    @tr = robot_test; 
    system.hand_control(@tr,"gamepad","X","RTUD");
}

Первой строкой программы создаётся переменная, к которой прикрепляется тестовый робот.

Вторая строка переводит тестового робота в ручной режим управления от геймпада.

Во время выполнения программы, использование правого стика по направлению вверх-вниз, передаст значение оси X в тестового робота. При каждом изменении положения стика, в окне отразится сообщение о том, что тестовый робот, получил смену значения по оси X.

Больше информации о ручном управлении находиться в разделе «Режим ручного управления».

17.3.4 Быстрое подключение Myo к RCML

Myo – это браслет, позволяющий при помощи различных жестов управлять компьютером, смартфоном или планшетом.

Более подробно о браслете Myo можно прочитать на официальном сайте:

Модуль управления myo используется для ручного управления роботом при помощи устройства Myo которое отслеживает жесты и положение руки в пространстве.

  1. Для начала Вам следует выполнить установить и настроить Myo на Вашем компьютере, как это показано в Setting up the Myo armband with your Windows PC.
  2. Далее следует установить следующие настройки для Myo Connect

2.1. Необходимо установить настройки General в соответствии с изображением.

2.2. Следует настроить Presentation Mode как показано на рисунке.

2.3. Кроме этого следует отключить Connector и Presentation Mode в соответствии с рисунком.

2.4. Необходимо также настроить Myo Keyboard Mapper как показано на изображении.

  • Для включения модуля myo необходимо добавить в секцию [control_modules] конфигурационного файла RCML свойство module и установить его значение равным myo в соответствии с разделом “Установка и настройка компилятора и интерпретатора RCML”. Дополнительно модуль в конфигурировании не нуждается и будет подключен по умолчанию к первому браслету в системе.
function main() {
    @r = robot_test;
    system.hand_control(@r,"myo","X","fist");
}

Первой строчкой программы, создаётся переменная, которая прикрепляется к тестовому роботу, о том, как подключить тестового робота, написано в разделе “Быстрое подключение модуля робота test к RCML”.

Вторая строчка программы, указывает что управление роботов будет происходить в режиме ручного управления. То есть в ось X тестового робота @r передаётся значение оси fist устройства управления myo. Более подробно о том, что означает функция ручного управления написано в руководстве по RCML в разделе “Режим ручного управления”.

Во время выполнения программы, необходимо сделать жесть fist (сжать кулак), жест будет распознан Myo и через модуль управления myo тестовому роботу, будет передано новое значение оси Х. Поскольку 1 это максимальное значение оси fist для Myo, а для тестового робота максимальное значение оси Х это 100, то верхняя граница значения оси fist будет преобразована до верхней границы оси X, тем самым робот изменит значение оси Х на максимально возможное – 100.

Браслет Myo умеет распознавать следующие жесты:

  • fist;
  • fingers spread;
  • double tap;
  • wave out;
  • wave in.
  • rest (отсутствие жеста, расслабленная рука)

Все из вышеперечисленных жестов доступны для программиста по умолчанию.

Кроме этого, программисту доступны следующие оси в модуле:

Наименование Верхняя граница Нижняя граница Примечание
locked 1 0 Бинарная ось. Отражает статус браслета Myo: блокирован - 1, разблокирован - 0.
fist 1 0 Бинарная ось. Если наблюдается жест fist - 1, в противном случае - 0.
left_or_right 1 -1 Дискретная ось. Удобна для задания направления перемещения или поворота. Основана на жестах wave out и wave in. Автоматически определяет направление кисти в данных жестах направо или налево (независимо от того на какую руку надет браслет) и принимает следующие значения: -1 - налево, 0 - нет жеста, 1 - направо.
fingers_spread 1 0 Бинарная ось. Если наблюдается жест fingers spread - 1, в противном случае - 0.
double_tap 1 0 Бинарная ось. Если наблюдается жест double tap - 1, в противном случае - 0.
fist_pitch_angle 18 0 Непрерывная ось. Отмечает угол pitch (тангаж) браслета относительно земли, при жесте fist.
fist_roll_angle 18 0 Непрерывная ось. Отмечает угол roll (крен) браслета относительно земли, при жесте fist.
fist_yaw_angle 18 0 Непрерывная ось. Отмечает угол yaw (рысканье) браслета относительно земли, при жесте fist.
fingers_spread_pitch_angle 18 0 Непрерывная ось. Отмечает угол pitch (тангаж) браслета относительно земли, при жесте fingers spread.
fingers_spread_roll_angle 18 0 Непрерывная ось. Отмечает угол roll (крен) браслета относительно земли, при жесте fingers spread.
fingers_spread_yaw_angle 18 0 Непрерывная ось. Отмечает угол yaw (рысканье) браслета относительно земли, при жесте fingers spread.

Примечания к таблице:

  1. Значения всех осей передаются 20 раз в секунду.
  2. Значение осей с суффиксом angle, изменяется и передается только при наблюдении соответствующего жеста.
  3. Если браслет блокирован, то значения всех осей жестов, кроме оси locked и осей с суффиксом angle, будут равны 0.
  4. Браслет блокируется автоматически, если некоторое малое время (1-2 сек) по датчикам наблюдается не известный браслету жест или наблюдается жест rest, а также если обнаружен сдвиг браслета на руке или снятие браслета с руки.
  5. Разблокировка браслета происходит жестом double tap, как и во многих Myo-совместимых приложениях.
  6. Рекомендуется проявлять осторожность при использовании для управления роботом одновременно оси fist или fingers_spread, и осей содержащих одноименные префиксы fist или fingers_spread соответственно, т.к. оси жестов принимают значение 1 при попытке изменения значения осей, активных при соответствующем жесте, поскольку для этого требуется появление этого жеста.

Кроме тестового робота, к системе RCML можно подключить другого робота, совместимого с RCML.

17.4 Пример подключения модуля выбора робота

17.4.1 Быстрое подключение модуля avg к RCML

Модуль выбора робота avg реализует простейшую логику выбора наиболее быстрого робота на основе статистических данных RCML.

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

Затем всегда выбирает того робота или робота того модуля, у которого наименьшее среднеарифметическое время выполнения требуемой функции по его (робота) числу попыток выполнения этой функции.

Включение модуля avg стандартно - нужно добавить в секцию [choice_modules] конфигурационного файла RCML свойство module и установить его равным avg в соответствии с разделом “Установка и настройка компилятора и интерпретатора RCML”.

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