6.5. Скрипты XILab

Скриптовый язык Xilab реализован с помощью QtScript, он в свою очередь основан на ECMAScript.

ECMAScript — это встраиваемый расширяемый не имеющий средств ввода/вывода язык программирования, используемый в качестве основы для построения других скриптовых языков. Стандартизирован международной организацией ECMA в спецификации ECMA-262.

Используется третья редакция стандарта .

6.5.1. Краткое описание языка

6.5.1.1. Типы данных

В ECMAScript поддерживаются девять типов данных. Три из них (Reference, List, и Completion) используются только как промежуточные результаты расчета значений выражений. Оставшиеся шесть типов это:

  • Неопределённый,
  • Нулевой,
  • Логический,
  • Строковый,
  • Числовой,
  • О бъектный

6.5.1.2. Инструкции

Наиболее распространенные конструкции языка ECMAScript представлены в таблице ниже:

Название Применение Краткие сведения
Блок {[<список инструкций>]} Несколько инструкций можно объединить в один блок фигурными скобками.
Объявление переменной var <список объявления переменных> Переменная объявляется с помощью ключевого слова «var».
Пустая инструкция ; Точка с запятой является пустой инструкцией. Заканчивать строки точкой с запятой не обязательно.
Условие if (<условие>) <инструкция> [ else <инструкция> ] Условное выполнение можно производить с помощью ключевых слов «if … else». Если условие верно, то выполняется инструкция блока if, в противном случае выполняется инструкция блока else, если он присутствует.
Цикл do <тело цикла> while (<условие>) while (<условие>) <тело цикла> for ([<выражение 1>]; [<условие>]; [<выражение 2>]) <тело цикла> Цикл можно реализовать несколькими различными способами. Форма «do … while …» выполняет тело цикла как минимум один раз и пока условие верно. Форма «while … do …» выполняет тело цикла пока условие верно. Форма «for …» выполняет выражение до начала (выражение 1), а затем выполняет тело цикла каждый раз после выполнения итеративного выражения (выражение 2) и проверки условия на истинность.
Возврат return [<выражение>] Прекращает выполнение функции и возвращает выражение как результат.
Генерация исключения throw <выражение> Генерирует исключение, которое может быть обработано конструкцией try (см.ниже).
Блок try try <блок> catch (<идентификатор>) <блок> try <блок> finally <блок> try <блок> catch (<идентификатор>) <блок> finally <блок> Используется совместно с исключениями. Конструкция «try … catch … finally» пытается выполнить блок try. Если в этом блоке происходит исключение идентификатор, то выполняется содержимое блока catch. После всего безусловно выполняется блок finally. Один из блоков «catch» и «finally» может отсутствовать.

6.5.1.3. Объявление переменных

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

6.5.1.4. Ключевые и зарезервированные слова

Следующие слова являются ключевыми в языке и не могут быть использованы как идентификаторы:

break     else        new     var
case      finally     return  void
catch     for         switch  while
continue  function    this    with
default   if          throw
delete    in          try
do        instanceof  typeof

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

abstract  enum        int        short
boolean   export      interface  static
byte      extends     long       super
char      final       native     synchronized
class     float       package    throws
const     goto        private    transient
debugger  implements  protected  volatile
double    import      public

6.5.1.5. Функции

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

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

function sum(arg1, arg2) { // a function which takes two parameters
    return arg1 + arg2;    // and returns their sum
}

6.5.2. Подсветка синтаксиса

Шрифт текста в окне скрипта имеет подсветку синтаксиса. Цвета:

Тип выражения цвет пример отображения
Произвольные функции фиолетовый image1
Функции XILab синий image2
Положительные числа зеленый image3
Отрицательные числа красный image4
Комментарии серый image5
Все остальное черный image6

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

6.5.3. Дополнительные функции, предоставляемые XILab

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

image6

  • log(string text [, int loglevel]) - запись в лог XILab
  • msleep(int ms) - задержка выполнения скрипта
  • new_axis(int serial_number) - создание объекта типа «ось»
  • new_file(string filename) - создание объекта типа «файл»
  • new_calibration(int A, int Microstep) - создание структуры калибровки для передачи калиброванным функциям
  • get_next_serial(int serial) - получение следующего серийного номера
  • command_wait_for_stop(int refresh_period) - ожидание остановки движения
  • а также все функции библиотеки libximc (см. Руководство по программированию)

Кроме этого, в скриптах определены и доступны для использования все константы протокола обмена. Пример использования.

6.5.3.1. Запись в лог XILab

Производится вызовом функции log(string text [, int loglevel] ). Дописывает в лог XiLab строку text. Если передаётся второй параметр loglevel, то сообщение получает соответствующий уровень логгирования и отображается соответствующим цветом.

Loglevel Тип
1 Error
2 Warning
3 Info

Пример:

var x = 5;
log("x = " + x);

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

Замечание: не рекомендуется вызывать функции интерфейса Xilab (запись в лог) чаще одного раза в 20 мс.

6.5.3.2. Задержка выполнения скрипта

Производится вызовом функции msleep(int ms). Скрипт делает паузу в данном месте выполнения длиной ms миллисекунд.

Пример:

msleep(200);

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

6.5.3.3. Cоздание объекта типа «ось»

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

Пример:

var x = new_axis(123);
x.command_move(50);

В этом примере в первой строке скрипта происходит создание оси с именем переменной x, которая соответствует контроллеру с серийным номером «123». Если такой контроллер не подключен к компьютеру, то скрипт выдаст ошибку выполнения и завершится. Во второй строке оси x подается команда переместиться в координату 50 [шагов].

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

6.5.3.4. Cоздание объекта типа «файл»

Скрипты Xilab имеют возможность чтения из файла и записи в файл. Для этого необходимо создать объект типа файл и работать с ним. Имя файла указывается при создании в конструкторе. Объект имеет следующие функции:

Тип_возврата Имя_функции Краткие сведения
bool open() Открывает файл. Файл открывается на чтение-запись, если это возможно; если нет, то только на чтение.
void close() Закрывает файл.
Number size() Возвращает размер файла в байтах.
bool seek(Number pos) Устанавливает текущую позицию в файле в pos байт [1].
bool resize(Number size) Изменяет размер файла до size байт. Если size меньше текущего размера, то файл обрезается, если больше, то дополняется нулями.
bool remove() Удаляет файл.
String read(Number maxsize) Читает строку из файла, но не более maxsize байт. Данные читаются в кодировке utf-8.
Number write(String s, Number maxsize) Записывает строку в файл, но не более maxsize байт. Данные записываются в кодировке utf-8, символ конца строки пользователь должен записать самостоятельно. Возвращает количество записанных байт, либо -1, если произошла ошибка.

Все функции работы с файлами, возвращающие значение типа bool, возвращают «true» в случае успеха, в противном случае «false».

Используйте символ «/» как разделитель путей для работы скриптов на всех платформах (Windows/Linux/Mac).

Пример:

var winf = new_file("C:/file.txt"); // An example of file name and path on Windows
var linf = new_file("/home/user/Desktop/file.txt"); // An example of file name and path on Linux
var macf = new_file("/Users/macuser/file.txt"); // An example of file name and path on Mac

var f = winf; // Pick a file name
if (f.open()) { // Try to open the file
  f.write( "some text" ); // If successful, then write desired data to the file
  f.close(); // Close the file
} else { // If file open failed for some reason
  log( "Failed opening file" ); // Log an error
}

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

6.5.3.5. Cоздание структуры калибровки

функция new_calibration(double A, int Microstep) принимает в качестве параметров коэффициент A пересчета из шагов в пользовательские единицы и деление микрошага Microstep (либо полученное ранее вызовом функции get_engine_settings в поле MicrostepMode, либо задаваемое одной из констант MICROSTEP_MODE_) и возвращает структуру типа calibration_t, которую необходимо передать в калиброванные get_/set_* функции для получения/задания величин в пользовательских единицах. Следующие две записи функционально эквивалентны:

// create calibration: type 1
var calb = new_calibration(c1, c2);
// create calibration: type 2
var calb = new Object();
calb.A = c1;
calb.MicrostepMode = c2;

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

6.5.3.6. Получение следующего серийного номера

функция get_next_serial(int serial) принимает в качестве параметра число и возвращает наименьший серийный номер из списка серийных номеров открытых устройств, который больше переданного ей параметра. Если такого серийного номера нет, то возвращается 0. Эта функция удобна для автоматического создания объекта типа «ось» без задания фиксированного серийного номера.

Пример:

var first_serial = get_next_serial(0);
var x = new_axis(first_serial);
var y = new_axis(get_next_serial(first_serial));

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

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

6.5.3.7. Ожидание остановки движения

Функция command_wait_for_stop(int refresh_period) останавливает выполнение скрипта до тех пор, пока контроллер не прекратит движение, то есть, пока флаг MVCMD_RUNNING в члене MvCmdSts структуры, возвращаемой функцией get_status(), не будет снят. Функция скриптов command_wait_for_stop напрямую использует функцию command_wait_for_stop библиотеки libximc и принимает в качестве параметра число, означающее периодичность (в миллисекундах) считывания состояния контроллера.

Эта функция также присутствует как метод объекта типа «ось».

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

6.5.3.8. функции библиотеки libximc

Функции библиотеки libximc начинающиеся на «get_» считывают из контроллера настройки и возвращают соотвествующую команде структуру данных. Функции библиотеки libximc начинающиеся на «set_» принимают как параметр структуру данных и записывают эти настройки в контроллер. Заполнить структуру данных для set-функций можно двумя способами:

  1. вызвать соответствующую get-функцию и модифицировать необходимые поля
// set settings: type 1
var m = get_move_settings();
m.Speed = 100;
set_move_settings(m);

2. create an Object and set all of its properties that are present as members of the data structure (case-sensitive).

// set settings: type 2
var m = new Object;
m.Speed = 100;
m.uSpeed = 0;
m.Accel = 300;
m.Decel = 500;
m.AntiplaySpeed = 10;
m.uAntiplaySpeed = 0;
set_move_settings(m);

Необходимо помнить, что при использовании первого способа в контроллер посылается дополнительная команда (вызывается не только set-функция, но и get- перед этим), а при использовании второго способа необходимо инициализировать все свойства объекта, соответствующие именам членов структуры. Если в объекте какое-то свойство будет пропущено, то оно будет считаться равным нулю. Если в объекте будет объявлено какое-либо дополнительное свойство, не входящее в структуру данных, то оно будет проигнорировано. Если тип данных какого-либо поля не будет соответствовать типу данных структуры, то будет произведено приведение типов по правилам EcmaScript. Структуры данных для всех команд описаны в главе Описание протокола.

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

6.5.4. Примеры

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

6.5.4.1. Скрипт циклического движения

var first_border = -10; // first border coordinate in mm
var second_border = 10; // second border coordinate in mm
var mm_per_step = 0.005; // steps to distance translation coefficient
var delay = 100; // delay in milliseconds
var calb = new_calibration(mm_per_step, get_engine_settings().MicrostepMode); // create calibration structure
command_stop(); // send STOP command (does immediate stop)
command_zero(); // send ZERO command (sets current position and encoder value to zero)
while (1) { // infinite loop
  command_move_calb(first_border, calb); // move towards one border
  command_wait_for_stop(delay); // wait until controller stops moving
  command_move_calb(second_border, calb); // move towards another border
  command_wait_for_stop(delay); // wait until controller stops moving
}

6.5.4.2. Скрипт сканирования и записи в файл

var start = 0; // Starting coordinate in steps
var step = 10; // Shift amount in steps
var end = 100; // Ending coordinate in steps

var speed = 300; // maximum movement speed in steps / second
var accel = 100; // acceleration value in steps / second^2
var decel = 100; // deceleration value in steps / second^2
var delay = 100;

var m = get_move_settings(); // read movement settings from the controller
m.Speed = speed; // set movement speed
m.Accel = accel; // set acceleration
m.Decel = decel; // set deceleration
set_move_settings(m); // write movement settings into the controller

var f = new_file("C:/a.csv"); // Choose a file name and path
f.open(); // Open a file
f.seek( 0 ); // Seek to the beginning of the file

command_move(start); // Move to the starting position
command_wait_for_stop(delay); // Wait until controller stops moving

while (get_status().CurPosition < end) {
  f.write( get_status().CurPosition + "," + get_chart_data().Pot + "," + Date.now() + "\n" ); // Get current position, potentiometer value and date and write them to file
  command_movr(step); // Move to the next position
  command_wait_for_stop(delay); // Wait until controller stops moving
}
f.close(); // Close the file

6.5.4.3. Скрипт движения по списку позиций из csv файла

var axis = new_axis(get_next_serial(0)); // Use first available controller
var x; // A helper variable, represents coordinate
var ms; // A helper variable, represents wait time in milliseconds
var f = new_file("./move_and_sleep.csv"); // Choose a file name and path; this script uses a file from examples in the installation directory
f.open(); // Open a file
while ( str = f.read(4096) ) { // Read file contents string by string, assuming each string is less than 4 KiB long
  var ar = str.split(","); // Split the string into substrings with comma as a separator; the result is an array of strings
  x = ar[0]; // Variable assignment
  ms = ar[1]; // Variable assignment
  log( "Moving to coordinate " + x ); // Log the event
  axis.command_move(x); // Move to the position
  axis.command_wait_for_stop(100); // Wait until the movement is complete
  log( "Waiting for " + ms + " ms" ); // Log the event
  msleep(ms); // Wait for the specified amount of time
}
log ( "The end." );
f.close(); // Close the file

move_and_sleep.csv

  • пример файла для использования с приведенным выше скриптом

6.5.4.4. Скрипт, который строит список осей с серийными номерами и позицией каждой оси

var i = 0; // Declare loop iteration variable
var serial = 0; // Declare serial number variable
var axes = Array(); // Declare axes array
while (true) { // The loop
  serial = get_next_serial(serial); // Get next serial
  if (serial == 0) // If there are no more controllers then...
      break; // ...break out of the loop
  var a = new Object(); // Create an object
  a.serial = serial; // Assign serial number to its "serial" property
  a.handle = new_axis(serial); // Assign new axis object to its "handle" property
  axes[i] = a; // Add it to the array
  i++; // Increment counter
}
for (var k=0; k < axes.length; k++) { // Iterate through array elements
  log ( "Axis with S/N " + axes[k].serial + " is in position " + axes[k].handle.get_status().CurPosition ); // For each element print saved axis serial and call a get_status() function
}

6.5.4.5. Скрипт-пример работы с битовыми масками

/*
  Bitmask example script
*/
var a = new_axis(get_next_serial(0)); // take first found axis
var gets = a.get_status(); // read status once and reuse it

var gpio = gets.GPIOFlags;
var left = STATE_LEFT_EDGE;
var right = STATE_RIGHT_EDGE;
var mask = left | right;
var result = gpio & mask;
log( to_binary(left) + " = left limit switch flag" );
log( to_binary(right) + " = right limit switch flag" );
log( to_binary(mask) + " = OR operation on flags gives the mask" );
log( to_binary(gpio) + " = gpio state" );
log( to_binary(result) + " = AND operation on state and mask gives result" );
if ( result ) {
  log("At least one limit switch is on");
} else {
  log("Both limit switches are off");
}

// Binary representation function
function to_binary(i)
{
  bits = 32;
  x = i >>> 0; // coerce to unsigned in case we need to print negative ints
  str = x.toString(2); // the binary representation string
  return (repeat("0", bits) + str).slice (-bits); // pad with zeroes and return
}

// String repeat function
function repeat(str, times)
{
  var result="";
  var pattern=str;
  while (times > 0) {
    if (times&1) {
      result+=pattern;
    }
    times>>=1;
    pattern+=pattern;
  }
  return result;
}
[1]Выйти из файла: если позиция находится за пределами файла, то seek () не должен немедленно расширять файл. Если запись выполняется в этой позиции, файл должен быть расширен. Содержимое файла между предыдущим концом файла и новыми записанными данными НЕ УКАЗАНО и варьируется между платформами и файловыми системами.