Друзья, добрый день! Я рад видеть вас на страницах своего блога. Сегодня, я продолжаю серию заметок, посвященных VCP Scripter, и, специально для тех, кто уже читал предыдущую заметку про API и осознал те удобства, которые в нем предлагаются, я хочу рассказать про синтаксис VCP Scripter и его особенности.
VCP Scripter использует синтаксис Pascal, поэтому если вы хорошо знакомы с Pascal и уже активно применяете VCP Scripter, то можете пропустить эту заметку и не читать дальше. Эта заметка в бОльшей степени ориентирована на тех пользователей, кто не знаком с Pascal, не знает особенностей реализации VCP Scripter, но имеет общее представление об основах программирования и хочет начать писать свои скрипты.
Важно помнить и понимать, что VCP Scripter это частичная реализация, поэтому некоторые примеры, которые доступны по ссылке http://www.tutorialspoint.com/pascal, рекомендуемой в качестве руководства в официальном API, не пройдут проверку синтаксиса в редакторе VCP System и не будут выполнены. Забегая вперед, скажу, что весь необходимый функционал, тем не менее, присутствует и “выпилено”, действительно, только то, что не очень-то и нужно или вообще не нужно =)
Русскоязычным пользователям в качестве справочника по Pascal могу порекомендовать материалы, представленные на https://pas1.ru/. Часть последних я активно использовал при написании это заметки.
Общее замечание: в VCP Scripter, как и в Pascal, синтаксис регистронезависимый, поэтому, например, разницы между переменными bcm и BcM нет.
Переменные и их типы
В VCP Scripter доступны для использования следующие типы переменных:
- ansistring;
- boolean;
- byte;
- char;
- integer;
- ShortInt;
- string;
- variant.
Если с первыми типами все более менее понятно, то последний тип variant требует небольшого комментария. Этот тип уникален тем, что тип определяется в процессе выполнения скрипта. Переменной такого типа можно присваивать значения других типов, например, integer или string, и менять их процессе выполнения скрипта.
Декларация переменных осуществляется указанием ключевого слова var. Инициализировать значение сразу при декларации переменной в VCP Scripter НЕЛЬЗЯ, зато можно декларировать типы сразу для нескольких переменных.
var
address1, address2: integer;
input: string;
Можно декларировать свои типы, для этого достаточно использовать ключевое слово type. При декларации типов есть ограничение: нельзя декларировать несколько типов за раз. Декларация для каждого типа должна быть на новой строчке, даже если они одинаковые.
type
ecuAddress = integer;
var
engine:ecuAddress;
Константы
В VCP Scripter можно объявлять константы. Для этого используется ключевое слово const:
const
bcmAddress = $09;
Массивы
Массивы в VCP Scripter, как и в Pascal, это структура, занимающая в памяти единую область и состоящая из фиксированного числа компонентов одного типа.
Декларация массива состоящего из трех элементов типа string может быть выполнена, например, следующим образом:
type
addressList = array [ 0..2] of string;
var
ecus: addressList;
Операторы
Оператор присваивания
В качестве оператора присваивания в VCP Scripter, как и в Pascal, используется конструкция вида :=.
v := a;
Арифметические операторы
Из арифметических операторов в VCP Scripter реализованы все, кроме оператора взятия по модулю % .
+ | сложение двух операндов |
– | вычитание правого операнда из левого |
* | умножение операндов |
/ | деление левого операнда на правый |
Операторы отношения
Операторы отношения в VCP Scripter доступны все.
= | равно |
<> | НЕ равно |
> | больше |
< | меньше |
>= | больше или равно |
<= | меньше или равно |
Логические операторы
Логические операторы так же представлены в полном объеме.
and | логическое И |
or | логическое ИЛИ |
not | логическое НЕ |
xor | логическое исключающее ИЛИ |
Битовые операторы
Из битовых операторов VCP Scripter не поддерживаются операторы сдвига вправо >> и влево << , но придумать зачем они могут понадобиться в скриптах сложно.
and | побитовое И |
or | побитовое ИЛИ |
not | побитовое НЕ |
Условные операторы
Оператор if-else
Когда выполнение скрипта доходит до условного оператора if-else, то в зависимости от результата логического выражения в его заголовке выполняются разные блоки кода. В качестве условия может стоять любое выражение, результатом вычисления которого является одно из булевых значений true или false. Если выражение вернуло true, то выполняется блок, начинающийся со слова then, если false – то блок, начинающийся со слова else. После выполнения одного из вложенных блоков кода, выполнение скрипта возвращается в основную ветку. Другой вложенный блок не выполняется.
if YOUR_CONDITION then
OPERATOR_IN_CONDITION_OF_TRUE
else
OPERATOR_IN_CONDITION_OF_TRUE;
В качестве примера, можете использовать вот этот скрипт
var
inp: string;
const
inpMsg = 'Please, input your choice';
begin
inp := AskUserForInput(inpMsg);
if (inp = '1') then
writeln('You choice is 1. Run block A' )
else
writeln('You choice is NOT 1. Run block B' );
end.
Непосредственно после then может стоять только один оператор. При необходимости выполнения нескольких операторов они должны быть заключены в операторные скобки begin-end.
if YOUR_CONDITION then
begin
YOUR_CODE_IN_CONDITION_OF_TRUE
end
else
begin
YOUR_CODE_IN_CONDITION_OF_FALSE
end
Ниже пример скрипта, где используется несколько операторов в блоке.
var
inp: string;
const
inpMsg1 = 'Please, input your first choice';
inpMsg2 = 'Please, input your second choice';
begin
inp := AskUserForInput(inpMsg);
if (inp = '1') then
begin
writeln('You choice is 1. Operation 1.');
writeln('... next operation of block');
end
else
writeln('You choice is NOT 1.')
end.
Допустимо вложение одного оператора if или if-else в другой. При этом, действует следующее правило: каждому then соответствует ближайшее else, не задействованное при установлении соответствия с другим then. Глубина вложенности операторов if может быть сколь угодно большой.
var
inp1: string;
inp2: string;
const
inpMsg1 = 'Please, input your first choice';
inpMsg2 = 'Please, input your second choice';
begin
inp1 := AskUserForInput(inpMsg1);
inp2 := AskUserForInput(inpMsg2);
if (inp1 = '1') then
if (inp2 = '3') then
writeln('You first choice is 1, second choice is 3')
else
writeln('You first choice is 1, second choice is NOT 3')
else
if (inp2 = '2') then
writeln('You first choice is NOT 1, second choice is 2')
else
writeln('You first choice is NOT 1, second choice is NOT 2')
end.
Оператор case
Кроме оператора if в VCP Scripter реализован оператор case. Его можно трактовать как некий вопрос, имеющий большое число ответов. В заголовке оператора case вместо логического выражения фигурирует переменная, которую называют селектором. До этого в скрипте ей присваивается какое-либо значение. По ходу выполнения оператора case, значение селектора сравнивается с различными, описанными в нем, альтернативами (метками). Как только совпадение будет найдено, то выполняется блок скрипта для данной метке и происходит выход в основную ветку программы. Метки являются константами, которые может принимать селектор. Тип меток и тип селектора должны быть совместимы по присваиванию.
case YOUR_SELECTOR of
YOUR_LABEL_A: YOUR_OPERATOR_FOR_LABEL_A ;
YOUR_LABEL_B: YOUR_OPERATOR_FOR_LABEL_B ;
YOUR_LABEL_C: YOUR_OPERATOR_FOR_LABEL_C ;
else YOUR_DEFAULT_OPERATOR
end;
Пример скрипта с использованием оператора case.
var
inp: string;
const
inpMsg = 'Please, input your choice';
begin
inp := AskUserForInput(inpMsg);
case inp of
'1': writeln ('Your choice is 1');
'2': writeln ('Your choice is 2');
'3': writeln ('Your choice is 3');
'4': writeln ('Your choice is 4');
else writeln ('Error')
end;
end.
На использование оператора case накладываются следующие ограничения:
- селектор должен иметь какой-либо порядковый тип;
- каждая метка не может быть переменной или выражением.
Циклы
Цикл for-do
Цикл позволяет выполнить операции, указанные в его теле заданное число раз. В заголовке указываются два значения. Первое значение присваивается переменной-счетчику и от этого значения начинается отсчет количества итераций (повторений). Отсчет идет всегда с шагом равным единице. Второе значение указывает при каком значении счетчика цикл должен остановиться. Количество итераций цикла определяется разностью между вторым и первым значением плюс единица.
for COUNTER:=INITIAL_VALUE to FINAL_VALUE do begin
YOUR_CODE_HERE;
end;
for COUNTER:=INITIAL_VALUE downto FINAL_VALUE do begin
YOUR_CODE_HERE;
end;
В Pascal доступно два варианта конструкций с использование служебного слова to (с увеличением счетчика) и с использованием служебного слова downto (с уменьшением счетчика).
VCP Scripter поддерживает оба варианта. Пример цикла for-to с увеличением счетчика ниже.
var
i:byte;
begin
for i:=1 to 3 do begin
writeln('iteration');
end;
end.
Цикл while
Цикл while является циклом с предусловием. В заголовке цикла находится логическое выражение. Если оно возвращает true, то тело цикла выполняется, если false – то нет.
while YOUR_CONDITION do begin
YOUR_CODE_IN_CONDITION_OF_TRUE
end;
Применение цикла while в примере ниже. Работу этого скрипта можно прекратить не только вводом цифры 3, но и нажатием кнопки Cancel или закрытием окна ввода.
var
inp: string;
const
inpMsg = 'Please, input 3';
begin
inp := '';
while (inp <> '3') do begin
writeln('You input ' + inp);
inp := AskUserForInput(inpMsg);
end;
end.
Тело цикла выполнится столько раз, сколько раз логическое выражение вернет true.
Цикл repeat
Тело цикла while не выполнится ни разу, если логическое выражение в заголовке сразу вернет false. На практике же бывает, что нужно предусмотреть выполнение тела цикла хотя бы один раз, вне зависимости от того, что вернуло логическое выражение в заголовке при первом обращении. Для таких случаев используется цикл с постусловием.
repeat
YOUR_CODE;
until YOUR_CONDITION;
Обратите внимание, в отличии от предыдущего примера построенного с использованием цикла while, здесь скрипт будет показывать окно ввода до тех пор, пока не будет введена цифра 3, нажата кнопка Cancel или закрыто окно.
var
inp: string;
const
inpMsg = 'Please, input 3б press Cancel or close box';
begin
inp := '';
repeat
inp := AskUserForInput(inpMsg);
until (inp <> '3') or (inp <>'');
end.
Операторы break и continue
Оба оператора используются для прерывания хода выполнения цикла.
Оператор break выполняет полный выход из цикла, т.е. все возможные итерации цикла прерываются.
Оператор continue прерывает только текущую итерацию.
Пример скрипта с использованием оператора break.
var
i:byte;
begin
for i:=1 to 10 do begin
writeln('iteration');
if i = 2 then
break;
end;
end.
Оператор безусловного перехода goto
Оператор goto осуществляет переход к оператору, помеченному специальной меткой, которая отделяется от самого оператора двоеточием. В качестве метки может быть использовано любое целое число без знака, содержащее более четырех цифр, или любое имя. Чтобы можно было использовать метку, она должна быть объявлена в разделе меток вашего скрипта. Этот раздел начинается служебным словом label. Чтобы перейти к помеченному оператору, используется оператор перехода goto .
label YOUR_LABEL_NAME;
begin
YOUR_LABEL_NAME:
YOUR_OPERATOR;
YOUR_CODE
goto YOUR_LABEL_NAME;
end.
В примере ниже реализован переход к началу скрипта с помощью метки goStart , в случае если пользователь введет цифру 2, в противном случае выполнение скрипта будет завершено.
label goStart;
var
inp: string;
const
inpMsg = 'Please, input 2 to run script from begining';
begin
goStart:
writeln('Begining');
inp := AskUserForInput(inpMsg);
if inp = '2' then
goto goStart;
writeln('End');
end.
Процедуры и функции
VCP Scripter поддерживает процедуры и функции. В общем виде, процедура – это подпрограмма, которая ничего не возвращает, а функция – это подпрограмма, которая возвращает одиночное значение.
Функции
Конструкция определения функции в VCP Scripter в целом повторяет синтаксис, принятый в Pascal. Единственный момент, это присвоение результата выполнения функции. В VCP Scripter для этого существует специальная переменная result, поэтому результат работы функции нужно присваивать этой переменной.
function FUNCTION_NAME(FIRST_ARG: arg1; SECOND_ARG: type2; ...): FUNCTION_RESULT_TYPE;
local FUNCTION_LOCAL_VAR;
begin
...
YOUR_CODE
...
result := RESULT_OF_CODE;
end;
Очевидно, что в функциях можно использовать только те типы переменных, которые доступны в VCP Scripter.
В следующем примере определена функция ReadLcBit, которая возвращает значение указанного бита, в указанном байте, указанного блока с использование протокола UDS. В теле скрипта функция вызывается фиксированными параметрами: 17 блок, 1 байт, 0 бит.
function ReadLcBit(ecuAddress : string; byteNo, bitNo : integer ) : integer;
var lc:string;
begin
if ConnectToEcuUDS(ecuAddress) then
begin
lc := ReadLongCoding;
if TestBitInHexString(lc, byteNo, bitNo) then
result := 1
else
result := 0;
end
else
result := -1;
end;
begin
case ReadLcBit('17', 1, 0) of
1: writeln('activated');
0: writeln('deactivated');
else writeln('could not connect to ECU');
end;
end.
Процедуры
Конструкция определения процедуры в VCP Scripter в целом повторяет конструкцию определения функцию, с тем отличием, что процедура не возвращает результат.
procedure PROCEDURE_NAME(FIRST_ARG: arg1; SECOND_ARG: type2; ...);
local PROCEDURE_LOCAL_VAR;
begin
...
YOUR_CODE
...
end;
Пример простой процедуры, осуществляющей логирование в файл с именем, начинающимся с VIN вы найдете ниже.
var
vin:string;
procedure LogToFile(msg : string);
var fPath: string;
begin
fPath := GetCurrentScriptPath + 'logs\' + vin + '_';
SaveStringToFile(msg, fPath);
end;
begin
vin:=ReadVin;
LogToFile('Your log message');
end.
Комментарии
Думаю, что не сильно ошибусь, если скажу, что это один из самых востребованных и полезных элементов синтаксиса в любом языке программирования. VCP Scripter поддерживаются все варианты, доступные в Pascal: как однострочные, так и многострочные. Причем многострочные варианты можно оформлять двумя способами.
// single line comment
(* multiline
comments *)
{ alternative multiline
comments }
Заключение
Пожалуй, это все что я хотел рассказать про синтаксис VCP Scripter. Надеюсь, что вы нашли в этом материале что-то полезное. Если у вас есть что добавить к заметке или обсудить, вы можете сделать на нашем форуме в этой теме.
Всем спасибо за внимание. Оставайтесь с нами, следующая часть в этой серии заметок уже на подходе…